오류 처리
Coddy Node.js 여정의 Express 기초 섹션에 포함된 레슨. 20개 중 15번째.
라우트에서 예외가 발생하면 Express는 자체 오류 페이지로 응답합니다. 이 페이지는 JSON이 아니며, 여러분을 위한 세부 정보를 표시합니다.
app.get('/api/fines/:member', (req, res) => {
throw new Error('Fines database is down');
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Try again later' });
});인수가 네 개인 미들웨어 (err, req, res, next)는 오류 처리기입니다. Express는 인수의 개수를 확인합니다. 인수가 세 개면 일반 미들웨어이며, 오류가 발생하면 건너뜁니다. 라우트에서 예외가 발생하면 Express는 일반 미들웨어를 건너뛰고 오류와 함께 오류 처리기를 호출합니다. 404 처리기 뒤에 마지막으로 추가하세요. 세부 정보는 여러분이 확인할 수 있도록 기록하고, 클라이언트에는 짧은 메시지를 보내세요.
잘못된 ?by=는 방문자의 실수이므로 여전히 400 응답을 받습니다. 오류는 방문자가 해결할 수 없는 문제를 뜻합니다. 예를 들면 레시피를 인분 수 없이 저장한 경우입니다.
챌린지
쉬움GET /api/recipes/:id/scale?by=2는{ "title", "servings" }를 반환하며, servings는 곱해집니다.- 양수가 아닌 계수:
400과{ "error": "Scale by a positive number" }를 반환합니다. servings가 없는 레시피는Recipe <id> has no servings오류를 발생시킵니다.- 오류 처리기는 오류를 기록하고
500과{ "error": "Something went wrong" }를 반환합니다.
?by=2와 ?by=lots를 전송하세요. 그런 다음 { "title": "Toast" }를 /api/recipes에 전송하고 레시피 5의 양을 조정하세요.
직접 해보기
import express from 'express';
import { randomUUID } from 'node:crypto';
import { recipes } from './data.js';
const app = express();
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
app.use((req, res, next) => {
res.set('X-Request-Id', randomUUID());
next();
});
app.use(express.json());
app.use(express.static('public'));
const escapeHtml = (text) =>
text
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"');
app.get('/', (req, res) => {
const items = recipes.map((r) => `<li><a href="/recipes/${r.id}">${escapeHtml(r.title)}</a></li>`);
res.send(`<link rel="stylesheet" href="/style.css"><h1>Recipe Box</h1><ul>${items.join('')}</ul>`);
});
// type('text')는 일반 텍스트를 전송하므로 각 제목이 한 줄에 하나씩 표시됩니다.
app.get('/recipes', (req, res) => {
const { tag } = req.query;
const list = tag ? recipes.filter((recipe) => recipe.tags.includes(tag)) : recipes;
res.type('text').send(list.map((recipe) => recipe.title).join('\n'));
});
app.get('/recipes/:id', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).send('No such recipe');
return;
}
res.send(recipe.title);
});
app.get('/api/recipes', (req, res) => {
res.json(recipes);
});
app.get('/api/recipes/:id', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).json({ error: 'No such recipe' });
return;
}
res.json(recipe);
});
app.post('/api/recipes', (req, res) => {
const { title, tags } = req.body;
if (!title) {
res.status(400).json({ error: 'A recipe needs a title' });
return;
}
const recipe = { id: recipes.length + 1, title, tags: tags ?? [] };
recipes.push(recipe);
res.status(201).json(recipe);
});
app.get('/about', (req, res) => {
res.send('Recipe Box collects family recipes.');
});
app.get('/menu', (req, res) => {
res.redirect(301, '/recipes');
});
app.get('/menu/:id', (req, res) => {
res.redirect(301, `/recipes/${req.params.id}`);
});
app.use((req, res) => {
if (req.path.startsWith('/api/')) {
res.status(404).json({ error: 'Not found' });
return;
}
res.status(404).send('<h1>Page not found</h1><p><a href="/">Back to Recipe Box</a></p>');
});
app.listen(3000, () => {
console.log('Recipe Box is running on port 3000');
});
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.