멱등성과 버전 관리가 적용된 REST API 설계 방법

Backend2026-09-17TryQuickToolBox

새로운 기능을 배포했는데, 갑자기 API가 타임아웃 후 재시도하는 클라이언트로부터 중복 POST 요청을 받기 시작합니다. 데이터베이스에는 이제 동일한 주문이 두 개 있습니다. 또는 호환성을 깨뜨리는 변경을 배포했는데, 모바일 앱이 이전 응답 형식을 기대하다가 충돌합니다. 이는 API 설계에서 가장 흔하고 고통스러운 두 가지 문제입니다: 비멱등성 작업과 호환성 깨짐입니다. 이 가이드는 REST API의 멱등성과 버전 관리를 위한 실용적인 패턴으로 두 문제를 모두 해결하는 방법을 보여줍니다.

REST API에서 멱등성이 중요한 이유

멱등성이란 동일한 요청을 여러 번 해도 한 번 한 것과 같은 효과를 갖는 것을 의미합니다. HTTP 메서드는 정의된 멱등성 의미를 가집니다: GET, HEAD, PUT, DELETE, OPTIONS는 멱등성이 있고, POST와 PATCH는 그렇지 않습니다. 그러나 실제 API에서는 리소스 생성이나 결제 처리 같은 작업에 POST를 받아들여야 하는 경우가 많습니다. 네트워크 장애, 클라이언트 타임아웃, 재시도 로직은 중복을 일으킬 수 있습니다. 멱등성이 없으면 이중 청구, 중복 레코드, 일관성 없는 상태의 위험이 있습니다.

멱등성 REST API를 위한 기법

1. POST 요청에 멱등성 키 사용

가장 일반적인 접근 방식은 클라이언트가 Idempotency-Key 헤더에 고유 키(예: UUID)를 보내도록 하는 것입니다. 서버는 키와 작업 결과를 저장합니다. 동일한 키가 다시 수신되면 서버는 작업을 재실행하지 않고 저장된 결과를 반환합니다.

POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "amount": 1000,
  "currency": "USD"
}

구현 단계:

  1. 클라이언트는 각 논리적 작업에 대해 고유 키(UUID v4)를 생성합니다.
  2. 서버는 영구 저장소(예: Redis 또는 데이터베이스)에 키가 존재하는지 확인합니다.
  3. 없으면 요청을 처리하고 응답 상태와 본문과 함께 키를 저장합니다.
  4. 있으면 재처리 없이 저장된 응답을 반환합니다.

무한한 저장소 증가를 피하기 위해 합리적인 만료 시간(예: 24시간)을 설정하세요.

2. 조건부 요청 활용

업데이트의 경우 ETag와 If-Match 헤더를 사용하여 업데이트 손실을 방지합니다. 서버는 현재 상태를 나타내는 ETag를 반환합니다. 클라이언트는 If-Match와 함께 이를 다시 보냅니다. 리소스가 변경되었으면 서버는 412 Precondition Failed를 반환합니다. 이는 PUT 요청을 경쟁 조건으로부터 안전하게 만듭니다.

PUT /articles/123
If-Match: "abc123"
Content-Type: application/json

{"title": "Updated title"}

3. 자연스러운 멱등성을 위해 PUT 설계

클라이언트가 리소스 URL을 결정할 수 있을 때는 POST보다 PUT을 선호하세요. 예를 들어 PUT /users/{userId}는 반복 호출이 동일한 리소스를 덮어쓰기 때문에 자연스럽게 멱등성이 있습니다. 서버가 ID를 할당할 때만 POST를 사용하세요.

4. 고유 제약 조건으로 중복 감지 처리

멱등성 키를 비즈니스 키(예: 주문 번호)에 대한 데이터베이스 고유 제약 조건과 결합하세요. 중복이 통과하면 데이터베이스가 이를 거부하고, 명확한 메시지와 함께 409 Conflict를 반환할 수 있습니다.

REST API의 버전 관리 전략

API는 진화합니다. 새로운 필드, 변경된 동작, 제거된 엔드포인트는 기존 클라이언트를 깨뜨릴 수 있습니다. 버전 관리를 통해 클라이언트를 방해하지 않고 변경 사항을 도입할 수 있습니다. 몇 가지 일반적인 전략이 있습니다:

전략 예시 장점 단점
URI 경로 /v1/users 명시적, 라우팅 쉬움, 캐시 친화적 URL 변경, 복잡해질 수 있음
쿼리 매개변수 /users?version=1 간단, 선택적 누락 쉬움, RESTful하지 않음
사용자 정의 헤더 Accept-Version: v1 URL 깔끔하게 유지 브라우저에서 테스트 어려움
미디어 타입 Accept: application/vnd.api.v1+json 콘텐츠 협상, 순수 REST 복잡, 덜 일반적

대부분의 팀에게 URI 경로 버전 관리가 가장 실용적인 선택입니다. 가시적이고, 문서화하기 쉬우며, API 게이트웨이와 CDN과 잘 작동합니다.

클라이언트를 깨뜨리지 않고 버전 관리하는 방법

  1. 처음부터 경로에 v1로 시작하세요. 나중에 버전 관리를 추가하는 것은 더 어렵습니다.
  2. 버전 내에서는 추가적 변경만 하세요: 새로운 선택적 필드, 새로운 엔드포인트. 필드를 제거하거나 이름을 바꾸지 마세요.
  3. 우아하게 폐기하세요: 지원 종료를 알리고, 마이그레이션 가이드를 제공하며, 이전 버전의 사용을 모니터링하세요.
  4. Sunset 헤더 사용: Sunset: Sat, 31 Dec 2025 23:59:59 GMT로 클라이언트에 알리세요.
  5. 유지 관리 부담을 제한하기 위해 최대 두 개의 활성 버전만 유지하세요.

종합하기: 실용적인 예시

결제 API를 고려해 보세요. 결제를 멱등적으로 생성하고 엔드포인트를 버전 관리하려고 합니다.

POST /v1/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "amount": 1000,
  "currency": "USD"
}

서버 로직:

업데이트의 경우 ETag와 함께 PUT을 사용하세요:

PUT /v1/payments/123
If-Match: "xyz789"
Content-Type: application/json

{"amount": 1500}

ETag가 일치하지 않으면 412를 반환합니다. 이는 동시 변경 사항을 덮어쓰는 것을 방지합니다.

일반적인 함정과 피하는 방법

FAQ

멱등성 REST API란 무엇인가요?

멱등성 REST API는 동일한 요청을 여러 번 해도 한 번 한 것과 같은 결과를 생성하도록 보장합니다. 이는 특히 POST와 같은 비멱등성 메서드의 재시도를 안전하게 처리하는 데 중요합니다.

어떤 HTTP 메서드가 멱등성이 있나요?

GET, HEAD, PUT, DELETE, OPTIONS, TRACE는 멱등성이 있습니다. POST와 PATCH는 기본적으로 멱등성이 없지만, 멱등성 키 같은 기법을 사용하여 멱등성 있게 만들 수 있습니다.

REST API를 버전 관리하는 가장 좋은 방법은 무엇인가요?

URI 경로 버전 관리(예: /v1/users)가 가장 일반적이고 실용적인 접근 방식입니다. 명시적이고, 라우팅하기 쉬우며, 캐싱과 API 게이트웨이와 잘 작동합니다. 다른 옵션으로는 쿼리 매개변수, 사용자 정의 헤더, 미디어 타입이 있지만 각각 장단점이 있습니다.

API의 JSON 응답을 테스트할 준비가 되셨나요? JSON Formatter를 사용하여 페이로드를 빠르게 검증하고 예쁘게 출력하세요.