Documentation
을 다시 보면 exec(execute)
라는 걸 볼수 있다.
https://mongoosejs.com/docs/api.html#model_Model.findById
await Adventure.findById(id).exec();
이게 뭘 하는 거냐면 이 부분이 query
를 실행(execute
)시키는 거다.
그런데 문제가 뭐냐면
videoController.js
에서 보면
const video = await Video.findById(id);
이 부분은 이미 실행이 된거다. 그래서 이 부분에 exec
를 입력하면
const video = await Video.findById(id).exec();
딱히 뭐가 달라지는건 없다. 새로고침을 해도 똑같은 video
페이지가 보인다.
무슨 일이 벌어지고 있는 거냐면 쉽게 말해 Mongoose
내부적으로
만약 execute
를 호출하면 promise
가 return
될 거다.
하지만 promise
에 대해선 전혀 신경 안 써도 된다. 그래서 그냥 삭제해 주도록 한다.
이렇게 해도 상관없는 이유는 지금 async
랑 await
을 쓰고 있기 때문이다.
그래서 똑같이 보이고 코드도 달라질게 없는 거다. 하지만 제대로 처리해야 할 것은
누군가가 존재하지 않는 video
페이지를 방문 했을때 어떻게 해야 하느냐 이다.
확인해 보기 위해서 video
를 console.log
로 출력하고
export const watch = async (req, res) => {
const { id } = req.params;
const video = await Video.findById(id);
console.log(video);
return res.render("watch", { pageTitle: video.title, video });
};
이게 굉장히 중요하다. 만약 누군가 존재하지 않는 video
페이지로 접근 할수도 있기 때문이다.
새로고침하고 터미널로 가보면 예상대로 video object
가 출력이 된다.
http://localhost:4000/videos/625528ea216ff9baacd03f1a
하지만 만약 여기
url
중에 숫자 0을 6으로 바꾸면 어떻게 될까?
어떤 일이 일어나냐면 무한 로딩중이다. (현재는 바로 페이지가 오류가 난다.
사이트에 연결을 할수 없다고 뜬다.)
터미널로 가서 확인해 보면
null
null
이 출력이 되고 있다. 단순히 말해서 video
를 찾을 수 없단 소리이다.
하지만 아직 에러가 있다. 이 에러는 MongoDB
에서 부터 나온 에러가 아니다.
이 경우에는 null
로 부터 title
이라는 property
를 찾을 수 없다는 게 문제이다.
TypeError: Cannot read properties of null (reading 'title')
무슨 말이냐면 영상 검색에 실패 했기 때문에 video
는 null
인 상태고
return res.render("watch", { pageTitle: video.title, video });
여기 pageTitle
을 보면 video
의 title
을 요구하는데 하지만 그 영상을 찾을 수 없었기 때문에
video
는 null
인 거다.
URL
을 바꿔준 해당 id
를 가진 영상은 존재하지 않기 때문이다.
굉장히 흥미로운 문제이다. 왜냐하면 뭔가를 코딩 할때는 계획한 조건에서는 잘 작동하게 되어 있다.
하지만 다른 조건들도 항상 체크해야 한다. 검색한 비디오가 존재하지 않을 때 어떻게 대처 할지
보다시피 현재 error
가 발생 하고 있다.
title
을 요청했지만 그 video
가 존재하지 않아서 null
이 되는 error
이다.
null
은 title
이 없다. 이 에러 메세지는 완전 별로 이다.
TypeError: Cannot read properties of null (reading 'title')
에러메세지가 video
는 null
이고 null
은 title
을 갖고 있지 않습니다.
이런 식으로만 나와도 조금 나은 메세지 일텐데 말이다.
이제 할수 있는건 미리 확인 하는거다.
videoController.js
에서
export const watch = async (req, res) => {
const { id } = req.params;
const video = await Video.findById(id);
if (video) {
return res.render("watch", { pageTitle: video.title, video });
}
return res.render("404", { pageTitle: "Video not found." });
만약 video
가 존재 한다면 render watch
부분을 리턴하고 존재하지 않는다면
아직 만들지는 않았지만 404를 렌더한다.
404도 만들어 준다.
views
폴더 안에 404.pug
파일을 만들어 준다.
extends base
watch.pug
와 같이 extends base
라고 만들어 준다.
그리고 base
의 header
에 pageTitle
이 필요하니 만들어 줬다.
이제 video
가 존재하지 않는 경우도 대처 한거다. 새로고침 해서 확인해 보면
"Video not found."
라고 잘 뜬다. 이제 not found
도 제대로 처리하게 되었다.
이제는
base.pug
로 돌아가서 모든 링크에 재대로 접근 할수 있게 고쳐 보도록 한다.
body
header
h1=pageTitle
nav
ul
li
a(href="/videos/upload") Upload Video
li
a(href="/") Home
링크를 이렇게 하나 생성하면 Home
으로 바로 가는 링크가 생긴 거다.
이제 query
들을 실행 할 뿐만 아니라 결과도 체크해서 404 not found
도 제대로 리턴한다.
이제 영상을 편집 할수 있게 해본다.
현재는 edit page
를 보면 에러가 있다. 보다 먼저 손 봐 줄건 edit video control
을 완성 하는 거다.
getEdit
과 postEdit
이 있는데 getEdit
부터 작업 할거다.
ID
는 req.params
로 부터 받는다. watch function
한 것과 비슷하게 할 거다.
일단은 비디오를 찾아야 하니
export const watch = async (req, res) => {
const { id } = req.params;
const video = await Video.findById(id);
if (!video) {
return res.render("404", { pageTitle: "Video not found." });
}
return res.render("watch", { pageTitle: video.title, video });
};
export const getEdit = async (req, res) => {
const { id } = req.params;
const video = await Video.findById(id);
if (!video) {
return res.render("404", { pageTitle: "Video not found." });
}
return res.render("edit", { pageTitle: `Editing`, video });
};
만약 await
를 쓸거라면 async
를 입력해야 한다는걸 잊지 말자.
그리고 만약 영상이 존재하지 않는다면 렌더해야 할것도 있다. 그리고 주로 사용하는 패턴이데
보통 에러를 먼저 처리하는게 좋다. 정리하자면 에러를 먼저 확인하고 처리하면
나머지 코드들은 다 정상적인 케이스에 작동 할 코드가 될거다.
만약 video
가 null
이면 error
를 return
하고 아닌 경우엔 정상적으로 리턴
(!video)
(비디오가 없을때)
(video === null)
이둘은 같은 개념이다.
보통 에러가 날 경우를 if
를 써서 처리하고 if
바깥에 있는 코드들은 정상적인 경우
실행 될것들로 코딩한다.
그래서 getEdit
에 이렇게 정리 하게 된거다.
왜냐하면 이번에도 편집할 영상이 존재 하는지 먼저 확인을 할 필요가 있기 때문이다.
그러니 영상을 먼저 검색하고 만약 존재하지 않는 영상이면 "Video not found."
를 출력한다.
다시 강조 하자면 에러체크를 먼저 한다. 그러면 나머지 코드는 에러 걱정 할 필요가 없다.
하지만 여기서 return
하는게 굉장히 중요하다.
if (!video) {
res.render("404", { pageTitle: "Video not found." });
}
return res.render("watch", { pageTitle: video.title, video });
};
만약 이런식으로 return
을 안하면 영상이 존재하지 않을때 javascript
는
res.render("404", { pageTitle: "Video not found." });
이 부분을 실행하고 계속해서 그 밑에 코드도 실행 할거다.
return res.render("watch", { pageTitle: video.title, video });
그러니까 function
을 바로 끝내야하니 if
안에 return
을 집어 넣어야 한다.
if
안에 return
이 없으면 javascript
는 영상이 없을때 if
안의 코드를 실행하고
그 밑에 코드들도 실행 할텐데 그런 원하지 않는 결과이니까 꼭 return
을 해주도록 한다.
getEdit
에서는 먼저 영상을 검색하고 만약 영상이 없을 경우엔 404를 리턴할거다.
정상적인 처리 방법이고 여기에서 edit
페이지를 출력할거다.
return res.render("watch", { pageTitle: video.title, video });
edit.pug
를 확인해 보면
input(name="title", placeholder="Video Title",value=video.title,required)
edit
페이지는 video object
를 필요로 한다. 그래서 Editing
이라고 하고
video object
를 보내준 결과이다.
return res.render("edit", { pageTitle: `Editing`, video });
이제 새로 고침을 하면 input
에 이미 제목이 preload
되어 있다.
return res.render("edit", { pageTitle: `Edit ${video.title}`, video });
그리고 Edit
하고 video.title
을 넣어 줬다. 새로 고침 해주면 잘 작동 한다.
아직 edit
해야 할것 들이 많다. 그러기 위해선 pug
파일을 수정 해야 한다.
제목도 필요 하지만, 영상 설명과 해시태그도 필요 하다.
upload.pug
에서
input(placeholder="Description", requried, type="text", name="description", minlength=20)
input(placeholder="Hashtags, separated by comma.", requried, type="text", name="hashtags")
이 두줄을 복사해서
edit.pug
에다가 넣어 준다.
block content
h4 Change Title of Video
form(method="POST")
input(name="title", placeholder="Video Title",value=video.title,required)
input(placeholder="Description", requried, type="text", name="description", minlength=20)
input(placeholder="Hashtags, separated by comma.", requried, type="text", name="hashtags")
input(value="Save",type="submit")
이제 제목뿐만 아니라 설명, 해시태그도 편집 할수 있다. 그리고 input
들은 upload form
에서 가져와서 내용이 없다.
하지만 지금 edit template
이기 때문에 미리 form
에 내용을 채워 줘야한다.
block content
h4 Change Title of Video
form(method="POST")
input(name="title", placeholder="Video Title",value=video.title,required)
input(placeholder="Description", requried, type="text", name="description", minlength=20, value=video.description)
input(placeholder="Hashtags, separated by comma.", requried, type="text", name="hashtags", value=video.hashtags)
input(value="Save",type="submit")
이런식으로 value
를 추가해 준다. 그러면 빈 input
이 아닌 가지고 있는 정보의
input
이 나오게 된다.
하지만 이제는 hashtags
에 문제가 생겼다. 뭐가 문제냐면 hashtags
는 array
이다 보니
있는 그대로 array
로 보여지고 있다. hashtags
를 array
되도록 정했기 때문이다.
그래서 지금부터 hashtags
를 format
해 줄 거다.
array
같이 보이지 않고 string
처럼 보여야 자연스럽기 때문이다.
이제 어떻게 주어진 array
를 string
으로 format
할지 알아 본다.
array
를 복사해서 inspect
를 통해 console
로 가서 복사한 array
에 다가
.join()
만 붙여 주면 끝난다.
이제 edit.pug
로 돌아가서
input(placeholder="Hashtags, separated by comma.", requried, type="text", name="hashtags", value=video.hashtags.join())
.join()
를 붙여 준다. 이제 새로고침하면 array
가 아닌 string
으로 나온다.