Toy Project/React로 영화 웹 서비스 만들기

React로 영화 웹 서비스 만들기

짬송 2021. 5. 4. 16:40

완성 소스1 → 영화진흥원 API사용(포스터 x)

완성 소스2 → 강의내용 API 사용(포스터o)

 

 

이 포스팅은 강의내용API를 사용한

완성 소스2

에 관한 포스팅이다.

[미리보기]

미리보기

 

 

프로젝트 시작

개발환경 구축, JSX 와 props, state에 대한 설명은 같은 카테고리의 글을 참고하길 바란다.

개발환경 구축이 완료되었다면 프로젝트를 시작할 수 있다 ~!

 

디렉토리 구조

 

실행 환경

 

 

API 받아오기

  • yts.mx 라는 사이트에서 영화 관련 API를 받아온다. (axios를 사용)
  • API로 받아오는 영화 데이터를 다루기 위해서 movies.js 파일을 생성했다.
  • 컴포넌트 라이프 사이클을 활용하여 로딩이 끝난 다음 데이터를 불러 띄우는 과정을 진행하였다.

 

render 함수가 호출되고 나서 어떤 함수가 실행되어야 하는지 정의해준다.

state 객체에는 로딩 여부를 체크하는 요소가 있고, 영화 데이터를 담을 빈 배열을 만들어주었다.

render 함수 내부에서는 브라우저에 보일 요소들을 return 하고,

각각의 엘리먼트에 className을 지정했다.

(html에서는 class 라고 사용하는데, className으로 자동변환되거나 콘솔경고 메세지가 나온다.)

class App extends React.Component {

   state = {
      isLoading: true,
      movies: []
   }

   componentDidMount() {

      // render가 mount된 이후 실행

   }

   render() {
      
      // render 함수에서 사용할 state의 키들을 정의
      const { isLoading, movies } = this.state;

      return <section className='container'> 
         { isLoading ?
            ( <div className="loader">
               <span className="loader_text">Loading</span>
            </div> ) : (

            )
         }
   }
}

 

componentDidMount()

랜더링을 하기 위한 기본세팅은 마쳤다.

이제 componentDidMount()로 실행시킬 함수를 구성해야 한다.

componentDidMount() 함수는 비동기로 처리 되는데, API를 이용하여 JSON 데이터를 받아와야 한다.

그 다음 기존의 state를 다시 set 해주는 this.setState() 를 처리해주면 된다.

비동기를 처리하기위한 문법으로 async/await 를 사용하였다.

async를 진행할것인데 특정 await를 기다리라는 명령어이다.

데이터를 제공하는 API는 data > data > movies 의 구조로 이루어져 있다.

(데이터의 중첩구조)

최종 movies 객체에서 각각의 영화 정보를 확인할 수 있다.

getMovies = async() => {
      const { data: { data: { movies }}} = await axios.get(url 주소)

      this.setState({ movies, isLoading: false})
      // movies = movies: movies
      // setState는 await로 비동기가 이뤄진 다음 작동
   } 

   componentDidMount() {
      this.getMovies();
   }

 

UI에 뿌리기

받아온 API의 데이터를 UI에 뿌려주어야 한다.

그래서 movies.js 파일을 만들어서 <Movie />라는 함수형 컴포넌트를 만들어주었다.

함수형 컴포넌트에는

props에 대한 정의와 사용

이 중요하다.

영화 API의 데이터 중에서 사이트에 보여질 데이터는 title, year, title, summary, poster, genre 이다.

그중에서 genre는 요소가 한개이상이기때문에 배열로 처리해주었다.

function Movie({ id, year, title, summary, poster, genres }) {
   return <div>
      < img src={poster} alt={title} title={title} />
      <div>
         <h3> { title } </h3>
         <h5> { year } </h5>
         <ul>
            {genres.map((genre, idx) => {
               <li key={idx}> { genre } </li>
            })}
         </ul>   
         <p> { summary.slice(0, 180) } </p>
      </div>
   </div>
}

slice 메소드 → 영화 별 summary의 길이가 달라서 보여지는게 이쁘지 않기때문에 일정크기로

자르는데에 사용하였다.

 

 

render함수 완성시키

componentDidMount() 의 호출로 인해 state 객체 안에 만들어둔

movies 배열에 데이터가 반영되었다.

API의 데이터가 다 받아와지면 getMovies() 함수가 작동하고

Movie 컴포넌트가 JSX에 의해서 브라우저에 떠오르게 된다.

<div className="movies">
            {movies.map(movie => {
              return < Movie key={movie.id}
                id={movie.id}
                year={movie.year}
                title={movie.title}
                summary={movie.summary}
                poster={movie.medium_cover_image}
                genres={movie.genres} />
            })}
          </div>)}

 

Github Pages

만든 앱 페이지를 배포해보자.

  1. gh-pages 를 설치한다 $ npm i gh-pages
  1. package.json 에 hompage를 추가한다.( “homepage”: “https://[유저이름].github.io/[레파지토리이름]/” ) -> 소문자와 띄어쓰기는 안됨!
  1. deploy와 predeploy 명령어를 추가해준다.
  1. npm run build명령어를 입력하여 build폴더를 얻는다.
  1. github 레파지토리의 settings → github pages를 활성화 시켜준다.
  1. npm run deploy 명령어를 입력하고나서 다시 github pages에 들어가보면gh pages라는 브런치가 생성되어있으니 gh pages로 브런치를 변경해준다.
  2.  

Building the Router

Github Pages에서 끝나도 되지만, 추가적인 코스로 라우터를 빌드해보자.

npm i react-router-dom 명령어 실행


import React from 'react';
import { HashRouter, Route  } from 'react-router-dom'
import About from './routes/About'

function App(){
  return (
    <HashRouter>
      {/* Route 안에 props 2개 */}
      {/* 1. 랜더링할 스크린 */}
      {/* 2. 뭘 할지 정해주는 props */}
      <Route path="/" component={Home} />
      <Route path="/about" component={About} />
      {/* 만약 내가  path="/about" 로 가게되면 component={About}을 보여줘 라는 뜻 */}
    </HashRouter>
  );
} 

export default App;

 

하지만 위와같은 코드에서는 /about로 들어가게되면 home , about 둘다 뜨는것을 확인할 수 있다.

렌더링 방식때문이다 path=”/about”이 path=”/” 안에 포함된다고 생각을하고 렌더링을 하기 때문

이때 아래와 같이 값을 추가해주면 된다.

function App(){
  return (
    <HashRouter>
      <Route path="/" exact={true} component={Home} />
      {/* only url path="/" 일때만 Home 이 렌더링 되게  */}
      <Route path="/about" component={About} />
    </HashRouter>
  );
}

exact ={ } : 이거 아니면 렌더링 안한다.

 

Building the Navigation

다음은 네비게이션을 빌드해보자.


import React from 'react'
import { Link } from 'react-router-dom'

function Navigation(){
    return (
      <div>
        {/* <a href="/">HOME</a>
        <a href="/about">ABOUT</a> */}
        {/* a  href 를 사용하면 페이지를 아예 새로고침해버린다. */}
        <Link to="/">HOME</Link>
        <Link to="/about">ABOUT</Link>
      </div>
    );
}

export default Navigation;

// src/App.js
function App(){
  return (
    <HashRouter>
      <Navigation />
      {/*  Navigation 안에서 사용되는 Link 는 Router 밖에서는 사용될 수 없다.*/}
      <Route path="/" exact={true} component={Home} />
      <Route path="/about" component={About} />
    </HashRouter>
  );
}

Sharing Props Between Routes

router 에 있는 모든 router들은 props를 가진다.

영화를 누르면 상세화면으로 넘어가기때문에 ,

Movie.js 의 div를 Link로 바꿔주고, state 의 props 값을 전달해 주었다.

 

Redirecting

카드를 눌렀을때만 detail page 에 들어가야하는데 새로고침하면

받아온 값이 없기 때문에

undefined가 뜬다 이때 리다이렉트 방법!

import React from 'react';

class Detail extends React.Component{
    componentDidMount(){
        const { location, history } = this.props;
        if(location.state === undefined){
            history.push("/")
            // 카드를 클릭해서 들어온게 아니라면 리다이렉트 시킴( 홈 화면으로 )
        }
        console.log(location.state); 
    }

    render(){
        const {location} = this.props
        if(location.state){
            return <span>{location.state.title}</span>;
        } else return null 
    }
}

export default Detail;

또한 pathname 을 /movie-detail 로 하기보다는 각 영화의 아이디 값을 부여해 줄 수 있다.

(github 소스코드 참고)

 

강의의 핵심적인 부분은 여기까지다.

강의에서 css에 관해서 다루지않으므로 전체 소스코드는 github페이지를 참고하면 된다.

(니코쌤의 github에서 css를 긁어와 사용했다.)