/
: DB 에 있는 모든 영화의 제목이 나열된 홈페이지
/upload
: 영화를 생성하는 Form 이 있는 페이지, 생성한 영화를 DB에 저장
/movies/:id
: 영화 상세 정보 페이지
/movies/:id/edit
: 영화를 편집하는 Form 이 있는 페이지, 편집한 영화를 DB 에 저장
/movies/:id/delete
: 영화를 삭제
/filter
: 제목, 별점, 연도 별로 영화를 검색하는 페이지
만약, 검색창에 제목 keyword 를 get 으로 받고 검색된 내용을 보여주는 search 페이지를 만들었다면 컨트롤러에는 다음과 같은 코드를 입력해야 할 것이다.
export const search = async (req, res) => {
const { query: { keyword } } = req;
let movies = [];
try {
if (keyword) {
movies = await Movie.find({
title: { $regex: new RegExp(keyword, "i") }
});
}
return res.render("search", { pageTitle: "Search", movies, keyword });
} catch (err) {
console.error(err);
}
};
movies 라는 빈 배열을 먼저 만들어주고, 키워드가 있으면 DB에서 키워드와 동일한 제목을 가지고 있는 영화를 찾아 movies 배열에 넣는다. res.render 에서 "search" 페이지로 movies 배열을 가지고 들어가게 된다.
그러나, 이 함수는 깃허브 코드에 없다. 제목 검색 뿐만 아니라 별점, 연도도 검색하여 보여주고 싶어서 아래와 같은 함수를 작성하였다.
filterMovie 함수로 실제 코드에 구현된 컨트롤러다. 제목 검색 보다 더 심화된 버전이라고 할까.
코드를 보면 알겠지만,
export const filterMovie = async(req, res) => {
const {
query: { title, year, rating }
} = req;
try {
const gettitleMovies = await Movie.find({
title : {$regex: new RegExp(`${title}`, "i")} //검색값이 포함되어 있는 title 값
});
const getyearMovies = await Movie.find({
year: { $gte: year } //크거나 같은 year 값
});
const getratingMovies = await Movie.find({
rating: {$gte: rating } //크거나 같은 rating 값
});
let movies = (title !== "") ? [...gettitleMovies] : [...getratingMovies, ...getyearMovies];
movies = _.uniqBy(movies, "id");
console.log(movies)
//제목 설정
const both = year !== "" && rating !== "" ? "||" : "";
let pageTitle_two;
if (title !== "") {
pageTitle_two = `title`
} else {
pageTitle_two = `
${year === "" ? "" : `year: ${year}`}
${both}
${rating === "" ? "" : `rating: ${rating}`}
`;
}
const pageTitle = `Searching by ${pageTitle_two}`
return res.render("movies/home", { pageTitle, movies });
} catch (err) {
console.error(err);
}
};
이것도 마찬가지로 페이지 렌더링으로 들어가는 movies 는 배열이라는 점. filterMovie 함수 더 예쁘게 만들 수 있나? 나중에 손대보자!
아래는 filterMovie 함수를 구현할 때 알게 되었던 정보들이다.
[...getratingMovies, ...getyearMovies] 이란, getratingMovies 의 배열들과 getyearMovies 배열들을 하나로 합친 배열을 생성하는 것이다.
[...args] : "전개구문" 이라고 부른다.
- 배열 : [...args]
- 객체 : {...obj}
전개 구문 없이, 이미 존재하는 배열을 일부로 하는 새로운 배열을 생성하기에, 배열 리터럴 문법은 더 이상 충분하지 않으며 push(), splice(), concat() 등의 조합을 사용하는 대신 명령형 코드를 사용해야 했습니다. 전개 구문으로 이는 훨씬 더 간결해졌습니다.
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulders", "knees", "and", "toes"]
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
filterMovie 함수에서 각각의 별점, 연도 검색으로 수집된 정보를 하나의 배열에 합쳤기 때문에, 중복 문제가 발생했다.
연도 2017년과 별점 3점을 동시에 검색하였더니
1. 연도 2017 년 이상의 영화를 찾고,
2. 별점 3점 이상의 영화를 찾아
두 조건 모두에 해당하는 영화는 결과에 두 번 등장했다.
따라서, 중복을 제거해야 했다.
(배열 내의 객체 중복 제거)
https://kyounghwan01.github.io/blog/JS/JSbasic/dupulication-property-remove/#lodash
Set을 이용하여 중복 제거해보았다.
let movies = (title !== "") ? [...gettitleMovies] : [...getratingMovies, ...getyearMovies];
movies = [...new Set(movies)];
그러나, 중복은 제거 되지 않았다.
구글링을 해보니, Set 의 경우 하나의 맹점이 있는데 id가 같으나, 다른 속성이 있다면, 중복이라 생각하지 않고 리턴한다고 한다.
예를 들어,
const example2 = [
{ id: 123, name: "nkh" },
{ id: 123, name: "ddd" },
{ id: 5456, name: "zxc" }
];
console.log([...new Set(example2.map(JSON.stringify))].map(JSON.parse));
// [{id: 123, name: 'nkh'}, {id: 123, name: 'ddd'}, {id: 5456, name: 'zxc'}]
mongoose 가 객체에 자동적으로 부여하는 _id 값이 있기 때문에, 다른 속성이 하나라도 있다고 인식하여 중복 처리 하지 않는 것이었다.
즉, 객체의 속성이 1개만 있거나, 객체 값이 완전히 같은 것을 중복 제거 할 경우만 new Set을 써야한다.
위 블로그글을 보다가, lodash 를 사용하면 아주 편리하게 중복 제거를 할 수 있다는 사실을 알게 되어서 lodash 라이브러리를 사용해보았다.
npm i --save lodash
home.pug 에 script(src="lodash.js")
삽입
MovieController.js 에 import _ from "lodash";
삽입
MovieController.js 안의 filterMovie 함수 수정
let movies = (title !== "") ? [...gettitleMovies] : [...getratingMovies, ...getyearMovies];
movies = _.uniqBy(movies, "id");
CastError: Cast to Number failed for value "lodash.js" (type string) at path "id" for model "Movie".
처음에는 home.pug 가 아니라 layout.pug 에 script 추가해줬더니 이런 에러나더라. 딱 home.pug 에서만 쓸거니까 거기에다가 추가해주니까 더이상 에러가 나지 않음!
중복 제거가 잘 되었다! 굿.
처음에는 쭉 개발하고, 나중에 오류 처리만 따로 하는 것도 좋은 방법일 듯 싶다. DB에서 값을 찾을 때는 오류 있을 수 있으므로 무조건 다 try catch 구문으로 오류 처리해줬다.