세상에 나쁜 코드는 없다

Application Error Handling Design: REST API 에러 핸들링 설계 본문

웹개발/백엔드

Application Error Handling Design: REST API 에러 핸들링 설계

Beomseok Seo 2022. 12. 30. 18:25

에러코드가 필요한 이유

개발 단계에서 서버에 요청을 보냈을 때 서버에서 “Oops, something went wrong” 따위의 오류 메시지를 반환한다면 Client의 문제인지 Server의 문제인지 알 기 어렵다. 따라서 Server 는 Client의 요청에 대해 구체적인 에러 메시지를 전달해 줄 필요가 있다.

장점

  • HTTP Status code 만으로 Response에 대한 충분한 설명을 할 수 없다.
    • 더욱 구체적인 에러 내용을 정의하여 소통할 수 있다.
  • 오류 메시지는 API 사용 방법에 대한 가시성을 제공하는 핵심 도구가 된다.
  • 클라이언트단에서 에러 코드에 따라 다른 처리 로직을 개발할 수 있다.
    • HTTP status code만으로 모든 예외처리를 할 수 없다.
    • 따라서 status code와 응답의 message를 추가로 참조하여 로직을 개발해야한다.
    • message 에 종속되는 것보다 내부 error code에 종속되는 것이 낫다
      • 클라이언트단의 코드가 더 변화가능성이 적은 부분에 종속되게 함

좋은 어플리케이션 에러 핸들링 디자인

Application Level 에러코드는 정확한 표준이 없어 어플리케이션마다 구조가 상이하다. 다음과 같은 내용을 고민해보는 것은 좀 더 나은 에러코드 구조를 만드는 것을 도와줄 것이다.

  1. 어떤 HTTP status code 들을 사용해야하는가
에러상황에 맞는 HTTP status code를 반환하는 것이 우선이다.

HTTP status code는 70개가 넘게 세분화되어 있지만, 대부분의 개발자가 모든 code를 암기하지 않을 뿐더러 잘 사용되지 않는 code를 사용하는 것은 개발에 추가적인 비용을 발생시킬 수 있다. 프로그램이 크지 않다면 HTTP status code를 일부만 사용하고 자세한 내용은 Response Body 에 담는 것이 좋다.

HTTP status code는 다음과 같이 3개의 코드로만 한정할 수도 있다.

  • 정상적 요청 및 정상적 응답(200)
  • Client 측 오류(404)
  • Server 오류(500)

만약 이 단계에서 API에 대해 좀 더 세분화한 코드가 필요하다고 생각되는 경우 새로운 status code를 도입할 수 있다. 너무 적은 status code는 관심사가 다른 application level error 가 같은 status code로 취급될 수 있고, 너무 많은 status code는 개발에 비용을 증가시킬 수 있다. 따라서 프로그램에 적합한 수준의 status code 개수를 선택해야 한다. Google GData API 는 10개, Netflix 는 9개, Digg는 8개의 code만을 사용한다.

  1. 어떤 내용을 담아야하는가

크게 세 가지로 나눌 수 있다.

  • Error Code : 오류에 대한 식별자. 오류 코드
  • Message : 오류 코드에 대한 설명. Human-readable
  • Detail : 오류에 대한 자세한 설명 및 메타데이터

Error Code

Error Code는 integer 일 수도 string 일 수도 있다.

예)

code: 1001 (숫자 방식)

code: INVALID_REQUEST (문자열 방식)

code: “AUTH-001” (문자열 방식2: 문자와 숫자 혼용)

숫자 방식은 코드만으로 에러의 내용을 파악하기 어렵다는 단점이 있지만, 컴퓨팅적 자원을 더 적게 소모할 수 있게 한다. 반대로 문자열 방식은 코드를 통해 에러의 내용을 유추할 수 있지만, 컴퓨팅적 연산에 있어 더 큰 자원을 소모한다.

에러 코드는 HTTP status code 와 구분되는 것을 방지하기 위해 음수로 작성할 수도 있다.

에러 코드에 계층을 부여하여 Code - SubCode 와 같이 운용할 수도 있다.

예)

{ “code”:300 , “subcode”:”001” }

Message

Message는 오류에 대해 간략하게 설명하는 사람이 읽을수 있는 글이다. 이 메시지는 일반적으로 사용자 인터페이스에 표시 될 수 있는 것으로 간주되므로, 만약 국제화를 지원하는 어플리케이션이라면 언어에 따라 다른 메시지를 반환할 수 있어야 한다.

개발자에게 전하는 메시지와 End-User에게 전하는 메시지를 나누기 위하여 message-dev, message-end-user 와 같이 분리하여 사용할 수도 있다.

Detail

Detail 에 포함될 수 있는 요소의 예로는 다음과 같은 것들이 있다.

  • errorDetail: 오류에 대한 자세한 설명
  • tip : 오류를 해결하기 위한 방법
  • reference : 해당 오류의 api 문서 주소
  • address : 오류가 발생한 URI
  • trace_id : 오류가 발생한 부분의 로그 id
  • version : API 버전
  • 기타등등

Detail 은 자유롭게 작성하면 된다.

반면 너무 많은 정보의 제공은 클라이언트-서버의 부하를 늘릴 뿐 더러, 개발자가 응답을 분석하고 이해하기에 어려워 디버깅에 문제가 생길 수 있다. 따라서 간결하게 필요한 정보만 담아 전달하는 것이 중요하다.

  1. Error Code 설정 전략

어떤 오류에 어떤 숫자를 매칭하는지는 중요한 부분으로 생각없이 오류 코드를 할당한다면 나중에 엉망진창이 된 오류 코드 목록을 확인할 수 있다. 이것을 방지하기 위해 오류 코드 범위를 정의할 필요가 있다.

example Error code (숫자방식)

  • 공통적인 오류 (general errors) for 1 to 100
  • 리소스별 오류
    • /users 이하에서 생기는 오류 for 101 to 200
    • /items 이하에서 생기는 오류 for 201 to 300

서로 다른 리소스에서 발생하는 오류의 개수가 다를 수 있으므로 서로 다른 리소스에 서로 다른 범위 크기를 할당할 필요가 있다. 이 범위의 크기를 추정하는 것 역시 API를 설계할 때 고려해야하는 사항이다.

다음과 같이 리소스의 이름을 Error Code에 넣을 수도 있다.

  • 공통 오류
    • general-001 ~ general-999
  • 리소스별 오류
    • resource-001 ~ resource-999

이 경우 리소스 이후에 나오는 숫자가 통일되게 사용한다면 좀 더 구조적인 접근을 할 수 있게 된다.\

예)

resource-001 ~ 015 : resource 생성시 발생하는 오류

resource-016 ~ 030 : resource 수정시 발생하는 오류

예시

IETF 표준

  • REST API의 에러 핸들링의 표준화를 위해서 RFC 7807에서 제시한 에러핸들링 일반 구조.
  • 총 5개의 필드로 구성
    1. type : 세부 에러 코드
    1. title : 에러에 대한 간략한 설명
    1. status(optional) : HTTP response code
    1. detail : 에러에 대한 자세한 설명
    1. instance : 에러 발생 근원지 URI
  • 예시
{
	"type":10001,
	"title":"Incorret username or password",
	"detail":"Authentication failed due to incorrect username or password.",
	"instance":"/api/login"
}

관련 도큐먼트 Link 포함 방식

  • 에러 코드 번호와 해당 에러 코드 번호에 대한 Error dictionary link를 제공하는 방법
    • error 문서의 관리가 요구됨
  • 예시
{
	"status":401,
	"message":"Authenticate",
	"code":"HY10001",
	"more info":"https://www.domain.com/docs/authenticate/hy10001"
}

Trace id 포함 방식

  • 어플리케이션에서 로깅에 사용한 trace id를 반환하여 추후에 쉽게 확인할 수 있게 한다.
  • 직접적인 에러 정보를 노출하지 않기 때문에 보안에 유리하다.
  • 예시
{
    "message": "Missing redirect_uri parameter.",
    "type": "OAuthException",
    "code": 191,
    "fbtrace_id": "AWswcVwbcqfgrSgjG80MtqJ"
}

Error Handling for REST with Spring | Baeldung
We rely on other people's code in our own work. Every day. It might be the language you're writing in, the framework you're building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.
https://www.baeldung.com/exception-handling-for-rest-with-spring
RESTful API Design: How to handle errors?
" We all do". The well known first reaction of all software developers when they first come to this sentence. But, of course, not when we're going to handle errors. We, as software developers, like to only consider happy paths in our scenarios and consequently, tend to forget the fact that Error Happens, even more than those ordinary happy cases.
https://alidg.me/blog/2016/9/24/rest-api-error-handling

Uploaded by N2T