본문 바로가기
RESTful API

RESTful Web 디자인

by 혀닙 2023. 3. 12.

well-formed 웹 디자인이 지원하는 특성

플랫폼 독립성

  • 모든 클라이언트는 내부에서 API가 구현되는 방법에 관계없이 API를 호출할 수 있어야 함
  • 그러려면 프로토콜을 사용해야 하며, 클라이언트 및 웹 서비스가 교환할 데이터 형식에 동의할 수 있는 메커니즘이 있어야 함

서비스진화

  • Web API는 클라이언트 앱과 독립적으로 기능을 진화시키고 추가할 수 있어야 함
  • API가 진화해도 기존 클라이언트 앱은 수정없이 계속 작동할 수 있어야 함
  • 모든 기능은 클라이언트 앱이 해당 기능을 완전히 이용할 수 있도록 검색 가능해야 함

 

REST란?

  • 2000년 Roy Fielding이 제안한 서비스를 디자인하는 아키텍처 접근방식
  • 하이퍼미디어 기반 분산 시스템을 구축하기 위한 아키텍처 스타일
  • REST는 어떤 기본 프로토콜과도 독립적이며 HTTP에 연결될 필요가 없음
  • 그러나 대부분 일반적인 REST API 구현은 HTTP를 앱 프로토콜로 사용함
  • REST는 개방형 표준을 사용하므로 API 또는 클라이언트 앱의 구현이 특정 구현에 바인딩 되지 않음

 

RESTful API의 기본 디자인 원칙

_Resource를 중심_으로 디자인

  • 클라이언트에서 접근할 수 있는 모든 종류의 개체, 데이터 또는 서비스가 Resource에 포함
  • Resource마다 해당 Resource를 고유하게 식별하는 URI인 _식별자(id)_가 있음
    • 예시: 특정 고객 주문의 URI
    • https://localhost:3000/orders/1
    • 클라이언트가 Resource의 _Representation_을 교환하며 서비스와 상호작용
      • 이때 많은 API가 교환 형식으로 JSON을 사용함 
{ "orderId": 1, "orderValue": 99.9, "productId": 1, "quantity": 1 }

 

REST API는 _균일한 인터페이스_를 사용함

  • 따라서 클라이언트와 서비스의 구현을 분리하는 데 도움됨
  • HTTP 를 기반으로 하는 REST API의 경우, 리소스에 표준 HTTP 동사 수행 작업을 사용하는 것이 균일한 인터페이스에 포함
    • GET
    • POST
    • PUT
    • PATCH
    • DELETE

REST API는 상태 비저장 요청 모델을 사용함

  • HTTP 요청은 독립적이어야 하며 임의의 순서로 발생할 수 있음
  • 따라서 요청 사이에 일시적인 상태 정보를 저장할 수 없음(stateless)
  • 정보는 Resource 자체에만 저장되며 각 요청은 자동 작업이어야 함
  • 이러한 제약 조건으로 인해 웹 서비스의 확장성이 우수함
  • 클라이언트과와 특정 서버 사이에 선호도를 유지할 필요가 없음
  • 모든 서버는 모든 클라이언트의 모든 요청을 처리할 수 있음

 

REST API는 Representation에 포함된 하이퍼미디어 링크에 따라 구동됨

  • 예시: 주문관 관련된 JOSN 표현을 보여줌, 주문과 관련된 고객을 가져오거나 업데이트 하는 링크를 포함
{
    "orderID":3,
    "productID":2,
    "quantity":4,
    "orderValue":16.60,
    "links": [
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"GET" },
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"PUT" }
    ]
}

 

 

리소스 중심 API 디자인 구성

  • 웹 API가 표시하는 비지니스 entity에 집중
  • 전자 상거래 시스템
    • 기본엔티티가 고객과 주문
    • 주문 정보가 포함된 HTTP POST 요청을 전송하여 주문 만들기 구현 가능
  • HTTP 응답은 주문이 성공적으로 수행되었는 지 여부를 나타냄
    • 가능하다면 URI는 동사(Resorce에 대한 작업)가 아닌 _명사(Resource)_를 기반으로 해야 함
[https://adventure-works.com/orders](https://adventure-works.com/orders) // Good  
[https://adventure-works.com/create-order](https://adventure-works.com/create-order) // Avoid  
  • Resource는 단일 실제 데이터 항목을 기반으로 할 필요는 없음
  • 주문 Resource는 내부적으로 RDBMS의 여러 테이블로 구현할 수 있지만, 클라이언트에 대해서는 단일 entity로 표시됨
  • REST의 목적
    • 단순히 DB 내부 구조를 반영하는 API가 아닌 entity 및 해당 entity에서 앱이 수행할 수 있는 작업을 모델링 하는 것
  • 클라이언트에게 내부 구현을 노출해서는 안 됨
  • entity가 컬렉션으로 그룹화 될 경우 컬렉션 내 항목들과는 별도의 Resource가 되며 고유한 URI가 있어야 함
  • 컬렉션 URI에 HTTP GET 요청 보내면 컬렉션에 있는 항목들의 목록을 검색
  • 이때 컬렉션의 항목들마다 고유의 URI 존재
  • 항목의 URI에 대한 HTTP GET 요청은 해당 항목의 세부 정보를 반환
[https://adventure-works.com/orders](https://adventure-works.com/orders)  

 

일관적인 URI 명명 규칙 적용

  • 일관된 URI 명명 규칙 적용 시 컬렉션을 참조하는 URI에 대해 복수 명사 사용 가능하며, 웹 API를 직관적으로 유지할 수 있음
  • 컬렉션 및 항목에 대한 URI를 _계층 구조_로 구성하는 것이 좋음
  • /customers는 고객 컬렉션의 경로
  • /customers/5는 ID가 5인 고객의 경로
    • 많은 Web API 프레임 워크는 매개변수가 있는 URI 경로를 기반으로 요청을 라우팅할 수 있음
  • 개발자는 경로 /customers/{id}에 대한 경로를 정의하면 됨

 

서로 다른 Resource 형식과 연결을 표시하는 방법 사이의 관계
 
복잡한 시스템
  • /customers/1/orders/99/products처럼 클라이언트가 여러 관계 수준을 탐색할 수 있는 URI를 제공하고 싶을 수 있지만
  • 이러한 수준의 복잡성은 유지하기 어려울 수 있음
  • 또한 나중에 Resource 사이의 관계가 변하면 유연성이 떨어짐
  • 간단한 URI를 유지하자
  • 앱이 Resource 참조를 지정한 후에는, 참조를 사용하여 해당 Resource와 관련된 항목을 찾을 수 있어야 함
  1. 이전의 요청 쿼리를 /customers/1/ordersURI로 바꿔서 고객 1의 모든 주문을 찾은 후
  2. /orders/99/products로 바꿔서 주문을 제품을 찾음
  • 즉, Resource URI는 컬렉션/항목/컬렉션수준의 URI까지만 제공하자!

 

웹 요청의 서버 부하 상승

  • 모든 웹 요청은 웹 서버의 부하를 높임
  • 요청이 많을수록 부하가 커지기 떄문에 다수의 작은 Resource를 표시하는 '번잡한' Web API를 피해야 함
  • 이러한 API를 사용하면 클라이언트 앱이 요구하는 모든 데이터를 찾기 위해 여러 요청을 보내야 할 수도 있음 > 서버 부하 상승
    • 따라서 데이터를 비정규화하고, 단일 요청을 통해 관련 정보를 검색할 수 있는 _더 큰 Resource로 결합_하는 것이 좋음
  • 다만, 클라이언트에 필요없는 데이터를 가져올 수 있기 때문에 오버헤드의 균형을 조정해야 함
  • 또한 큰 개체를 검색할 경우 요청의 대기 시간이 증가하고 추가 대역폭 비용이 발생할 수 있음
    • 성능 안티 패턴 관련
  • 번잡한 I/O: https://learn.microsoft.com/ko-kr/azure/architecture/antipatterns/chatty-io/
  • 불필요한 가져오기: https://learn.microsoft.com/ko-kr/azure/architecture/antipatterns/extraneous-fetching/

 

Web API와 기본 데이터 원본 사이의 관계

  • Web API와 기본 데이터 원본 사이에 종속성이 발생하지 않도록 해야 함
  • 데이터가 RDBMS에 저장되는 경우 Web API는 각 테이블을 Resource 컬렉션으로 표시하는 것이 아니라
  • Web API를 DB의 추상화라고 생각
  • 필요 시 DB와 Web API 사이에 매핑 계층을 도입
    • 클라이언트 앱과 기본 DB 스키마를 분리
    • DB 스키마 변경 시에도 앱과 스키마 변경 내용을 분리 가능

 

작업을 특정 Resource에 매핑하기 어려운 경우

  • HTTP GET 요청을 통해 기능을 호출하고 결과를 HTTP 응답 메세지로 반환하는 Resource가 아닌 시나리오 처리할 경우가 있음
  • 예시
    • 더하기 빼기 같은 단순한 계산기 작업을 구현하는 Web API
  • 이러한 작업을 Pesudo Resource로 표시하고 쿼리 문자열을 사용하여 필요한 매개변수를 지정하는 URI 제공 가능
    • /add?operand1=99&operand2=1 에 대한 GET 요청은 본문에 값 100이 포함된 응답을 반환
      • Pesudo Resource로 작업을 표시한 이러한 형식의 URI는 제한적으로 사용해야 함

 

 

Reference :

https://learn.microsoft.com/ko-kr/azure/architecture/best-practices/api-design?fbclid=IwAR3TZPok-d2vsIwMyguAGAzfJS8LK5qITS9a2PE5YeaJBtNsUCrtiFDfg74

'RESTful API' 카테고리의 다른 글

HTTP 의미 체계 준수  (0) 2023.03.13
HTTP 메서드 측면에서의 API 작업 정의  (0) 2023.03.13

댓글