본문 바로가기

Clipper 스터디

Inflearn 4주차

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={{
                backgroundImage:`url("https://image.tmdb.org/t/p/original/${movie?.backdrop_path}")`,
                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>
                <Iframe width="640" height="360" src="https://www.youtube.com/embed/Ga6RYejo6Hk?si=mMb8AkAgZdyWD_LQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></Iframe>
                </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 버튼 누를 시 비디오 재생화면으로 이동

'Clipper 스터디' 카테고리의 다른 글

백준 4주차  (0) 2023.11.07
Inflearn 3주차  (0) 2023.11.05
백준 2주차  (0) 2023.10.05
Inflearn 2주차  (0) 2023.10.02
백준_1주차  (0) 2023.09.28