티스토리 뷰

front-end/React

리액트 라우터 적용해보기

이안_ian 2019. 12. 13. 00:01




반응형

Route 사용하기

실습할 프로젝트를 생성하고 해당 프로젝트 안에서 react-router-dom을 설치하자.

yarn add react-router-dom

그리고 라우터를 적용할 때는 src/index.js 파일에서 BrowserRouter 컴포넌트로 감싸면 됩니다. 이 컴포넌트는 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로고침하지 않고도 주소를 변경하고, 현재 주소에 관련된 정보를 props로 쉽게 조회하거나 사용할 수 있도록 해 줍니다.

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

serviceWorker.unregister();

 

그리고 각각 다른 경로에 띄울 간단한 컴포넌트를 만들고 App.js에 추가해 봅시다.

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

const App = () => {
  return (
    <div>
      <Route path="/" component={Home} exact={true} />
      <Route path="/about" component={About} />
    </div>
  );
};

export default App;

 

Route라는 컴포넌트를 사용해 사용자의 현재 경로에 따라 다른 컴포넌트를 보여줍니다. exact 옵션은 현재 두 컴포넌트의 경로가 / 라는 것으로 겹치기에 2개의 컴포넌트가 둘다 보입니다. 그래서 경로가 바뀌었을 때 Home은 가리기 위한 옵션입니다.

Link 컴포넌트를 사용하여 다른 주소로 이동하기

일반 웹 앱에서는 a태그를 사용하여 페이지를 전환합니다. 하지만 리액트 라우터를 사용할 때는 이 태그를 직접 사용하면 안됩니다. 이 태그는 페이지를 전환하는 과정에서 페이지를 새로 불러오기 때문에 애플리에키션이 들고 있던 상태들을 모두 날려 버리게 됩니다. 렌더링된 컴포넌트들도 모두 사라지고 다시 처음부터 렌더링하게 되죠.

 

따라서 Link 컴포넌트로 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 상태에서 HTML5 History API를 사용하여 페이지의 주소만 변경해 줍니다. 이 컴포넌트는 a태그로 이루어져 있지만, 페이지 전환을 방지하는 기능이 내장되어있습니다.

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

const App = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">홈</Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
      </ul>
      <hr />
      <Route path="/" component={Home} exact={true} />
      <Route path="/about" component={About} />
    </div>
  );
};

export default App;

 Route 하나에 여러 개의 path 설정

 

<Route path={['/about','/info']} component={About}/>

URL 파라미터와 쿼리

페이지 주소를 정의할 때 가끔은 유동적인 값을 전달해야 할 때도 있습니다. 이는 파라미터와 쿼리로 나눌 수 있습니다.

  • 파라미터 예시: /profiles/velopert
  • 쿼리 예시: /about?details=true

유동적인 값을 사용해야하는 상황에서 파라미터를 사용할지 쿼리를 사용할지 정할 때, 무조건 따라야 하는 규칙은 없습니다. 다만 일반적으로 파라미터는 특정 아이디 혹은 이름을 사용하여 조회할 때 사용하고, 쿼리는 우리가 어떤 키워드를 검색하거나 페이지에 필요한 옵션을 전달할 때 사용합니다.

 

URL 파라미터

Profile 페이지에서 파라미터를 사용해 봅시다. /profile/velopert 같은 형식으로 뒷부분에 유동적인 username값을 넣어줄 때 해당 값을 props로 받아와서 조회하는 방법을 해보겠습니다.

import React from "react";

const data = {
  velopert: {
    name: "싸이",
    description: "강남스타일로 대박친 남자"
  },
  gildong: {
    name: "홍길동",
    description: "고전 소설속 주인공"
  }
};

const Profile = ({ match }) => {
  const { id } = match.params;
  const profile = data[id];
  if (!profile) {
    return <div>존재하지 않는 사용자 입니다.</div>;
  }
  return (
    <div>
      <h3>
        {id}({profile.name})
      </h3>
      <p>{profile.description}</p>
    </div>
  );
};

export default Profile;

 

match 객체안에 params 값을 참조합니다. match 객체 안에는 현재 컴포넌트가 어떤 경로 규칙에 의해 보이는지에 대한 정보가 들어 있습니다. App 컴포넌트에서 Profile 컴포넌트를 위한 라우트를 정의해 보세요. 이번에 사용할 path 규칙은 /profiles/:id 이라고 넣어주면 됩니다. 이렇게 설정하면 match.params.id 값을 통해 현재 id값을 조회할 수 있습니다.

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

const App = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">홈</Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
        <li>
          <Link to="/profile/PSY">PSY 프로필</Link>
        </li>
        <li>
          <Link to="/profile/gildong">gildong 프로필</Link>
        </li>
      </ul>
      <hr />
      <Route path="/" component={Home} exact={true} />
      <Route path="/about" component={About} />
      {/* <Route path={["/about", "/info"]} component={About} /> */}
      <Route path="/profile/:id" component={Profile} />
    </div>
  );
};

export default App;

URL 쿼리

이번에는 About 페이지에서 쿼리를 받아오겠습니다. 쿼리는 location 객체에 들어 있는 search값에서 조회할 수 있습니다. location 객체는 라우트로 사용된 컴포넌트에게 props로 전달되며, 웹 앱의 현재 주소에 대한 정보를 지니고 있습니다.

{
  "pathname" : "/about",
  "search" : "?detail=true",
  "hash" : ""
}

위 location 객체는 http://localhost:3000/about?detail=true 주소로 들어갔을 때의 값입니다. URL 쿼리를 읽을 때는 위 객체가 지닌 값중 search 값을 확인해야 합니다. 이 값은 문자열 형태로 되어 있습니다. URL 쿼리는 ?detail=true&another=1 과 같이 문자열에 여러 가지 값을 설정해 줄 수 있습니다. search 값에서 특정값을 읽어 오기 위해서는 이 문자열을 객체 형태로 변환해 주어야 합니다.

 

쿼리 문자열을 객체로 변환할 때는 qs라는 라이브러리를 사용합니다. yarn을 사용하여 해당 라이브러리를 설치하세요.

yarn add qs
import React from "react";
import qs from "qs";

const About = ({ location }) => {
  const query = qs.parse(location.search, {
    ignoreQueryPrefix: true // 이 설정을 통해 문자열 맨 앞의 ?를 생략합니다.
  });
  const showDetail = query.detail === "true"; //쿼리의 파싱 결과 값은 문자열입니다.
  return (
    <div>
      <h1>소개</h1>
      <p>이 프로젝트는 리액트 라우터 기초를 실습해 보는 예제 프로젝트입니다.</p>
      {showDetail && <p>detail 값을 true로 설정하셨군요!</p>}
    </div>
  );
};

export default About;

객체로 파싱하는 과정에 결과 값은 항상 문자열이라는 점을 주의하세요. 그렇기에 숫자를 받아와야 한다면 parseInt 함수를 통해 꼭 숫자로 변환해 주고, 지금처럼 논리 자료형 값을 사용해야 하는 경우에는 정확히 "true" 문자열과 일치하는지 비교해 주세요. 이제 브라우저에 http://localhost:3000/about?detail=true 으로 접속해보세요!

서브 라우트

서브 라우트는 라우트 내부에 또 라우트를 정의하는 것을 의미합니다. 이 작업은 그렇게 복잡하지 않습니다. 그냥 라우트로 사용되고 있는 컴포넌트의 내부에 Route컴포넌트를 또 사용하면 됩니다. 기존 App 컴포넌트에서는 두 종류의 프로필 링크를 보여주었는데, 이를 잘라내서 프로필 링크를 보여주는 Profiles 라우트 컴포넌트를 따로 만들고, 그 안에서 Profile 컴포넌트를 서브 라우트로 사용해 봅시다.

import React from "react";
import { Link, Route } from "react-router-dom";
import Profile from "./Profile";

const Profiles = () => {
  return (
    <div>
      <h3>사용자 목록:</h3>
      <ul>
        <li>
          <Link to="/profiles/PSY">PSY</Link>
        </li>
        <li>
          <Link to="/profiles/gildong">gildong</Link>
        </li>
      </ul>

      <Route
        path="/profiles"
        exact
        render={() => <div>사용자를 선택해주세요</div>}
      />
      <Route path="/profiles/:id" component={Profile} />
    </div>
  );
};

export default Profiles;
import React from "react";
import { Route, Link } from "react-router-dom";
import About from "./About";
import Home from "./Home";
import Profiles from "./Profiles";

const App = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">홈</Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
        <li>
          <Link to="/profiles">프로필</Link>
        </li>
      </ul>
      <hr />
      <Route path="/" component={Home} exact={true} />
      <Route path="/about" component={About} />
      {/* <Route path={["/about", "/info"]} component={About} /> */}
      <Route path="/profiles" component={Profiles} />
    </div>
  );
};

export default App;

리액트 라우터 부가 기능

history

history 객체는 라우트로 사용된 컴포넌트에 match, location과 함께 전달되는 props중 하나로, 이 객체를 통해 컴포넌트 내에 구현하는 메서드에서 라우터 API를 호출할 수 있습니다. 예를 들어 특정 버튼을 눌렀을 때 뒤로 가거나, 로그인 후 화면을 전환하거나, 다른 페이지로 이탈하는 것을 방지해야 할 때 history를 이용합니다.

import React, { Component } from "react";

class HistorySample extends Component {
  handleGoBack = () => {
    this.props.history.goBack();
  };
  handleGoHome = () => {
    this.props.history.push("/");
  };
  componentDidMount() {
    //이것을 설정하고 나면 페이지에 변화가 생기려고 할 때마다 정말 나갈 것인지를 질문함
    this.unblock = this.props.history.block("정말 나가실 건가요?");
  }
  componentWillUnmount() {
    //컴포넌트가 언마운트되면 질문은 멈춤
    if (this.unblock) {
      this.unblock();
    }
  }
  render() {
    return (
      <div>
        <button onClick={this.handleGoBack}>뒤로</button>
        <button onClick={this.handleGoHome}>홈으로</button>
      </div>
    );
  }
}

export default HistorySample;

App.js에가서 HistorySample의 Route를 설정해보자.

 

withRouter

이 함수는 HoC(Higher-order Component)입니다. 라우트로 사용된 컴포넌트가 아니어도 match, location, history 객체를 접근할 수 있게 해줍니다.

import React from "react";
import { withRouter } from "react-router-dom";

const WithRouterSample = ({ location, match, history }) => {
  return (
    <div>
      <h4>location</h4>
      <textarea
        value={JSON.stringify(location, null, 2)}
        rows={7}
        readOnly={true}
      />
      <h4>match</h4>
      <textarea
        value={JSON.stringify(match, null, 2)} //null, 2 옵션을 줄 경우 들여쓰기가 됨
        rows={7}
        readOnly={true}
      />
      <button onClick={() => history.push("/")}>홈으로</button>
    </div>
  );
};

export default withRouter(WithRouterSample); //이렇게 감싸서 내보냄

이제 Profiles에 렌더링 해 봅시다

import React from "react";
import { Link, Route } from "react-router-dom";
import Profile from "./Profile";
import WithRouterSample from "./WithRouterSample";

const Profiles = () => {
  return (
    <div>
    (...)
      <WithRouterSample />
    </div>
  );
};

export default Profiles;

그런데 여기서 match 객체를 보면 params가 비어있습니다. withRouter를 사용하면 현재 자신을 보여주고 있는 라우트 컴포넌트 기준으로 match가 전달됩니다. Profiles를 위한 라우트를 설정할 때는 path="/profiles"라고만 입력했으므로 id파라미터를 읽어 오지 못하는 상태입니다. WithRouterSample컴포넌트를 Profiles에서 지우고 Profile에 넣으면 match쪽에 URL파라미터가 잘 보일 겁니다.

import React from "react";
import { withRouter } from "react-router-dom";
import WithRouterSample from "./WithRouterSample";

(...)

<p>{profile.description}</p>
      <WithRouterSample />
    </div>
  );
};

export default withRouter(Profile);

Switch

스위치 컴포넌트는 여러 Router를 감싸서 그 중 일치하는 단 하나의 라우트만을 렌더링 시켜줍니다. 모든 규칙이 존재하지 않을 때 보여줄 Not Found 페이지도 구현이 가능합니다. 그냥 우리가 흔히 알고 있는 switch문과 유사하다.

import React from "react";
import { Route, Link, Switch } from "react-router-dom";

(...)

 <Switch>
        <Route path="/" component={Home} exact={true} />
        <Route path="/about" component={About} />
        {/* <Route path={["/about", "/info"]} component={About} /> */}
        <Route path="/profiles" component={Profiles} />
        <Route path="/history" component={HistorySample} />
        <Route
          render={({ location }) => (
            <div>
              <h2>이 페이지는 존재하지 않습니다:</h2>
              <p>{location.pathname}</p>
            </div>
          )}
        />
      </Switch>
    </div>
  );
};

export default App;

이제 존재하지 않는 페이지인 localhost:3000/now로 들어가 보세요.

 

 

 

 

 

 

출처: 리액트를 다루는 기술

반응형

'front-end > React' 카테고리의 다른 글

리액트 서버 사이드 렌더링의 이해  (0) 2019.12.31
리액트 Context API  (0) 2019.12.15
리액트 SPA 요점  (0) 2019.12.11
리액트 Hook #2  (0) 2019.12.08
리액트 Hooks  (0) 2019.12.07
댓글
반응형
최근에 달린 댓글
글 보관함
Total
Today
Yesterday
최근에 올라온 글
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30