들어가기
DB연결 및 사용 방법을 알아본다.
https://mongoosejs.com/docs/guides.html
공식문서는 대단히 중요함.
import mongoose from 'mongoose'
///mongoose를 import한다
mongoose.connect('mongodb://127.0.0.1:27017/jmtube', {
useNewUrlParser: true,
})
///cmd창에서 mongosh를 치면, 나오는 주소를 입력한다.
///(mongodb://127.0.0.1:27017/jmyube)
const db = mongoose.connection
const handleOpen = () => console.log('Connected to DB')
///콜백으로 넣어도 되고, 만들어서 넣어도 되고
db.on('error', (error) => console.log('DB error', error))
///on은 여러번, once 한번
db.once('open', handleOpen)
///DB에 연결되면, Connected to DB가 찍힌다.
import './db'
///맨 위에 ./db를 import해 준다.
import Video from './models/Video'
import express from 'express'
import morgan from 'morgan'
import globalRouter from './routes/globalRouter'
import userRouter from './routes/userRouter'
import videoRouter from './routes/videoRouter'
const PORT = 4000
const app = express()
const logger = morgan('dev')
app.set('view engine', 'pug')
app.set('views', process.cwd() + '/src/views')
app.use(logger)
app.use(express.urlencoded({ extended: true }))
app.use('/', globalRouter)
app.use('/videos', videoRouter)
app.use('/users', userRouter)
app.listen(PORT, () => console.log('Server start in 4000'))
import mongoose from 'mongoose' ///시작은 import mongoose
// export const formatHashtags = (hashtags) =>
// hashtags.split(',').map((word) => (word.startsWith('#') ? word : `#${word}`))
--->DB에 hashtags가 save되기전에 단어를 분리하고 앞에 #을 붙여주는 함수.
///mongo는 document형식으로 하나의 전체 묶음으로 입력되므로
///schema생성 문법을 잘 본다.
const videoSchema = new mongoose.Schema({
title: {
type: String,
required: true,
upppercase: true, ///무조건 대문자로 입력되게
trim: true, ///빈 칸은 자동적으로 없애주게
maxLength: 140, ///최대 입력 글자.
},
description: String,
createdAt: { type: Date, required: true, default: Date.now },
///기본값은 현재시간. 뒤에 ()이 있으면, 입력값과 중복실행됨.
hashtags: [{ type: String, trim: true }], ///배열로 설정,
meta: {
views: { type: Number, default: 0, required: true },
rating: { type: Number, default: 0, required: true },
},
})
///.static는 DB에 입력되기 전 middleware를 만든것임.
///formatHashtags라는 middleware로 hashtags를 분리, 입력함.
///맨 위의 함수로 만들어도 되고, 아래 아래방법을 이용해도 되지만,
///static가 가장 깔끔함, 공식문서 참조할 것!!
videoSchema.static('formatHashtags', function (hashtags) {
return hashtags
.split(',')
.map((word) => (word.startsWith('#') ? word : `#${word}`))
})
// videoSchema.pre('save', async function () {
// console.log('we saving this.', this)
// this.hashtags = this.hashtags[0]
// .split(',')
// .map((word) => (word.startsWith('#') ? word : `#${word}`))
// })
///pre방법도 나쁘지는 않음.
const Video = mongoose.model('Video', videoSchema)
export default Video
///위에서 만든 videoSchema를 Video로 만들어서 export함.
import Video from '../models/Video'
///가장 먼저 사용할 DB를 import한다.
// const handleSearch = (error, videos) => {
// console.log('error', error)
// console.log('videos', videos)
// }
export const trending = async (req, res) => {
try {
const videos = await Video.find({}).sort({ createdAt: 'desc' }) //asc
///await는 DB에서 load를 할 경우, load가 끝난 다음에 next를 실행하게
///하는 명령어, await가 쓰이면, 반드시 위에 asyn를 붙여준다.
///.sort(desc, asc)는 DB에서 load한 data 배열순서를 지정(최신data부터~)
return res.render('home', { pageTitle: 'Home', videos })
///db로 부터 load한 data를 videos에 담아, home.pug에 videos로 넘겨준다.
} catch (error) {
return res.render('server-error', { error })
///error가 있을시 try, catch로 처리함.
}
}
///홈 path에서 DB(Video)를 불러줌
///Video Detail 페이지
export const seeVideo = async (req, res) => {
const { id } = req.params ///req.params로 id를 받아옴.
const video = await Video.findById(id)
///findById로 Video DB에서 video를 찾아서 return해 줌.
if (!video) {
return res.render('404', { pageTitle: 'Video not found' })
}
///video가 없을때, 404 페이지로 redirect함.
return res.render('watch', { pageTitle: video.title, video })
///watch 페이지에 id로 찾은 video를 return해줌.
}
///Edit Video 페이지 controller.(GET요청)
export const getEdit = async (req, res) => {
const { id } = req.params
///id 받아옴
const video = await Video.findById(id)
///받아온 id로 edit 함 video 찾음.
if (!video) {
return res.render('404', { pageTitle: 'Video not found' })
}
///video없을 경우 404 페이지로 redirect시킴
return res.render('edit', { pageTitle: 'Edit Video', video })
/// 'videoRouter.route('/:id([0-9a-f]{24})/edit').get(getEdit).post(postEdit)
/// 위의 path로 req가 오면, edit.pug로 get method로 요청처리함.
}
///Edit Video 페이지 controller.(POST요청)
export const postEdit = async (req, res) => {
const { id } = req.params
///id 받아옴.
const { title, description, hashtags } = req.body
///edit.pug에서 보낸 argument(title, description, hashtags)들을
///req.body로 받음.
const video = await Video.exists({ _id: id })
///exists명령어는 id로 video를 찾아서, true, false로 return함
if (!video) {
return res.render('404', { pageTitle: 'Video not found' })
}
///video가 없을 시, 404페이지로 return함.
await Video.findByIdAndUpdate(id, {
title,
description,
hashtags: Video.formatHashtags(hashtags),
})
///findByIdAndUpdate는 id로 video를 찾고, 찾은 video를 update(edit)함.
///hashtags의 formatHashTags는 Video.js에서 만들어 준, static임.
///이렇게 사용된다는 것을 잘 본다.
// video.title = title
// video.description = description
// video.hashtags = hashtags
// .split(',')
// .map((word) => (word.startsWith('#') ? word : `#${word}`))
// await video.save()
---->findByIdAndUpdate를 사용안하고 썡으로 올리는 코딩, 참고만 할 것!
return res.redirect(`/videos/${id}`)
///update(edit)가 끝났으면, videoDetail page로 redirect한다.
}
///videoRouter.route('/:id([0-9a-f]{24})/delete').get(deleteVideo)
/// 위 path의 controller. /delete path의 get Controller.
/// findByIdAndDelete명령어로 id로 찾아서 찾은 video를 DB에서 삭제.
///삭제 후, 홈 path로 redirect함.
export const deleteVideo = async (req, res) => {
const { id } = req.params
await Video.findByIdAndDelete(id)
return res.redirect('/')
}
///videoRouter.route('/upload').get(getUpload).post(postUpload)
///위 path의 /upload path의 get Controller. upload.pug 페이지 rendering
export const getUpload = (req, res) => {
return res.render('upload', { pageTitle: 'Upload Video' })
}
///videoRouter.route('/upload').get(getUpload).post(postUpload)
///위 path의 /upload path의 post controller.
export const postUpload = async (req, res) => {
const { title, description, hashtags } = req.body
///req.body로 upload.pug에서 보낸 argument(title, description, hashtags)
///를 받음.
///DB에 입력하는 것이므로, await를 사용함.
try {
await Video.create({
title,
description,
hashtags: Video.formatHashtags(hashtags),
})
return res.redirect('/')
///try~catch로 Video DB에 video를 upload하고, 홈 path로
///redirect함.
///여기서도 Video.js에서 만든 formatHashtags라는 static사용함.
} catch (error) {
console.log(error)
return res.render('upload', {
pageTitle: 'Upload Video',
errorMessage: error._message,
})
}
///error발생시, upload.pug 페이지에, error message를 보내주는데,
///console에 보면 error message는 error._message로 찍힘.
// const dbVideo = await video.save()
//console.log(dbVideo)
---->위의 Video.create방법이 아닌 다른 방법.
---->입력된 video를 console에 찍어 보고싶을떄, 사용.
}
///globalRouter.get('/search', search)
///위의 path로 get 요청시 적용되는 controller
///search path는 globalRouter에 존재함.
///위와 같이 router에 /:id 와 같은 것이 붙은것이 아니면,
///search.pug에서 검색어(keyword)를 입력했을때
///localhost:4000/search?keyword=avengers 와 같은 형식으로 url찍힘.(어밴져서검색시)
///참고로 req.ruery는 url data임.
export const search = async (req, res) => {
const { keyword } = req.query
///req.query로 keyword 받아옴.
let videos = []
///let으로 빈 배열의 videos를 만들어 놓음.
if (keyword) {
videos = await Video.find({
title: {
$regex:new RegExp(keyword, 'i')
},
})
}
///seatch.pug에서 입력한 keyword로 Video DB에서 입력한
///keyword가 title에 포함된 video를 찾아서, videos에 담아줌.
res.render('search', { pageTitle: 'Search', videos })
///찾은 videos들을 search.pug파일에 videos를 넘겨줌.
}
https://regexr.com/
search에서 title에 { $regex:new RegExp(keyword, 'i')}는 title중에
keyword가 포함된 video를 찾는다는 뜻. 밑의 공식문서를 참조하면 된다.
search관련 해서~~
videos title을 검색할때 keyword가 포함된것들을 regex operator를 통해 검색해 줄 수 있다.
(regex = regular expression의 약자)
const { keyword } = req.query;
.
.
regex: new RegExp(keyword, "i") -> keyword가 포함된 것들을 검색.
regex: new RegExp(^${keyword}
, "i") -> keyword로 시작되는 것들을 검색.
regex: new RegExp(${keyword}$
, "i") -> keyword로 끝나는 것들을 검색.
-(여기서 "i" = Welcome,welcome 둘다 같게 해주는것 즉 lowercase,uppercase의 구분을 없게 해주는것)
( mongoose가 아닌 mongoDB가 해주는 기능이다)
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/RegExp
NOTICE!!! path의 id 관련해서~~
mongoDB에 save를 하면 return받는 id가 342lk34j23klh이런식인데,
이런 id를 js가 읽을 수 있게 바꿔주는것임([0-9a-f]{24})<--요게, 공식문서 참조
videoRouter.route('/:id([0-9a-f]{24})/delete').get(deleteVideo)
https://docs.mongodb.com/manual/reference/operator/query/regex/
정규표현식
https://www.regexpal.com
몽고DB regex ($regex)
몽고DB에서 정규표현식을 사용하기 위해 사용하는 키워드
쿼리의 패턴 일치 문자열에 대한 정규식 기능을 제공합니다.
https://docs.mongodb.com/manual/reference/operator/query/regex
RegExp mdn
RegExp 생성자는 패턴을 사용해 텍스트를 판별할 때 사용합니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/RegExp
RegExp 사용 방법
RegExp 객체는 리터럴 표기법과 생성자로써 생성할 수 있습니다.
리터럴 표기법의 매개변수는 두 빗금으로 감싸야 하며 따옴표를 사용하지 않습니다.
생성자 함수의 매개변수는 빗금으로 감싸지 않으나 따옴표를 사용합니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/RegExp
/ab+c/i 를 아래 RegExp 생성자를 이용해서 만들 수 있습니다.
new RegExp(/ab+c/, 'i') // 리터럴 표기법
new RegExp('ab+c', 'i') // 생성자 함수