Javascript

RESTful API 완벽 이해: 원칙부터 디자인, 베스트 프랙티스까지

드리프트2 2025. 5. 5. 20:29

RESTful API 완벽 이해: 원칙부터 디자인, 베스트 프랙티스까지

안녕하세요!

 

웹 개발이나 서버 개발에 조금이라도 발을 들여놓으셨다면 'RESTful API'라는 용어를 정말 많이 들어보셨을 텐데요.

 

오늘은 바로 이 RESTful API(레스트풀 API)가 무엇인지, 어떤 원칙을 가지고 설계되어야 하는지, 그리고 좋은 RESTful API(레스트풀 API)를 만들기 위한 베스트 프랙티스(Best Practice)는 무엇인지 깊이 있게 알아보는 시간을 갖도록 하겠습니다.

 

미국에서 블로그를 운영하다 한국 독자분들을 만나게 되어 설레는 마음인데요, 쉽고 재미있게 설명해 드릴 테니 편하게 따라오시면 좋겠습니다!

 

RESTful API (레스트풀 API)란 무엇일까요?

 

RESTful API(Representational State Transfer API, 대표 상태 전송 API)는 네트워크(network) 애플리케이션(application) 간의 상호작용을 위해 사용되는 네트워크 인터페이스 디자인 스타일입니다.

 

여기서 중요한 점은 REST(레스트) 자체가 어떤 표준이나 프로토콜(protocol)이라기보다는, 잘 설계된 네트워크(network) 아키텍처(architecture)를 만들기 위한 일련의 원칙과 제약 조건의 집합이라는 것입니다.

 

어떤 웹 서비스가 'RESTful(레스트풀)하다'고 말할 때는, 바로 이 REST(레스트) 원칙들을 잘 따라서 효율적이고, 신뢰성 있으며, 확장 가능한 네트워크(network) 서비스를 제공한다는 의미를 내포하는데요.

 

RESTful(레스트풀) 서비스의 핵심 원칙 중 하나는 '무상태성(Statelessness)' 입니다.

 

각 요청은 그 요청을 처리하는 데 필요한 모든 정보를 담고 있어야 하며, 서버(server)는 클라이언트(client)의 이전 요청 상태에 대한 어떤 정보도 저장하거나 기억해서는 안 됩니다.

 

또한, RESTful(레스트풀) 아키텍처(architecture)는 여러 계층(layer)으로 구성될 수 있으며, 각 계층은 특정 기능을 수행합니다. 이러한 구조는 더 복잡하고 강력한 애플리케이션(application) 개발을 가능하게 합니다.

 

이제 구체적인 디자인 원칙들을 하나씩 살펴볼까요?

1. URI 디자인: 리소스(Resource)를 명확하게 표현하기

RESTful API(레스트풀 API) 디자인에서 URI(Uniform Resource Identifier, 통합 자원 식별자) 또는 흔히 URL(Uniform Resource Locator)이라고 부르는 주소는 일반적으로 리소스(Resource), 즉 정보의 자원(대상 객체)를 나타냅니다.

 

그리고 이 리소스(resource)에 대한 행위(동사)는 GET(겟), POST(포스트), PUT(풋), DELETE(딜리트) 같은 HTTP 메서드(HTTP Method)를 사용하여 표현합니다.

 

즉, 디자인 스타일 자체가 '어떤 동작'보다는 '리소스의 상태와 표현'에 초점을 맞추는 것인데요.

 

동사(Verb) + 객체(Object)

 

RESTful API(레스트풀 API)에서 '동사(Verb)' 역할은 주로 다섯 가지 HTTP 메서드(HTTP Method)가 담당하며, 이는 데이터 처리의 기본 작업인 씨알유디(CRUD - Create, Read, Update, Delete)와 자연스럽게 연결됩니다.

  • GET: 리소스(Resource) 읽기 (Read)
  • POST: 리소스(Resource) 생성 (Create)
  • PUT: 리소스(Resource) 전체 수정 (Update)
  • PATCH: 리소스(Resource) 부분 수정 (Update)
  • DELETE: 리소스(Resource) 삭제 (Delete)

HTTP(Hypertext Transfer Protocol) 명세에 따라, 이 메서드(method) 이름(동사)들은 항상 대문자로 사용해야 합니다.

 

객체(Object)는 반드시 명사여야 합니다

 

API(Application Programming Interface)를 디자인할 때, URL(Uniform Resource Locator)은 HTTP(Hypertext Transfer Protocol) 동사의 '객체' 역할을 하는 리소스(resource)를 나타내야 합니다.

 

RESTful(레스트풀) 디자인 원칙에 따르면, URL(Uniform Resource Locator)은 어떤 '행위'가 아닌 '리소스(resource)'의 집합(컬렉션)이나 단일 개체를 나타내므로, 동사가 아닌 명사를 사용해야 하는데요.

  • 잘못된 예시:
    • /getAllCars (모든 차 가져오기)
    • /createNewCar (새 차 만들기)
    • /deleteAllRedCars (모든 빨간 차 삭제하기)
    위 URL(Uniform Resource Locator)들은 'get', 'create', 'delete'와 같은 동사를 포함하고 있어서, 리소스(resource) 자체보다는 '행동'을 묘사합니다. 이런 디자인은 RESTful(레스트풀)의 의미론적 표준에 부합하지 않습니다.
  • 올바른 접근 방식:
    URL(Uniform Resource Locator)은 행위가 아닌 리소스(resource)를 설명하는 데 집중해야 합니다. 아래는 규약을 준수하는 URL(Uniform Resource Locator) 디자인의 예시입니다.
    • /users: 사용자들의 집합(컬렉션)을 나타냅니다.
    • /users/123: 특정 ID(아이디)(123)를 가진 단일 사용자를 나타냅니다.
    위 예시에서 /users/users/123은 모두 명사이며, 각각 사용자 컬렉션(collection)과 특정 사용자 리소스(resource)를 명확하게 나타냅니다. 이러한 URL(Uniform Resource Locator) 디자인은 API(Application Programming Interface)를 이해하기 쉽게 만들고, RESTful(레스트풀)의 '자원 중심(resource-oriented)' 원칙과 잘 들어맞습니다.
  • 이런 명명 규칙을 따르면 API(Application Programming Interface) 경로(path)가 명확하고 일관되며, 이해하고 유지보수하기 쉬워집니다.

URL(Uniform Resource Locator)에는 복수형 명사 사용하기

 

일관성과 명확성을 위해 URL(Uniform Resource Locator)에 복수형 명사를 사용하는 것이 일반적으로 권장됩니다.

 

왜냐하면 URL(Uniform Resource Locator)이 보통 리소스(resource)의 '집합(컬렉션)'을 나타내는 경우가 많기 때문인데요.

  • 여러 리소스(resource)를 포함하는 컬렉션(collection)을 가리킬 때는 복수 명사를 사용합니다. 예를 들어, 모든 사용자의 컬렉션(collection)을 나타낼 때는 /user 대신 /users를 사용합니다.
  • 설령 단일 리소스(resource)를 가리킬 때라도, URL(Uniform Resource Locator)의 일관성을 유지하기 위해 복수형을 사용하는 것이 좋습니다. 예를 들어, ID(아이디)가 123인 사용자를 나타낼 때도 /users/123과 같이 표현합니다. (/user/123 보다는 /users/123가 더 일관성 있어 보입니다.)
  • 리소스(resource) 간에 계층 관계(예: 사용자가 작성한 게시물)가 있을 때는 URL(Uniform Resource Locator) 구조에 이를 반영해야 합니다. 예를 들어, /users/123/posts는 123번 사용자의 게시물 컬렉션(collection)을 나타낼 수 있습니다.

너무 깊은 URL(Uniform Resource Locator) 중첩은 피하기

 

리소스(resource)를 분류하다 보면 URL(Uniform Resource Locator) 계층이 너무 깊어지는 경우가 발생할 수 있습니다.

 

예를 들어, 특정 작가(author)가 쓴 특정 카테고리(category)의 글(article)을 가져오는 경우를 생각해 봅시다.

GET /authors/12/categories/2

 

이런 URL(Uniform Resource Locator)은 확장하기 어렵고 그 의미가 명확하지 않아 이해하는 데 추가적인 노력이 필요할 수 있습니다.

 

더 나은 접근 방식은 첫 번째 계층 이후의 분류 기준은 쿼리 파라미터(Query Parameter) 를 사용하는 것입니다.

GET /authors/12?categories=2

 

또 다른 예로, 발행된(published) 상태의 글(article)을 조회한다고 할 때, 다음과 같이 URL(Uniform Resource Locator)을 디자인할 수도 있습니다.

GET /articles/published

 

하지만 이 역시 쿼리 파라미터(Query Parameter)를 사용하는 것이 훨씬 명확하고 좋은 방법입니다.

GET /articles?published=true

2. 상태 코드(Status Code): 요청 결과는 명확하게!

클라이언트(client)가 요청을 보낼 때마다, 서버(server)는 반드시 HTTP 상태 코드(HTTP Status Code) 와 데이터를 함께 응답해야 합니다.

 

상태 코드(Status Code)만 보고도 요청이 성공했는지, 실패했다면 왜 실패했는지 대략 알 수 있어야 하는데요.

 

HTTP 상태 코드(HTTP Status Code)는 세 자리 숫자로 구성되며, 크게 다섯 가지 카테고리(category)로 나뉩니다.

  • 1xx: 정보 제공 (Informational)
  • 2xx: 성공 (Success)
  • 3xx: 리다이렉션 (Redirection)
  • 4xx: 클라이언트 오류 (Client Errors)
  • 5xx: 서버 오류 (Server Errors)

이 다섯 카테고리(category) 안에는 100개가 넘는 상태 코드(Status Code)가 존재하며, 거의 모든 가능한 상황을 다룹니다.

 

각 상태 코드(Status Code)는 표준(또는 관례적으로 받아들여지는) 의미를 가지므로, 클라이언트(client)는 상태 코드(Status Code)만 확인해도 어떤 일이 일어났는지 판단할 수 있습니다.

 

따라서 서버(server)는 가능한 가장 정확한 상태 코드(Status Code)를 반환해야 합니다.

 

API(Application Programming Interface)에서는 보통 1xx 상태 코드(Status Code)를 사용할 필요가 없습니다. 나머지 네 가지 카테고리(category)에 대해 좀 더 자세히 알아보겠습니다.

 

 

2xx 상태 코드 (성공)

 

각기 다른 HTTP(Hypertext Transfer Protocol) 요청 메서드(method)에 대해, 요청 결과를 나타내는 적절한 성공 상태 코드(Status Code)를 반환해야 합니다.

 

200 OK가 일반적인 성공 응답이긴 하지만, 메서드(method)에 따라 더 구체적인 코드를 사용하는 것이 좋습니다.

  • GET: 200 OK - 요청이 성공했고, 요청한 리소스(resource)가 응답 본문에 포함됩니다.
  • POST: 201 Created - 새로운 리소스(resource)가 성공적으로 생성되었음을 나타냅니다. 응답 헤더(header)의 Location 필드에 생성된 리소스(resource)의 URI(Uniform Resource Identifier)를 포함하는 것이 일반적입니다.
  • PUT: 200 OK 또는 204 No Content - 리소스(Resource) 전체를 성공적으로 수정했을 때 사용됩니다. 수정된 리소스(resource) 내용을 응답 본문에 포함하면 200 OK, 포함하지 않으면 204 No Content(내용 없음)를 사용합니다.
  • PATCH: 200 OK 또는 204 No Content - 리소스(Resource) 일부를 성공적으로 수정했을 때 사용되며, PUT과 유사합니다. 응답 본문에 내용이 없으면 204를 사용합니다.
  • DELETE: 204 No Content - 리소스(Resource)가 성공적으로 삭제되었음을 나타냅니다. 일반적으로 응답 본문에 내용을 포함하지 않습니다.
  • 202 Accepted - 요청이 접수되었지만 아직 처리가 완료되지 않았음을 나타냅니다. 비동기(asynchronous) 작업 처리에 유용합니다.
  • 206 Partial Content - 응답 본문에 전체 리소스(resource) 중 일부만 포함됨을 나타냅니다. 클라이언트(client)가 Range 헤더(header)를 사용하여 큰 파일의 일부만 요청할 때 주로 사용됩니다.

 

3xx 상태 코드 (리다이렉션)

 

API(Application Programming Interface)는 보통 301 (영구 이동)이나 302 (임시 이동, 307 포함) 상태 코드(Status Code)를 직접적으로 사용하지는 않습니다.

 

이 코드들은 주로 브라우저(browser) 수준의 네비게이션(navigation)에 관련되어 있기 때문인데요. API(Application Programming Interface) 수준에서는 다른 방식으로 처리할 수 있습니다.

 

하지만 API(Application Programming Interface)에서 303 See Other 상태 코드(Status Code)를 사용할 수는 있습니다. 이 코드는 다른 URL(Uniform Resource Locator)을 참조하라는 의미인데요.

 

302307처럼 "임시 리다이렉션(redirection)"을 의미하지만, 303은 특별히 POST, PUT, DELETE 요청에 대한 응답으로 사용됩니다.

 

중요한 차이점은, 브라우저(browser)는 303 응답을 받으면 자동으로 리다이렉션(redirection)을 따르지 않고, 사용자(또는 클라이언트 코드)가 다음 단계를 결정하도록 한다는 것입니다.

  • 예시 응답:
  • HTTP/1.1 303 See Other Location: /api/orders/12345

 

4xx 상태 코드 (클라이언트 오류)

 

4xx 상태 코드(Status Code)는 요청을 보낸 클라이언트(client) 측의 오류를 나타냅니다.

 

자주 사용되는 코드들은 다음과 같은데요.

  • 400 Bad Request - 서버(Server)가 클라이언트(client)의 요청을 이해할 수 없거나 유효하지 않아 처리하지 못했음을 나타냅니다. (예: 요청 파라미터(parameter) 누락, 형식 오류 등)
  • 401 Unauthorized - 사용자가 인증(authentication) 자격 증명(credential)을 제공하지 않았거나, 제공한 자격 증명이 유효하지 않음을 나타냅니다. (즉, 로그인이 필요하거나 실패)
  • 403 Forbidden - 사용자가 인증(authentication)에는 성공했지만, 요청한 리소스(resource)에 접근할 권한(permission)이 없음을 나타냅니다. (로그인은 했지만 권한 부족)
  • 404 Not Found - 요청한 리소스(resource)를 서버(server)에서 찾을 수 없음을 나타냅니다.
  • 405 Method Not Allowed - 요청한 리소스(resource)에 대해 사용된 HTTP 메서드(HTTP Method)가 허용되지 않음을 나타냅니다. (예: 읽기 전용 리소스(resource)에 DELETE 요청)
  • 410 Gone - 요청한 리소스(resource)가 영구적으로 제거되었음을 나타냅니다. 404와 유사하지만, 리소스(resource)가 의도적으로 영구 삭제되었음을 더 명확히 알려줍니다.
  • 415 Unsupported Media Type - 요청 본문의 미디어 타입(media type, 예: Content-Type)이 서버(server)에서 지원하지 않는 형식임을 나타냅니다. (예: 서버는 JSON(제이슨)만 지원하는데 클라이언트가 XML(엑스엠엘)로 요청)
  • 422 Unprocessable Entity - 요청 형식은 올바르지만, 내용에 문법적 또는 의미적 오류가 있어 서버(server)가 처리할 수 없음을 나타냅니다. (예: 필수 필드 누락)
  • 429 Too Many Requests - 클라이언트(Client)가 일정 시간 동안 너무 많은 요청을 보냈음을 나타냅니다. (API(Application Programming Interface) 속도 제한(rate limiting) 초과)

 

5xx 상태 코드 (서버 오류)

 

5xx 상태 코드(Status Code)는 서버(server) 측의 오류를 나타냅니다.

 

일반적으로 API(Application Programming Interface)는 서버(server) 내부의 상세한 오류 정보를 사용자에게 직접 노출하지 않는 것이 좋으므로, 주로 다음 두 가지 코드가 사용됩니다.

  • 500 Internal Server Error - 클라이언트(Client) 요청은 유효했지만, 서버(server)에서 요청을 처리하는 도중 예기치 못한 내부 오류가 발생했음을 나타냅니다. 가장 일반적인 서버(server) 오류 코드입니다.
  • 503 Service Unavailable - 서버(Server)가 일시적으로 요청을 처리할 수 없는 상태임을 나타냅니다. 주로 서버(server) 점검이나 과부하 시 사용됩니다.

 

3. 서버 응답: 명확하고 유용한 정보 전달하기

 

API(Application Programming Interface)가 클라이언트(client)에게 응답을 보낼 때도 몇 가지 중요한 원칙이 있습니다.

 

순수 텍스트(Plain Text)는 반환하지 않기

 

API(Application Programming Interface) 응답은 단순히 "성공"이나 "오류" 같은 순수 텍스트(plain text)가 아니라, 일관된 형식을 유지하기 위해 구조화된 JSON(제이슨) 객체 형태여야 합니다.

 

이를 위해 서버(server) 응답 헤더(header)의 Content-Type (콘텐트-타입)을 application/json으로 설정해야 합니다.

 

클라이언트(client) 또한 자신이 JSON(제이슨) 형식의 응답을 받기를 원한다는 것을 요청 헤더(header)의 Accept (억셉트) 필드를 통해 명시하는 것이 좋습니다.

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

 

오류 발생 시 200 상태 코드를 반환하지 않기

 

잘못된 접근 방식 중 하나는, 오류가 발생했음에도 불구하고 항상 200 OK 상태 코드(Status Code)를 반환하고, 실제 오류 내용은 응답 본문(body) 안에 포함시키는 것입니다.

 

이 방식은 클라이언트(client)가 요청 성공 여부를 판단하기 위해 응답 본문(body)을 항상 파싱(parsing)해서 특정 필드(field) 값을 확인해야만 하므로, 상태 코드(Status Code) 본연의 목적을 무색하게 만듭니다.

  • 잘못된 예시:이 경우, 요청은 실패했지만 서버(server)는 여전히 200 OK를 반환했습니다. 클라이언트(Client)는 응답 본문(body)의 "status": "failure" 필드를 확인해야만 오류 발생을 감지할 수 있습니다. 이런 방식은 RESTful(레스트풀)하지 않으며, 오류 처리를 더 복잡하고 오류 발생 가능성을 높입니다.
  • HTTP/1.1 200 OK Content-Type: application/json { "status": "failure", "data": { "error": "리스트에는 최소 두 개의 아이템이 필요합니다." } }
  • 올바른 예시:
    상태 코드(Status Code) 자체가 요청 결과를 명확히 나타내야 합니다. 오류는 적절한 4xx 또는 5xx 상태 코드(Status Code)로 전달하고, 응답 본문(body)에서는 오류에 대한 더 상세한 정보를 제공해야 합니다.
    HTTP/1.1 400 Bad Request
    Content-Type: application/json
    
    {
      "error": "잘못된 요청 페이로드(payload)입니다.",
      "detail": {
        "surname": "이 필드는 필수 항목입니다." 
      }
    }
    여기서는 400 상태 코드(Status Code)가 잘못된 요청임을 명확히 나타내고, 응답 본문(body)은 클라이언트(client)가 문제를 이해하는 데 도움이 되는 구체적인 오류 정보를 제공합니다.
  • 예를 들어, 요청이 유효하지 않다면 서버(server)는 400 Bad Request를 반환하고, 오류 세부 정보는 JSON(제이슨) 형식으로 제공해야 합니다.

링크(Link) 정보 제공하기 (HATEOAS)

 

RESTful API(레스트풀 API)에서는 응답에 관련 리소스(resource)로 이동할 수 있는 링크(link) 정보를 포함하는 것이 좋은 관행으로 여겨집니다.

 

이는 HATEOAS(Hypermedia as the Engine of Application State) 라는 원칙을 따르는 것인데요.

 

API(Application Programming Interface)의 발견 가능성(discoverability)과 자기 서술성(self-descriptiveness)을 높여줍니다.

 

즉, 클라이언트(client)는 응답에 포함된 링크(link)를 통해 다음에 어떤 요청을 보낼 수 있는지 알 수 있게 됩니다.

 

링크(Link)를 포함하는 일반적인 두 가지 방법은 다음과 같습니다.

  1. HAL(Hypertext Application Language, 하이퍼텍스트 응용 언어) 사용:
    HAL(할)은 리소스(resource) 간의 관계를 표현하는 데 널리 사용되는 하이퍼미디어(hypermedia) 형식입니다. JSON(제이슨) 응답 내에 _links라는 필드를 사용합니다.
  2. { "id": 1, "name": "예시 리소스", "_links": { "self": { "href": "http://api.example.com/resource/1" }, "related": { "href": "http://api.example.com/resource/2" } } }
  3. JSON(제이슨)에 직접 링크 임베딩(Embedding):
    간단하게 links와 같은 필드를 직접 정의하여 사용할 수도 있습니다.
  4. { "id": 1, "name": "예시 리소스", "links": { "self": "http://api.example.com/resource/1", "related": "http://api.example.com/resource/2" } }

콘텐츠 반환 정책 (Content Return Policies)

 

RESTful API(레스트풀 API) 디자인에서 POST 요청은 새로운 리소스(resource)를 생성하는 데 사용됩니다.

 

이때 응답에 새로 생성된 리소스(resource) 정보를 포함해야 하는지는 구현 요구사항에 따라 달라질 수 있으며, 두 가지 일반적인 접근 방식이 있습니다.

  1. 생성된 리소스 반환:
    이 방식은 201 Created 상태 코드(Status Code)와 함께, 응답 본문(body)에 새로 생성된 리소스(resource)의 전체 상세 정보를 포함하여 반환합니다. 또한, Location 헤더(header)에 생성된 리소스(resource)의 URI(Uniform Resource Identifier)를 명시합니다. 클라이언트(client)는 즉시 생성된 리소스(resource)의 정보를 활용할 수 있습니다.
  2. HTTP/1.1 201 Created Location: /resources/123 Content-Type: application/json { "id": 123, "name": "새로운 리소스" }
  3. 콘텐츠 미반환:
    다른 방식으로는, 서버(server)가 201 Created 또는 204 No Content 상태 코드(Status Code)와 Location 헤더(header)만 반환하고, 리소스(resource) 상세 정보는 생략할 수 있습니다. 이는 데이터 전송량을 최소화하고, 클라이언트(client)가 필요하다면 나중에 Location 헤더(header)의 URI(Uniform Resource Identifier)를 통해 리소스(resource) 정보를 직접 조회하도록 하는 방식입니다.
  4. HTTP/1.1 201 Created Location: /resources/123

결론

RESTful API(레스트풀 API)는 HTTP(Hypertext Transfer Protocol) 프로토콜(protocol)의 원칙을 충실히 따르면서, 리소스(resource) 중심의 표현과 상태 없는(stateless) 상호작용을 강조하는 디자인 스타일입니다.

 

표준 HTTP 메서드(HTTP Method) (GET, POST, PUT, DELETE 등)를 명확한 역할에 맞게 사용하고,

 

정확한 상태 코드(Status Code)를 반환하며, 일관된 URI(Uniform Resource Identifier) 디자인 규칙을 따름으로써,

 

RESTful(레스트풀) 아키텍처(architecture)는 네트워크(network) 애플리케이션(application)을 구축하는 데 있어 단순하고,

 

효율적이며, 유지보수하기 쉬운 방법을 제공합니다. 이러한 접근 방식은 웹 서비스(web service)의 확장성, 유연성, 그리고 유지보수성을 크게 향상시킬 수 있습니다.

 

오늘 알아본 내용들이 여러분이 RESTful API(레스트풀 API)를 더 깊이 이해하고 실제로 좋은 API(Application Programming Interface)를 디자인하는 데 도움이 되기를 바랍니다!