요청 본문 읽기
Coddy Node.js 여정의 Express 기초 섹션에 포함된 레슨. 23개 중 15번째.
POST 요청은 본문에 데이터를 보내며, Express는 본문을 읽어 달라고 요청하지 않으면 본문을 읽지 않습니다.
app.use(express.json());
app.post('/api/loans', (req, res) => {
const { bookId, member } = req.body;
res.status(201).json({ bookId, member, due: '2026-11-08' });
});GET은 무언가를 요청하고, POST는 새로 만들 항목을 보냅니다. express.json()은 모든 JSON 본문을 req.body로 읽어 오는 미들웨어입니다. 이 미들웨어가 없으면 req.body는 undefined입니다. 무언가를 생성하는 경로는 생성한 항목과 함께 201로 응답합니다.
저장하기 전에 본문을 확인하세요. 필수 항목이 누락된 것은 클라이언트의 실수이므로, 400으로 응답하고 아무것도 저장하지 마세요.
챌린지
쉬움방문자는 자신만의 레시피를 추가할 수 있습니다. 프로그램은 POST 요청 본문에 새 레시피를 JSON으로 보내고, API가 이를 저장합니다.
server.js에서 라우트 위의 다른app.use줄들과 함께express.json()을 추가하면 JSON 본문이req.body에 들어옵니다./api/recipes에 대한 POST 요청 라우트를 추가하세요. 본문에서title과tags를 읽습니다. 예를 들면{"title": "Miso Soup", "tags": ["vegan", "soup"]}입니다.- 다음 번호를 id로 사용해 새 레시피를 만듭니다. 레시피 4개의 id는 1부터 4까지이므로, 처음 추가되는 레시피의 id는 5입니다. 전달받은 제목과 태그를 그대로 유지합니다.
tags가 전달되지 않으면 빈 목록인[]을 사용하세요. - 레시피를
recipes배열에 추가하면GET /api/recipes에서 해당 레시피가 표시됩니다. 그런 다음 상태 코드 201과 새 레시피를 JSON으로 응답하세요.{"id": 5, "title": "Miso Soup", "tags": ["vegan", "soup"]} - 본문에
title이 없으면 아무것도 저장하지 않고, 상태 코드 400과 다음 JSON을 응답하세요.{"error": "A recipe needs a title"}
직접 해보기: 실행을 누르세요. 메서드 선택기에서 POST를 고르고, 경로에 /api/recipes를 입력한 다음 본문 입력란에 {"title": "Miso Soup", "tags": ["vegan", "soup"]}를 넣으세요. 상태 코드 201과 "id": 5인 레시피가 표시되어야 합니다. GET을 선택하고 /api/recipes를 보내세요. 레시피 5개가 표시되고 Miso Soup가 마지막에 나옵니다. 그런 다음 {"tags": ["dinner"]}를 /api/recipes로 POST하세요. 상태 코드 400과 {"error":"A recipe needs a title"}가 표시됩니다. GET /api/recipes에는 여전히 레시피 5개가 표시됩니다.
직접 해보기
import { escapeHtml } from './html.js';
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.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 플레이그라운드