REST API - 개념 다지기

hwanchive·2023년 12월 26일
post-thumbnail

해당 포스팅은 REST를 REST답게 쓰기 위한 제약사항과 규칙을 정리한 내용이지만, 누군가가 공식적으로 발표한 것이 아니고 그저 개발자들이 비공식적으로 의견을 제시한 것이라 정확한 정의는 없다고 합니다.

그러나 REST의 목적은 명확합니다.
간단하고, 일관적이며, 사용성이 뛰어난 API를 만드는 것입니다.


0. REST API? RESTful 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의 구성요소와 설계 규칙들을 살펴보며 어떤 방법으로 자원과 행위를 분리하는지 알아보겠습니다.


1. 구성요소

  • 자원(Resource) : URI
    • 모든 자원에 고유한 ID가 존재하고, 이 자원은 Server에 존재합니다.
    • Client는 URI를 이용해서 자원을 지정하고, 해당 자원 상태(정보)에 대한 조작을 Server에 요청합니다.

  • 행위(Verb) : HTTP Method
    • Server에 요청을 보내기 위한 방식으로, HTTP 프로토콜의 Method를 사용합니다.
    • CRUD 연산 중에서 목적에 맞는 Method를 사용해야 합니다.

  • 표현(Representation of Resource)
    • Client가 자원의 상태(정보)에 대한 조작을 요청하면, Server는 이에 적절한 응답을 보냅니다.
    • Client와 Server가 데이터를 주고받는 형태로, JSON과 XML 등이 있습니다.

URI를 통해 자원을 표시하고, HTTP Method를 이용하여 해당 자원의 행위를 정해주며 그 응답을 받는 일련의 구조를 REST라고 다시 한번 상기시킬 수 있습니다.


2. 설계 규칙

2-1. HTTP METHOD

  • 자원에 대한 행위는 URI에 직접 표현하는 것이 아닌, HTTP Method로 표현합니다.
[POST] /users/delete/94 (X)
[POST] /users/94/delete (X)
[DELETE] /users/94 (O)
  • HTTP Method는 크게 다음과 같이 나눌 수 있습니다.
HTTP Method역할
GETResource(자원) 조회
POSTResource(자원) 생성
PUTResource(자원) 전체 수정
PATCHResource(자원) 일부 수정
DELETEResource(자원) 삭제

단순 CRUD의 개념을 HTTP Method로 조작하도록 정의한 내용이기 때문에 이해하는 데 큰 어려움은 없습니다.
하지만 해당 내용을 처음 접했을 때, 개인적으로 PUTPATCH의 정확한 구분이 힘들었던 기억이 있습니다. 둘 다 Resource(자원)의 수정 행위를 담당하는 HTTP Method이지만 ‘전체’‘일부’를 수정한다는 차이가 있네요. 정확히 어떤 차이가 있는지 아래 예시를 통해 살펴보겠습니다.

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

아래는 수정할 닉네임 정보를 Request Body에 담아 PUTPATCH Method를 사용한 API로 요청했을 때, 각각 기대할 수 있는 요청 결과입니다.

  • ① PUT

  [요청]

[PUT] /users/1
{
  "nickName" : “서땡환변경”
}

  [결과]

필드
id1
age(NULL)
gender(NULL)
name(NULL)
nick_name서땡환변경
mail(NULL)
  • ② PATCH

  [요청]

[PATCH] /users/1
{
  "nickName" : “서땡환변경”
}

  [결과]

필드
id1
age28
genderM
name홍길동
nick_name서땡환변경
mailabc@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
id1
age28
genderM
name홍길동
nick_name서땡환변경
mailabc@naver.com



2-2. URI 기본 설계 규칙

  • 슬래시 구분자(/)는 계층 관계를 나타낼 때 사용합니다.
    • URI에 포함되는 모든 글자는 리소스의 유일한 식별자로 사용되어야 하며, URI가 다르다는 것은 리소스가 다르다는 것을 뜻합니다.

  • URI 마지막 문자로 슬래시(/)를 포함하지 않습니다.
    • 분명한 URI를 만들어 통신을 해야 하기 때문에 혼동을 주지 않도록 URI 경로 마지막에 슬래시(/)를 포함하지 않습니다.
/sports/soccer/ (X)
/sports/soccer (O)
  • 언더바(_) 대신 하이픈(-)을 사용합니다.
    • 불가피하게 긴 URI 경로를 사용하게 된다면 하이픈(-)을 사용해 가독성을 높일 수 있습니다.
/users/94/profile_image (X)
/users/94/profile-image (O)
  • URI 경로는 소문자로 작성해야 합니다.
    • URI 경로에 대문자 사용보다 소문자 사용을 권장합니다.
/USERS/94 (X)
/users/94 (O)
  • CRUD 행위는 포함하지 않는다.
    • 자원에 대한 행위는 URI에 직접 표현하는 것이 아닌, HTTP Method로 표현합니다.
[POST] /books/delete/30 (X)
[DELETE] /books/30 (○)



2-3. Resource 원형 규칙

REST API는 세부적으로 Resource를 다음과 같이 분리하여 사용합니다.

1. 도큐먼트(Document)

  • 데이터베이스 레코드와 유사한 단일 개념으로, row 단위 또는 컬렉션에서 하나의 객체 단위로 단일 정보를 포함하고 있는 데이터라고 생각하면 됩니다.

2. 컬렉션(Collection)

  • 도큐먼트(Document)들의 집합으로, 서버에서 관리하는 리소스입니다.

3. 스토어(Store)

  • 도큐먼트(Document)들의 집합으로, 클라이언트에서 관리하는 리소스입니다.

4. 컨트롤러(Controller)

  • 일종의 Method 기능으로, CRUD라는 표준적인 행위에 매핑되지 않는 특정 기능을 요구할 때 사용됩니다. 일반적으로 컨트롤러 이름은 URI 경로의 제일 마지막 부분에 표시되며, 계층적으로 뒤따르는 자식 Resource는 없습니다.
  • 해당 URI로 접근했을 때 일어날 행위를 생성한다는 관점에서, 컨트롤러 리소스 접근을 위해서는 POST 요청을 보내야 합니다.
[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

3. 특징

1. Server-Client(서버-클라이언트 구조)
자원을 가지고 있는 쪽을 Server, 자원을 요청하는 쪽을 Client로 구분지어 역할을 분리한 구조를 의미합니다.

  • Server는 API를 제공함으로써 요청에 대한 비즈니스 로직 처리와 데이터 저장을 책임지고, Client는 사용자 인증이나 Context(세션, 로그인 정보) 등을 직접 관리하는 등 역할을 구분시켜 서로 간의 의존성을 줄입니다.

2. Stateless(무상태성)
REST API가 작업을 위한 상태정보를 저장 및 관리하지 않는 무상태성을 띄는 것을 의미합니다.

  • API Server는 각각의 요청을 별개로 인식하고 처리합니다. 이것은 곧, 이전 요청이 다음 요청에 연관되지 안된다는 것을 의미하며 작업을 위한 상태정보(쿠키, 세션)를 따로 저장 및 관리하지도 않습니다.
  • 이러한 특징 덕분에 REST API는 서비스의 자유도가 높으며, 서버에서 불필요한 정보를 관리하지 않으므로 구현이 단순하며 부담이 줄어듭니다.

3. Cacheable(캐시 처리 가능)
HTTP가 가진 캐싱 기능 적용이 가능하다는 것을 의미합니다.

  • Rest API는 결국 HTTP라는 기존의 웹 표준을 그대로 사용하기 때문에, 웹의 기존 인프라를 그대로 활용할 수 있습니다.
  • 따라서 REST API에서도 캐싱 기능을 적용할 수 있는데, HTTP 프로토콜 표준에서 사용하는 Last-Modified Tag 또는 E-Tag를 이용하여 캐싱을 구현할 수 있고, 이것은 대량의 요청을 효울척으로 처리할 수 있게 도와줍니다.

4. Uniform Interface(일관된 인터페이스)
Resource(URI)에 대한 요청을 통일되고, 한정적으로 수행하는 아키텍처 스타일을 의미합니다.

  • 이러한 특징 덕분에 REST API는 플랫폼(Android, IOS 등)과 무관하게 HTTP를 사용하는 모든 플랫폼의 요청을 받을 수 있으며, 특정 언어나 기술에 종속받지 않고 사용이 가능합니다.

5. Self-Descriptiveness(자체 표현)
REST API 메시지만 보고도 쉽게 이해할 수 있는 자체 표현 구조를 가지고 있음을 의미합니다.

[GET] /users/94 : id가 94에 해당하는 사용자 정보 조회
[DELETE] /users/94/profile-image : id가 94에 해당하는 사용자의 프로필 이미지 정보 삭제
  • 위와 같은 API를 보고 REST API 원칙을 숙지한 사람이라면, 해당 요청이 어떤 목적을 담고 있는지 쉽게 파악할 수 있습니다.
  • 이처럼 해당 API가 어떤 목적을 가지고 어떤 행위를 통해 어떤 결과를 기대할 수 있을지, 메세지 자체가 의미를 담고 있는 자체 표현 특성을 가짐으로써 개발자 간에 불필요한 의사소통 비용을 줄일 수 있습니다.

마치며

제가 생각하는 좋은 API란, 특별한 Description을 참고할 필요 없이 명세된 스펙만으로 '이 API가 어떤 목적을 가지고 이러한 동작을 수행하겠구나'라는 파악이 누구나 가능할만큼 간단명료한 API입니다. 여태 살펴본 REST API의 가이드라인도 좋은 API를 설계하기 위한 개발자 간의 획일화된 약속 중 하나라고 생각합니다.

다음 포스팅에서는 REST를 지향하는 서비스 API를 구현하며 제가 느꼈던 점을 글로 정리하고자 합니다.


References

0개의 댓글