하위 호환성
Backward Compatibility
개념
새 기능을 더하면서도 예전 방식으로 부르던 쪽의 코드가 손대지 않고 그대로 돌아가게 유지하는 성질입니다.
등장 배경
API 는 프로그램끼리 주고받기로 약속한 창구입니다. 창구를 열어 두면 남이 그 규격에 맞춰 자기 코드를 짜고, 그 코드는 배포되는 순간 우리 손을 떠납니다. 상대 회사 서버에서 돌고, 이미 팔린 휴대폰 안에서 돌고, 담당자가 퇴사한 뒤에도 계속 돕니다.
그 상태에서 창구의 모양을 바꾸면 무슨 일이 생기는지가 문제였습니다. 금요일 저녁에 아무도 안 쓸 것 같은 칸 하나를 응답에서 지우고 배포합니다. 월요일 아침에 연동사 세 곳에서 전화가 옵니다. 그쪽은 코드를 한 줄도 안 고쳤는데 결제가 멈췄고, 끊긴 시점은 우리가 배포한 금요일 저녁입니다. 이런 변경을 브레이킹 체인지라고 부릅니다.
한두 번 겪으면 붙는 쪽이 계산을 바꿉니다. 언제 바뀔지 모르는 창구에 회사의 결제 흐름을 얹지 않습니다. 붙는 사람이 줄면 창구를 열어 둔 이유 자체가 사라집니다.
하위 호환성은 그래서 기능이 아니라 지키기로 한 성질입니다. 옛 방식으로 부르던 쪽은 계속 돌게 두고, 새 기능은 그 위에 얹어 필요한 쪽만 쓰게 합니다.
작동 방식
빼지 않고 더하는 쪽으로만 바꾸면 지켜집니다. 규칙 다섯 가지로 정리됩니다.
- 칸은 더하기만 합니다. 응답에
nickname을 새로 넣는 것은 안전합니다. 모르는 칸이 하나 는 것을 옛 코드는 그냥 지나칩니다. 반대로name을 빼면 그 칸을 읽던 코드가 멈춥니다. - 이름을 바꾸지 않습니다.
owner를ownerId로 고치는 것은 정리처럼 보이지만 받는 쪽에게는 칸 하나가 사라지고 낯선 칸이 생긴 일입니다. 새 이름을 더하고 옛 이름도 한동안 같이 내보냅니다. - 새 입력을 필수로 만들지 않습니다. 요청에
currency를 새로 받기로 했다면 안 보낸 요청은 원화로 처리하는 식으로 기본값을 둡니다. 필수로 걸면 어제까지 잘 돌던 요청이 오늘부터 거절당합니다. - 이미 나가던 값의 뜻을 바꾸지 않습니다. 상태 칸이
ok를 내보내다가success로 바뀌면 칸 이름은 그대로라도ok를 비교하던 조건문이 전부 빗나갑니다. 상태 코드처럼 숫자로 약속한 값도 같습니다. - 받는 쪽도 모르는 칸을 무시하도록 짭니다. 그래야 보내는 쪽이 칸을 더해도 깨지지 않습니다. JSON처럼 칸 이름으로 값을 찾는 형식이 널리 쓰이는 이유입니다.
이사한 뒤 우체국에 신청해 두는 주소 이전 서비스와 같은 구조입니다. 새 집으로 옮기는 것은 내 사정이고, 옛 주소로 보낸 편지도 한동안은 도착해야 합니다. 그 기간이 끝나기 전에 옛 주소를 아는 사람들에게 미리 알리는 일이 디프리케이션입니다.
쓰이는 곳
한번 내보낸 뒤 상대를 내 마음대로 고칠 수 없는 자리에는 어디든 걸립니다. 네 군데가 대표적입니다.
- 공개 API. REST처럼 주소로 자원을 다루는 방식이든 GraphQL처럼 필요한 칸만 골라 받는 방식이든 같습니다. 웹훅은 우리가 상대 주소로 먼저 보내는 구조라 더 까다롭습니다. 내용이 바뀌면 받는 쪽이 조용히 틀린 값을 쓰게 됩니다.
- 남이 끌어다 쓰는 라이브러리. 시맨틱 버저닝은 이 성질을 버전 번호로 드러냅니다. 2.4.1 에서 2.5.0 으로 오르면 기능이 더해졌다는 뜻이고, 3.0.0 으로 오르면 하위 호환이 깨졌으니 고쳐야 한다는 신호입니다.
- 저장해 둔 데이터. 데이터베이스 표에서 칸을 지우는 배포는 잠깐이지만 위험합니다. 배포가 도는 몇 분 동안 새 코드와 옛 코드가 같은 표를 함께 쓰기 때문에, 칸을 더하는 배포와 지우는 배포를 몇 주 떼어 놓습니다.
- 이미 설치된 앱. 가장 아픈 자리입니다. 서버는 오늘 바꿀 수 있지만 휴대폰의 앱은 그 사람이 열어야 갱신됩니다. 옛 버전이 전체의 12퍼센트라면 그 12퍼센트가 계속 옛 규격으로 서버를 부릅니다.
지키는 기간은 영원이 아닙니다. 옛 칸을 읽어 간 호출이 지난 30일 동안 하루 평균 몇 건이었는지를 보고, 그 수가 0 에 가까워지면 그때 뺍니다.
실무 용례
"이 변경 하위 호환 되나요? 안 되면 버전을 따로 파야 합니다."
지금 방식대로 부르던 쪽이 깨지는지 먼저 확인하자는 질문입니다.
"칸을 지우지 말고 새로 하나 더 만들죠. 옛 칸은 6개월 뒤에 빼고요."
한 번에 바꾸는 대신 두 칸을 같이 내보내며 옮겨 갈 시간을 주자는 제안입니다.
"구버전 앱이 아직 12퍼센트라 이번 분기에는 못 뺍니다."
기술적으로는 지울 수 있지만 그 12퍼센트가 오늘 멈추므로 미루자는 뜻입니다.
페이지덱에서는 태오가 문서 목록 응답의 owner 칸을 ownerId 로 바꾸자고 제안한 적이 있습니다. 값의 뜻이 사람 이름이 아니라 번호라 이름이 틀렸다는 게 이유였습니다. 지현이 그 칸을 읽어 가는 쪽을 세어 보니 기업 고객 네 곳이 붙어 있었습니다.
결론은 두 칸을 같이 내보내는 쪽이었습니다. ownerId 를 추가하고 owner 는 그대로 두되 문서에 곧 없앨 예정이라고 적었습니다. 네 곳에 메일을 보내고 6개월 뒤를 마감일로 잡았습니다. 마감일에 옛 칸을 읽는 호출은 하루 두 건이었고, 그 주인을 찾아 연락한 뒤에 뺐습니다.
흔한 오해
하위 호환성을 지키라는 말을 아무것도 바꾸지 말라는 말로 듣는 경우가 가장 많습니다. 깨뜨리지 말라는 요구가 손대지 말라는 요구처럼 들려서 생깁니다. 실제로 막히는 것은 빼기와 이름 바꾸기뿐이고 더하기는 얼마든지 할 수 있습니다. 이 오해가 남으면 팀은 둘 중 하나로 갑니다. 개선을 포기하고 규격을 10년째 그대로 두거나, 아무에게도 알리지 않고 조용히 바꾼 뒤 사고가 나기를 기다립니다.
응답의 칸 이름만 챙기면 된다는 오해도 흔합니다. 문서에 적어 둔 것이 칸 목록이니 계약도 거기까지라고 생각하기 쉽습니다. 그런데 붙은 쪽은 문서에 없는 것에도 기댑니다. 목록이 늘 최신순으로 오던 순서, 실패했을 때 돌아오던 오류 번호, 한 번에 최대 100건까지 오던 개수 같은 것들입니다. 순서를 이름순으로 바꾼 배포가 아무 칸도 건드리지 않고 상대 화면을 뒤집어 놓는 일은 그래서 생깁니다. 무엇이 계약인지는 우리가 적은 범위가 아니라 상대가 실제로 기대고 있는 것이 정합니다.
자주 헷갈리는 용어
| 용어 | 무엇을 가리키는가 | 하위 호환성과의 차이 |
|---|---|---|
| API 버저닝 | 규격이 바뀔 때 옛 판과 새 판을 나란히 두는 방법 | 하위 호환을 지킬 수 없을 때 꺼내는 수단입니다. 하위 호환성은 지키려는 성질이고 버저닝은 못 지킨 뒤의 대처라, 버전을 새로 파는 일이 잦다면 앞쪽을 놓치고 있다는 신호입니다 |
같이 보면 좋은 페이지
마지막 업데이트 2026-09-16