유튜브 클로닝 #6-1 CRUD (1) CREATE & READ

이현정·2022년 4월 15일
1

🔖 강의 범위: #6.15~19

Preview

지난 시간에 db 를 설치, 연결, 데이터 형식 세팅까지 해주었다. 이제 진짜 db와 소통하는 crud 기능을 설치해줄 차례이다.

비디오를 업로드하는 페이지를 만들면서 어떤식으로 CRUD 를 설정할 수 있는지 알아볼 것이다.
그럼 각 기능을 쪼개서 먼저 C(create) 기능부터 만들어보자.
그리고 이어서 R(read) 기능을 통해 만들어진 데이터가 잘 불러와지는지 확인해볼 것이다.
비디오를 업로드하는 페이지를 만들며 새로운 데이터를 데이터베이스에 만들고 읽어오는 작업을 해보자..

핵심 개념

몽구스 스키마 타입 확인

Mongoose 스키마는 Mongoose 모델을 구성하기 위한 객체로 생각할 수 있습니다.
https://mongoosejs.com/docs/schematypes.html

몽구스 스키마 타입 정의

몽구스의 모든 것은 스키마로 시작합니다. 각 스키마는 MongoDB 컬렉션에 매핑되고 해당 컬렉션 내 문서의 모양을 정의합니다.
https://mongoosejs.com/docs/guide.html

강의 내용

C(Create) 기능

step 0. upload 페이지를 만들어준다.

1) upload template
2) controller 에서 getUpload, postUpload
3) router 에서 videos/upload 라우팅 해주고 get, post 각각 기능 추가해줌

step 1. template 손보기: input name

Video model 에 작성한 형식대로 form 은 데이터가 오기를 기대할 것이다.
upload form 이 있는 upload template 으로 돌아가서 input name 에 데이터 형식에 작성했던 키값들을 적어주자.

step 2. controller, 새로운 데이터 양식 만들기: new Model, Model.create

이제 컨트롤러로 돌아와 클라쪽에서 들어온 정보들을 어떻게 다룰지 보자.

1) new Model : 컨트롤러에 데이터형식에 맞춰 새로운 데이터를 만들어주자.

//view
const videoSchema = new mongoose.Schema({
    title: String,
    description: String,
    createdAt: Date,
    hashtags: [String],
    meta: {
        views: Number,
        rating: Number,
    },
});

//controller
export const postUpload = (req, res) => {
  const { title, description, hashtags } = req.body;
  const video = new Video ({
    title,
    description,
    createdAt: Date.now(),
    hashtags: hashtags.split(",").map(word => `#${word}`),
    meta: {
      views: 0,
      rating: 0,
    }
  });
  return res.redirect(`/videos/${id}`);
}
  • 이 과정에서 문자열을 split("spliter"), map() 매서드를 배웠다.
  • 참고로 새로운 데이터가 생성됐을 뿐, 아직 db 에 저장되진 않았다.
  • 콘솔에 찍어보면 추가해주지 않은 id 가 부여된 걸 알 수 있는데 new Model 로 새 데이터 생성시 몽구스가 자동으로 id 를 데이터에 부여한다.
  • 혹시 데이터형식 타입에 맞지 않게 입력하더라도 (ex. title:String 인데 title:6 으로 설정) 몽구스가 알아서 타입 변경을 설정해둔 model 에 맞게 변경해준다.
    • 만약 타입 변경이 불가능한 경우라면 (ex. views: Number 인제 views: "lalala" 로 설정) 그 부분은 삭제하고 데이터를 만든다.
  • 데이터 타입 외에 다른 속성을 넣고 싶다면 이렇게 해주면 된다.
    ex. { type: String, required: true, default: Date.now }

    ❗️ default 속성

    • default 속성 설정시 이 때 속성에 함수가 있다면 () 는 빼고 해줘야 한다.
      Date.now() (x)
      Date.now (o)
    • default 속성 설정시 컨트롤러에 굳이 언급해줄 필요 없다. 알아서 디폴트값으로 생성해주니까.
    • default 속성 외에 mongoose.Schema 에 적용할 수 있는 다양한 속성들공식 홈페이지에서 안내해준다.

2) .save(): 이제 만들어진 데이터를 데이터베이스에 저장하자

1)

await 생성된데이터변수명.save()

🌟 ! 주의: 잊지말자. 데베 다룰 때는 await 걸어서 기다려줘야 한다.

3) Model.create() : 또 다른 옵션

만들 때 new Model 대신 Model.create() 을 써주면 .save() 기능을 포함하고 있는 매서드이기 때문에 .save() 를 따로 써줄 필요가 없다.

export const postUpload = async (req, res) => {
  const { title, description, hashtags } = req.body;
  const video = new Video ({
    title,
    description,
    createdAt: Date.now(),
    hashtags: hashtags.split(",").map(word => `#${word}`),
    meta: {
      views: 0,
      rating: 0,
    }
  });
  await 생성된데이터변수명.save()
  return res.redirect(`/videos/${id}`);
}

export const postUpload = async (req, res) => {
  const { title, description, hashtags } = req.body;
  await Video.create ({
    title,
    description,
    createdAt: Date.now(),
    hashtags: hashtags.split(",").map(word => `#${word}`),
    meta: {
      views: 0,
      rating: 0,
    }
  });
  return res.redirect(`/videos/${id}`);
}

은 같다.

4) try/catch : error 시 에러메세지 창 만들기

try/catch 구문으로 에러 잡고, 에러메세지 있을 시 view 에도 띄어주기

//controller

export const postUpload = async (req, res) => {
  const { title, description, hashtags } = req.body;
  try {
    const video = new Video ({
      title,
      description,
      hashtags: hashtags.split(",").map(word => `#${word}`),
    });
    await video.save();
    return res.redirect(`/`);
  } catch (error) {
    console.log(error);
    return res.render("upload", {pageTitle: "Upload Video", errorMessage: error._message});
  }
}
// upload template

extends base
include mixins/videoMixin

block contents
    h1 Welcome to Wetube!
    a(href="/videos/upload") Upload Videos →
    each video in videos 
        +videoMixin(video)
    else 
        li Sorry nothing found.

R(Read) 기능: Model.findOne(), Model.findById()

1. read Home

postUpload 과정을 마치고 마지막에 res.redirect("/") 을 설정해 줌으로써 form 에서 정보 전송이 끝나면 홈 화면으로 돌아가도록 해주었다. 그리고 홈 화면에는 home template 이 db 에서 데이터를 불러와 띄우도록 해두었다. 홈화면에 가보자: 데이터를 잘 불러오는 것을 확인할 수 있다.

// videoController / home

export const home = async (req, res) => {
  try {
    const videos = await Video.find({});
    return res.render("home", { pageTitle: "Home", videos })
  } catch(error) {
    console.log("server error", error)
  }
}

2. read Watch

불러와진 데이터 타이틀을 클릭하면 watch template 을 띄어 데이터에 관한 상세 내용을 읽을 수 있도록 설정해보자.

//video router
videoRouter.get("/:id([0-9a-f]{24})", watch);

! 정규식 해석: 0부터 9까지, a부터 f까지 문자와 숫자의 배열이며 24개의 문자열이다

//video controller

export const watch = async (req, res) => {
  const { id } = req.params; 
  const video = await Video.findById(id);
  return res.render("watch", { pageTitle: video.title, video });
}

! Video.findById(id) 라는 몽구스의 기능 하나를 더 배웠다.

//watch template
block contents
    div
        p=video.description
        small=video.createdAt
    a(href=`${video.id}/edit`) Edit Video →

이렇게 생성한 데이터를 잘 불러와지는지도 잘 확인할 수 있었다.

3. 추가) 존재하지 않는 비디오 요청시 띄울 화면 만들기

if 조건문을 이용해 404 템플릿(만들어라)을 띄운다.

export const watch = async (req, res) => {
  const { id } = req.params; // const id = req.params.id; 의 es6 버전.
  const video = await Video.findById(id);
  if (!video) {
    return res.render("404", { pageTitle : "404, Not Found"});
  } 
  return res.render("watch", { pageTitle: video.title, video });
}

추가 공부 ?

req.body

The req. body object allows you to access data in a string or JSON object from the client side => 클라이언트 사이드, 즉 웹페이지의 form 에서 요청(req)한 데이터들을 볼 수 있게 해준다는 말. 여기에는 input 에 적은 값(키는 input name), 요청한 웹사이트의 params 등이 해당된다.

mongoose.Model 에서 default 설정해주기

db 관련 CIL

db 에 show dbs -> use "내 db" -> show collections -> 내 데이터들 모음집이 나온다.

참고로, 이름이 Video 가 아니라 videos 로 뜨는 이유는, 몽구스가 알아서 제일 첫번쨰는 소문자로 + 끝에 s 를 붙이기 때문이다.

.excu

몽구스 매서드 중 다루지 않은 핵심 내용은 .excu 이다. .excu 는 excute(실행하다) 의 약자로 promise 를 따로 걸지 않아도 해당 매서드를 건 statement 에 await 을 걸어준다. 그런 게 있다, 정도로만 이해하고 넘어가자.

요점

  • 데이터베이스의 데이터를 생성(create) 해보았다: Schema() 설정, Model(), Model.create() 또는 new Model(), callback & promise, try/catch
  • 데이터베이스의 데이터를 찾고 읽는(read) 작업도 해보았다: Model.find(), Model.findById(), 이때 promise 필수

0개의 댓글