201 CREATED 는 "POST" 요청 등 서버에 새로운 리소스를 생성했을 때 사용하는 HTTP 상태 코드이다.
즉, 단순히 "요청이 성공했다." 라는 의미가 아니라,
"서버에서 새로운 리소스가 생성되었다" 라는 의미를 명확히 전달하는 HTTP 상태 코드이다.
해당 응답에는 생성된 리소스의 URI를 알려주기 위해서 Location 헤더를 사용하는 것이 HTTP 표준에서 강하게 권장된다.
관련 표준
[RFC 7231 - 6.3.2. 201 Created]
RFC 7231: Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
The Hypertext Transfer Protocol (HTTP) is a stateless \%application- level protocol for distributed, collaborative, hypertext information systems. This document defines the semantics of HTTP/1.1 messages, as expressed by request methods, request header fie
datatracker.ietf.org

[RFC 9110 - HTTP Semantics > 15.3.2. 201 Created]
RFC 9110: HTTP Semantics
The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document describes the overall architecture of HTTP, establishes common terminology, and defines aspects of
datatracker.ietf.org

왜? Location 헤더가 필요한 이유
- 리소스 URI 제공: 클라이언트가 생성된 리소스를 추후에 조회, 수정 등 추가 작업할 수 있어야 한다.
- 후속 요청 가능: 클라이언트가 생성된 리소스에 다시 접근하기 위해서는 URI가 필요하다.
- REST 원칙 준수: 리소스는 URI로 식별되며, 상태 전이는 URI를 통해 이루어져야 한다.
- REST는 리소스의 상태를 URI로 표현하는게 핵심이기 때문
- 표준 문서 규정: HTTP 표준(RFC)에서 응답의 Location 헤더 필드를 통해 식별한다는 등의 내용이 포함되어 있다
그래도 잘 이해가 안가는데, 왜 굳이? 201 CREATED에 Location 헤더가 필요한걸까?
백엔드 입장에서는 생성된 리소스가 DB에 잘 저장되었으면 URI를 다시 쓸 일이 별로 없다.
하지만 프론트엔드나 API 소비자 입장에서는, 생성된 리소스가 어디에 저장되었는지 알아야
이후에 해당 리소스를 조회하거나 수정하는 등의 후속 작업이 가능하다.
그렇기 때문에 URI가 필요한 것이고,
이는 REST/HTTP 설계 측면으로 봤을 때 리소스를 생성했을 경우
그 URI를 응답으로 알려줘야 HTTP 의미의 완성이라고 할 수 있겠다.
201 CREATED 외 200 OK와 같은 상태 코드에는 Location 헤더가 필요하지 않나?
200 OK는 "요청이 성공했다" 라는 의미의 상태 코드이다.
앞서 말한 "새로운 리소스를 생성했다" 의 의미가 아니기 때문에 Location 헤더가 필수가 아니다.
또한 200 OK는 이미 해당 URI를 이미 알고 요청하는 것이기 때문에 다시 알려줄 필요가 없는 것이다.
그럼 201 상태 코드 외 어떤 상태 코드에서 Location 헤더가 사용되는가?
보통 3xx Redirect 계열에서 Location 헤더가 필수적으로 사용된다.
- 3xx Redirect 계열은 리다이렉트할 주소를 알려줘야하기 때문
정리
따라서 201 Created 응답 시 Location 헤더는 단순 옵션이 아니라 REST API의 신뢰성과 완성도를 나타내는 요소이다.
추가로
HTTP 201 Created 응답에는 생성된 리소스의 URI를 명시하는 Location 헤더가 꼭 포함되어야 하며,
응답 본문에 데이터가 포함되는 경우에는 Content-Type 도 함께 포함되어야 한다.
(Date는 선택적이긴 하지만 일반적으로 포함된다.)
- 요청 (Request)
POST /api/v1/tasks
- 응답 헤더 (Response - Header)
HTTP/1.1 201 Created
Location: /api/v1/tasks/123
Content-Type: application/json
- 응답 바디 (Response - Body)
{
"success": true,
"message": "Task가 생성되었습니다.",
"data": {
"id": 123,
"title": "기능 구현하기",
...
}
}
참고로 헤더의 종류는 아래와 같다.
1) Location: 새로 생성된 리소스의 URI를 제공한다.
- 클라이언트는 이 URI를 사용하여 방금 생성된 리소스에 대한 추가적인 작업을 할 수 있다.
Location: /users/12345
2) Content-Type: 응답 본문의 데이터 유형을 명시한다.
- 예) 생성된 리소스의 내용이 JSON 형태라면 application/json으로 설정된다.
Content-Type: application/json
3) Content-Length: 응답 본문 크기를 바이트 단위로 지정한다.
- 클라이언트가 전체 응답을 받은 후 처리할 수 있도록 도와준다.
Content-Length: 123
4) Cache-Control: 응답 내용을 캐시할 지 여부를 결정한다.
Cache-Control: no-store
5). ETag: 리소스의 고유한 식별자나 버전을 제공하는 헤더
ETag: "v1e6y73h3k"
6). Date: 응답이 생성된 시간 (서버의 시스템 시간) -> 클라이언트가 응답을 처리하는 시간을 알 수 있다.
Date: Tue, 01 Jun 2021 16:00:00 GMT
7) Server: 서버 정보를 제공
Server: Apache/2.4.46 (Unix)
8) X-Request-Id: 고유한 요청 ID 제공 -> 각 요청을 추적, 디버깅할 수 있도록 해준다.
X-Request-ID: 123e4567-e89b-12d3-a456-426614174000
'Dev > Spring' 카테고리의 다른 글
[Spring] GET Method에 RequestBody 요청 적합한가? (0) | 2025.06.16 |
---|---|
[Spring] JWT(JSON Web Token) (0) | 2025.05.29 |
[Spring] Session & Cookie (로그인 인증) (0) | 2025.05.26 |
[Spring] Servlet Filter (0) | 2025.05.22 |
[Spring] 연관 관계 매핑 (0) | 2025.05.19 |