SPA(SinglePageApplication) 프레임워크 중 하나인 React로 개발을 진행하려다 보니 문뜩 궁금증이 하나듭니다. React는 하나의 화면에서 변화를 감지할때마다 VirtualDOM에 업데이트를 하고 변화가 일어난 부분만 Rendering을 하는데, "그럼 모든 페이지는 URI구분없이 같은건가?" 라는 질문이 생겼습니다. 하지만, 우리가 평소에 자주 사용하는 포털사이트만 보더라도 모든 페이지는 고유의 URI를 가지고 있고, 이를 다른사람들과 공유할때 해당 주소를 보내고 구분도 합니다. 그럼 React로 만들어진 web Application은 라우팅 서비스를 지원하지 않을까요? 공식적으로는 지원하지않고 있습니다. 그래서 이번 글을 통해 가장 사용을 많이하는 Third-party 라이브러리인 REACT-ROUTER를 사용하여 React에서 라우팅(Routing)과 각 화면의 고유한 URI를 할당해보고, 라이브러리의 여러 컴포넌트 샘플을 만들어보도록 합시다.

목차

  • 환경 구성
  • Link
  • Route
  • Redirect
  • Router
  • Switch

준비물

  1. node.js
  2. npm(node.js 설치중 기본옵션 선택시 함께 설치됨)
  3. yarn
  4. CRA(create-react-app)
  5. 기타 모듈-Library(react-router)
  6. VS Code (VisualStudioCode)
  7. React를 다룬 경험  (React 학습 Part1 : https://www.nextree.io/react-component-lifecycle/)

설치

  1. node.js - https://nodejs.org/ko/ Node.js 공식 홈페이지에서 LTS 버전으로 자신의 컴퓨터의 스펙에 맞춰 설치 합니다.
  2. node.js를 기본 옵션으로 설치를 마쳤다면, 기본적으로 npm을 사용할 수 있습니다. (npm 명령어는 https://docs.npmjs.com/ 참조 바랍니다.)
  3. yarn을 설치 하기위해 "npm -g install yarn" 라고 입력합니다.(Window - PowerShell, Command | MacOS - terminal)
  4. yarn을 통해 CRA(create-react-app)을 설치합니다. "yarn global add create-create-app"
  5. REACT-ROUTER 를 설치합니다."yarn global add react-router"
  6. 개발툴인 VSCode를 설치합니다. https://code.visualstudio.com/Download

프로젝트 생성

샘플 프로젝트를 CRA를 통해 생성합니다. 우선 Command나 Terminal에서 프로젝트를 위치로 이동합니다.

저의 경우 [내컴퓨터] - [문서] 아래에 프로젝트 생성을 진행해보겠습니다.

<프로젝트 위치로 이동>

cd C:\Users\Nextree\Documents

<MacOS>

 create-react-app router-master

<Windows>

npx create-react-app router-master

VSCode를 열고 [File] - [Open Folder..] 로 생성된 프로젝트 위치를 선택합니다.

src 아래 index.js 와 index.css 를 제외한 모든 파일을 삭제 합니다.

우선 index.js를 아래와 같이 작성합니다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css'

ReactDOM.render(< />, document.getElementById('root'));`

이제 샘플을 작성할 빈껍데기(?)가 준비 되었습니다. 이제 시작해보죠!

샘플을 작성하기전 index.js와 같은 디렉토리 레벨로 SampleRouter.js 파일을 생성합니다.

import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

class SampleRouter extends React.Component {
    //
    render(){
        return(
            <div>
                <Link to='/link1' >Link1</Link>
                <Link to='/link2' >Link2</Link>
            </div>
        );
    }
}

export default SampleRouter;

작성을 완료하면, index.js 로 돌아가서 아래와 같이 작성해줍니다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import SampleRouter from './SampleRouter';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
    <BrowserRouter>
        <SampleRouter/>
    </BrowserRouter>
,
 document.getElementById('root'));

index.js 수정도 끝났으면, 우선 서버를 실행을 해봅시다.

VSCode 상단 메뉴바에서 [Terminal] - [New Terminal] 로 터미널 열어주시고, 아래와 같이 입력합니다.

yarn start

입력 후 조금 기다리면 브라우저에 방금 작성한 Link가 2개가 보일것입니다. 하나씩 눌러보죠. 각 링크를 누르면 주소표시창에 http://localhost:3000/link1http://localhost:3000/link2 가 보일겁니다.

자이제 링크를 걸었으니 각 링크에 해당하는 컴포넌트로 라우팅을 해주도록 하겠습니다.

Route

우선 서버는 중지하지않습니다. 코드를 변경하여 저장이 될때마다 자동으로 reload가 되기때문에 굳이 종료할 이유는 없습니다. 만약 중지를 시키고 싶다면, Ctrl + C 를 눌러 중지 시킵니다.

다시 SampleRouter.js 로 돌아가서, 아래와 같이 입력합니다.

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

class SampleRouter extends React.Component {
    //
    render(){
        return(
            <div>
                <Link to='/link1' >Link1</Link>
                <Link to='/link2' >Link2</Link>

                <Route path='/link1' component={SampleComponent1} />
                <Route path='/link2' component={SampleComponent2} />
            </div>
        );
    }
}

const SampleComponent1 = () => {
    return(
        <div>Hello SampleComponent1!</div>
    )
}

const SampleComponent2 = () => {
    return(
        <div>Hello SampleComponent2!</div>
    )
}

export default SampleRouter;

Rout 태그를 사용하여 각 링크에 맞는 path와 component 프로퍼티에 맞춰 선언했습니다. 첫번째 Route를 보면 path='/link1' 와 component={SampleComponent} 두개의 프로퍼티가 있습니다. path /link1 에 대한 라우팅을 하고, component는 path가 일치시 선언된 SampleComponent라는 컴포넌트로 라우팅을 하겠다는 의미입니다.

저장 후, 브라우저에서 샘플을 확인해봅시다. 연결된 링크를 선택시 해당되는 Component는 <Route> 태그가 있는곳에 자리잡게 됩니다.

Redirect

Redirect는 일반적으로 쓰이는 Redirect와 같습니다. 사용자가 어떤 url로 서비스를 요청한다고 가정해봅시다. 하지만 해당 url 로 요청한 서비스는 Redirect 명령과 함께 url을 브라우저에게 응답을 보냅니다. 브라우저는 redirect 명령을 받고 함께 받은 url로 요청을 다시 하여 다른 서비스를 요청하는 행위가 Redirect입니다.

이제 해당기능을 수행하는 react-router의 <Redirect> 를 살펴 보겠습니다. 기본적으로 아래와 같이 사용합니다.

<Redirect to="/news" />

Redirect 명령과 함께  /another 에 다시 요청하라는 의미입니다. 하지만 이대로 사용하면 사용할 곳이 많지않겠죠? 한가지 예를 들어 설명하겠습니다. 한 WebApplication은 '/news' 라는 api를 제공했다고 가정합시다. 하지만 추후에 api를 '/news' 를 사용하지않고 '/good_news' 로 변경했다고 합시다.  이땐 사용자들이 /news 로 요청하더라도 /good_news 로 갈수 있도록 해줘야겠죠? 아래와 같이 작성하면 됩니다.

<Redirect from="/news" to="/good_news" />

하지만, 이렇게만 사용하지않겠죠? 조건에 따라 랜더링을 할 수 있도록 Redirect를 작성해보도록하겠습니다. 상황은 다음과 같습니다. "로그인은 한 사용자는 '나의정보' 페이지로 이동, 로그인이 되어있지않으면 '로그인' 페이지로 이동" 시나리오를 예를 들어 설명하겠습니다. 코드는 아래와 같습니다.

<Route exact path="/my_info" render={()=>(
isLoggedIn?
(<MyInformation/>):
(<Redirect to="/login"/>)}

3항연산자를 사용한 Routing 입니다. '/my_info' 를 요청할때 isLoggedIn 값이 true 면 <MyInformation/> 컴포넌트를 랜더링 하고, false 면 '/login' 으로 Redirect를 하게됩니다.

Router

더 자세히 살펴보기전에 index.js에 있는 BrowserRouter을 보고가겠습니다. Router는 총 6가지가 있습니다.

  • Router
  • BrowserRouter
  • HashRouter
  • MemoryRouter
  • NativeRouter
  • SaticRouter

위 6가지중 우리가 사용하는것은 BrowserRouter입니다. Memory, Native Router는 일반적인 Web환경이 아닌 Native 환경에 사용이 되며, HashRouter의 경우 오래된 브라우저 지원을 위해, Router는 .low-level 사용자화를 위해, StaticRouter는 서버환경에서 사용합니다. BrowserRouter 외엔 필요시 자세히 살펴보기로하고 설명을 하겠습니다. BrowserRouter 아래 구성된 모든 컴포넌트는 어디든 라우팅이 가능합니다. 이 컴포넌트 아래에서 브라우저에서 공통으로 사용하는 프로퍼티도 공유합니다 (location, history 등.. 매우 많습니다). 그러므로 라우팅을 사용하는 컴포넌트 최상단에 위치 시켜주도록 합시다.

Switch

Switch 는 Route와 Redirect를 래핑하여 사용합니다. 물론 Switch가 무조건 필요한것은 아니지만, 사용함에 따라 라우팅 설정이 매우 달라집니다. 또한 Switch를 사용한다면, Route 또는 Redirect 를 랜더링시 조건에 맞는것중 제일 앞에 있는 하나만 랜더링합니다. 차이점을 알아보기위해, 사용하지않았을때를 예를 들어 모호하게 라우팅되는 코드를 하나 예를 들겠습니다.

                <Link to='/main' >Link1</Link>
                <Link to='/sub' >Link2</Link>
 
                 <Route path='/' component={Home} />
                 <Route path='/main' component={Main} />
                 <Route path='/main/sub' component={Sub} />
                 <Route component={NoMatch}/>

위 코드는 매우 모호합니다. 예를들어 'http://localhost:3000/main/sub' 를 호출했다고 가정해보죠. 결과 부터 말하자면 Route에 정의된 4개의 컴포넌트가 모두 랜더링됩니다.  Route 위에서 부터 path 비교를 해보겠습니다. Switch로 묶여있지않으면 일치하는 모든 path 조건을 가진 컴포넌트를 전부 랜더링합니다. 다시 말해, /main/sub 는 ('/', '/main', '/main/sub', '조건없음')  4가지 Route 조건을 모두 포함하게 됩니다.

물론 이에대한 해결방법은 있습니다. Route는 8개의 옵션(나머지는 위 Route 문단을 참고)을 내장하고 있는데,  그중 exact를 사용하면 조건과 정확히 일치할 경우에만 랜더링을 합니다. 아래를 보죠.

<Link to='/main' >Link1</Link>
<Link to='/sub' >Link2</Link>
 
<Route exact path='/' component={Home} />
<Route exact path='/main' component={Main} />
<Route exact path='/main/sub' component={Sub} />
<Route component={NoMatch}/>

자 이렇게 exact를 사용하면 정확히 path와 일치해야만 해당 컴포넌트가 랜더링됩니다.  이제는 /main/sub 를 호출하면 /main/sub 의 컴포넌트만 호출하게 된것입니다. 하지만 문제점이 하나있습니다. 조건이 없는 Route는 어떤 path에도 조건이 일치하여 무조건 랜더링하게 됩니다. 조건이 적용이 안된다면 해당 컴포넌트는 Route를 사용할 이유가 하나도 없게 됩니다. 그래서 Switch를 사용하여 조건이 없는 Route를 처리 해보도록 하겠습니다.

				<Link to='/main' >Link1</Link>
                <Link to='/sub' >Link2</Link>                  
                  
                <Switch>
                    <Route path='/' component={Home} />
                    <Route path='/main' component={Main} />
                    <Route path='/main/sub' component={Sub} />
                    <Route component={NoMatch}/>
                </Switch>

Switch는 맨 처음 언급했던것처럼, 조건이 일치하는 것들 중 제일 첫번째 선언된 Route의 컴포넌트만 랜더링합니다. 위를 예를 들어 살펴보면, /main/sub를 호출했다고 가정해보죠. 이땐 첫번째로 일치하는 "/" 이 랜더링 됩니다. 하지만, 개발자입장에선 /main/sub를 랜더링 하는게 맞겠죠? 다른 예를 들어보면 "/main" 을 호출했다고 가정해 봅시다. 이 역시도 첫번째로 일치하는 "/" 가 랜더링이 됩니다. 그래서 우리가 생각하는것 처럼 컴포넌트들을 랜더링하기 위해선 아래와 같이 exact 옵션을 사용하시면 됩니다.

				<Link to='/main' >Link1</Link>
                <Link to='/sub' >Link2</Link>                  
                  
                <Switch>
                    <Route exact path='/' component={Home} />
                    <Route exact path='/main' component={Main} />
                    <Route exact path='/main/sub' component={Sub} />
                    <Route component={NoMatch}/>
                </Switch>

이로써 각 Route들은 path에 맞는 컴포넌트들을 호출할 수 있게 되었고, 어느것도 일치 하지않으면 , 조건이 없는 Route 컴포넌트를 랜더링 하게 됩니다.

마치며..

react-router 라는 third-party 라이브러리를 살펴보았습니다. 위 5개의 개념만 알고 있어도 기본적인 라우팅은 가능합니다. 다음 포스팅으로 각 컴포넌트에 대해 깊이 살펴보고, 관리, 응용하는 샘플로 진행하도록 하겠습니다. 감사합니다.

다음포스팅

<Link…>

참조

React: https://reactjs.org/

react-router: https://reacttraining.com/react-router/