stack-analysis
CHAPTER 37 / 90
읽기 약 2분
FUNCTION
API 버저닝 전략
핵심 개념
URL·Header·Accept-Version 비교·Deprecation·Sunset — 호환성 유지.
본문
3가지 버저닝 방식
1. URL Path: /v1/users, /v2/users
✅ 명확·캐싱·테스트 쉬움
❌ URL 변경 = 새 리소스처럼 보임
→ 가장 인기
2. Custom Header: X-API-Version: 2
✅ URL 깔끔
❌ 헤더 누락 시 혼란
3. Accept Header: Accept: application/vnd.api.v2+json
✅ HTTP 표준
❌ 디버깅 어려움URL 버저닝 (권장)
// app.ts
import v1Routes from './routes/v1';
import v2Routes from './routes/v2';
app.use('/api/v1', v1Routes);
app.use('/api/v2', v2Routes);
// routes/v1/user.routes.ts (레거시 유지)
router.get('/users/:id', (req, res) => {
const user = await userService.findById(req.params.id);
res.json({
id: user.id,
name: user.name,
email: user.email,
});
});
// routes/v2/user.routes.ts (개선)
router.get('/users/:id', (req, res) => {
const user = await userService.findById(req.params.id);
res.json({
id: user.id,
profile: { // 그룹화
displayName: user.name,
email: user.email,
avatar: user.avatarUrl,
},
metadata: {
createdAt: user.createdAt,
lastSeenAt: user.lastSeenAt,
},
});
});Deprecation 헤더
// v1 라우트에 자동 경고
function deprecate(sunset: string, alternative: string) {
return (req: Request, res: Response, next: NextFunction) => {
res.setHeader('Deprecation', 'true');
res.setHeader('Sunset', new Date(sunset).toUTCString());
res.setHeader('Link', `<${alternative}>; rel="successor-version"`);
next();
};
}
app.use('/api/v1', deprecate(
'2026-12-31',
'https://api.example.com/v2'
));
// 응답 헤더:
// Deprecation: true
// Sunset: Wed, 31 Dec 2026 00:00:00 GMT
// Link: <https://api.example.com/v2>; rel="successor-version"점진적 마이그레이션
// v1과 v2 데이터 모델 변환 레이어
class UserSerializer {
static v1(user: User) {
return { id: user.id, name: user.name, email: user.email };
}
static v2(user: User) {
return {
id: user.id,
profile: { displayName: user.name, email: user.email },
metadata: { createdAt: user.createdAt },
};
}
}
// 같은 DB 모델, 다른 직렬화
router.get('/v1/users/:id', async (req, res) => {
const user = await userService.findById(req.params.id);
res.json(UserSerializer.v1(user));
});
router.get('/v2/users/:id', async (req, res) => {
const user = await userService.findById(req.params.id);
res.json(UserSerializer.v2(user));
});호환성 유지 원칙
✅ 절대 안전 (Backward Compatible):
- 새 필드 추가
- 새 엔드포인트
- 새 enum 값 (클라이언트가 unknown 처리 시)
- HTTP 200 → 추가 필드
❌ Breaking Change:
- 필드 제거
- 필드 타입 변경 (string → number)
- enum 값 제거
- HTTP 응답 코드 변경
- URL 변경
→ Breaking은 새 버전, 그 외는 같은 버전 유지클라이언트 라이브러리
// SDK 자동 마이그레이션 도움
import { ApiClient } from '@my/sdk';
const client = new ApiClient({
apiKey: '...',
version: 'v2', // 또는 'latest'
});
// SDK 내부 — 자동 변환
class ApiClient {
async getUser(id: string) {
const res = await fetch(`/${this.version}/users/${id}`);
const data = await res.json();
// v1 응답을 v2 형태로 자동 변환 (같은 인터페이스)
if (this.version === 'v1') {
return { profile: { displayName: data.name, email: data.email }, ... };
}
return data;
}
}마이그레이션 시간표
[Phase 1: 출시] 2026-04
- v2 출시 + 문서화
- v1, v2 동시 운영
[Phase 2: 안내] 2026-07
- v1 응답에 Deprecation 헤더
- 사용자에게 이메일
[Phase 3: 강제 안내] 2026-10
- v1 호출 시 응답 본문에 경고
- 대시보드에 마이그레이션 진행률
[Phase 4: 종료] 2026-12-31 (Sunset)
- v1 410 Gone 응답
- 또는 v2로 자동 리다이렉트
→ 최소 6~12개월 마이그레이션 기간다음 챕터
CH.38 "GraphQL vs REST: 실전 선택 기준".
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 코드의 API 버저닝 부분을 분석해서 실전 분석 + 개선 우선순위를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
API 버저닝 관련 인기 라이브러리/패턴 5개를 비교 분석해서 패턴 추출를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 프로젝트 전체에서 API 버저닝 최적화 가능 위치를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 백엔드 시장의 API 버저닝 트렌드를 솔직히 알려줘.
⭐ 이것만 기억하세요
API 버저닝 전략은 이 3가지만 확실히 잡으세요
1.URL 버저닝(/v1, /v2)이 가장 명확 — 캐싱·디버깅 쉬움
2.Backward Compatible 변경(필드 추가)은 같은 버전 유지
3.Deprecation + Sunset 헤더 + 6~12개월 마이그레이션 기간
공유하기
진행도 37 / 90