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 자체에만 저장되며 각 요청은 자동 작업이어야 함
- 이러한 제약 조건으로 인해 웹 서비스의 확장성이 우수함
- 클라이언트과와 특정 서버 사이에 선호도를 유지할 필요가 없음
- 모든 서버는 모든 클라이언트의 모든 요청을 처리할 수 있음
- 단, 다른 요소가 확장성을 제한할 수는 있음
- 예시: 많은 웹 서비스가 백엔드 DB에 데이터를 저장할 경우 스케일 아웃이 어려울 수 있음
- DB 스케일 아웃 관련 - https://learn.microsoft.com/ko-kr/azure/architecture/best-practices/data-partitioning
- 단, 다른 요소가 확장성을 제한할 수는 있음
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/5/orders
는 고객 5에 대한 모든 주문을 나타낼 수 있음- 반대로
/orders/99/customer
과 같은 URI를 통해 주문 > 고객으로 연결을 표시할 수도 있음 - 그러나 이러한 모델을 너무 많이 확장하면 구현이 어려울 수 있기 때문에
- HTTP 응답 메세지의 본문에 연결된 _Resource에 대한 탐색 가능한 링크_를 제공하는 방법이 더 좋음
- 관련 메커니즘: https://learn.microsoft.com/ko-kr/azure/architecture/best-practices/api-design?fbclid=IwAR3TZPok-d2vsIwMyguAGAzfJS8LK5qITS9a2PE5YeaJBtNsUCrtiFDfg74#use-hateoas-to-enable-navigation-to-related-resources
복잡한 시스템
/customers/1/orders/99/products
처럼 클라이언트가 여러 관계 수준을 탐색할 수 있는 URI를 제공하고 싶을 수 있지만- 이러한 수준의 복잡성은 유지하기 어려울 수 있음
- 또한 나중에 Resource 사이의 관계가 변하면 유연성이 떨어짐
- 간단한 URI를 유지하자
- 앱이 Resource 참조를 지정한 후에는, 참조를 사용하여 해당 Resource와 관련된 항목을 찾을 수 있어야 함
- 이전의 요청 쿼리를
/customers/1/orders
URI로 바꿔서 고객 1의 모든 주문을 찾은 후 /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는
제한적으로 사용
해야 함
- 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 |
댓글