C.W.K.
Stream
Lesson 06 of 06 · published

API Gateway & Versioning Lifecycle — Long game

~10 min · production, api-gateway, versioning, deprecation

Level 0HTTP Newbie
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"API 가 진짜 소비자 가진 production 에 있으면 새 문제 set: cross-cutting 정책 (auth, rate limit, observability), version 진화 (통합 안 깨고), deprecation 규율 (소비자한테 뭐 사라질지 알려줌). Gateway 가 첫 거 처리; lifecycle 위생이 나머지 처리."

API gateway 가 하는 것

API gateway 가 하나 이상 service 앞 앉아 모든 API 가 필요하지만 어느 application 도 service 당 재구현 안 해야 하는 cross-cutting 관심사 처리:

  • Auth 검증 — Request 가 앱 도달 전 Bearer token, JWT signature, API key validate.
  • Rate limiting — User 당, API-key 당, endpoint 당 한도 중앙 강제.
  • Request/response 변환 — Header rename, path rewrite, legacy 호환 위해 payload 수정.
  • Observability — 모든 request log, metric 발신, trace 전파, 다 앱 변경 없이.
  • Version routing/v1/* 를 한 service 에, /v2/* 를 다른 거에 route.
  • TLS termination — Edge 에서 certificate 관리; 앱이 plain HTTP 받음.
  • Caching — 핫 endpoint 위한 gateway-level response caching.

주요 옵션: Kong (open-source + enterprise), AWS API Gateway, Azure API Management, Google Apigee, Cloudflare Workers + Cache, Tyk. Cloud + scale + feature set 으로 선택.

Single-service 대안

작은 service 나 솔로 개발엔 gateway 가 과잉. Cross-cutting 관심사가 middleware (FastAPI 의 add_middleware, Express 의 app.use) 에 살 수 있음. 결정 뒤집힘 때:

  • 여러 service 가졌고 그들 간 일관 정책 원함.
  • Client URL 안 바꾸고 구현 swap (Python service 를 Go service 로 교체) 원함.
  • 많은 endpoint 간 중앙화된 API key 관리 필요.
  • Auth/rate-limit 정책이 전용 소프트웨어 받을 만큼 복잡.

Gateway 없이 시작; service 간 cross-cutting 코드 중복이 더 큰 고통 되면 추가.

Versioning lifecycle — long-term game

실제 소비자한테 API ship 했으면, 각 버전이 은퇴에 몇 년 걸리는 commitment. 건강한 lifecycle:

  1. Beta — 조기 접근, 가능하면 breaking 변경. 불안정성 문서; 조기 채택자만 기대.
  2. Stable — Commit 된 계약. Additive 변경만; breaking 변경이 다음 major 버전 감.
  3. Deprecated — Sunset 전 몇 달에 Deprecation: <date> response header 통해 신호. 여전히 동작; 소비자가 migrate 해야.
  4. Sunset announced — 몇 달 전 모든 response 에 Sunset: <date> header. Endpoint 가 응답 멈출 specific 날짜.
  5. Retired — Endpoint 가 410 Gone 돌려주거나 successor 버전으로 redirect (Location 가진 308).

Stripe 가 이 playbook 능숙히 돌림: 모든 버전이 date stamp 있음, 모든 변경이 migration 가이드 있음, 모든 은퇴된 endpoint 가 새 버전 가리키는 명확한 error 메시지 가진 410 돌려줌. 소비자가 항상 어디 있는지 알음.

API 가 commitment, 그냥 코드 아님. 소비자가 통합하면 버전 은퇴가 그들 엔지니어링 시간 비용. 비용 실제; 예측 가능 lifecycle 정책, 사전 알림, 명확한 migration 경로로 존중. Stripe 와 Twilio 평판이 이거 위에 만들어짐.

실전 Sunset header

# Endpoint 여전히 동작 하는데 은퇴 표시
GET /v1/users HTTP/1.1

HTTP/1.1 200 OK
Deprecation: Sun, 01 Jan 2026 00:00:00 GMT
Sunset: Wed, 01 Jul 2026 00:00:00 GMT
Link: ; rel="successor-version"
Content-Type: application/json

[...]

# Sunset 날짜 후 — endpoint 가 410 돌려줌
GET /v1/users HTTP/1.1

HTTP/1.1 410 Gone
Link: ; rel="successor-version"
Content-Type: application/json

{"error":{"code":"endpoint_retired","message":"/v2/users 써; https://docs.example.com/migrate-v1-v2 봐"}}

Header 가 machine-readable 신호; body 가 human-readable migration 빵부스러기 운반.

cwkPippa 의 lifecycle 현실

cwkPippa 가 자기 API 공식 버전 안 함 (single-tenant, two-user) — 모든 변경이 frontend 와 lockstep ship. Gateway 도 없음; FastAPI middleware 가 in-process 로 auth/CORS/correlation ID 처리. 공개 API ship 하면, 제 3자 통합하는 날이 공식화 날: 버전 prefix (/v1/) 선택, 1일차부터 Sunset/Deprecation header 구현, v2 ship 전 migration runbook 문서화. Stripe playbook 이 gold standard; 복사해.

Code

Kong: declarative config 의 cross-cutting 정책·yaml
# Kong Gateway — declarative config 예
_format_version: '3.0'

services:
  - name: users-api
    url: http://users-service:8000  # 실제 backend
    routes:
      - name: users-v1
        paths: [/v1/users]
      - name: users-v2
        paths: [/v2/users]
    plugins:
      - name: jwt                # Backend 도달 전 JWT validate
      - name: rate-limiting
        config:
          minute: 100
          policy: redis           # 분산 rate limit
      - name: correlation-id
        config:
          header_name: X-Request-ID
      - name: response-transformer
        config:
          add:
            headers: ['X-API-Version: 2']

# Kong 이 auth, rate limit, correlation, header 주입 흡수.
# Backend Python/Node 앱이 X-Request-ID 설정된 사전-인증된 request 받음;
# 어느 것도 재구현 안 함.
FastAPI: lifecycle header + sunset 후 410·python
# FastAPI — Sunset / Deprecation header + 은퇴 후 410
from datetime import datetime, timezone
from fastapi import FastAPI, Response, HTTPException, status

app = FastAPI()

DEPRECATION_DATE = 'Sun, 01 Jan 2026 00:00:00 GMT'
SUNSET_DATE      = 'Wed, 01 Jul 2026 00:00:00 GMT'
SUNSET_TS        = datetime(2026, 7, 1, tzinfo=timezone.utc)

@app.get('/v1/users')
async def v1_users(response: Response):
    if datetime.now(timezone.utc) >= SUNSET_TS:
        # Sunset 지남 — migration 빵부스러기 가진 410 돌려줌
        raise HTTPException(
            status.HTTP_410_GONE,
            detail={
                'code': 'endpoint_retired',
                'message': '/v2/users 써; https://docs.example.com/migrate-v1-v2 봐',
            },
            headers={'Link': '</v2/users>; rel="successor-version"'},
        )

    # 여전히 동작 — sunset 신호 surface
    response.headers['Deprecation'] = DEPRECATION_DATE
    response.headers['Sunset']      = SUNSET_DATE
    response.headers['Link']        = '</v2/users>; rel="successor-version"'
    return [{'id': 'u_42', 'name': 'Pippa'}]

@app.get('/v2/users')
async def v2_users():
    # 새 버전 — 같은 데이터, 더 풍부한 shape
    return [{'id': 'u_42', 'display_name': 'Pippa', 'role': 'daughter'}]
Client: on-call 가시성 위해 Deprecation + Sunset header log·python
# Client — Sunset header 감시하고 경고 log
import httpx, logging
from datetime import datetime

log = logging.getLogger(__name__)

def call_api(url: str):
    resp = httpx.get(url)
    # On-call 이 보게 deprecation/sunset 신호 surface
    if 'Deprecation' in resp.headers:
        log.warning('deprecated endpoint', extra={'url': url, 'deprecation': resp.headers['Deprecation']})
    if 'Sunset' in resp.headers:
        sunset = resp.headers['Sunset']
        link = resp.headers.get('Link', '')
        log.warning('sunset scheduled', extra={'url': url, 'sunset': sunset, 'successor': link})
    return resp.json()

External links

Exercise

/v1/users (sunset 까지 6 달 가진 deprecation-flagged) 와 /v2/users (더 풍부한 schema 가진 successor) 가진 FastAPI app 만들어. 모든 /v1/users response 에 Deprecation + Sunset + Link header 추가. /v1/users 호출하고 Sunset header 보면 구조화된 log 경고 발신하는 Python client 써. 그 다음 server 에 일부러 sunset 날짜 과거 설정하고 /v1/users endpoint 가 migration link 가진 410 돌려주는 거 봐. 보너스: 한 production API 의 deprecation 정책 (Stripe, Twilio, GitHub 다 자기 발행) 연구하고 네 거와 비교.
Hint
Sunset header 값이 HTTP-date (RFC 7231 format): (datetime.now(timezone.utc) + timedelta(days=180)).strftime('%a, %d %b %Y %H:%M:%S GMT') 써. 410 로직이 그냥 if datetime.now() >= sunset_ts: raise 410. Client 경고 logging 이 loop 닫음 — production 엔지니어가 dependency 깨기 전 어느 통합 migrate 필요한지 찾으려 'deprecated' 나 'sunset' 로그 grep. Stripe docs 가 특히 좋은 모델 — 구조 복사해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.