NetFlix 앱 만들기 - part1
1) The Movie DB API 생성
넷플릭스 애플리케이션을 만들기 위해선 영화 정보를 담은 API를 가져다 써야하므로, TMDB라는 사이트에서
영화 정보에 대한 openAPI를 가져다 쓸 것이다.
아래 사이트 접속해서 회원가입 한 후 프로필 사진 클릭 > Settings > API 들어가면 API 키를 복사할 수 있다.
https://www.themoviedb.org/
The Movie Database (TMDB)
Welcome. Millions of movies, TV shows and people to discover. Explore now.
www.themoviedb.org
넷플릭스 애플리케이션 작업 폴더 src에 api 폴더를 생성한 후 위에서 생성한 API키를 이용해 data를 가져오기 위한 파일 2개를 생성한다. -> axios.js, requests.js
[axios.js]
생성한 api키를 params api_key에 복붙한다.
import axios from "axios" ;
const instance = axios . create ({
params : {
api_key : "3a2a9cfbe446caa4d9dbbece8a90829a" ,
language : "ko-KR" ,
},
})
export default instance ;
[requests.js]
requests.js에선 넷플릭스오리지널, 액션무비, 코미디무비 등 카테고리별로 영화 data를 분류해 가져올 수 있도록 객체를 생성한다.
const requests = {
fetchNowPlaying : "movie/now_playing" ,
fetchNetflixOriginals : "/discover/tv?with_networks=213" ,
fetchTrending : "/trending/all/week" ,
fetchToRated : "/movie/top_rated" ,
fetchActionMovies : "/discover/movie?with_genres=28" ,
fetchComedyMovies : "/discover/movie?with_genres=35" ,
fetchHorrorMovies : "/discover/movie?with_genres=27" ,
fetchRomanceMovies : "/discover/movie?with_genres=10749" ,
fetchDocumentaries : "/discover/movie?with_genres=99" ,
}
export default requests ;
2) 넷플릭스 애플리케이션 전체 구조 생성
넷플릭스 애플리케이션을 만드는데 해야할 기초 작업은 전체 구조를 부분별로 나눠 각 섹션을 컴포넌트화 하는 것이다.
애플리케이션 기본 뼈대 구조
전체 구조 그룹처럼 각 컴포넌트를 생성할 파일을 src폴더 아래 아래와 같이 생성해준다.
+)MovieModal 폴더는 영화 data에 대한 resource를 가져올 때 나중에 사용할 것이므로 폴더를 생성해놓았다.
이번 part1에서는 Navigation Bar와 Banner 부분을 만들어볼 것이다.
그럼 이제 본격적으로 네비게이션 바 부터 구현을 시작해보자.
3) Navigation Bar 생성
네비게이션 바는 넷플릭스 화면 상단부에 넷플릭스 로고와 사용자 프로필 아이콘이 떠 있는 부분이다.
const [ show , handleShow ] = useState ( false );
사용자가 마우스를 아래로 스크롤하면, 네브바의 색이 투명해지도록 설정해주기 위해 스크롤을 감지할 변수인 show를 useState 훅을 이용해 관리해 줄 것이다.
useEffect (() => {
/*window가 scroll될 때마다 실행될 콜백 함수를 정의하는 리스너*/
window . addEventListener ( "scroll" , () => {
if ( window . scrollY > 50 ){
handleShow ( true ); /*handleShow(true) 호출하여 어떤 상태 변화나 동작 실행*/
} else {
handleShow ( false ); /*handleShow(false) 호출하여 다른 상태 변화나 동작 실행*/
}
})
/*clean-up 함수: 컴포넌트 언마운트 될 때 실행*/
return () => {
/*스크롤 이벤트 리스너를 제거하는 데 필요한 참조가 없는 상태, 빈 함수 전달*/
window . removeEventListener ( "scroll" ,() => {});
}
},[]);
EventListener가 scroll을 감지하여 (scrollY>50)이면 세터함수를 사용해 show=true로 설정해주고 그렇지 않으면 false로 설정해주어 변수 값 변화에 다라 다른 동작을 수행하도록 한다.
-> 아래 코드는 네브바의 UI를 구성하는 태그들을 코드로 작성하여 App.js파일에서 <Nav /> 컴포넌트 호출 시 웹페이지에 띄워지는 화면부를 작성한 것이다.
return (
/*css클래스 동적 적용: boolean 변수 show값에 따라 nav_black클래스가 조건적으로 추가됨.*/
< nav className = { `nav ${ show && "nav_black" } ` } >
< img
alt = "Netflix logo"
className = "nav_logo"
onClick = { () => window . location . reload () }
/>
< img
alt = "User logged"
className = "nav_avatar"
/>
</ nav >
)
<nav>태그에서 보듯이 scrollY>50인 경우, 즉 show=true인 경우는 className="nav nav_black"이 되어 해당 클래스 이름에 맞는 css 스타일이 적용되고(darkMode) show=false인 경우는 className="nav"가 되어 또 다른 css 스타일이 적용된다. 즉, 스크롤에 따라 동적으로 다른 css 스타일이 적용되도록 작성한 것이다.
4) 이미지 배너 생성
const fetchData = async () => {
/*axios 라이브러리를 사용해 http get 요청 보내고 현재 상영중인 영화 목록 가져옴.*/
/*await은 api에서 data 가져올 때까지 기다렸다 코드 실행*/
const request = await axios . get ( requests . fetchNowPlaying );
console . log ( request );
/*request.data.results 배열에서 랜덤하게 하나의 영화 선택하기*/
const movieId = request . data . results [
/*JS로 랜덤 숫자 가져오기: Math.floor(Math.random()*max);*/
Math . floor ( Math . random () * request . data . results . length )
]. id ;
/*다시 axios.get 사용해 선택된 movieId에 해당하는 영화의 상세 정보 요청*/
const { data : movieDetail } = await axios . get ( `movie/ ${ movieId } ` ,{
/*응답에 비디오 정보도 포함시켜라(추가적 쿼리 파라미터)*/
params : { append_to_response : "videos" },
});
/*movieDetail 변수에 상세정보요청으로부터 반환된 데이터가 저장됨*/
/*컴포넌트가 해당 영화의 상세정보로 업데이트됨*/
setMovie ( movieDetail );
}
fetchData함수로 axios라이브러리를 사용해 http get 요청을 보내 TMDB에서 API 키 생성해서 API 폴더 만든 곳에서 현재 상영중인 영화 목록을 가져온다.
*이때 useEffect()함수를 사용해 컴포넌트가 마운트 된 후 딱 한번만 fetchData()를 호출하도록 한다.
<Banner /> 컴포넌트의 return부분에는 화면에 표시될 UI 부분을 작성한다.
클래스 네임이 "banner__contents"라는 div 안에 "banner_title" "banner__buttons" "banner_description" div를 생성해 Banner 안에 영화 제목, button(Play버튼, More Information 버튼)을 생성한다.
"banner__description" div에서는 영화 줄거리에 대한 간략한 소개글을 가져와 화면에 띄우는데, truncate라는 함수를 정의해 글자수가 300자 이상이라면 문자열이 잘리도록 설정해주었다.
const truncate = ( str , n ) => {
return str ?. length > n ? str . substr ( 0 , n - 1 ) + "..." : str ;
};
-> str.length > n 이면, str의 0~n-1번째 index 글자까지 문자열을 잘라 화면에 출력하도록 도와주는 함수
5) 비디오 배너 생성
위 과정에서 기본적인 이미지 Banner 구조를 만들었고, 이제 Play 버튼을 누르면 영화 트레일러 비디오가 재생되도록 비디오 배너를 생성해볼 것이다.
const [ isClicked , setClicked ] = useState ( false );
button의 onClick 이벤트가 발생한 것을 감지하기위해 useState 훅을 사용해 isClicked 변수를 생성한다.
< div className = "banner__buttons" >
< button className = "banner__button play"
onClick = { () => setClicked ( true ) }
> Play
</ button >
< button
className = "banner__button info"
>
< div className = "space" ></ div > More Information
</ button >
</ div >
className="banner__button play"에 해당하는 버튼이 눌리면, 영화 트레일러에 대한 비디오 재생화면이 뜨도록 event를 설정해 줄 것이다. 따라서 해당 button에 onClick 이벤트가 발생하면 setClicked 세터함수를 사용해 isClicked=TRUE가 되도록 한다.
isClicked==false인 경우라면, 원래대로 이미지 배너 화면을 return해주면 되고, isClicked==true라면, 비디오 재생 화면을 띄워줘야한다.
if ( ! isClicked ){
return (
< header
className = "banner"
style = { {
backgroundPosition : "top center" ,
backgroundSize : "cover" ,
} }
>
< div className = "banner__contents" >
< h1 className = "banner__title" >
{ movie . title || movie . name || movie . original_name }
</ h1 >
< div className = "banner__buttons" >
< button className = "banner__button play"
onClick = { () => setClicked ( true ) }
> Play
</ button >
< button
className = "banner__button info"
>
< div className = "space" ></ div > More Information
</ button >
</ div >
< h1 className = "banner__description" >
{ truncate ( movie ?. overview , 300 ) }
</ h1 >
</ div >
< div className = "banner--fadeBottom" />
</ header >
)
} else {
return (
< Container >
< HomeContainer >
</ HomeContainer >
</ Container >
)
}
}
위 코드처럼 if(!isClicked)인 경우는 이미지배너에서 작성했던 코드를 그대로 넣어주고, isClicked==True인 경우는
Iframe 컴포넌트를 사용해 비디오 player를 가져오도록 한다.
이때 Styled Component를 이용해 태그에 css를 적용하고, 새로운 이름으로 정의한 태그를 return문 안에 써주었다.
https://styled-components.com/
styled-components
CSS for the <Component> Age
styled-components.com
-Styled Component란?
기존 돔을 만드는 방식인 css, scss 파일을 밖에 두고, 태그나 id, class이름으로 가져와 쓰지 않고, 동일한 컴포넌트 에서 컴포넌트 이름을 쓰듯 스타일을 지정하는 것을 styled - components 라고 한다.
const Iframe = styled . iframe `
width:100%;
height:100%;
z-index:-1;
opacity:0.65;
border:none;
&::after{
content:"";
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
}
`
const Container = styled . div `
display:flex;
justify-content:center;
alien-items:center;
flex-direction:column;
width:100%;
height:100vh;
`
const HomeContainer = styled . div `
width:100%;
height:100%;
`
해당 코드는 styled component를 이용해 작성한 스타일 지정 컴포넌트이다.
const {새롭게 지정한 컴포넌트 이름} = styled.{tagName}`{style 적용 코드}` 이러한 형식으로 작성해주면,
<{새롭게 지정한 컴포넌트 이름} />이런식으로 컴포넌트 형태로 사용자 임의의 스타일이 적용된 태그를 사용할 수 있다.
[구현 현황]
->기본 화면(Nav bar + image Banner)
->Play 버튼 누를 시 비디오 재생화면으로 이동