프로젝트/영화 목록 (Netflix) 앱

영화목록 (Netflix) 앱 7 - 좋아요 기능 추가하기

syleemomo 2021. 11. 24. 20:53
728x90

 

* Movie 컴포넌트에 좋아요 속성 추가하기

import React from 'react'
import './Movie.css'

const Movie = ({ title, genres, cover, summary, year, rating, likes }) => {

    return (
        <div className='movie-container'>
            <img src={cover} alt={title}></img>
            <h3>{title} ({year})</h3>
            <h4>{genres.join(" ")}</h4>
            <h4>{rating} / ❤️ {likes ? likes : 0} </h4>
        </div>
    )
}

export default Movie

Movie.js 파일을 위와 같이 수정하자! likes 속성을 추가하고 화면에 렌더링한다. 

 

* 홈화면에 좋아요 업데이트 기능 추가하기

import React, { useState, useEffect } from 'react'
import { Link, useNavigate } from 'react-router-dom'

import { Movie, Loading, Input, Button, Menu } from 'components'
import './Home.css'

const Home = () => {
    const [loading, setLoading] = useState(true)
    const [movies, setMovies] = useState([])
    const [query, setQuery] = useState('')
    const [isSorted, setIsSorted] = useState(-1)
    const navigate = useNavigate()

    const likes = JSON.parse(sessionStorage.getItem('likes')) || {}
    console.log(likes)
    

    useEffect( () => {
        fetch('https://yts.mx/api/v2/list_movies.json?limit=20')
        .then( res => res.json())
        .then( result => {
            const {data: {movies}} = result
            console.log(movies)
            setLoading(false)
            setMovies(movies)
        })
    }, [])

    const handleChange = (e) => {
        const { value } = e.target
        setQuery(value)
    }

    const sortByYear = (e) => {
        setIsSorted(isSorted * -1)
    }

    const updateLikes = (id) => {
        const likes = JSON.parse(sessionStorage.getItem('likes')) || {}
       
        if(likes[id] === null || likes[id] === undefined){
            likes[id] = 0
        }
        likes[id] += 1
        sessionStorage.setItem('likes', JSON.stringify(likes))
    }

    const homeUI = movies
                        .filter(movie => {
                            const title = movie.title.toLowerCase()
                            const genres = movie.genres.join(' ').toLowerCase()
                            const q = query.toLowerCase()
                        
                            return title.includes(q) || genres.includes(q)
                        })
                        .sort( (a, b) => {
                            return (b.year - a.year) * isSorted;
                        })
                        .map(movie =>
                            <Link key={movie.id}  
                                  to='/detail'
                                  state={{ movie }} 
                                  style={{ textDecoration: 'none', color: 'white'}}
                                  onClick={() => updateLikes(movie.id)}
                            >
                                
                                <Movie 
                                        title={movie.title} 
                                        genres={movie.genres} 
                                        cover={movie.medium_cover_image} 
                                        summary={movie.summary}
                                        year={movie.year}
                                        rating={movie.rating}
                                        likes={likes[movie.id]}
                                       />
                            </Link> 
                                    )

    const toRankPage = () => {
        navigate('/recommend', { state: { movies }})
    }
    return (
        <>
            {loading? <Loading/>: <div className='Home-container'>
                                    <Menu>
                                        <Button handleClick={toRankPage}>Rank</Button>
                                    </Menu>
                                    <div className='Home-contents'>
                                        <Input name='search' type='text' placeholder='Search movies ...' value={query} onChange={handleChange}/>
                                        <Button handleClick={sortByYear}>정렬</Button>
                                        <div className='Home-movies'>{homeUI}</div>
                                    </div>
                                  </div>}
        </>
    )
}

export default Home

Home.js 파일을 위와 같이 수정하자! 

const likes = JSON.parse(sessionStorage.getItem('likes')) || {}
console.log(likes)

세션 스토리지로부터 likes 정보를 조회한다. likes 정보가 존재하지 않으면 빈 객체로 초기화한다. 

const updateLikes = (id) => {
    const likes = JSON.parse(sessionStorage.getItem('likes')) || {}

    if(likes[id] === null || likes[id] === undefined){
        likes[id] = 0
    }
    likes[id] += 1
    sessionStorage.setItem('likes', JSON.stringify(likes))
}

영화목록에서 특정 영화를 클릭하면 실행되는 이벤트핸들러 함수이다. 세션스토리지에서 전체 영화목록에 대한 likes 정보를 조회한다. likes 정보가 존재하지 않으면 빈 객체로 초기화한다. 클릭한 영화의 id 값을 이용하여 해당 영화에 대한 좋아요 숫자를 1만큼 증가시킨다. 업데이트된 likes 정보를 다시 세션 스토리지에 저장한다. 

<Link key={movie.id}  
      to='/detail'
      state={{ movie }} 
      style={{ textDecoration: 'none', color: 'white'}}
      onClick={() => updateLikes(movie.id)}
>

    <Movie 
            title={movie.title} 
            genres={movie.genres} 
            cover={movie.medium_cover_image} 
            summary={movie.summary}
            year={movie.year}
            rating={movie.rating}
            likes={likes[movie.id]}
           />
</Link>

Link 컴포넌트에 onClick 이벤트에 대한 이벤트핸들러 함수를 연결한다. Link 컴포넌트가 클릭되면 updateLikes 이벤트핸들러 함수에 해당 영화에 대한 id 값이 전달된 다음 실행된다. Movie 컴포넌트에 likes 속성이 추가되었다. 세션스토리지로부터 영화목록에 대한 likes 정보를 조회하고, 특정 영화의 id 값을 이용하여 해당 영화에 대한 좋아요 숫자를 Movie 컴포넌트의 likes 속성에 전달한다.  

 

* 상세페이지에 좋아요 숫자 보여주기

import React from 'react'
import { Movie, Button, Menu } from 'components'
import { useLocation, useNavigate } from 'react-router-dom'

import './Detail.css'

const Detail = () => {
    const location = useLocation()
    const { movie } = location.state
    const { yt_trailer_code } = movie
    console.log(movie)
    const navigate = useNavigate()

    const likes = JSON.parse(sessionStorage.getItem('likes')) || {}
    console.log(likes)

    const watchMovieTrailer = () => {
        window.location.href = yt_trailer_code? `https://www.youtube.com/watch?v=${yt_trailer_code}`: ""
    }
    const toHomePage = () => {
        navigate('/home')
    }
    return (
        <div className='Detail-container'>
            <Menu>
                <Button handleClick={toHomePage}>Home</Button>
            </Menu>
            <div className='Detail-contents'>
                <Movie title={movie.title} 
                        genres={movie.genres} 
                        cover={movie.medium_cover_image} 
                        summary={movie.summary}
                        year={movie.year}
                        rating={movie.rating}
                        likes={likes[movie.id]}>    
                </Movie>

                <div className='Movie-info'>
                    <p className='Movie-runtime'>Runtime {movie.runtime} min.</p>
                    <p className='Movie-summary'>{movie.summary}</p>
                    <a href={movie.torrents.length !== 0 ? movie.torrents[0].url : ''} download>Download Torrent</a><br/>
                    <Button handleClick={watchMovieTrailer}>Watch Youtube trailer</Button>
                </div>
            </div>
        </div>
    )
}
export default Detail

Detail.js 파일을 위와 같이 수정하자!

const likes = JSON.parse(sessionStorage.getItem('likes')) || {}
console.log(likes)

세션 스토리지로부터 likes 정보를 조회한다. likes 정보가 존재하지 않으면 빈 객체로 초기화한다.

<Movie title={movie.title} 
        genres={movie.genres} 
        cover={movie.medium_cover_image} 
        summary={movie.summary}
        year={movie.year}
        rating={movie.rating}
        likes={likes[movie.id]}>    
</Movie>

Movie 컴포넌트에 likes 속성이 추가되었다. 세션스토리지로부터 영화목록에 대한 likes 정보를 조회하고, 특정 영화의 id 값을 이용하여 해당 영화에 대한 좋아요 숫자를 Movie 컴포넌트의 likes 속성에 전달한다.  

 

* 영화추천 페이지에 좋아요 숫자를 기준으로 추천 영화 보여주기

import React from 'react'
import { useLocation, Link, useNavigate } from 'react-router-dom'

import { Movie, Menu, Button } from 'components'
import './Recommendation.css'

const Recommendation = () => {
    const location = useLocation()
    const { movies } = location.state
    console.log(movies)
    const navigate = useNavigate()

    const likes = JSON.parse(sessionStorage.getItem('likes')) || {}
    console.log(likes)

    const toHomePage = () => {
        navigate('/home')
    }

    const bestMovies = movies
                            .sort( (a, b) => {
                                return (b.rating - a.rating);
                            })
                            .slice(0, 3)
                            .map(movie =>
                                <Link key={movie.id}  
                                    to='/detail'
                                    state={{ movie }} 
                                    style={{ textDecoration: 'none', color: 'white'}}
                                >
                                    <Movie 
                                            title={movie.title} 
                                            genres={movie.genres} 
                                            cover={movie.medium_cover_image} 
                                            summary={movie.summary}
                                            year={movie.year}
                                            rating={movie.rating}
                                            likes={likes[movie.id]}
                                        />
                                </Link> 
                                        )
    
    // likes 객체를 배열 객체로 변환하기
    const likesArray = []
    for(let like in likes){
        likesArray.push({ id: like, favorite: likes[like]})
    }

    // favorite (좋아요 숫자) 기준으로 정렬하기
    const bestMoviesByLikes = likesArray
    .sort( (a, b) => {
        return (b.favorite - a.favorite);
    })
    .slice(0, 3)
    .map(likeInfo => {
        const movieId = parseInt(likeInfo.id) 
        const movie = movies.filter(movie => movie.id === movieId)[0]
        console.log('movie by likes',parseInt(likeInfo.id))
        console.log('movie: ', movie)

        return (
            <Link key={movie.id}  
            to='/detail'
            state={{ movie }} 
            style={{ textDecoration: 'none', color: 'white'}}
        >
            <Movie 
                    title={movie.title} 
                    genres={movie.genres} 
                    cover={movie.medium_cover_image} 
                    summary={movie.summary}
                    year={movie.year}
                    rating={movie.rating}
                    likes={likes[movie.id]}
                />
        </Link> 
        )     
     } )
                


    return (
        <div className='Recommendation-container'>
            <Menu>
                <Button handleClick={toHomePage}>Home</Button>
            </Menu>
            <div className='Recommendation-text first-text'>Best Movies by rating</div>
            <div className='Recommendation-bestmovies'>{bestMovies}</div>
            <div className='Recommendation-text second-text'>Best Movies by likes</div>
            <div className='Recommendation-bestmovies'>{bestMoviesByLikes}</div>
        </div>
    )
}
export default Recommendation

 Recommendation.js 파일을 위와 같이 수정하자!

const likes = JSON.parse(sessionStorage.getItem('likes')) || {}
console.log(likes)

세션 스토리지로부터 likes 정보를 조회한다. likes 정보가 존재하지 않으면 빈 객체로 초기화한다. 

 // likes 객체를 배열 객체로 변환하기
const likesArray = []
for(let like in likes){
    likesArray.push({ id: like, favorite: likes[like]})
}

likes 정보는 객체로 되어 있으므로 정렬하기 위하여 배열로 변환한다. 배열 요소는 객체이며 id, favorite 프로퍼티를 가진다. id 값은 해당 영화에 대한 id 값이며, favorite 값은 해당 영화에 대한 좋아요 숫자를 의미한다. 

// favorite (좋아요 숫자) 기준으로 정렬하기
const bestMoviesByLikes = likesArray
.sort( (a, b) => {
    return (b.favorite - a.favorite);
})
.slice(0, 3)

배열의 sort 메서드를 사용하여 favorite 프로퍼티를 기준으로 배열을 정렬한다. 즉, 좋아요 숫자를 기준으로 내림차순으로 정렬한다. 그런 다음 slice 메서드를 사용하여 좋아요 숫자가 많은 영화들 중에서 3개만 추출한다. 

.map(likeInfo => {
        const movieId = parseInt(likeInfo.id) 
        const movie = movies.filter(movie => movie.id === movieId)[0]
        console.log('movie by likes',parseInt(likeInfo.id))
        console.log('movie: ', movie)

        return (
            <Link key={movie.id}  
            to='/detail'
            state={{ movie }} 
            style={{ textDecoration: 'none', color: 'white'}}
        >
            <Movie 
                    title={movie.title} 
                    genres={movie.genres} 
                    cover={movie.medium_cover_image} 
                    summary={movie.summary}
                    year={movie.year}
                    rating={movie.rating}
                    likes={likes[movie.id]}
                />
        </Link> 
        )     
     } )

좋아요 숫자가 많은 영화 3개를 화면에 렌더링한다. 

const movieId = parseInt(likeInfo.id)

서버에서 가져온 movies 데이터의 id 값은 숫자인데 반해 정렬한 배열의 id 값은 문자열이다. 그래서 특정 영화를 id 값으로 검색하기 위하여 정렬한 배열의 id 값을 숫자로 변경한다. 

const movie = movies.filter(movie => movie.id === movieId)[0]

서버에서 가져온 영화목록 중에서 id 값과 일치하는 영화를 검색한다. 

<div className='Recommendation-text first-text'>Best Movies by rating</div>
<div className='Recommendation-bestmovies'>{bestMovies}</div>
<div className='Recommendation-text second-text'>Best Movies by likes</div>
<div className='Recommendation-bestmovies'>{bestMoviesByLikes}</div>

평점이 높은 영화와 좋아요 숫자가 많은 영화를 순서대로 화면에 보여준다. 

.Recommendation-container{
    background: url('../assets/images/background.jpg');
    width: 100%;
    text-align: center;
    border: 1px solid black;
}
.Recommendation-text{
    color: #a9a9a9;
    padding: 10px;
    font-weight: bold;
    font-size: 1.5rem;
}
.first-text{
    margin-top: 100px;
}
.second-text{
    margin-top: 50px;
}
.Recommendation-bestmovies{
    display: flex;
    justify-content: center;
    padding: 20px;
}

Recommendation.css 파일을 위와 같이 수정하자!

 

* 영화추천 페이지 화면

평점 기준 영화 추천 리스트
좋아요 숫자 기준 추천 리스트

 

728x90