개발자를 향해...

[ReactJS로 영화 웹 서비스 만들기] #6 ROUTING BONUS 본문

웹 자바스크립트 공부/ReactJS + node.js

[ReactJS로 영화 웹 서비스 만들기] #6 ROUTING BONUS

eugeneHwang1124 2021. 2. 10. 15:23
728x90
반응형

본 내용은 노마드코더 - ReactJS로 영화 웹 서비스 만들기 를 수강하며 작성되었습니다.

 

 

Router 넣기

 npm i react-router-dom을 설치한다. 그리고 components와 routes라는 폴더를 src폴더의 하위에 생성하고 movie.js와 movie.css파일을 components 폴더에 넣어준다.

routes에다 App.js파일의 내용을 Home.js파일에 넣는다.

import React from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import Movie from '../components/Movie';
import './Home.css';

class Home extends React.Component {
  
  state = {
    isLoading: true,
    movies:[]
  }
  
  getMovies=async()=>{
    const {data: {data :{movies}}} =await axios.get("https://yts-proxy.now.sh/list_movies.json");
    this.setState({movies,isLoading:false});
  }
  componentDidMount() {
    this.getMovies();
  }

  render() {
    const { isLoading,movies}=this.state;
    return (
      <section className="container">
        { isLoading ? (<div className="loader">
          <span className="loader__text">Loading...</span>
        </div>
        ) : (
          <div className="movies">
            {movies.map(movie =>{
              console.log(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>
        )}
      </section>
      );
  }
}

export default App;

그리고 App.css 파일도 routes에 넣어준다. App.js 파일은 다음과 같이 수정한다.

import React from 'react';


function App() {
  return<span></span>;
}

export default App;

<span>부분에는 rout를 넣어줄 것이다. rout는 심플한 리액트 컴포넌트로 설정한 주소에서 특정 파일로 이동할 수 있는 것이다. 다시말해 라우터는 url을 가져다 우리가 설정한 컴포넌트를 불러온다.  import로 HashRouterRoute를 가져오고 return 부분에 HashRouter을 리턴한다. Route 안에는 스크린을 render할 prop과 url이 뭘할지 정해주는 prop이 들어간다.

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

function App() {
  return <HashRouter>
    <Route path="/about" component={About}/>
  </HashRouter>
}

export default App;

path로 about.js로 들어가서 About 컴포넌트를 보여준다. About의 리턴 부분에 라우터로 경로를 추가한다.

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

function App() {
  return <HashRouter>
    <Route path="/">
      <h1>Home</h1>
    </Route>
    <Route path="/about">
      <h1>About</h1>
    </Route>
  </HashRouter>
}

export default App;

about url일 때

만약 path 부분을 서로다르게 설정한다면 주소에 home과 about을 입력했을 때 각각에 해당하는 화면만 출력될 것이다.

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

function App() {
  return <HashRouter>
    <Route path="/home">
      <h1>Home</h1>
    </Route>
    <Route path="/about">
      <h1>About</h1>
    </Route>
  </HashRouter>
}

export default App;

home url 일 때
about url일 때

처음 코드에서는 path 경로가 home과 about를 같이 렌더링 한다. 이렇게 작동하는 이유는 url일 작동하는 방식이기 때문이다. Router는 기본적으로 url을 가져와서 라우터의 모든 요소들과 비교한다.

만약 여기에

<Route path="/home/introduction">
	<h1>Intro</h1>
</Route>

라는 라우터를 추가한다면 다음과 같은 결과를 얻을 수 있다.

  router는 모든 라우터들과 비교를 한다, 그 결과 2개의 라우터가 매치되는데 /home과 /home/introduction을 모두 찾아 렌더링 하게된다. 따라서 첫번째 경우에 /about를 주소에 입력하면 /라는 라우터와 /about라는 라우터가 모두 렌더링된것이다. 이렇게 두가지가 한번에 렌더링 되는 것을 고치는 방법이 있다.

exact true를 첫번째 라우터에 추가해준다.

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

function App() {
  return <HashRouter>
    <Route path="/" exact={true} component={Home}/>
    <Route path="/home/introduction">
      <h1>Intro</h1>
    </Route>
    <Route path="/about">
      <h1>About</h1>
    </Route>
  </HashRouter>
}

export default App;

이렇게 하면 주소가 정확히 /라우터 하나와만 일치할때만 렌더링한다.

exact={ture}일 때 about url
exact={true}일 때 / url

네비게이션  넣기

App.js 의 라우터에 <Navigation />을 추가하고 Navigation이라는 컴포넌트를 components에 생성한다.

<Navigation.js>

import React from 'react';

function Navigation(){
    return <div>
        <a href="/">Home</a>
        <a href="/about">About</a>
    </div>
}

export default Navigation;

실행해보면 버튼이 잘 작동되지 않는것을 볼 수 있다. 왜냐하면 home을 누르면 현재 리액트가 죽고 전체 페이지가 새 페이지가 새로고침되기 때문이다. 따라서 a와 href가 아니라 Link를 사용해보자.

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

function Navigation(){
    return <div>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
    </div>
}

export default Navigation;

이제 모든 페이지가 새로고침 되지 않는다.

※Link를 router 밖에서 쓸 수 없다. 

만약 App.js에서 HashRouter대신 BrowserRouter를 넣어주면 주소에 나타나던 #이 없어진다. 그러나 github 페이지에 설정하기 힘들다.

이제 네비게이션을 꾸며보자. Navigation.css를 만들고 코드를 작성한다. 링크

 

영화 상세 설명 페이지

모든 라우터의 라우터들은 props를 가진다. 직접 넣지 않아도 기본으로 주어지는 props가 있다. 이외에도 우리가 넣어준다면 클릭으로 props를 전달할 수 있다. 만약 home에서 영화를 클릭하면 영화의 모든 데이터를 상세페이지의 props로 넘겨준다.  react-rout의 공식 문서를 보면 Link의 to는 string으로 값을 바꿀 수 있다. 

<Navigation.js>

import React from "react";
import { Link } from "react-router-dom";
import "./Navigation.css";

function Navigation() {
  return (
    <div className="nav">
      <Link to="/">Home</Link>
      <Link to={{
          pathname:"/about",
          state:{
              fromNavigation:true
          }
      }}>About</Link>
    </div>
  );
}

export default Navigation;

이렇게 object를 전달할 수 있다.

<About.js>
import React  from "react";

function About(props){
    console.log(props);
    return <span>현재 페이지에 대하여: i built it because i love movie</span>
}

export default About;

<App.js>
import React from 'react';
import {HashRouter, Route} from 'react-router-dom';
import About from'./routes/About';
import Navigation from './components/Navigation';
import Home from './routes/Home';

function App() {
  return <HashRouter>
    <Navigation />
    <Route path="/" exact={true} component={Home}/>
    <Route path="/about" component={About}/>
  </HashRouter>
}

export default App;

About의 콘솔창

이제 Linkt to를 원래대로 수정하자. 그리고 Movie.js를 다음과 같이 바꾸어 링크를 설정해준다.

import React from 'react';
import PropTypes from 'prop-types';
import {HashRouter, Route, Link} from 'react-router-dom';
import axios from 'axios';
import './Movie.css';

function Movie({id, year,title, summary,poster, genres}){
    return(
        <Link to={{
            pathname:'/movie-detail',
            state:{
                year,
                title,
                summary,
                poster,
                genres
            }
        }}>
        <div className="movie">
            <img src={poster} alt={title} title={title}/>
            <div className="movie__data">
            <h3 className="movie_title">{title}</h3>
            <h5 className="movie_year">{year}</h5>
            <ul className="genres">{genres.map((genre,index)=><li key={index} className="genres__genre">{genre}</li> )}</ul>
            <p className="movie_summary">{summary.slice(0,180)}</p>
            </div>
        </div>
        </Link>
    );
}
Movie.propTypes = {
    id:PropTypes.number.isRequired,
    year:PropTypes.number.isRequired,
    title:PropTypes.string.isRequired,
    summary:PropTypes.string.isRequired,
    poster:PropTypes.string.isRequired,
    genres:PropTypes.arrayOf(PropTypes.string).isRequired
}

export default Movie;

이제 영화를 클릭하면 /movie-detail로 가게 되는데 이때 Movie의 모든 props를 전달하도록 했다. 이제 movie-detail을 만들자.

// <Detail.js> in router파일
import React from 'react';
import PropTypes from 'prop-types';
import {HashRouter, Route, Link} from 'react-router-dom';

//function Detail({year,title, summary,poster, genres}){
function Detail(props){
    console.log(props);
    return<h1>hello</h1>;
}

export default Detail;

// <App.js>
import React from 'react';
import {HashRouter, Route} from 'react-router-dom';
import About from'./routes/About';
import Navigation from './components/Navigation';
import Home from './routes/Home';
import Detail from './routes/Detail';

function App() {
  return <HashRouter>
    <Navigation />
    <Route path="/" exact={true} component={Home}/>
    <Route path="/about" component={About}/>
    <Route path="/movie-detail" component={Detail}/>
  </HashRouter>
}

export default App;

Home 에서 영화를 클릭하여 movie-detail을 들어가면

이렇게 전달 된것을 볼 수 있다.

 

영화 제목을 가져와 화면에 표시해보자.

import React from 'react';
import PropTypes from 'prop-types';
import {HashRouter, Route, Link} from 'react-router-dom';

//function Detail({year,title, summary,poster, genres}){
class Detail extends React.Component{
    componentDidMount(){
        console.log(this.props);
        const {location, history}=this.props;
        
        if(location.state===undefined){ // 영화 링크를 통해 들어오지 않았다면 홈화면으로 보냄
            history.push('/');
        }
    }
    render(){
        const { location } = this.props;
        if (location.state) {
            return <span>{location.state.title}</span>;
        } else {
            return null;
        }
    }
}

export default Detail;

위 코드는 만약 영화 정보를 가져오지 않았을 경우 홈페이지로 리다이렉팅하는 기능을 추가했다. Detail페이지로 올 수 있는 방법은 영화를 클릭했을 때만이다.

 

마지막으로 주소부분에 현재 영화의 id를 넣어보자.

<App.js>

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

function App() {
  return <HashRouter>
    <Navigation />
    <Route path="/" exact={true} component={Home}/>
    <Route path="/about" component={About}/>
    <Route path="/movie-detail/:id" component={Detail}/>
  </HashRouter>
}

export default App;

<Movie.js>

import React from 'react';
import PropTypes from 'prop-types';
import {HashRouter, Route, Link} from 'react-router-dom';
import axios from 'axios';
import './Movie.css';

function Movie({id, year,title, summary,poster, genres}){
    return(
        <Link to={{
            pathname:`/movie-detail/${id}`,
            state:{
                year,
                title,
                summary,
                poster,
                genres
            }
        }}>
        <div className="movie">
            <img src={poster} alt={title} title={title}/>
            <div className="movie__data">
            <h3 className="movie_title">{title}</h3>
            <h5 className="movie_year">{year}</h5>
            <ul className="genres">{genres.map((genre,index)=><li key={index} className="genres__genre">{genre}</li> )}</ul>
            <p className="movie_summary">{summary.slice(0,180)}</p>
            </div>
        </div>
        </Link>
    );
}
Movie.propTypes = {
    id:PropTypes.number.isRequired,
    year:PropTypes.number.isRequired,
    title:PropTypes.string.isRequired,
    summary:PropTypes.string.isRequired,
    poster:PropTypes.string.isRequired,
    genres:PropTypes.arrayOf(PropTypes.string).isRequired
}

export default Movie;

반응형