[Node.js] 영화 테마 기능 구현하기

HoonDong_K·2023년 2월 5일
0

[project #1] movie-inner

목록 보기
8/10
post-thumbnail
post-custom-banner

영화 테마 구상


Movie-inner 프로젝트의 주요 기능 중 하나인 테마 부분은 서비스 관리자 또는 인증받은 회원이 구상한 주제에 맞게 영화들을 모아 하나의 테마로 제공하는 서비스이다.

테마 부분을 적용하기 위해 필요한 API는 3가지로 구분하였다.

  • 테마에 영화를 등록하는 기능
  • 테마에 저장된 영화들을 가져오는 기능
  • 등록된 모든 테마들을 가져오는 기능

1번의 경우, movie_theme 테이블을 만들어 영화를 하나씩 등록하는 방식을 채택하였다. theme_name에는 제작하려는 테마의 이름을 적고, 나머지 칼럼에는 테마에 등록하려는 영화의 정보를 담는다.

movie_theme
idx
theme_name
movie_id
movie_name
release_date
poster_path
backdrop_path
created_at
updated_at

2번의 경우, 유저가 해당 테마를 클릭하였을 때, 테마에 등록된 영화들을 보여주기 위한 용도로 사용되며 DB에서 SELECT WHERE절을 사용하여 해당 테마에 속한 영화들 정보를 가져올 것이다.

3번의 경우, 테마 서비스를 제공하는 메인 페이지에 유저가 도착하였을 때, 여러 테마를 구경할 수 있도록 등록된 모든 테마와 영화 정보들을 보여줄 수 있도록 하였다.

실제 프로젝트 적용



1. inerstMoviesInTheme 는 해당 영화 정보를 TMDB로 부터 가져와 movie_theme 테이블에 테마 이름과 함께 저장하는 역할을 한다.

const { name, movieId } = params // 테마 이름과 등록할 영화의 ID 매개변수 전달
let movieDetails: any = []
try {
   const response = await axios.get(
         `https://api.themoviedb.org/3/movie/${movieId}?api_key=${TMDB_API_KEY}&language=ko`
    )
    movieDetails = response.data
    await connection.run(
        `INSERT INTO movie_theme(theme_name,movie_id,movie_name,release_date,poster_path,backdrop_path) VALUES (?,?,?,?,?,?)`,
        [
            name,
            movieDetails.id,
            movieDetails.title,
            movieDetails.release_date,
            movieDetails.poster_path,
            movieDetails.backdrop_path,
        ]
    )
  catch (e:any){
    console.error(e)
  }

TMDB에서 movie/{movie_id}를 통해 영화 상세 정보를 가져온 후, DB에 저장하였다.
TMDB API 참고 >> TMDB 영화 정보들 불러오기

  1. 해당 테마에 속해있는 모든 영화를 불러오는 것은 어렵지 않다.
const { name } = params //path - theme_name

let response = {}
try {
    response = await connection.run( 
   `SELECT * FROM movie_theme WHERE theme_name=?`,
       [name]
    )
} catch (e: any) {
    tmdbErrorHandler(e)
}

SELECT WHERE절을 이용하여 해당 테마 이름에 속한 모든 영화 정보를 불러오면 된다.

  1. 등록된 모든 테마와 영화 정보를 불러오는 것 또한 어렵지는 않다. 하지만 그렇게 DB로부터 불러온 응답값은 사용하기에 그렇게 편리하지 않았다.
const response = await connection.run(`SELECT * FROM movie_theme`)
return {
    status: 201,
    data: response,
    }

만약 DB에 저장된 모든 테마와 영화 정보를 불러온다면,

[
    {
        "idx": 1,
        "theme_name": "music",
        "movie_id": "313369",
        "movie_name": "라라랜드",
        "release_date": "2016-11-29",
        "poster_path": "/6v4g6yW01uTmbxqwg75iEkMkrNP.jpg",
        "backdrop_path": "/ik2D3KqxFD0O0Bc3Wv1CZm8sOg8.jpg",
        "created_at": "2022-09-16T16:20:08.000Z",
        "updeated_at": "2022-09-16T16:20:08.000Z"
    },
    {
        "idx": 2,
        "theme_name": "music",
        "movie_id": "515195",
        "movie_name": "예스터데이",
        "release_date": "2019-06-27",
        "poster_path": "/pmiFyESTXQXivV9cN6mFCPKv4A.jpg",
        "backdrop_path": "/5om2iHhm2dgv0s2YgPDSEMIkRNx.jpg",
        "created_at": "2022-09-16T16:20:22.000Z",
        "updeated_at": "2022-09-16T16:20:22.000Z"
    },
    ,,,
]

이렇게 모든 정보들을 가져올 것이고, 이는 사용하기에 굉장히 불편한 응답값이 될 것이다. 그렇기 때문에 이 값을 편하기 사용하기 위해 구조를 변화시켜주었다. 내가 희망하는 형태는 다음과 같았다.

{
	"테마 1": [
    	//영화 1
         {  
            "theme_name": "",
            "movie_id": "",
            "movie_name": "",
            "poster_path": "",
            "backdrop_path": "",
            "release_date": ""
        },
        //영화 2
        {
        },
        ,,
  "테마 2":[],
  "테마 3":[]
}

위의 형태로 변화시키기 위해서는
1. 반복되는 테마의 이름을 중복값을 제외하고 객체의 key값으로 설정한다.
2. 이후 각 영화 정보가 포함된 객체의 theme_name가 상위 객체( 1에서 설정한 테마 객체) 의 key값과 비교하여 동일할 경우, 그 key의 value로 들어가게 만든다.

첫 번째 순서의 경우,

let duplicateThemeNames: string[] = []
//theme Array= DB에서 불러온 모든 테마, 영화 정보 
themeArray.map((resObj: MovieInfoType) => {
    duplicateThemeNames.push(resObj.theme_name) 
})
//theme 이름 중복 제거
const themeNames = [...new Set(duplicateThemeNames)]
//expected : [ 'music', 'horror', 'action', 'romance', 'animation', 'sf' ] 

DB에 저장된 모든 테마, 영화 정보를 map를 통해 테마 이름만 추출한 뒤, Set 을 통해 중복값을 제거해준다.

let movieInfo: any[] = []

for (let i = 0; i < themeArray.length; i++) {
   movieInfo.push( 
   theme_name: themeArray[i].theme_name,
   movie_id: themeArray[i].movie_id,
   movie_name: themeArray[i].movie_name,    
   poster_path: themeArray[i].poster_path,
   backdrop_path: themeArray[i].backdrop_path,
   release_date: themeArray[i].release_date,
    })
}
//theme 별 영화 객체 
let movieThemeList: { [key: string]: Array<MovieInfoType> } = {}

for (let i = 0; i < themeNames.length; i++) {
    movieThemeList[themeNames[i]] = []
    for (let j = 0; j < movieInfo.length; j++) {
      //theme 객체의 key값과 각 영화의 theme_name 이 동일할 경우
        if (themeNames[i] === movieInfo[j].theme_name) {
           movieThemeList[themeNames[i]] = [
               ...movieThemeList[themeNames[i]],
               movieInfo[j],
           ] 
       }
    }
}
return movieThemeList
{
    "music": [
        {
            "theme_name": "music",
            "movie_id": "313369",
            "movie_name": "라라랜드",
            "poster_path": "/6v4g6yW01uTmbxqwg75iEkMkrNP.jpg",
            "backdrop_path": "/ik2D3KqxFD0O0Bc3Wv1CZm8sOg8.jpg",
            "release_date": "2016-11-29"
        },
      {},{},,
    ],
    "horror": [
        {
            "theme_name": "horror",
            "movie_id": "756999",
            "movie_name": "블랙폰",
            "poster_path": "/eJPIkRiYGnvQaPCZtTRTDmhMJVK.jpg",
            "backdrop_path": "/AfvIjhDu9p64jKcmohS4hsPG95Q.jpg",
            "release_date": "2022-06-22"
        },
        {},{},,
    ],
    "action": [
        {
            "theme_name": "action",
            "movie_id": "616037",
            "movie_name": "토르: 러브 앤 썬더",
            "poster_path": "/bZLrpWM065h5bu1msUcPmLFsHBe.jpg",
            "backdrop_path": "/jsoz1HlxczSuTx0mDl2h0lxy36l.jpg",
            "release_date": "2022-07-06"
        },
        {},{},,
    ],
    "romance": [
        {
            "theme_name": "romance",
            "movie_id": "19913",
            "movie_name": "500일의 썸머",
            "poster_path": "/jXFTwKzM1YVek4999NuTUjIHiIu.jpg",
            "backdrop_path": "/n5qb67923cqojccZmMqkMOgTMWq.jpg",
            "release_date": "2009-07-17"
        },
        {},{},,,
    ],
    "animation": [
        {
            "theme_name": "animation",
            "movie_id": "610150",
            "movie_name": "드래곤볼 슈퍼: 슈퍼 히어로",
            "poster_path": "/uohymzBVaIYjbnoQstbnlia6ZPJ.jpg",
            "backdrop_path": "/ugS5FVfCI3RV0ZwZtBV3HAV75OX.jpg",
            "release_date": "2022-06-11"
        },
      {},{},,
    ],
    "sf": [
        {
            "theme_name": "sf",
            "movie_id": "762504",
            "movie_name": "놉",
            "poster_path": "/52McjY2vAEiF0lJe8iBdOBNuyok.jpg",
            "backdrop_path": "/xVbppM1xgbskOKgOuV8fbWBWHtt.jpg",
            "release_date": "2022-07-20"
        },
      {},{},,
    ]
}

이렇게 성공적으로 원하는 형태의 응답값을 얻을 수 있다.

profile
개발을 잘하고 싶은 개발자
post-custom-banner

0개의 댓글