GraphQL 실패 시 상태코드 관리
일반적으로 HTTP 통신에서는 통신이 실패하면 4**, 5** 대 에러를 반환합니다. 하지만 GraphQL에서는 성공해도 200, 실패해도 200으로 에러를 반환하고 있어 이렇게 처리하는 이유에 대해 궁금했습니다.
200대로 처리하는 이유를 찾아보고 에러 코드를 어떻게 클라이언트에게 전달할지 고민하였습니다.
(1) 서버 단에서 통신 실패 시 상태 코드를 변환할 것인지, (2) error 메시지 안에 상태 코드를 전달할 것인지 고민하였습니다.
GrpahQL 실패 시 200 코드를 반환하는 이유
1. Multiple 요청 처리
GraphQL에서는 단일 요청뿐만 아니라 여러 개의 Query나 Mutation을 처리할 수 있습니다.
일부 요청이 실패할 수 있고, 성공할 수도 있어 Multiple 요청 모두를 4**, 5** 에러로 반환하는 것이 더 혼란을 줄 수 있겠다 생각이 들었습니다.
query {
user1: getUser(id: "1") {
id
name
}
user2: getUser(id: "2") {
id
name
}
book: getBook(id: "101") {
id
title
}
}
2. 클라이언트 - 서버 표준
GrpahQL 클라이언트 라이브러리 (Apollo, Relay 등)은 기본적으로 HTTP 상태 코드 200을 전제로 설계되었다.
200이 아닌 경우 전체 요청이 실패한 것으로 처리합니다.
3. 에러 정보의 일관성
GraphQL은 에러를 errors 필드에 포함하여 응답 본문을 통해 전달하도록 설계되었습니다. 상태 코드도 errors에 담아 세부적인 에러 메시지를 전달합니다.
Spring 환경 GraphQL 예외 처리 다루기
@QueryMapping
public Optional<Book> bookById(@Argument Long id) {
// Error 발생시키기
if (id == 0) {
throw new GraphqlException("올바른 id가 아닙니다.", HttpStatus.BAD_REQUEST.value());
}
return Book.getBookById(id);
}
GraphqlException을 커스텀하게 만들어 테스트해보겠습니다.
@Getter
public class GraphqlException extends RuntimeException implements GraphQLError {
private final String message;
private final int httpStatus;
private final GraphqlErrorType errorType;
public GraphqlException(String message, int httpStatus, GraphqlErrorType errorType) {
super(message);
this.message = message;
this.httpStatus = httpStatus;
this.errorType = errorType;
}
@Override
public List<SourceLocation> getLocations() {
return null;
}
// classification 대신 errorType으로 전달하기 위해 null 반환
@Override
public ErrorClassification getErrorType() {
return null;
}
// ErrorClassification 구현체 반환하는 메서드
public GraphqlErrorType getGraphqlErrorType() {
return errorType;
}
@Override
public List<Object> getPath() {
return GraphQLError.super.getPath();
}
@Override
public Map<String, Object> toSpecification() {
System.out.println("GraphQLError.super.toSpecification() = " + GraphQLError.super.toSpecification());
return GraphQLError.super.toSpecification();
}
// extensions 객체 반환
@Override
public Map<String, Object> getExtensions() {
return Map.of(
"httpStatus", httpStatus,
"errorType", errorType.toString()
);
}
}
- GraphQLError은 grpahql에서 발생하는 에러를 처리할 때 사용합니다.
- 기본적으로 제공하는 정보를 전달하지 않으려면 return null 하여 에러 정보를 안 보낼 수 있습니다.
@Configuration
public class GraphqlExceptionHandler extends DataFetcherExceptionResolverAdapter {
@Override
protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) {
if (ex instanceof GraphqlException graphqlException) {
return new GraphqlException(graphqlException.getMessage(), graphqlException.getHttpStatus(),
graphqlException.getGraphqlErrorType());
}
return null;
}
}
- 전달받은 예외를 가지고 GraphQLError를 원하는 형식으로 만들어 처리합니다.
DataFetcherExceptionResolverAdapter 란
GraphQL Query or Mutation을 처리하는 중에 발생한 예외를 해결하고 적절한 에러 형식으로 변환하는 데 사용됩니다.
단일 요청 실패
단일 요청 실패 시 하나의 요청에 대한 메시지만 담겨있습니다.
Exception을 커스텀하게 설정하여 message와 httpStatus 정보만 전달하였습니다.
멀티 요청 실패, 성공 둘 다 있는 경우
{
bookById(id: 0) {
id
name
author {
id
name
}
}
books {
id
name
}
}
에러가 발생한 부분은 그대로 errors 객체에 담아서 전달하고, books{ .. } 요청은 성공되어 data { ... } 담겨 전달되는 것을 확인할 수 있습니다.
'Web > GraphQL' 카테고리의 다른 글
GraphQL schema & type 개념 이해하기 (0) | 2024.12.29 |
---|