Node.js/Express body-parser/POST request 처리
09 May 2021views에서 POST
메소드로 요청한 form
데이터를 controllers에서 받아오는 방법을 알아보자.
DB는 사용하지 않고 간단하게 전역 객체 배열을 이용
시나리오
- views
비디오 수정 페이지에 form
작성 → video title 수정 → POST
request
- controllers
request.body
객체에서 form
데이터 파싱 → videos
객체 배열에서 해당 video의 title 수정 → 비디오 시청 페이지로 redirect
- views
비디오 시청 페이지에서 수정 사항 확인
디렉토리 구조(이전 포스팅 참고)
┌──node_modules
├─┬src
│ ├─┬controllers
│ │ ├──userController.js
│ │ └──videoController.js
│ ├─┬routers
│ │ ├──globalRouter.js
│ │ ├──userRouter.js
│ │ └──videoRouter.js
│ └─┬views
│ ├─┬partial
│ │ └──footer.pug
│ ├──base.pug
│ ├──home.pug
│ ├──videoEdit.pug<-----추가
│ └──watch.pug
├──server.js
└──pakage.json
☄ views
// views/watch.pug
extends base.pug
block content
h1=video.title
h3 #{video.views} #{video.views <= 1 ? "view" : "views"}
a(href=`${video.id}/edit`) edit video →
#{}
을 이용해 JavaScript의 삼항 조건 연산자를 사용할 수 있다.
그리고 .pug
에서 속성 값에 string과 함께 JS 객체 변수를 사용하고 싶을 때는 ${}
문법을 사용하면 된다.
// views/videoEdit.pug
extends base
block content
form(method="post")
input(name="title", placeholder="Video Title", value=`${video.title}`, required)
input(value="save", type="submit")
name
속성의 값이 전달될 input
데이터의 변수 명이 됨(없으면 파싱 불가)
☄ routers
// routers/videoRouter.js
import { getEdit, postEdit } from "../controllers/videoController";
const videoRouter = express.Router();
videoRouter.get("/:id", watch);
videoRouter.route("/:id/edit").get(getEdit).post(postEdit);
export default videoRouter;
app.route()
는 하나의 route에 여러 개의 http method를 체인 형식으로 라우팅할 수 있다.
이렇게 설정하면 하나의 view에 라우팅한 하나의 url에서 http request method 별로 사용자의 다양한 처리 및 응답할 수 있다.
/videos/:id/edit
으로
- GET request →
getEdit
controller 호출 - POST request →
postEdit
controller 호출
☄ controllers
// controllers/videoController.js
let videos = [
{
title: "First video",
rating: 5,
comments: 4,
createdAt: "2 minutes ago",
views: 100,
id: 1,
},
{
title: "Second video",
rating: 5,
comments: 4,
createdAt: "2 minutes ago",
views: 100,
id: 2,
},
{
title: "Third video",
rating: 5,
comments: 4,
createdAt: "2 minutes ago",
views: 100,
id: 3,
},
];
export const watch = (req, res) => {
const { id } = req.params;
const video = videos[id - 1];
return res.render("watch", { pageTitle: `Whatching ${video.title}`, video });
};
export const getEdit = (req, res) => {
const { id } = req.params;
const video = videos[id - 1];
return res.render("videoEdit", { pageTitle: `Edit ${video.title}`, video });
};
export const postEdit = (req, res) => {
const { id } = req.params;
const { title } = req.body;
videos[id - 1].title = title;
return res.redirect(`/videos/${id}`);
};
getEdit
은 라우팅 된 url로 GET 요청이 오면, videoEdit.pug을 렌더링 해 사용자에게 비디오 수정 페이지를 보여주는 처리를 하는 컨트롤러다. 때문에 마지막에 videoEdit.pug
페이지를 렌더링하는 res.render
함수를 반환한다.
postEdit
은 마찬가지로 POST 요청을 처리하는 컨트롤러다. getEdit
는 다르게 /videos/${id}
(watch.pug 페이지)로 redirect
응답을 반환한다.
본래 POST 요청 시 사용자가 작성한 데이터들은 request
객체에 담겨 전달된다. 하지만 express
에서 바로 request.body
에 접근하면 undefined
에러가 발생한다.
// controllers/videoController.js
// ...
export const postEdit = (req, res) => {
const { id } = req.params;
console.log(req.body);
return res.end();
// videos[id - 1].title = title;
// return res.redirect(`/videos/${id}`);
};
때문에 body-parser
라는 미들웨어의 도움을 받아야 한다.
☄ body-parser middleware
request body
에 있는 데이터를 분석해 JavaScript가 해석할 수 있도록req.body
형태의 객체로 만들어 주는 미들웨어다.
설치
npm install body-parser
사실 Express v4.16.0부터 body-parser가 빌트 인 되어 따로 설치하지 않아도 됨.
https://expressjs.com/en/4x/api.html#express.urlencoded
// server.js
//...
app.set("views", process.cwd() + "/src/views");
app.set("view engine", "pug");
app.use(logger);
app.use(express.urlencoded({ extended: true })); // <-----추가
app.use("/", globalRouter);
app.use("/users", userRouter);
app.use("/videos", videoRouter);
//...
urlencoded({ extended: true })
속성은 주소 형식으로 전달된 데이터를 해석하겠다는 의미로 주로 form
요청 데이터 해석 시 사용된다.
req.body
객체에 input
태그의 name
속성으로 지정한 title
이 속성 이름, 입력한 body-parser test
가 값으로 들어가 있다.
/videos/${id}
(watch.pug 페이지)로 redirec
까지 잘 된다.
Reference
Node.js 교과서(2판) - 조현영