REST(Representational State Transfer) API

@Hudi · May 04, 2022 · 6 min read

rest api

API 란?

API는 Application Programming Interface의 약자로, 소프트웨어간의 응답과 요청을 통한 데이터 통신을 위한 방법과 규칙을 의미한다. API는 OS에서도 제공 (e.g. Windows API) 할 수 있고, 프로그래밍 언어 (e.g. Java API) 에서도 제공할 수 있고, 웹 애플리케이션 (e.g. Facebook API) 에서도 제공할 수 있다.

웹 프로그래밍 맥락에서는 주로 웹 애플리케이션에서 제공하는 API를 주로 의미한다. 앞으로 이 포스팅에서 API라고 부르는 것은 웹 프로그래밍 맥락에서의 웹 API를 의미한다.

API는 주로 서버와 클라이언트 관점에서 설명된다. 클라이언트는 요청을 보내고, 서버는 요청에 응답한다. 웹 API는 SOAP API, RPC API, Websocket API, REST API등이 있는데 이 문서에서는 REST API 에 대해서 다룬다.

REST API 란?

REST API는 Representaional State Transfer API의 약자로써, REST 아키텍처의 제약 조건을 준수하는 API 를 의미한다. 이 REST 제약 조건을 잘 준수함을 RESTful 하다고 표현한다.

그렇다면, REST는 무엇일까? REST는 표준 혹은 프로토콜 같은 것이 아니라, 일반적으로 통용되는 규약이다. 따라서 아래의 내용은 정답이 아니라 일반적으로 권고되는 규칙이란 점을 참고하자.

REST는 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용하는 아키텍처이다. 또한 REST API 는 Stateless 하게 동작한다. 각 HTTP 요청은 서로 연관되지 않고 독립적이어야 한다. 따라서 요청과 요청 사이에 일시적으로 저장되는 상태가 없어야한다.

REST API의 구성요소

  • 자원(Resource)

    REST API는 HTTP의 URI를 사용해 자원을 명시한다. 이때 리소스란 클라이언트에서 접근할 수 있는 모든 개체, 데이터, 서비스가 포함된다.

  • 행위(Verb)

    REST API는 HTTP의 Method (POST, GET, PUT, PATCH, DELETE 등) 사용하여 명시된 자원에 대한 CRUD(Create, Read, Update, Delete) 명령을 실행한다.

  • 표현(Representaion)

    REST API에서 리소스는 다양한 형태로 표현될 수 있다. 가장 많이 사용되는 형태는 JSON이고, XML, TEXT, RSS 등의 표현이 존재한다.

리소스 중심 디자인

그리고 REST API는 리소스 중심으로 디자인 된다. 즉, REST API는 비즈니스 엔티티에 집중해야한다. 만드는 서비스가 이커머스라면 비즈니스 엔티티는 고객, 주문, 상품 등이 될 것이다.

리소스는 각각을 고유하게 식별하는 URI가 존재한다. 예를 들어 어떤 블로그 서비스의 특정 포스트 리소스는 아래와 같은 URI로 표현할 수 있다.

https://some-blog-service.com/posts/1

또한 많은 REST API가 요청과 응답의 형식으로 JSON을 채택한다. 예를들어 위 요청에 대한 응답은 아래와 같을 수 있다.

{
  "postId": 1,
  "title": "REST API가 뭘까?",
  "content": "REST API는 Representational Sta..."
}

리소스를 표현할 때 실제 저장된 데이터를 그대로 표현하려하지 않아도 된다. 포스팅이라는 리소스는 실제 관계형 데이터베이스에서는 여러 테이블이 Join 되어 표현되겠지만 (예를 들어 Posting 테이블과 Comment 테이블), 이 내부 구현을 그대로 리소스로 표현하지 않고, 포스팅이라는 단일 엔티티로 표현하자.

{
   "post": {
      "postId": 1,
      "title": "REST API가 뭘까?",
      "content": "REST API는 Representational Sta..."
   },
   "comments": [
      {
         "commentId": 1,
         "content": "감사합니다"
      }
   ]
} // BAD

{
   "postId":1,
   "title":"REST API가 뭘까?",
   "content":"REST API는 Representational Sta...",
   "comments":[
      {
         "commentId":1,
         "content":"감사합니다"
      }
   ]
} // GOOD

HTTP 메서드에 따른 API 작업 정의

RESTful 하게 구현된 REST API는 HTTP 메서드를 아래와 같은 행위에 맞도록 디자인한다.

GET

명시된 URI에 해당하는 리소스를 가져온다.

일반적으로 HTTP 상태 코드 200을 반환하고, 리소스를 찾을 수 없을 경우 404를 반환한다. 요청은 정상적으로 처리되었지만, 보여줄 데이터가 없을 경우 (예를 들어 검색결과가 없을 경우) 204를 반환한다.

POST

명시된 URI에 새 리소스를 생성한다.

리소스가 성공적으로 생성되었을 경우 HTTP 상태 코드 201을 반환하고, 반환할 결과가 딱히 없을 경우 204를 반환한다. 리소스를 생성할 때 클라이언트가 잘못된 요청을 전송했을 경우 (예를 들어 양수여야 하는 필드를 음수로 전달한 경우) 400을 반환한다.

PUT

명시된 URI를 새 리소스로 대체하거나, 없다면 새로운 리소스를 생성한다.

PATCH

명시된 URI에 해당하는 리소스의 일부분을 업데이트한다.

PATCH는 앞서 이야기한 PUT과 비슷해 보이는데, 어떤 차이점이 존재할까? PUT은 리소스의 모든 정보를 업데이트 한다. 즉 새로운 리소스로 대체한다. 하지만, PATCH는 리소스의 일부분만을 업데이트 한다는 차이점이 존재한다.

DELETE

지정된 URI에 해당하는 리소스를 제거한다. 삭제가 성공하면 응답으로 빈 본문을 반환하고, HTTP 상태 코드 204를 반환한다. 존재하지 않는 리소스에 요청한 경우 404를 반환한다.

메소드 예시

리소스 POST GET PUT DELETE
/posts 새 포스팅 생성 모든 포스팅 가져오기 모든 포스팅 업데이트 모든 포스팅 제거
/posts/ (에러) 포스팅 1 가져오기 포스팅 1이 있는 경우 해당 리소스 업데이트 포스팅 1 제거
/posts/1/comments 포스팅 1에 대한 새 코멘트 생성 포스팅 1의 모든 코멘트 가져오기 포스팅 1의 모든 코멘트 업데이트 포스팅 1의 모든 코멘트 제거

위 표와 같이 리소스가 컬렉션(리소스의 집합)인지 단일 개체인지에 따라 행위의 결과가 달라질 수 있다.

REST 관점에서의 HTTP Status Code

REST API는 클라이언트의 요청에 대한 처리 결과를 HTTP Status Code를 이용해 표현할 수 있다. 단, 모든 HTTP 상태 코드를 나열할 수는 없으므로, 자주 사용하는 상태 코드와 그에 대한 설명, 용도를 간단히 정리해본다.

2XX - 성공 응답

클라이언트의 요청이 유효하고, 서버도 그 요청을 성공적으로 처리했을 때 반환하면 적절한 상태코드이다.

  • 200 (OK) : 클라이언트의 요청을 서버가 정상적으로 처리했음.
  • 201 (CREATE) : 클라이언트의 요청을 서버가 정상적으로 처리했으며, 새로운 리소스가 생성되었음.
  • 202 (Accepted) : 클라이언트의 요청을 서버가 정상적으로 수신했지만, 아직 요청을 완료하지 못했음. 서버의 작업이 오래걸릴 경우에 비동기적으로 요청을 처리하기 위해 사용하는 응답 코드이다.
  • 204 (No Content) : 클라이언트의 요청을 서버가 정상적으로 처리했지만, 응답할만한 데이터가 없음. DELETE 등의 메소드를 사용하여 리소스가 제거되었을 경우, PUT 등의 메소드로 리소스를 업데이트 하였으나 변화가 없을 경우 반환하면 적절한 상태코드이다.

성공에 대한 응답을 모두 200 으로 처리해도 큰 문제는 없지만, 클라이언트에게 자세한 정보를 제공하기 위해서는 조금 더 적절한 상태 코드를 반환하는 것이 좋다.

4XX - 실패 응답

클라이언트의 요청이 유효하지 않을 경우 반환하는 상태코드이다.

  • 400 (Bad Request) : 필수 필드 누락, 유효성 검사 실패 등 클라이언트의 요청이 유효하지 않을 경우 반환하는 실패 응답. 400 응답 코드와 함께 에러의 이유를 함께 Body로 명시해주는 것이 좋다.
  • 401 (Unauthorized) : 인증정보(로그인 등)이 필요한 요청인데 불구하고, 인증정보가 존재하지 않은 경우에 반환하는 실패 응답. 이름만 보면 Authorize (인가) 관련 응답인 것 같지만, Authenticate (인증) 에 관한 응답이라고 한다.
  • 403 (Forbidden) : 인증된(Authenticated) 클라이언트가 권한이 없는(Unauthorized) 경우 반환하는 실패 응답. 401 과 그 용도를 혼동하지 않도록 주의하자.
  • 404 (Not Found) : 클라이언트가 요청한 자원이 존재하지 않을 경우 반환하는 실패 응답.
  • 405 (Method Not Allowed) : URI에 해당하는 자원은 존재하지만, 요청한 메소드를 허용하지 않을 경우 반환하는 실패 응답. 예를 들어 GET 메소드로 접근할 수 있는 자원이지만 PUT 으로 수정은 허용하지 않을 경우에 반환하기 적절한 상태코드이다.
  • 409 (Conflict) : 클라이언트의 요청이 서버의 상태와 충돌이 발생한 경우 반환하는 실패 응답. 충돌의 의미가 굉장히 모호하여 명확한 기준을 세우는 것이 중요할 것 같다.
  • 429 (Too Many Requests) : 클라이언트가 단기간에 너무 많은 요청을 보냈을 경우 응답하는 실패 코드.

인증(Authenticate)은 클라이언트 자신이 주장하는 사용자와 클라이언트가 실제 같은 사용자인지 검증하는 로그인 같은 과정을 의미한다. 반면, 인가(Authorized)는 인증이 된 이후 클라이언트가 해당 요청에 대한 권한이 있는지 검사하는 것을 의미한다.

5XX - 서버 에러

클라이언트의 요청은 유효하지만, 그 작업을 수행하는데 있어 서버에서 오류가 발생했을 경우 반환하기 적합한 코드이다. 정말 예상치 못한 경우를 제외하고는 클라이언트에게 5XX 에러와 그 내용을 보여줘서는 안된다.

좋은 URI 설계를 위한 여러가지 규칙

  • URI의 맨 뒤에는 / 가 붙지 않는다.

    즉, /posts 와 /posts/ 는 동일한 리소스를 가리킨다.

  • 계층적 관계를 나타낼 때 / 를 사용한다.

    /posts/1/comments 는 포스팅 컬렉션, 개별 포스팅 개체, 개별 포스팅의 코멘트 컬렉션을 계층적으로 나타낸다.

  • 밑줄(_)을 사용하지 않는다. 대신, 가독성을 위해 하이픈(-) 을 사용한다.

    /best_posts 대신 /best-posts 로 표현하자.

  • 대소문자를 섞어 쓰지 않고, 소문자만을 사용한다.
  • URI에 파일 확장자를 포함하지 않는다.
  • 행위는 URI에 표기하지 않는다. URI는 동사가 아니라 명사를 기반으로 디자인 해야한다. 즉, 리소스에 대한 작업이 아니라 대상 리소스를 중심으로 디자인한다.

    https://some-blog-service.com/posts // 좋음
    https://some-blog-service.com/create-post // 좋지 않음

참고

@Hudi
꾸준히, 의미있는 학습을 기록하기 위한 공간입니다.