ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flutter - GraphQL 에러 처리 방법
    개발/flutter 2020. 10. 14. 20:51

    에러 핸들링

     

    graphql client 를 사용하여 query() 메소드를 실행하면 리턴값 혹은 에러가 리턴이된다.

    그 리턴된 내용은 QueryResult 에서 확인할수있다.

    아래는 결과를 핸들링 하는 코드이다.

     String _handleReturn(QueryResult queryResult) {
        //에러가 있다면 exception을 throw 해주고
        if (queryResult.hasException) { 
          throw queryResult.exception;
        }
        //에러가 없다면 서버로 부터 받은 값을 파싱하여 리턴한다
        final String url = queryResult.data['getSomething'];
        return url;
      }
    

     

    queryResult.execption 은 두가지의 예외로 나뉜다.

    Graphql Exception  /   Client Exception

    Client Exception 은 클라이언트의 상황에 따른 에러이다.

    대표적으로 네트워크 오류이다. 인터넷 연결이 느리거나 끊겨있을경우 api 쿼리 이후 Client Exception 이 발생한다.

     

    Graphql Exception 은 서버가 던져주는 에러이다.

    특정상황에 예로 비밀번호가 틀렸다던가, 품절된 상품이라거나 하는 에러를 서버가 던져주는 경우이다.

     

    다시 위 코드로 와서 queryResult.exception 에서

    서버에러는 queryResult.exception.graphqlErrors

    클라이언트 에러는 queryResult.exception.clientException

     

    이렇게 두개를 가져올수있다 .

    클라이언트 오류의경우 graphqlErrors 는 비어있고

    서버가주는에러 일경우 clientException 은 null 값이다.

     

    Future<String> getSomething(String id) {
        return getGraphQLClient()
            .query(_queryOptions(orderId, paymentModel))
            .then(_handleReturn)
            .timeout(Duration(milliseconds: 10000), onTimeout: () {
          throw TimeoutException("에러발생");
        });

    추가적으로 timeout 메소드를 넣어줬다. 10초 동안 아무런 응답을( 에러든, 리턴값이든) 받지못할경우 TimeoutException을 발생 시켰다.

    그러면 이제 총 세가지의 exception 타입이있고 그에따른 처리를 하면된다.

    모든 코드는 다음과같다.

    class GetSomethingProvider {
      Future<String> getSomething(String id) {
        return getGraphQLClient()
            .query(_queryOptions(orderId, paymentModel))
            .then(_handleReturn)
            .timeout(Duration(milliseconds: 10000), onTimeout: () {
          throw TimeoutException("에러발생");
        });
    
      QueryOptions _queryOptions(String id) {
        final query = readOrderInfoRequest(id);
        return QueryOptions(
          fetchPolicy: FetchPolicy.networkOnly,
          documentNode: gql(query),
        );
      }
    
      String _handleReturn(QueryResult queryResult) {
        if (queryResult.hasException) {
          throw queryResult.exception;
        }
    
        final String url = queryResult.data['getSomething'];
        return url;
      }
    
      String getSomethingQuery(
              String id) =>
          '''
        {	
          getSomething(input: {
            id: "$id"
          })
        }
        ''';
    }

    이제 에러가 발생할경우 이 getSomething() 을 실행시킨곳에서 에러를 catchError() 를 통해

    에러를 받아올수있다. 

     

    에러 처리 과정 

    나같은 경우에는 네트워크 통신을 하는 기능을 파일명_provider.dart 에몰아넣고

    비즈니스 로직의 경우 파일명_bloc.dart 에 넣어놓았다.

    비즈니스로직에서 네트워크 통신을 시키고 리턴값을 처리하도록 구현했으므로

    즉 Bloc 파일에서 Provider 파일을 부르고 네트워크 통신을하고 에러를 처리했다.

    Future<String> getSomething(String id) {
    	final getSomthingProvider = GetSomthingProvider();
        return getSomthingProvider
            .getSomething(id)
            .then((value) {
              return value;
            })
            .catchError((exception) => {ExceptionHandler.handleError(exception)})
      }

    여기서  ExceptionHandler.handleError(exception) 이 정적 메소드에서

    이곳에 에러를 핸들링 하는 코드를 넣었다.

    여기서 에러에 맞는 로직을 처리하고 다시 에러를 다시 throw 할것이다.

    그러면 이 getSomething(id) 를 호출한 view단(위젯) 에서 다시 catchError 를 하여

    에러 팝업을 띄우든 전페이지로 돌아가든 작업을 할수있다.

     

    ExceptionHandler.handleError(exception)

    class ExceptionHandler {
      static Future<void> handleError(Exception exception) {
        if (exception is TimeoutException) {
          
          throw exception.message;
          
        } else if (exception is OperationException) {
          if (exception.graphqlErrors.isEmpty) {        
            throw exception.clientException.message;
          } else {
            if (exception.graphqlErrors[0].message.contains('error_code')) {
                //error_code 에맞는 작업 처리 하는 함수
              analyzeErrorAddNetworkState(
                  exception.graphqlErrors[0].message);
                  
            }
    		//에러 다시던져줌
            throw exception.graphqlErrors[0].message;
          }
        }
      }

     

    exceptionHandler 에서는 비즈니스로직에서 이 에러를 위해 처리해줘야 하는 과정들을 넣어줬다.

     

    TimeoutException이거나 OperationException이거나 다른 타입의 exception을 모두 이곳에서 처리하면된다.

    graphql관련 에러는 OperationException 안에 들어가있다.

    graphQLErrors가 비어있음은 클라이언트에러이므로 클라이언트에러를 처리하면된다.

    비어있지 않으면 graphqlError를 원하는 로직에 맞게 처리해주면된다.

     

    나의경우 타임아웃이나 네트워크 에러의경우

    비즈니스 로직에서는 크게할게없고 위젯에서 알람창을 띄우는게 다여서

    메시지를 throw 해줬고 그 에러메시지를 가지고

    view단 (위젯) 에서도 아래 코드처럼 에러에 맞는 작업을 할수있다. 

     

    getSomethingBloc.getSomething(id).then((data) {
       //do Something when return value
    }).catchError((error) {
      //do Something when its error
        showErrorDialog(context, error);
    });

     

     

     

     

     

     

    추후 포스팅

     

    페이지 네이션 하기

    댓글

Designed by Tistory.