API Backward Compatibility (안정성, 기술부채, 버전관리)
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
저는 초반에 Backward Compatibility가 그냥 "이전 버전이랑 잘 맞춰주는 것" 정도라고만 알고 있었습니다. 그 안에 이렇게 많은 설계 판단과 트레이드오프가 숨어 있는 줄은, 직접 실무에서 API를 운영하면서 깨달았습니다. 이 글은 그 과정에서 겪은 시행착오와 제 나름의 결론을 풀어낸 이야기입니다.
안정성 — 기존 사용자를 지키는 설계
제가 처음으로 Backward Compatibility(하위 호환성)의 중요성을 체감한 건, 모바일 앱 연동 API를 수정했다가 구버전 앱 사용자들이 단체로 오류를 쏟아냈을 때였습니다. 하위 호환성이란, 서버를 변경했더라도 기존 클라이언트가 수정 없이 그대로 동작할 수 있도록 보장하는 설계 방식입니다. 그 사건 이후로 저는 API 변경 전에 반드시 "지금 이 변경이 기존 클라이언트를 깨뜨리는가?"를 먼저 확인하는 습관이 생겼습니다.
모바일 앱은 웹과 달리 사용자가 직접 업데이트를 해야 합니다. 구버전 앱을 여전히 쓰는 사람이 전체의 30~40%를 차지하는 경우도 적지 않습니다. 이런 환경에서 서버 응답 구조를 갑자기 바꾸면, 아무 잘못도 없는 사용자들이 그냥 앱을 못 쓰게 됩니다. 제가 직접 겪어봤는데, 그 민원 대응만으로 하루가 날아갔습니다.
다행히 이 문제를 피하는 방법은 상대적으로 명확합니다. 기존 응답 구조를 유지하면서 새 필드를 추가하는 방식으로만 확장하면, 구버전 클라이언트는 새 필드를 무시하고 기존 방식대로 동작합니다. 이것이 하위 호환 변경(non-breaking change)의 핵심 원칙입니다. 반대로 기존 필드를 삭제하거나 타입을 바꾸는 것은 파괴적 변경(breaking change), 즉 클라이언트를 즉시 망가뜨리는 변경입니다.
REST API 설계 원칙을 정리한 REST API 버저닝 가이드(RESTful API)에서도 이 구분은 API 설계의 기본 중 기본으로 다뤄집니다. 저도 이 원칙을 팀 내에서 공유한 뒤로는 "일단 배포하고 보자" 식의 변경이 눈에 띄게 줄었습니다.
기술부채 — 안정성을 지키다 쌓이는 구조의 무게
그런데 솔직히 말하면, Backward Compatibility를 잘 지킨다고 해서 모든 게 해결되는 건 아니었습니다. 오히려 몇 년이 지나자 다른 문제가 생겼습니다. API 응답에 아무도 안 쓰는 레거시 필드가 20개 넘게 붙어 있는 거였습니다. 제 경험상 이건 꽤 흔한 상황입니다. 기술 부채(Technical Debt)란 단기적인 편의를 위해 내린 설계 결정이 장기적으로 누적되어 유지보수 비용이 높아지는 현상을 말합니다.
문제는 필드 하나를 지우거나 구조를 바꾸려 해도 "혹시 쓰는 클라이언트가 있을까봐" 손을 못 댄다는 겁니다. 그러다 보니 새 기능을 추가할 때도 깔끔하게 설계하지 못하고 기존 구조에 억지로 끼워 넣는 식이 됩니다. 이걸 API 드리프트(API Drift)라고 부르기도 하는데, 설계 의도와 실제 구현 사이의 간격이 점점 벌어지는 현상입니다.
제가 그 상황에서 팀과 함께 정리한 기준이 있습니다. 변경 유형을 명확히 나누는 것이었습니다.
- 하위 호환 변경(Non-breaking Change): 새 필드 추가, 선택적 파라미터 추가, 새 엔드포인트 추가 — 기존 클라이언트에 영향 없음
- 파괴적 변경(Breaking Change): 필드 삭제, 필드명 변경, 응답 타입 변경, 필수 파라미터 추가 — 기존 클라이언트를 즉시 깨뜨림
- 지연 제거(Deprecated + Sunset): 일정 기간 경고 후 단계적으로 제거 — 이행 기간을 두는 방식
이 기준을 잡고 나서야, 아무도 안 쓰는 필드를 정리하는 작업이 가능해졌습니다. Deprecated 정책, 즉 "이 필드는 다음 메이저 버전에서 제거됩니다"라고 클라이언트에 미리 알리는 방식을 병행하면, 기술 부채를 쌓지 않으면서도 호환성을 유지하는 균형을 어느 정도 잡을 수 있습니다. 시맨틱 버저닝(Semantic Versioning) 공식 문서에서도 이 세 가지 변경 유형의 구분을 버전 번호 체계의 기반으로 삼고 있습니다.
버전관리 — 균형을 잡는 실전 전략
결국 Backward Compatibility 문제는 API 버전 관리(API Versioning) 전략으로 귀결됩니다. API 버전 관리란 동일한 기능에 대해 여러 버전을 동시에 운영하거나, 새 버전으로의 전환을 관리하는 방식입니다. 처음엔 저도 "그냥 v1, v2 나누면 되는 거 아닌가?" 싶었는데, 실제로 해보면 그게 전혀 단순하지 않습니다.
v1을 유지하면서 v2를 동시에 운영하면 서버 쪽 유지보수 비용이 두 배가 됩니다. 버그를 고쳐도 두 버전 모두 반영해야 하고, 인프라 비용도 늘어납니다. 그렇다고 v1을 너무 빨리 종료하면 아직 마이그레이션을 못 한 클라이언트들이 피해를 봅니다. 이 지점이 설계자로서 가장 판단이 어려운 부분이었습니다.
제 경험에서 가장 효과적이었던 접근은 "변경의 크기에 따라 전략을 다르게 가져가는 것"이었습니다. 작은 개선은 하위 호환 방식으로 기존 버전 안에서 처리하고, 구조적으로 대규모 개편이 필요한 경우에만 새 버전을 열었습니다. 그리고 이행 기간을 충분히 두고 Deprecated 헤더로 클라이언트에 미리 신호를 줬습니다. 이렇게 하니 클라이언트 팀과의 충돌도 줄고, 레거시 코드가 무한정 쌓이는 것도 막을 수 있었습니다.
테스트 전략도 중요합니다. 저는 계약 기반 테스트(Contract Testing)를 도입한 뒤로, 서버 변경이 기존 클라이언트를 깨뜨리는지 배포 전에 자동으로 확인할 수 있게 됐습니다. 계약 기반 테스트란 서버와 클라이언트 사이에 맺은 API 명세를 기준으로, 양쪽 모두 그 명세를 준수하는지 자동으로 검증하는 방식입니다. 이 테스트 없이 대규모 API를 운영하는 건 솔직히 이건 예상 밖의 리스크를 매번 안고 가는 셈이었습니다.
Backward Compatibility는 "지키냐 마냐"의 이분법으로 접근할 문제가 아닙니다. 기존 사용자를 보호하는 안정성과, 잘못된 설계를 고칠 수 있는 유연성 사이에서 의식적으로 균형을 찾아야 합니다. 처음부터 확장성을 고려한 설계, 변경 유형의 명확한 분류, 그리고 Deprecated 정책과 버전 관리를 함께 운영하는 것이 제가 실무에서 찾은 가장 현실적인 답이었습니다. API를 처음 설계하는 단계에서 이 기준을 잡아두는 것, 나중에 기술 부채를 쌓고 나서 수습하는 것보다 훨씬 비용이 적게 든다는 걸 저는 직접 겪어보고 나서야 알게 됐습니다.
관련 글
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
댓글
댓글 쓰기