Rest API 설계 원칙과 네이밍 컨벤션

» dev

REST?

Representational State Transfer의 약자로 “대표로 상태를 전송하는 것” 을 뜻한다.
데이터 이름으로 상태를 구분하고 상태에 대한 정보를 주고 받는 형식이다.

image

REST는 다음과 같이 구성되어 있다.

  • 자원(Resource): URI
  • 행위(Verb): HTTP Method
  • 표현(Representations)

즉, REST는 URI를 통해 자원(Resource)을 표시하고, HTTP Method를 기반으로 해당 자원의 행위를 규정하여 그 결과를 받는 것을 말한다.

REST에는 여섯가지의 기본 원칙이 있고, 이 가이드를 준수한 인터페이스는 RESTful하다고 표현한다.

RSET API 설계 6원칙

📚 Client-server 구조

클라이언트와 서버는 독립적이야 한다. 클라이언트는 오직 URL 리소스만 알아야 한다. 서버의 인터페이스가 변경되지 않는 한, 이 둘은 독립적으로 개발되거나 대체될 수 있도록 유지해야한다.

📚 Stateless (무상태성)

클라이언트의 모든 요청에는 해당 요청을 이해할 수 있는 모든 정보가 포함되어야 한다.
서버는 HTTP 요청에 대한 어떤것도 저장하지 않는다. 컨텍스트를 유지해야하는 세션, 인증과 인가에 대한 정보 또한 클라이언트에만 보관되며, 각 요청 시 해당 정보를 모두 포함하여 서버에 요청한다.

📚 Cacheable

서버는 Response cache-control 헤더에 해당 요청이 캐싱이 가능한지에 대한 여부를 제공해야 한다.
클라이언트는 Response를 캐싱하여 서버와 클라이언트 간의 상호작용을 줄이고, 성능과 서버 가용성을 늘릴 수 있다.

📍 위 내용에 대해서 실습 예제들을 찾아보았으나 찾지 못하였음…

📚 Uniform interface(일괄된 인터페이스)

서버와 클라이언트 간의 통신을 단순화하고 통일성을 부여하여 서비스의 확장성과 유연성을 향상한다.
일괄된 인터페이스를 얻기 위해 REST에서는 4가지 인터페이스 규칙을 정의하였다.

  • identification of resources (리소스 식별)
    • 모든 리소스는 고유한 식별자에 의해 식별되어야 한다. 일반적으로 URL을 통해 리소스를 식별한다.
  • manipulation of resources through representations (표현을 통한 리소스 조작)
    • 클라이언트는 리소스를 조작하기 위해 리소스의 표현(Representation)을 사용한다.
    • 클라이언트가 리소스의 상태를 변경하거나 검색하기 위해 사용하는 데이터 형식을 의미한다. 예를 들어, JSON, XML 형식의 데이터가 리소스의 표현이 될 수 있다.
  • self-descriptive messages (자기 기술 메시지)
    • 자신을 어떻게 처리해야하는지 정보를 포함해야 한다.
    • 요청과 응답을 이해하는데 필요한 정보가 포함되어야 한다. 예를 들어, JSON 형식의 데이터르 전송할 떄 Content-Type 헤더를 사용하여 데이터 형식을 명시한다.
      POST /blog/posts
      Contnet-Type: application/json
      
      {
          "title": "새로운 블로그의 글"
          "content": "이것은 새로운 글의 내용입니다."
      }
      
  • hypermedia as the engine of application state (HATEOAS)
    • 서버는 클라이언트에게 리소스와 관련된 다양한 작업을 수행할 수 있는 링크를 제공해야 한다. 클라이언트는 이 링크를 통해 다음에 취할 수 있는 조치를 알 수 있다.
      // GET /blog/posts/123
      {
      "id": 123,
      "title": "블로그 글 제목",
      "content": "블로그 글 내용",
      "links": [
          {
          "rel": "self",
          "href": "/blog/posts/123"
          },
          {
          "rel": "edit",
          "href": "/blog/posts/123/edit"
          },
          {
          "rel": "delete",
          "href": "/blog/posts/123/delete"
          }
      ]
      }
      

📚 Layered System (다중 계층)

RESTful 서비스에서 다중 계층 구조를 가질 수 있도록 허용하는데, 각 계층은 서로 독립적으로 동작하며 특정 계층과 직접 통신하는 것 외에는 다른 계층에 대한 정보를 알 수 없다.

예를 들어, 클라이언트는 API 서버와만 통신할 수 있으며, API 서버가 상호작용하는 다른 계층(예: 데이터베이스 서버, 인증 서버)에는 직접적으로 요청할 수 없다.
클라이언트는 API 서버를 통해 원하는 작업을 수행하고, API 서버가 다른 계층과 상호작용하여 요청을 처리한다.

📚 Code on Demand (optional)

서버가 클라이언트에서 실행시킬 수 있는 로직을 전송하여 클라이언트의 기능을 확장시킬 수 있다. 이를 통해 클라이언트가 사전에 구현해야하는 기능의 수를 줄여 간소화시킬 수 있다.

자바에서는 권장하는 방법이라는데 뭔 소리인가 싶다. 스스로 생각하기엔 API를 요청 후 받은 json 데이터를 스크립트를 통해서 뷰로 표현하라는 것 같은데… 예제 코드를 찾기가 싶지 않았다.

RESTful API Naming

📖 URI (Uniform Resource Identifier)

  • Uniform: 리소스 식별하는 통일된 방식
  • Resource: 자원, URI로 식별할 수 있는 모든 것(제한 없음)
  • Identifier: 다른 항목과 구분하는데 필요한 정보

URI는 Resource(or Representation)만 식별한다.

  • ex) 미네랄을 캐라 -> 미네랄이 리소스
  • 회원을 등록하고 수정하는 기능 -> 회원이라는 개념 자체가 리소스

최근에는 Resource 대신 Representation 라는 표현을 사용하기 시작했다.

📚 1. 리소스를 표현하기 위해 명사를 사용해라

REST FUL URI이 가르키는 resource(or representation)는 수행되는 행위가 아니라 객체이다.이 resource (or representation)를 네 가지 범주로 나눌 수 있다. 항상 리소스가 어느 범주에 해당하는지 확인하고, 그 범주에 맞는 네이밍 컨벤션을 일관되게 사용해야 한다.

문서 (Document)

Document는 1개의 개체를 나타내는 것으로 객체 인스턴스, DB의 record와 유사한 개념을 갖는다.
단수를 사용하여 문서 자원을 표현한다.

http://api.example.com/device-management/managed-devices/{device-id}
http://api.example.com/user-management/users/{id}
http://api.example.com/user-management/users/admin

컬렉션 (Collection)

서버가 관리하는 리소스 디렉터리이다. 복수를 사용하고 POST 기반으로 등록한다.

http://api.example.com/user-management/users
http://api.example.com/user-management/users/{id}

스토어 (Store)

클라이언트가 관리하는 자원 저장소이다. 클라이언트는 API를 이용하여 자원을 넣거나 가져올 수 있고 삭제 할 수 있다 PUT 기반으로 등록한다.

http://api.example.com/files
http://api.example.com/files/new_file.txt

컨트롤러 (Controller)

컨트롤러 자원은 인자와 반환 값, 입력 및 출력이 있는 실행 가능한 함수와 같다.
문서, 컬렉션, 스토어로 해결이 어려운 절차적 실행을 수행하기 위한 모델이다.
특정 자원을 가리키는 것이 아니라 실행 인 만큼 예외적으로 동사를 사용한다.

http://api.example.com/cart-management/users/{id}/cart/checkout
http://api.example.com/song-management/users/{id}/playlist/play

📚 2. 일관성이 핵심

계층 관계 표현을 위해 /를 사용하라

http://api.example.com/device-management
http://api.example.com/device-management/managed-devices

마지막 문자로 /를 사용하지 마라

http://api.example.com/device-management/managed-devices/   X
http://api.example.com/device-management/managed-devices    O

가독성을 위해 -(하이픈)을 사용하라

긴 경로 이름에 ‘-’(하이픈)을 사용 하면 가독성을 높일수 있으며, 사람들이 쉽게 인식하고 해석 할 수 있게 도움을 준다.

http://api.example.com/device-management/managed-devices

_(언더스코어)는 사용하지 마라

어플리케이션의 폰트에 따라, 일부 브라우저에서 -(언더스코어)는 불명확하게 표현되거나 보이지 않을 수 있다.

http://api.example.com/inventory_management/managed_entities/{id}/install_script_location
//폰트에 따라 읽기 불편하고, 에러 발생의 여지가 있다.

http://api.example.com/inventory-management/managed-entities/{id}/install-script-location
//더 읽기 편하다.

소문자를 사용하라

Schem과 HOST에만 대소문자 구별이 없고, 이 외에는 대소문자가 구별된다. 아래 3개의 URL는 대소문자 구별이 없다면 모두 동일한 URL지만, 1번과 2번은 동일하고 3번은 같지 않다.

예를 들어, “http://example.com”에서 “http”는 스키마이고, “example.com”은 호스트(서버)를 나타낸다.

http://api.example.org/my-folder/my-doc     //1
HTTP://API.EXAMPLE.ORG/my-folder/my-doc     //2
http://api.example.org/My-Folder/my-doc     //3

파일확장자를 사용하지 마라

파일 확장자는 가독성을 해치고 어떤 이점도 존재 하지 않는다.
만약 자원의 유형을 강조 해야 할 필요가 있다면 Content-Type 헤더를 이용하는 방법을 활용 할 수 있다.

http://api.example.com/device-management/managed-devices.xml    // x
http://api.example.com/device-management/managed-devices        // o

📚 3. CRUD 함수명을 사용하지 마라

URI는 자원을 가리키는 것이다. 어떤 동작을 가리켜서는 안된다.
CRUD는 HTTP의 요청 메소드를 이용하여 표현 할 수 있다.

HTTP GET http://api.example.com/device-management/managed-devices  //모든 디바이스를 요청
HTTP POST http://api.example.com/device-management/managed-devices  //새로운 디바이스를 생성

HTTP GET http://api.example.com/device-management/managed-devices/{id}  //ID를 이용하여 디바이스를 요청
HTTP PUT http://api.example.com/device-management/managed-devices/{id}  //ID를 이용하여 디바이스를 갱신
HTTP DELETE http://api.example.com/device-management/managed-devices/{id}  //ID를 이용하여 디바이스 삭제

📚 4. 필터를 위해 쿼리 파라미터를 사용해라

자원을 정렬, 필터링 하거나 제한된 수량을 요청 해야 할 필요가 있다. 이때, 신규 API를 생성 하려 하지 말고 쿼리 파라미터를 활용 한다.

http://api.example.com/device-management/managed-devices
http://api.example.com/device-management/managed-devices?region=USA
http://api.example.com/device-management/managed-devices?region=USA&brand=XYZ
http://api.example.com/device-management/managed-devices?region=USA&brand=XYZ&sort=installation-date