API 체인지로그: 무엇을 공개하고 누가 읽는가
5분 분량 업데이트
API 체인지로그란 호출자가 알아챌 수 있는 모든 변경 사항을 날짜순으로 기록한 것으로, 이를 출시하는 팀이 아니라 그 API와 통합하는 사람들을 위해 작성된다. 이 독자층이야말로 그것을 제품 체인지로그와는 다른 문서로 만든다. 독자는 자신의 코드가 다음 달에도 계속 작동할지를 판단하고 있는 것이다. 대부분의 API 체인지로그는 같은 방식으로 실패한다. 내부 릴리스 피드를 걸러낸 사본에 지나지 않아서, 제거된 필드가 문구 수정과 똑같은 무게로 나란히 놓이고, 둘 다 읽히지 않는다.
API 체인지로그란 무엇인가
다른 사람들이 그것에 대해 코드를 작성한 인터페이스의 변경 사항을 담은, 공개되고 날짜가 적힌 기록이다. 무언가가 여기에 속하는지 판단하는 유용한 테스트는 그 변경이 내부적으로 얼마나 컸는지와는 아무 상관이 없다. 이 테스트는 작년에 작성되고 그 이후로 손대지 않은 올바른 호출자가 그것 때문에 다르게 동작할 수 있는지를 묻는다. 이 테스트는 아주 작은 변경 일부는 받아들이고 아주 큰 변경 일부는 제외한다.
아래의 모든 내용은 호출자가 회사 밖에 있고 이 문서 외에는 사실상 닿을 수 없다고 가정한다. 호출자가 같은 회사의 다른 팀일 때는 계산이 충분히 달라져서 그 자체로 다뤄질 가치가 있다. 내부용 API 체인지로그는 그 독자에게 대신 무엇이 필요한지를 다룬다.
| 문서 | 독자 | 답하는 질문 |
|---|---|---|
| API 체인지로그 | API를 호출하는 개발자 | 내 통합이 아직도 작동하는가? |
| 릴리스 노트 | 제품 사용자 | 이제 예전에 못 하던 무엇을 할 수 있는가? |
| 비추천 공지 | 특정 한 가지를 호출하는 사람 | 이것은 언제 작동을 멈추는가? |
| 상태 페이지 | 지금 영향을 받는 모두 | 지금 서비스가 다운되었는가? |
| 마이그레이션 가이드 | 업그레이드하는 호출자 | A에서 B로 어떻게 옮기는가? |
API 마이그레이션 가이드는 어떻게 작성하는가가 이 마지막 문서를 완전히 다룬다. 짧게 말하면, 이것은 호환되지 않는 변경 항목이 대체하려 하는 대신 링크해야 하는 것이다.
이 다섯 가지는 각기 다른 수명 주기를 가진 별개의 문서다. 비추천 공지는 날짜가 적힌 약속이며 체인지로그에도 속하지만, 체인지로그 항목은 한 번 작성되는 반면 비추천은 서비스 종료까지 추적된다. 이 둘을 뒤섞는 것이 서비스 종료를 놓치는 이유다.
하나의 항목에는 무엇이 들어가야 하는가
여섯 가지가 있고, 처음 세 가지가 보통 빠져 있는 것들이다. 내부 컴포넌트가 아니라 요청이나 응답의 관점에서 표현된 변경 사항. 올바른 호출자를 깨뜨리는지 여부. 호출자가 해야 할 일, “아무것도 없음”을 포함해서. 발효된 날짜. 영향을 받는 버전 또는 버전들. 존재한다면 마이그레이션 가이드로의 링크.
“accounts 엔드포인트 개선됨”이라고 말하는 항목은 여섯 가지 모두에서 실패한다. “accounts.type 필드가 이전에 personal을 반환하던 곳에서 이제 individual을 반환한다. 9월 2일 이전에 생성된 계정은 기존 값이 변경되지 않는다. 문자열을 비교하지 않는 한 조치가 필요하지 않다”라고 말하는 항목은 한 문장으로 여섯 가지 모두에 답한다.
부서가 아니라 결과에 따라 항목을 분류하라. 세 가지 라벨이 거의 모든 가치를 담는다. breaking, additive, fixed다. Semantic Versioning은 이미 처음 두 가지를 정확하게 정의하고 있으며, 독자적인 정의를 만들어내는 대신 그 정의를 빌려오는 것은 semver를 아는 독자가 여러분의 라벨을 이해한다는 뜻이다. Keep a Changelog는 원한다면 더 긴 세트를 제공하며, 그 핵심 규칙은 다른 어디서보다 여기서 더 강하게 적용된다. 로그는 사람을 위한 것이고, 커밋 제목 덤프는 그렇지 않다.
API 체인지로그는 릴리스 노트와 어떻게 다른가
릴리스 노트는 제품이 이제 무엇을 할 수 있는지 설명한다. API 체인지로그는 계약이 이제 무엇인지 설명한다. 동일한 출시 작업이 흔히 두 문서 모두에 항목을 만들어내지만, 서로 다르게 표현된다. 독자층이 필요로 하는 것이 다르기 때문이다. 새로운 내보내기 형식은 사용자에게는 기능이고, 그 필드로 분기하는 호출자에게는 새로운 enum 값이다.
실질적인 결과는 이 둘이 스타일만 다른 같은 피드가 될 수 없다는 것이다. 여러분이 출시하는 모든 것을 구독하는 호출자는 결국 구독을 취소하게 되고, 그러면 파괴적 변경을 놓치게 된다. 하나의 피드를 발행한다면 필터링하라. 둘을 발행한다면 API용은 더 좁게 만들고 마케팅 항목이 절대 들어가지 않게 하라. 두 형태를 나란히 체인지로그 대 릴리스 노트에서 비교한다.
API 체인지로그는 어디에 있어야 하는가
참조 문서 옆에, 안정적인 URL로, 각 항목이 프래그먼트나 자체 경로를 통해 개별적으로 주소 지정될 수 있게. 호출자는 사고 분석과 내부 티켓에서 항목을 링크한다. 링크할 수 없는 항목은 대신 스크린샷으로 붙여넣어진다.
페이지뿐 아니라 기계가 읽을 수 있는 출력으로도 발행하라. JSON Feed 사양을 따르는 JSON 피드나 RSS 피드는 항목이 구조화된 데이터가 되는 순간 아무런 비용도 들지 않으며, 이것이야말로 고객이 여러분의 변경 사항을 자신들의 릴리스 프로세스에 통합할 수 있게 해주는 것이다. 이것은 또한 누군가가 그 위에 무언가를 구축할지를 결정하는 부분이기도 하다. GitHub는 같은 이유로 REST API 버전을 참조 바로 옆에 문서화한다. 버전 정책은 인터페이스의 일부이기 때문이다.
실제로 좋은 항목은 어떤 모습인가
같은 주간의 세 항목을, 위에서 설명한 형태로 보여준다.
2026-09-02 Breaking v2
`POST /invoices`는 이제 고객 계정 통화와 일치하지 않는 `currency`를
거부하며, 조용히 변환하는 대신 422를 반환한다. 변환에 의존했던
호출자는 계정 통화를 보내야 한다. v2에만 영향을 미친다. v1은
2027-01-15의 서비스 종료까지 변경되지 않는다.
2026-09-02 Additive v1, v2
`Invoice`는 청구서가 결제될 때까지 null인 `settled_at` 타임스탬프를
얻는다. 조치가 필요하지 않다. 알 수 없는 필드를 거부하는 클라이언트는
업데이트되어야 한다.
2026-08-31 Fixed v2
`GET /invoices?status=`는 알 수 없는 상태에 대해 400 대신 빈 페이지를
반환했다. 이제는 허용되는 값과 함께 400을 반환한다. 오타를 낸
호출자는 이전에는 결과가 0개였지만 이제는 오류를 본다.
세 번째는 내부적으로 버그 수정이기 때문에 가장 자주 생략되는 유형이다. 그 빈 페이지 주위에 재시도 로직을 구축한 호출자에게는 이것이 동작 변경이며, 항목이야말로 지원 티켓을 막아주는 것이다. 라벨은 fixed라고 말하고 본문은 호출자가 알아챌 수 있는 것을 말한다. 이 구분이야말로 모든 수정을 파괴적 변경으로 부풀리지 않으면서 로그를 정직하게 유지하는 것이다.
호출자는 어떻게 구독하는가
호출자에게 하나 이상의 채널을 제공하라. 그들의 임무가 다르기 때문이다. 모든 것을 원하는 개발자를 위한 피드. 파괴적 변경만 원하는 사람을 위한 이메일. 코드 자체를 위한 응답 헤더는 확인을 절대 잊지 않는 유일한 구독자다. RFC 8594에 정의된 Sunset 헤더는 서비스 종료 날짜를 응답에 담아, 클라이언트 라이브러리가 그것을 로그로 남길 수 있게 한다.
대부분의 팀이 놓치는 채널은 직접적인 것이다. 어떤 호출자가 지난주에 여러분이 바꾸려는 필드를 사용했다면, 그가 누구인지 알고 있는 것이다. 그 계정들에 보내는 이메일은 어떤 규모의 일괄 발송보다도 가치가 크다. 이것은 아무도 요청하지 않은 변경에 적용된 고객 피드백 루프 닫기와 같은 원칙이다. 영향을 받은 사람들은 개별적으로 통보받고, 나머지 모두는 피드를 받는다. 웹훅은 의존하기 전에 알아둘 가치가 있는 자기만의 실패 방식을 가진 네 번째 채널이다. 웹훅 체인지로그는 그곳에서의 페이로드 변경이 왜 새 형태를 거부할 호출자 없이 조용히 깨지는지를 다룬다.
파괴적 변경을 위한 항목은 어떻게 작성하는가
이유가 아니라 파괴 자체로 시작하라. 열 개의 항목을 훑어보는 호출자는 첫 문장에서 이것이 자신에게 작업을 발생시킬지 알아야 한다. 그다음 날짜, 영향을 받는 버전, 마이그레이션, 그리고 예전 동작이 변경되는 것이 아니라 사라지는 경우의 마감일.
같은 내용을 비추천 공지, 응답 헤더, 직접 이메일에 일관되게 표현하여 넣고, 넷 모두에 같은 날짜를 부여하라. 이들 사이의 불일치는 계획된 변경을 사고로 바꾸는 실패다. 그중 하나만 읽은 호출자가 잘못된 날짜에 따라 행동하기 때문이다. 파괴적 변경이란 무엇인가는 그 결정 자체를 다루고, API를 비추천 처리하는 방법은 그 뒤에 이어지는 일정을 다룬다.
changeloop에서는 pull request가 병합되고, 누군가 초안을 편집하고 승인하며, 그 항목이 피드와 위젯에 발행될 때 API 변경이 항목이 된다. 바로 그 순간, 위젯 피드백이 GitHub 이슈가 되었고 pull request가 그 이슈를 닫는 호출자는 그 이슈에서 통보를 받는다. 여기서 중요한 것은 검토 단계다. API 체인지로그는 계약 문서이며, 사람이 읽지 않은 초안이 호출자에게 도달해서는 안 된다.
FAQ
모든 API 변경에 체인지로그 항목이 필요한가? 올바른 호출자가 알아챌 수 있는 모든 변경은 그렇다. 내부적이라고 여기는 것들도 포함해서. 요청이나 응답에 관찰 가능한 영향이 없는 변경은 아니며, 그것들을 추가하면 독자가 대충 훑어보도록 훈련시키게 된다.
API 체인지로그는 문서에 있어야 하는가, 마케팅 사이트에 있어야 하는가? 문서에, 참조 바로 옆에. 독자는 보통 이미 거기에 있으며, 마케팅 사이트의 체인지로그는 그것이 쓰이지 않은 독자층을 얻는 경향이 있다.
얼마나 과거까지 거슬러 올라가야 하는가? 무한정. 항목은 몇 년 후에도 사고 분석에서 인용되며, 잘려나간 로그는 그 링크들을 깨뜨린다. 잘라내는 대신 페이지네이션을 사용하라.
API 버전마다 별도의 체인지로그가 필요한가? 아니다. 항목마다 버전 필드가 있는 하나의 로그가 읽기와 검색이 더 쉽다. 버전별 필터링은 페이지의 기능이지, 문서를 나눌 이유가 아니다.
이 글의 기술적 내용은 독립적으로 검토되지 않았습니다. 잘못된 부분이 있으면 알려 주세요. 바로잡겠습니다.