티스토리 뷰

Infra Structure/.system

200 vs 404

가그린민트 2020. 2. 12. 03:17

ATDD 강의 코드리뷰 중 이런 질문을 받았다. 질문의 요지는,  /stations/{id}  혹은  /stations?name={name}  등과 같은 요청에 해당하는 데이터가 없을 경우, 어떤 응답코드가 적절한가이다.

HTTP Status Code에 대해서는 나름대로 기준을 세웠던지라 별다른 고민은 없었는데, 팀원과 이야기 중에 생각이 다른 부분이 있어 다른 리뷰어들과도 이야기를 나누어보았다. 이 내용을 정리하고 공유하고 또 다른 의견들을 듣는 것도 좋을 거 같아 포스팅해본다.


우선, 200이 적절하다는 주장을 살펴보자.

 

HTTP에서 이야기하는 resource 라는 개념을 서버 데이터와 엮지 않고 온전히 URI 라고 보아야 한다. 즉, 서버의 데이터 없음을 HTTP resource와 엮지 않아야 한다는 것으로, 위의 경우에는 빈값의 200 응답을 해야 한다는 것이다.

 

다만, DELETE 메서드에서 없는 데이터를 삭제하려고 했을 때 이 요청은 400 에러로 처리하여야 하는데, 이는 서버에서 요청이 수용되었으나 클라이언트의 요청이 잘못되었음을 의미하기 때문이다. 이렇게 리소스와 데이터 자원을 분리하는 이유는 명확함을 강조하기 위함이다.

 

그리고 REST를 지향하는 API에서도, 삭제를 실행 했을 때 그 데이터가 삭제 가능한 상황을 단순하게 데이터가 존재한다고 판단하지 않는다. 그 데이터에 얽혀있는 비지니스적인 요소에 따라 요청 클라이언트에게 데이터가 없다고 밝혀야 할 수도 있고, 삭제가 불가능하다고 밝혀야 할 때도 있다.

 

가령, 주문을 실행한다고 했을 때 주문이 실행 불가능한 여러가지 경우가 있다. 재고가 떨어져서, 혹은 판매자가 폐업한 경우 등의 상황에서 404, 400, 500 에러는 적절치 않다. 리소스 수용이 정상적으로 이루어졌고, 요청한 데이터에도 이상이 없고(클라이언트에게 책임이 없고), 서버에서 비정상적이고 식별 불가한 에러를 발생한 것도 아니므로 200 응답을 주는 것이 적절해 보인다. 다만 클라이언트에서 식별에 대한 응답을 하기 위해 요청은 성공했지만 주문에는 실패했다는 의미로 내부적으로 관리되는 상태 코드를 응답 body 에 명시하도록 한다. 이러한 사용은 실제 서버에 이상이 있는지, 클라이언트가 잘못된 요청을 하는지에 대한 구분을 명확히 함으로써 장애시 트래킹 포인트를 좁히는데에도 큰 역할을 한다. 

 

즉, 조회시 없을 때는 200, PUT/DELETE의 경우 상황에 따라 (데이터가 있어야하는데 없는 경우) 4xx, 아니면 200에 응답 body에 별도로 메시지나 정책적으로 만든 상태코드를 담아서 보내는 것이 적절하다는 주장이다.

 

위의 주장은 현재 국내 몇 군데 규모있는 회사 개발자들의 의견이었고, 나 역시 그렇게 생각하고 있었다. 그래서 확인차 몇군데 둘러보니 실제로 그렇게 운영되고 있다.

 

> 쿠팡 404 페이지 : https://www.coupang.com/vp/productsasdsasd
> 쿠팡 200 페이지 : https://www.coupang.com/vp/products/1229153213123123124124 

 

https://luckyyowu.tistory.com/377

 

HTTP 404 Status Code 에 대한 고찰

뭐가 문제였나 필자는 현재 HMR(가정간편식) 커머스를 다루는 모 스타트업에서 백엔드 개발자로 재직 중이다. 말이 백엔드지 최근 변화되고 있는 트렌드에 맞춰 열심히 API 작성 셔틀을 하고 있다. API 개발에 주..

luckyyowu.tistory.com


위의 내용만 보면 200이 타당한 듯 보인다. 하지만 404 의견을 듣고 자료를 찾아보니 이거 역시 만만치가 않다.

우선, 404 NOT FOUND가 적절하다는 입장은, https://tools.ietf.org/html/rfc7231 에 근거를 두었다.

 

## 200 status code

1
2
3
4
5
The 200 (OK) status code indicates that the request has succeeded. 
The payload sent in a 200 response depends on the request method.
 
 
GET a representation of the target resource;

200은 요청이 성공했음을 나타내며 페이로드로 해당 resouce의 representation이 응답된다.

 

1
2
Aside from responses to CONNECT, a 200 response always has a payload, though an origin server MAY generate a payload body of zero length. 
If no payload is desired, an origin server ought to send 204 (No Content) instead.

200은 항상 payload를 가지고 있어야 하고 응답이 없는 경우 204를 보내주어야 한다.

 

## 204 status code

1
The 204 (No Content) status code indicates that the server has successfully fulfilled the request and that there is no additional content to send in the response payload body.
 

204는 요청을 성공적으로 이행하고 응답 페이로드에 추가적으로 보낼 컨텐츠가 없는 경우를 나타낸다.

 

1
For example, if a 204 status code is received in response to a PUT request and the response contains an ETag header field, then the PUT was successful and the ETag field-value contains the entity-tag for the new representation of that target resource.
 

예를 들면, 추가적으로 보낼 컨텐츠가 없는 경우란 PUT 요청을 받고, ETag Header 필드를 포함하여 contents에 보낼 필요가 없을 때를 말한다. 즉, 204 응답은 타겟 리소스에 성공적으로 적용되었음을 나타내며, 암시적으로 useragent가 횡단(접근)할 필요가 없음을 나타낸다.


## 404 status code

1
The 404 (Not Found) status code indicates that the origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
 

404 응답은 서버가 타겟 리소스의 representation을 찾지 못하였거나 존재한다는 것을 드러내려고 하지 않음을 나타낸다.

 

즉, HTTP 요청의 대상이 리소스임을 생각해 본다면

The target of an HTTP request is called a "resource"

서버가 정상적으로 동작함의 여부가 아니라 요청한 리소스가 정상적으로 응답이 오는가에 따라서 응답 코드를 판단해야 한다고 생각하다는 것으로, rfc7231문서에 정의된 Response Status Code 중 200과 204 두 상태 코드 모두 uri가 가리키는 resource가 존재하지 않는 경우의 상태를 나타내기는 어렵다는 주장이며, 404 상태 코드가 보다 명확하다는 입장이었다.

 

추가적으로, ResponseEntity 구현체를 보면 Body에 null을 넘겨줄 경우 notFound()를 리턴하도록 정적 팩토리 메서드를 작성했음을 확인할 수 있다.

1
2
3
4
    public static <T> ResponseEntity<T> of(Optional<T> body) {
        Assert.notNull(body, "Body must not be null");
        return body.map(ResponseEntity::ok).orElse(notFound().build());
    }

 

그리고 HTTP 완벽가이드를 보면, Not Found에 대해 이런 설명이 있다.

"서버가 요청한 URL을 찾을 수 없음을 알려주기 위해 사용한다. 종종, 클라이언트 애플리케이션이 사용자에게 보여주기 위한 엔터티가 포함된다."

 

그리고 트위터의 status code 정의를 보면, "The URI requested is invalid or the resource requested, such as a user, does not exist." 라고 가이드 하고 있다. 

https://developer.twitter.com/en/docs/basics/response-codes

 

Response codes

The standard Twitter API returns HTTP status codes in addition to JSON-based error codes and messages.

developer.twitter.com

아래의 링크도 같이 확인해보자.

 

https://softwareengineering.stackexchange.com/questions/203492/when-to-use-http-status-code-404-in-an-api

 

When to use HTTP status code 404 in an API

I am working on a project and after arguing with people at work for about more than a hour. I decided to know what people on stack-exchange might say. We're writing an API for a system, there is a...

softwareengineering.stackexchange.com

https://medium.com/@laeshiny/get-요청-시-데이터가-없으면-200-or-404-4ab7430084af

 

Get 요청 시 데이터가 없으면 200 or 404 ?

아래와 같은 URL을 Get Method로 호출했다고 가정하겠습니다

medium.com

(위 포스팅의 저자분은 이제 200으로 보내신다는 이야기를 듣기는 했다만..)


사실 우리와 비슷한 논쟁이 stackoverflow에 이미 있었기는 하다.

https://stackoverflow.com/questions/11746894/what-is-the-proper-rest-response-code-for-a-valid-request-but-an-empty-data

 

What is the proper REST response code for a valid request but an empty data?

For example you run a GET request for users/9 but there is no user with id #9. Which is the best response code? 200 OK 202 Accepted 204 No Content 400 Bad Request 404 Not Found

stackoverflow.com

위의 stackoverflow 답글 중에 query string의 경우 빈 배열로 리턴해야 한다는 의견도 있지만, 나는 query string도 url의 한 파트라고 보기에 이는 반대한다. (https://en.wikipedia.org/wiki/Query_string)

 

 


이쯤되니 상태코드도 팀 컨벤션처럼 생각해야 하나, 아니면 이것도 글로벌 기준과 국내 실무와의 괴리 정도로 보면 되는 것인가하는 생각도 든다. 이런 이야기들을 하고 있으니 옆에서 "그냥 규원님께 물어봐. 😈" 하는 의견도 있었다. (사실 이 포스팅을 해야겠다는 생각이 들었던 시점도..🙄)

다른 분들은 어떻게들 생각하시는지, 어떻게들 하고 계신지 궁금하여 포스팅해본다.

이 포스팅에 답글이나, 페북에 댓글이 달리면 추가로 본문을 수정해볼까 합니다.

댓글
링크
최근에 달린 댓글
«   2024/03   »
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
31
Total
Today
Yesterday