"API는 반드시 변해. 선택지는 변화를 관리할지, 아니면 어느 날 기존 클라이언트를 뜻밖에 깨뜨릴지 둘 중 하나야. 첫 외부 계약을 내놓기 전에 진화 전략부터 정해 둬."
버전 관리가 해결하는 문제
API에는 필드 이름, 오류 형식, 상태 코드, 기본값 같은 결정이 계속 쌓여. 시간이 지나면 잘못된 선택을 발견하기도 하고, 시장과 제품 요구가 달라지기도 해. 문제는 이미 수많은 클라이언트가 기존 동작에 의존하고 있다는 점이야. 버전 관리는 기존 계약을 유지하면서 호환되지 않는 새 동작을 별도의 계약으로 제공하는 방법이야.
핵심은 버전을 요청의 어디에 둘지 정하고, 라우팅·문서·로그·캐시·지원 중단 정책까지 같은 전략으로 일관되게 운영하는 거야. URL 경로, 사용자 정의 헤더, 미디어 타입, 쿼리 매개변수 모두 가능하지만 운영 비용은 서로 달라.
네 가지 전략 — 각각 언제 맞을까
1. URL 경로 버전 관리. https://api.example.com/v1/users와 /v2/users처럼 경로에 버전을 넣는 방식이야. 여러 공개 API에서 널리 사용해.
- 장점: 로그와 curl 명령만 봐도 버전이 드러나고, 프레임워크 라우팅과 문서화가 단순하며 캐시 키도 명확해.
- 단점: 같은 논리적 리소스의 URI가 버전마다 달라지고, 버전별 라우트와 링크를 함께 유지해야 해.
- 결론: 이해하기 쉽고 운영 도구의 지원도 좋아, 공개 API에서 가장 무난한 기본 선택이야.
2. 사용자 정의 헤더 버전 관리. X-API-Version: 2처럼 별도 요청 헤더로 버전을 고르는 방식이야. 일부 기업용 API에서 사용해.
- 장점: 버전이 바뀌어도 URI를 유지할 수 있고, 리소스 식별과 표현 동작을 분리하기 좋아.
- 단점: 헤더를 수집하지 않는 로그에서는 버전 정보가 사라져. CDN이 버전별 응답을 구분하려면
Vary: X-API-Version도 필요하고, 브라우저 주소창이나 링크만 봐서는 버전을 알 수 없어. - 결론: 충분히 사용할 수 있지만 관찰 가능성과 캐시 설정에 추가 운영 비용이 들어.
3. 미디어 타입 버전 관리. Accept: application/vnd.example.v2+json처럼 Accept의 미디어 타입에 버전을 포함하는 방식이야. 일부 하이퍼미디어 API에서 볼 수 있어.
- 장점: 버전을 리소스 표현의 일부로 다루므로 콘텐츠 협상 모델과 잘 맞아.
- 단점: 브라우저, OpenAPI 도구, curl을 사용하는 사람이 긴 미디어 타입을 매번 다뤄야 해. Accept에 따라 응답이 달라지므로 Vary 설정도 필요해.
- 결론: 의미론적으로 깔끔하지만 도구와 운영 경험이 불편해 실무에서는 비교적 드물어.
4. 쿼리 매개변수 버전 관리. /users?version=2처럼 쿼리 값으로 버전을 선택하는 방식이야.
- 장점: 기존 경로를 유지한 채 애플리케이션 분기 로직으로 쉽게 도입할 수 있어.
- 단점: 버전 정보가 다른 필터·페이지 매개변수와 섞이고, 일부 CDN이나 클라이언트가 쿼리 문자열을 정규화하거나 캐시 키에서 제외하면 버전별 응답이 충돌할 수 있어. 문서와 링크에서도 계약 경계가 덜 선명해.
- 결론: 금지할 방식은 아니지만, 캐시와 도구 설정을 통제하기 어렵다면 URL 경로가 대체로 더 단순해.
날짜 기반 버전 관리 — Stripe 방식
Stripe는 경로와 날짜 기반 버전을 함께 사용해. 경로는 /v1/charges를 유지하고, 요청별 헤더 Stripe-Version: 2024-09-30.acacia로 API 동작의 특정 스냅샷을 선택해. 계정이나 요청이 통합 당시의 버전에 머물고, 클라이언트가 명시적으로 업그레이드할 때 새 동작으로 이동하는 방식이야.
날짜 기반 버전은 서로 다른 시점에 나온 변경을 v2라는 한 번의 대규모 묶음으로 모을 필요가 없다는 장점이 있어. 대신 서버는 여러 동작 스냅샷을 동시에 유지하고, 각 버전의 차이를 문서화하며, 업그레이드 경로를 제공해야 해. 호환성 보호가 강한 만큼 내부 구현 비용도 큰 전략이야.
추가형 변경과 호환성을 깨는 변경
추가형 변경은 많은 경우 새 버전 없이 배포할 수 있지만, 안전성은 기존 계약과 클라이언트의 견고성에 달려 있어.
- 응답에 새 필드 추가 → 알 수 없는 필드를 무시하도록 약속된 클라이언트라면 대체로 안전해. 엄격한 역직렬화기는 깨질 수 있어.
- 새 선택 사항 쿼리 매개변수 추가 → 기존 클라이언트가 보내지 않으므로 기존 동작을 유지하기 쉬워.
- 새 엔드포인트 추가 → 기존 클라이언트가 호출하지 않으므로 보통 기존 계약에 영향을 주지 않아.
- 새 상태 코드나 오류 경우 추가 → 상태 코드 계열을 먼저 처리하는 클라이언트라면 견딜 수 있지만, 개별 코드만 열거한 클라이언트는 실패할 수 있어.
다음과 같은 호환성을 깨는 변경에는 새 버전이나 명시적인 전환 절차가 필요해.
- 필드를 제거하거나 이름을 바꾸면 그 필드를 읽는 클라이언트가 실패해.
- 필드의 타입이나 형식을 바꾸면 기존 파서와 검증 로직이 깨질 수 있어.
- 검증 규칙을 강화하면 이전에 허용되던 페이로드가 422로 거부될 수 있어.
- 선택 사항 매개변수의 기본값을 바꾸면 요청 형식은 같아도 결과 동작이 달라져.
운영 API는 가능한 한 추가형으로 진화하면서 호환 기간을 늘려. 그래도 새 동작이 기존 계약과 공존할 수 없다면, 그때 버전 경계를 만들고 지원 중단 계획까지 함께 제시해야 해.
지원 중단 — 예고하고 떠나보내기
이전 버전을 은퇴시킬 때는 응답 헤더와 문서로 기계와 사람 모두에게 일정을 알려야 해. 대표적인 헤더는 두 가지야.
Deprecation: Sun, 01 Jan 2026 00:00:00 GMT— 이 HTTP-date 표기는 이전 예시일 뿐 RFC 9745 문법이 아니야. 현재 Deprecation 헤더는 Structured Field Date를 사용하므로 실제 값은 Deprecation: @1767225600처럼 Unix 시간을 @ 뒤에 적어야 해.Sunset: Wed, 01 Jul 2026 00:00:00 GMT(RFC 8594) — 이 응답을 제공하는 리소스가 언제 더 이상 제공되지 않을 예정인지 알려줘.
헤더만 보내고 끝내면 부족해. 변경 기록, 대체 버전, 마이그레이션 절차, 지원 종료 일정을 함께 문서화해야 해. 헤더는 자동화에 신호를 주고, 전환 가이드는 실제 변경을 수행할 사람에게 길을 보여줘.
cwkPippa의 현재 선택
/v1/ 같은 명시적 경계와 지원 중단 정책이 필요해지겠지만, 그전에는 버전 표면을 유지하는 비용이 얻는 보호보다 클 수 있어.