해당 포스팅은 REST를 REST답게 쓰기 위한 제약사항과 규칙을 정리한 내용이지만, 누군가가 공식적으로 발표한 것이 아니고 그저 개발자들이 비공식적으로 의견을 제시한 것이라 정확한 정의는 없다고 합니다.
그러나 REST의 목적은 명확합니다.
간단하고, 일관적이며, 사용성이 뛰어난 API를 만드는 것입니다.
일반적으로 REST 기반의 원칙을 적절히 지킨 API 디자인을 REST API 혹은 RESTful API라고 표현합니다. 그렇다면 여기서 말하는 REST(Representational State Transfer)란 무엇일까요? 아래와 같이 정의할 수 있습니다.
- HTTP 통신에서 어떤 자원에 대한 CRUD 요청을 Resource와 Method로 표현하여 특정한 형태로 전달하는 방식
- URI(Uniform Resource Identifier)를 통해 Resource을 명시하고, HTTP Method를 통해 해당 Resource에 대한 CRUD Operation을 적용하는 방식
벌써 Resource(자원)와 Method(행위)라는 단어가 여러 번 등장합니다. 위 내용을 여러 번 곱씹어 보니, REST에서 요청에 대한 자원과 행위를 분리해 표현하는 것이 핵심이라는 것을 얼추 짐작해볼 수 있겠네요.
이제 REST의 구성요소와 설계 규칙들을 살펴보며 어떤 방법으로 자원과 행위를 분리하는지 알아보겠습니다.
URI를 통해 자원을 표시하고, HTTP Method를 이용하여 해당 자원의 행위를 정해주며 그 응답을 받는 일련의 구조를 REST라고 다시 한번 상기시킬 수 있습니다.
HTTP Method로 표현합니다.[POST] /users/delete/94 (X)
[POST] /users/94/delete (X)
[DELETE] /users/94 (O)
| HTTP Method | 역할 |
|---|---|
| GET | Resource(자원) 조회 |
| POST | Resource(자원) 생성 |
| PUT | Resource(자원) 전체 수정 |
| PATCH | Resource(자원) 일부 수정 |
| DELETE | Resource(자원) 삭제 |
단순 CRUD의 개념을 HTTP Method로 조작하도록 정의한 내용이기 때문에 이해하는 데 큰 어려움은 없습니다.
하지만 해당 내용을 처음 접했을 때, 개인적으로 PUT과 PATCH의 정확한 구분이 힘들었던 기억이 있습니다. 둘 다 Resource(자원)의 수정 행위를 담당하는 HTTP Method이지만 ‘전체’와 ‘일부’를 수정한다는 차이가 있네요. 정확히 어떤 차이가 있는지 아래 예시를 통해 살펴보겠습니다.

서버에서 사용자 정보를 user라는 테이블에서 관리하고 있다고 가정해보겠습니다. user 테이블에는 id, age, gender, name, nick_name, mail 총 5개의 필드가 존재하며 위와 같은 데이터를 가지고 있는 상태입니다. 그리고 이제 id 1번에 해당하는 사용자의 닉네임을 기존 ‘서땡환’에서 ‘서땡환변경’으로 수정하는 요청을 보내겠습니다.

아래는 수정할 닉네임 정보를 Request Body에 담아 PUT과 PATCH Method를 사용한 API로 요청했을 때, 각각 기대할 수 있는 요청 결과입니다.
[요청]
[PUT] /users/1
{
"nickName" : “서땡환변경”
}
[결과]
| 필드 | 값 |
|---|---|
| id | 1 |
| age | (NULL) |
| gender | (NULL) |
| name | (NULL) |
| nick_name | 서땡환변경 |
| (NULL) |
[요청]
[PATCH] /users/1
{
"nickName" : “서땡환변경”
}
[결과]
| 필드 | 값 |
|---|---|
| id | 1 |
| age | 28 |
| gender | M |
| name | 홍길동 |
| nick_name | 서땡환변경 |
| abc@naver.com |
차이점이 보이시나요?
PATCH의 경우 본 목적에 맞게 닉네임에 해당되는 정보만 수정되었지만, PUT의 경우 id와 닉네임을 제외한 기존 데이터가 모두 NULL(혹은 초기값) 처리되었다는 것을 확인할 수 있습니다. 이를 통해 PATCH는 자원의 일부를 교체한다는 개념으로, PUT은 자원 전체를 Request Body에 담긴 내용으로 대체한다는 개념으로 이해할 수 있겠습니다.
결과적으로 PUT을 통해 닉네임만 수정되는 동작을 보장받기 위해서는 수정할 사항(nick_name)만 Request Body에 담아 보내는 게 아니라, 다음과 같이 수정하지 않을 사항(기존 age, gender, name, mail)까지 같이 보내야 합니다.
[요청]
[PUT] /users/1
{
"age" : 28,
"gender" : “M”,
"name" : “홍길동”,
"nickName" : “서땡환변경”,
"mail" : “abc@naver.com”
}
[결과]
| Feild | 값 |
|---|---|
| id | 1 |
| age | 28 |
| gender | M |
| name | 홍길동 |
| nick_name | 서땡환변경 |
| abc@naver.com |
/sports/soccer/ (X)
/sports/soccer (O)
/users/94/profile_image (X)
/users/94/profile-image (O)
/USERS/94 (X)
/users/94 (O)
[POST] /books/delete/30 (X)
[DELETE] /books/30 (○)
REST API는 세부적으로 Resource를 다음과 같이 분리하여 사용합니다.
1. 도큐먼트(Document)
2. 컬렉션(Collection)
3. 스토어(Store)
4. 컨트롤러(Controller)
[GET] /books/30 : id가 30에 해당하는 도서 정보 조회
- books : Collection
- 30 : Document
[DELETE] /sports/94/profile-image : id가 94에 해당하는 사용자의 프로필 이미지 정보 삭제
- sports : Collection
- 94 : Document
- profile-image : Document
[POST] /alerts/25/resend : id가 25에 해당하는 경고(Alert) 정보 재전송
- alerts : Collection
- 25 : Document
- resend : Controller
1. Server-Client(서버-클라이언트 구조)
자원을 가지고 있는 쪽을 Server, 자원을 요청하는 쪽을 Client로 구분지어 역할을 분리한 구조를 의미합니다.
2. Stateless(무상태성)
REST API가 작업을 위한 상태정보를 저장 및 관리하지 않는 무상태성을 띄는 것을 의미합니다.
3. Cacheable(캐시 처리 가능)
HTTP가 가진 캐싱 기능 적용이 가능하다는 것을 의미합니다.
4. Uniform Interface(일관된 인터페이스)
Resource(URI)에 대한 요청을 통일되고, 한정적으로 수행하는 아키텍처 스타일을 의미합니다.
5. Self-Descriptiveness(자체 표현)
REST API 메시지만 보고도 쉽게 이해할 수 있는 자체 표현 구조를 가지고 있음을 의미합니다.
[GET] /users/94 : id가 94에 해당하는 사용자 정보 조회
[DELETE] /users/94/profile-image : id가 94에 해당하는 사용자의 프로필 이미지 정보 삭제
제가 생각하는 좋은 API란, 특별한 Description을 참고할 필요 없이 명세된 스펙만으로 '이 API가 어떤 목적을 가지고 이러한 동작을 수행하겠구나'라는 파악이 누구나 가능할만큼 간단명료한 API입니다. 여태 살펴본 REST API의 가이드라인도 좋은 API를 설계하기 위한 개발자 간의 획일화된 약속 중 하나라고 생각합니다.
다음 포스팅에서는 REST를 지향하는 서비스 API를 구현하며 제가 느꼈던 점을 글로 정리하고자 합니다.