API 변경

REST API 버저닝: URL, 헤더, 쿼리 중 무엇이 맞는가

4분 분량

REST API 버저닝은 요청이 어느 계약을 쓰는지 API가 호출자에게 알리는 방식이며, 그 덕분에 서버는 아직 옮기지 않은 호출자를 망가뜨리지 않고 계약을 바꿀 수 있다. 버전이 들어갈 자리는 세 곳이다. URL 경로(/v2/orders), 요청 헤더(Accept: application/vnd.acme.v2+json 또는 사용자 정의 Api-Version: 2026-10-01), 쿼리 파라미터(/orders?version=2). 경로가 가장 흔한 이유는 어디서나 보이기 때문이고, 헤더는 URL이 가장 깔끔하며, 쿼리 문자열은 가장 많은 팀이 후회하는 방식이다. 어느 것이 맞는지는 호출자가 누구인지, 변경을 어떻게 알릴 계획인지에 달려 있다.

버전이 있는 곳예시보이는 범위기본 캐시 가능 여부사용하는 곳
URL 경로GET /v2/orders모든 로그, 링크, 프록시, 브라우저가능Twitter, Shopify, 대부분의 공개 API
사용자 정의 헤더Api-Version: 2026-10-01헤더를 보내는 클라이언트만Vary: Api-Version과 함께Stripe(Stripe-Version), GitHub(X-GitHub-Api-Version)
Accept 헤더Accept: application/vnd.acme.v2+json헤더를 보내는 클라이언트만Vary: Accept와 함께GitHub 미디어 타입, 일부 엔터프라이즈 API
쿼리 파라미터GET /orders?api-version=2로그와 링크, 필터와 섞여서가능하나 캐시 키를 오염시킴Azure REST API
버전 없음GET /orders에 추가적 변경만아무도 못 봄가능내부 API와 GraphQL 식 API

REST API에서 v1과 v2란 무엇인가

v1과 v2는 API 계약의 두 버전을 가리키는 이름표다. 같은 리소스가 다른 경로로 열려 있고, v2는 v1 호출자가 의존하는 필드 이름, 타입, 엔드포인트, 오류 형태를 바꿀 수 있다. /v1/orders로 보낸 요청은 v1이 지원되는 동안 계속 예전 형태를 받고, /v2/orders로 보낸 요청은 새 형태를 받는다. 번호는 호환성에 대한 약속이고, 그 약속이 번호를 쓸모 있게 만든다. 릴리스마다 번호를 올리면 이름표는 소음이 된다.

대부분의 API에 새 메이저 버전이 필요한 경우는 기존 호출자를 망가뜨리는 변경뿐이다. 필드 추가, 새 엔드포인트, 새 선택적 파라미터는 현재 버전 아래서 출시할 수 있다. 잘 짜인 클라이언트는 모르는 것을 무시하기 때문이다. API를 언제 버전으로 관리하고 대신 무엇을 할지는 그 결정을 다루고, 이 글은 올리기로 정한 뒤 REST API 버저닝에서 버전을 어디에 둘지에 관한 것이다.

REST API 버저닝은 URL에 두어야 하는가, 헤더에 두어야 하는가

호출자 중에 브라우저에 요청을 직접 치는 사람, 문서의 예시를 복사하는 사람, 로그를 읽는 사람이 있다면 URL에 두고, 리소스마다 안정된 URL 하나를 원하고 호출자가 전부 SDK이거나 직접 관리하는 코드라면 헤더에 두어라. URL 경로는 다른 무엇도 따라올 수 없을 만큼 명시적이다. 요청 줄이 들어 있는 지원 티켓은 그 자체로 버전을 알려준다. 헤더는 요청마다 설정해야 하고, 링크에서는 보이지 않으며, 프록시에서 잃어버리기 쉽다. 그래서 헤더로 버전을 관리하는 API는 언제나 헤더를 보내지 않는 요청을 위한 기본값을 정의한다.

헤더 방식에는 깔끔한 URL 말고도 진짜 장점이 하나 있다. 리소스의 정체성과 표현을 분리하는데, HTTP가 바로 그렇게 설계되었다. RFC 9110은 Accept를 통한 콘텐츠 협상을 설명하며, application/vnd.github.v3+json 같은 미디어 타입은 그 방식으로 표현한 버전이다. 실무에서는 설계의 순수성보다 호출자가 헤더를 보내는 것을 기억하는지가 더 중요하다.

날짜 기반 버저닝은 어떻게 동작하는가

v1과 v2 대신 버전이 날짜이고, 파괴적 변경마다 새 날짜가 붙는다. 호출자는 자기가 연동한 날짜를 보통 계정 수준에서 한 번 고정하고, 스스로 옮기기로 할 때까지 그 동작을 유지한다. Stripe의 Stripe-Version 헤더와 GitHub의 X-GitHub-Api-Version이 모두 이렇게 동작한다.

대가는 구현에 있다. 모든 파괴적 변경이 고정된 날짜와 현재 형태 사이를 변환하는 작은 변환 계층이 되고, 서버는 그것들을 쌓는다. 2년 전 날짜에 고정된 요청은 그 이후의 모든 변경을 거친다. 핸들러를 한 벌 더 복사하는 것보다 만들기 무겁고, 변경이 드문 API에 유리하다. 매달 파괴적 변경을 내보내는 팀은 수십 개의 변환을 떠안게 된다. Stripe API 버저닝은 체인지로그가 무엇을 담아야 하는지를 포함해 이 모델을 호출자 쪽에서 살펴본다.

쿼리 문자열 버저닝은 왜 권장되지 않는가

계약을 필터와 섞기 때문이다. /orders?status=open&api-version=2는 버전을 응답의 모든 필드 형태를 결정하는 것이 아니라 선택적 세부 조건처럼 보이게 한다. 실수로 빠뜨리기도 가장 쉽다. 쿼리 문자열은 클라이언트 라이브러리가 다시 조립하고, 일부 프록시가 떼어내고, 사람이 손으로 고치기 때문이다. Microsoft의 REST API 설계 지침은 Azure가 쓰는 쿼리 파라미터 형식을 문서화하고 있으며, 그것이 돌아가는 이유는 주변의 생성된 SDK 생태계다. 그 도구 없이는 파라미터를 잊은 호출자가 조용히 기본 버전을 받는다.

예전 버전은 어떻게 되는가

종료되는 날짜를 달고 계속 돌아간다. 버저닝의 목적은 호출자가 자기 일정대로 옮기게 하는 것이고, 그것은 예전 버전이 그러기에 충분히 오래 남아 있고 언제 끝나는지 알려줄 때만 참이다. Google의 API 버저닝 지침은 자체 API에 쓰는 수명 주기를 설명한다. 안정 버전은 공개된 비추천 날짜까지 지원되고, 대체 버전은 비추천 기간 내내 사용할 수 있다.

대부분의 팀이 덜 만드는 부분은 공지다. 새 경로나 헤더는 그것이 있다는 것, 무엇이 바뀌었는지, v1이 언제 사라지는지 아는 호출자에게만 쓸모가 있다. 사실이 셋이고, 각각 자리가 필요하다. 필드 수준 차이를 담은 API 체인지로그의 날짜 있는 항목, 종료일을 밝히는 비추천 공지, 예전 버전에 남은 호출자를 변경 하나하나 안내하는 마이그레이션 가이드다. API 버전 종료는 클라이언트가 모든 응답에서 날짜를 알아낼 수 있게 하는 Sunset 헤더를 포함해 마지막 단계를 다룬다.

REST API 버저닝을 아예 피할 수 있는가

모든 변경이 추가적인 동안은 그렇다. 필드는 더하되 지우거나 이름을 바꾸지 않고, 엔드포인트는 더하되 기존 것이 돌려주는 것을 바꾸지 않으며, 새 선택적 파라미터는 받되 옛 파라미터의 의미는 바꾸지 않는다. 많은 내부 API가 이 규칙만으로 몇 년을 돌아가고, GraphQL 스키마는 설계상 이 규칙을 따른다. 대가는 커지기만 하는 계약이며, 어딘가의 누군가가 아직 읽고 있어서 남겨야 하는 비추천 필드가 쌓인다.

추가적 변경이 불가능할 때 버전이 도구다. 경로, 헤더, 날짜 사이의 선택은 호출자가 무엇을 보고 변경이 어떻게 공지되는지를 결정한다. 변하지 않는 것은 변경을 호출자가 읽을 어딘가에 적어야 한다는 사실이다. 외부 호출자가 있는 API에서 그 자리는 체인지로그이고, 무엇이 파괴적 변경으로 치는지는 파괴적 변경 글이 다룬다.

FAQ

REST API 버전을 관리하는 가장 좋은 방법은 무엇인가? URL 경로의 메이저 버전이 가장 널리 이해되고 가장 틀리기 어려워서 공개 API의 기본값이 된다. 헤더 버전은 URL을 안정적으로 유지하며 SDK 중심 API에 맞고, 날짜 기반 헤더는 변경이 드물고 호출자가 동작을 고정하기를 바라는 API에 맞는다.

API URL의 v1은 무엇인가? API 계약의 첫 메이저 버전이다. /v1/...로 보낸 요청은 다른 필드나 동작을 가진 /v2/가 생긴 뒤에도 버전 1에 문서화된 형태를 약속받는다.

모든 변경에 새 API 버전이 필요한가? 아니다. 새 필드, 엔드포인트, 선택적 파라미터 같은 추가적 변경은 기존 버전 아래서 출시할 수 있다. 기존 호출자를 망가뜨리는 변경만 새 버전이 필요하다.

예전 API 버전은 얼마나 오래 지원해야 하는가? 호출자가 이전하기에 충분할 만큼이며, 공개 API라면 보통 대체 버전이 나온 뒤 6개월에서 12개월이다. 날짜는 공지 때 공개하고 비추천 공지에서 다시 알린다.


이 글의 기술적 내용은 독립적으로 검토되지 않았습니다. 잘못된 부분이 있으면 알려 주세요. 바로잡겠습니다.

changeloop 관련 페이지: 개발자 문서, changelog 예시

changeloop
루프를 닫는 changelog를 만드는 팀입니다. 사용자가 무언가를 요청하면 팀이 전달하고, 요청한 사람은 알게 됩니다.