Development/ETC

RESTful API 디자인의 핵심: Best Practices

Danny Seo 2024. 2. 15. 22:56

RESTful API

RESTful 은 현재 웹 데이터 인터페이스 설계에 있어 가장 선호되는 API 디자인으로 널리 채택되고 있습니다.

이러한 API를 디자인하는 기본 원칙들은 직관적으로 이해할 수 있지만, 세부적인 요소들을 정확히 구현하는 데는 주의와 노력이 필요합니다.

이 글은 RESTful의 디자인 세부 정보를 요약해 보고 이해하고 사용하기 쉬운 API를 디자인하는 방법을 소개하겠습니다.

 

1. URL 디자인

1.1 동사 + 목적어

RESTful의 핵심 아이디어는 클라이언트가 보낸 데이터 작업 지시사항이 "동사 + 목적어" 구조에 있어야 한다는 것입니다.

예를 들어, 명령어 GET /articles에서 GET은 동사이고 /articles는 목적어입니다.

동사는 일반적으로 CRUD 작업을 나타내는 다섯 가지 HTTP 메소드와 일치합니다:

  • GET: 읽기
  • POST: 생성
  • PUT: 업데이트
  • PATCH: 업데이트, 일반적으로 부분적인 업데이트입니다.
  • DELETE: 삭제

HTTP 사양에 따르면 동사는 항상 대문자입니다.


1.2 동사의 범위

일부 클라이언트는 GETPOST 메소드만 사용할 수 있습니다. 서버는 다른 세 가지 메서드(PUT, PATCH, DELETE)를 모방하기 위해 POST를 수락(Accept) 해야 합니다.


이 경우 클라이언트의 HTTP 요청은 X-HTTP-Method-Override 속성을 포함하여 서버에게 어떤 동사를 사용할지 알리고 POST 메서드를 대체해야 합니다.


예시:

POST /api/Person/4 HTTP/1.1  
X-HTTP-Method-Override: PUT


1.3 목적어는 명사여야 합니다

목적어는 API의 URL로, HTTP 동사의 대상입니다. 목적어는 동사가 아닌 명사를 사용해야 합니다.
예를 들어, /articles은 올바른 URL이지만, 아래 URL들은 명사가 아니기 때문에 올바르지 않습니다:

  • /getAllCars
  • /createNewCar
  • /deleteAllRedCars


1.4 복수형 URL

URL은 명사이므로 복수형 또는 단수형중 어느 것을 선택해야 할까요?

이에 대한 통일된 규칙은 없지만, 일반적인 실천 방법은 모음을 읽는 것입니다.
예를 들어 GET /articles(모든 기사 읽기)와 같이 복수형으로 사용해야 합니다.

일관성을 위해 복수형 URL을 사용하는 것이 좋습니다.
예를 들어 GET /articles/2가 GET /article/2보다 나은 방법입니다.


1.5 다단계 URL 피하기

일반적인 상황에서 리소스를 여러 수준으로 분류해야 될 때가 있습니다.
예를 들어, 아래와 같이 특정 작가의 카테고리에 도달하기 쉽게 아래와 같이 '다단계' URL을 생성할 수 있습니다.

예시:

GET /authors/12/categories/2

하지만, 이러한 유형의 URL은 확장 가능성을 증진시키지 않으며 의미도 불명확합니다.
종종 의미를 이해하는 데 시간이 걸립니다.


더 나은 접근 방식은 첫 번째 수준을 제외한 모든 수준에 대해 쿼리 문자를 사용하는 것입니다.

GET /authors/12?categories=2

또 다른 예는 발행된 기사를 쿼리 하는 것입니다.

GET /articles/published

쿼리 문자의 쓰기 스타일이 더 나은 것입니다.

GET /articles?published=true

 

2. 상태 코드

2.1 상태 코드는 정확해야 합니다

클라이언트의 모든 요청에 대해 서버는 응답해야 합니다. 응답에는 HTTP 상태 코드와 데이터가 포함됩니다.

HTTP 상태 코드는 다섯 가지 범주로 나뉘는 세 자리 숫자입니다:

  • 1xx: 관련 정보
  • 2xx: 작업 성공
  • 3xx: 리디렉션
  • 4xx: 클라이언트 오류
  • 5xx: 서버 오류

이 다섯 가지 범주에는 총 100가지가 넘는 상태 코드가 포함되어 있으며 대부분의 가능한 상황을 다룹니다.
각 상태 코드에는 표준(또는 합의된) 설명이 있으며 클라이언트는 상태 코드를 확인함으로써 일어난 일을 간단히 파악할 수 있습니다.
따라서, 서버는 가능한 정확한 상태 코드를 반환해야 합니다.


API는 1xx 상태 코드를 필요로 하지 않습니다.
다른 네 가지 범주의 상태 코드의 정확한 의미는 아래에서 설명합니다.

 

2.2 2xx 상태 코드

200 상태 코드는 작업이 성공적으로 완료되었음을 나타냅니다.
몇몇의 메서드에서는 더 정확한 상태 코드를 반환할 수 있습니다.

예시:

  • GET: 200 OK
  • POST: 201 Created
  • PUT: 200 OK
  • PATCH: 200 OK
  • DELETE: 204 No Content

 

2.3 3xx 상태 코드

API는 301 상태 코드(영구 리디렉션)와 302 상태 코드(임시 리디렉션, 또는 307로도 알려짐)를 사용할 필요가 없습니다.
왜냐하면 이러한 상태 코드는 애플리케이션 수준에서 반환될 수 있으며 브라우저가 직접 이동합니다.
API 수준에서는 이러한 두 상황을 고려할 필요가 없습니다.

API에서 사용되는 3xx 상태 코드는 주로 다음과 같습니다:

  • 303 See Other: 다른 URL을 참조함을 의미합니다.
    이는 "임시 리디렉션"을 의미하는 302와 307과 동일한 의미를 가지지만, 302 및 307은 GET 요청에 사용되며 303은 POST, PUT 및 DELETE 요청에 사용됩니다.
    303 상태 코드를 수신한 후 브라우저는 자동으로 리디렉션 하지 않고 사용자에게 다음에 무엇을 할지 결정하도록 합니다.

예시:

HTTP/1.1 303 See Other
Location: /api/orders/12345

 

2.4 4xx 상태 코드

4xx 상태 코드는 클라이언트 오류를 나타내며 주로 다음과 같은 유형이 있습니다:

  • 400 Bad Request: 서버가 클라이언트의 요청을 이해하지 못하고 아무 조치도 취하지 않습니다.
  • 401 Unauthorized: 사용자가 인증 자격 증명을 제공하지 않았거나 인증을 통과하지 못했습니다.
  • 403 Forbidden: 사용자가 식별 확인을 통과했지만 리소스에 액세스 할 권한이 없습니다.
  • 404 Not Found: 요청한 리소스가 존재하지 않거나 사용할 수 없습니다.
  • 405 Method Not Allowed: 사용자가 인증을 통과했지만 사용된 HTTP 메서드는 권한 내에 있지 않습니다.
  • 410 Gone: 요청한 리소스가 이 주소에서 이동되었으며 더 이상 사용할 수 없습니다.
  • 415 Unsupported Media Type: 요청된 반환 형식이 지원되지 않습니다. 예를 들어, API는 JSON 형식만 반환할 수 있지만 클라이언트가 반환되는 XML 형식을 요청했습니다.
  • 422 Unprocessable Entity: 클라이언트가 업로드한 첨부 파일을 처리할 수 없어 요청이 실패했습니다.
  • 429 Too Many Requests: 클라이언트 요청 수가 제한을 초과했습니다.

 

2.5 5xx 상태 코드

5xx 상태 코드는 서버 오류를 나타냅니다. 일반적으로 API는 사용자에게 서버에 대한 자세한 정보를 공개하지 않으므로 두 가지 상태 코드만 충분합니다:

  • 500 Internal Server Error: 클라이언트의 요청은 유효하지만 서버 처리 중 예기치 않은 오류가 발생했습니다.
  • 503 Service Unavailable: 서버가 요청을 처리할 수 없으며 일반적으로 웹 사이트 유지보수 상태에 사용됩니다.

 

3. 서버 응답

3.1 Plain Text를 반환하지 마세요

API가 반환하는 데이터 형식은 일반 텍스트가 아닌 JSON 객체여야 하므로 표준화된 구조화된 데이터를 반환할 수 있습니다.
따라서, 서버 응답의 HTTP 헤더의 Content-Type 속성은 application/json으로 설정해야 합니다.

클라이언트 측에서 요청할 때도 JSON 형식이 허용된다고 명시적으로 서버에 알려야 합니다. 즉, 요청의 HTTP 헤더의 ACCEPT 속성도 application/json으로 설정해야 합니다.

예시:

GET /orders/2 HTTP/1.1 
Accept: application/json

 

3.2 오류 발생 시 200 상태 코드를 반환하지 마세요

잘못된 방법 중 하나는 오류가 발생해도 200 상태 코드를 반환하고 오류 메시지를 데이터 본문에 넣는 것입니다.

예시:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "status": "failure",
  "data": {
    "error": "Expected at least two items in list."
  }
}

위의 코드에서 데이터 본문을 파싱 한 후에야 작업이 실패했음을 알 수 있습니다.

올바른 접근 방식은 상태 코드가 발생한 오류를 반영하고 특정 오류 정보가 데이터 본문 내에 반환되도록 하는 것입니다.

예시:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "Invalid payoad.",
  "detail": {
     "surname": "This field is required."
  }
}

 

3.3 링크 제공

API 사용자는 URL이 어떻게 디자인되었는지를 반드시 알 필요는 없습니다.
하나의 해결책은 응답에 관련 링크를 제공하여 추가 작업을 용이하게 하는 것입니다.
이렇게 하면 사용자는 다른 URL을 발견하기 위해 하나의 URL만 기억하면 됩니다.
해당 방법을 HATEOAS라고 합니다.

 

예시:

{
  ...
  "feeds_url": "https://api.github.com/feeds",
  "followers_url": "https://api.github.com/user/followers",
  "following_url": "https://api.github.com/user/following{/target}",
  "gists_url": "https://api.github.com/gists{/gist_id}",
  "hub_url": "https://api.github.com/hub",
  ...
}

위의 응답에서 선택한 URL에 액세스 하여 다른 URL을 얻을 수 있습니다.
사용자에게는 URL 디자인을 기억할 필요가 없으며 api.github.com에서 단계별로 검색하기만 하면 됩니다.

 

HATEOAS의 형식에 대한 통일된 명세는 없습니다.
위의 예에서 GitHub은 다른 속성과 함께 관련 링크를 함께 두었습니다.
더 좋은 실천 방법은 관련된 링크를 다른 속성에서 분리하는 것입니다.

 

예시:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "status": "In progress",
   "links": {[
    { "rel":"cancel", "method": "delete", "href":"/api/status/12345" } ,
    { "rel":"edit", "method": "put", "href":"/api/status/12345" }
  ]}
}

4. 참고 링크

지금까지 RESTful API 디자인에 대해 알아보았습니다.
조금이나마 도움이 되셨으면 좋겠습니다.
감사합니다 :)