API 버전 관리가 필요한 이유
API는 한번 공개되면 클라이언트가 의존하게 됩니다. 응답 구조 변경, 필드 삭제, 동작 변경 등 Breaking Change가 발생할 때, 기존 클라이언트를 깨뜨리지 않으면서 새 버전을 제공해야 합니다.
1. URL Path 버전 관리
const express = require('express');
const app = express();
const v1Router = express.Router();
v1Router.get('/users', (req, res) => {
res.json({ users: [{ name: 'Kim', email: 'kim@test.com' }] });
});
const v2Router = express.Router();
v2Router.get('/users', (req, res) => {
res.json({
data: [{ fullName: 'Kim', email: 'kim@test.com', avatar: null }],
meta: { total: 1, page: 1 }
});
});
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);
2. Custom Header 버전 관리
function apiVersion(req, res, next) {
const version = req.headers['x-api-version'] ||
req.headers['api-version'] || '1';
req.apiVersion = parseInt(version);
next();
}
app.use(apiVersion);
app.get('/api/users', (req, res) => {
if (req.apiVersion >= 2) {
return res.json({
data: [{ fullName: 'Kim', email: 'kim@test.com' }],
meta: { total: 1 }
});
}
res.json({ users: [{ name: 'Kim', email: 'kim@test.com' }] });
});
3. Content Negotiation (Accept Header)
app.get('/api/users', (req, res) => {
const accept = req.headers.accept || '';
const match = accept.match(/application\/vnd\.myapi\.v(\d+)\+json/);
const version = match ? parseInt(match[1]) : 1;
if (version >= 2) {
res.type('application/vnd.myapi.v2+json');
return res.json({ data: [{ fullName: 'Kim' }], meta: { total: 1 } });
}
res.type('application/vnd.myapi.v1+json');
res.json({ users: [{ name: 'Kim' }] });
});
전략 비교표
| 기준 | URL Path | Custom Header | Content Negotiation |
|---|---|---|---|
| 직관성 | 높음 | 중간 | 낮음 |
| RESTful | 낮음 | 중간 | 높음 |
| 캐싱 | 쉬움 | Vary 헤더 필요 | Vary 헤더 필요 |
| 브라우저 테스트 | 쉬움 | 어려움 | 어려움 |
| 도입 난이도 | 낮음 | 중간 | 높음 |
| 대표 사용자 | Stripe, Google | Azure | GitHub API v3 |
Deprecation 전략
app.use('/api/v1', (req, res, next) => {
res.set('Deprecation', 'true');
res.set('Sunset', 'Sat, 01 Mar 2025 00:00:00 GMT');
res.set('Link', '; rel="successor-version"');
next();
}, v1Router);
선택 가이드
- 공개 API / 다수 외부 클라이언트: URL Path 방식이 가장 안전하고 직관적입니다.
- 내부 API / 마이크로서비스 간 통신: Header 방식으로 URL을 깔끔하게 유지하세요.
- Breaking Change가 드문 경우: 버전 관리 대신 Additive Change 원칙(필드만 추가)으로 단일 버전을 유지하세요.
- 어떤 방식이든, Sunset/Deprecation 헤더와 명확한 마이그레이션 가이드를 제공하세요.
댓글 0