[REST API 진화] Day 4: 하위 호환성과 버전 관리 - 클라이언트를 깨뜨리지 않기
[REST API 진화] Day 4: 하위 호환성과 버전 관리 - 클라이언트를 깨뜨리지 않기
이 글은 AI(Claude)의 도움을 받아 작성하고, 작성자가 검토·편집했습니다.
서론: 서버 배포가 끝나도 클라이언트는 남아 있다
모바일 앱, 외부 파트너, 다른 팀 서비스는 서버와 동시에 배포되지 않는다. 이미 배포된 클라이언트가 수개월간 남을 수 있어 API 변경은 코드 리팩터링과 다르다. 먼저 비파괴 변경으로 진화하고, 정말 필요할 때만 새 버전을 만든다.
1. 일반적으로 안전한 변경과 위험한 변경
1
2
3
4
5
6
7
8
9
10
대체로 안전:
응답에 optional 필드 추가
새 endpoint·새 enum 입력 옵션 추가
더 구체적인 문서와 예시
위험:
필드 제거·이름/타입 변경
required 필드 추가
상태 코드·오류 code 의미 변경
기존 enum 응답 값 추가도 엄격한 클라이언트에는 위험 가능
클라이언트가 알 수 없는 필드를 무시하고 enum의 unknown 값을 처리하도록 SDK 지침을 제공한다.
2. Tolerant Reader와 명시적 Writer
1
2
3
4
5
응답을 읽을 때:
모르는 필드는 무시, 필요한 필드만 의존
요청을 쓸 때:
계약에 정의된 필드만 전송, 오타·미지원 필드는 오류
서버가 요청의 알 수 없는 필드를 조용히 무시하면 클라이언트 오타를 성공으로 착각할 수 있다. 읽기와 쓰기의 관용 정책은 다를 수 있다.
3. 버전 위치 선택
1
2
3
URI: /v2/orders
Header: Accept: application/vnd.example.v2+json
Query: ?api-version=2
어느 방식도 모든 문제를 해결하지 않는다. 게이트웨이·캐시·문서·SDK가 식별하기 쉬운 방식과 조직의 운영 능력을 기준으로 하나를 일관되게 사용한다.
4. 버전은 복사본이 아니다
v2를 만들 때 Controller부터 DB까지 전체를 복제하면 버그 수정과 보안 패치를 두 군데에 적용해야 한다.
1
2
3
v1 Adapter ─┐
├→ 공통 Use Case/Domain
v2 Adapter ─┘
버전별 차이는 외부 DTO와 변환 계층에 두고 가능한 한 업무 로직을 공유한다. 정책 자체가 달라졌다면 명시적으로 버전별 Policy를 둔다.
5. Expand-Contract API 변경
1
2
3
4
5
1. 새 필드/새 endpoint 추가
2. 서버가 구·신 계약 모두 지원
3. 클라이언트 사용량 관찰·이관
4. 폐기 공지와 deadline
5. 트래픽 0과 승인 확인 후 제거
DB 마이그레이션처럼 API도 확장 후 수축한다. 사용량 측정 없이 오래된 계약을 제거하지 않는다.
6. 폐기 계약
1
2
3
4
5
공지에 포함:
대체 API와 migration guide
영향받는 기능·클라이언트
종료 날짜와 지원 창구
sandbox/테스트 방법
응답 헤더·개발자 포털·담당자 알림을 함께 사용하고, 종료 전에 경고 로그와 대시보드로 남은 호출자를 찾는다.
7. Day 4 체크리스트
- 필드·타입·상태 코드·enum 변경의 호환성 위험을 분류했다.
- 응답은 tolerant reader, 요청은 명시적 검증 원칙을 정했다.
- 버전 식별 방식을 게이트웨이·캐시·SDK와 일관되게 선택했다.
- 버전 Adapter가 공통 Use Case를 재사용하게 했다.
- Expand-Contract와 사용량 기반 폐기 절차를 마련했다.
다음 편 예고
계약을 지속적으로 지키려면 문서와 구현을 자동으로 비교해야 한다. 마지막 Day 5에서는 OpenAPI·계약 테스트·SLO·API 거버넌스로 시리즈를 완성한다.
This post is licensed under CC BY 4.0 by the author.