API 변경

API Sunset 헤더, 언제 보내야 하는가

4분 분량

Sunset은 RFC 8594에 정의된 단일 응답 헤더로, 어떤 리소스가 언제 응답을 멈출지를 호출자에게 알려준다. API 비추천 처리 는 공지-재알림-브라운아웃-폐기로 이어지는 전체 타임라인과 그에 따른 통지들을 다룬다. 이 글은 그 타임라인 안에서 유일하게 기계가 읽을 수 있는 신호인 이 헤더에 관한 것으로, 그것이 실제로 무엇을 말하는지, 그리고 RFC 자체가 보내지 말라고 말하는 유일한 경우에 관한 것이다.

Sunset 헤더는 무엇을 말하고, 무엇을 말하지 않는가

이 헤더는 단일 HTTP 날짜, 즉 그 리소스가 응답하지 않게 될 것으로 예상되는 시점을 담는다:

Sunset: Sat, 31 Dec 2028 23:59:59 GMT

RFC는 이를 보장이 아니라 힌트라고 부른다. 그 시점까지 리소스가 계속 작동한다는 것을 약속하지 않으며, 이후 실패가 어떤 모습일지에 대해서도 아무 말이 없다. 호출자는 4xx를 받을 수도, 리다 이렉트될 수도, 아예 응답을 받지 못할 수도 있다. 헤더는 이를 구분하지 않는다. 이미 과거인 타임 스탬프는 값의 오류가 아니라 “지금, 혹은 언제든지”를 의미한다. 이 중 어느 것도 프로토콜에 의해 강제되지 않는다. 헤더를 전혀 읽지 않는 클라이언트는 늘 그래왔던 것과 똑같이 동작하며, 리소스 가 사라졌다는 사실을 어차피 알게 됐을 방식 그대로 알게 된다.

실제로 언제 보내야 하는가

리소스가 정말로 응답을 멈추게 될 때만 보내야 하며, 단지 더 이상 권장되는 선택지가 아닌 단계 에서는 보내지 않는다. RFC는 비추천 처리가 두 단계로 일어난다는 점을 명시하며, Sunset 헤더 필드는 그중 두 번째 단계에만 속한다. 첫 번째 단계, 즉 어떤 버전이 더 이상 선호되지 않는다는 공지 단계에서는 API가 완전히 정상 작동하며, 이 헤더 필드는 그 단계에 적용되지 않는다. 버전이 실제로 응답을 멈추도록 예정된 시점이 되어야 적용된다.

이는 비추천 처리 타임라인과 정확히 맞물린다. Deprecation 헤더는 공지 단계인 첫날부터 나가 고, Sunset은 기존 동작이 실제로 멈추는 날짜를 나타내는데, 이는 4단계 타임라인 이 폐기라고 부르는 것과 같은 날짜다. 첫날에 Sunset을 보내는 것 자체는 틀리지 않다. 그 시점 에 날짜가 이미 확정되어 있다면 말이다. 하지만 비추천 처리를 공지하지도 않은 채 보내거나, 실제 로는 폐기를 확정하지 않은 버전에 설정해 버리면, 아직 결정하지도 않은 것을 호출자에게 말해 버리는 셈이 된다.

이것은 캐싱과 상호작용하는가

아니다. RFC는 이를 명확히 밝히고 있다. Sunset과 HTTP 캐싱은 서로 무관한 문제를 해결하며, 겹치는 것이 아니라 서로 보완하는 것으로 읽어야 한다. 캐싱 헤더는 캐시된 사본을 언제 재사용해도 안전한지를 말해준다. Sunset은 리소스의 현재 상태에 대해서는 아무 말도 하지 않으며, 오직 그 리소스 자체가 언젠가 존재하지 않게 될 것이라는 점만을 말한다. 응답은 실제로 종료를 맞이하는 순간 직전까지도 완전히 캐시 가능할 수 있다. 하나로 다른 하나를 근사하려 하지 말고, 긴 max-age 가 다가오는 종료일을 상쇄한다거나 그 반대라고 가정하지도 말라.

하나의 헤더로 여러 엔드포인트를 종료시킬 수 있는가

헤더는 그것을 반환한 리소스에 적용되지만, RFC는 서비스가 더 넓은 범위를 문서화하는 것을 허용한다. API의 홈 리소스에 설정된 종료 날짜를 그 URL 하나만이 아니라 API 전체가 사라진다는 의미로 정의할 수 있다. 다만 이는 그 스코프 규칙을 이미 알고 있는 호출자에게만 통한다는 함정이 있다. 헤더를 액면 그대로 읽는 호출자에게는 자신이 요청한 리소스 하나에 대한 종료만 보이고 그 외에는 아무것도 보이지 않는다. 따라서 더 넓은 범위는 암묵적으로 남겨둘 것이 아니라 호출자 가 찾을 수 있는 어딘가에 명시해 두어야 한다.

헤더와 함께 무엇을 실어야 하는가

폐기에 대해 설명하는 곳으로 가는 링크다. RFC 8594는 정확히 이를 위해 자체 sunset 링크 관계 를 등록해 두었다. 폐기 정책, 다가오는 날짜, 또는 마이그레이션 방법을 설명하는 리소스를 가리 키기 위한 것으로, 헤더의 단순한 타임스탬프와는 별개다.

HTTP/1.1 200 OK
Sunset: Sat, 31 Dec 2028 23:59:59 GMT
Link: <https://example.com/docs/sunset-policy>; rel="sunset"

그 링크를 자체 체인지로그 예시나 전용 마이그레이션 페이지로 향하게 하면, 거의 아무도의 클라이언트 코드도 검사하지 않는 헤더가 실제로 찾아보는 사람이 즉시 발견 할 수 있는 것으로 바뀐다. 비추천 처리 헤더 의 successor-version 관계와 결합하면, 호출자는 응답만으로 어디로 가야 하는지와 무엇이 이를 대체하는지를 모두 얻는다.

이것은 처음부터 끝까지 어떤 모습인가

v1이 2027년 3월 1일에 사라진다고 하자. 첫날의 비추천 처리 공지는 비추천 처리 헤더 에 따라 모든 v1 응답에 Deprecation과 Link: rel="successor-version"을 추가하지만, 폐기 날짜가 임시값이 아니라 정말로 확정될 때까지는 Sunset을 보류한다. 확정되고 나면, 모든 v1 응답은 다음을 싣는다:

HTTP/1.1 200 OK
Deprecation: @1756425600
Sunset: Mon, 01 Mar 2027 00:00:00 GMT
Link: <https://api.example.com/v2/reports>; rel="successor-version"
Link: <https://example.com/docs/sunset-policy>; rel="sunset"

호출자의 게이트웨이나 모니터링은 두 헤더 각각에 독립적으로 경보를 걸 수 있다. Deprecation 은 더 새로운 버전이 존재한다는 것을, Sunset은 이 버전에 시계가 걸려 있다는 것을 말한다. 3월 1일 이전에 두 헤더 중 어느 것도 바뀔 필요는 없다. 바뀌는 것은 응답 그 자체이며, 그것도 당일과 그 전에 예정된 브라운아웃 기간 동안 일어난다.

브라운아웃은 헤더의 내용을 바꾸는가

예정된 브라운아웃 때문에 헤더 값 자체가 움직일 필요는 없다. 종료 날짜는 그 이전에 리소스가 간헐적으로 실패하든 아니든 여전히 종료 날짜 그대로다. 바뀌는 것은 헤더가 아니라 응답이다. API 비추천 처리가 설명하듯, 공지된 날짜 이전 몇 주 동안 짧은 410 Gone 구간을 예약해 두면, 호출자가 그 실패를 처음 접하는 순간이 헤더의 날짜가 도래하는 당일의 실전이 아니라 예행연습이 된다.

FAQ

실제 HTTP 클라이언트나 도구가 Sunset 헤더를 정말로 읽는가? 클라이언트 쪽에서는 드물다. 그 가치는 주로 여러분과 호출자 사이의 인프라를 운영하는 쪽을 위한 것이다. 헤더를 감시하도록 설정한 API 게이트웨이나 모니터링 도구는 호출자의 코드가 알아 차리기 훨씬 전에 자사 팀이나 파트너 팀에 경보를 보낼 수 있다. 상대편이 이미 대비하고 있다고 가정할 수 있는 신호가 아니라, 여러분이 직접 도구를 만들어 대응하는 신호로 취급하라.

Sunset은 Cache-Control: max-age와 같은 것인가? 아니다. max-age는 캐시된 사본이 얼마나 오래 유효한지에 관한 것이고, Sunset은 리소스 자체 가 언제 존재하지 않게 되는지에 관한 것이다. 응답은 짧은 max-age와 몇 년 뒤의 Sunset 날짜 를 동시에 가질 수도 있고 그 반대일 수도 있으며, 어느 헤더도 다른 헤더를 제약하지 않는다.

엔드포인트 전체가 아니라 필드 하나가 사라지는 경우에도 Sunset을 보낼 수 있는가? 아니다. 이 헤더는 리소스, 즉 URL에 스코프되어 있으며 응답 본문 안의 필드에 스코프되어 있지 않다. 엔드포인트 자체는 유지된 채 사라지는 필드나 파라미터, 열거값에 대해서는 대신 Deprecation 헤더와 체인지로그 항목을 사용하라. API 비추천 처리가 바로 그런 종류의 변경을 공지하는 방법을 다룬다.

종료 날짜를 옮겨야 한다면 어떻게 하는가? 헤더 값을 갱신하고, 애초에 그것을 공지했던 체인지로그 항목에도 그렇게 적어라. 이미 공개된 날짜를 조용히 바꾸는 것은 호출자로 하여금 여러분의 날짜가 하나도 진짜가 아니라고 판단하게 만드는 방식이다. RFC가 이 값을 굳이 보장이 아닌 힌트로 규정한 것은 날짜가 실제로 움직일 때가 있기 때문이지만, 설명 없이 옮겨진 날짜는 다음 날짜에 대한 신뢰마저 갉아먹는다.


이 글의 기술적 내용은 독립적으로 검토되지 않았습니다. 잘못된 부분이 있으면 알려 주세요. 바로잡겠습니다.

changeloop 관련 페이지: 개발자 문서, changelog 예시

changeloop
루프를 닫는 changelog를 만드는 팀입니다. 사용자가 무언가를 요청하면 팀이 전달하고, 요청한 사람은 알게 됩니다.