[React] ReactJS로 영화 웹 서비스 만들기 (7) 完

Lpla

·

2021. 2. 20. 22:16

반응형

ReactJS로 영화 웹 서비스 만들기 (1)

ReactJS로 영화 웹 서비스 만들기 (2)

ReactJS로 영화 웹 서비스 만들기 (3)

ReactJS로 영화 웹 서비스 만들기 (4)

ReactJS로 영화 웹 서비스 만들기 (5)

ReactJS로 영화 웹 서비스 만들기 (6)

 


  • 이번 시간에는 라우터의 기본 기능을 익히며 리액트 기초 강의를 마무리하도록 하겠다.

 

라우터 설치 및 준비

  • 라우터는 한 페이지 내에서 명령에 따라 필요한 컴포넌트를 불러오는 형태를 말한다.
  • 우리는 2개의 라우터를 만들 것이다.
  • 이전 시간까지 만든 영화 페이지를 Home 탭에, 사이트를 소개하는 내용을 About 탭에 만드는 것이 목적이다.
  • npm i react-router-dom으로 라우터를 설치한다.
  • 그리고 src폴더 안에 components, routes 라는 이름으로 2개의 새 폴더를 만든다.
  • components 폴더 안에 Movie.css와 Movie.js를 옮긴다.

 

  • 그리고 routes 폴더 안에 About.js와 Home.js를 만든다.

 

  • 이제 App.js와 App.css 를 Home.js와 Home.css 로 바꾼다. App.js 의 코드를 모두 복사하여 Home.js 에 붙여 넣는다.
  • App.css 의 코드는 비어 있을 것이다. 일단 이름만 Home.css로 바꿔준다.
  • 경로와 이름이 달라졌으니 App.js 상단에 import Movie from "./Movie"import Movie from "../components/Movie"로 수정하고 바로 아래 css도 import "./Home.css" 도 바꿔준다.
  • class App extends React.Componentclass Home extends React.Component로 바꾼다.
  • App.js 의 기존 코드를 모두 지우고 처음부터 작성한다.

App.js

import React from "react";

function App() {
  return <span>없음</span>
}
export default App;

  • 에러 없이 화면이 보인다면 파일을 무사히 옮긴 것이다.

 

라우터로 네비게이션 만들기

  • 이제 App.js에 라우터를 만든다.

App.js

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;
  • HashRouter를 import 한다.
  • 이 HashRouter를 사용하여 url에 해시(#)을 붙일 수 있고 서버에 요청 없이 화면을 보여줄 수 있다.
  • url에 해시가 붙는 것이 싫다면 HashRouter 대신에 BrowserRouter 를 사용하면 된다.
  • (실제로 BrowerRouter 사용률이 훨씬 높다. 다만 우리가 자료를 배포하고 있는 gh-pages에는 BrowserRouter를 사용하면 에러가 발생한다고 하니 HashRouter를 사용하겠다.)
  • 그리고 비어 있는 About.js에 컴포넌트를 하나 생성한다.

About.js

import React from "react";

function About() {
  return (
    <div>
      <p>About페이지입니다.</p>
      <p>영화 웹서비스 만들기 공부 중입니다.</p>
    </div>
  )
}

export default About;

 

  • /about 으로 접속하면 입력한 내용을 확인할 수 있다.
  • Home도 똑같은 방식으로 생성하면 되는데 일단 아래와 똑같이 만들어보자.

App.js

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

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

export default App;
  • 기본 경로(/)에 Home 컴포넌트를 연결했고, /about에 About 컴포넌트를 연결했다.
  • 이제 기본 페이지를 확인하면 영화 리스트가 잘 보인다.

  • 여기까지는 문제 없다.
  • 하지만 /about 주소로 이동하면 about만 단독으로 있지 않고 Home도 함께 보인다.

  • 조금만 생각하면 상위 컴포넌트까지 렌더링한 것을 알 수 있다.
  • 이것이 리액트 라우터가 작동하는 방식이다.
  • 헤결 방법은 <Route path="/" exact={true} component={Home} />처럼 exact를 추가하면 된다.

 

  • 이제 각각의 페이지를 구분 지었으니 네비게이션을 만들면 된다.
  • App.js 상단에 Navigation을 import하고 HashRouter 안에 네비게이션 컴포넌트를 불러온다.
import Navigation from "./components/Navigation";

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

 

  • components 폴더 안에 Navigation.js 를 만들고 컴포넌트를 생성한다.
import React from "react";

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

export default Navigation;

 

  • css로 중앙정렬만 맞추고 페이지를 새로고침하면 네비게이션이 정상적으로 보이지만 한 가지 문제가 있다.
  • 우리는 네비게이션에 a태그를 사용했기 때문에 Home이나 About을 클릭하게 되면 페이지가 새로고침된다.
  • 새로고침 없이 페이지를 이동하길 원하기 때문에 이를 없애야 한다.
  • 먼저 App.js 상단에 import { HashRouter, Route } from "react-router-dom"; 을 import 한다.
  • 그리고 <a href="/"> 대신에 <Link to="/"> 로 두 태그를 바꾼다.
  • 이제 다시 시도해보면 새로고침 없이 페이지가 바뀐다.
  • 다만 Link는 라우터 안에 있어야 한다.

 

영화 정보 가져가기

  • 이제 영화 리스트에서 영화를 하나 클릭했을 때 그 영화의 세부 정보가 담긴 페이지로 이동하려고 한다.
  • 그전에 route props 를 알아야 한다. About.js 를 열어보자.
  • 모든 컴포넌트는 props를 가진다. About 컴포넌트에 props를 추가하고 console.log(props); 를 추가해보자.
import React from "react";
import "./About.css";

function About(props) {
  console.log(props);
  return (
    <div className="about_con">
      <p>About페이지입니다.</p>
      <p>영화 웹서비스 만들기 공부 중입니다.</p>
    </div>
  )
}

export default About;

  • history, location, match, staticContext 총 네 개의 props가 존재하고 있다.
  • 이것은 react-router에 의해 만들어진 것으로 이 props의 정보를 다른 곳에 전달할 수 있다.
  • 이제 props를 about에 보내는 작업을 해보자.
  • Navigation.js 의 Link to는 문자열(string)이나 객체(object)로 대체할 수 있다.

출처 : https://reactrouter.com/web/api/Link

function Navigation() {
  return (
    <div className="nav_con">
     <Link to="/">Home</Link>
     <Link to={{
       pathname:"/about",
       state: {
         fromNavigation: true
       }
     }}>About</Link>
    </div>
  )
}
  • Link에 기존의 /about 대신 pathname과 state를 입력하고 다시 about 페이지로 이동해봤다.

  • 그러자 pathname과 state가 about페이지에도 따라 오는 것을 확인할 수 있었다.
  • 이 과정은 props를 다른 곳에 전달할 수 있다는 것을 확인하기 위함이었으니 다시 Navigation.js를 원래대로 돌리고 영화 정보를 전달하기 위한 새로운 컴포넌트를 만든다.

Movie.js

import { Link } from "react-router-dom";

function Movie({ id, year, title, summary, poster, genres }) {
  return (
    <Link to={{
      pathname: "/movie-detail",
      state: {
        year:year,
        title:title,
        summary:summary,
        poster:poster,
        genres:genres
      }
    }}>
      <div className="movie_list">
        <div className="movie_poster">
          <img src={poster} alt={title} />
        </div>
        <div className="movie_contents">
          <h3 className="movie_title">{title}</h3>
          <h5 className="movie_year">{year}</h5>
          <ul className="movie_genres">
            {genres.map((genres, index) => (
              <li key={index} className="genres_list">
                {" "}
                {genres}{" "}
              </li>
            ))}
          </ul>
          <p className="movie_summary">{Component(summary)}</p>
        </div>
      </div>
    </Link>
  );
}

 

  • Movie.js에도 Link 를 만들기 위해 상단에 Link 를 import 하고 movie_list 클래스를 Link로 감싼다.
  • state에 영화의 모든 정보를 담는다. movie_list 바깥에 a태그를 추가했기 때문에 레이아웃이 깨졌을 것이다.

Movie.css

.movies_container > a { margin-bottom: 10rem; display: flex; flex-basis: 48%; background: #fff; box-shadow: 4px 4px 14px 0px #cacaca; } 
.movie_list { display: flex; }
  • 각자 맞게 css를 다시 맞추고 routes 폴더에 Detail.js 를 추가한다.
  • 우선 잘 동작하는지 확인하기 위해 컴포넌트를 간단하게 만든다.

 Detail.js

import React from "react";

function Detail(props) {
  console.log(props);
  return <span>테스트</span>
}

export default Detail;

 

  • 마지막으로 App.js 에 detail.js 를 불러온다.
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;

 

  • 이제 영화를 하나 클릭하면 테스트라는 글자가 보이고 콘솔을 확인하면 영화의 정보를 가져온 것을 알 수 있다.
  • 훌륭하다!

 

리다이렉트

  • 우리는 링크를 통해 영화 정보를 라우터로 보내는데 성공했다.
  • 그 말은 즉 영화를 클릭해야 영화 정보가 전송된다. 그렇지 않고 단순히 /movie-detail 를 직접 입력해서 들어오면 아래처럼 state에 undefined가 찍힌다.

  • 우리는 if문으로 state가 undefined일 때 사용자들을 영화 리스트 페이지로 강제 이동시킬 수 있다.
  • 먼저 Detail.js 에 작성한 내용을 클래스 컴포넌트로 바꿔야 한다.
  • 클래스 컴포넌트로 바꾸는 이유는 생명주기(Life Cycle)로 컴포넌트 실행 순서를 조작하기 위함이다.

Detail.js

import React from "react";

class Detail extends React.Component {
  componentDidMount() {
    const { location } = this.props;
    console.log(location.state);
  }
  render() {
    return <span>테스트</span>
  }
}
export default Detail;
  • 다시 /movie-detail 페이지에서 새로고침하면 콘솔에 undefined가 찍힌다.
  • 자 그럼 console.log 대신에 조건문으로 홈 디렉토리로 리다이렉트시키면 끝이다.
  • 페이지를 강제이동 시키는 방법에는 history.push() 가 있다.
const { location, history } = this.props;
console.log(history);
  • componentDidMount에 console.log(history); 를 추가하고 확인해보자.

  • go, goBack, goForward 등 여러가지가 존재하는데 이것들이 url을 변경하는데 사용된다.
  • 그 중에서 가장 많이 사용하는 방법이 push이다.
 componentDidMount() {
  const { location, history } = this.props;
  if (location.state === undefined) {
    history.push("/");
  }
}
  • 조건문을 완성하면 영화를 클릭했을 때, /movie-detail 에 접속할 수 있고 그렇지 않다면 홈으로 리다이렉트된다.
  • 이것으로 리다이렉트는 완성했다.

 

영화 정보 입력하기

  • 이제 조건을 만족하면 영화 정보를 html에 쓰는 작업만 남았다.
  • 지금까지 배운 것을 바탕으로 생각하면 금방 할 수 있을 것이다.
import React from "react";

class Detail extends React.Component {
  componentDidMount() {
    const { location, history } = this.props;
    if (location.state === undefined) {
      history.push("/");
    }
  }
  render() {
    const { location } = this.props;
    return (
    <div className="movie_detail">
      <span>{location.state.year}</span>
      <span>{location.state.title}</span>
      <span>{location.state.summary}</span>
      <span>{location.state.poster}</span>
      <span>{location.state.geners}</span>
    </div>
    )
  }
}
export default Detail;
  • 이제 영화를 클릭하면 영화 정보가 보인다.

  • 하지만 그 상태에서 다시 /movie-detail 로 이동하면 에러가 발생한다.

  • year 즉 영화 정보를 찾을 수 없기 때문에 발생하는 오류이다.
  • render에도 조건문을 추가해야 한다.
render() {
  const { location } = this.props;
  console.log(location.state);
  if (location.state){
    return (
      <div className="movie_detail">
        <span>{location.state.year}</span>
        <span>{location.state.title}</span>
        <span>{location.state.summary}</span>
        <span>{location.state.poster}</span>
        <span>{location.state.geners}</span>
      </div>
      )
  } else {
    return null;
  }
}

 

영화마다 다른 url 이동하기

  • 지금은 영화를 클릭하면 어떤 영화든지 /movie-detail 로 이동한다.
  • 만약 고유한 url을 갖게 하고 싶다면 어렵지 않다. Movie.js 에서 pathname을 수정한다.
function Movie({ id, year, title, summary, poster, genres }) {
  return (
    <Link to={{
      state: {
        pathname: `movie/${title}`,
      }
    }}>
  )
}
  • 나는 /movie/"영화제목" 으로 만들었다.
  • 그리고 App.js 에서 라우터를 수정해준다.
<HashRouter>
  <Route path="/movie/" component={Detail} />
</HashRouter>

  • 각각의 고유한 url이 확인된다.

 

총평

  • 이 수업으로 베일에 감춰진 리액트를 흐릿하게나마 알 수 있었다.
  • 하지만 아직까지는 리액트가 여전히 낯설고 코드도 쉽게 떠오르지 않는다.
  • 리액트 입문은 이것으로 마치고 여름이 찾아올 때 쯤 국내 기초~중급 강의를 들으면서 실무에 대한 감각을 익혀야겠다.
반응형