카카오스타일이 GraphQL 에서 에러를 어떤 규칙으로 전달하고 처리하는지 정리한 글이다. `errors` 가 왜 배열인지부터 시작해, 사용자 메시지를 서버가 관리하기로 한 이유, 에러 코드를 `extensions` 에 싣는 방식, HTTP 상태 코드는 항상 200 으로 두기로 한 결정까지 이어진다.
핵심 포인트- GraphQL 은 한 요청에서 여러 데이터를 가져오고 일부만 실패할 수 있으므로 `errors` 가 배열이다. 이 구조를 모르면 API 설계를 잘못하기 쉽다.
- 판정 규칙은 명확하다. `errors` 에 하나라도 있으면 에러로 처리하고, `data` 가 null 이라는 것만으로는 에러로 보지 않는다.
- 사용자 친화적 메시지는 서버가 관리한다. 클라이언트(특히 앱)는 수정이 어렵고 상황마다 분기해야 하며, 고정 문구가 아닌 메시지도 있기 때문이다.
- 클라이언트가 분기해야 하는 상황(예: 미로그인 시 로그인 페이지 이동)을 위해 에러 코드를 함께 보내며, 표준이 없어 `extensions` 에 담는다.
- HTTP 상태 코드는 문법 오류일 때만 400 을 쓰고 그 외에는 항상 200 이다. 비즈니스 실패는 `errors` 로 판단한다.
상세 정리- 도입: Java 의 checked exception 을 IDE 가 시키는 대로 try-catch 로 감싸고 넘어간 경험을 예로 들며, 예외를 제대로 이해하고 처리하기가 쉽지 않다는 문제의식에서 출발한다.
- 현재 한계도 함께 밝힌다. 에러가 여러 개일 때 적절한 처리 방법을 몰라 첫 번째 에러만 유의미하게 다룬다.
- 반대 방향 규칙: `errors` 에 값이 있으면 `data` 에 값이 있어도 에러로 처리한다.
- 메시지 관리 위치 논쟁: UI 이슈로 보면 클라이언트가 맞을 것 같지만, 앱은 배포가 느리고 에러마다 분기가 필요하며 "xxx 상품은 구매할 수 없습니다" 처럼 동적인 문구도 있어 서버로 정했다.
- 서버 관리의 대가: 다국어를 서버가 떠안는다. 클라이언트에서 언어 정보를 받아 요청별로 메시지를 다르게 구성해야 한다.
- 에러 코드의 필요: 언어마다 다른 사용자 메시지를 보고 클라이언트가 분기할 수는 없으므로 코드가 따로 있어야 한다.
- `ignorable` 필드: 프로세스상 일상적으로 발생하는 에러는 사용자에게는 에러로 보내되 모니터링에서는 에러로 세지 않도록 `true` 로 표시한다.
- 표시 정보 확장: 에러 팝업의 타이틀·아이콘·닫기 버튼 문구까지 내려주고 싶은 경우를 위해 스키마를 정해 소통하는 API 도 있지만 아직 표준화가 덜 됐다고 단서를 단다.
- 부가 데이터 사례: 소셜 로그인 시 같은 이메일 계정이 이미 있으면 그냥 로그인시키지 않고, 마스킹된 이메일을 `email` 필드에 담아 반환해 해당 계정으로 로그인하도록 유도한다.
- 상태 코드의 역사: 처음 쓴 서버는 에러 시 500 을 반환했고 다음에 쓴 것은 200 을 반환해 혼란이 있었다. 논의 중인 스펙은 2xx 가 아닌 코드를 쓰는 방향(부분 성공은 200)이다.
- 대비: 향후 스펙이 바뀔 여지가 있으므로 클라이언트는 HTTP 상태 코드에 의존하지 않는 형태로 작성하고 있다.
왜 읽나GraphQL 서버의 에러 규약을 처음 정하는 팀에게 메시지 관리 주체, 에러 코드 위치, 상태 코드 정책의 선택지와 각각의 대가를 정리해 준다.