#6.0, #6.1 Array Database
먼저 fake database를 만들어주자.
let videos = [
{
title: "Video #1",
rating: 5,
comments: 2,
createAt: "2 minutes ago",
views: 59,
id: 1,
},
{
title: "Video #2",
rating: 4,
comments: 100,
createAt: "1 hours ago",
views: 1000,
id: 2,
},
{
title: "Video #3",
rating: 3,
comments: 9,
createAt: "40 minutes ago",
views: 59,
id: 3,
}
];
export const trending = (req, res) => {return res.render("home", {pageTitle: "Home", videos})};
export const search = (req, res) => {return res.send("Search")};
export const edit = (req, res) => {return res.render("edit")};
export const watch = (req, res) => {return res.render("watch")};
export const upload = (req, res) => {return res.send("Upload Video.")};
export const deleteV = (req, res) => {return res.send("Delete Video.")};
videos라는 fake array database를 만들어줬다.
pug에서 attribute에는 #{} 방식으로 변수와 일반 텍스트를 함께 사용할 수 없다.
대신 “안에 ${}로 입력하거나 +로 연결해주는 방식을 사용해야 한다.
mixin video(info)
div
h4
a(href=`/videos/${info.id}`)=info.title
ul
li #{info.rating}/5
li #{info.comments} comments.
li Posted #{info.createAt}.
li #{info.views} Views.
이제 비디오 제목을 누르면 해당 페이지로 이동할 수 있도록 만들어주었다.
export const watch = (req, res) => {
const { id } = req.params;
const video = videos[id-1];
return res.render("watch", {pageTitle: `Watching ${video.title}`, video})
};
videocontroller.js
video 객체에 다음과 같이 접근해주고,
extends base.pug
block content
h3 #{video.views} #{video.views === 1 ? "view" : "views"}
watch.pug를 다음과 같이 바꿔주었다. Inline 연산자를 통해서 복수를 처리해줄 수 있다.
상대경로 vs 절대경로
- a(href=”jin”) → localhost:4000/root/a/b/c/jin
- a(href=”/jin”) → localhost:4000/jin
웹해킹 문제에서도 위 방법을 사용해봤었다…. 잘 기억하길.
extends base.pug
block content
h3 #{video.views} #{video.views === 1 ? "view" : "views"}
a(href=`${video.id}/edit`) Edit video →
이렇게 상대경로로 정해주면

Edit video가 생기고,

클릭하면 상대경로로 이동할 수 있게 된다!
#6.2, #6.3 Edit video
extends base.pug
block content
h4 Change Title of Video
form(action='')
input(placeholder='Video title',value=`${video.title}`,required)
input(value='Send',type='submit')
먼저 간단하게 다음과 같이 edit.pug를 수정해주었다.

적용된 모습.
이제는 실제로 저 Send 버튼을 눌렀을 때 backend로 어떻게 데이터가 전송되는지에 대해 알아보자.
form의 default 값은 GET이다. method를 바꿔주려면 method=”POST”와 같이 선언해줘야 한다.
extends base.pug
block content
h4 Change Title of Video
form(method="POST")
input(placeholder='Video title',value=`${video.title}`,required)
input(value='Send',type='submit')
그리고 videoRouter.js에 새로운 post 라우터를 선언해주고,
import express from "express";
import { watch, upload, deleteV, getEdit, postEdit } from "../controllers/videocontroller";
const videoRouter = express.Router();
videoRouter.get("/:id(\d+)", watch);
videoRouter.get("/:id(\d+)/edit", getEdit);
videoRouter.post("/:id(\d+)/edit", postEdit);
export default videoRouter;
그에 맞는 함수를 videocontroller.js에도 선언해줘야 한다.
export const getEdit = (req, res) => {
const { id } = req.params;
const video = videos[id-1];
return res.render("edit", {pageTitle: `Editing ${video.title}`, video})
};
export const postEdit = (req, res) => {
};
이제 post 데이터를 어떻게 처리할 것인지에 대해 알아보자.
videoRouter.route("/:id(\d+)/edit").get(getEdit).post(postEdit);
// same as
videoRouter.get("/:id(\d+)/edit", getEdit);
videoRouter.post("/:id(\d+)/edit", postEdit);
먼저, 위와 같이 route를 활용해서 두줄을 한줄로 줄여줄 수 있다.
하나의 url에 get, post를 모두 사용할 때 쓸 수 있는 유용한 방법이다.
pug의 form 태그에서 데이터를 받아오기 위해서는 server.js에 express 미들웨어를 삽입해줘야 한다.

이렇게 express.urlencoded를 추가해주면 urlencoded 페이로드로 들어오는 요청을 구문 분석하고 바디 파서 기능을 수행해준다.
export const postEdit = (req, res) => {
const { id } = req.params;
const {title} = req.body;
videos[id-1].title = title;
return res.redirect(`/videos/${id}`);
};
그리고 videocontroller.js의 postEdit 함수를 다음과 같이 바꿔주면…


title이 바뀌게 된다!!!!
+req.body에서 데이터를 보기 위해서는 꼭 name을 넣어주자!

#6.5, #6.6 More Practice
실제 데이터베이스를 다루는 부분으로 넘어가기 전에, 지금까지 배운 것을 더 연습해보자.
이번엔 비디오를 업로드하는 페이지를 만들어볼 것이다.
- videoRouter.js에 추가해주기
videoRouter.route("/upload").get(getUpload).post(postUpload);
videoRouter.js
- videocontroller.js에 함수 추가해주기
export const getUpload = (req, res) => {
return res.render("upload",{pageTitle: 'Upload Video'})
};
export const postUpload = (req, res) => {
return res.redirect('/');
};
videocontroller.js
- upload.pug 만들어주기
extends base.pug
block content
form(method='POST')
input(placeholder='title',type='text',required)
input(type='submit',value='Upload')
- postUpload 수정
export const postUpload = (req, res) => {
const {title} = req.body;
const newVideo = {
title,
rating: 0,
comments: 0,
createAt: "just now",
views: 0,
id: videos.length + 1,
}
videos.push(newVideo);
return res.redirect('/');
};
우리의 가짜 Database인 array에 newVideo 객체를 넣어주는 코드를 추가해주었다.

upload에서 ‘Last Video’라는 제목을 가진 video를 upload 해주면,

새로운 video가 추가된 것을 확인할 수 있다.
title을 클릭하면,

id가 4인 video 페이지로 이동된다!
#6.7, #6.8 MongoDB & Mongoose
MongoDB는 noSQL 기반 DB이다.
즉, SQL로 쿼리를 주고받지 않고, document-based이므로 자바스크립트 객체와 같은 모습으로 데이터를 주고 받게 된다.
설치 방법
brew tap mongodb/brew
brew install mongodb-community@7.0
mongosh
→ mongoDB shell 열기
Mongoose → MongoDB와 node.js를 연결해주는 역할!
npm i mongoose
import mongoose from "mongoose";
mongoose.connect("mongodb://127.0.0.1:27017/wetube");
const db = mongoose.connection;
const handleOpen = () => console.log("Connected to DB ✅");
const handleError = (error) => console.log(`DB ERROR : ${error}`);
db.on("error", handleError);
db.once("open", handleOpen);
db.js
mongoose.connect()의 url은 mongosh를 입력해서 얻은 url이다.
db.on → 이벤트가 발생할때마다 항상 실행되는 함수
db.once → 처음 한번만 실행되는 함수
해당 ./db를 server.js에 import 해주어야 한다.
#6.9, #6.10 CRUD & Video model
CRUD→C: Create, R: Read, U: Update, D: Delete

/src에 models 디렉토리를 만들고, /models에 Video.js를 만든다.
이제 Video model을 만들 것이다. DB 스키마를 만드는 것이라고 생각하면 될 듯!
import mongoose from "mongoose";
const videoSchema = new mongoose.Schema({
title: String,
description: String,
createdAt: Date,
hashtags: [{type: String}],
meta:{
views: Number,
rating: Number
}
})
const Video = mongoose.model("Video", videoSchema);
export default Video;
Video.js
위와 같이 videoSchema를 만들어주었다.
그리고 Video라는 모델을 만들어서 export 해주었다.
마지막으로 server.js에 /models/Video.js를 import 해주면 된다.
→ import “./models/Video.js”
#6.11, #6.12, #6.13 Our First Query
우리는 server.js에 모델을 import 했다. 그런데 import한 모듈이 많아질수록 코드가 길어질 것이다.
따라서 init.js를 따로 만들어주자.
import "./db";
import "./models/Video";
import app from "./server";
const PORT = 4000;
const handleListening = () =>
console.log(`✅ Server listening on port http://localhost:${PORT} 🚀`);
app.listen(PORT, handleListening);
init.js
import express from "express";
import morgan from "morgan";
import globalRouter from "./routers/globalRouter";
import videoRouter from "./routers/videoRouter";
import userRouter from "./routers/userRouter";
const app = express();
const logger = morgan("dev");
app.set("view engine", "pug");
app.set("views", process.cwd() + "/src/views");
app.use(logger);
app.use(express.urlencoded({ extended: true }));
app.use("/",globalRouter);
app.use("/videos",videoRouter);
app.use("/users",userRouter);
export default app;
server.js
init.js와 server.js로 분리해주었다.
그리고 package.json에서 scripts를 변경해줘야 한다.
"scripts": {
"dev": "nodemon --exec babel-node src/init.js"
},
package.json
이제 가짜 array database를 삭제하고, 실제 database와 연결시켜보자!
db로부터 데이터를 가져온다는 것은 즉, js 코드 내에 없는 데이터를 받아와서 사용한다는 것이다. 즉, db로부터 받아온 후에 실행을 해야한다는 것이며, 만약 데이터를 받아오기 전에 실행되면 문제가 발생할 수 있다!!!!
쿼리를 주는 방법으로는 callback과 promise가 존재한다.
- callback
→ 더 이상 사용하지 않는다고 하며 에러를 출력한다..
- promise
export const home = async(req, res) => {
try{
const videos = await Video.find({});
console.log(videos);
return res.render("home", {pageTitle: "Home", videos});
}
catch(err){
return res.render("server-error",err);
}
};
async, await를 사용하면, await 부분에서 데이터를 받아올 때까지 기다릴 수 있다.
만약 await 시에 에러가 발생하면 catch 부분으로 넘어간다. (오류 처리 가능)
** 본 글은 노마드 코더의 ‘유튜브 클론코딩’ 강의를 참조하여 작성하였습니다. **