다음을 통해 공유


RESTful 웹 API 디자인에 대한 모범 사례

RESTful 웹 API 구현은 REST(Representational State Transfer) 아키텍처 원칙을 사용하여 클라이언트와 서비스 간에 느슨하게 결합된 상태 비 상태 인터페이스를 달성하는 웹 API입니다. RESTful인 웹 API는 표준 HTTP 프로토콜을 지원하여 리소스에 대한 작업을 수행하고 하이퍼미디어 링크 및 HTTP 작업 상태 코드를 포함하는 리소스의 표현을 반환합니다.

RESTful 웹 API는 다음 원칙에 부합해야 합니다.

  • 플랫폼 독립성- 클라이언트가 내부 구현에 관계없이 웹 API를 호출할 수 있음을 의미합니다. 플랫폼 독립성을 달성하기 위해 웹 API는 HTTP를 표준 프로토콜로 사용하고 명확한 설명서를 제공하며 JSON 또는 XML과 같은 친숙한 데이터 교환 형식을 지원합니다.

  • 느슨한 결합은 클라이언트와 웹 서비스가 독립적으로 발전할 수 있음을 의미합니다. 클라이언트는 웹 서비스의 내부 구현을 알 필요가 없으며 웹 서비스는 클라이언트의 내부 구현을 알 필요가 없습니다. RESTful 웹 API에서 느슨한 결합을 달성하려면 표준 프로토콜만 사용하고 클라이언트와 웹 서비스가 교환할 데이터 형식에 동의할 수 있는 메커니즘을 구현합니다.

이 문서에서는 RESTful 웹 API를 디자인하기 위한 모범 사례를 설명합니다. 또한 이해하기 쉽고 유연하며 유지 관리가 가능한 웹 API를 빌드하기 위한 일반적인 디자인 패턴 및 고려 사항을 다룹니다.

RESTful 웹 API 디자인 개념

RESTful 웹 API를 구현하려면 다음 개념을 이해해야 합니다.

  • URI(Uniform Resource Identifier): REST API는 클라이언트가 액세스할 수 있는 모든 종류의 개체, 데이터 또는 서비스인 리소스를 중심으로 설계되었습니다. 각 리소스는 해당 리소스를 고유하게 식별하는 URI로 표시됩니다. 예를 들어 특정 고객 주문에 대한 URI는 다음과 같습니다.

    https://api.contoso.com/orders/1
    
  • 리소스 표현 은 URI로 식별되는 리소스를 XML 또는 JSON과 같은 특정 형식으로 HTTP 프로토콜을 통해 인코딩 및 전송하는 방법을 정의합니다. 특정 리소스를 검색하려는 클라이언트는 API에 대한 요청에서 리소스의 URI를 사용해야 합니다. API는 URI가 나타내는 데이터의 리소스 표현을 반환합니다. 예를 들어 클라이언트는 URI 식별자에 https://api.contoso.com/orders/1 GET 요청을 수행하여 다음 JSON 본문을 받을 수 있습니다.

    {"orderId":1,"orderValue":99.9,"productId":1,"quantity":1}
    
  • 균일한 인터페이스 는 RESTful API가 클라이언트와 서비스 구현 간의 느슨한 결합을 달성하는 방법입니다. HTTP를 기반으로 빌드된 REST API의 경우, 균일한 인터페이스에는 리소스에 대해 GET, POST, PUT, PATCH, DELETE과 같은 작업을 수행하기 위해 표준 HTTP 동사들을 사용하는 것이 포함됩니다.

  • 상태 비지정 요청 모델: RESTful API는 상태 비정상 요청 모델을 사용합니다. 즉, HTTP 요청은 독립적이며 어떤 순서로든 발생할 수 있습니다. 이러한 이유로 요청 간에 일시적인 상태 정보를 유지하는 것은 불가능합니다. 정보가 저장되는 유일한 위치는 리소스 자체에 있으며 각 요청은 원자성 작업이어야 합니다. 상태 비저장 요청 모델은 클라이언트와 특정 서버 간의 선호도를 유지할 필요가 없으므로 높은 확장성을 지원합니다. 그러나 상태 비저장 모델은 웹 서비스 백 엔드 스토리지 확장성 문제로 인해 확장성을 제한할 수도 있습니다. 데이터 저장소를 스케일 아웃하는 전략에 대한 자세한 내용은 데이터 분할을 참조하세요.

  • 하이퍼미디어 링크: REST API는 각 리소스 표현에 포함된 하이퍼미디어 링크에 의해 구동될 수 있습니다. 예를 들어 다음 코드 블록은 주문의 JSON 표현을 보여줍니다. 여기에는 주문과 연결된 고객을 얻거나 업데이트하는 링크가 포함되어 있습니다.

    {
      "orderID":3,
      "productID":2,
      "quantity":4,
      "orderValue":16.60,
      "links": [
        {"rel":"product","href":"https://api.contoso.com/customers/3", "action":"GET" },
        {"rel":"product","href":"https://api.contoso.com/customers/3", "action":"PUT" }
      ]
    }
    

RESTful 웹 API 리소스 URI 정의

RESTful 웹 API는 리소스를 중심으로 구성됩니다. 리소스를 중심으로 API 디자인을 구성하려면 비즈니스 엔터티에 매핑되는 리소스 URI를 정의합니다. 가능하면 기본 리소스 URI는 명사(리소스)를 기반으로 하고, 동사(리소스에 대한 작업)를 기반으로 하지 않도록 합니다.

예를 들어 전자 상거래 시스템에서 기본 비즈니스 엔터티는 고객 및 주문일 수 있습니다. 주문을 만들기 위해 클라이언트는 HTTP POST 요청의 주문 정보를 리소스 URI로 보냅니다. 요청에 대한 HTTP 응답은 주문 생성에 성공했는지 여부를 나타냅니다.

주문 리소스를 만들기 위한 URI는 다음과 같을 수 있습니다.

https://api.contoso.com/orders // Good

URI에서 동사를 사용하여 작업을 나타내지 않습니다. 예를 들어 다음 URI는 권장되지 않습니다.

https://api.contoso.com/create-order // Avoid

엔터티는 종종 고객 또는 주문과 같은 컬렉션으로 그룹화됩니다. 컬렉션은 컬렉션 내의 항목과 별도의 리소스이므로 자체 URI가 있어야 합니다. 예를 들어 다음 URI는 주문 컬렉션을 나타낼 수 있습니다.

https://api.contoso.com/orders

클라이언트가 컬렉션을 검색한 후 각 항목의 URI에 GET 요청을 만들 수 있습니다. 예를 들어 특정 주문에 대한 정보를 수신하기 위해 클라이언트는 URI https://api.contoso.com/orders/1 에 대해 HTTP GET 요청을 수행하여 다음 JSON 본문을 내부 주문 데이터의 리소스 표현으로 받습니다.

{"orderId":1,"orderValue":99.9,"productId":1,"quantity":1}

리소스 URI 명명 규칙

RESTful 웹 API를 디자인할 때 리소스에 올바른 명명 및 관계 규칙을 사용하는 것이 중요합니다.

  • 리소스 이름에 명사 사용 명사로 리소스를 나타냅니다. 예를 들어 /orders 대신 /create-order을 사용합니다. HTTP GET, POST, PUT, PATCH 및 DELETE 메서드는 이미 구두 동작을 의미합니다.

  • 복수 명사로 컬렉션 URI의 이름을 지정합니다. 일반적으로 컬렉션을 참조하는 URI에 복수 명사 사용이 도움이 됩니다. 컬렉션 및 항목에 대한 URI를 계층 구조로 구성하는 것이 좋습니다. 예를 들어 /customers 고객 컬렉션의 경로이며 /customers/5 ID가 5인 고객의 경로입니다. 이 방법은 웹 API를 직관적으로 유지하는 데 도움이 됩니다. 또한 많은 웹 API 프레임워크는 매개 변수가 있는 URI 경로를 기반으로 요청을 라우팅할 수 있으므로 경로 /customers/{id}에 대한 경로를 정의할 수 있습니다.

  • 다양한 유형의 리소스 간의 관계와 이러한 연결을 노출하는 방법을 고려합니다. 예를 들어 /customers/5/orders 고객 5에 대한 모든 주문을 나타낼 수 있습니다. 주문에서 고객으로의 연결을 표시하여 다른 방향으로 관계에 접근할 수도 있습니다. 이 시나리오에서는 URI가 될 수 있습니다 /orders/99/customer. 그러나 이 모델을 너무 멀리 확장하면 구현하는 데 번거로울 수 있습니다. 더 나은 방법은 클라이언트가 관련 리소스에 쉽게 액세스할 수 있도록 HTTP 응답 메시지 본문에 링크를 포함하는 것입니다. HYPERTEXT를 HATEOAS(애플리케이션 상태 엔진)로 사용하여 관련 리소스에 대한 탐색을 사용하도록 설정하면 이 메커니즘에 대해 자세히 설명합니다.

  • 관계를 단순하고 유연하게 유지합니다. 더 복잡한 시스템에서는 클라이언트가 다음과 같은 /customers/1/orders/99/products여러 수준의 관계를 탐색할 수 있는 URI를 제공하려고 할 수 있습니다. 그러나 이러한 수준의 복잡성은 유지 관리가 어려울 수 있으며 나중에 리소스 간의 관계가 변경될 경우 유연하지 않습니다. 대신 URI를 비교적 단순하게 유지합니다. 애플리케이션에 리소스에 대한 참조가 있으면 이 참조를 사용하여 해당 리소스와 관련된 항목을 찾을 수 있어야 합니다. 이전 쿼리를 URI /customers/1/orders 로 바꿔 고객 1에 대한 모든 주문을 찾은 다음 이 순서로 제품을 찾는 데 사용할 /orders/99/products 수 있습니다.

    팁 (조언)

    컬렉션/항목/컬렉션보다 더 복잡한 리소스 URI가 필요하지 않습니다.

  • 적은 수의 리소스를 사용하지 않습니다. 모든 웹 요청은 웹 서버에 부하를 부과합니다. 요청이 많을수록 부하가 커지게 됩니다. 많은 수의 작은 리소스를 노출하는 웹 API를 번잡한 웹 API라고 합니다. 클라이언트 애플리케이션이 필요한 모든 데이터를 찾기 위해 여러 요청을 보내야 하므로 이러한 API를 피하려고 합니다. 대신 데이터를 비정규화하고 관련 정보를 단일 요청을 통해 검색할 수 있는 더 큰 리소스로 결합하는 것이 좋습니다. 그러나 클라이언트가 필요로 하지 않는 데이터를 가져오는 오버헤드와 이 방법의 균형을 유지해야 합니다. 큰 개체 검색은 요청의 대기 시간을 증가시키고 더 많은 대역폭 비용을 초래할 수 있습니다. 이러한 성능 안티패턴에 대한 자세한 내용은 번잡한 I/O불필요한 인출을 참조하세요.

  • 데이터베이스의 내부 구조를 미러링하는 API를 만들지 않습니다. REST의 목적은 비즈니스 엔터티 및 애플리케이션이 해당 엔터티에서 수행할 수 있는 작업을 모델링하는 것입니다. 클라이언트는 내부 구현에 노출되어서는 안 됩니다. 예를 들어 데이터가 관계형 데이터베이스에 저장된 경우 웹 API는 각 테이블을 리소스 컬렉션으로 노출할 필요가 없습니다. 이 방법은 공격 표면을 증가시키고 데이터 누출을 초래할 수 있습니다. 대신 웹 API를 데이터베이스의 추상화로 간주합니다. 필요한 경우 데이터베이스와 웹 API 사이에 매핑 계층을 도입합니다. 이 계층은 클라이언트 애플리케이션이 기본 데이터베이스 스키마에 대한 변경 내용으로부터 격리되도록 합니다.

팁 (조언)

웹 API에서 구현한 모든 작업을 특정 리소스에 매핑하는 것은 불가능할 수 있습니다. 함수를 호출하고 결과를 HTTP 응답 메시지로 반환하는 HTTP 요청을 통해 이러한 리소스가 아닌 시나리오를 처리할 수 있습니다.

예를 들어 추가 및 빼기와 같은 간단한 계산기 작업을 구현하는 웹 API는 이러한 작업을 의사 리소스로 노출하고 쿼리 문자열을 사용하여 필요한 매개 변수를 지정하는 URI를 제공할 수 있습니다. URI /add?operand1=99&operand2=1에 대한 GET 요청은 값이 100인 본문이 포함된 응답 메시지를 반환합니다.

그러나 이러한 형태의 URI는 적게 사용해야 합니다.

RESTful 웹 API 메서드 정의

RESTful 웹 API 메서드는 HTTP 프로토콜에 의해 정의된 요청 메서드 및 미디어 형식과 일치합니다. 이 섹션에서는 RESTful 웹 API에서 사용되는 가장 일반적인 요청 메서드 및 미디어 형식에 대해 설명합니다.

HTTP 요청 메서드

HTTP 프로토콜은 리소스에서 수행하려는 작업을 나타내는 많은 요청 메서드를 정의합니다. RESTful 웹 API에서 사용되는 가장 일반적인 방법은 GET, POST, PUT, PATCHDELETE입니다. 각 메서드는 특정 작업에 해당합니다. RESTful 웹 API를 디자인할 때 프로토콜 정의, 액세스 중인 리소스 및 수행 중인 작업과 일치하는 방식으로 이러한 메서드를 사용합니다.

특정 요청 메서드의 효과는 리소스가 컬렉션인지 개별 항목인지에 따라 달라집니다. 다음 표에는 대부분의 RESTful 구현에서 사용하는 몇 가지 규칙이 포함되어 있습니다.

중요합니다

다음 표에서는 예제 전자 상거래 엔터티를 customer 사용합니다. 웹 API는 모든 요청 메서드를 구현할 필요가 없습니다. 구현하는 메서드는 특정 시나리오에 따라 달라집니다.

자원 올리기 가져오기 놓다 삭제하다
/고객 새 고객 만들기 모든 고객 검색 고객 정보의 대량 업데이트 모든 고객 제거
/customers/1 오류 고객 1에 대한 세부 정보 검색 고객 1의 세부 정보(있는 경우) 업데이트 고객 1 제거
/고객/1/주문 고객 1에 대한 새 주문 만들기 고객 1에 대한 모든 주문 검색 고객 1 주문 대량 업데이트 고객 1에 대한 모든 주문 제거

GET 요청

GET 요청은 지정된 URI에서 리소스의 표현을 검색합니다. 응답 메시지의 본문에는 요청된 리소스의 세부 정보가 포함됩니다.

GET 요청은 다음 HTTP 상태 코드 중 하나를 반환해야 합니다.

HTTP 상태 코드 이유
200(OK) 메서드가 리소스를 성공적으로 반환했습니다.
204(내용 없음) 응답 본문에는 검색 요청이 HTTP 응답에서 일치하는 항목을 반환하지 않는 경우와 같은 콘텐츠가 포함되지 않습니다.
404(찾을 수 없음) 요청된 리소스를 찾을 수 없습니다.

POST 요청

POST 요청은 리소스를 만들어야 합니다. 서버는 새 리소스에 대한 URI를 할당하고 해당 URI를 클라이언트에 반환합니다.

중요합니다

POST 요청의 경우 클라이언트는 자체 URI를 만들려고 시도해서는 안 됩니다. 클라이언트는 컬렉션의 URI에 요청을 제출해야 하며 서버는 새 리소스에 URI를 할당해야 합니다. 클라이언트가 자체 URI를 만들려고 시도하고 특정 URI에 POST 요청을 발급하는 경우 서버는 HTTP 상태 코드 400(BAD REQUEST)을 반환하여 메서드가 지원되지 않음을 나타냅니다.

RESTful 모델에서 POST 요청은 URI가 식별하는 컬렉션에 새 리소스를 추가하는 데 사용됩니다. 그러나 POST 요청을 사용하여 새 리소스를 만들지 않고 기존 리소스에 처리하기 위한 데이터를 제출할 수도 있습니다.

POST 요청은 다음 HTTP 상태 코드 중 하나를 반환해야 합니다.

HTTP 상태 코드 이유
200(OK) 메서드가 일부 처리를 수행했지만 새 리소스를 만들지는 않습니다. 작업의 결과가 응답 본문에 포함될 수 있습니다.
201(만들어짐) 리소스가 성공적으로 만들어졌습니다. 새 리소스의 URI는 응답의 위치 헤더에 포함됩니다. 응답 본문에는 리소스의 표현이 포함됩니다.
204(내용 없음) 응답 본문에 콘텐츠가 없습니다.
400(잘못된 요청) 클라이언트가 요청에 잘못된 데이터를 배치했습니다. 응답 본문에는 오류에 대한 자세한 정보 또는 자세한 정보를 제공하는 URI에 대한 링크가 포함될 수 있습니다.
405(메서드 허용 안 됨) 클라이언트가 POST 요청을 지원하지 않는 URI에 대한 POST 요청을 시도했습니다.

PUT 요청

PUT 요청이 있는 경우 기존 리소스를 업데이트하거나, 존재하지 않는 경우 새 리소스를 만들어야 합니다. PUT 요청을 수행하려면 다음을 수행합니다.

  1. 클라이언트는 리소스에 대한 URI를 지정하고 리소스의 전체 표현을 포함하는 요청 본문을 포함합니다.
  2. 클라이언트가 요청을 합니다.
  3. 이 URI가 있는 리소스가 이미 있는 경우 대체됩니다. 그렇지 않으면 경로에서 지원하는 경우 새 리소스가 만들어집니다.

PUT 메서드는 컬렉션 대신 특정 고객과 같은 개별 항목인 리소스에 적용됩니다. 서버는 업데이트를 지원하지만 PUT을 통해 만들지는 않을 수 있습니다. PUT을 통해 생성을 지원할지 여부는 클라이언트가 존재하기 전에 의미 있고 안정적으로 리소스에 URI를 할당할 수 있는지 여부에 따라 달라집니다. 그렇게 할 수 없는 경우 POST를 사용하여 리소스를 만들고 서버에서 URI를 할당하도록 합니다. 그런 다음 PUT 또는 PATCH를 사용하여 URI를 업데이트합니다.

중요합니다

PUT 요청은 idempotent여야 합니다. 즉, 동일한 요청을 여러 번 제출하면 동일한 리소스가 동일한 값으로 수정됩니다. 클라이언트가 PUT 요청을 다시 보내면 결과는 변경되지 않은 상태로 유지됩니다. 반면에 POST 및 PATCH 요청은 멱등성(idempotent)이 보장되지 않습니다.

PUT 요청은 다음 HTTP 상태 코드 중 하나를 반환해야 합니다.

HTTP 상태 코드 이유
200(OK) 리소스가 성공적으로 업데이트되었습니다.
201(만들어짐) 리소스가 성공적으로 만들어졌습니다. 응답 본문에는 리소스의 표현이 포함될 수 있습니다.
204(내용 없음) 리소스가 성공적으로 업데이트되었지만 응답 본문에 콘텐츠가 포함되지 않습니다.
409(충돌) 리소스의 현재 상태와 충돌하여 요청을 완료할 수 없습니다.

팁 (조언)

컬렉션의 여러 리소스에 업데이트를 일괄 처리할 수 있는 대량 HTTP PUT 작업을 구현하는 것이 좋습니다. PUT 요청은 컬렉션의 URI를 지정해야 합니다. 요청 본문은 수정할 리소스의 세부 정보를 지정해야 합니다. 이 방법은 수다를 줄이고 성능을 향상시키는 데 도움이 될 수 있습니다.

PATCH 요청

PATCH 요청은 기존 리소스에 대한 부분 업데이트를 수행합니다. 클라이언트는 리소스에 대한 URI를 지정합니다. 요청 본문은 리소스에 적용할 변경 내용 집합을 지정합니다. 이 메서드는 클라이언트가 리소스의 전체 표현이 아닌 변경 내용만 보내기 때문에 PUT 요청을 사용하는 것보다 더 효율적일 수 있습니다. 서버에서 이 작업을 지원하는 경우 PATCH는 빈 리소스 또는 null 리소스에 대한 업데이트 집합을 지정하여 새 리소스를 만들 수도 있습니다.

PATCH 요청을 통해 클라이언트는 패치 문서 형식으로 기존 리소스에 업데이트 집합을 보냅니다. 서버는 패치 문서를 처리하여 업데이트를 수행합니다. 패치 문서는 전체 리소스를 설명하는 대신 적용할 변경 내용 집합만 지정합니다. PATCH 메서드 RFC 5789의 사양은 패치 문서에 대한 특정 형식을 정의하지 않습니다. 요청의 미디어 형식에서 형식을 유추해야 합니다.

JSON은 웹 API에 대한 가장 일반적인 데이터 형식 중 하나입니다. 두 가지 주요 JSON 기반 패치 형식은 JSON 패치 및 JSON 병합 패치입니다.

JSON 병합 패치는 JSON 패치보다 간단합니다. 패치 문서는 원래 JSON 리소스와 구조가 동일하지만 변경하거나 추가해야 하는 필드의 하위 집합만 포함합니다. 또한 패치 문서의 필드 값을 지정하여 null 필드를 삭제할 수 있습니다. 이 사양은 원래 리소스에 명시적 null 값이 있을 수 있는 경우 병합 패치가 적합하지 않음을 의미합니다.

예를 들어 원래 리소스에 다음과 같은 JSON 표현이 있다고 가정합니다.

{
    "name":"gizmo",
    "category":"widgets",
    "color":"blue",
    "price":10
}

이 리소스에 대한 가능한 JSON 병합 패치는 다음과 같습니다.

{
    "price":12,
    "color":null,
    "size":"small"
}

이 병합 패치는 서버에 업데이트 price, 삭제 color및 추가 size를 지시합니다. namecategory 값은 수정되지 않았습니다. JSON 병합 패치에 대한 자세한 내용은 RFC 7396을 참조하세요. JSON 병합 패치의 미디어 유형은 .입니다 application/merge-patch+json.

원래 리소스에 명시적 null 값이 포함될 수 있는 경우, 패치 문서의 특별한 의미인 null 때문에 병합 패치는 적합하지 않습니다. 또한 패치 문서는 서버에서 업데이트를 적용해야 하는 순서를 지정하지 않습니다. 이 순서가 중요한지 여부는 데이터와 도메인에 따라 달라집니다. RFC 6902에 정의된 JSON 패치는 값의 유효성을 검사하기 위해 추가, 제거, 바꾸기, 복사 및 테스트를 포함하여 적용할 작업 시퀀스로 변경 내용을 지정하기 때문에 더 유연합니다. JSON 패치의 미디어 유형은 .입니다 application/json-patch+json.

PATCH 요청은 다음 HTTP 상태 코드 중 하나를 반환해야 합니다.

HTTP 상태 코드 이유
200(OK) 리소스가 성공적으로 업데이트되었습니다.
400(잘못된 요청) 잘못된 형식의 패치 문서입니다.
409(충돌) 패치 문서는 유효하지만 변경 내용은 현재 상태의 리소스에 적용할 수 없습니다.
415(지원되지 않는 미디어 형식) 패치 문서 형식은 지원되지 않습니다.

DELETE 요청

DELETE 요청은 지정된 URI에서 리소스를 제거합니다. DELETE 요청은 다음 HTTP 상태 코드 중 하나를 반환해야 합니다.

HTTP 상태 코드 이유
204(콘텐츠 없음) 리소스가 성공적으로 삭제되었습니다. 프로세스가 성공적으로 처리되었으며 응답 본문에 추가 정보가 포함되지 않습니다.
404(찾을 수 없음) 리소스가 없습니다.

리소스 MIME 형식

리소스 표현은 URI로 식별되는 리소스를 XML 또는 JSON과 같은 특정 형식으로 HTTP 프로토콜을 통해 인코딩 및 전송하는 방법입니다. 특정 리소스를 검색하려는 클라이언트는 API에 대한 요청에서 URI를 사용해야 합니다. API는 URI로 표시된 데이터의 리소스 표현을 반환하여 응답합니다.

HTTP 프로토콜에서 리소스 표현 형식은 MIME 형식이라고도 하는 미디어 형식을 사용하여 지정됩니다. 비이진 데이터의 경우 대부분의 웹 API는 JSON(미디어 형식 = application/json) 및 XML(미디어 형식 = application/xml)을 지원합니다.

요청 또는 응답의 Content-Type 헤더는 리소스 표현 형식을 지정합니다. 다음 예제에서는 JSON 데이터를 포함하는 POST 요청을 보여 줍니다.

POST https://api.contoso.com/orders
Content-Type: application/json; charset=utf-8
Content-Length: 57

{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

서버에서 미디어 형식을 지원하지 않는 경우 HTTP 상태 코드 415(지원되지 않는 미디어 형식)를 반환해야 합니다.

클라이언트 요청에는 클라이언트가 응답 메시지의 서버에서 수락하는 미디어 형식 목록이 포함된 Accept 헤더가 포함될 수 있습니다. 다음은 그 예입니다.

GET https://api.contoso.com/orders/2
Accept: application/json, application/xml

서버가 나열된 미디어 형식 중 하나와 일치할 수 없는 경우 HTTP 상태 코드 406(허용되지 않음)을 반환해야 합니다.

비동기 메서드 구현

경우에 따라 POST, PUT, PATCH 또는 DELETE 메서드를 완료하는 데 시간이 걸리는 처리가 필요할 수 있습니다. 클라이언트에 응답을 보내기 전에 완료될 때까지 기다리는 경우 허용할 수 없는 대기 시간이 발생할 수 있습니다. 이 시나리오에서는 메서드를 비동기식으로 만드는 것이 좋습니다. 비동기 메서드는 HTTP 상태 코드 202(수락됨)를 반환하여 요청이 처리에 허용되었지만 불완전함을 나타내야 합니다.

클라이언트가 상태 엔드포인트를 폴링하여 상태를 모니터링할 수 있도록 비동기 요청의 상태를 반환하는 엔드포인트를 노출합니다. 202 응답의 위치 헤더에 상태 엔드포인트의 URI를 포함합니다. 다음은 그 예입니다.

HTTP/1.1 202 Accepted
Location: /api/status/12345

클라이언트가 이 엔드포인트에 GET 요청을 보내는 경우 응답에는 요청의 현재 상태가 포함되어야 합니다. 필요에 따라 예상 완료 시간 또는 작업을 취소하는 링크를 포함할 수 있습니다.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "status":"In progress",
    "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }
}

비동기 작업이 새 리소스를 만드는 경우 상태 엔드포인트는 작업이 완료된 후 상태 코드 303(기타 참조)을 반환해야 합니다. 303 응답에 새 리소스의 URI를 제공하는 위치 헤더를 포함합니다.

HTTP/1.1 303 See Other
Location: /api/orders/12345

자세한 내용은 장기 실행 요청에 비동기 지원 제공비동기 Request-Reply 패턴을 참조하세요.

데이터 페이지 매김 및 필터링 구현

데이터 검색을 최적화하고 페이로드 크기를 줄이려면 API 디자인에서 데이터 페이지 매김 및 쿼리 기반 필터링을 구현합니다. 이러한 기술을 통해 클라이언트는 필요한 데이터의 하위 집합만 요청할 수 있으므로 성능을 향상시키고 대역폭 사용량을 줄일 수 있습니다.

  • 페이징은 큰 데이터셋을 더 작고 관리하기 쉬운 청크로 나눕니다. 반환할 항목 수를 지정하고 limit 시작점을 지정하는 것과 같은 offset 쿼리 매개 변수를 사용합니다. 에 대한 limitoffset의미 있는 기본값(예: limit=25offset=0.)도 제공해야 합니다. 다음은 그 예입니다.

    GET /orders?limit=25&offset=50
    
    • limit: 반환할 최대 항목 수를 지정합니다.

      팁 (조언)

      서비스 거부 공격을 방지하려면 반환되는 항목 수에 상한을 적용하는 것이 좋습니다. 예를 들어, 귀하의 서비스가 max-limit=25를 설정하고 클라이언트가 limit=1000를 요청하는 경우, 서비스는 API 설명서에 따라 25개 항목을 반환하거나 HTTP BAD-REQUEST 오류를 반환할 수 있습니다.

    • offset: 데이터의 시작 인덱스를 지정합니다.

  • 필터링을 사용하면 클라이언트가 조건을 적용하여 데이터 세트를 구체화할 수 있습니다. API를 사용하면 클라이언트가 URI의 쿼리 문자열에 필터를 전달할 수 있습니다.

    GET /orders?minCost=100&status=shipped
    
    • minCost: 최소 비용이 100인 주문을 필터링합니다.
    • status: 특정 상태가 있는 주문을 필터링합니다.

다음 모범 사례를 고려합니다.

  • 정렬을 사용하면 클라이언트가 다음과 같은 매개 변수를 사용하여 데이터를 정렬할 sort 수 있습니다 sort=price.

    중요합니다

    쿼리 문자열 매개 변수는 많은 캐시 구현이 캐시된 데이터의 키로 사용하는 리소스 식별자의 일부를 구성하기 때문에 정렬 방법은 캐싱에 부정적인 영향을 줄 수 있습니다.

  • 클라이언트 정의 프로젝션에 대한 필드 선택을 통해 클라이언트는 다음과 같은 fields매개 변수를 사용하여 fields=id,name 필요한 필드만 지정할 수 있습니다. 예를 들어 /orders?fields=ProductID,Quantity와 같이 쉼표로 구분된 필드 목록을 허용하는 쿼리 문자열 매개 변수를 사용할 수 있습니다.

API는 요청된 필드의 유효성을 검사하여 클라이언트가 액세스하도록 허용하고 API를 통해 일반적으로 사용할 수 없는 필드를 노출하지 않도록 해야 합니다.

부분 응답 지원

일부 리소스에는 파일 또는 이미지와 같은 큰 이진 필드가 포함됩니다. 불안정하고 일시적인 연결로 인한 문제를 극복하고 응답 시간을 개선하려면 큰 이진 리소스의 부분 검색을 지원하는 것이 좋습니다.

부분 응답을 지원하려면 웹 API가 대규모 리소스에 대한 GET 요청에 대한 Accept-Ranges 헤더를 지원해야 합니다. 이 헤더는 GET 작업이 부분 요청을 지원한다는 것을 나타냅니다. 클라이언트 애플리케이션은 바이트 범위로 지정된 리소스의 하위 집합을 반환하는 GET 요청을 제출할 수 있습니다.

또한 이러한 리소스에 대한 HTTP HEAD 요청을 구현하는 것이 좋습니다. HEAD 요청은 리소스를 설명하는 HTTP 헤더만 빈 메시지 본문과 함께 반환한다는 점을 제외하고 GET 요청과 유사합니다. 클라이언트 애플리케이션은 부분 GET 요청을 사용하여 리소스를 가져올지 여부를 결정하기 위해 HEAD 요청을 실행할 수 있습니다. 다음은 그 예입니다.

HEAD https://api.contoso.com/products/10?fields=productImage

다음은 응답 메시지의 예입니다.

HTTP/1.1 200 OK

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 4580

Content-Length 헤더는 리소스의 총 크기를 제공하며, Accept-Ranges 헤더는 해당 GET 작업이 부분 결과를 지원한다는 것을 나타냅니다. 클라이언트 애플리케이션은 이 정보를 사용하여 더 작은 청크로 이미지를 검색할 수 있습니다. 첫 번째 요청은 Range 헤더를 사용하여 처음 2,500바이트를 가져옵니다.

GET https://api.contoso.com/products/10?fields=productImage
Range: bytes=0-2499

응답 메시지는 HTTP 상태 코드 206을 반환하여 이 응답이 부분적임을 나타냅니다. Content-Length 헤더는 리소스 크기가 아니라 메시지 본문에 반환된 실제 바이트 수를 지정합니다. Content-Range 헤더는 반환되는 리소스의 일부(4580에서 0-2499바이트)를 나타냅니다.

HTTP/1.1 206 Partial Content

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580

[...]

클라이언트 애플리케이션의 후속 요청은 나머지 리소스를 검색할 수 있습니다.

HATEOAS 구현

REST를 사용하는 주된 이유 중 하나는 URI 스키마에 대한 사전 지식 없이 전체 리소스 집합을 탐색할 수 있기 때문입니다. 각 HTTP GET 요청은 응답에 포함된 하이퍼링크를 통해 요청된 개체와 직접 관련된 리소스를 찾는 데 필요한 정보를 반환해야 합니다. 요청은 이러한 각 리소스에서 사용할 수 있는 작업을 설명하는 정보도 제공해야 합니다. 이 원칙을 HATEOAS 또는 애플리케이션 상태의 엔진으로 하이퍼텍스트라고 합니다. 시스템은 사실상 유한 상태 컴퓨터이며 각 요청에 대한 응답에는 한 상태에서 다른 상태로 이동하는 데 필요한 정보가 포함됩니다. 다른 정보는 필요하지 않습니다.

비고

HATEOAS 원칙을 모델링하는 방법을 정의하는 범용 표준은 없습니다. 이 섹션의 예제에서는 하나의 가능한 전용 솔루션을 보여 줍니다.

예를 들어 주문과 고객 간의 관계를 처리하기 위해 주문 표현에는 주문 고객에게 사용 가능한 작업을 식별하는 링크가 포함될 수 있습니다. 다음 코드 블록은 가능한 표현입니다.

{
  "orderID":3,
  "productID":2,
  "quantity":4,
  "orderValue":16.60,
  "links":[
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"DELETE",
      "types":[]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"DELETE",
      "types":[]
    }]
}

이 예제에서는 배열에 links 링크 집합이 있습니다. 각 링크는 관련 엔터티에 대한 작업을 나타냅니다. 각 링크의 데이터에는 관계("고객"), URI(https://api.contoso.com/customers/3), HTTP 메서드 및 지원되는 MIME 형식이 포함됩니다. 클라이언트 애플리케이션은 작업을 호출하기 위해 이 정보가 필요합니다.

배열에는 links 검색된 리소스에 대한 자체 참조 정보도 포함됩니다. 이러한 링크는 관계 자기를 가지고 있습니다.

반환되는 링크 집합은 리소스의 상태에 따라 변경됩니다. 하이퍼텍스트가 애플리케이션 상태의 엔진 이라는 생각은 이 시나리오를 설명합니다.

버전 관리 구현

웹 API는 정적 상태로 유지되지 않습니다. 비즈니스 요구 사항이 변경되면 새 리소스 컬렉션이 추가됩니다. 새 리소스가 추가되면 리소스 간의 관계가 변경될 수 있으며 리소스의 데이터 구조가 수정될 수 있습니다. 새 요구 사항 또는 다른 요구 사항을 처리하도록 웹 API를 업데이트하는 것은 간단한 프로세스이지만 이러한 변경 내용이 웹 API를 사용하는 클라이언트 애플리케이션에 미치는 영향을 고려해야 합니다. 웹 API를 디자인하고 구현하는 개발자는 해당 API를 완전히 제어할 수 있지만 파트너 조직에서 빌드한 클라이언트 애플리케이션에 대해 동일한 수준의 제어가 없습니다. 새 클라이언트 애플리케이션에서 새 기능 및 리소스를 사용할 수 있도록 하면서 기존 클라이언트 애플리케이션을 계속 지원하는 것이 중요합니다.

버전 관리 구현 웹 API는 노출하는 기능 및 리소스를 나타낼 수 있으며 클라이언트 애플리케이션은 특정 버전의 기능 또는 리소스로 전달되는 요청을 제출할 수 있습니다. 다음 섹션에서는 각각의 장점과 단점이 있는 여러 가지 접근법을 설명합니다.

버전 관리 없음

이 방법은 가장 간단하며 일부 내부 API에서 작동할 수 있습니다. 중요한 변경 내용은 새 리소스 또는 새 링크로 나타낼 수 있습니다. 기존 리소스에 콘텐츠를 추가해도 기대하지 않는 클라이언트 애플리케이션은 이를 처리하지 않으므로 파괴적인 변화가 발생하지 않을 수 있습니다.

예를 들어 URI https://api.contoso.com/customers/3에 대한 요청은 클라이언트 애플리케이션이 기대하는 id, name, address 필드를 포함한 단일 고객의 세부 정보를 반환해야 합니다.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

비고

간단히 하기 위해 이 섹션에 표시된 예제 응답에는 HATEOAS 링크가 포함되지 않습니다.

필드가 DateCreated 고객 리소스의 스키마에 추가되면 응답은 다음과 같습니다.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}

인식할 수 없는 필드를 무시할 수 있는 경우 기존 클라이언트 애플리케이션이 계속 올바르게 작동할 수 있습니다. 한편, 이 새 필드를 처리하도록 새 클라이언트 애플리케이션을 설계할 수 있습니다. 그러나 필드 제거 또는 이름 바꾸기를 포함하여 리소스 스키마를 보다 크게 수정할 수 있습니다. 또는 리소스 간의 관계가 변경될 수 있습니다. 이러한 업데이트는 기존 클라이언트 애플리케이션이 제대로 작동하지 않도록 하는 호환성이 손상되는 변경을 구성할 수 있습니다. 이러한 시나리오에서는 다음 방법 중 하나를 고려합니다.

URI 버전 관리

웹 API를 수정하거나 리소스의 스키마를 변경할 때마다 각 리소스의 URI에 버전 번호를 추가합니다. 기존 URI는 원래 스키마를 준수하는 리소스를 반환하여 정상적으로 계속 작동해야 합니다.

예를 들어, 이전 예제에서 address 필드는 streetAddress, city, state, 및 zipCode와 같이 주소의 각 구성 요소 부분을 포함하는 하위 필드로 재구성됩니다. 이 버전의 리소스는 다음과 같은 https://api.contoso.com/v2/customers/3버전 번호가 포함된 URI를 통해 노출할 수 있습니다.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

이 버전 관리 메커니즘은 간단하지만 요청을 적절한 엔드포인트로 라우팅하는 서버에 따라 달라집니다. 그러나 웹 API가 여러 반복을 통해 완성되고 서버가 다양한 버전을 지원해야 하므로 다루기 어려울 수 있습니다. 순수주의자의 관점에서 볼 때, 모든 경우에 클라이언트 애플리케이션은 동일한 데이터(고객 3)를 가져오므로 버전에 따라 URI가 달라서는 안 됩니다. 또한 이 스키마는 모든 링크가 URI에 버전 번호를 포함해야 하기 때문에 HATEOAS의 구현을 복잡하게 만듭니다.

쿼리 문자열 버전 관리

여러 URI를 제공하는 대신 HTTP 요청에 https://api.contoso.com/customers/3?version=2추가된 쿼리 문자열 내의 매개 변수를 사용하여 리소스의 버전을 지정할 수 있습니다. 이전 클라이언트 애플리케이션에서 생략하는 경우 버전 매개 변수는 기본적으로 1과 같은 의미 있는 값으로 설정되어야 합니다.

이 방법은 동일한 리소스가 항상 동일한 URI에서 검색된다는 의미 체계 이점이 있습니다. 그러나 이 메서드는 쿼리 문자열을 구문 분석하고 적절한 HTTP 응답을 다시 보내는 요청을 처리하는 코드에 따라 달라집니다. 또한 이 방법은 URI 버전 관리 메커니즘과 동일한 방식으로 HATEOAS의 구현을 복잡하게 만듭니다.

비고

일부 이전 웹 브라우저 및 웹 프록시는 URI에 쿼리 문자열을 포함하는 요청에 대한 응답을 캐시하지 않습니다. 캐시되지 않은 응답은 웹 API를 사용하고 이전 웹 브라우저 내에서 실행되는 웹 애플리케이션의 성능을 저하시킬 수 있습니다.

헤더 버전 관리

버전 번호를 쿼리 문자열 매개 변수로 추가하지 않고 리소스의 버전을 나타내는 사용자 지정 헤더를 구현할 수 있습니다. 이 방법을 사용하려면 클라이언트 애플리케이션이 모든 요청에 적절한 헤더를 추가해야 합니다. 그러나 버전 헤더를 생략하면 클라이언트 요청을 처리하는 코드에서 버전 1과 같은 기본값을 사용할 수 있습니다.

다음 예제에서는 Custom-Header라는 사용자 지정 헤더를 사용합니다. 이 헤더의 값은 웹 API의 버전을 나타냅니다.

버전 1:

GET https://api.contoso.com/customers/3
Custom-Header: api-version=1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

버전 2:

GET https://api.contoso.com/customers/3
Custom-Header: api-version=2
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

URI 버전 관리쿼리 문자열 버전 관리와 마찬가지로 HATEOAS를 구현하려면 모든 링크에 적절한 사용자 지정 헤더를 포함해야 합니다.

미디어 유형 버전 관리

클라이언트 애플리케이션이 웹 서버에 HTTP GET 요청을 보낼 때 Accept 헤더를 사용하여 처리할 수 있는 콘텐츠의 형식을 지정해야 합니다. 일반적으로 Accept 헤더의 목적은 클라이언트 애플리케이션이 응답 본문이 XML, JSON 또는 클라이언트가 구문 분석할 수 있는 다른 일반적인 형식인지 여부를 지정하도록 허용하는 것입니다. 그러나 클라이언트 애플리케이션이 예상되는 리소스의 버전을 나타낼 수 있는 정보를 포함하는 사용자 지정 미디어 유형을 정의할 수 있습니다.

다음 예제에서는 값 application/vnd.contoso.v1+json이 있는 Accept 헤더를 지정하는 요청을 보여 있습니다. 요소는 vnd.contoso.v1 웹 서버에 리소스의 버전 1을 반환해야 임을 나타냅니다. 요소는 json 응답 본문의 형식이 JSON이어야 한다고 지정합니다.

GET https://api.contoso.com/customers/3
Accept: application/vnd.contoso.v1+json

요청을 처리하는 코드는 Accept 헤더를 읽고 그것을 가능한 한 많이 준수해야 합니다. 클라이언트 애플리케이션은 Accept 헤더에서 여러 형식을 지정할 수 있으므로 웹 서버가 응답 본문에 가장 적합한 형식을 선택할 수 있습니다. 웹 서버는 Content-Type 헤더를 사용하여 응답 본문의 데이터 형식을 확인합니다.

HTTP/1.1 200 OK
Content-Type: application/vnd.contoso.v1+json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

Accept 헤더가 알려진 미디어 형식을 지정하지 않는 경우 웹 서버는 HTTP 406(허용되지 않음) 응답 메시지를 생성하거나 기본 미디어 형식의 메시지를 반환할 수 있습니다.

이 버전 관리 메커니즘은 리소스 링크에 관련 데이터의 MIME 형식을 포함할 수 있는 HATEOAS에 간단하고 적합합니다.

비고

버전 관리 전략을 선택하면 특히 웹 서버 캐싱과 관련하여 의미가 있습니다. 동일한 URI 또는 쿼리 문자열 조합이 매번 동일한 데이터를 참조하기 때문에 URI 버전 관리 및 쿼리 문자열 버전 관리 스키마는 캐시에 친숙합니다.

헤더 버전 관리 및 미디어 형식 버전 관리 메커니즘은 일반적으로 사용자 지정 헤더 또는 Accept 헤더의 값을 검사하기 위해 더 많은 논리가 필요합니다. 대규모 환경에서는 여러 버전의 Web API를 사용하는 많은 클라이언트가 서버 쪽 캐시에서 상당한 양의 중복 데이터를 생성할 수 있습니다. 클라이언트 애플리케이션이 캐싱을 구현하는 프록시를 통해 웹 서버와 통신하고 요청된 데이터의 복사본이 현재 캐시에 포함되어 있지 않은 경우에만 웹 서버에 요청을 전달하는 경우 이 문제가 심각해질 수 있습니다.

다중 테넌트 웹 API

다중 테넌트 웹 API 솔루션은 고유한 사용자 그룹이 있는 고유 조직과 같은 여러 테넌트에서 공유됩니다.

다중 테넌트는 단일 웹 API 내의 여러 테넌트에서 리소스에 액세스하고 검색하는 방법을 결정하므로 웹 API 디자인에 크게 영향을 줍니다. 격리, 확장성 또는 테넌트별 사용자 지정을 구현하기 위해 향후 리팩터링이 필요하지 않도록 다중 테넌트를 염두에 두고 API를 디자인합니다.

잘 설계된 API는 하위 도메인, 경로, 헤더 또는 토큰을 통해 요청에서 테넌트를 식별하는 방법을 명확하게 정의해야 합니다. 이 구조는 시스템 내의 모든 사용자에게 일관적이면서도 유연한 환경을 보장합니다. 자세한 내용은 다중 테넌트 솔루션에서 테넌트에 요청을 매핑하는 방법을 참조하세요.

다중 테넌시는 엔드포인트 구조, 요청 처리, 인증 및 권한 부여에 영향을 줍니다. 이 방법은 API 게이트웨이, 부하 분산 장치 및 백 엔드 서비스가 요청을 라우팅하고 처리하는 방법에도 영향을 줍니다. 다음 전략은 웹 API에서 다중 테넌트를 달성하는 일반적인 방법입니다.

하위 도메인 또는 도메인 기반 격리 사용(DNS 수준 테넌트)

이 방법은 테넌트별 도메인을 사용하여 요청을 라우팅합니다. 와일드카드 도메인은 유연성과 단순성을 위해 하위 도메인을 사용합니다. 테넌트가 자체 도메인을 사용할 수 있도록 하는 사용자 지정 도메인은 더 큰 제어를 제공하며 특정 요구 사항에 맞게 조정할 수 있습니다. 두 방법 모두 ACNAME 레코드가 포함된 적절한 DNS 구성을 사용하여 트래픽을 적절한 인프라로 유도합니다. 와일드카드 도메인은 구성을 간소화하지만 사용자 지정 도메인은 더 브랜드화된 환경을 제공합니다.

URL 리디렉션과 같은 문제를 방지하고 내부 URL이 노출되지 않도록 역방향 프록시와 백 엔드 서비스 간에 호스트 이름을 유지합니다. 이 메서드는 테넌트별 트래픽의 올바른 라우팅을 보장하고 내부 인프라를 보호하는 데 도움이 됩니다. DNS 확인은 데이터 보존을 달성하고 규정 준수를 보장하는 데 중요합니다.

GET https://adventureworks.api.contoso.com/orders/3

테넌트별 HTTP 헤더 전달

테넌트 정보는 호스트 기반 헤더와 같은 X-Tenant-IDX-Organization-ID 사용자 지정 HTTP 헤더를 Host 통해 전달되거나 X-Forwarded-HostJWT(JSON 웹 토큰) 클레임에서 추출될 수 있습니다. 선택은 API 게이트웨이 또는 역방향 프록시의 라우팅 기능에 따라 달라집니다. 헤더 기반 솔루션은 각 요청을 검사하기 위해 계층 7(L7) 게이트웨이가 필요합니다. 이 요구 사항은 처리 오버헤드를 추가하므로 트래픽이 확장되면 컴퓨팅 비용이 증가합니다. 그러나 헤더 기반 격리는 주요 이점을 제공합니다. 중앙 집중식 인증을 사용하도록 설정하여 다중 테넌트 API에서 보안 관리를 간소화합니다. SDK 또는 API 클라이언트를 사용하면 테넌트 컨텍스트가 런타임에 동적으로 관리되므로 클라이언트 쪽 구성 복잡성이 줄어듭니다. 또한 헤더에 테넌트 컨텍스트를 유지하면 URI에서 테넌트별 데이터를 방지하여 더 깨끗하고 더 깨끗한 RESTful API 디자인이 생성됩니다.

헤더 기반 라우팅의 중요한 고려 사항은 캐시 계층이 URI 기반 키만 사용하고 헤더를 고려하지 않는 경우 캐싱이 복잡하다는 점입니다. 대부분의 캐싱 메커니즘은 URI 조회에 최적화되므로 헤더를 사용하면 조각화된 캐시 항목이 발생할 수 있습니다. 조각화된 항목은 캐시 적중을 줄이고 백 엔드 로드를 증가시킵니다. 더 중요한 것은 캐싱 계층이 헤더로 응답을 구분하지 않는 경우 한 테넌트를 위한 캐시된 데이터를 다른 테넌트에 제공하고 데이터 유출 위험을 초래할 수 있다는 점입니다.

GET https://api.contoso.com/orders/3
X-Tenant-ID: adventureworks

또는

GET https://api.contoso.com/orders/3
Host: adventureworks

또는

GET https://api.contoso.com/orders/3
Authorization: Bearer <JWT-token including a tenant-id: adventureworks claim>

URI 경로를 통해 테넌트별 정보 전달

이 방법은 리소스 계층 내에 테넌트 식별자를 추가하고 API 게이트웨이 또는 역방향 프록시를 사용하여 경로 세그먼트에 따라 적절한 테넌트를 결정합니다. 경로 기반 격리는 효과적이지만 웹 API의 RESTful 디자인을 손상시키고 더 복잡한 라우팅 논리를 도입합니다. URI 경로를 구문 분석하고 정식화하려면 패턴 일치 또는 정규식이 필요한 경우가 많습니다.

반면 헤더 기반 격리는 HTTP 헤더를 통해 테넌트 정보를 키-값 쌍으로 전달합니다. 두 방법 모두 효율적인 인프라 공유를 통해 운영 비용을 절감하고 대규모 다중 테넌트 웹 API의 성능을 향상시킬 수 있습니다.

GET https://api.contoso.com/tenants/adventureworks/orders/3

API에서 분산 추적 기능 및 추적 컨텍스트 기능 사용 설정

분산 시스템과 마이크로 서비스 아키텍처가 표준이 됨에 따라 최신 아키텍처의 복잡성이 증가합니다. API 요청에서 추적 컨텍스트를 전파하는 등의 Correlation-IDX-Request-IDX-Trace-ID헤더를 사용하는 것이 엔드 투 엔드 가시성을 달성하는 것이 가장 좋습니다. 이 방법을 사용하면 클라이언트에서 백 엔드 서비스로 이동하는 요청을 원활하게 추적할 수 있습니다. 오류의 신속한 식별을 용이하게 하고, 대기 시간을 모니터링하고, 서비스 간에 API 종속성을 매핑합니다.

추적 및 컨텍스트 정보 포함을 지원하는 API는 관찰성 수준 및 디버깅 기능을 향상시킵니다. 분산 추적을 사용하도록 설정하면 이러한 API를 통해 시스템 동작을 보다 세밀하게 이해하고 복잡한 다중 서비스 환경에서 문제를 보다 쉽게 추적, 진단 및 해결할 수 있습니다.

GET https://api.contoso.com/orders/3
Correlation-ID: 0f8fad5b-d9cb-469f-a165-70867728950e
HTTP/1.1 200 OK
...
Correlation-ID: 0f8fad5b-d9cb-469f-a165-70867728950e

{...}

Web API 완성도 모델

2008년, 레너드 리처드슨은 현재 웹 API용 리처드슨 성숙 모델(RMM)으로 알려진 것을 제안했습니다. RMM은 웹 API에 대한 4가지 완성도 수준을 정의하며 웹 서비스 설계에 대한 아키텍처 접근 방식인 REST의 원칙을 기반으로 합니다. RMM에서 만기 수준이 증가함에 따라 API는 RESTful이 되고 REST의 원칙을 더 밀접하게 따릅니다.

수준은 다음과 같습니다.

  • 수준 0: 하나의 URI를 정의하고 모든 작업은 이 URI에 대한 POST 요청입니다. 단순 개체 액세스 프로토콜 웹 서비스는 일반적으로 이 수준에 있습니다.
  • 수준 1: 개별 리소스에 대한 별도의 URI를 만듭니다. 이 수준은 아직 RESTful이 아니지만 RESTful 디자인에 맞게 조정되기 시작합니다.
  • 수준 2: HTTP 메서드를 사용하여 리소스에 대한 작업을 정의합니다. 실제로 게시된 많은 웹 API는 이 수준에 대략적으로 맞춥니다.
  • 수준 3: 하이퍼미디어(HATEOAS)를 사용합니다. Fielding의 정의에 따르면 이 수준은 실제로 RESTful API입니다.

OpenAPI 이니셔티브

OpenAPI 이니셔티브는 공급업체 전체에서 REST API 설명을 표준화하기 위해 업계 컨소시엄에 의해 만들어졌습니다. 표준화 사양은 OpenAPI 이니셔티브에 포함되고 OAS(OpenAPI 사양)로 이름이 바뀌기 전에 Swagger라고 했습니다.

RESTful 웹 API에 OpenAPI를 채택할 수 있습니다. 다음 사항을 고려합니다.

  • OAS에는 REST API 디자인에 대한 의견 지침 집합이 함께 제공됩니다. 이 지침은 상호 운용성에 유리하지만 디자인이 사양을 준수하는지 확인해야 합니다.

  • OpenAPI는 구현 우선 접근 방식 대신 계약 우선 접근 방식을 승격합니다. 계약 우선은 먼저 API 계약(인터페이스)을 디자인한 다음 계약을 구현하는 코드를 작성한다는 것을 의미합니다.

  • Swagger(OpenAPI)와 같은 도구는 API 계약에서 클라이언트 라이브러리 또는 설명서를 생성할 수 있습니다. 예제는 Swagger/OpenAPI를 사용하는 ASP.NET Core Web API 설명서를 참조하세요.

다음 단계