[React]react-router-dom을 이용한 라우팅해보기

UkiUkhui·2021년 10월 23일
0

React 공부중

목록 보기
17/25

SPA

  • single page application : 한 개의 페이지로 이루어진 애플리케이션
  • 뷰 렌더링 : 사용자 브라우저에서 담당
  • 애플리케이션을 브라우저에 불러와서 실행시킨 후, 사용자와의 인터랙션 발생 시 필요한 부분만 자바스크립트 이용하여 업데이트해줌
  • 새로운 데이터 필요 시 서버 API 호출하여 필요한 데이터만 새로 불러옴
  • 사용자에게 제공하는 페이지는 한 종류이나, 해당 페이지에서 로딩된 자바스크립트와 현재 사용자 브라우저 주소 상태에 따라 다양한 화면을 보여줄 수 있음
    -ex: 블로그 홈, 포스트 목록, 포스트, 글쓰기 등 화면

SPA 단점

  • 앱 규모가 커지면 자바스크립트 파일이 너무 커짐
  • 페이지 로딩 시 사용자가 실제로 방문하지 않는 페이지의 스크립트도 불러오기 때문에 > 코드 스플리팅(code splitting) 사용으로 해결 가능
  • 브라우저에서 자바스크립트를 사용하여 라우팅을 관리하는 것 : 일반 크롤러에서는 페이지의 정보를 제대로 수집하지 못함 > 서버 사이드 렌더링을 통해 해결 가능

전통적인 웹 페이지

  • 사용자가 다른 페이지로 이동할 때마다 새로운 html을 받아왔음
  • 페이지 로딩 시 서버에서 리소스를 전달받아 해석한 뒤 화면에 보여줌

    사용자에게 보이는 화면은 서버에서 준비해줌

  • 서버측 부하 발생

라우팅

  • 다른 주소에 다른 화면을 보여주는 것
  • 리액트 라이브러리에는 자체적 기능이 없음
  • 리액트 라우팅 라이브러리 : 리액트 라우터, 리치 라우터, next.js
  • 리액트 라우터 : 클라이언트 사이드에서 이루어지는 라우팅을 간단하게 구현할 수 있도록 함

React-router-dom을 이용한 프로젝트 만들기

  • BrowserRouter : HTML5의 History API를 사용하여 페이지를 새로고침하지 않고도 주소를 변경하고, 현재 주소에 관련된 정보를 props로 쉽게 조회하거나 사용

index.js

import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";

import App from "./App";

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

1. Route 컴포넌트 사용

<Router path="주소 규칙" component={보여줄 컴포넌트} />

  • 컴포넌트를 사용하여 사용자의 현재 경로에 따라 다른 컴포넌트를 보여줌
  • 어떤 규칙을 가진 경로에 어떤 컴포넌트를 보여줄 지 정의 가능

App.js

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

export default function App() {
  return (
    <div>
      <Route path="/" component={Home} />
      <Route path="/about" component={About} />
    </div>
  );
}

/about으로 들어가면 home, about화면 모두 나옴

  • exact = {true} 로 설정하면 경로가 완전히 똑같을 때만 해당 컴포넌트를 보내줌

App.js

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

export default function App() {
  return (
    <div>
      <Route path="/" component={Home} exact={true}/>
      <Route path="/about" component={About} />
    </div>
  );
}
  • 클릭하면 다른 주소로 이동시켜주는 컴포넌트임
  • 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 상태에서 HTML5 History API를 사용하여 페이지의 주소만 변경

< Link to="주소">내용< /Link>

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

export default function App() {
  return (
    <div>
      <Link to="/">home</Link>
      <br />
      <Link to="/about">about</Link>
      <hr />
      <Route path="/" component={Home} exact={true} />
      <Route path="/about" component={About} />
    </div>
  );
}

3. URL 파라미터와 쿼리

파라미터 : /profiles/jjaa9292
쿼리 : /about?details=true

  • 파라미터 : 특정 아이디/이름을 사용하여 조회할 경우 사용
  • 쿼리 : 키워드 검색, 페이지에 필요한 옵션 전달 시 사용

3.1. URL 파라미터

profile.js

import React from "react";

const data = {
  a : {
    name: 'kim',
    description : '개발자'
  },
  b : {
    name : 'hong',
    description : '디자이너'
  }
}

const Profile = ({match}) => {
  const {username} = match.params;
  const profile = data[username];
  if(!profile){
    return <div>"존재하지 않음"</div>
  }
  return(
    <div>
      <h3>{username}({profile.name})</h3>
      <p>{profile.description}</p>
    </div>
  )
}

export default Profile;
  • match : 현재 컴포넌트가 어떤 규칙에 의해 보이는지에 대한 정보가 있음
  • params : 파라미터를 받아올 때 이 값을 참조함.
import "./styles.css";
import React from "react";
import { Link, Route } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profile from "./Profile";

export default function App() {
  return (
    <div>
      <Link to="/">home</Link>
      <br />
      <Link to="/about">about</Link>
      <hr />
      <Route path="/" component={Home} exact={true} />
      <Route path="/about" component={About} />
      <Route path="/profile/:username" component={Profile} />
    </div>
  );
}
  • :username 이 파라미터가 되어 match props를 통해 전달받을 수 있음.

3.2. URL 쿼리

  • location 객체에 있는 search를 통해서 쿼리를 받아올 수 있음
  • location 객체 : 라우트로 사용된 컴포넌트에게 props로 전달되며, 웹 애플리케이션의 현재 주소에 대한 정보를 가지고 있음.

{
“pathname”: “/about”,
“search”: “?detail=true”,
“hash”: “”
}
/about?detail=true 이런 경로로 들어가면 됨

  • search : 문자열 형태이므로 값을 읽어오기 위해서는 문자열로 형변환을 해줘야 함
  • qs : 쿼리 문자열을 객체로 변환할 때 사용하는 라이브러리

about.js

import qs from "qs";
import React from "react";

const About = ({location}) => {
  const query = qs.parse(location.search, {
    ignoreQueryPrefix:true
  })
  const showDetail = query.detail==='true';

  return (
    <div>
      <h1>About</h1>
      <p>소개 페이지</p>
      {showDetail&&<p>detail값이 true</p>}
    </div>
  );
};

export default About;
  • query에서 받아오는 값은 논리값, 숫자 할 것 없이 모두 문자열로 받아오기 때문에 형변환이 필요하다면 무조건 해줘야함
  • ignoreQueryPrefix:true을 통해 문자열 맨 앞의 ?를 생략함

4. 서브라우트 만들기

  • 라우트 내부에 또 라우트를 정의

Profiles.js

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/a">a</Link>
        </li>
        <li>
          <Link to="/profiles/b">b</Link>
        </li>
      </ul>

      <Route
        path="/profiles"
        exact
        render={() => <div>유저를 선택해주세요.</div>}
      />
      <Route path="/profiles/:username" component={Profile} />
    </div>
  );
};

export default Profiles;
  • 새로운 Profiles.js를 만들어서 각 유저들의 프로필 링크와 라우트를 렌더링함.

App.js

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

export default function App() {
  return (
    <div>
      <Link to="/">home</Link>
      <br />
      <Link to="/about">about</Link>
      <br />
      <Link to="/profiles">프로필</Link>
      <hr />

      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/profiles" component={Profiles} />
    </div>
  );
}

5. 리액트 라우터 부가 기능

5.1. history

  • match, location과 같이 라우트로 사용된 컴포넌트에게 같이 넘어오는 props 중 하나
  • 컴포넌트 내에 구현하는 메서드에서 라우트로 직접 접근 가능

HistorySample.js

import React, { useEffect } from "react";

const HistorySample = ({ history }) => {
  const goBack = () => {
    history.goBack();
  };

  const goHome = () => {
    history.push("/");
  };

  useEffect(() => {
    console.log(history);
    const unblock = history.block("정말 떠나실건가요?");
    return () => {
      unblock();
    };
  }, [history]);

  return (
    <div>
      <button onClick={goBack}>뒤로가기</button>
      <button onClick={goHome}>홈으로</button>
    </div>
  );
};

export default HistorySample;

App.js

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

const App = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/"></Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
        <li>
          <Link to="/profiles">프로필 목록</Link>
        </li>
        <li>
          <Link to="/history">예제</Link>
        </li>
      </ul>
      <hr />
      <Route path="/" exact={true} component={Home} />
      <Route path="/about" component={About} />
      <Route path="/profiles" component={Profiles} />
      <Route path="/history" component={HistorySample} />
    </div>
  );
};

export default App;

5.2. withRouter HoC

  • HoC : Higher-order Component
  • 라우트가 아닌 컴포넌트에서도 match, location, history객체 접근.

WithRouterSample.js

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

const WithRouterSample = ({match, history, location}) => {
  return(
    <div>
      <h4>location</h4>
      <textarea value={JSON.stringify(location, null, 2)} readOnly />
      <h4>match</h4>
      <textarea value={JSON.stringify(match, null, 2)} readOnly />
      <button onClick={() => history.push('/')}>홈으로</button>
    </div>
  )
}

export default withRouter(WithRouterSample);
  • withRouter 사용 시 컴포넌트를 함수로 감싸줘야함
  • JSON.stringify의 두 번째 파라미터와 세 번째 파라미터를 위와 같이 null, 2로 설정해 주면 JSON에 들여쓰기가 적용된 상태로 문자열이 만들어짐

Profiles.js

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

const Profiles = () => {
  return (
    <div>
      <h3>유저 목록:</h3>
      <ul>
        <li>
          <Link to="/profiles/a">a</Link>
        </li>
        <li>
          <Link to="/profiles/b">b</Link>
        </li>
      </ul>

      <Route
        path="/profiles"
        exact
        render={() => <div>유저를 선택해주세요.</div>}
      />
      <Route path="/profiles/:username" component={Profile} />
      <WithRouterSample />
    </div>
  );
};

export default Profiles;
  • match params : withRouter는 현재 자신을 보여주고 있는 라우트 컴포넌트(Profiles)를 기준으로 match 객체를 보여주므로 비어있음
  • 실질적인 username이 존재하는 Profile.js에서 withRouter 입력해야함.

Profile.js

import React from "react";
import { withRouter } from "react-router-dom";
import WithRouterSample from "./WithRouterSample";
const data = {
  a: {
    name: "kim",
    description: "개발자"
  },
  b: {
    name: "hong",
    description: "디자이너"
  }
};

const Profile = ({ match }) => {
  const { username } = match.params;
  const profile = data[username];
  if (!profile) {
    return <div>"존재하지 않음"</div>;
  }
  return (
    <div>
      <h3>
        {username}({profile.name})
      </h3>
      <p>{profile.description}</p>
      <WithRouterSample />
    </div>
  );
};

export default withRouter(Profile);

5.3.switch

  • 여러 라우트를 감싸서 그중 일치하는 단 하나의 라우트만 렌더링함
  • Not Found도 구현 가능
import React from "react";
import { Link, Route, Switch } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
import HistorySample from "./HistroySample";

const App = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/"></Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
        <li>
          <Link to="/profiles">프로필 목록</Link>
        </li>
        <li>
          <Link to="/history">예제</Link>
        </li>
      </ul>
      <hr />
      <Switch>
        <Route path="/" exact={true} component={Home} />
        <Route path="/about" 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;
profile
hello world!

0개의 댓글

관련 채용 정보