200 Exception

나르·2023년 4월 17일
1

개발일기

목록 보기
13/14
post-thumbnail

사내 몇몇 서비스는 Custom Exception 에 대해 Http Status Code 200, body 내 정의된 result code 로 어떤 오류인지를 내려주고 있다. (심지어 최근까지 뭐를 표준으로 할지 싸웠음)

나도 한때는 "어쨋든 서버랑 통신이 됐으니 200을 줘도 되는거 아냐? 무슨 에러인지는 리턴코드 보면 알 수 있으니까" 라는 생각으로 200 에러 리스폰스를 리턴한 적이 있었다.
말도 안되는 논리다. 오죽하면 위같은 짤도 돌아다닌다.

Status 200 으로 에러를 반환하면 안되는 이유에 대한 생각을 해봤다.


클라이언트에서의 처리

정말 슬프게도 아직도 사내의 몇몇 서비스는 ajax와 jQuery 를 사용한다.
내가 프론트 라이브러리에 대한 이해도가 떨어져 비교분석이 힘든 것도 있지만, 여전히 (꽤나 많이) 사용 중인 저 조합을 기준으로 얘기하고자한다.


$.ajax({
  method: "POST",
  url: sendUrl,
  data: body,
  async: true,
  success: function (res) { handleSuccess(res) },
  error: function (err) { handleFailure(err) },
  complete: function () { loader.hide() }
});

서버에 API를 요청하는 부분은 일반적으로 위와 같은 형태이다.

만약 저게 어떠한 폼을 등록하는 요청이고, validation 에서 예외가 발생해도 200 에러 리스폰스를 주게 되면 당연히 응답값은 handleSuccess() 를 타게 될 것이다.

클라이언트 입장에서는 성공했다고 200을 받았는데, 나중에 자세히 확인해보니 아니었다는 상황이 발생하는 것이다.

POST 메서드는 처리 결과를 리스폰스로 주는 것이 이상적인 방법인 것을 알지만, 보통 등록 API 는 호출 후 handleFailure() 를 타는게 아닌 이상 리스폰스를 까서 확인하는 절차는 잘 구현돼있지 않다...


Feign에서의 처리

org.springframework.http.HttpStatus 에는 다음과 같은 메서드가 정의되어있다.

/**
 * Whether this status code is in the HTTP series
 * {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR} or
 * {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}.
 * This is a shortcut for checking the value of {@link #series()}.
 * @since 5.0
 * @see #is4xxClientError()
 * @see #is5xxServerError()
 */
public boolean isError() {
  return (is4xxClientError() || is5xxServerError());
}

비슷하게, FeignExceptionFeignClientException(4xx)FeignServerException(5xx) 를 전부 처리한다.

때문에 feign 등 서버간 통신에서 받는 경우, 200 으로 에러를 줘버리면 요청이 실패한 경우에도 무조건 response body 를 까봐야지 실패/성공을 구분할 수 있다.

또한 feign 을 통해 받아온 값을 캐싱할 때, 200 에러를 내려줘버리면 서버는 에러 응답값을 캐싱하게 된다.
이는 일시적 서버 장애로 인한 에러여도, 캐시 만료 시간까지 해당 요청값에 대한 에러 상태가 유지될 수 있다는 의미이다.


그것이 약속이다

HTTP 상태코드는 HTTP 요청에 대한 서버의 응답 결과를 나타내는 표준 규약이다.
그런데 멋대로 커스텀 응답 코드를 표준으로 사용하면 기존의 표준 상태 코드 체계를 벗어나게 되고, 이는 협업시 어려움을 준다.

사실 이것만으로도 200 에러를 쓰면 안된다고 생각한다.


뭐가 정답인지는 모르겠다

반대로 에러를 그대로 던지는 것이 아니라 재처리를 해야 한다면, 200 에러를 주는 것이 편하다고 느낄 수도 있다.

feign의 경우 ErrorDecoder 을 커스텀해야 특정 에러에 대한 재처리를 구현할 수 있다.
(재처리는 retry 가 아닌 empty 처리 혹은 실패시 처리 동선을 타는 것을 말한다)

혹은 뭐 처리과정에서 문제가 있긴 있었는데 일단 처리됐다... 같은 결과면 200 예외와 400~500 중에 고민이 될수도 있겠다.

이런 것들이 그럼에도 불구하고 언쟁이 있는 이유가 아닐까?

Returning http 200 OK with error within response body

profile
💻 + ☕ = </>

0개의 댓글