본 포스트는 공식 레퍼런스를 참고해 GraphQL을 공부하며 직접 작성한 가이드 입니다. 본 포스트는 2021년 7월 최신 버전인 v16.2를 기준으로 작성되어 있습니다.
Exception Handling
Spring에서는 예외를 잘 다루기 위해서 @ControllerAdvice
와 같은 전역 예외 처리를 지정해줄 수 있었다. 예외가 발생하면 해당 예외에 따라 적절한 메세지를 내려주는 등의 정의를 GraphQL
에서도 할 수 있다. 간단하게 ImageResolver
에서 예외를 발생시켜 보자.
1 2 3 4 5 6 7 8 @Slf4j @Component public class ImageResolver implements GraphQLResolver <WebToon> { public DataFetcherResult<Image> image (WebToon webToon, DataFetchingEnvironment env) { log.info("requesting image data for webtoon id: {}" , webToon.getId()); throw new RuntimeException (String.format("No Images on webtoon title : %s" , webToon.getTitle())); } }
그러면 다음과 같은 응답이 떨어진다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { "errors" : [ { "message" : "Internal Server Error(s) while executing query" , "locations" : [] } ], "data" : { "webToon" : { "id" : "3568e088-ec83-11eb-9a03-0242ac130003" , "title" : "여신강림" , "webToonType" : "Origianls" , "image" : null } } }
우리는 No Images on webtoon title : %s
의 포멧을 가진 응답이 떨어지기를 기대했는데 Internal Server Error(s) while executing query
라는 예외 메세지가 떨어졌다. 이제 이를 고쳐서 우리 스타일의 응답을 만들기 위해 GraphQL
용 예외 처리기를 만들어보자.
WebToonException.class 일단 더 명확한 예외를 던지기 위해 WebToonException.class
를 만들어준다.
1 2 3 4 5 6 7 8 9 10 11 12 public class WebToonException extends RuntimeException { private WebToonErrorCode errorCode; public WebToonException (String message, WebToonErrorCode errorCode) { super (message); this .errorCode = errorCode; } public WebToonErrorCode getErrorCode () { return errorCode; } }
조금 더 디테일한 에러를 위해 errorCode를 정의해주자.
1 2 3 4 5 6 7 8 9 10 11 @AllArgsConstructor @Getter public enum WebToonErrorCode { NO_TITLE(1001 , "No Title Exsist" ), NO_IMAGE(1002 , "No Image Exsist" ), UNKNOWN(9999 , "Unknown Exception" ) ; private int errorCode; private String errorMessage; }
그리고 우리가 정의한 예외를 던지도록 하자.
1 throw new WebToonException (String.format("No Images on webtoon title : %s" , webToon.getTitle()), WebToonErrorCode.NO_IMAGE);
@ExceptionHandler GraphQL
에서 예외 처리를 위한 여러 방법들이 있는데 먼저 @ExceptionHanlder
를 사용하는 방법부터 해보자. 먼저 예외처리를 위한 빈을 등록해준다. @GraphQLAdvice
는 직접 만든 어노테이션인데 일반 @Bean
으로 등록해도 상관없다.
1 2 3 4 5 6 7 8 9 10 @Slf4j @GraphQLAdvice public class GraphQLExceptionAdvice { @ExceptionHandler(WebToonException.class) public WebToonError handle (WebToonException e) { log.error(String.format("Webtoon Exception caused by [%s], errorCode : [%s]" , e.getMessage(), e.getErrorCode())); return new WebToonError (e); } }
코드를 보면 WebToonException
을 잡아서 WebToonError
로 변경해준다.
WebToonError.class
의 구현을 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class WebToonError implements GraphQLError { private WebToonException e; public WebToonError (WebToonException e) { this .e = e; } @Override public String getMessage () { return e.getErrorCode().getErrorMessage(); } @JsonIgnore @Override public List<SourceLocation> getLocations () { return null ; } @JsonIgnore @Override public ErrorClassification getErrorType () { return null ; } }
WebToonError.class
는 GraphQLError
를 상속하고 메세지로 에러코드의 메세지를 내려준다.
1 2 3 graphql: servlet: exception-handlers-enabled: true
그리고 마지막으로 exception handler
를 활성화 한 뒤 요청을 보내고 응답을 봐보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "errors" : [ { "message" : "No Image Exsist" } ] , "data" : { "webToon" : { "id" : "3568e088-ec83-11eb-9a03-0242ac130003" , "title" : "여신강림" , "webToonType" : "Origianls" , "image" : null } } }
우리가 정의한 에러코드 메세지인 No Image Exsist
가 잘 내려온다.
GraphQL답게 예외 처리하기 GraphQLError
인터페이스는 예외에 대한 다양한 정보를 담아 내려주는 메소드들을 정의한다. 이를 통해 발생한 예외에 대해 상세하고 다양한 정보를 담을 수 있다. 먼저 우리가 만들어둔 WebToonException.class
를 리팩토링하자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class WebToonException extends GraphQLException implements GraphQLError { private WebToonErrorCode errorCode; public WebToonException (String message, WebToonErrorCode errorCode) { super (message); this .errorCode = errorCode; } public WebToonErrorCode getErrorCode () { return errorCode; } @Override public List<SourceLocation> getLocations () { return null ; } @Override public ErrorClassification getErrorType () { return null ; } @Override public Map<String, Object> getExtensions () { return WebToonExceptionHelper.makeExtensions(this ); } }
WebToonExceptionHelper
에 추가로 정의될 메세지들을 넣었다.
1 2 3 4 5 6 7 8 9 10 public class WebToonExceptionHelper { public static Map<String, Object> makeExtensions (WebToonException e) { Map<String, Object> extensions = new LinkedHashMap <>(); extensions.put("errorCode" , e.getErrorCode().getErrorCode()); extensions.put("errorMessage" , e.getErrorCode().getErrorMessage()); return extensions; } }
이제 WebToonException
이 GraphQLError
의 역할까지 수행하니 exception-handlers-enabled
를 다시 false
로 바꿔주고 응답을 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 { "errors" : [ { "message" : "Exception while fetching data (/webToon/image) : null" , "locations" : [ { "line" : 6 , "column" : 5 } ] , "path" : [ "webToon" , "image" ] , "extensions" : { "errorCode" : 1002 , "errorMessage" : "No Image Exsist" , "classification" : "DataFetchingException" } } ] , "data" : { "webToon" : { "id" : "3568e088-ec83-11eb-9a03-0242ac130003" , "title" : "여신강림" , "webToonType" : "Origianls" , "image" : null } } }
extensions
에 필요한 에러코드와 에러메세지가 내려오는걸 확인할 수 있다. 또한 어느 위치에서 에러가 났는지에 대한 상세한 정보도 GraphQL spec
에 따라 내려오는걸 볼 수 있다.
Repository
모든 가이드의 예제 코드는 SongHayoung/springboot-graphql-tutorial 에서 확인할 수 있습니다.