RESTful 웹 API 구현에 대한 모범 사례

이 문서에서는 RESTful 웹 API를 구현, 배포 및 운영하기 위한 모범 사례를 설명합니다. 요청 처리, 성능, 안정성, 게시 및 모니터링에 중점을 둡니다. 리소스 모델링 및 API 디자인에 대한 지침은 Web API 디자인을 참조하세요.

아키텍처 지침은 구현 언어와 프레임워크 전반에 적용됩니다. C# 예제에서는 ASP.NET Core 컨트롤러를 보여 줍니다.

요청 처리

요청을 처리하기 위해 코드를 구현하는 경우 다음 사항을 고려합니다.

HTTP 메서드의 의미 체계를 유지하고 멱등성을 구현

GET을 사용해 리소스 데이터를 반환하고 HEAD를 사용해 본문 없이 응답 헤더를 반환합니다. 리소스를 만들거나 업데이트하거나 삭제할 때 두 방법 모두 사용하지 마세요.

GET, HEAD, PUT 및 DELETE는 idempotent여야 하므로 동일한 요청을 반복하면 한 번 수행하는 것과 동일한 의도된 효과가 있습니다. 예를 들어 DELETE는 리소스 상태를 변경하지만 반복해도 추가 리소스는 삭제되지 않습니다. 첫 번째 DELETE 요청은 204(콘텐츠 없음)를 반환할 수 있고 후속 요청은 404(찾을 수 없음)를 반환할 수 있습니다.

POST 및 PATCH는 본질적으로 idempotent가 아닙니다. 속성을 값으로 설정하는 PATCH 작업은 idempotent일 수 있지만 카운터를 증가시키는 작업은 그렇지 않습니다. 클라이언트가 안전하게 다시 시도할 수 있는 상태 변경 작업을 문서화합니다. 자연적으로 멱등적이지 않은 작업에 대한 재시도를 지원하는 경우, 서비스에서 중복 효과를 방지하는 방법을 구현하고 문서화하세요. 메서드 의미 체계는 HTTP 요청 메서드를 참조하세요.

참고

구현 지침은 Idempotent 소비자 패턴을 참조하세요. 실제로는 먼저 작업이 자연스럽게 멱등적인지 여부를 확인합니다. 그렇지 않은 경우 처리된 메시지 ID를 추적하고 중복 항목을 사전에 또는 사후적으로 처리하여 인공 멱등성을 적용합니다.

요청 데이터 유효성 검사 및 필드 액세스를 제어

API 계약의 필드만 노출하는 요청 및 응답 모델을 정의합니다. 요청 본문을 데이터베이스 엔터티에 직접 바인딩하거나 저장된 모든 속성을 반환하는 대신 이러한 모델을 저장된 데이터에 매핑합니다. 호출자가 추가 필드를 제출하여 보호된 속성을 변경하지 못하도록 하고 읽을 권한이 없는 속성을 생략합니다. DTO(데이터 전송 개체)는 이 경계를 적용하는 한 가지 방법입니다. 자세한 내용은 ASP.NET Core의 과도한 게시 방지를 참조하세요.

변경 내용을 저장하기 전에 필요한 필드, 형식, 범위 및 비즈니스 규칙의 유효성을 검사합니다. 필드 선택을 지원하는 경우 응답을 프로젝션하기 전에 허용 목록 및 호출자의 권한에 대해 요청된 필드의 유효성을 검사합니다. 필터링 및 정렬에도 문서화된 제한을 적용합니다. 해당 선택 항목에 대해서는 페이지 매김 및 필터링 구현을 참조하세요.

리소스를 만드는 POST 작업의 부작용 제한

POST 요청이 리소스를 만들 때 그 영향을 해당 리소스 및 직접 관련된 리소스로 제한합니다. 예를 들어 주문을 만들면 인벤토리를 업데이트하고 청구 정보를 생성할 수도 있습니다. 관련 없는 정보는 수정해서는 안 됩니다.

번잡한 POST, PUT 및 DELETE 작업을 구현하지 않습니다.

API를 지나치게 많은 커뮤니케이션을 수반하는 운영 방식에 기반하여 설계하지 마십시오. 모든 요청에는 프로토콜, 네트워크 및 컴퓨팅 오버헤드가 수반됩니다. 예를 들어 하나의 더 큰 일괄 처리 요청 대신 100개의 작은 요청을 실행하면 클라이언트, 네트워크 및 리소스 서버에서 오버헤드가 추가됩니다. 대량 작업이 필요한 워크로드의 경우 여러 개별 요청 대신 제한된 일괄 처리 요청을 고려합니다.

  • 컬렉션에 대한 GET 요청은 동시에 여러 리소스를 검색할 수 있습니다.
  • POST 요청은 여러 새 리소스의 세부 정보를 포함시키고 동일한 컬렉션에 모두 추가할 수 있습니다.
  • PUT 요청은 컬렉션에 있는 전체 리소스 집합을 대체할 수 있습니다.
  • DELETE 요청은 전체 컬렉션을 제거할 수 있습니다.

최대 일괄 처리 크기, 페이로드 크기, 실행 시간 및 동시성을 정의합니다. 일괄 처리가 원자성인지 아니면 부분적으로 성공할 수 있는지를 지정하고 각 작업에 대한 결과를 반환합니다. 일괄 처리의 모든 작업을 승인하고 유효성을 검사합니다.

OData를 사용하는 경우 ASP.NET Core OData를 사용하여 일괄 처리 요청을 구현할 수 있습니다. 자세한 내용은 ASP.NET Core OData 개요를 참조하세요.

응답을 보낼 때 HTTP 사양을 따릅니다.

클라이언트가 응답을 해석하고 처리할 수 있도록 적절한 HTTP 상태 코드, 헤더 및 본문 형식을 반환합니다.

예를 들어 리소스를 동기적으로 만드는 POST 작업은 201(생성됨)을 반환하고 Location 응답 헤더에서 리소스를 식별해야 합니다. 이후 처리를 위해 작업을 수락하는 작업은 202(수락됨)를 반환할 수 있지만 해당 응답은 작업이 성공했음을 나타내지 않습니다. 응답 본문에 204(콘텐츠 없음) 또는 304(수정되지 않음)를 포함하지 마세요. 자세한 내용은 HTTP 요청 메서드를 참조하세요.

콘텐츠 협상을 지원합니다.

응답 본문은 JSON 또는 XML과 같은 다양한 형식의 데이터를 포함할 수 있습니다. 클라이언트는 Accept 요청 헤더를 사용하여 지원되는 미디어 형식을 지정할 수 있습니다. 예를 들어 이미지를 요청하는 클라이언트는 image/jpeg, image/gif, image/png를 지정할 수 있습니다. 지원되는 표현을 반환하고 Content-Type 응답 헤더에서 해당 미디어 형식을 식별합니다.

클라이언트가 Accept 헤더를 지정하지 않으면 문서화된 기본 형식을 사용합니다. ASP.NET Core 컨트롤러는 기본적으로 JSON 포맷터를 사용합니다. API가 요청된 표현을 생성할 수 없을 때 발생하는 동작을 정의합니다. 예를 들어 지원되지 않는 형식을 아무 알림 없이 반환하지 않고 406(허용되지 않음)을 반환하도록 API를 구성합니다. 자세한 내용은 ASP.NET Core Web API에서 응답 데이터 서식 지정을 참조하세요.

본문이 있는 요청의 Content-Type를 확인하고 작업이 지원하는 형식만 수락합니다. 지원되지 않는 형식에 대해 415(지원되지 않는 미디어 형식)를 반환합니다. 이 검사는 Accept를 통한 응답 협상과는 별개입니다. ASP.NET Core에서는 입력 포맷터를 구성하고 작업의 요청 형식을 제한해야 할 때는 [Consumes]를 사용합니다. 자세한 내용은 ASP.NET Core 사용하여 웹 API 만들기를 참조하세요.

선택한 API 버전 관리 전략 구현

애플리케이션 및 게이트웨이에서 디자인 중에 선택한 버전 관리 전략을 일관되게 적용합니다. 선택한 버전의 처리기 및 응답 스키마로 요청을 라우팅합니다. 기존 호출자가 수신하는 계약을 명시적 알림 없이 변경하는 대신 생략, 형식이 잘못된, 그리고 지원되지 않는 버전 식별자에 대한 동작을 정의합니다.

지원되는 버전의 요청 및 응답 계약을 유지합니다. 호환성이 손상되는 변경이 도입되면 새 버전을 제공하고 지원되는 이전 버전을 계속 제공합니다. 새로 추가된 선택적 필드를 생략하는 기존 호출자의 요청을 처리합니다. 자세한 내용은 API 버전 관리 및 호환성을 참조하세요.

생성된 리소스 링크 및 캐시된 표현을 선택한 버전과 일관되게 유지합니다. 헤더 기반 버전 관리의 경우 캐시가 URI에만 의존하는 대신 다른 버전에 대한 요청을 구분하는지 확인합니다.

테넌트 컨텍스트 확정 및 권한 부여

다중 테넌트 API의 경우 각 요청에 대한 테넌트를 확인하고 인증된 호출자가 해당 테넌트에 액세스할 수 있는지 확인합니다. 경로, 호스트 이름 및 헤더의 테넌트 식별자를 라우팅 입력으로만 처리합니다. 자세한 내용은 다중 테넌트 솔루션에서 테넌트에 요청을 매핑하는 방법을 참조하세요.

데이터를 쿼리하거나 업데이트할 때와 백그라운드 프로세서에 작업을 전달할 때 권한 있는 테넌트 컨텍스트를 일관되게 사용합니다. 애플리케이션 또는 게이트웨이 캐시가 테넌트별 데이터를 저장하는 경우 캐시 키에 테넌트 식별자를 포함합니다. 게이트웨이 관련 지침은 다중 테넌트를 지원하는 API Management 기능을 참조하세요.

HATEOAS(애플리케이션 상태의 엔진으로서의 하이퍼텍스트)는 클라이언트가 리소스를 검색하고 탐색하는 데 도움이 되는 링크를 사용합니다. 예를 들어 고객 세부 정보가 포함된 응답에는 해당 고객의 주문에 대한 링크가 포함될 수 있습니다. 각 연결된 리소스가 지원하는 작업(예: GET, POST, PUT 및 DELETE)을 설명하고 해당 URI를 제공합니다. 자세한 내용은 API 설계를 참조하세요.

다음 예제에서는 애플리케이션 정의 링크 표현을 사용합니다. 클라이언트가 사용자 지정 링크 필드를 해석할 수 있다고 가정하지 않고 해당 규칙을 문서화합니다. 고객에 대한 GET 요청은 해당 고객의 주문에 대한 링크를 반환합니다.

GET https://api.contoso.com/customers/2 HTTP/1.1
Accept: application/json
HTTP/1.1 200 OK
...
Content-Type: application/json; charset=utf-8
...
Content-Length: ...
{"customerId": 2,"customerName": "Bert","links": [
    {"rel": "self",
     "href": "https://api.contoso.com/customers/2",
     "action": "GET",
     "types": ["text/xml","application/json"]},
    {"rel": "self",
     "href": "https://api.contoso.com/customers/2",
     "action": "PUT",
     "types": ["application/x-www-form-urlencoded"]},
    {"rel": "self",
     "href": "https://api.contoso.com/customers/2",
     "action": "DELETE",
     "types": []},
    {"rel": "orders",
     "href": "https://api.contoso.com/customers/2/orders",
     "action": "GET",
     "types": ["text/xml","application/json"]},
    {"rel": "orders",
     "href": "https://api.contoso.com/customers/2/orders",
     "action": "POST",
     "types": ["application/x-www-form-urlencoded"]}
]}

다음 Customer 레코드는 고객 데이터를 나타냅니다. 해당 Links 속성에는 HATEOAS 링크가 포함되어 있습니다.

public sealed record Customer(
    int CustomerId,
    string CustomerName,
    IReadOnlyList<Link> Links);

public sealed record Link(
    string Rel,
    string Href,
    string Action,
    IReadOnlyList<string> Types);

GET 작업은 스토리지에서 고객 데이터를 검색하고, 해당 링크와 함께 Customer 레코드를 생성하고, JSON 응답을 반환합니다. 각 링크에는 다음 필드가 포함됩니다.

  • 반환된 리소스와 연결된 리소스 간의 관계(Rel)입니다. 이 예제에서 self는 고객을, orders는 고객의 주문 컬렉션을 가리킵니다.
  • 링크에서 설명하는 개체의 하이퍼링크(Href)(URI 형식)입니다.
  • URI로 보낼 수 있는 HTTP 요청(Action)의 형식입니다.
  • HTTP 요청에 제공되어야 하는 데이터 형식 또는 요청 유형에 따라 응답으로 반환될 수 있는 데이터의 형식(Types).

HTTP 응답 예제에 있는 HATEOAS 링크는 클라이언트 애플리케이션이 다음 작업을 수행할 수 있다는 것을 나타냅니다.

  • 고객 세부 정보를 XML 또는 JSON으로 검색하기 위한 https://api.contoso.com/customers/2 HTTP GET 요청입니다.
  • 고객 세부 정보를 수정하기 위한 https://api.contoso.com/customers/2 HTTP PUT 요청입니다. 요청 본문은 다음 형식 application/x-www-form-urlencoded을 사용합니다.
  • 고객 레코드를 삭제하기 위한 https://api.contoso.com/customers/2에 대한 HTTP DELETE 요청 이 작업에는 요청 본문이 필요하지 않으며 응답 본문을 반환하지 않습니다.
  • 고객의 주문을 XML 또는 JSON으로 가져오기 위한 https://api.contoso.com/customers/2/orders에 대한 HTTP GET 요청입니다.
  • 고객을 위한 주문을 만들기 위한 HTTP POST 요청 https://api.contoso.com/customers/2/orders 입니다. 요청 본문은 다음 형식 application/x-www-form-urlencoded을 사용합니다.

예외 처리

작업이 처리되지 않은 예외를 발생시키는 경우 다음 지침을 고려합니다.

예외 사항을 확인하고 클라이언트에 의미 있는 응답을 반환합니다.

예외를 일관되게 처리하고 클라이언트와 공유해도 안전한 의미 있는 오류 응답을 반환합니다. 프로덕션 응답에서 예외 개체, 스택 추적 또는 내부 구현 세부 정보를 반환하지 마세요. 모든 상황에 대해 상태 코드 500을 반환하는 대신 오류를 반영하는 HTTP 상태 코드를 반환하세요. 예를 들어 사용자 요청으로 인해 제약 조건을 위반하는 데이터베이스 업데이트가 발생하는 경우(예: 미해결 주문이 있는 고객을 삭제하려고 시도) 409(충돌)와 안전한 설명을 반환합니다. 예기치 않은 서버 오류가 아닌 잘못된 형식의 입력 또는 요청 유효성 검사 실패에 대해 400(잘못된 요청)을 반환합니다. HTTP 상태 코드 정의는 상태 코드를 참조하세요. 구현 지침은 ASP.NET Core 오류 처리를 참조하세요.

다음 컨트롤러는 예상 도메인 결과를 HTTP 응답에 매핑합니다. 리포지토리 작업은 삭제 작업의 일부로 권한 부여 및 미처리 요청을 확인합니다. 예기치 않은 오류는 각 작업에서 조용히 처리되지 않고 중앙 집중식 예외 처리로 전파됩니다.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Authorize]
[Route("customers")]
public sealed class CustomersController(ICustomerRepository repository) : ControllerBase
{
    [HttpDelete("{id:int}")]
    public async Task<IActionResult> DeleteCustomer(int id, CancellationToken cancellationToken)
    {
        var outcome = await repository.DeleteAuthorizedAsync(id, User, cancellationToken);

        return outcome switch
        {
            DeleteOutcome.Deleted => NoContent(),
            DeleteOutcome.NotFound => NotFound(),
            DeleteOutcome.HasOutstandingOrders => Conflict(new ProblemDetails
            {
                Status = StatusCodes.Status409Conflict,
                Title = "The customer has outstanding orders."
            }),
            _ => throw new InvalidOperationException("Unknown deletion outcome.")
        };
    }
}

public enum DeleteOutcome
{
    Deleted,
    NotFound,
    HasOutstandingOrders
}

Important

API에 대한 무단 액세스를 시도하는 공격자에게 유용할 수 있는 정보는 포함하지 마세요.

호스팅 계층은 API에 도달하기 전에 요청을 거부할 수 있습니다. 예를 들어 인증 실패로 인해 401(인증되지 않음) 응답이 발생할 수 있습니다. 인증 후 호출자가 요청된 리소스에 액세스할 권한이 있는지 확인합니다. 권한 부여가 실패하면 403(금지됨)을 반환합니다.

일관되게 예외를 처리하고 오류에 대한 정보를 기록합니다.

예외를 일관되게 처리하려면 전체 웹 API에서 전역 오류 처리 전략을 구현하는 것이 좋습니다. 서버에 진단 정보를 기록하지만 비밀, 액세스 토큰 및 중요한 개인 데이터는 제외합니다. 로그 항목을 작성하기 전에 민감한 값을 마스킹하고, 권한 있는 사용자로 로그 액세스를 제한합니다. 자세한 내용은 모니터링 및 위협 탐지를 위한 아키텍처 전략을 참조하세요.

ASP.NET Core에서는 일관된 오류 계약을 위해 예외 처리 미들웨어 및 문제 세부 정보 응답을 사용합니다. 진단 세부 정보를 생략하고 지원 팀이 해당 로그를 찾는 데 사용할 수 있는 상관 관계 식별자를 포함하도록 프로덕션 응답을 구성합니다. 자세한 내용은 ASP.NET Core의 오류 처리를 참조하세요.

클라이언트쪽 오류와 서버쪽 오류를 구별합니다.

클라이언트 오류에는 HTTP 4xx 상태 코드를 사용하고 서버 오류의 경우 HTTP 5xx 상태 코드를 사용합니다. 오류 응답에서 이러한 구분을 유지합니다.

클라이언트 쪽 데이터 액세스 최적화

요청이 빈번하고 응답 크기가 클 경우 네트워크에 병목 현상이 발생할 수 있습니다. 리소스 검색 및 업데이트를 구현할 때 불필요한 전송을 줄입니다.

클라이언트쪽 캐싱을 지원합니다.

응답 헤더를 Cache-Control 사용하여 클라이언트와 중간 서버가 응답을 저장할 수 있는지 여부와 최신 상태로 유지되는 기간을 지정합니다.

다음 예제는 HTTP GET 요청 및 Cache-Control 헤더를 포함하는 해당 응답입니다.

GET https://api.contoso.com/orders/2 HTTP/1.1
HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: application/json
Content-Length: 59

{"orderId":2,"productId":4,"quantity":2,"orderValue":10.00}

이 예제에서는 응답이 600초 동안 최신 상태로 유지됩니다. private 지시문은 브라우저 캐시와 같은 프라이빗 캐시에 저장하는 것을 허용하지만 공유 캐시에 저장하는 것은 금지합니다. 호출자 간에 공유해도 안전한 응답에만 사용합니다 public . 저장해서는 안 되는 중요한 응답에는 no-store를 사용합니다.

ASP.NET Core 컨트롤러에서 응답을 반환하기 전에 캐시 헤더를 설정합니다.

Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
{
    Private = true,
    MaxAge = TimeSpan.FromMinutes(10)
};

참고

이 지시문은 no-cache 저장을 허용하지만 다시 사용하기 전에 재검증이 필요합니다. no-store을 사용하여 저장을 금지합니다.

API는 응답에서 캐시 정책 및 검증자를 제어합니다. 클라이언트 및 중간 서버는 해당 정보를 사용하여 캐시를 관리합니다. 호출자가 불필요한 데이터 전송 없이 응답을 다시 사용할 수 있도록 이러한 헤더를 일관되게 설정합니다.

max-age를 결정하려면 워크로드가 허용할 수 있는 오래됨을 고려합니다. 캐시된 응답이 여전히 최신 상태인 동안 리소스가 변경될 수 있습니다. 유효 기간이 만료되면 캐시는 응답을 폐기하고 전체 표현을 다시 다운로드하는 대신 응답의 유효성을 다시 검사할 수 있습니다. 변경 내용을 즉시 표시해야 하는 데이터에는 긴 최신 상태 유지 시간을 사용하지 마세요.

쿼리 처리를 최적화하기 위해 ETag를 제공합니다.

ETag(엔터티 태그)는 선택한 리소스 표현에 대한 불투명 유효성 검사기입니다. 클라이언트는 캐시된 응답과 함께 이를 보관하고 If-None-Match에서 이를 전송하여 표현이 변경되었는지 여부를 확인할 수 있습니다. 일치하는 GET 또는 HEAD 요청은 응답 본문 없이 304(수정되지 않음)를 반환합니다.

애플리케이션 인스턴스 간에 일관성을 유지하고 표현이 변경되면 변경되는 유효성 검사기를 생성합니다.

다음 컨트롤러는 반환하는 정확한 JSON 바이트를 해시하고 애플리케이션의 구성된 JSON 옵션을 다시 사용합니다. FileContentResult은 사용자 지정 결과 래퍼 없이 ASP.NET Core의 조건부 응답 처리를 적용합니다. 리포지토리는 호출자가 읽을 권한이 있는 주문만 반환합니다. 호스트 애플리케이션에서 인증 및 권한 부여를 구성합니다.

using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;

[ApiController]
[Authorize]
[Route("orders")]
public sealed class OrdersController(IOrderRepository repository,IOptions<JsonOptions> jsonOptions) : ControllerBase
{
    [HttpGet("{id:int}")]
    [HttpHead("{id:int}")]
    public async Task<IActionResult> GetOrder(int id, CancellationToken cancellationToken)
    {
        var order = await repository.FindAuthorizedAsync(id, User, cancellationToken);
        if (order is null)
        {
            return NotFound();
        }

        var payload = JsonSerializer.SerializeToUtf8Bytes(order, jsonOptions.Value.JsonSerializerOptions);
        var etag = new EntityTagHeaderValue($"\"{Convert.ToHexString(SHA256.HashData(payload))}\"");

        Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue
        {
            Private = true,
            MaxAge = TimeSpan.FromMinutes(10)
        };

        return new FileContentResult(payload, "application/json")
        {
            EntityTag = etag
        };
    }
}

이 예제는 표현을 버퍼링하고 조건부 요청에 대해서도 순서를 읽는 예제입니다. 경계가 지정된 민감하지 않은 응답에만 사용합니다. 큰 리소스의 경우 전체 페이로드를 생성하지 않고 요청의 유효성을 검사할 수 있도록 선택한 표현이 변경될 때마다 변경되는 저장된 버전 토큰을 사용하는 것이 좋습니다. 콘텐츠 협상, 압축 또는 게이트웨이 변환이 표현을 변경하는 경우 검증자 및 캐시 변이가 해당 표현에 대해 올바른 상태로 유지되는지 확인합니다.

응답에는 ETag 및 캐시 정책이 포함됩니다. 다음 예제에서는 표시된 JSON 본문의 SHA-256 해시의 처음 16개의 16진수 문자(64비트)를 보여 주며, 간결성을 위해 뒤에 ...가 붙습니다. 실제 헤더에는 줄임표가 없는 전체 해시가 포함됩니다.

HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: application/json
ETag: "3097D1FD51082205..."
Content-Length: 59

{"orderId":2,"productId":4,"quantity":2,"orderValue":10.00}

팁

민감한 데이터가 포함된 응답에는 Cache-Control: no-store를 사용합니다. 캐시할 수 있는 다른 사용자별 응답의 경우 공유 캐시에 저장되지 않도록 private를 사용합니다. 구현 고려 사항은 ASP.NET Core 응답 캐싱 미들웨어를 참조하세요.

호출자가 변경되지 않은 리소스 표현을 다운로드하지 않고 캐시된 응답의 유효성을 다시 검사할 수 있도록 조건부 GET 요청을 지원합니다. 다음 요청 및 응답 동작을 구현합니다.

  1. 다음과 같이 If-None-Match 헤더에 ETag를 포함하는 조건부 GET 요청을 수락합니다.

    GET https://api.contoso.com/orders/2 HTTP/1.1
    If-None-Match: "3097D1FD51082205..."
    
  2. 요청된 리소스에 대한 현재 ETag(이 예제의 순서 2)를 If-None-Match 조건과 비교합니다.

    • 조건이 일치하는 경우 응답 본문 없이 304(수정되지 않음)를 반환합니다.

    • 조건이 일치하지 않으면 현재 표현 형식과 함께 200(OK)를 반환합니다.

    • 요청된 리소스가 더 이상 없으면 404(찾을 수 없음)를 반환합니다.

참고

ETag는 응답을 캐시에 안전하게 만들지 않습니다. 조건부 및 오류 응답을 포함하여 캐시 지시문을 일관되게 적용하고, 민감한 데이터에는 no-store를 사용합니다.

가능한 경우 조건부 요청에 프레임워크 지원을 사용합니다. 직접 비교를 구현하는 경우 첫 번째 태그만 비교할 것이 아니라 제공된 모든 엔터티 태그, 와일드카드 조건, 그리고 약한 비교 규칙과 강한 비교 규칙을 처리합니다. 304(수정되지 않음) 응답에 적절한 ETag 및 캐시 헤더를 포함하고 리소스 메타데이터를 반환하기 전에 인가를 수행합니다.

팁

저장된 검증자를 사용하면 전체 리소스를 검색하지 않고도 표현 형식이 변경되었는지 여부를 확인할 수 있습니다. 이 방법은 리소스가 크거나 검색 비용이 많이 드는 경우 데이터 전송 및 처리를 줄일 수 있습니다.

ETag를 사용하여 낙관적 동시성을 지원

업데이트 손실을 방지하려면 상태 변경 요청에 대한 If-Match 사전 조건을 리소스의 현재 ETag에 대해 평가합니다. 이를 통해 API는 다른 작업이 이미 수정한 표현에 따라 변경 내용을 거부할 수 있습니다. 다음 동작을 구현하세요:

  1. If-Match 헤더에서 예상 버전을 식별하는 업데이트 요청을 수락합니다. 다음 PUT 요청은 주문 2를 업데이트합니다. 동일한 방식으로 축약된 이전 GET 응답의 ETag를 사용합니다. 태그는 새 요청 본문이 아닌 기존 표현을 식별합니다.

    PUT https://api.contoso.com/orders/2 HTTP/1.1
    If-Match: "3097D1FD51082205..."
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 37
    
    productId=3&quantity=5&orderValue=250
    
  2. 요청된 리소스에 대한 현재 ETag(이 예제의 순서 2)를 If-Match 조건과 비교합니다.

    • 사전 조건이 일치하는 경우 업데이트를 원자적으로 수행하고 문서화된 성공 응답(예: 204(콘텐츠 없음))을 반환합니다. Location 헤더는 단순히 기존 리소스가 업데이트되었기 때문에 필요하지 않습니다. 저장된 표현 형식에 대한 HTTP의 유효성 검사기 요구 사항을 충족하는 경우에만 성공적인 PUT 응답에 ETag를 반환합니다.

    • 조건이 일치하지 않으면 412(사전 조건 실패)를 반환합니다. 이 예제에서는 응답 본문을 반환하지 않습니다.

    • 업데이트할 리소스가 더 이상 없으면 404(찾을 수 없음)를 반환합니다.

  3. 호출자가 업데이트에 성공했는지 또는 다른 변경 내용을 제출하기 전에 현재 표현을 검색해야 하는지 여부를 결정할 수 있도록 성공 및 충돌 응답을 문서화합니다.

업데이트 손실을 방지해야 하는 작업의 경우 사전 조건 If-Match이 필요하며, 없으면 428(사전 조건 필요)을 반환합니다. If-Match에 대해 엄격한 비교를 사용합니다.

애플리케이션 메모리에서 버전을 확인한 다음 무조건 저장하면 경합 상태가 만들어집니다. 데이터 저장소에서 버전 비교 및 쓰기를 원자적으로 적용합니다. 예를 들어 저장된 버전이 요청을 평가하는 데 사용된 버전과 여전히 일치하는 경우에만 업데이트가 성공하도록 데이터베이스 동시성 토큰을 사용합니다.

EF(Entity Framework) Core에서 동시 변경 내용 덮어쓰기 대신 동시성 토큰을 구성하고 DbUpdateConcurrencyException 동시 변경을 처리합니다. API의 데이터 액세스 코드에서 작업의 의미 체계가 허용하는 경우가 아니면 예상 버전을 자동으로 바꾸고 충돌하는 데이터베이스 업데이트를 다시 시도하지 마세요. 자세한 내용은 동시성 충돌 처리를 참조하세요.

큰 요청 및 응답 처리

큰 요청 및 응답 본문은 API 메모리, 대역폭 및 연결 용량을 사용합니다. 대규모 전송으로 서비스의 리소스가 고갈되지 않도록 스트리밍 및 리소스 제한을 구현합니다. 상당한 양의 데이터를 처리할 때 다음 사항을 고려합니다.

큰 개체를 포함하는 요청 및 응답을 최적화합니다.

이미지 또는 기타 이진 데이터와 같은 큰 개체의 경우 스트리밍 업로드 및 다운로드를 지원합니다.

비동기 스트리밍 API를 사용하고 서버에서 협상된 HTTP 버전에 대한 전송 프레이밍을 선택하도록 합니다. 응답의 각 부분에 Transfer-Encoding: chunked를 수동으로 추가하지 마세요. 스트리밍은 HTTP/1.1 청크 인코딩으로 제한되지 않습니다. 애플리케이션 메모리에서 제한되지 않은 전체 요청 또는 응답을 버퍼링하지 마십시오.

애플리케이션 및 호스팅 계층에서 요청 본문 제한, 업로드 제한, 시간 제한 및 취소 정책을 설정합니다. 응답을 시작하기 전에 413(콘텐츠가 너무 큼)과 함께 크기가 너무 큰 요청을 거부합니다.

HTTP 압축은 서버 및 클라이언트에서 추가 처리의 비용으로 전송 크기를 줄일 수 있습니다. 예를 들어 클라이언트는 Accept-Encoding: gzip를 보낼 수 있습니다. 서버가 gzip 압축 콘텐츠를 반환하는 경우 Content-Encoding: gzip를 사용하여 인코딩을 식별합니다.

호스팅 플랫폼 또는 미들웨어를 통해 압축을 구성하고 클라이언트와 서버 측 모두에서 CPU 및 대역폭 간의 상충 관계를 측정합니다.

  • 이미 압축된 형식을 다시 압축하지 않습니다.
  • 동적으로 생성된 HTTPS 응답에 압축을 사용하도록 설정하기 전에 , BREACH와 같은 압축 위험을 평가하고 적절한 완화를 적용합니다. 미들웨어에서 HTTPS 압축을 사용하지 않도록 설정하더라도 호스팅 서버가 압축을 적용하도록 구성될 수 있습니다.
  • 콘텐츠 인코딩이 변경될 때 캐시 변형 및 ETag가 올바르게 유지되는지 확인합니다.

압축 구현 지침 및 보안 고려 사항은 ASP.NET Core의 응답 압축을 참조하세요.

대규모 리소스의 부분 다운로드 지원

API가 대규모 리소스의 부분 다운로드를 지원하는 경우 해당 기능을 Accept-Ranges로 알립니다. 응답 본문 없이 총 Content-Length 수를 포함하여 리소스의 메타데이터를 반환하여 HEAD 요청을 처리합니다. 지원되는 바이트 범위를 지정하는 GET 요청의 경우 206(부분 콘텐츠), 반환된 부분의 Content-Length, 리소스의 해당 부분을 식별하는 Content-Range를 반환합니다. 이 응답 계약을 통해 호출자는 API가 전체 개체를 반환할 필요 없이 리소스의 일부를 검색할 수 있습니다.

HTTP HEAD 요청 및 부분 응답은 API 디자인에서 자세히 설명합니다.

대용량 업로드에 대해 100-Continue 처리하기

대규모 HTTP/1.1 업로드의 경우 클라이언트는 Expect: 100-continue가 포함된 요청 헤더를 보내고 본문을 전송하기 전에 기다릴 수 있습니다. 서버는 중간 100(계속) 응답을 보내거나 전체 본문을 받지 않고 요청을 거부할 수 있습니다. 이 교환은 사전의 빈 요청 뒤에 두 번째 완전한 요청이 이어지는 것이 아니라 하나의 요청의 일부입니다.

호스팅 스택 및 게이트웨이에서 Expect: 100-continue가 있는 경우와 없는 경우 요청을 처리하는 방법을 구성하고 테스트합니다. 업로드를 수락하거나 거부할 때 API의 동작을 확인하고 서버 리소스 사용 및 요청 대기 시간에 미치는 영향을 측정합니다.

많은 수의 개체를 반환할 수 있는 요청에 대한 페이지 매김 지원

전체 큰 컬렉션을 검색하면 서버 처리, 네트워크 트래픽 및 대기 시간이 증가할 수 있습니다.

서버에서 페이지 매김 입력의 유효성을 검사하고 최대 페이지 크기를 제한합니다. 다음 컨트롤러는 요청 유효성 검사를 보여 줍니다. 기본값은 결과 10개이며 1~100 이외의 limit 값 또는 0~10,000 범위를 벗어난 오프셋을 거부합니다. 이러한 경계는 예시적입니다. 워크로드 및 부하 테스트에 따라 제한을 선택합니다. [ApiController]를 사용하는 경우 유효성 검사 실패로 400 응답이 생성됩니다. 자세한 내용은 ASP.NET Core 사용하여 웹 API 만들기를 참조하세요.

using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Authorize]
[Route("orders")]
public sealed class OrderCollectionsController(IOrderRepository repository) : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult<IReadOnlyList<Order>>> GetOrders(
        [FromQuery, Range(1, 100)] int limit = 10,
        [FromQuery, Range(0, 10000)] int offset = 0,
        CancellationToken cancellationToken = default)
    {
        var orders = await repository.ListAuthorizedAsync(limit, offset, User, cancellationToken);
        return Ok(orders);
    }
}

클라이언트는 https://api.contoso.com/orders?limit=30&offset=50을 사용하여 오프셋 50부터 30개의 주문을 요청할 수 있습니다.

앞의 예제에 표시된 요청 유효성 검사 외에 응답 형식에 다음 페이지 링크 또는 연속 토큰을 포함합니다. 예를 들어 다른 페이지를 사용할 수 있을 때 nextLink 속성이 https://api.contoso.com/orders?limit=30&offset=80로 설정된 주문을 반환합니다. 링크에서 필터, 정렬 순서 및 API 버전을 유지합니다. nextLink을 생략하거나 마지막 페이지에서 null로 설정합니다. next-link 계약의 예제는 Microsoft Graph 페이징을 참조하세요.

전체 컬렉션을 로드한 후가 아니라 데이터 저장소에 필터링 및 페이지 매김을 적용합니다. 결정적 정렬 순서를 사용합니다. 크거나 자주 변경되는 컬렉션의 경우 커서 기반 페이지 매김을 평가하여 비용이 많이 드는 오프셋과 불안정한 페이지 경계를 방지합니다.

팁

쿼리 복잡성, 필터 식 및 요청 크기에 문서화된 범위를 적용합니다. 요청 경로에서 모든 게이트웨이, 프록시 및 서버의 제한을 테스트합니다.

응답성, 확장성 및 가용성 유지 관리

API의 중요한 작업에 대한 대기 시간, 처리량 및 가용성 목표를 정의합니다. 예상 트래픽, 버스트 및 종속성 실패에 대비해 설계하고 테스트합니다. 스케일 아웃 및 무상태 처리는 이러한 목표를 지원합니다. 다음 구현 요구 사항을 고려하십시오.

오래 실행되는 요청에 대해 비동기 지원을 제공합니다.

일반 HTTP 요청보다 오래 지속되는 작업의 경우 요청의 유효성을 검사하고 요청을 인가하고 작업을 지속적으로 기록한 후 202(수락됨)를 반환합니다. 애플리케이션 다시 시작, 규모 감축 및 배포로 중단될 수 있습니다. 응답 후에 추적되지 않는 프로세스 내 작업이 완료될 것이라고 기대하지 마세요. 이러한 이벤트에도 불구하고 작업을 계속 실행해야 하는 경우 지속성 큐 및 작업자 또는 지속성 워크플로를 사용합니다. 전체 HTTP 의미 체계 및 구현 지침은 비동기 Request-Reply 패턴을 참조하세요.

호출자가 작업의 상태 및 결과를 검색하는 방법을 제공합니다. API는 처리가 완료되면 호출자에게 알릴 수도 있습니다.

클라이언트가 폴링하는 상태 리소스를 제공할 수 있습니다.

  1. 클라이언트가 요청을 제출합니다. 작업을 수락하기 전에 해당 입력값과 권한의 유효성을 검사합니다.
  2. Azure Table Storage 같은 지속성 스토리지에 작업 식별자와 상태를 영구 저장하고, 예를 들어 Azure Service Bus를 사용하여 별도의 프로세서가 처리할 작업을 큐에 추가합니다. 이 두 작업 사이에서 실패가 발생하더라도 수락된 작업이 손실되지 않도록 지속성 및 큐에 넣기를 조정합니다.
  3. 상태 엔드포인트를 Location 식별하는 헤더와 폴링 빈도를 제어하는 Retry-After 헤더가 있는 202를 반환합니다. 작업이 영구적으로 저장된 후에만 요청을 수락합니다.
  4. 작업자가 연산을 처리하고 지속성 상태를 업데이트합니다. 작업자가 동일한 작업을 두 번 이상 받을 수 있으므로 처리를 멱등적으로 만드세요. 일시적인 오류에 제한된 재시도를 적용합니다.
  5. 클라이언트는 상태 엔드포인트를 폴링합니다. 상태 및 결과 엔드포인트 모두에 리소스 수준 권한 부여를 적용합니다. 추측할 수 없는 식별자는 권한 부여가 아닙니다.
  6. 작업이 진행 중인 동안 문서화된 상태 값 객체를 반환합니다. 완료 시 결과 또는 결과 리소스로의 303(See Other) 리디렉션을 제공합니다. 일관되고 안전한 오류 처리 규약을 통해 오류를 보고합니다.
  7. 작업 레코드 및 결과에 대한 보존 및 정리를 정의합니다. 문서 작업 만료, 중복 제출 및 지원되는 취소 동작

연결된 클라이언트의 경우 완료 알림에 ASP.NET Core SignalR을 사용하는 것이 좋습니다. 클라이언트가 연결 끊기 또는 누락된 알림 후에 복구할 수 있도록 지속성 상태 리소스를 사용 가능한 상태로 유지합니다. 자세한 내용은 ASP.NET Core SignalR 개요를 참조하세요.

각 요청이 무상태임을 확인하세요.

이후 요청이 이전 요청과 동일한 애플리케이션 인스턴스에 도달하도록 요구하지 마세요. 워크플로 및 리소스 상태를 프로세스 메모리에만 있는 것이 아니라 적절한 공유 저장소에 유지합니다. 요청은 서버 인스턴스 종속성을 만들지 않고도 지속성 리소스 상태에 따라 달라질 수 있습니다. 무상태 요청 처리는 스케일 아웃을 지원합니다.

다운스트림 종속성 이해하기

각 다운스트림 호출에 대한 시간 제한을 설정합니다. 호출, 재시도 및 지연의 총 소요 시간이 API 요청의 기한을 넘지 않도록 하며 요청 취소를 다운스트림 작업에 전파합니다.

반복하기에 안전한 일시적 실패 및 작업만 다시 시도합니다. 백오프 및 지터를 적용한 제한된 재시도를 사용합니다. 요청당 시도뿐만 아니라 종속성당 집계 재시도를 제한합니다. 자세한 내용은 일시적인 오류 처리를 참조하세요.

반복된 종속성 오류가 API 용량을 소모할 수 있는 경우 회로 차단기를 사용합니다.

클라이언트를 추적하고 제한을 구현하여 DoS 공격 가능성을 줄입니다.

요청 속도 제한, 동시성, 페이로드 및 실행 시간 제한을 적용합니다. 가능한 경우 인증된 호출자 또는 테넌트별로 제한을 분할하세요. 호출자가 구성된 속도 제한을 초과하면 429(너무 많은 요청)를 반환하고 임시로 서비스를 사용할 수 없으면 503(서비스를 사용할 수 없음)을 반환합니다. 적절한 경우 Retry-After를 포함하세요. API가 다운스트림 종속성에 대한 호출을 다시 시도하면 오버로드를 증폭하지 않도록 해당 재시도에 제한을 둡니다.

게이트웨이 및 애플리케이션 제한은 서로 다른 목적을 가집니다. 게이트웨이 제한은 공유 용량을 보호할 수 있습니다. 애플리케이션 제한은 운영 비용 및 다운스트림 용량을 반영할 수 있습니다. 둘 다 네트워크 계층 DDoS 보호 또는 웹 애플리케이션 방화벽을 대체하지 않습니다. 자세한 내용은 제한 패턴을 참조하세요.

영구적인 HTTP 연결을 신중하게 관리합니다.

인바운드 연결의 경우 워크로드 및 네트워크 경로에 대해 서버 및 게이트웨이 연결 유지 시간 제한, 요청 시간 제한, 동시 연결 제한을 구성합니다. 각 응답 후에 연결을 닫도록 습관적으로 강제하지 마세요. ASP.NET Core 서버 설정에 대한 자세한 내용은 Kestrel 웹 서버에 대한 구성 옵션을 참조하세요.

API 자체가 다운스트림 HTTP 서비스를 호출하는 경우 해당 아웃바운드 연결을 재사용하세요. .NET API 구현에서 HttpClient이 적절한 장수명 PooledConnectionLifetime 인스턴스를 사용하거나 IHttpClientFactory로 생성된 클라이언트를 사용합니다. 더 이상 필요하지 않은 경우 아웃바운드 응답 메시지 및 스트림을 해제합니다. 이러한 설정은 들어오는 클라이언트 연결이 아니라 API의 다운스트림 호출에 적용됩니다. 자세한 내용 및 쿠키 처리 주의 사항은 HttpClient 사용에 대한 지침을 참조하세요.

Web API 게시 및 관리

운영 요구 사항을 충족하는 호스팅 환경에 API를 배포합니다. 다음과 같은 전략을 고려해 보세요.

  • HTTPS를 사용합니다. 보호된 엔드포인트에 대한 요청을 인증하고 각 리소스 및 작업에 대한 권한 부여를 적용합니다.
  • 수요가 변화함에 따라 응답 시간 약정을 충족하도록 호스팅 환경을 확장합니다.
  • API의 청구 모델에 필요한 경우에만 Meter를 요청합니다.
  • 트래픽을 규제하고 필요한 경우 클라이언트 할당량을 강제 적용합니다.
  • 규정 준수 및 인시던트 응답에 필요한 감사 이벤트를 기록하지만 자격 증명, 토큰 또는 중요한 요청 및 응답 본문을 기록하지는 않습니다.
  • 호스트 상태를 모니터링하고 비정상 호스트를 다시 시작하는 것과 같은 복구 조치를 정의합니다.

공유 관리 문제를 처리하고 유효성이 검사된 요청을 API에 전달하는 별도의 파사드를 사용하는 것이 좋습니다. 파사드는 다음과 같은 기능을 제공할 수 있습니다.

  • 여러 Web API에 대한 통합 지점 역할을 합니다.
  • 메시지를 변환하고 다양한 기술을 사용하여 빌드한 클라이언트의 통신 프로토콜을 변환합니다.
  • Web API를 호스팅하는 서버의 부하를 줄이기 위해 요청 및 응답을 캐시합니다.

API 계약 및 게이트웨이 구성을 버전 제어 하에 유지하고, 배포 파이프라인의 변경 내용의 유효성을 검사하고, 롤백 절차를 정의합니다. 호스팅, ID 관리, 네트워킹 및 거버넌스를 조직의 랜딩 존 접근 방식에 맞게 조정합니다. 자세한 내용은 Azure 랜딩 존이란?을 참조하세요.

Web API 테스트

단위 테스트를 사용하여 API의 논리를 검증하고, 통합 테스트를 사용하여 HTTP 동작을 확인합니다.

다음 확인 사항을 포함하세요:

  • 올바른 작업을 호출하는지 확인하기 위하여 모든 경로를 테스트합니다. 특히 예기치 않게 반환되는 HTTP 상태 코드 405(메서드 허용 안 됨)에 유의하세요. 이 상태 코드는 경로와 해당 경로로 디스패치할 수 있는 HTTP 메서드(GET, POST, PUT, DELETE) 간의 불일치를 나타낼 수 있습니다.

    각 경로에서 지원되지 않는 메서드를 사용하는 요청을 보내고 문서화된 405(메서드 허용 안 함) 응답 및 Allow 헤더를 확인합니다.

  • 모든 경로가 적절하게 보호되는지 적절한 인증 및 권한에 속하는지 확인합니다.

    참고

    사용자 인증과 같은 보안의 일부 측면은 웹 API가 아닌 호스트 환경의 책임이 될 수 있지만 배포 프로세스의 일부로 보안 테스트를 포함해야 합니다.

  • 각 작업에 의해 수행되는 예외 처리를 테스트한 후 적절하고 의미 있는 HTTP 응답이 클라이언트 애플리케이션에 반환되는지를 확인합니다.

  • 요청 및 응답 메시지가 올바르게 구성되었는지 확인합니다. 예를 들어 리소스를 만드는 POST 요청의 경우 역직렬화, 구문 분석, 리소스 만들기 및 응답의 Location 헤더를 확인합니다.

  • 지원되는 요청 및 지원되지 않는 요청 미디어 유형을 응답 콘텐츠 협상과 별도로 테스트합니다. 지원되지 않는 요청 형식에 대한 415(지원되지 않는 미디어 형식) 응답과 잘못된 본문에 대한 문서화된 유효성 검사 오류를 확인합니다.

  • 라우팅, 요청 유효성 검사 및 응답 스키마를 포함하여 지원되는 모든 API 버전을 테스트합니다. 생략, 잘못된 형식 및 지원되지 않는 버전 식별자를 테스트하고 캐시된 응답이 버전 경계를 넘지 않는지 확인합니다.

  • 응답 메시지의 모든 링크와 URI를 확인합니다. 예를 들어, HTTP POST 메시지는 새로 생성된 리소스의 URI를 반환해야 합니다. 모든 HATEOAS 링크는 유효해야 합니다.

  • 각 작업이 다양한 입력 조합에 대해 올바른 상태 코드를 반환하는지 확인합니다. 예를 들면 다음과 같습니다.

    • 쿼리가 성공하면 상태 코드 200(OK)를 반환해야 합니다.
    • 리소스를 찾을 수 없는 경우 작업은 HTTP 상태 코드 404(찾을 수 없음)를 반환해야 합니다.
    • 클라이언트가 요청을 전송하여 리소스 삭제를 완료한 경우, 상태 코드는 204(콘텐츠 없음)여야 합니다.
    • 클라이언트에서 새 리소스를 생성하는 요청을 보낸 경우 상태 코드는 201(생성됨)이어야 합니다.

    애플리케이션 또는 호스팅 계층에서 예기치 않은5xx 응답을 조사합니다.

  • 클라이언트 애플리케이션에서 지정할 수 있는 다양한 조합의 헤더 요청을 테스트하고 웹 API가 응답 메시지에 예상 정보를 반환하는지 확인합니다.

  • 쿼리 문자열을 테스트합니다. 동작에서 선택적 매개 변수(예: 페이지 매김 요청)를 사용할 수 있는 경우 다른 조합과 매개 변수 순서를 테스트합니다.

  • 비동기 작업이 완료되었는지 확인합니다. 스트리밍 응답의 경우 API가 전체 페이로드를 버퍼링하지 않고 클라이언트 연결 끊김 및 취소를 올바르게 처리하는지 확인합니다. API가 장기 실행 작업에 대한 상태 리소스를 제공하는 경우 작업 진행률 및 결과를 올바르게 보고하는지 확인합니다.

정확성에 영향을 주는 실패 사례 및 경계 사례를 테스트합니다.

  • 최소, 최대, 음수, 잘못된 형식 및 과도한 페이지 매김 및 일괄 처리 입력의 유효성을 검사합니다.
  • 마지막 페이지까지 페이지 매김 링크를 따릅니다. 링크가 쿼리 옵션을 유지하고 문서화된 완료 신호가 반환되는지 확인합니다.
  • 추가 입력 필드가 보호된 속성을 변경할 수 없고 필드 선택에서 권한이 없는 데이터를 노출할 수 없는지 확인합니다.
  • 일치하는, 일치하지 않는, 약한, 다중 및 와일드카드 ETag를 테스트하고 304 응답에 본문이 없는지 확인합니다.
  • 동시 업데이트를 연습하고 데이터 저장소가 오래된 쓰기를 원자적으로 거부하는지 확인합니다.
  • 호출자가 다른 호출자의 리소스, 상태 레코드 또는 캐시된 응답을 읽지 못하는지 확인합니다.
  • 다중 테넌트 API의 경우 위조되거나 일치하지 않는 테넌트 식별자를 테스트합니다. 캐싱을 사용하도록 설정하면 다른 테넌트에서 동일한 리소스 URI에 대한 요청을 테스트하세요.
  • 시간 제한, 클라이언트 취소, 종속성 오류, 작업자 다시 시작 및 백그라운드 작업의 중복 배달을 시뮬레이션합니다.
  • 다운스트림 시간 제한, 재시도 예산, 취소 및 회로 차단기 복구를 확인합니다.
  • 429(너무 많은 요청) 및 503(서비스를 사용할 수 없음) 응답, 재시도 동작 및 오버로드된 요청을 거부하는 비용을 확인합니다.

프로덕션과 유사한 인프라 및 실제 작업 조합에 대해 부하 테스트를 실행합니다. 처리량, 대기 시간 백분위수, 오류율 및 리소스 포화에 대한 통과 및 실패 조건을 정의합니다. Azure Load Testing 사용하고 회귀 검사를 배포 파이프라인에 통합합니다.

API Management 사용

정책 적용을 통해 관리되는 게이트웨이가 필요한 경우 Azure API Management 사용하는 것이 좋습니다. 비용과 네트워크 홉이 하나 더 추가되고, 요청 경로에 종속성이 생깁니다. 용량, 네트워킹, 가용성 및 지역 요구 사항에 따라 서비스 계층과 토폴로지를 선택하세요. 기능은 계층마다 다릅니다. 개발자 계층은 프로덕션 워크로드용이 아닙니다. API Management에 대한 Well-Architected 서비스 지침을 따르세요.

다음 단계를 완료하여 웹 API를 게시하고 관리할 수 있습니다.

  1. 웹 API를 Azure App Service, Azure Container Apps, AKS(Azure Kubernetes Service) 또는 Azure Virtual Machines 같은 호스팅 플랫폼에 배포합니다.

  2. 게이트웨이 URL에서 백 엔드 API로 요청을 라우팅하도록 API Management를 구성합니다. 하나의 API Management 인스턴스는 요청을 여러 API로 라우팅할 수 있습니다. 애플리케이션에 다른 액세스 경계가 필요한 경우 여러 인스턴스가 동일한 백 엔드 API를 노출할 수도 있습니다.

    참고

    응답의 HATEOAS 링크는 백 엔드 서버 URL이 아닌 API Management 게이트웨이 URL을 참조해야 합니다.

  3. 버전 관리되는 OpenAPI 정의를 가져오고 가져온 작업, 매개 변수, 보안 요구 사항 및 응답 스키마가 구현과 일치하는지 확인합니다. 지원되는 OpenAPI 버전 및 가져오기 제한 사항을 확인합니다. 자세한 내용은 OpenAPI 사양 가져오기를 참조하세요.

  4. 클라이언트-게이트웨이 및 게이트웨이-백 엔드 통신 모두에 대한 인증 및 권한 부여를 구성합니다. OAuth 2.0 액세스 토큰의 유효성을 검사하고 클라이언트 요청에 필요한 권한을 적용합니다. Microsoft Entra ID 인증을 지원하는 백 엔드의 경우 필요한 권한만 있는 API Management 관리 ID를 선호합니다. 인증서 인증은 또 다른 옵션입니다. 더 안전한 옵션을 사용할 수 있는 경우 기본 인증을 사용하지 마세요. 자세한 내용은 Azure API Management의 API에 대한 인증 및 권한 부여를 참조하세요.

    참고

    API Management 구독 키가 필요한 경우, 인증 및 리소스 수준 권한 부여를 대신하는 용도로 API Management 구독 키를 사용하지 마세요.

  5. 필요에 따라 인증, 교차 출처 액세스, 메시지 변환, IP 필터링, 속도 제한 및 할당량에 대한 정책을 구성합니다. 캐시에 안전한 데이터에 대해서만 응답 캐싱을 구성합니다. 적절한 범위에서 정책을 적용하고 각 작업 항목에 대한 효과적인 동작을 확인합니다.

자세한 내용은 API Management 설명서를 참조하세요.

Important

게이트웨이는 백 엔드를 비공개로 만들거나 리소스 수준 권한 부여를 대체하지 않습니다. 게이트웨이를 포함하여 워크로드 전체에 대한 백 엔드 액세스 제어, 오류 처리, 용량 및 관찰 가능성을 디자인합니다. 선택한 계층의 기능에 대해 영역 및 지역 중복성을 평가합니다. 비용 모델에 게이트웨이 요금, 원격 분석 수집 및 보존 및 데이터 전송을 포함합니다.

클라이언트 쪽 개발자 지원

API 소비자에게 인증, 오퍼레이션, 매개 변수, 데이터 형식 및 응답 코드에 대한 설명서를 제공합니다.

Web API에 대한 REST 작업 문서화

API의 작업, 매개 변수, 요청 및 응답 스키마 및 보안 요구 사항을 설명하는 OpenAPI 정의를 게시합니다. 구현과 함께 버전 제어를 유지하고 API의 실제 동작과 일치하는지 확인합니다.

API 참조에 소비자가 API를 올바르게 사용하기 위해 필요한 지침을 추가합니다.

  • 각 작업에 대한 인증 요구 사항 및 권한입니다.
  • 대표 요청, 성공적인 응답 및 오류 응답
  • 입력 유효성 검사 규칙, 페이로드 제한, 페이지 매김 및 속도 제한입니다.
  • 시간 제한 동작, 재시도 안전성 및 멱등성 요구 사항
  • 장기 실행 작업에 대한 상태 및 결과 검색입니다.
  • 버전 관리 및 호환성 기대 사항.

API 계약이 변경되면 설명서 및 예제를 최신 상태로 유지합니다.

클라이언트 SDK 구현

API 소비자에게 SDK를 제공하는 것이 좋습니다. SDK는 요청을 생성하고, 응답을 구문 분석하고, API의 문서화된 오류를 표시하는 타입이 지정된 메서드를 노출할 수 있습니다.

API의 OpenAPI 정의에서 형식화된 클라이언트 생성을 고려하되, 생성된 동작은 서비스에 대해 검증하세요. 문서 인증, 시간 제한, 취소, 페이지 매김, 오류 응답 및 다시 시도하기에 안전한 작업 클라이언트 및 API 호환성 정책이 일치하도록 유지합니다. 생성된 코드는 통합 테스트의 필요성을 제거하지 않습니다.

Web API 모니터링

API, 해당 종속성 및 게이트웨이를 함께 모니터링합니다. 호스트 상태에만 의존하지 않고 고객 영향을 기반으로 서비스 수준 목표 및 경고를 정의합니다.

Web API 직접 모니터링

OpenTelemetry 계측을 사용하여 추적, 메트릭 및 로그를 수집하고 Application Insights 또는 지원되는 다른 백 엔드로 신호를 내보냅니다. .NET, Node.js및 Python 계측의 경우 Microsoft OpenTelemetry 배포판 지침을 따릅니다. Java는 Azure Monitor OpenTelemetry Distro를 사용합니다. 언어별 OpenTelemetry 활성화 지침을 따릅니다.

API 동작을 설명하는 신호를 모니터링하세요:

  • 작업별 요청 볼륨, 실패율 및 대기 시간 백분위수
  • 종속성 대기 시간, 실패 및 시간 제한
  • 속도 제한으로 인한 거부, 동시 작업 및 리소스 포화
  • 백그라운드 작업 큐 경과 시간, 완료 시간, 실패 및 다시 시도
  • 캐싱이 디자인의 일부인 경우 캐시 효율성

모니터링 도구가 수집하는 원격 분석은 언어, 라이브러리 및 구성에 따라 자동으로 달라집니다. 도메인 작업 및 백그라운드 작업에 대한 계측을 추가합니다. 브라우저 세션, 사용자 역할 또는 모든 요청 세부 정보를 자동으로 사용할 수 있다고 가정하지 마세요. 게이트웨이, 애플리케이션 및 작업자 간에 추적 컨텍스트를 전파합니다. 진단 요구 사항, 데이터 민감도 및 원격 분석 비용의 균형을 맞추기 위해 샘플링 및 보존을 구성합니다.

적절한 경우 반복적인 외부 검사에 Application Insights 표준 가용성 테스트를 사용합니다. 중요한 데이터를 노출하지 않고 의미 있는 헬스 체크 또는 운영 엔드포인트를 확인하고, 실행 가능한 경고를 정의하고, 대응 런북을 유지 관리합니다.

API Management를 사용하여 웹 API 모니터링

API Management에서 지원하는 Azure Monitor 메트릭, 로그, 분석 및 진단을 사용하여 게이트웨이 오류를 백 엔드 오류와 구분합니다. 게이트웨이 및 애플리케이션 원격 분석을 상호 연관시키고 정책 오류, 용량, 요청 대기 시간 및 제한을 모니터링합니다. 사용 가능한 신호는 계층 및 게이트웨이 구성에 따라 달라집니다.

API Management에 대한Well-Architected 서비스 지침의 관찰 가능성 및 용량 권장 사항을 따릅니다.

다음 단계