[Youtube 클론코딩] Video 업로드, mongoose populate, 접근제어

2022. 11. 24. 19:20공부/Javascript

업로드 기능 자체는 이전에 작성했던 프로필 사진 업로드와 같은 기능을 사용해 비디오도 업로드 가능하다.

유저 및 비디오 스키마 변경

다만, 비디오를 업로드 한 사람만 수정 및 삭제할 수 있는 권리를 부여하기 위해서는 두 스키마의 연결이 불가피하다.

DB를 배워보면 각 DB를 join 하기 위해서는 unique key를 이용해야 하는데

mongoDB는 자체적으로 _id 라는 unique key를 발행해서 관리한다.

 

따라서, 비디오의 스키마에서는 업로드 한 사람의 _id를 저장하고

유저의 스키마에서는 해당 유저가 업로드 한 비디오의  _id 들을 배열 형태로 저장하면 된다.

 

여기서 특이한 점은, 데이터의 type을 지정할 때, mongoose.Schema.Types.ObjectId 라고 지정했는데

그 이유는 다음과 같다.

1. mongoose의 populate 기능을 이용하기 위함

2. populate는 ObjectId를 인수로 넘겨받음.

3. ObjectId는 기본 자료형이 아니므로 mongoose에서 가져와야 함

 

비디오 업로드 기능 구현

폼 수정

label과 input 요소를 넣고, form에 multipart/form-data를 넣어준다.

미들웨어 수정

이제 우리는 프로필 사진 뿐만 아니라 비디오 파일도 받게 되므로 둘을 구분해서 저장할 필요가 있다.

또한 데이터 크기를 제한해서 너무 큰 파일은 들어오지 못하게 해야 한다.

라우터 수정

수정한 미들웨어를 라우터에 적용한다.

컨트롤러 수정

업로드 한 파일의 메타데이터는 req.file 에서 다음과 같은 형태의 객체로 저장된다.

우리는 각 스키마에 다음과 같은 정보를 저장해야한다.

유저 스키마

 - 업로드 하는 비디오의 _id

비디오 스키마

- 비디오를 업로드하는 유저의 _id

 

업로드 하는 유저의 _id는 req.session.user._id 에서 접근이 가능하며

업로드 되는 비디오의 _id는 Video.create()의 return 값이 생성된 비디오 객체이므로 거기서 가져오면 된다.

해시 미들웨어 수정

위의 로직을 보면, user에 videos를 추가한 뒤 다시 save()를 하게 된다.

그렇게되면 save 시 기존에 있던 해시된 비밀번호가 또다시 해시되어 저장되는 참사가 발생한다.

따라서 미들웨어를 수정해서 비밀번호를 변경할 때만 해시 기능이 동작하도록 만들어 주어야 한다.

미들웨어에서 this는 user 객체를 가리키게 되는데,

mongoose는 객체의 수정 여부를 감지하는 메소드인 isModified()를 제공한다.

.Mongoose v6.7.3: API docs (mongoosejs.com)

 

Mongoose v6.7.3: API docs

 

mongoosejs.com

따라서 this.isModified()를 사용하여 예외처리를 해주면 된다.

Watch 페이지에 비디오 재생기능 구현

이제 비디오 객체에 비디오 파일 경로 정보가 저장됐으므로 이를 재생할 공간을 만들자.

watch 페이지는 이전에 video _id를 이용해 해당 비디오 정보를 DB에서 가져와 표시하는 페이지였다.

watch.pug

video 객체에서 fileUrl로 해당 파일에 접근이 가능하므로

video 태그를 사용해 watch.pug에 비디오를 띄워준다.

controls 속성을 true로 설정하면 비디오의 재생 정지와 같은 조작이 가능해진다.

mongoose populate

이제 비디오를 누가 업로드 했는지를 표시해보자

현재 비디오 객체에는 owner의 _id 만이 저장돼있으므로 user 모델에서 owner의 정보를 가져와야 한다.

 

이 방법에는 두 가지가 있다.

1. video.owner의 _id를 가지고 User.findById() 해서 가져오기

2. mongoose populate 이용하기

 

첫 번째 방법은 직관적이지만, DB에 무조건 두 번씩 요청을 보내야 하는 단점이 존재한다.

두 번째 방법은 mongoose에서 지원하는 기능으로, 사용하게 되면 한번에 여러 모델에서 정보를 가져온다.

 

사용법은 간단하다. 그냥 findById() 뒤에 populate(Object Id) 를 메소드체이닝 해주면 된다.

그럼 다음과 같이 원래는 Object Id 가 저장됐을 위치에 user 객체가 저장되어 들어오는 것을 볼 수 있다.

owner에 유저 객체가 들어왔다

이렇게 하면 다음처럼 owner 정보를 표시할 수 있다.

접근제어

이제 우리는 비디오에 업로드 유저 정보를 가지고 있다.

따라서 비디오의 주인만이 해당 비디오를 수정하고 삭제할 수 있게 만들어주려고 한다.

그렇게 하기 위해선, 현재 로그인 한 유저(세션 유저)와 비디오의 주인이 서로 같은 사람인지만 파악하면 된다.

 

프론트엔드

pug template에서는 session이 아니라 locals에 접근할 수 있으므로 loggedInUser의 정보와 비교한다.

백엔드

프론트엔드에서 버튼들을 숨겼다고 해도, url을 직접 입력해서 이동하면 수정이 가능해지므로

백엔드에서도 이를 막아주어야 한다.

session.user._id와 video.owner가 서로 다르면, 다시 우회하는 로직을 추가해주었다.

이를 postEdit, deleteVideo에도 동일하게 로직을 구성해주자.

여기서는 id값만 필요하므로 굳이 populate를 실행할 필요는 없다.