[Nodejs, mongoDB] 영화 필터링 웹사이트 - CRUD 기능 추가

devsr·2022년 1월 26일
0
  • 프로젝트 설명: 이전에 제작한 Nodejs, mongoDB 기반의 영화 필터링 웹사이트에 CRUD 기능을 추가하였습니다.

/ : 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] 이란, 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

1. Set

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을 써야한다.


2. lodash

위 블로그글을 보다가, lodash 를 사용하면 아주 편리하게 중복 제거를 할 수 있다는 사실을 알게 되어서 lodash 라이브러리를 사용해보았다.

  1. npm i --save lodash

  2. home.pug 에 script(src="lodash.js") 삽입

  3. MovieController.js 에 import _ from "lodash"; 삽입

  4. 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 구문으로 오류 처리해줬다.

profile
웹/앱 개발자

0개의 댓글