버전 관리
Coddy Node.js 여정의 REST API 섹션에 포함된 레슨. 25개 중 22번째.
응답의 형태를 바꾸면 이미 그 응답을 사용하는 모든 클라이언트가 작동하지 않게 됩니다. 경로에 버전을 지정하면 이전 클라이언트와 새 클라이언트가 나란히 사용할 수 있습니다. 현재는 /api/v1/orders, 형태가 바뀌면 /api/v2/orders를 사용합니다. 경로가 바뀌면 이전 경로는 새 경로로 리디렉션을 응답합니다.
GET /api/orders/5 → 308 Location: /api/v1/orders/5
POST /api/orders → 308 Location: /api/v1/orders301과 302는 클라이언트가 POST를 GET으로 다시 요청하도록 허용하며, 이 경우 본문이 사라집니다. 308은 메서드와 본문을 포함해 "같은 요청을 해당 위치에서 다시 하라"고 나타냅니다. 마운트된 미들웨어 안에서 req.url에는 마운트 경로가 남아 있지 않지만, req.originalUrl에는 전체 경로가 유지됩니다.
// GET /api/orders/5?full=1 요청의 app.use('/api/orders', ...)에서
req.url // '/5?full=1'
req.originalUrl // '/api/orders/5?full=1'챌린지
쉬움기존 클라이언트가 예상하지 못하는 방식으로 API가 변경될 수 있으므로, 경로에 버전 번호를 넣으세요. 오늘의 API는 버전 1이 되어 /api/v1/tasks에서 제공되고, 이전 주소는 클라이언트를 이곳으로 보냅니다.
server.js에서 작업 라우터를/api/tasks대신/api/v1/tasks에 마운트하세요.- 또한
server.js에서/api/tasks에 마운트되어 리디렉션만 수행하는 미들웨어를 추가하세요. 이 미들웨어는 나머지 경로와 쿼리 문자열을 유지하면서 같은 주소의/api/v1버전으로 상태 코드 308을 응답합니다. 마운트된 미들웨어 안에서req.originalUrl에는 이전 주소 전체가 들어 있습니다. 예를 들면 다음과 같습니다.GET /api/tasks/2는/api/v1/tasks/2로 리디렉션됩니다.POST /api/tasks는/api/v1/tasks로 리디렉션됩니다.GET /api/tasks?done=false&sort=title은/api/v1/tasks?done=false&sort=title로 리디렉션됩니다.
301이 아니라 308을 사용하세요. 308을 받으면 클라이언트는 본문을 포함해 POST 요청을 POST 요청으로 다시 보냅니다.
routes/tasks.js에서 POST 경로는Location을 새 주소로 설정합니다. 예를 들어 작업 4의 경우/api/v1/tasks/4로 설정합니다.
직접 해 보기: GET /api/tasks/2는 308을 반환하고, Headers 탭에는 Location: /api/v1/tasks/2가 표시됩니다. 요청 표시줄에는 리디렉션 자체가 표시되며, 리디렉션을 따라가지는 않습니다. /api/tasks에 { "title": "Order new chairs" }를 POST하면 Location: /api/v1/tasks와 함께 308을 받고, 작업은 생성되지 않습니다. 같은 본문을 /api/v1/tasks에 POST하면 Location: /api/v1/tasks/4와 함께 201을 받습니다. GET /api/v1/tasks/2는 작업 2를 반환합니다.
직접 해보기
import express from 'express';
import { sendError } from './errors.js';
import taskRoutes from './routes/tasks.js';
const BOARD_ORIGIN = 'https://board.example.com';
const app = express();
// BOARD_ORIGIN의 페이지에서 브라우저를 통해 이 API를 호출할 수 있게 합니다.
app.use((req, res, next) => {
res.set('Access-Control-Allow-Origin', BOARD_ORIGIN);
if (req.method === 'OPTIONS') {
res.set('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
res.set('Access-Control-Allow-Headers', 'Content-Type');
return res.status(204).end();
}
next();
});
app.use(express.json());
app.use(express.static('public'));
app.use('/api/tasks', taskRoutes);
// 라우트에서 발생한 오류는 여기서 처리됩니다. 유효한 JSON이 아닌 본문은
// 클라이언트의 잘못(400)이고, 그 밖의 오류는 서버의 잘못(500)입니다.
app.use((err, req, res, next) => {
if (err.type === 'entity.parse.failed') {
return sendError(res, 400, 'invalid_json', 'The body is not valid JSON');
}
console.error(err);
sendError(res, 500, 'server_error', 'Something went wrong');
});
app.listen(3000, () => {
console.log('Task tracker API on port 3000');
});
REST API의 모든 레슨
직접 연습해 보세요: Express 플레이그라운드