Menu
CoddyTech

순서가 중요합니다

Coddy Node.js 여정의 Express 기초 섹션에 포함된 레슨. 23개 중 14번째.

Express는 코드에서 미들웨어와 경로를 추가한 순서대로 실행하며, 응답하는 경로는 그 지점에서 요청 처리를 끝냅니다.

app.get('/bikes', (req, res) => {
    res.json(bikes);
});

app.use((req, res, next) => {
    res.set('Cache-Control', 'no-store');
    next();
});

res.set(name, value)는 응답에 헤더를 추가하며, 응답을 보내기 전에 실행해야 합니다. 여기서는 미들웨어에 도달하기 전에 GET /bikes가 응답하므로, 해당 응답에는 헤더가 포함되지 않습니다. 미들웨어는 위쪽에 있는 경로가 응답하지 않은 요청만 확인합니다. 모든 응답에 적용되어야 하는 미들웨어는 경로와 express.static보다 위에 둡니다.

challenge icon

챌린지

쉬움

각 응답에는 ID가 포함되므로 방문자가 문제를 신고하면 로그에서 해당 요청을 찾을 수 있습니다. 페이지, API 데이터, public의 파일 등 모든 응답에 ID가 필요합니다.

  1. server.js에서 node:crypto로부터 randomUUID를 가져오세요. 호출할 때마다 3b241101-e2bb-4255-8caf-4136c566a962와 같은 새로운 임의 ID를 반환합니다.
  2. 응답 헤더 X-Request-Id를 설정하는 미들웨어를 추가하세요. 이 헤더에는 요청마다 새 randomUUID()가 담기며, 그런 다음 next()를 호출합니다.
  3. 모든 요청이 이 미들웨어를 거치도록 배치하세요. 여기에는 express.static이 전송하는 파일도 포함됩니다. 미들웨어가 응답이 생성되기 전에 실행되는 경우에만 응답에 헤더가 추가됩니다.

직접 해보기: ‘실행’을 누르고 /api/recipes를 두 번 전송하세요. Headers 탭에서 X-Request-Id 값이 매번 다릅니다. /style.css와 /도 전송해 보세요. 각 응답에도 X-Request-Id가 포함됩니다.

직접 해보기

import { escapeHtml } from './html.js';
import express from 'express';
import { recipes } from './data.js';

const app = express();

app.use((req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
});

app.use(express.static('public'));

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.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.listen(3000, () => {
    console.log('Recipe Box is running on port 3000');
});

Express 기초의 모든 레슨

직접 연습해 보세요: Express 플레이그라운드