React Router

 

React 의 라우팅 이해하기

Dynamic 라우팅

Switch 와 NotFound

JSX 링크로 라우팅 이동하기

Redirect

Lead Software Engineer @ProtoPie

Microsoft MVP

TypeScript Korea User Group Organizer

Marktube (Youtube)

Mark Lee

이 웅재

React 의 라우팅 이해하기

react-router-dom

/

/profile

/about

Client (Browser)

Server

Single Page Application

/

/profile

/about

Client (Browser)

SPA 라우팅 과정

  1. 브라우저에서 최초에 '/' 경로로 요청을 하면,

  2. React Web App 을 내려줍니다.

  3. 내려받은 React App 에서 '/' 경로에 맞는 컴포넌트를 보여줍니다.

  4. React App 에서 다른 페이지로 이동하는 동작을 수행하면,

  5. 새로운 경로에 맞는 컴포넌트를 보여줍니다.

npm i react-router-dom
  • cra 에 기본 내장된 패키지가 아닙니다.

  • react-router-dom 은 Facebook 의 공식 패키지는 아닙니다.

  • 가장 대표적인 라우팅 패키지입니다.

특정 경로에서 보여줄 컴포넌트를 준비합니다.

  • '/'  =>  Home 컴포넌트

  • '/profile'  =>  Profile 컴포넌트

  • '/about'  =>  About 컴포넌트

// src/pages/Home.jsx
import React from 'react';

export default function Home() {
  return <div>Home 페이지 입니다.</div>;
}
// src/pages/Profile.jsx
import React from 'react';

export default function Profile() {
  return <div>Profile 페이지 입니다.</div>;
}
// src/pages/About.jsx
import React from 'react';

export default function About() {
  return <div>About 페이지 입니다.</div>;
}

   /

   /profile

   /about

// src/App.js

import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import Home from './pages/Home';
import Profile from './pages/Profile';
import About from './pages/About';

function App() {
  return (
    <BrowserRouter>
      <Route path="/" component={Home} />
      <Route path="/profile" component={Profile} />
      <Route path="/about" component={About} />
    </BrowserRouter>
  );
}

export default App;
- Route 컴포넌트에 경로(path) 와 컴포넌트(component) 를 설정하여 나열해줍니다.

- BrowserRouter 로 Route 들을 감싸줍니다.

- 브라우저에서 요청한 경로에 Route 의 path 가 들어있으면 해당 component 를 보여줍니다.

/

  • /

/profile

  • /
  • /profile

/about

  • /
  • /about
<Route path="/" exact component={Home} />

Dynamic 라우팅

/profile/1

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

function App() {
  return (
    <BrowserRouter>
      <Route path="/" exact component={Home} />
      <Route path="/profile" exact component={Profile} />
      <Route path="/profile/:id" component={Profile} />
      <Route path="/about" component={About} />
    </BrowserRouter>
  );
}

export default App;
<Route path="/profile/:id" component={Profile} />

/profile/1

import React from "react";

export default function Profile(props) {
  const id = props.match.params.id;
  console.log(id, typeof id);
  return (
    <div>
      <h2>Profile 페이지 입니다.</h2>
      {id && <p>id 는 {id} 입니다.</p>}
    </div>
  );
}
props.match.params.id

typeof id => 'string'

/about?name=mark

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

function App() {
  return (
    <BrowserRouter>
      <Route path="/" exact component={Home} />
      <Route path="/profile" exact component={Profile} />
      <Route path="/profile/:id" component={Profile} />
      <Route path="/about" component={About} />
    </BrowserRouter>
  );
}

export default App;
<Route path="/about" component={About} />

/about?name=mark

// src/pages/About.jsx
import React from "react";

export default function About(props) {
  const searchParams = new URLSearchParams(props.location.search);
  const name = searchParams.get("name");
  console.log(name);
  return (
    <div>
      <h2>About 페이지 입니다.</h2>
      {name && <p>name 는 {name} 입니다.</p>}
    </div>
  );
}
new URLSearchParams(props.location.search);

/about?name=mark

// src/pages/About.jsx
import React from "react";
import queryString from "query-string";

export default function About(props) {
  const query = queryString.parse(props.location.search);
  const { name } = query;
  console.log(name);
  return (
    <div>
      <h2>About 페이지 입니다.</h2>
      {name && <p>name 는 {name} 입니다.</p>}
    </div>
  );
}
const query = queryString.parse(props.location.search);

npm i query-string -S

Switch 와 NotFound

Switch

  • 여러 Route 중 순서대로 먼저 맞는 하나만 보여줍니다.

  • exact 를 뺄 수 있는 로직을 만들 수 있습니다.

  • 가장 마지막에 어디 path 에도 맞지 않으면 보여지는 컴포넌트를 설정해서,
    "Not Found" 페이지를 만들 수 있습니다.

Switch

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

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route path="/" component={Home} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;

Switch

import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
import NotFound from "./pages/NotFound";

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route path="/" exact component={Home} />
        <Route component={NotFound} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;

JSX 링크로 라우팅 이동하기

<Link to="/">Home</Link>
<a href="/">Home</a>
  • 앱을 새로고침하면서 경로를 이동합니다.
  • import { Link } from 'react-router-dom';
  • 브라우저의 주소를 바꾸고,
  • 맞는 Route 로 화면을 변경합니다.
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
import NotFound from "./pages/NotFound";
import Links from "./components/Links";

function App() {
  return (
    <BrowserRouter>
      <Links />
      <Switch>
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route path="/" exact component={Home} />
        <Route component={NotFound} />
      </Switch>
    </BrowserRouter>
  );
}

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

function Links() {
  return (
    <ul>
      <li>
        <Link to="/">Home</Link>
      </li>
      <li>
        <Link to="/profile">Profile</Link>
      </li>
      <li>
        <Link to="/profile/1">Profile/1</Link>
      </li>
      <li>
        <Link to="/about">About</Link>
      </li>
      <li>
        <Link to="/about?name=mark">About?name=mark</Link>
      </li>
    </ul>
  );
}

export default Links;
  • import { NavLink } from 'react-router-dom';

  • activeClassName, activeStyle 처럼 active 상태에 대한 스타일 지정이 가능합니다.

  • Route 의 path 처럼 동작하기 때문에 exact 가 있습니다.

import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
import NotFound from "./pages/NotFound";
import Links from "./components/Links";
import NavLinks from "./components/NavLinks";

function App() {
  return (
    <BrowserRouter>
      <Links />
      <Switch>
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route path="/" exact component={Home} />
        <Route component={NotFound} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;
import React from 'react';
import { NavLink } from 'react-router-dom';

const activeStyle = { color: 'green' };

export default function NavLinks() {
  return (
    <ul>
      <li><NavLink exact to="/" activeStyle={activeStyle}>Home</NavLink></li>
      <li><NavLink exact to="/profile" activeStyle={activeStyle}>Profile</NavLink></li>
      <li><NavLink to="/profile/1" activeStyle={activeStyle}>Profile/1</NavLink></li>
      <li>
        <NavLink
          to="/about"
          activeStyle={activeStyle}
          isActive={(match, location) => {
            if (location.pathname !== '/about') return false;
            const searchParams = new URLSearchParams(location.search);
            return !searchParams.has('name');
          }}
        >
          About
        </NavLink>
      </li>
      <li>
        <NavLink
          to="/about?name=mark"
          activeStyle={activeStyle}
          isActive={(match, location) => {
            if (location.pathname !== '/about') return false;
            const searchParams = new URLSearchParams(location.search);
            return searchParams.has('name');
          }}
        >
          About?name=mark
        </NavLink>
      </li>
    </ul>
  );
}

JS 로 라우팅 이동하기

props.history.push("/");

/login

// src/pages/Login.jsx

import React from "react";

export default function Login(props) {
  console.log(props);
  function login() {
    setTimeout(() => {
      props.history.push("/");
    }, 1000);
  }
  return (
    <div>
      <h2>Login 페이지 입니다.</h2>
      <button onClick={login}>로그인하기</button>
    </div>
  );
}

<Route component={컴포넌트} />

  • history
  • location
  • match
// src/components/LoginButton.jsx

import React from "react";

export default function LoginButton(props) {
  console.log(props);
  function login() {
    setTimeout(() => {
      props.history.push("/");
    }, 1000);
  }
  return <button onClick={login}>로그인하기</button>;
}

withRouter()

// src/pages/Login.jsx

import React from "react";
import LoginButton from "../components/LoginButton";

export default function Login(props) {
  return (
    <div>
      <h2>Login 페이지 입니다.</h2>
      <LoginButton />
    </div>
  );
}
import React from "react";
import { withRouter } from "react-router-dom";

function LoginButton(props) {
  console.log(props);
  function login() {
    setTimeout(() => {
      props.history.push("/");
    }, 1000);
  }
  return <button onClick={login}>로그인하기</button>;
}

export default withRouter(LoginButton);

withRouter()

export default withRouter(LoginButton);

Redirect

<Redirect />

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

// jsx
<Redirect to="/" />
import React from "react";
import {
  BrowserRouter,
  Route,
  Switch,
  Redirect
} from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
import Login from "./pages/Login";

const isLogin = true;

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route
          path="/login"
          render={() => (isLogin ? <Redirect to="/" /> : <Login />)}
        />
        <Route path="/" component={Home} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;