유튜브 클론코딩 복습노트-4
-
이 포스팅은 노마드 코더의 "유튜브 클론코딩" 강의를 바탕으로 작성되었습니다. https://nomadcoders.co
-
썸네일 이미지 출처: https://developers.google.com/youtube
2021년 7월 26일
지난 시간까지 Express의 기초와 pug를 사용한 HTML 코드 재사용에 대해 알아보았습니다. 이번 포스팅에서는 데이터베이스 파트로 들어가기 전에 데이터베이스와 서버의 연동이 어떤 느낌인지 연습해보는 시간을 갖도록 하겠습니다.
비디오별 페이지 구현🎬
비디오 페이지로 이동
우선 지난번에 만든 비디오 리스트를 수정해보겠습니다. 비디오 제목을 클릭하면 해당 비디오로 이동할 수 있게 만들겠습니다. 비디오 리스트를 만드는 틀은 mixins
폴더 안에 있는 video.pug
파일입니다. 비디오 제목마다 a
태그를 달아주겠습니다.
mixin video(video)
div
h4
a(href=`/videos/${video.id}`)=video.title
ul
li #{video.rating}/5.
li #{video.comments} comments.
li Posted #{video.createdAt}.
li #{video.views} views.
원래 h4
에는 video.title
만 들어갔지만 해당 비디오 페이지로 이동을 위해 a
태그를 만듭니다. 비디오 객체에서 해당 비디오의 id
값을 얻어와 URL에 추가합니다.
이제 비디오 제목을 누르면 해당 비디오 페이지로 이동할 수 있습니다.
비디오 페이지 타이틀 수정✍️
비디오 제목을 눌러 비디오 페이지로 이동한 모습입니다. 지금은 어떤 비디오를 눌러도 똑같은 제목에 똑같은 내용이 뜹니다. 페이지 타이틀은 비디오 타이틀로, 내용은 해당 비디오의 조회수로 바꿔보겠습니다. 우선 타이틀을 바꾸기 위해 videoContoller.js
파일의 see
컨트롤러 부분을 수정합니다.
export const watch = (req, res) => {
const { id } = req.params;
const video = videos[id - 1];
res.render("watch", {
pageTitle: `Watching: ${video.title}`,
});
};
우선, watch.pug
파일과 연결되는 컨트롤러이므로 컨트롤러 이름을 watch
로 바꾸는 것이 나을 것 같습니다. videoRouter
파일에서도 이름을 수정해서 가져와야 합니다.
const { id } = req.params;
컨트롤러가 현재 비디오가 어떤 비디오인지 알게 하기 위해서 req
의 params
에서 id
값을 가져옵니다. 사용자가 a
태그를 눌렀을 때 이동하게 되는 URL에 포함된 id
값입니다.
const video = videos[id - 1];
아직 데이터베이스가 없기 때문에 id
에 해당하는 비디오 객체를 얻는 과정은 임시로 만들었던 비디오 객체 배열을 통해 구현합니다. id
값은 첫 번째 비디오부터 순서대로 메겨저 있으므로 id - 1
을 통해 비디오의 인덱스를 구합니다.
res.render("watch", {
pageTitle: `Watching: ${video.title}`,
});
마지막으로 비디오 객체의 title
값을 pageTitle
에 적절히 넣습니다. 이제 비디오를 클릭해 들어가면 해당 비디오 제목이 타이틀로 나오게 됩니다.
비디오 조회수 표시하기👍
이제 저 watch videos! 부분에 조회수를 표시하겠습니다. 23 views 처럼 조회수를 표시하되, 만약 조회수가 1이라면 1view 처럼 단수형으로 표시해야 합니다. 우선 조회수 정보를 뽑아내야하기 때문에 videoController
의 watch
컨트롤러에 video
객체를 전달합니다.
export const watch = (req, res) => {
const { id } = req.params;
const video = videos[id - 1];
res.render("watch", {
pageTitle: `Watching: ${video.title}`,
video, // here
});
};
이제 video
객체를 watch.pug
파일에서 다룰 수 있습니다.
조건에 따라 view와 views 중 하나를 보여주는 기능은 자바스크립트의 삼항연산자를 사용하여 간단하게 구현할 수 있습니다. watch.pug
파일을 다음과 같이 수정합니다.
extends base.pug
block content
h3 #{video.views} #{video.views === 1 ? "view" : "views"}
a(href=`${video.id}/edit`) Edit Video →
먼저 video
객체의 views
값을 표시합니다. 그리고 만약 그 값이 1이라면 view
를, 아니라면 views
를 표시합니다. 이제 웹 페이지에 들어가보면 정상적으로 조회수가 표시됩니다.
89는 1이 아니므로 views
가 표시됩니다.
조회수가 1이면 view
가 표시됩니다.
비디오 수정 기능 구현
a 태그 만들기
이제 비디오 페이지로 이동해 edit 버튼을 누르면 비디오 제목을 수정할 수 있는 기능을 구현해보겠습니다. 우선 watch.pug
에 에딧 페이지로 갈 수 있는 a
태그를 만듭니다.
extends base
block content
h3 #{video.views} #{video.views === 1 ? "view" : "views"}
a(href=`${video.id}/edit`) Edit Video →
비디오 페이지 이동과 마찬가지로 비디오 객체의 id
값을 URL에 포함시킵니다. 이렇게 URL을 만들어주면 이전에 만들어두었던 videoRouter
가 에딧 페이지로 이동시켜줄 것입니다.
참고로 →
이라고 적힌 부분은 화살표 모양으로 렌더링됩니다.
Edit form 만들기📑
이제 edit.pug
파일에 비디오 제목을 수정할 수 있는 form
을 만들겠습니다. 그 전에 pug 파일에서 비디오 객체를 사용할 수 있도록 videoController.js
파일의 edit
컨트롤러에게 해당 id
의 비디오 객체를 전달합니다.
export const edit = (req, res) => {
const { id } = req.params;
const video = videos[id - 1];
res.render("edit", {
pageTitle: `Editing: ${video.title}`,
video,
});
};
그리고 pageTitle
에 들어갈 부분도 Editing: ${video.title}
로 수정합니다.
이제 edit.pug
파일에 다음과 같이 수정 form
을 만듭니다.
extends base
block content
h4 Change Title of video
form(method="POST")
input(name="title", placeholder="Video Title", value=video.title, required, type="text")
input(type="submit", value="save")
form
과 input
에 들어가는 속성들은 HTML 기초에서 다루므로 생략하겠습니다. 다만 form
의 속성인 method
의 종류에 대해서는 조금 알아둘 필요가 있습니다.
GET vs POST📮
사용자가 form
양식을 작성하고 제출 버튼을 누르면 폼 데이터가 HTTP로 전송됩니다. 이 데이터를 전송하는 방법이 get
과 post
로 나뉘는 것입니다. 둘의 특징을 정리하면 다음과 같습니다.
GET:
- 폼 데이터가 URL 안에 name/value 쌍으로 포함됩니다.
- URL의 길이는 3000 char로 제한됩니다.
- URL에 데이터가 그대로 노출되므로 개인정보를 GET으로 전송하면 안 됩니다.
- 구글 검색 같이 사용자가 검색한 페이지로 바로 이동하는 것에 적합합니다.
POST:
- 데이터가 HTTP request body 안에 들어있습니다.
- 사이즈 제한이 없습니다.
- 데이터베이스나 서버에 데이터를 보낼 때 적합합니다.
비디오 타이틀을 수정하면 결국 데이터베이스에 반영될 것이므로 수정 form
은 데이터를 POST 방식으로 전송합니다. 여기까지 만들고 제출 버튼을 누르면 브라우저에서 오류 메시지를 보여줍니다.
라우터 수정
지금까지 만든 서버는 POST 요청에 응답하는 방법을 모르기 때문입니다. videoRouter.js
파일에서 /:id/edit
URL의 POST 요청에 응답할 수 있도록 해줍니다.
videoRouter.get("/:id/edit", edit);
videoRouter.post("/:id/edit", postEdit);
기존에 있던 edit 페이지의 get
밑에 post
를 달아줍니다. 이렇게 경로가 같을 경우 둘을 합쳐서 반복되는 코드를 줄일 수 있습니다.
videoRouter.route("/:id/edit").get(edit).post(postEdit);
컨트롤러 수정
컨트롤러도 조금 손봐줘야 합니다. 우선, edit 컨트롤러는 하나밖에 없으므로 컨트롤러를 getEdit
과 postEdit
으로 나눠줍니다.
export const getEdit = (req, res) => {
const { id } = req.params;
const video = videos[id - 1];
res.render("edit", {
pageTitle: `Editing: ${video.title}`,
video,
});
};
export const postEdit = (req, res) => {};
videoRouter.js
파일 위의 import
문도 같은 이름으로 수정해줍니다.
이제 비디오 수정 기능이 작동할 수 있게끔 postEdit
컨트롤러를 만들어줍니다.
export const getEdit = (req, res) => {
const { id } = req.params;
const video = videos[id - 1];
res.render("edit", {
pageTitle: `Editing: ${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}`);
};
아직 데이터베이스가 없기 때문에 videos
배열의 해당 비디오의 title
을 직접 수정해줍니다. 물론 이렇게 수정된 제목은 페이지를 새로고침하면 원래대로 돌아옵니다. 타이틀 변경을 마치고 해당 비디오 페이지로 redirect
합니다.
urlencoded()
이제 pug파일, 라우터 파일, 컨트롤러 파일에 비디오 수정 기능 구현을 위한 작업을 모두 마쳤습니다. 하지만 작업 하나를 더 해줘야 합니다. 사용자가 폼 데이터를 담은 HTTP 리퀘스트를 보내면 Express가 해당 리퀘스트의 body
를 파싱할 수 있어야 합니다. 익스프레스에서는 urlencoded()
라는 내장 미들웨어를 제공합니다.
server.js
파일에 해당 미들웨어를 적용해줍니다.
app.set("view engine", "pug");
app.set("views", process.cwd() + "/src/views");
app.use(morgan("dev"));
app.use(express.urlencoded({ extended: true })); // here
app.use("/", globalRouter);
app.use("/videos", videoRouter);
app.use("/users", userRouter);
비디오 업로드 기능 구현📽
마지막으로, 비디오를 업로드하는 기능을 구현해보겠습니다. 업로드 기능 역시 데이터베이스가 없는 상태에서 간단하게 느낌만 내보겠습니다.
네비게이션 만들기
업로드 버튼은 nav
안에다 만들겠습니다. base.pug
파일의 header
안에 nav
태그를 만듭니다.
doctype html
html(lang="ko")
head
link(rel="stylesheet" href="https://unpkg.com/mvp.css")
title #{pageTitle} | Wetube
body
header
h1=pageTitle
nav
ul
li
a(href="videos/upload") Upload Video
main
block content
include partials/footer.pug
upload.pug 만들기
a
태그를 누르면 이동할 페이지를 만듭니다. 비디오 제목을 입력하고 제출할 수 있는 폼을 만들어줍니다.
extends base
block content
form(method="POST")
input(name="title", placeholder="Title", required, type="text")
input(type="submit", value="Upload video")
폼과 관련된 내용은 비디오 수정 기능 구현 때 만들었던 폼과 동일합니다.
컨트롤러 수정
이제 videoController.js
파일에서 컨트롤러를 수정합니다.
export const getUpload = (req, res) => {
return res.render("upload");
};
export const postUpload = (req, res) => {
const { title } = req.body;
const newVideo = {
title,
rating: 0,
comments: 0,
createdAt: "Just now",
views: 0,
id: videos.length + 1,
};
videos.push(newVideo);
return res.redirect("/");
};
컨트롤러의 구분을 위하여 기존 upload
컨트롤러의 이름을 getUpload
로 바꿉니다. 그리고 postUpload
컨트롤러를 새로 만듭니다.
postUpload
역시 HTTP body에서 폼에 입력한 비디오 제목을 가져옵니다.
const { title } = req.body;
그리고 기존 비디오 객체 배열에 추가할 새 비디오 객체를 만듭니다.
const newVideo = {
title,
rating: 0,
comments: 0,
createdAt: "Just now",
views: 0,
id: videos.length + 1,
};
title
만 입력값을 받아오고 나머지 요소들은 임으로 정합니다. id
에는 현재 비디오 배열의 길이에 1을 더한 값을 넣습니다(id가 1부터 시작하기 때문에).
videos.push(newVideo);
return res.redirect("/");
새로 만들어진 newVideo
객체를 videos
배열 뒤에 추가하고 사용자를 홈으로 redirect
시킵니다.
POST 요청 처리
videoRouter.js
에서 위에서 만든 두 컨트롤러를 import
합니다. 그리고 비디오 수정 때와 같은 방식으로 get
과 post
요청을 /upload
URL로 받습니다.
videoRouter.route("/upload").get(getUpload).post(postUpload);
이제 홈 페이지에서 Upload Video 를 누르고 들어가서 비디오 제목을 입력하면 새로 만들어진 비디오 객체가 정상적으로 비디오 배열에 추가된 것을 볼 수 있습니다.
이번 포스팅에서는 지금까지 배운 기초 지식을 기반으로 서버와 데이터베이스의 소통을 가볍게나마 연습해보았습니다. 다음 포스팅에서는 MongoDB의 기초에 대해 배워보겠습니다.
<참고 문서>
Author And Source
이 문제에 관하여(유튜브 클론코딩 복습노트-4), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@soonitoon/유튜브-클론코딩-복습노트-4저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)