-
320x100
๐ฉ๐ป ์ด ๊ธ์ ๋ ธ๋ง๋ ์ฝ๋์ [์ ํ๋ธ ํด๋ก ์ฝ๋ฉ] ๋ด์ฉ์ ๋ด๊ณ ์์ต๋๋ค.
+ ์ด ์๋ฆฌ์ฆ์ ์ฒซ ๋ฒ์งธ ๊ธ ๐ ์ ํ๋ธ ํด๋ก 01
(2-18) Search Controller
์ด๋ฒ์๋ ๊ฒ์ ํ์ด์ง๋ฅผ ๋ง๋ค์ด ๋ด ์๋ค. ๊ฒ์์ฐฝ์ ํค๋ ์์ ๋ค์ด๊ฐ ๊ฑฐ๋ header.pug๋ฅผ ์์ ํฉ๋๋ค.
/* header.pug */ header h1.title a(href=routes.home) #{siteName} div.search form(action=routes.search, method="get") input(type="text", placeholder="๊ฒ์", name="term") ...
form์ action์ ํผ ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ URL์ ๋งํฉ๋๋ค. ์ฐ๋ฆฐ search ํ์ด์ง์์ ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ค ๊ฑฐ๋๊น, locals์ ์ ์ฅ๋ routes.search๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค.
form์ method๋ ํผ ๋ฐ์ดํฐ๋ฅผ ์ด๋ค HTTP ๋ฉ์๋๋ก ๋ณด๋ผ ๊ฑด์ง ์ ํ๋ ๊ฑด๋ฐ์, ๊ฒ์ ๋ฑ ๋จ์ ์์ ์๋ ์ฃผ๋ก GET์ ์ฌ์ฉํฉ๋๋ค.
GET์ URL์ ํผ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ์ฌ ์๋ฒ๋ก ์ ๋ฌํ๊ธฐ ๋๋ฌธ์, ์ฃผ์์ฐฝ ๋ค์ ๋ฐ์ดํฐ๊ฐ ์ซ๋์ซ๋ ๋ถ์ด ๋์ต๋๋ค.
๋ ๋ธ๋ผ์ฐ์ ์ ์บ์๋์ด ์ ์ฅ๋๋ฉฐ, ๊ธธ์ด์ ์ ํ์ด ์๋ค๋ ํน์ง๋ ์์ด์.
input์ name์ ๋ช ์ํ๋ฉด ํผ ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํ ๋ ์ธ ์ ์์ด์. ํ ๋ฒ ์ํํด ๋ณด์ฃ .
ํผ์ ๊ฒ์์ด๋ฅผ ์ ๋ ฅ ํ ์ํฐ๋ฅผ ๋ ์ณค๋๋ ์ฃผ์์ฐฝ ๋ค์ '?term=๋๋' ๋ผ๊ณ ํ์๋๋ค์!
์, ๊ทธ๋ผ ์ด์ ์ด ๊ฒ์์ด๋ฅผ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ค๊ณ ํฉ๋๋ค. '๋๋๋ก ๊ฒ์ํ ๊ฒฐ๊ณผ์ ๋๋ค' ์ด๋ฐ ์์ผ๋ก ํ์ํ ๊ฑด๋ฐ์, ์ด ๊ฒ์์ด๋ ์ด๋ป๊ฒ ์ ์ ์์๊น์? ์... ์๊ฐํด๋ณด๋ ์ด๊ฑด ์ฌ์ฉ์๊ฐ '์์ฒญ'ํ ๋ด์ฉ์ด๊ตฐ์. ๋ฐ๋ผ์ reqest object ์์ ๋ค์ด์์ ๊ฑฐ ๊ฐ๋ค์.
์ผ๋ฅธ ์ฝ์์ ์ฐ์ด ํ์ธํด ๋ด ๋๋ค.
/* videoConteroller.js */ export const search = (req, res) => { console.log(req); res.render("search", { pageTitle: "Search" }); };
์ฝ์์ ์ฐ์๋๋ ์ด๋ง์ด๋งํ ์ค๋ธ์ ํธ๋ฅผ ๋์ ธ์คฌ์ต๋๋ค (ํฌ์)
๊ฒ์ํด๋ณด๋ req.query ์์ { tem: 'HeyNana' } ํํ๋ก ๋ค์ด์์๋ค์. ์ด๊ฑธ search.pug์ ๋ฃ์ด์ฃผ๋ฉด ๋๊ฒ ์ต๋๋ค. ๋๊ธธ ์ด๋ฆ์ searchTerm์ด๋ผ๊ณ ํ ๊ฒ์!
/* videoController.js */ const searchTerm = req.query.term; res.render("search", { pageTitle: "Search", searchTerm: searchTerm }); };
/* search.pug */ extends layouts/main block content .search__header h3 '#{searchTerm}'๋ก ๊ฒ์ํ ๊ฒฐ๊ณผ์ ๋๋ค.
์์ req.qeury.tem์ด๋ผ ์จ๋ ๋์ง๋ง, ๋ ๋ญ๊ฐ ์ข ๋ ๊น๋ฆฌํ๊ฒ ์ฐ๊ณ ์ถ๋ค! ๊ทธ๋ด ๋ ์ด๋ ๊ฒ ์จ๋ณผ ์๋ ์์ด์.
const { query: { term } } = req;
๋ฐ๋ก ์ด๋ ๊ฒ์! ์... ์ด๊ฑด ES6 ๋ฌธ๋ฒ์ธ๋ฐ, ์์ฒญ ์ฝ๊ฒ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์๋ค์ ๋จ๋ .
๊ทธ๋ฆฌ๊ณ ์ด ๊ฐ์ ธ์จ ์ ํํ ์ด๋ฆ์ ๋ถ์ผ ์๋ ์์ด์.
const { query: { term : searchTerm } } = req;
๊ทธ๋ฆฌ๊ณ searchTerm: searchTerm์ผ ๋ ๊ทธ๋ฅ searchTerm์ด๋ผ๊ณ ๋ง ์จ๋ ์์์ ์ธ์ํฉ๋๋ค.
/* videoController.js */ export const search = (req, res) => { const { query: { term: searchTerm } } = req; res.render("search", { pageTitle: "Search", searchTerm }); };
(2-19~20) HTML Forms
์ด์ ํผ์ด ํ์ํ ๋ช ๊ฐ์ง ํ์ด์ง์ HTML์ ์์ ํฉ๋๋ค. ๋์ ์ ์ถฉ ๋ผ๋๋ง ๋ง๋ค์ด๋ด์!
์๋ฅผ ๋ค๋ฉด ์๋์ฒ๋ผ ํ๋กํ์ ์์ ํ๋ ํ์ด์ง๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
/* editProfile.pug */ extends layouts/main block content .editProfile h3 ํ๋กํ ์์ form(action=routes.editProfile, method="post") label(for="avatar") Avatar input(type="file", id="avatar", name="avatar") input(type="text", name="userName", placeholder="์ด๋ฆ์ ์ ๋ ฅํ์ธ์.") input(type="email", name="email", placeholder="์ด๋ฉ์ผ์ ์ ๋ ฅํ์ธ์." ) input(type="submit", value="ํ๋กํ ์ ๋ฐ์ดํธ") a(href=`${routes.users}${routes.changePassword}`) ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝํ๊ธฐ
์ด ๊ฒฝ์ฐ์๋ ์ฌ์ฉ์ ๋น๋ฐ๋ฒํธ์ฒ๋ผ ์ค์ํ ์ ๋ณด๊ฐ ๋ค์ด์ค๋ http ํ๋กํ ์ฝ์ด POST ๋ฐฉ์์ด์ฌ์ผ๊ฒ ์ฃ ?
๋์ ์ฐ๋ฆฌ๋ GET ๋ฐฉ์์ ๋ํด์๋ง ์์ฑํ๊ธฐ ๋๋ฌธ์ ์ง๊ธ์ ๋์ํ์ง ์์ ๊ฑฐ์์. ์๊ฑด ๋์ค์ ์์ ํด ์ค์๋ค.
์ด์ userController.js์ userRouter.js์ ๊ฐ์ ์ด์ด์ฃผ๋ ์์ ์ ํฉ๋๋ค.
/* userController.js */ export const editProfile = (req, res) => { res.render("editProfile", { pageTitle: "Edit Your Profile" }) };
/* userRouter.js */ import { editProfile } from "../controllers/userController"; userRouter.get(routes.editProfile, editProfile);
์ด๋ฐ ์์ผ๋ก ํ์ด์ง๋ค์ ์ญ์ญ ์ฐ๊ฒฐํด์ฃผ๋ฉด ๋ฉ๋๋ค. ๐
์ด๋ ์ฃผ์ํ ์ ! userController.js๋ฅผ ์์ฑํ ๋, editProfile → userDetail ์์๋๋ก get() ์์ผ์ผ ํฉ๋๋ค.
๋ง์ฝ userDetail → editProfile ์๋๋ก ํ๋ค๋ฉด, /:id ์๋ฆฌ์ /edit-profile์ด ๋ค์ด๊ฐ๋ค๊ณ ์ธ์ํ๊ธฐ ๋๋ฌธ์ editProfile ํ์ด์ง๊ฐ ๋์ค์ง ์์ต๋๋ค.
/* routes.js */ // Users const USERS = "/users"; const USERS_DETAIL = "/:id"; const EDIT_PROFILE = "/edit-profile"; const CHANGE_PASSWORD = "/change-password";
/* userRouter.js */ userRouter.get(routes.editProfile, editProfile); userRouter.get(routes.userDetail, userDetail);
(2-21~22) Home Controller
์ด๋ฒ์ ๊ฐ์ง DB ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ์์ ์ ํด๋ณผ๊ฒ์!
db.js ๋ฅผ ์์ฑ ํ, ์๋์ฒ๋ผ ๊ฐ์ง ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด ์ค๋๋ค. ๋ด์ฉ์ ์ํ๋ ๋งํผ ์์ฑํ์ธ์~!
/* db.js */ export const videos = [{ id: 1881, title: 'A Study in Scarlet', description: "I should like to meet him.", views: 28, videoFile: "https://interactive-examples.mdn.mozilla.net/media/examples/flower.webm", creator: { id: 12345, name: "Nana", email: "nykim@nykim.net", } }]
๊ทธ๋ฆฌ๊ณ videoController.js ์ importํ์ฌ ํ ํ๋ฉด์ ๋๊ฒจ์ค๋๋ค.
/* videoController.js */ import { videos } from "../db"; export const home = (req, res) => { res.render("home", { pageTitle: "Main", videos: videos //videos ๋ผ๊ณ ๋ง ์จ๋ ๋ผ์! }); };
๊ทธ๋ฆฌ๊ณ home.pug๋ก ๋์ด์ each๋ฌธ์ ์จ์ ๋ฐ์ดํฐ๋ฅผ ์ซ์ - ๋ฟ๋ ค์ค๋๋ค.
/* home.pug */ extends layouts/main block content .videos h1 video each video in videos h2=video.title p=video.description
์ฌ๊ธฐ์ ๋ง์ณ๋ ์ถฉ๋ถํ ๋ฐ์ดํฐ๋ฅผ ์ ๊ฐ์ ธ์ฌ ์ ์์ง๋ง, HTML ์์ ์ ์ข ๋ ํจ์จ์ ์ผ๋ก ํด๋ณผ๊ฒ์.
๋ฐ๋ก pug์ mixin์ ์ด์ฉํด์์ ๐คฉ
SASS์์ mixin์ ์จ๋ณธ ์ ์ด ์๋ค๋ฉด ์ง์ํ ์ ์๊ฒ ์ง๋ง, ๋ฐ๋ณต๋๋ ์ฝ๋๋ฅผ ๋ด๊ณ ์๊ธฐ ๋๋ฌธ์ ํ์ํ ๋๋ง๋ค ๋ถ๋ฌ ์ธ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, "<h1>๋๋ ๋๋๋ค!</h1>"๋ผ๋ HTML์ ๋งค๋ฒ ์จ์ผํ๋ค๋ฉด mixin nana()๋ฅผ ๋ง๋ค๋ฉด ๋ฉ๋๋ค.
ํ ๋ฒ ์ํํด๋ณด์ฃ ! ์ฐ์ views/ ํด๋ ๋ด์ mixins/ ํด๋๋ฅผ ๋ง๋ญ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ฆ์ nanaTitle.pug๋ก ์ง๊ณ ์๋์ฒ๋ผ ์์ฑํฉ๋๋ค.
/* nanaTitle.pug */ mixin nanaTitle() h1="๋๋ ๋๋๋ค!"
์ด๊ฑธ ์ฐ๋ ค๋ฉด include๋ก ๊ฐ์ ธ์ค๋ฉด ๋ฉ๋๋ค. home.pug์์ ์ํํด ๋ณผ๊ฒ์.
/* home.pug */ extends layouts/main include mixins/nanaTitle block content +nanaTitle
๊ทธ๋ฆฌ๊ณ localhost:4000์ ์ ์ํ๋ฉด ์ ๊ฐ์ ธ์์ ์ด ๊ฑธ ๋ณผ ์ ์์ต๋๋ค.
๋ณธ๋ก ์ผ๋ก ๋์๊ฐ์ ์๋ ํ๋ ค๋ ์์ ์ ๊ณ์ ์งํํด ๋ด ์๋ค.
ํ ํ๋ฉด์์๋ ๋น๋์ค ์ ๋ณด๋ฅผ ๋ณด์ฌ์ค ๊ฑด๋ฐ์, ์ด๊ฒ์ ๋ํ mixin์ด ์์ผ๋ฉด ์์ ์ด ์ฐธ ํธํ ๊ฒ ๊ฐ์ต๋๋ค.
๊ทธ๋ผ ์๊น์ฒ๋ผ mixins/ ํด๋ ๋ด์ videoBlock.pug๋ฅผ ๋ง๋ค๋ฉด ๋๊ฒ ์ฃ ?
/* videoBlock.pug */ mixin videoBlock() .videoBlock
์ด๋ฒ์๋ mixin ๋ด์ ์ธ์๋ฅผ ํ๋ ๋ฃ์ ๊ฑฐ์์. ์ด๋ฆ์ด video์ธ ๋น ๊ฐ์ฒด๋ฅผ ์ ๋ฃ์ด์ค์๋ค.
/* videoBlock.pug */ mixin videoBlock(video = {}) .videoBlock
๊ทธ๋ฆฌ๊ณ ์ด ๊ฐ์ฒด๋ก๋ถํฐ ๋ฐ์์จ ํ๋กํผํฐ๋ฅผ ๋ฟ๋ ค์ค์๋ค.
/* videoBlock.pug */ mixin videoBlock(video = {}) .videoBlock video(src=video.videoFile) h2=video.title p=video.description
์ ์ฝ๋๊ฐ ์ดํด ๋์๋์?
videoBlock์ด๋ผ๋ mixin์ ์ธ์๊ฐ ํ๋ ์ ๋ ฅ๋๋ฉด, ๊ทธ ์ธ์์ ์ด๋ฆ์ video๋ก ํ๊ฒ ๋ค๋ ๋ป์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๊ทธ ์ธ์์ ์์ฑ๋ค(videoFile, title, description)์ ๊ฐ์ ธ์ ๋ณด์ฌ์ฃผ๊ฒ ๋ค๋ ๊ฑฐ๊ณ ์.
๊ทธ๋ผ ์ด์ ์ด HTML์ด ํ์ํ home.pug๋ก ๊ฐ์ include ํด์ค๋๋ค.
/* home.pug */ extends layouts/main include mixins/videoBlock block content .videos h1 ๋น๋์ค ๋ชฉ๋ก each item in videos +videoBlock({ title: item.title, description: item.description, videoFile: item.videoFile })
each๋ฌธ์ผ๋ก videos๋ด์ ๋ฃจํ๋ฅผ ๋๋ฆฌ๊ณ , ๊ทธ ์ ๋ณด๋ฅผ ์ฐจ๊ณก์ฐจ๊ณก ๊ฐ์ ธ์ mixin์ผ๋ก ๋๊ฒจ์ฃผ๊ณ ์๋ ๋ชจ์ต์ ๋๋ค.
๋ฐ์ดํฐ๊ฐ ์ค๋ฌด์คํ๊ฒ ํ๋ฌํ๋ฌ ๋ค์ด๊ฐ๋ ๋ชจ์ต์ ๋ณด๋ ๊ธฐ๋ถ์ด ์ฃ ์์!
์์ฐธ, ์ ๋ ์ฌ๊ธฐ์ ๋ฌธ๋ '์...? ๊ทผ๋ฐ each item in videos์์ videos๋ ์ด๋์ ์จ ๊ฑฐ์ง? include๋ ์ ํ๋๋ฐ?'๋ผ๊ณ ์๊ฐํ๋๋ฐ์.
์ด๋ home.pug๋ฅผ ๋ ๋๋งํ๋ videoController.js์์ ๋๊ฒจ์คฌ๊ธฐ ๋๋ฌธ์ด์์ต๋๋ค.
๋ต. ์ด ๊ธ์ ์ฝ์ ๋ฏธ๋์ ์ ์๊ฒ ๊น๋จน์ง ๋ง๋ผ๊ณ ์ ๋ ๋ด์ฉ์ ๋๋ค ๐
/* videoController.js */ import { videos } from "../db"; export const home = (req, res) => { res.render("home", { pageTitle: "Main", videos: videos }); };
์ฌ๊ธฐ๊น์ง ํ์ผ๋ฉด search.pug๋ ์ ๊น ์๋ณด๊ณ ๊ฐ์๋ค.
๊ฒ์์ ์๋ฃํ์ ๋ ํด๋น ํ์ด์ง์๋ ๋น๋์ค ๋ชฉ๋ก์ ๋ฟ๋ ค์ค ๊ฑฐ์์์? ๋ฐ๋ผ์ videoBlock์ ๋ถ๋ฌ์ ์ธ ์ ์๊ฒ ๋ค์.
/* videoController.js */ export const search = (req, res) => { const { query: { term: searchTerm } } = req; res.render("search", { pageTitle: "Search", searchTerm, videos }); };
/* search.pug */ extends layouts/main include mixins/videoBlock block content .search__header h3 '#{searchTerm}'๋ก ๊ฒ์ํ ๊ฒฐ๊ณผ์ ๋๋ค. .search__video each item in videos +videoBlock({ title: item.title, description: item.description, videoFile: item.videoFile })
์์์์์ฃผ ์ฃ ์์์ค
(2-23) Join Controller
์ด๋ฒ์๋ Join ์ชฝ์ ๊ฑด๋๋ ค ๋ณผ๊ฒ์. userController.js๋ก ๋ค์ด๊ฐ๋๋ค.
๊ธฐ์กด์ ์๋ Join์ getJoin์ผ๋ก ์ด๋ฆ์ ๋ฐ๊พธ๊ณ , POST์ ๋ํ postJoin๋ ๋ง๋ญ๋๋ค.
/* userController.js */ export const getJoin = (req, res) => { res.render("join", { pageTitle: "Join" }) }; export const postJoin = (req, res) => { res.render("join", { pageTitle: "Join" }) }
getJoin์ /join ํ์ด์ง๋ก GET ์์ฒญ์ด ์์ ๋ ๋ณด์ฌ์ฃผ๊ณ ,
postJoin์ /join ํ์ด์ง ๋ด์์ ํผ ์ ์ก์ด ์ด๋ค์ก์ ๋(=POST ์์ฒญ์ด ์์ ๋) ๋ณด์ฌ์ฃผ๋ฉด ๋๊ฒ ๋ค์.
์ข์์, ๊ทธ๋ผ globalRouter๋ก ๊ฐ์ ์ฐ๊ฒฐ์์ผ ์ค๋๋ค.
/* globalRouter.js */ import { getJoin, postJoin } from "../controllers/userController"; globalRouter.get(routes.join, getJoin); globalRouter.post(routes.join, postJoin);
ํ์๊ฐ์ ๋ฒํผ์ ๋๋ ์ ๋, ๊ฐ์ ์ด ์๋ฃ๋๋ค๊ณ ๊ฐ์ ํ๊ณ ํํ๋ฉด์ผ๋ก ๋ณด๋ด๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ๊ฒ ์ต๋๋ค.
์ด๋ ๋น๋ฐ๋ฒํธ์ ๋น๋ฐ๋ฒํธ ์ฌ์ ๋ ฅ ๋ ํผ์ ๋ด์ฉ์ด ๊ฐ์์ง ์๋์ง ํ๋จ์ด ํ์ํ๋ฐ์, req์ ๋ธ๋ ค์ค๋ ์ ๋ณด๋ฅผ ๋ณด๋ฉด ๋ ๊ฒ ๊ฐ์ต๋๋ค.
๋ต, ์ ์ ๋ณด๋ req.body์์ ํ์ธํ ์ ์์ด์!
/* userController.js */ export const postJoin = (req, res) => { console.log(req.body); }
์ด๊ฑธ
const bodyInfo = req.body
์ด๋ ๊ฒ ๊ฐ์ ธ์ bodyInfo.userName, bodyInfo.email์ ๊ฐ์ด ์ธ ์๋ ์์ง๋ง,const { body: {} } = req;
์ฒ๋ผ ํจ์จ์ ์ผ๋ก ์ฐ๋ฉด ์ข์ ๊ฒ ๊ฐ์์!/* userController.js */ //... export const postJoin = (req, res) => { const { body: { userName, email, password, password2 } } = req; }
๊ทธ๋ ๋ค๋ฉด ์ด๊ฑธ ๊ฐ์ง๊ณ password !== password2 ์ธ์ง ํ์ธํ๋ฉด ๋๊ฒ ๋ค์.
๋ง์ฝ ์ผ์นํ์ง ์๋ ๊ฒฝ์ฐ์ ์๋ฌ ์ฝ๋ 400์ ๋๊ฒจ์ฃผ์๊ณ ์.
res.status()
๋ฅผ ์ฐ๋ฉด ๋ฉ๋๋ค.๊ทธ๋ผ ๋ธ๋ผ์ฐ์ ๋ "์ ์๋ฌ๋น!"ํ๊ณ ๋น๋ฐ๋ฒํธ ์ ์ฅ ๋ฉ์์ง ๊ฐ์ ๊ฑธ ๋ฌป์ง ์์ ๊ฑฐ์์.
/* userController.js */ //... export const postJoin = (req, res) => { const { body: { userName, email, password, password2 } } = req; if (password !== password2) { res.status(400) } }
๋ง์ฝ ์ผ์นํ๋ค๋ฉด home ํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ ํด์ค ๊ฑฐ์์. routes๋ฅผ importํ ๋ค,
res.redirect()
๋ฅผ ์จ์ค๋๋ค./* userController.js */ import routes from "../routes"; //... export const postJoin = (req, res) => { const { body: { userName, email, password, password2 } } = req; if (password !== password2) { res.status(400) } else { res.redirect(routes.home) } }
์ด์ ํผ์ ์๋ง๊ฒ ์ ๋ ฅ ํ ํ์๊ฐ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ํ ํ๋ฉด์ผ๋ก ์์ ์ด๋ํ๊ฒ ๋ฉ๋๋ค.
(2-24) Log In and User Profile Controller
Join๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ก๊ทธ์ธ์ ๋ํ ๊ฒ๋ getLogin๊ณผ postLogin์ผ๋ก ๋๋์ด ์์ฑํฉ๋๋ค.
/* userController.js */ //... export const getLogin = (req, res) => { res.render("login", { pageTitle: "Login" }) }; export const postLogin = (req, res) => { console.log("๋ก๊ทธ์ธ ์ฑ๊ณต!"); res.redirect(routes.home); } //...
/* globalRouter.js */ //... import { //... getLogin, postLogin } from "../controllers/userController"; globalRouter.get(routes.login, getLogin); globalRouter.post(routes.login, postLogin); //...
ํํธ, ๋ก๊ทธ์ธ ๋์ ๋๋ ํค๋์ ๋ค๋ฅธ ๋ฉ๋ด๋ฅผ ๋ณด์ฌ์ค์ผ ํฉ๋๋ค. ์ ๋ก๋, ํ๋กํ ํ์ธ, ๋ก๊ทธ์์ ๊ฐ์ ๊ธฐ๋ฅ์ ์จ์ผ ํ๋๊น์.
header.pug๋ก ๊ฐ์ ์กฐ๊ฑด๋ฌธ์ ์์ฑํฉ๋๋ค.
/* header.pug */ header h1.title a(href=routes.home) #{siteName} div.search form(action=routes.search, method="get") input(type="text", placeholder="๊ฒ์", name="term", value=searchTerm) ul if !user.isAuthenticated li a(href=routes.login) Login li a(href=routes.join) Join else li a(href=`/videos/${routes.upload}`) Upload li a(href=routes.userDetail) Profile li a(href=routes.logout) Logout hr
์ ๊ธฐ์ ์๋ user.isAuthenticated๋ ์์ง ์กด์ฌํ์ง ์๋ ๋ณ์์ ๋๋ค.
middleware.js๋ก ๊ฐ์ ์์๋ก ์์ฑํด ์ค๋๋ค. ๋ก๊ทธ์ํ ์ ์น์๋ ๊ฑฐ์ฃ ๐
/* middleware.js */ export const localsMiddleware = (req, res, next) => { //... res.locals.user = { isAuthenticated: true, id: 12345 } //... }
์ง์- ๋ฉ๋ด๊ฐ ๋ฐ๋ ๊ฑธ ๋ณผ ์ ์์ต๋๋ค.
๋ค๋ง ์ด ์ํ์์ Profile ๋งํฌ๋ฅผ ๋๋ฅด๋ฉด localhost:4000/:id ๋ก ์ด๋ํ๊ฒ ๋ฉ๋๋ค.
์๊น ์ฐ๋ฆฌ๊ฐ ์์๋ก ๋ฃ์ด๋๋ locals.user.id๋ฅผ ์ฐ๊ฒฐ์ํค๋ ์์ ์ ํด์ผ ํฉ๋๋ค.
routes.js๋ก ๊ฐ์ userDetail ๋ถ๋ถ์ ํจ์๋ก ๋ฐ๊ฟ๋๋ค. ์ธ์๊ฐ ๋ค์ด์ค๋ฉด /users/${์ธ์}๋ฅผ ๋ฆฌํดํ๋ ํจ์์ ๋๋ค.
/* routes.js */ const routes = { //... userDetail: (id) => { if (id) { return `/users/${id}`; } } }
์ธ์๊ฐ ์๋ค๋ฉด, ๊ธฐ์กด๋๋ก /:id ๊ฐ ๋ค์ด๊ฐ๊ฒ ํด์ค์๋ค.
/* routes.js */ const routes = { //... userDetail: (id) => { if (id) { return `/users/${id}`; } else { return USERS_DETAIL; } } }
์ด๋ ๊ฒ ํ๋ฉด ์ธ์๋ก "nana"๋ฅผ ๋๊ฒผ์ ๊ฒฝ์ฐ /users/nana ๋ก routes๊ฐ ์ค์ ๋๊ฒ ์ฃ ?
locals์ ๋ฃ์ด๋ user.id์ ์ฐ๊ฒฐ์์ผ์ค๋๋ค.
/* header.pug */ header //... li a(href=routes.userDetail(user.id)) Profile
๋ง์ง๋ง์ผ๋ก userRouter.js๋ก ๊ฐ์ ํจ์๋ก ๋ฐ๋ userDetail์ ์คํ์์ผ ์ค๋๋ค.
/* userRouter.js */ userRouter.get(routes.userDetail(), userDetail);
์ด์ Profile ๋ฉ๋ด๋ฅผ ๋๋ฅด๋ฉด ์์์ ์ง์ ์ ์ด๋ํ๋ ๊ฑธ ๋ณผ ์ ์์ด์.
๋ฌผ๋ก ์ฃผ์์ฐฝ์ /users/121212์ด๋ /users/nananana ์ด๋ฐ ์์ผ๋ก ์ง์ ์ณ๋ ์ฌ์ ํ ์ ๋์ํฉ๋๋ค.
์๋ํ๋ฉด ์ธ์ ์์ด userRouter.get(routes.userDetail(), userDetail);๊ฐ ์คํ๋์ผ๋, /users/:id๋ก ๋ค์ด์จ ์ ์ด ๋๊ฑฐ๋ ์.
(2-25) More Controllers
userDetail๊ณผ ๋น์ทํ๊ฒ videosDetail๋ /:id๋ฅผ ๋ฐ์ผ๋ ์๊น์ฒ๋ผ ์์ ํ๋ฉด ๋ ๊ฒ ๊ฐ์ต๋๋ค.
/* routes.js */ const routes = { //... videoDetail: (id) => { if (id) { return `/users/${id}`; } else { return USERS_DETAIL; } } }
/* videoRouter.js */ videoRouter.get(routes.videoDetail(), videosDetail);
๊ทธ๋ฆฌ๊ณ ๋น๋์ค ๋ชฉ๋ก์ ๋๋ ์ ๋ videosDetail๋ก ์ด๋ํ๊ฒ ํฉ๋๋ค.
videoBlock.pug๋ก ๊ฐ์ a ํ๊ทธ๋ก ๊ฐ์ธ์ฃผ๋๋ฐ, ์ด๋ video.id๋ฅผ ์ธ์๋ก ๋๊ฒจ์ค๋๋ค.
๊ทธ๋ฌ๋ ค๋ฉด mixin์ ๋ถ๋ฌ์ ์ธ ๋๋ id๊ฐ์ ๋๊ฒจ์ค์ผ๊ฒ ์ฃ ?
id:item.id
๋ก ์ ๋ฌํ๋ฉด ๋๊ฒ ์ต๋๋ค./* videoBlock.pug */ mixin videoBlock(video = {}) .videoBlock a(href=routes.videoDetail(video.id)) video(src=video.videoFile) h2=video.title p=video.description
/* home.pug */ extends layouts/main include mixins/videoBlock block content .videos h1 ๋น๋์ค ๋ชฉ๋ก each item in videos +videoBlock({ title: item.title, description: item.description, videoFile: item.videoFile, id: item.id })
์ด๋ฒ์ ๋ก๊ทธ์์ ์ฐจ๋ก์ ๋๋ค. ์ด ๊ฒฝ์ฐ์ ์ฒ๋ฆฌ๊ฐ ๋๋๋ฉด ํ ํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ ์ํค๋ฉด ๋๊ฒ ๋ค์.
์ฐ์ ์
res.redirect(routes.home);
๋ง ์์ฑํ๊ณ , ์์ธ ๊ธฐ๋ฅ์ ๋ค์์ ์์ ํ๊ฒ ์ต๋๋ค./* userController.js */ export const logout = (req, res) => { //TODO: ๋ก๊ทธ์์ ๊ธฐ๋ฅ ๊ตฌํ res.redirect(routs.home); }
๊ทธ ๋ค์์ ๋จ์ ๊ฑด... ์, ๋น๋์ค ์ ๋ก๋๊ฐ ์์๊ตฐ์.
์ด๊ฒ๋ GET๊ณผ POST ๋ ๋ค ์์ฒญ์ด ์์ ํ ๋, ์ปจํธ๋กค๋ฌ์์ ๋ ๊ฐ๋ก ๋๋์ด์ค๋๋ค.
/* videoController.js */ export const getUpload = (req, res) => { res.render("videosUpload", { pageTitle: "Upload your video" }) } export const postUpload = (req, res) => { res.render("videosUpload", { pageTitle: "Upload your video" }) } //...
/* videoRouter.js */ //... import { getUpload, postUpload, //... } from "../controllers/videoContoller" const videoRouter = express.Router(); videoRouter.get(routes.upload, getUpload); videoRouter.post(routes.upload, postUpload);
/* videosUpload.pug */ extends layouts/main block content .video-upload h3 ๋น๋์ค ์ ๋ก๋ form(action=routes.postUpload, method="post") input(type="file", name="videoFile") input(type="text", name="videoTitle", placeholder="์ ๋ชฉ") input(type="textarea", name="videoDesc", placeholder="์ค๋ช ") input(type="submit", value="์ ๋ก๋")
์, ์ด์ postUpload()์ ๋ํ ๋ณด์์ด ํ์ํ๊ฒ ๊ตฐ์.
์กฐ๊ธ ์ ์ postJoin()์์ req.body๋ก ์ ๋ณด๋ฅผ ๊ฐ์ ธ์๋ ๊ฒ๊ณผ ๋น์ทํ๊ฒ ํ๋ฉด ๋ผ์.
/* videoContoller.js */ import routes from "../routes"; export const postUpload = (req, res) => { const { body: { videoFile, videoTitle, videoDesc } } = req; res.redirect(routes.home) } //...
ํฌ์ผ ๋ญ๊ฐ ์์์ ์ ์ด๋ํ๋ ๊ฑธ ๋ณด๋ ๋ฟ๋ฏํ๋ค์ ๐
์ง๊ธ๊น์ง๋ ๊ฐ์ง ๋ฐ์ดํฐ๋ก ์์ ์ ํ์ง๋ง, ๋ค์๋ถํฐ๋ ์ง์ง! ๋ ์! DB๋ฅผ ๊ฐ๊ณ ์์ ํด ๋ณด๊ฒ ์ต๋๋ค.
๋ค์ ๊ธ์์ ๋ง๋์! (์ด.. ์ธ์ ๊ฐ ์ต๋๋ค!)
728x90๋๊ธ