ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 영화목록 (Netflix) 앱 6 - 영화 추천 페이지 만들기
    프로젝트/영화 목록 (Netflix) 앱 2021. 11. 24. 15:35
    728x90

     

    * 영화 추천 페이지 라우트 추가하기

    import React from "react"
    import ReactDOM from "react-dom"
    import { BrowserRouter, Route, Routes } from "react-router-dom"
    import { Register, Login, Home, Detail, Recommendation } from 'pages'
    
    import './index.css'
    
    const App = () => {
      return (
        <div>
          <Routes>
              <Route path='/' element={<Register/>}/>
              <Route path="/login" element={<Login/>}/>
              <Route path="/home" element={<Home/>}/>
              <Route path='/detail' element={<Detail/>}/>
              <Route path='/recommend' element={<Recommendation/>}/>
          </Routes>
        </div>
      );
    };
      
    ReactDOM.render(<BrowserRouter>
                      <App />
                    </BrowserRouter>, 
                    document.getElementById("app"));

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

    import { Register, Login, Home, Detail, Recommendation } from 'pages'

    Recommendation 페이지 컴포넌트를 추가로 임포트한다. 

    <Route path='/recommend' element={<Recommendation/>}/>

    영화추천 페이지에 대한 라우트를 추가한다. 

     

    * 영화추천 페이지 구현하기

    export { default as Register } from './Register'
    export { default as Login } from './Login'
    export { default as Home } from './Home'
    export { default as Detail } from './Detail'
    export { default as Recommendation } from './Recommendation'

    pages > index.js 파일에 Recommendation 페이지 컴포넌트를 내보낸다. 

    import React from 'react'
    
    const Recommendation = () => {
        return (
            <div>Recommendation page</div>
        )
    }
    export default Recommendation

    pages 폴더에 Recommendation.js 파일을 생성하고 위와 같이 작성하자!

     

    * 네비게이션 바 만들기

    components 폴더에 아래 파일을 추가한다. 

    import React from 'react'
    import './Menu.css'
    
    function Menu({children}){
        return (
            <div className='Menu-container'>{children}</div>
        )
    }
    export default Menu

    Menu.js 파일을 위와 같이 작성하자!

    .Menu-container{
        width: 100%;
        height: 70px;
        color: #a9a9a9;
        box-shadow: 0px 2px 10px rgba(255, 255, 255, 0.35);
        position: fixed;
        z-index: 1;
    
        display: flex;
        justify-content: flex-end;
        align-items: center;
        padding: 10px;
        box-sizing: border-box;
    }

    Menu.css 파일을 위와 같이 작성하자!

    export { default as Input } from './Input'
    export { default as Movie } from './Movie'
    export { default as Loading } from './Loading'
    export { default as Button } from './Button'
    export { default as Menu } from './Menu'

    components > index.js 파일에 Menu 컴포넌트를 내보낸다. 

     

    * 홈화면에서 추천 페이지로 이동하기

    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()
        
    
        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 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'}}
                                >
                                    
                                    <Movie 
                                            title={movie.title} 
                                            genres={movie.genres} 
                                            cover={movie.medium_cover_image} 
                                            summary={movie.summary}
                                            year={movie.year}
                                           />
                                </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 파일을 위와 같이 수정하자!

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

    추천 페이지로 이동하기 위하여 useNavigate 함수를 추가한다. 

    import { Movie, Loading, Input, Button, Menu } from 'components'

    네비게이션 바를 보여주기 위하여 Menu 컴포넌트를 임포트한다. 

    const navigate = useNavigate()

    useNavigate 함수를 실행하면 다른 URL 경로로 이동할 수 있는 navigate 함수를 반환한다. 

    const toRankPage = () => {
        navigate('/recommend', { state: { movies }})
    }

    Rank 버튼을 클릭하면 실행되는 이벤트핸들러 함수이다. 버튼을 클릭하면 추천 페이지로 이동하면서 서버에서 가져온 영화목록(movies) 데이터를 추천 페이지로 전달한다. 그러면 추천 페이지에서 영화목록(movies) 데이터를 사용할 수 있다. navigate 함수의 2번째 인자로 객체를 설정하고, state 프로퍼티에 객체 형태로 전달할 데이터를 설정할 수 있다. 

    <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>

    Menu 컴포넌트를 추가로 렌더링한다. children props 로 Button 컴포넌트를 전달하여 추천 페이지로 이동할 수 있도록 하였다. 그밖에 기존 컴포넌트들은 Home-contents 라는 클래스명을 가진 div 요소로 감싸서 스타일을 적용하였다. 

    .Home-container{
        background: url('../assets/images/background.jpg');
        width: 100%;
        text-align: center;
        border: 1px solid black;
    }
    .Home-contents{
        /* border: 5px solid orange; */
        margin-top: 100px;
    }
    .Home-movies{
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        align-items: center;
        width: 60%;
        margin: 0px auto;
        text-align: center;
    }

    Home.css 파일을 위와 같이 수정하자! Home-contents 클래스명에 대한 스타일이 추가되었다. 

    import React from 'react'
    import { useLocation } from 'react-router-dom'
    
    const Recommendation = () => {
        const location = useLocation()
        const { movies } = location.state
        console.log(movies)
        
        return (
            <div>Recommendation page</div>
        )
    }
    export default Recommendation

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

    import { useLocation } from 'react-router-dom'

    홈화면에서 전달받은 movies 데이터를 조회하기 위하여 useLocation 함수를 임포트한다. 

    const location = useLocation()
    const { movies } = location.state
    console.log(movies)

    상세페이지에서와 같이 홈화면에서 전달받은 movies 데이터를 조회하기 위하여 location 객체의 state 프로퍼티에 접근한다. 

     

    * 추천 페이지에 평점이 높은 영화 보여주기

    import React from 'react'
    import './Movie.css'
    
    const Movie = ({ title, genres, cover, summary, year, rating }) => {
        return (
            <div className='movie-container'>
                
                <img src={cover} alt={title}></img>
                <h3>{title} ({year})</h3>
                <h4>{genres.join(" ")}</h4>
                <h4>{rating}</h4>
            </div>
        )
    }
    
    export default Movie

    Movie.js 파일을 위와 같이 수정하자! rating props 를 추가해서 화면에 영화 평점을 보여줄 수 있도록 한다. 

    import React from 'react'
    import { useLocation, Link } from 'react-router-dom'
    
    import { Movie } from 'components'
    import './Recommendation.css'
    
    const Recommendation = () => {
        const location = useLocation()
        const { movies } = location.state
        console.log(movies)
    
        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}
                                            />
                                    </Link> 
                                            )
    
        return (
            <div className='Recommendation-container'>
                <div className='Recommendation-bestmovies'>{bestMovies}</div>
            </div>
        )
    }
    export default Recommendation

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

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

    상세페이지로 이동하기 위하여 Link 컴포넌트를 추가로 임포트한다.

    import { Movie } from 'components'

    Movie 컴포넌트를 사용하기 위하여 임포트한다. 

    import './Recommendation.css'

    추천페이지에 대한 스타일을 적용하기 위하여 Recommendation.css 파일을 임포트한다.

    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}
                                            />
                                    </Link> 
                                            )

    홈화면에서 영화 발매일 순으로 최신순, 과거순으로 정렬한 것처럼 동일하게 sort 메서드를 사용하여 영화 평점(rating) 을 기준으로 내림차순으로 정렬한다. slice 메서드를 사용하여 평점이 높은 영화 중에서 3개만 추출한다. Link 컴포넌트를 사용하여 특정 영화 클릭시 상세 페이지로 이동할 수 있도록 하며, 해당 영화의 데이터도 함께 전달한다.

    영화평점을 보여주는 rating props 를 Movie 컴포넌트에 추가로 전달한다.

    .Recommendation-container{
        background: url('../assets/images/background.jpg');
        width: 100%;
        text-align: center;
        border: 1px solid black;
    }
    .Recommendation-bestmovies{
        display: flex;
        border: 1px solid orange;
        justify-content: center;
        padding: 20px;
    }

    Recommendation.css 파일을 위와 같이 작성하자!

     

    * 홈화면, 상세페이지에 영화 평점 추가하기

    import React from 'react'
    import { Movie, Button } from 'components'
    import { useLocation } 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 watchMovieTrailer = () => {
            window.location.href = yt_trailer_code? `https://www.youtube.com/watch?v=${yt_trailer_code}`: ""
        }
        return (
            <div className='Detail-container'>
                <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}>    
                    </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 파일을 위와 같이 수정하자!

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

    Movie 컴포넌트에 rating 속성을 추가한다.

    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()
        
    
        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 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'}}
                                >
                                    
                                    <Movie 
                                            title={movie.title} 
                                            genres={movie.genres} 
                                            cover={movie.medium_cover_image} 
                                            summary={movie.summary}
                                            year={movie.year}
                                            rating={movie.rating}
                                           />
                                </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 파일을 위와 같이 수정하자!

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

    Movie 컴포넌트에 rating 속성을 추가한다.

     

    * 상세페이지에 네비게이션 바 추가하기

    .Menu-container{
        width: 100%;
        height: 70px;
        color: #a9a9a9;
        box-shadow: 0px 2px 10px rgba(255, 255, 255, 0.35);
        top: 0;
        left: 0;
        right: 0;
        position: fixed;
        z-index: 1;
    
        display: flex;
        justify-content: flex-end;
        align-items: center;
        padding: 10px;
        box-sizing: border-box;
    }

    Menu.css 파일을 위와 같이 수정하자!  top, left, right 속성을 모두 0 으로 설정한다.

    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 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}>    
                    </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 파일을 위와 같이 수정하자!

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

    상세페이지에 네비게이션 바를 구현하기 위하여 Menu 컴포넌트와 useNavigate 함수를 임포트한다.

    const navigate = useNavigate()

    페이지를 이동하기 위하여 useNavigate 함수를 실행하여 navigate 함수를 반환한다.

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

    Home 버턴을 클릭하면 실행되는 이벤트핸들러 함수이다. 버튼을 클릭하면 홈화면으로 이동한다. 

     <Menu>
        <Button handleClick={toHomePage}>Home</Button>
    </Menu>

    홈화면으로 이동하기 위하여 네비게이션 바를 추가하였다. Menu 컴포넌트를 렌더링하고 Button 컴포넌트를 children 으로 전달한다. 

     

    * 영화추천 페이지에 네비게이션 바 추가하기

    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 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}
                                            />
                                    </Link> 
                                            )
    
    
        return (
            <div className='Recommendation-container'>
                <Menu>
                    <Button handleClick={toHomePage}>Home</Button>
                </Menu>
                <div className='Recommendation-bestmovies'>{bestMovies}</div>
            </div>
        )
    }
    export default Recommendation

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

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

    추천 페이지에 네비게이션 바를 구현하기 위하여 useNavigate 함수를 임포트한다.

    import { Movie, Menu, Button } from 'components'

    추천 페이지에 네비게이션 바를 구현하기 위하여 Menu, Button 컴포넌트를 임포트한다.

    const navigate = useNavigate()

    페이지를 이동하기 위하여 useNavigate 함수를 실행하여 navigate 함수를 반환한다.

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

    Home 버턴을 클릭하면 실행되는 이벤트핸들러 함수이다. 버튼을 클릭하면 홈화면으로 이동한다. 

    <Menu>
        <Button handleClick={toHomePage}>Home</Button>
    </Menu>

    홈화면으로 이동하기 위하여 네비게이션 바를 추가하였다. Menu 컴포넌트를 렌더링하고 Button 컴포넌트를 children 으로 전달한다. 

    .Recommendation-container{
        background: url('../assets/images/background.jpg');
        width: 100%;
        text-align: center;
        border: 1px solid black;
    }
    .Recommendation-bestmovies{
        display: flex;
        justify-content: center;
        padding: 20px;
        margin-top: 100px; 
    }

    Recommendation.css 파일을 위와 같이 수정하자! margin-top 속성을 100px 로 설정한다.

     

    728x90
Designed by Tistory.