schema
가 가질수 있는 옵션을 살펴 보도록 한다.
https://mongoosejs.com/docs/schematypes.html
사이트를 살펴 보면 다양한 옵션을 적용 할수 있는데 나중에 한번씩 다뤄 보도록 한다.
한가지 살펴 볼게 있는데
lowercase: boolean, whether to always call .toLowerCase() on the value
uppercase: boolean, whether to always call .toUpperCase() on the value
string
을 작성하거나 schema
에 string
이 있으면
lowercase
또는 uppercase
에 boolean(true or false)
을 적용 할수 있다.
Mongoose
가 값에 대해 toLowercase
또는 toUppercase
호출 하도록 하는 거다.
예시를 한번 적요해 보도록 한다.
title
에 uppercase
를 적용 해본다.
video.js
에서
title: { type: String, required: true, uppercase: true },
새 video
를 생성하기 전까지 바뀐 점은 없어 보일테니 새 video
를 생성 해본다.
확인해 보면 title
을 대문자로 적지 않았는데도 적용이 되어 있는 걸 확인 할수 있다.
mongoose
가 알아서 처리 해줬다. uppercase
가 true
라서 그렇다.
코드를 다시 원상 복귀 해주고 또 뭐가 있는지 살펴 본다.
trim
이 있다. trim
이 무엇을 하는지 확인해 본다.
trim: boolean, whether to always call .trim() on the value
이 코드는 string
양쪽의 빈 공간들을 없애 준다.
예를 들어 어떤 유저가 이렇게 "hello"
적고 거기에 trim
을 추가해주면
"hello".trim()
"hellow"
라고 return
된다.
하지만 누군가 이렇게 " h . "
적은 것에 trim
을 추가 해주면
" h . ".trim()
"h ."
이렇게 결과가 나온다.
"hello".trim()
'hello'
" h .".trim()
'h .'
이렇게 말이다.
양쪽에 있던 스페이스가 사라졌다. 이 기능은 굉장히 유용하다.
그래서 모든 string
에 trim
을 적어 준다.
지금 현재 경우엔 title
이랑 description
이 trim
이 될거다.
hashtags
도 string
이니 적용해 준다.
Video.js
에서
const videoSchema = new mongoose.Schema({
title: { type: String, required: true, trim: true },
description: { type: String, required: true, trim: true },
createdAt: { type: Date, required: true, default: Date.now },
hashtags: [{ type: String, trim: true }],
이런 operation
들은 보통 수동적으로 해야하는데 mongoose
덕에 자동으로 할수 있게 되었다.
계속 강조하지만 데이터에 대한 구체적인 설정은 정말 중요하다.
왜냐하면 데이터 타입을 더 구체화 할 수록 trim
같은 것들을 활용 할수 있게 된다.
Mongoose
가 언제든 도와 줄수 있게 된다.
match: RegExp, creates a validator that checks if the value matches the given regular expression
match
는 정규표현식(regular expression)을 추가하는걸 도와주고
enum: Array, creates a validator that checks if the value is in the given array.
enum
도 있고
minLength: Number, creates a validator that checks if the value length is not less than the given number
minLength
도 있고
maxLength: Number, creates a validator that checks if the value length is not greater than the given number
maxLength
도 활욜 할수 있게 된다.
예시로 한번 적용 해 보도록 한다.
description
에 minLength
를 20자로 설정해 본다.
title
에는 maxLength
를 80으로 설정한다.
Video.js
에서
title: { type: String, required: true, trim: true, maxLength: 80 },
description: { type: String, required: true, trim: true, maxLength: 20 },
이렇게 해주면 title
은 최대 80자, description
은 최소 20자로 작성 하도록 설정 되었다.
이제 두 가지 선택지가 있는데 하나는 여기까지만 설정하는 거다.
그렇게 하면 에러가 조금 생길거다. 사용자와 해당 코드가 제대로 연결되지 않으면 말이다.
그래서 이 경우엔 upload.pug
에 직접 들어가서 min
과 max
를 설정해 준다.
HTML input reference
안에 min
과 max
가 존재한다.
https://developer.mozilla.org/ko/docs/Web/HTML/Element/Input
사이트에서 확인해 보면 text
에 최소값을 부여하는 minlength
upload.pug
에서
block content
if errorMessage
span=errorMessage
form(method="POST")
input(placeholder="Title", requried, type="text", name="title", maxlength=80)
input(placeholder="Description", requried, type="text", name="description", minlength=20)
이렇게 설정해주면 browser
가 도와준다.
새로고침해서 title
칸에서 글자를 입력하는데
비디오를 생성할 때 이렇게 최대 글자수를 넘어가는 걸 제한 하고 있다.
이렇게 되면 생각 할수 있는게 이걸 form
에서도 할수 도 있겠다.
그런데 굳이 database
에 알려줘야 하나?? 둘 다 해줘야 한다.
하나는 사용자를 위한 것인데 만약 누군가가 홈페이지를 해킹을 하면
HTML
에 들어가서 이코드를
maxlength=80
삭제 할수 있다. 그렇게 되면 글자 수 제한이 풀리고 해당 기능에 대한 시스템적 보호가 사라진다.
물론 소수의 사용자들만 홈페이지를 검사하고 코드를 변경 할것이다.
하지만 어쨋든 그들로 부터 홈페이지를 보호해야 하니 database
에도 해줘야 한다.
그런걸 보호 해주려면 HTML
에서 maxLength
와 minLength
를 사용해야 하고
database
쪽에서도 이러한 설정을 해줘야 한다.
이렇게 하면 최대, 최소 글자 수를 변형을 이용한 비정상적인 업로드는 할 수가 없게 되는 거다.
이걸로 어느 정도의 보안이 구축 된거다.
default
에 대해서도 배웠는데 그로 인해 video
를 만들때 meta
에 대해 신경 쓸 필요가 없어 졌다.
이제
hashtags
에 대해 다뤄 보도록 한다.
사실 지금 상황에서는 완벽한(?) 코드이다. 나중에 video
를 수정 할때 문제가 생길 거다.
수정 할때의 form
은 업로드 form
과 생긴건 똑같지만 다르다.
그럼 이때 새로운 hashtags
의 string
데이터를 받게 될텐데
그 때 이 코드를 한 번 더 실행 해 줘야 한다.
hashtags: hashtags.split(",").map((word) => `#${word}`),
문제가 발생 하는걸 보고 고쳐야 이해가 더 잘 될수 있다고 생각하기 때문에
이 코드는 나중을 위해 남겨 두기로 한다.
video
수정을 다룰 때 쯤이면 코드를 복붙하는게 좋은 방법은 아니라는 걸 깨달을 수 있을 거다.
이에 대한 방법은 그 때 가서 알아 보도록 한다. 지금은 현재 코드 만으로도 만족 하면 좋을것 같다.
처음 시작 했을 때의 코드를 생각해 보면 코드가 엄청 길었는데 현재는 어느 정도 줄었다.
나중엔 hashtags
조차 지저분하게 존재하지 않을 거다.
지금 당장은 아니고 나중에 다뤄 보도록 한다.
이번 파트에 어떤걸 하였는지 다시 한번 복습해 보도록 한다.
schema
의 힘은 대단하다. 지금 schema
는 데이터의 종류와 필수 여부를 구분할 줄 알고
string
의 길이도 검사 할수 있고 trim
도 자동으로 실행되고 있다.
여기에 더해 default
값들도 설정 되어 있다. 이것이 바로 구체적인 schema
의 힘이다.
이제 video
를 생성하는 건 다 했다. 이제 해야 할건 홈페이지를 조금 바꾸는 거다.
보다시피 지금 홈페이지는 솔직히 못 생겼다. mixin
에서 불려진 이상한 코드들도 있어서
지금 바로 수정해 보도록 한다.
왜냐하면 이제 가짜 database
가 없기 때문이다. 바로 video mixin
을 바꿔준다.
video
는 title
을 가지고 있으니깐 두고
video.pug
에서
mixin video(video)
div
h4
a(href=`/videos/${video.id}`)=video.title
나머지
ul
li #{video.rating}/5.
li #{video.comments} comments.
li Posted #{video.createdAt}.
li #{video.views} views.
이 부분은 필요 없으니 삭제해 준다.
description
은 p
가 된다. 기억하자. video
는 description
을 갖고 있다.
mixin video(video)
div
h4
a(href=`/videos/${video.id}`)=video.title
p=video.description
이렇게 수정 해준 다음 새로고침을 해주면 title
과 description
만 뜨게 된다.
mixin video(video)
div
h4
a(href=`/videos/${video.id}`)=video.title
p=video.description
small=video.createdAt
hr
hr
을 넣어주면 목록별로 구분선이 생긴게 보인다. 훨씬 보기 좋아 졌다.
small=video.createdAt
을 넣어주면 만든 시간이 나와서 좀 더 보기 좋다.
다른건 굳이 보여줄 필요 없이 이정도면 괜찮은 video
리스트 인것 같다.
전체적인 디자인은 나쁘지 않은데 그렇다고 엄청 대단하고 그런것 아니다.
하지만 데이터는 진짜이다. 서버 재시작 기능도 있고 video
들은 저장되서 어디 안 도망간다.
database
에 저장된 것들이 보여진다.
여기서 꼭 기억해야 하는건
mixin
만 바꾸고 있다.mixin
이 뭘 하는지 대해서 따로 복습을 해보자.
video.pug
가 home.pug
에서 사용되고 있다는 걸 알고 있다.
mixin video(video)
div
h4
a(href=`/videos/${video.id}`)=video.title
p=video.description
small=video.createdAt
hr
extends base
include mixins/video
block content
each view in videos
+video(view)
else
div Sorry nothing found.
그리고 videoController
가 home
이란 controller function
을 갖고 있는것도 안다.
export const home = async (req, res) => {
const videos = await Video.find({});
return res.render("home", { pageTitle: "Home", videos });
};
extends base
include mixins/video
block content
each view in videos
+video(view)
else
div Sorry nothing found.
home
은 모든 video
를 찾아내고 videos
는 video
들로 구성된 array
이다.
이 videos
를 home template
으로 전송 하고 있다.
export const home = async (req, res) => {
const videos = await Video.find({});
return res.render("home", { pageTitle: "Home", videos });
};
home template
는 videos
에 있는 각각의 video
에 video mixin
을 사용하고 있다.
extends base
include mixins/video
block content
each view in videos
+video(view)
else
div Sorry nothing found.
video object
전체를 mixin
으로 보내고 있는 거다.
mixin video(video)
div
h4
a(href=`/videos/${video.id}`)=video.title
p=video.description
small=video.createdAt
hr
video object
는 참고로 이거를 가르킨다.
{ "_id" : ObjectId("6254cdf75499452878f5f059"), "title" : "Something with HTML", "description" : "Delicious", "hashtags" : [ "#adfadsfasfa", "#asdfasfdaf" ], "meta" : { "views" : 0, "rating" : 0 }, "__v" : 0 }
mixin
이 이 video object
들을 얻는 거다. 그리고 video.id
링크로 render
하고 있다.
그래서 video
링크를 하나 클릭하면 url
이 해당 영상의 id
임을 확인 할수 있다.
http://localhost:4000/videos/6252a57c867de2bae69b9f31
그리고 보다시피 mongoDB
가 부여한 id
가 좀 이상한 형태인 것도 알수 있다.
흔히 알고 있는 짧은 숫자가 아니라 완전히 랜덤으로 설정된 거다.
아무것도 하지 않아도 이렇게 알아서 만들어 준다. 하지만 문제가 있는데 바로 이런 메세지가 뜬다는 거다.
Cannot GET /videos/6252a57c867de2bae69b9f31
이건 video router
때문인데 기억 하다시피 video router
는 숫자만 인식하게 되어있다.
이상한 string
이 아니라 이건 다음 파트에서 해보도록 한다.