Post

[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 체크리스트

  1. 필드·타입·상태 코드·enum 변경의 호환성 위험을 분류했다.
  2. 응답은 tolerant reader, 요청은 명시적 검증 원칙을 정했다.
  3. 버전 식별 방식을 게이트웨이·캐시·SDK와 일관되게 선택했다.
  4. 버전 Adapter가 공통 Use Case를 재사용하게 했다.
  5. Expand-Contract와 사용량 기반 폐기 절차를 마련했다.

다음 편 예고

계약을 지속적으로 지키려면 문서와 구현을 자동으로 비교해야 한다. 마지막 Day 5에서는 OpenAPI·계약 테스트·SLO·API 거버넌스로 시리즈를 완성한다.

This post is licensed under CC BY 4.0 by the author.