본문 바로가기

프로그래밍/React

리액트(React)/라우터(Router) - Router 기초

리액트에서 라우터는 SPA를 사용할 때 사용됩니다. 리액트를 이용해서 만든 라이브러리 React Router를 알아봅니다.

 

SPA란?

SPA는 Single Page Application의 약자입니다. 웹페이지가 여러 개로 이루어진 것이 아닌 하나의 페이지에서 모든 정보를 나타냅니다. 

 

기본적인 웹은 여러 개의 페이지를 따로 만들어 서버에 해당하는 페이지를 요청하여 페이지를 나타냅니다. 하지만 SPA는 하나의 페이지에서 필요한 정보들만 서버에 요청하여 페이지를 나타냅니다. 

 

기존의 여러 개의 페이지로 이루어져 있을 경우 새로운 페이지를 요청할 때 바뀌지 않아도 되는 부분도 모두 새로 로드하기 때문에 효율적이지 않습니다. 반면 SPA로 만든다면 필요한 부분만 다시 자바스크립트로 변경하기 때문에 새로 로드하지 않고 효율적으로 나타낼 수 있습니다. 

 

리액트 라우터는 리액트를 이용해서 만들어진 라이브러리입니다. 리액트가 만든 공식 라이브러리가 아니지만 많이 알려진 라우터 라이브러리입니다.

 

해당 글에서는 최신 업데이트 버전인 v6이 아닌 v5로 작성합니다. (v6은 추후 공부)

 

URL 파라미터, URL 쿼리

URL 파라미터 - /menu/bread1
URL 쿼리 - /menu?option=1

위와 같은 형식으로 이름 같은 것으로 URL 파라미터는 데이터를 조회할 때 사용하고 URL 쿼리는 페이지에 필요한 옵션을 넘기거나 서버에 요청할 때 쓰입니다.

 

REST API를 사용할때 사용하는 URL 규칙입니다.

 

URL 파라미터는 다른 말로 Path Variable URL 쿼리는 Query String이라고 부르는 것 같습니다.

설치

리액트 라우터 v5를 설치하려면 버전은 명시해야 합니다.

npm install react-router-dom@5

이렇게 하면 v5의 마지막 버전을 다운로드합니다.

 

라우터 적용

라우터에는 5가지 라우터가 있습니다.

  • BrowserRouter - History API를 사용하여 UI와 URL을 동기화합니다.
  • HashRouter - URL의 해시(#) 부분을 이용하여 UI와 URL을 동기화합니다.
  • MemoryRouter - URL의 기록을 메모리에 보관하는 라우터입니다.
  • NativeRouter - React Native를 이용한 라우터입니다.
  • StaticRouter - 위치가 변경되지 않는 라우터입니다.

자세한 라우터 설명은 공식 문서를 참고하시기 바랍니다.

 

이 글에서는 BrowserRouter만 다룹니다.

 

라우터를 사용하려면 BrowserRouter 컴포넌트로 감싸야합니다. 

//index.js
<BrowserRouter>
    <App />
</BrowserRouter>

BrowserRouter 컴포넌트는 HTML5의 History API를 사용하며 기본적인 기능을 쉽게 사용할 수 있도록 해주며 정보들을 확인할 수 있게 해 줍니다. URL과 UI를 동기화시켜줍니다.

 

BrowserRouter 컴포넌트의 속성은 4가지가 있습니다.

  • basename - 기본 URL를 지정합니다. 최상위 디렉터리입니다. 
  • getUserConfirmation - <Prompt> 컴포넌트를 사용하면 발생되며 설정한 함수가 발생됩니다. 사용자가 위치를 벗어날 때 모달이나 confirm창을 발생시킬 때 사용됩니다.
  • forceRefresh - 속성 값이 true이면 페이지를 렌더링 할 때 전체를 새로고침 합니다.
  • keyLength - location.key의 개수를 설정합니다. 기본은 6입니다.

Route

화면을 렌더링 하려면 Route 컴포넌트를 이용해야 합니다. Route 컴포넌트는 지정한 URL과 일치하면 렌더링 합니다.

 

Route에서 렌더링 하는 방법은 component, render, children 속성을 사용하여 렌더링 할 수 있습니다.

 

component 방식 - 따로 만들어둔 컴포넌트를 지정하여 렌더링 합니다. component 방식은 재사용되지 않고 React.createElement를 사용해 새로운 ReactElement가 생성되며 사용이 끝나면 언마운트가 됩니다.

//App.js
import React from "react";
import { Route } from "react-router-dom";

const Home = ({location}) => {
    return <div>홈 - {location.pathname}</div>;
}

const App = () => {
    return (
        <div>
            <Route path="/home" component={Home} />
        </div>
    );
}

export default App;

만들어둔 컴포넌트에 match, location, history 객체를 받아 필요한 것들을 사용할 수 있습니다.

 

render 방식 - path와 일치하다면 render에 전달한 함수가 호출됩니다. component 방식과 다르게 Element를 생성하지 않습니다. 인라인 렌더링 또는 함수를 사용할 때 사용됩니다. component 방식보다 render를 이용하는 게 더 효율적!

component 방식과 마찬가지로 match, location, history 객체를 사용할 수 있습니다.

//App.js
import React from "react";
import { Route } from "react-router-dom";

const App = () => {
    return (
        <div>
            <Route path="/home" render={({ location }) => <div>홈 - {location.pathname}</div> } />
        </div>
    );
}

export default App;

children 방식 - path가 불일치해도 모두 렌더링 하며 지정한 함수가 호출될 때 조건을 걸어 원하는 렌더링을 합니다.

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

const App = () => {
    return (
        <div>
            <Route exact path="/home1" children={() => <div>홈</div>} />
            <Route exact path="/home2" children={() => <div>홈</div>} />
            <Route
                exact
                path="/home3"
                children={({ match }) => match && <div>홈</div>}
            />
        </div>
    );
};

export default App;

어느 URL에서도 렌더링이 되며 match를 이용해 path와 일치하는지 확인하여 렌더링 해야 합니다. 어디에 써야 할지 모르겠네요??

 

Route 컴포넌트의 path 속성

path 속성은 URL 또는 URL 배열입니다. 렌더링 하는 컴포넌트가 같다면 배열을 이용해서 path를 지정할 수 있습니다.

<Route path={["/user","/profile"]} render={() => <profile />} />

또한 path에 /:변수를 입력하면 URL에 따라 변수에 값이 들어갑니다.

//App.js
import React from "react";
import { Route } from "react-router-dom";

const Test = ({match}) => {
    return (
        <div>
            {match.params.color}
        </div>
    )
}

const App = () => {
    return (
        <div>
            <Route path="/test/:color" render={({match}) => <Test match={match}/>} />
        </div>
    );
};

export default App;

위 코드에서 주소를 /test/blue를 입력하면 match.params 객체에 지정한 변수 color에 URL에 입력한 blue가 들어가 있습니다.

 

이것을 이용하면 같은 컴포넌트에 여러 가지 데이터중 하나를 가져와 표현할 때 효율적입니다.

 

exact 속성

exact가 true이면 주소 경로가 path와 일치해야 렌더링이 됩니다. 기본값을 지정하지 않은 상태로 exact만 사용하면 true입니다.

 

Link 컴포넌트

클릭하여 다른 주소로 이동시키는 컴포넌트입니다. Route에서 a태그를 이용하면 페이지를 다시 로드하기 때문에 a태그가 아닌 Link 컴포넌트를 사용하여 이동을 시켜야 합니다. Link의 To 속성은 문자열, 객체, 함수가 될 수 있습니다. 

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

const Menu = () => {
    return (
        <div>
            <ul>
                <li>빵</li>
                <li>음료수</li>
            </ul>
        </div>
    );
}

const App = () => {
    return (
        <div>
            <ul>
                <li>
                    <Link to="/home">홈</Link>
                </li>
                <li>
                    <Link to="/menu">메뉴</Link>
                </li>
            </ul>
            <Route path="/home" render={() => <div>홈</div>} />
            <Route path="/menu" render={() => <Menu />} />
        </div>
    );
};

export default App;

NavLink 컴포넌트

NavLink 컴포넌트는 Link 컴포넌트와 똑같지만 현재 URL과 일치하면 스타일을 지정할 수 있는 컴포넌트입니다.

className , activeClassName, style 속성을 가지고 있습니다.

 

className 속성은 문자열 또는 함수를 사용할 수 있으며 함수를 사용하면 링크의 활성 상태를 인자로 전달되어 상태를 확인하여 스타일을 지정할 수 있습니다.

 

activeClassName 속성은 링크가 활성화 상태인 경우 스타일을 적용합니다.

 

style 속성은 객체 또는 함수를 가집니다. 스타일 객체를 지정할 수 있고 함수를 사용하면 활성 상태가 인자로 전달됩니다.

 

서브 라우트

서브 라우트는 중첩 라우트입니다. 라우트 내부에 또 다른 라우트를 사용하는 것입니다. 

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

const Menu = () => {
    return (
        <div>
            <ul>
                <li>
                    <Link to="/menu/bread">빵</Link>
                </li>
                <li>
                    <Link to="/menu/drink">음료수</Link>
                </li>
            </ul>
            <Route path="/menu" exact render={() => <div>골라주세요!</div>} />
            <Route path="/menu/bread" render={() => <div>소보로</div>} />
            <Route path="/menu/drink" render={() => <div>우유</div>} />
        </div>
    );
}

const App = () => {
    return (
        <div>
            <ul>
                <li>
                    <Link to="/home">홈</Link>
                </li>
                <li>
                    <Link to="/menu">메뉴</Link>
                </li>
            </ul>
            <Route path="/home" render={() => <div>홈</div>} />
            <Route path="/menu" render={() => <Menu />} />
        </div>
    );
};

export default App;

 

History, Location, Match 객체

라우트에서 컴포넌트에 전달해주는 3가지 객체입니다. 

 

History 객체

히스토리 객체는 프로퍼티와 메서드를 사용할 수 있습니다. 뒤로 가기, 앞으로 등

 

Location 객체

로케이션 객체는 현재 URL, URL 쿼리 스트링 같은 현재 정보를 가지고 있는 객체입니다.

 

Match 객체

매치 객체는 라우트 속성의 path와 일치할 때 만들어지는 정보를 가지고 있습니다. path와 일치하지 않는다면 null을 가지고 있습니다.

 

 

withRouter 함수

withRouter는 HoC(고차 컴포넌트)입니다. Route를 꼭 사용하지 않아도 withRouter를 사용하면 History, Location, Match 객체를 사용할 수 있습니다.

//App.js
import React from "react";
import { Route } from "react-router-dom";
import Test2 from "./Test2";

const App = () => {
    return (
        <div>
            <Test2 />
        </div>
    );
};

export default App;

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

const Test2 = ({ history, location, match }) => {
    return (
        <div>
            histroy: {JSON.stringify(history)}
            <br />
            location: {JSON.stringify(location)}
            <br />
            match: {JSON.stringify(match)}
        </div>
    );
}

export default withRouter(Test2);

 

Switch 컴포넌트

라우트를 쓸 때 path와 일치하는 경우 모두 렌더링 합니다. 하지만 switch는 현재 경로와 일치하는 단 하나만 렌더링이 진행됩니다.

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

const Home = () => {
    return <div>홈</div>
}

const Menu = () => {
    return <div>메뉴입니다.</div>;
};

const Info = () => {
    return <div>정보입니다.</div>;
};

const App = () => {
    return (
        <div>
            <Switch>
                <Route exact path="/" component={Home} />
                <Route path="/home" component={Home} />
                <Route path="/menu" component={Menu} />
                <Route path="/info" component={Info} />
                <Route render={({ location }) => (
                    <div>
                        <b>페이지가 없어요! - {location.pathname}</b>
                    </div>
                )} />
            </Switch>
        </div>
    );
};

export default App;

Route에 path에 없는 주소로 이동한다면 마지막 Route에서 페이지가 없다고 나타납니다. 

 

참고

리액트를 다루는 기술(개정판) - 김민준 지음

https://v5.reactrouter.com/web/api/Switch

 

Declarative routing for React apps at any scale | React Router

Version 6 of React Router is here! React Router v6 takes the best features from v3, v5, and its sister project, Reach Router, in our smallest and most powerful package yet.

reactrouter.com