[TIL] 211124

Lee Syong·2021년 11월 24일
0

TIL

목록 보기
98/204
post-thumbnail

📝 오늘 한 것

  1. 백엔드에 데이터 저장하는 법 (array database 이용)

  2. 절대 경로 / 상대 경로 / GET method / POST method / res.redirect() / req.body


📚 배운 것

1. 백엔드에 데이터 저장하는 법

1) 가짜 데이터베이스(array database)

지금은 데이터를 받기만 하고 있다. 데이터를 저장할 줄 알아야 한다.
MongoDB를 배우기 전, 일단은 실제 데이터베이스가 아니라 배열로 된 데이터베이스(가짜 데이터베이스)를 사용할 것이다.

일단, videos 배열의 각 video 객체의 id 속성을 1, 2, 3이라고 순서대로 붙인다.
videos 배열을 trending 컨트롤러 밖으로 빼내, 모든 컨트롤러에서 사용할 수 있도록 한다.

// videoController.js
let videos = [
  {
    title: "First Video",
    rating: 5,
    comments: 2,
    createdAt: "2 months ago",
    views: 59,
    id: 1,
  },
  {
    title: "Second Video",
    rating: 5,
    comments: 2,
    createdAt: "2 months ago",
    views: 59,
    id: 2,
  },
  {
    title: "Third Video",
    rating: 5,
    comments: 2,
    createdAt: "2 months ago",
    views: 59,
    id: 3,
  },
];

(1) user가 Home에서 데이터베이스의 모든 비디오를 볼 수 있도록

  • Home에 가짜 데이터베이스에 있는 모든 비디오들을 나열해야 한다.

  • 어제 공부에서 이미 완료한 부분이다.
    video mixin을 이용하여, video(info)의 id를, (Home에서 클릭할 video 타이틀의) url에 넣어줬다.

//- home.pug

extends base
include mixins/video

block content
  h2 Welcome here you will see the trending videos
  each potato in videos
    +video(potato)
  else
    li Sorry nothing found.
//- video.pug

mixin video(info)
  div 
    h4
      a(href=`/videos/${info.id}`)=info.title
      //- 타이틀 클릭 시 '/videos/id 넘버'로 이동
    ul 
      li #{info.rating}/5.
      li #{info.comments} comments.
      li Posted #{info.createdAt}.
      li #{info.views} views.

(2) user가 하나의 비디오를 볼 수 있도록

  • 화면에서 타이틀을 클릭하면, /videos/id 넘버로 이동하게끔 만든다.

이를 위해선 video.pug 파일에서 HTML을 만들고 있으므로 이 파일을 살펴야 한다.
video.pug 파일에서 h4를 수정한다.

//- video.pug

h4
  a(href=`/videos/${info.id}`)=info.title

💡 pug 파일 작성 규칙

  • 변수 + 텍스트 → #{변수}
  • 단, href, id, class 등 '속성'에서는 이렇게 할 수 없다. 자바스크립트의 규칙을 따라야 한다.
    ( " " + or 템플릿 문자열)
  • /videos/id 넘버로 이동했을 때, 클릭한 타이틀의 video가 보이게끔 만든다.

videoController.js 파일에서 see 컨트롤러를 수정한다.

① see 컨트롤러에 req.params(파라미터)를 출력해보는 코드를 추가한다.
그 후 시험 삼아 second video 타이틀을 클릭해 localhost:4000/videos/2로 들어간다.
VS code의 내장 터미널에 의도한 대로 { id: '2' }가 출력되는 것을 확인한다.

// videoRouter.js
videoRouter.get("/:id(\\d+)", see);
//- video.pug

h4
  a(href=`/videos/${info.id}`)=info.title
// videoController.js 수정
export const see = (req, res) => {
  console.log(req.params); // { id: '2' }
}

② 이를 이용해 video의 id를 가져온다.
( videos 배열의 video(info)의 id === video 페이지 url의 파라미터 )

// videoController.js
export const see = (req, res) => {
  const { id } = req.params; // const id = req.params.id; 와 같다
}

③ 가져온 video id를 이용해 videos 배열 안에서 그 video를 가져온다.
가져온 video를 이용해 그 video 타이틀을 출력한다.

// videoController.js
export const see = (req, res) => {
  const { id } = req.params; // 비디오 아이디를 가져옴
  const video = videos[id - 1]; // 비디오를 가져옴
  return res.render("see", { pageTitle: `Seeing ${video.title}` }); // 비디오 타이틀을 보여줌
}

지금은 가짜 데이터베이스를 사용 중이라 실제로 video을 표시할 수는 없다.
그러나, 간략하게 로직을 적어보자면

📌 video 페이지의 url은 어떤 id를 가진다.
📌 그 id를 얻어서 그 id를 가지고 있는 video를 찾는다.
📌 그 video의 정보를 바탕으로 video 페이지에 이것저것을 render 한다.

④ 가져온 video를 see 템플릿으로 보낸다. see.pug 파일에서 video (object)를 다룰 수 있게 되었다.

// videoController.js
export const see = (req, res) => {
  const { id } = req.params;
  const video = videos[id - 1];
  return res.render("see", { pageTitle: `Seeing ${video.title}`, video }); // video: video와 같다
}

⑤ 가져온 video 정보를 이용해 see.pug 파일을 수정한다.
기존의 See Video 텍스트 대신에 video.views를 보여주려고 한다.
삼항 조건 연산자를 이용해 단·복수의 경우를 나누어 view 또는 views를 보여준다.

//- see.pug

extends base.pug

block content
  h3 #{video.views} #{video.views === 1 ? "view" : "views"}

(3) user가 비디오를 수정할 수 있도록

  • home에서 video 타이틀을 클릭해 이동한 '/videos/id 넘버' 페이지에, 영상 수정 페이지로 이동하는 링큏뼟 만든다.

① 링크는 절대 경로와 상대 경로 중 '상대 경로'를 이용해야 한다.
현재 페이지(/videos/id 넘버)에서, 이동해야 하기 때문이다.

//- home.pug

extends base.pug

block content 
  h3 #{video.views} #{video.views === 1 ? "view" : "views"}
  a(href=`${video.id}/edit`) Edit Video → //- 추가 ❗

💡 절대 경로와 상대 경로

  • 절대 경로

    href의 앞머리에 /edit 을 붙이면, 현재 어떤 페이지에 있는지와 상관없이, root 경로 + /edit으로 이동한다.

localhost:4000/profile/edit-profile/password
=====> a(href="/potato")
localhost:4000/potato
  • 상대 경로

    href의 앞머리에 그냥 edit 을 붙이면, 현재 페이지에서 /eidt으로 이동한다. (∵ videoRouter.js)

localhost:4000/profile/edit-profile/password
=====> a(href="potato")
localhost:4000/profile/edit-profile/potato

  • /videos/id 넘버/edit으로 이동했을 때, 수정하려는 video가 보이게끔 만든다.

① 이번에도 수정할 video를 가져와야 하므로, videoController.js 파일에서 앞서 see 컨트롤러를 수정한 대로, edit 컨트롤러도 똑같이 수정한다. ===> (2)의 ① ~ ④ 반복

// videoController.js
export const edit = (req, res) => {
  const { id } = req.params;
  const video = videos[id - 1];
  return res.render("edit", { pageTitle: `Editing: ${video.title}`, video });
};

② 가져온 video 정보를 이용해 edit.pug 파일을 수정한다.
이 페이지에서 user가 video의 title을 수정할 수 있게끔 하려고 한다.
form과 text input, submit input 태그를 만든다.

//- edit.pug (수정 미완)

extends base.pug

block content
  h4 Change Title of Video
  form(action="")
    input(name="title", placeholder="Video Title", value=`${video.title}`, required)
    input(value="save", type="submit")

cf. form 태그의 action 속성 ❕

폼 데이터(form data)를 서버로 보낼 때 해당 데이터가 도착할 url
이 경우에는 따로 action 속성에 값을 적어주지 않는다면
기본적으로 localhost:4000/videos/id 넘버/edit 이 된다.

그러나, 위 코드는 아직 수정이 필요한 미완성 코드이다.

지금까지 한 것은 백엔드에서 비디오를 가져오는 것이었다.
이제 수정한 내용을 다시 백엔드로 보낼 차례이다.

즉, save 버튼을 누르면 input에 입력한 비디오의 새로운 title이 백엔드에 저장되도록 해야 한다.

데이터를 받아올 때는 GET request 즉, videoRouter.get("url", 컨트롤러)을 사용했다.
데이터를 보낼 때는 POST request 즉, videoRouter.post("url", 컨트롤러)을 사용해야 한다.

💡 GET method VS POST method (with 예제)

  • method : formęłź ë°ą 엔드 사이의 정보 전송 방식
  • GET method

    • 기본적으로 method="GET"이 설정되어 있다.
    • 데이터를 받는 게 목적일 때 사용된다.
    • 구글이나 네이버 등에서 검색할 때 사용된다. (검색어가 url 주소에 포함된다.)
//- see.pug

form(action="/save-changes")
  input(name="title", placeholder="Video Title", value=`${video.title}`, required)
  input(value="save", type="submit")

form 태그에 action 속성을 추가했다.
→ save 버튼을 클릭 시 localhost:4000/save-changes?title=Third+Video 로 이동한다. form에 있는 정보가 url에 들어간 것이다.
→ 화면에는 Cannot GET /save-changes 라고 뜬다. 서버는 (다른 GET에 대해선 아는 바가 있지만) GET /save-changes에 대해선 아는 바가 없기 때문이다.

  • POST method

    • 기본값이 GET이므로 POST request뼟 사용하기 위해서는 method="POST"뼟 추가해야 한다.
    • 데이터를 업데이트하는 게 목적일 때 사용된다.
    • 파일을 보내거나 로그인을 하거나 데이터베이스에 있는 값을 바꿀 때 사용된다.
//- see.pug

form(action="/save-changes", method="POST")
  input(name="title", placeholder="Video Title", value=`${video.title}`, required)
  input(value="save", type="submit")

form 태그에 method 속성을 추가했다.
→ save 버튼 클릭 시 localhost:4000/save-changes 로 이동한다. (?title=...은 표시되지 않는다.)
→ 화면에는 Cannot POST /save-changes 라고 뜬다. 서버는 POST에 대해선 아는 바가 없기 때문이다.

위 내용을 참고해서 edit.pug 파일을 다시 아래와 같이 수정한다.

//- edit.pug

form(method="POST")
  input(name="title", placeholder="Video Title", value=`${video.title}`, required)
  input(value="save", type="submit")

서버는 아직 POST에 대해선 아는 바가 없는 상태이다.


  • 이제부터 save 버튼(submit input)을 클릭할 때 form의 행동을 통제하고자 한다.

① 서버에 해당 POST에 대해 알려주기 위해 videoRouter.js 파일에서 똑같은 url로 post request를 보내는 코드를 작성한다.
get과 post를 구분할 수 있도록 기존의 edit 컨트롤러의 이름을 getEdit으로 변경했다. (videoController.js 에서도)

// videoRouter.js

// videoRouter.get("/:id(\\d+)/edit", getEdit);
// videoRouter.post("/:id(\\d+)/edit", postEdit);
// 위 두 줄의 코드는, 아래 한 줄의 코드로 바꿔쓸 수 있다.

videoRouter.route("/:id(\\d+)/edit").get(getEdit).post(postEdit);

② videoController.js 파일에 postEdit 컨트롤러를 추가한다.
일단은 아무것도 반환하지 않고, 형식만 작성해줬다.

✔ getEdit 컨트롤러는 form을 render 해준다.
✔ postEdit 컨트롤러는 video를 수정해준다. (아직 코드 작성 x)

// 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) => {}; // 추가

이제 localhost:4000/videos/3/edit 으로 들어가면, Cannot POST 가 나오지 않고 브라우저는 무한 로딩을 한다.
서버에 post가 뭔지는 알려줬지만, post request를 받았을 때 뭘 해야 하는지는 정해주지 않았으므로 서버는 어떤 처리도 하지 않은 채 브라우저에 응답하지 않아 브라우저가 무한 대기 상태인 것이다.

따라서, 서버가 post request를 처리 및 응답할 수 있도록 postEdit 컨트롤러를 마저 완성해줘야 한다.
postEdit 컨트롤러의 역할은 video를 수정하는 것인데 여기서는 video의 title만을 바꿔보자.


  • postEdit 컨트롤러가 input 값을 이용해 video의 title을 업데이트하도록 한다.

① postEdit 컨트롤러에 res.redirect()를 추가한다.
res.redirect()는 '지정한 url로 브라우저가 자동으로 이동'하도록 한다.
여기서는 save 버튼을 누르면 see 페이지(즉, /videos/id 넘버)로 돌아가도록 할 것이다.

// videoController.js

export const postEdit = (req, res) => {
  const { id } = req.params;
  return res.redirect(`/videos/${id}`);
};

② postEdit 컨트롤러에 req.body를 추가한다.
앞서 req.params를 통해 url의 파라미터 값(=video(info)의 id)를 받아왔던 것처럼
req.body를 통해 'form의 value를 받아올 수 있다.'

그 전에 먼저, express가 form을 다룰 수 있도록, server.js에 middleware를 추가해야 한다.
(위치가 중요하다. videoRouter보다 전에 추가해야 한다. 그래야만 어떤 request가 videoRouter.post에 이르렀을 때 이미 req.body가 준비되어 있을 수 있다.)
express.urlencoded()는 express가 form의 value를 이해한 후 자바스크립트 object 형식으로 변환하도록 한다.
extended 옵션은 form의 body에 있는 정보들이 보기 좋은 형식을 갖추도록 한다.

// server.js
app.use(express.urlencoded({ extended: true }))
// videoController.js
export const postEdit = (req, res) => {
  const { id } = req.params;
  const { title } = req.body;
  videos[id - 1].title = title; // 가짜 데이터베이스 사용 중이라 이렇게 업데이트해준 것. 실제로는 이렇게 안함
  return res.redirect(`/videos/${id}`);
};

로직 정리

📌 페이지를 get 해서 들어간다.
📌 (다시 무엇인가를 클릭해 들어가서) form에 뭔가를 입력한 후 submit 버튼을 누른다.
📌 submit 버튼을 눌러서 post request를 보내면, post 컨트롤러가 실행된다.
📌 post 컨트롤러는 req.body로부터 form에 입력한 값을 얻어, 이를 바탕으로 이것저것을 한다.

(4) user가 비디오를 업로드할 수 있도록

(3) 복습용 parctice이다

여기서 비디오를 업로드한다는 것은
가짜 데이터베이스(array database)에 video 객체를 하나 더 추가한다는 뜻이다.

  • base.pug 파일에 nav(upload video 링큏) 추가하기
//- base.pug

doctype html
html(lang="ko")
  head
    title #{pageTitle} | Wetube
    link(rel="stylesheet" href="https://unpkg.com/mvp.css")
  body
    header
      h1=pageTitle
      nav
        ul 
          li 
            a(href=`/videos/upload`) Upload Video →
    main
      block content
    include partials/footer.pug
  • upload.pug 파일 만들기
//- upload.pug

extends base.pug

block content
  form(method="POST")
    input(name="title", placeholder="Video Title", required)
    input(value="upload", type="submit")
  • videoRouter.js 파일 수정하기
// videoRouter.js
import express from "express";
import {
  see,
  getEdit,
  postEdit,
  getUpload,
  postUpload,
} from "../controllers/videoController";

const videoRouter = express.Router();

videoRouter.get("/:id(\\d+)", see);
videoRouter.route("/:id(\\d+)/edit").get(getEdit).post(postEdit);
videoRouter.route("/upload").get(getUpload).post(postUpload); // 추가

export default videoRouter;
  • videoController.js 파일 수정하기 ( getUpload, postUpload 컨트롤러 추가 )
// videoController.js
const videos = [
  {
    title: "First Video",
    rating: 5,
    comments: 2,
    createdAt: "2 months ago",
    views: 1,
    id: 1,
  },
  // 이하 생략
];

export const trending = (req, res) => {
  return res.render("home", { pageTitle: "Home", videos });
};

export const getUpload = (req, res) => {
  return res.render("upload", { pageTitle: "Upload Video" });
};

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("/");
};

+마지막으로 모든 pug 파일 안에서 .pug 삭제하고, see video를 watch video로 바꿈


✨ 내일 할 것

  1. 강의 계속 듣기

  2. wsl2 재설치, MongoDB 설치

profile
능동적으로 살자, 행복하게😁

0개의 댓글