요약하자면
URL 버전 관리(/v1/pets)는 대부분의 팀에게 가장 실용적인 API 버전 관리 전략입니다. 이는 가시적이고, 캐시 가능하며, 테스트하기 쉽습니다. 헤더 버전 관리와 콘텐츠 협상은 더 "순수한" REST 방식이지만 복잡성을 추가합니다. Modern PetstoreAPI는 시맨틱 버저닝과 명확한 사용 중단 정책을 사용하여 URL 버전 관리를 사용합니다.
서론
API에 호환성을 깨뜨리는 변경 사항이 필요합니다. /pets에 대한 응답 형식을 단순 배열에서 페이지네이션 메타데이터를 포함하는 래핑된 객체로 변경하고 있습니다. 기존 클라이언트는 작동을 멈출 것입니다. 어떻게 해야 할까요?
API 버전 관리가 필요합니다. 하지만 어떤 전략을 사용할까요? URL 버전 관리(/v1/pets 대 /v2/pets)? 헤더 버전 관리(Accept: application/vnd.petstore.v1+json)? 콘텐츠 협상? 각 접근 방식에는 열정적인 지지자와 강력한 의견이 있습니다.
답변: URL 버전 관리는 대부분의 팀에게 가장 유리합니다. 실용적이고, 가시적이며, 모든 HTTP 툴링과 잘 작동합니다. 헤더 버전 관리와 콘텐츠 협상은 이론적으로는 더 깔끔하지만, 대부분의 팀에게 불필요한 복잡성을 추가합니다.
Modern PetstoreAPI는 시맨틱 버저닝과 명확한 사용 중단 정책을 사용하여 URL 버전 관리를 사용합니다. 현재 버전은 v1이며, 향후 호환성을 깨뜨리는 변경을 위해 v2가 계획되어 있습니다.
이 가이드에서는 세 가지 주요 버전 관리 전략, 장단점, 그리고 Modern PetstoreAPI를 참고하여 버전 관리를 올바르게 구현하는 방법을 배웁니다.
API에 버전 관리가 필요한 이유
API는 진화합니다. 기능을 추가하고, 버그를 수정하며, 설계를 개선합니다. 때로는 이러한 변경 사항이 기존 클라이언트를 손상시킬 수 있습니다.
호환성을 깨뜨리는 변경 사항
기존 클라이언트를 손상시키는 변경 사항:
1. 필드 제거:
// v1
{"id": "123", "name": "Fluffy", "age": 3}
// v2 (호환성을 깨뜨림: age 필드 제거)
{"id": "123", "name": "Fluffy"}
2. 필드 유형 변경:
// v1
{"price": "19.99"}
// v2 (호환성을 깨뜨림: 문자열에서 숫자로 변경)
{"price": 19.99}
3. 응답 구조 변경:
// v1 (단순 배열)
[{"id": "123"}]
// v2 (호환성을 깨뜨림: 래핑된 객체)
{"data": [{"id": "123"}], "pagination": {...}}
4. URL 구조 변경:
// v1
GET /pet/123
// v2 (호환성을 깨뜨림: 복수형)
GET /pets/123
5. 인증 변경:
// v1: 쿼리의 API 키
GET /pets?api_key=xxx
// v2 (호환성을 깨뜨림: Bearer 토큰)
GET /pets
Authorization: Bearer xxx
호환성을 유지하는 변경 사항
클라이언트를 손상시키지 않는 변경 사항:
- 새 엔드포인트 추가
- 요청에 선택적 필드 추가
- 응답에 새 필드 추가 (클라이언트는 알 수 없는 필드를 무시해야 함)
- 새 쿼리 매개변수 추가
- 기존 리소스에 새 HTTP 메서드 추가
버전 관리 결정
호환성을 깨뜨리는 변경이 필요할 때, 두 가지 선택지가 있습니다:
1. 모든 클라이언트를 강제로 업그레이드 - 간단하지만 기존 통합을 손상시킴
2. 여러 버전 지원 - 더 많은 작업이 필요하지만 하위 호환성을 유지함
대부분의 공개 API는 2번 옵션을 선택합니다. 버전 관리는 API를 발전시키면서 클라이언트에게 마이그레이션 시간을 제공합니다.
URL 버전 관리
URL 버전 관리는 URL 경로에 버전 번호를 넣는 방식입니다.
작동 방식
GET /v1/pets
GET /v2/pets
버전은 리소스 식별자의 일부입니다. 다른 버전은 다른 리소스입니다.
장점
1. 가시적이고 명시적
버전은 URL에 있습니다. 로그, 브라우저 기록 및 문서에서 볼 수 있습니다. 기억할 숨겨진 헤더가 없습니다.
2. 테스트하기 쉬움
curl https://petstoreapi.com/v1/pets
curl https://petstoreapi.com/v2/pets
두 버전을 간단한 HTTP 요청으로 테스트할 수 있습니다.
3. 모든 HTTP 툴링과 호환
브라우저, 캐시, 프록시 및 로드 밸런서는 다른 URL을 인식합니다. 각 버전을 독립적으로 라우팅, 캐싱 및 로깅할 수 있습니다.
4. 클라이언트에게 간단함
클라이언트는 URL만 변경하면 됩니다. 사용자 정의 헤더나 콘텐츠 협상 로직이 필요 없습니다.
5. 사용 중단 용이
/v1 엔드포인트를 /v2에 영향을 주지 않고 제거할 수 있습니다.
단점
1. "순수한" REST가 아님
REST 순수주의자들은 /v1/pets/123와 /v2/pets/123가 동일한 리소스이므로 동일한 URL을 가져야 한다고 주장합니다. 버전은 헤더 또는 콘텐츠 협상에 있어야 합니다.
2. URL 오염
API에는 여러 URL 공간이 있습니다: /v1/*, /v2/* 등.
3. 개별 리소스 버전 관리의 어려움
하나의 엔드포인트만 버전 관리하려면 전체 API를 버전 관리하거나 불일치를 만들어야 합니다.
구현
URL에 주요 버전:
/v1/pets
/v2/pets
마이너 버전은 포함하지 마세요:
❌ /v1.2/pets (너무 세분화됨)
✅ /v1/pets (주요 버전만)
내부적으로 시맨틱 버저닝 사용:
- v1.0.0 - 초기 릴리스
- v1.1.0 - 새 필드 추가 (호환성 유지)
- v1.2.0 - 새 엔드포인트 추가 (호환성 유지)
- v2.0.0 - 호환성을 깨뜨리는 변경 (새 URL: /v2)
Modern PetstoreAPI는 /v1을 현재 버전으로 사용하여 URL 버전 관리를 사용합니다.
헤더 버전 관리
헤더 버전 관리는 사용자 정의 HTTP 헤더에 버전 번호를 넣는 방식입니다.
작동 방식
GET /pets
API-Version: 1
GET /pets
API-Version: 2
URL은 동일하게 유지됩니다. 헤더가 버전을 지정합니다.
장점
1. 깔끔한 URL
/pets는 모든 버전에서 동일합니다. /v1 또는 /v2 접두사가 없습니다.
2. 더 "RESTful"함
리소스 식별자(/pets/123)는 변경되지 않습니다. 헤더에 따라 표현이 변경됩니다.
3. 세분화된 버전 관리
개별 리소스에 대해 버전 관리를 할 수 있습니다:
GET /pets
API-Version: 2
GET /orders
API-Version: 1
단점
1. 비가시적
버전이 URL에 없습니다. 헤더를 확인하지 않으면 로그나 브라우저 기록에서 볼 수 없습니다.
2. 테스트하기 어려움
curl -H "API-Version: 1" https://petstoreapi.com/pets
curl -H "API-Version: 2" https://petstoreapi.com/pets
헤더를 포함하는 것을 기억해야 합니다.
3. 캐싱 복잡성
캐시는 API-Version 헤더를 고려해야 합니다. 응답에 Vary: API-Version이 필요합니다.
4. 클라이언트 복잡성
클라이언트는 사용자 정의 헤더 로직이 필요합니다. 모든 HTTP 클라이언트가 이를 쉽게 만들지는 않습니다.
5. 기본 버전 모호성
클라이언트가 헤더를 보내지 않으면 어떻게 될까요? 암묵적인 동작을 생성하는 기본값이 필요합니다.
구현
사용자 정의 헤더:
API-Version: 1
또는 Accept 헤더 사용:
Accept: application/vnd.petstore.v1+json
Vary 헤더 포함:
Vary: API-Version
이는 캐시에게 캐싱 시 헤더를 고려하도록 지시합니다.
콘텐츠 협상
콘텐츠 협상은 사용자 정의 미디어 타입을 가진 Accept 헤더를 사용합니다.
작동 방식
GET /pets
Accept: application/vnd.petstore.v1+json
GET /pets
Accept: application/vnd.petstore.v2+json
버전은 미디어 타입의 일부입니다.
장점
1. 가장 "RESTful"함
이것이 REST가 설계된 방식입니다. 동일한 리소스의 다른 표현입니다.
2. HTTP 표준 준수
표준 HTTP 콘텐츠 협상을 사용합니다.
3. 여러 형식 지원
버전과 형식을 동시에 지정할 수 있습니다:
Accept: application/vnd.petstore.v1+json
Accept: application/vnd.petstore.v1+xml
단점
1. 복잡함
클라이언트는 미디어 타입과 콘텐츠 협상을 이해해야 합니다.
2. 테스트하기 어려움
curl -H "Accept: application/vnd.petstore.v1+json" https://petstoreapi.com/pets
3. 툴링 지원 부족
많은 HTTP 클라이언트 및 도구가 사용자 정의 미디어 타입을 잘 처리하지 못합니다.
4. 캐싱 복잡성
캐시는 Accept 헤더를 고려해야 합니다. Vary: Accept가 필요합니다.
5. 대부분의 API에 과도함
대부분의 API는 이 정도의 정교함이 필요하지 않습니다.
구현
벤더별 미디어 타입:
Accept: application/vnd.petstore.v1+json
응답:
Content-Type: application/vnd.petstore.v1+json
Vary: Accept
Modern PetstoreAPI는 버전 관리를 어떻게 구현하는가
Modern PetstoreAPI는 명확한 정책과 함께 URL 버전 관리를 사용합니다.
현재 버전: v1
https://petstoreapi.com/v1/pets
https://petstoreapi.com/v1/orders
https://petstoreapi.com/v1/users
모든 엔드포인트는 /v1 아래에 있습니다.
버전 응답 헤더
모든 응답에는 API 버전이 포함됩니다:
X-API-Version: 1.2.0
이는 URL에 주요 버전만 표시되더라도 정확한 버전(major.minor.patch)을 보여줍니다.
사용 중단 경고
버전이 사용 중단될 때, 응답에는 다음이 포함됩니다:
Deprecation: true
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Link: <https://docs.petstoreapi.com/migration/v1-to-v2>; rel="deprecation"
Deprecation- 버전이 사용 중단되었음을 나타냅니다.Sunset- 버전이 제거될 시점입니다.Link- 마이그레이션 가이드
버전 발견
루트 엔드포인트는 사용 가능한 버전을 나열합니다:
GET https://petstoreapi.com/
{
"versions": [
{
"version": "v1",
"status": "current",
"docsUrl": "https://docs.petstoreapi.com/v1"
}
]
}
시맨틱 버저닝
Modern PetstoreAPI는 내부적으로 시맨틱 버저닝을 따릅니다:
- 주요 버전 (v1, v2) - 호환성을 깨뜨리는 변경, 새 URL
- 마이너 버전 (v1.1, v1.2) - 새 기능, 하위 호환성 유지
- 패치 버전 (v1.1.1, v1.1.2) - 버그 수정, 하위 호환성 유지
URL에는 주요 버전만 표시됩니다.
Apidog로 API 버전 테스트하기
Apidog는 여러 API 버전을 테스트하는 데 도움이 됩니다.
여러 버전 가져오기
각 버전에 대한 OpenAPI 사양을 가져옵니다:
petstore-v1.yaml → 환경: v1
petstore-v2.yaml → 환경: v2
모든 버전에 대해 테스트 실행
두 버전 모두에 대해 실행되는 테스트 스위트를 생성합니다:
// v1 테스트
pm.environment.set("baseUrl", "https://petstoreapi.com/v1");
pm.sendRequest(pm.environment.get("baseUrl") + "/pets");
// v2 테스트
pm.environment.set("baseUrl", "https://petstoreapi.com/v2");
pm.sendRequest(pm.environment.get("baseUrl") + "/pets");
버전별 동작 검증
v1과 v2가 다르게 동작하는지 테스트합니다:
// v1은 단순 배열을 반환합니다.
pm.test("v1 returns array", function() {
pm.expect(pm.response.json()).to.be.an('array');
});
// v2는 래핑된 객체를 반환합니다.
pm.test("v2 returns wrapped object", function() {
pm.expect(pm.response.json()).to.have.property('data');
pm.expect(pm.response.json()).to.have.property('pagination');
});
사용 중단 헤더 확인
사용 중단된 버전이 적절한 헤더를 포함하는지 테스트합니다:
pm.test("사용 중단된 버전에는 헤더가 포함됩니다.", function() {
pm.response.to.have.header("Deprecation");
pm.response.to.have.header("Sunset");
});
버전 사용 중단 전략
클라이언트를 손상시키지 않고 이전 버전을 사용 중단하는 방법.
1. 사용 중단 조기 공지
클라이언트에게 최소 6~12개월 전에 공지합니다:
Deprecation: true
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
2. 마이그레이션 가이드 제공
모든 호환성을 깨뜨리는 변경 사항과 마이그레이션 방법을 문서화합니다:
Link: <https://docs.petstoreapi.com/migration/v1-to-v2>; rel="deprecation"
3. 사용량 모니터링
어떤 클라이언트가 아직 사용 중단된 버전을 사용하는지 추적합니다:
X-API-Version: 1.2.0
X-Client-ID: abc123
필요한 경우 클라이언트에 직접 연락합니다.
4. 점진적 종료
버전을 즉시 제거하지 마세요:
- 1-6개월: 사용 중단 공지
- 7-9개월: 사용 중단 헤더 추가
- 10-11개월: 사용 중단된 버전에 대한 속도 제한 감소
- 12개월: 사용 중단된 버전 제거
5. 문서 유지
제거된 후에도 이전 버전에 대한 문서를 유지하세요. 클라이언트가 이를 참조해야 할 수도 있습니다.
결론
URL 버전 관리는 대부분의 팀에게 가장 실용적인 API 버전 관리 전략입니다. 이는 가시적이고, 테스트하기 쉬우며, 모든 HTTP 툴링과 호환됩니다. 헤더 버전 관리와 콘텐츠 협상은 더 "순수한" REST 방식이지만 복잡성을 추가합니다.
Modern PetstoreAPI는 /v1을 현재 버전으로 사용하여 URL 버전 관리, 내부적으로 시맨틱 버저닝, 그리고 명확한 사용 중단 정책을 사용합니다. 이 접근 방식은 실용성과 좋은 API 설계를 균형 있게 유지합니다.
Apidog를 사용하여 여러 API 버전을 테스트하고, 버전별 동작을 검증하며, 버전 간 원활한 마이그레이션을 보장하세요.
자주 묻는 질문
URL 버전 관리를 사용해야 할까요, 아니면 헤더 버전 관리를 사용해야 할까요?
특별한 이유가 없는 한 URL 버전 관리를 사용하세요. 더 간단하고, 가시적이며, 테스트하기 쉽습니다. 헤더 버전 관리는 더 "RESTful"하지만, 대부분의 팀에게 불필요한 복잡성을 추가합니다.
동시에 몇 개의 버전을 지원해야 할까요?
최대 2개 버전(현재 버전과 이전 버전)을 지원하세요. 더 많은 버전을 지원하면 유지 보수 부담이 커집니다. 클라이언트에게 마이그레이션할 시간을 6~12개월 주고, 그 후에 이전 버전을 제거하세요.
v0부터 버전을 시작해야 할까요, v1부터 시작해야 할까요?
v1부터 시작하세요. v0은 불안정성을 의미합니다. API가 v1을 출시할 만큼 충분히 안정적이지 않다면 아직 공개하지 마세요.
모든 엔드포인트를 버전 관리해야 할까요?
아니요. 호환성을 깨뜨리는 변경을 할 때만 버전 관리를 하세요. 기존 엔드포인트를 변경하지 않고 새 엔드포인트를 추가하는 경우 새 버전이 필요하지 않습니다.
URL에 마이너 버전은 어떻게 하나요?
URL에 마이너 버전을 포함하지 마세요. /v1.2 대신 /v1을 사용하세요. 마이너 버전은 하위 호환되므로 클라이언트가 URL을 변경할 필요가 없습니다.
버전별 버그는 어떻게 처리하나요?
지원되는 모든 버전에서 버그를 수정하세요. 버그가 v1에만 존재한다면 v1에서 수정하세요. 버그 수정을 위해 클라이언트에게 v2로 업그레이드하도록 강요하지 마세요.
시맨틱 버저닝을 사용해야 할까요?
네, 내부적으로는 그렇습니다. major.minor.patch 버전을 추적하되, URL에는 주요 버전만 노출하세요. 이는 호환성을 깨뜨리지 않는 변경에 대한 유연성을 제공합니다.
하나의 엔드포인트만 버전 관리해야 한다면 어떻게 해야 할까요?
URL 버전 관리를 사용하면 전체 API를 버전 관리하거나 불일치를 만들어야 합니다. 이는 트레이드오프입니다. 대부분의 팀은 단순성을 위해 전체 API를 버전 관리하는 것을 받아들입니다.
