[React] React Router DOM

심언조·2021년 4월 22일
1

React

목록 보기
2/2
post-thumbnail

📕 SPA 구현과 React Router DOM

SPA(Single Page Application)는 페이지가 단일인 어플리케이션을 뜻한다. 보통의 웹 페이지는 사용자가 페이지를 이동할 때 마다 페이지가 새로고침되고 페이지를 로딩할 때 마다 서버로부터 리소스를 받아 렌더링 한다. 그렇기 때문에 로딩시간이 지연된다는 단점이 생긴다.

그러나 SPA는 브라우저에 어플리케이션을 로드하고 이후에는 렌더링이 필요한 부분의 데이터만 불러와 동적으로 보여줘 사용자에게 매력적인 경험을 선사할 수 있다.

그러나 처음 한번 어플리케이션을 로딩할 때 파일의 규모가 크면 브라우저에 빈 화면이 보일 수 있는 단점이 있다. 또 자바스크립트를 실행하지 않는 크롤러(Web Crawler)에서는 페이지의 정보를 제대로 받아가지 못하는 잠재적인 단점도 존재한다.

이번에 다뤄볼 react-router-dom서드파티 라이브러리로 공식적으로 리액트에서 지원하는 기능이 아니지만 SPA를 쉽게 작성하게 해준다. 여러화면으로 구성된 웹 어플리케이션을 만들고 싶다면 react-router-dom을 사용해 보길 권장한다.

설치하기

선호하는 패키지 관리자를 사용하여 react-router-dom을 설치한다.

$npm install react-router-dom
$yarn add react-router-dom

기본적인 사용방법

라우터 적용은 index.js에서 BrowserRouter 라는 컴포넌트를 사용한다.

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

// * App 을 BrowserRouter 로 감싸준다
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

다음으로 라우트로 사용할 페이지 컴포넌트를 작성한다. 웹페이지에 처음 들어왔을 때 먼저 보여줄 main 페이지 사용자의 정보를 보여주는 user 페이지를 작성한다.

// Main.js 컴포넌트
const Main = ()=>{
	return (
      <div>
        <h1>메인 페이지</h1>
        <hr/>
        <p>메인 페이지 내용 입니다.</p>
      </div>
    );
};

// User.js 컴포넌트
const User = () =>{
	return (
      <div>
        <h1>사용자 페이지</h1>
        <hr/>
        <p>사용자의 정보를 보여주는 페이지 입니다.</p>
      </div>
    );
}

이제 페이지 컴포넌트를 작성했으니 Route 컴포넌트를 사용하여 주소에 따라 보여줄 컴포넌트를 지정할 수 있다.

import React from 'react';
import { Route } from 'react-router-dom';
import Main from './Main';
import User from './User';

const App = () => {
	return (
      <div>
        <Route path='/' exact={true} component={Main}/>
        <Route path='/user' component={User}/>
      </div>
    );
};

export default App;

Route 사용방법
<Route path="주소" component={보여줄 컴포넌트}/>
주소의 값이 정확히 일치할 때 컴포넌트를 보여 주려면 exact 값을 true 로 설정한다.

위와 같이 작성했을 때 / 경로로 진입했을 때 메인페이지가 보이고 /user 경로로 진입시 사용자 페이지가 출력된다.

Link를 사용하여 다른 주소로 이동

리액트 라우터에서는 a태그를 사용할 수 없는 대신 Link 컴포넌트를 사용하여 다른 주소로 이동할 수 있다.

만약 a 태그를 사용하여 주소를 변경하면 리액트 앱에서 가지고 있던 상태들 초기화 되며 컴포넌트 또한 다시 렌더링 되므로 Link 컴포넌트를 사용해야한다.

위의 예제를 사용하여 브라우저의 주소창이 아닌 Link 컴포넌트를 사용하여 페이지 이동을 하도록 코드를 작성하면 아래와 같다.

import React from 'react';
import { Route, Link } from 'react-router-dom';
import Main from './Main';
import User from './User';

const App = () => {
	return (
      <div>
        <ul>
          <li><Link to="/">메인 페이지</Link></li>
          <li><Link to="/user">사용자 페이지</Link></li>
        </ul>
        <Route path='/' exact={true} component={Main}/>
        <Route path='/user' component={User}/>
      </div>
    );
};

export default App;

지금까지 작성한 예제코드 실행 결과는 아래에서 확인할 수 있다.

파라미터, 쿼리를 사용하여 컴포넌트에 인자전달

페이지 전환시 보여질 컴포넌트에 인자를 전달해야 하는 경우 파라미터와 쿼리를 사용하여 전달할 수 있다.

파라미터 : /profile/kimcoding
쿼리 : /about?details=true

보편적으로 파라미터는 특정 아이디나 이름을 가지고 조회할 때 사용하고, 쿼리는 어떤 키워드를 검색하거나 요청할 때 필요한 옵션을 전달할 때 사용한다.

파라미터를 사용하여 인자를 받아 해당하는 값에 맞는 정보를 표출하는 profile컴포넌트를 작성하면 다음과 같다.

import React from 'react';

const userProfile = {
  kimcoding:{
    name:"김코딩",
    desc:"내 이름은 김코딩 개발자죠."
  },
  parkhacker:{
  	name:"박해커",
    desc:"안녕하시오, 나는 박해커요",
  }
};

const Profile ({ match }) =>{
  // 리액트 라우트 컴포넌트는 props에 match, location, history가 포함되어 있다.
  // 파라미터를 받아올 때는 match를 사용한다.
  const { userid } = match.params;
  const user = userProfile[userid];
  
  return user ? (
    <div>
      <h2>
        {userid} - {user.name}
      </h2>
      <p>{user.desc}</p>
    </div>
  ) : (
    <div>존재하지 않는 사용자 입니다.</div>
  );
}

export default Profile;

리액트 라우트는 propsmatch, location, history가 포함되어 있다. 그중 파라미터를 사용할 때는 matchparams값을 사용하여 사용할 수 있다. 아래 그림과 같이 주소창에 파라미터값을 줘서 확인할 수 있다.

이번에는 쿼리를 사용하여 값을 받아오는 컴포넌트를 작성해 본다. 쿼리는 라우트 컴포넌트에서 전달되는 location 객체에 있는 search 를 사용하여 읽어올 수 있다.

// location 예시 데이터
{
  key: 'ac3df4',
  pathname: '/somewhere',
  search: '?some=search-string', // 쿼리로 입력받은 값
  hash: '#howdy',
  state: {
    [userDefined]: true
  }
}

위의 search 값은 문자열 형태로 되어있어서 이를 사용하기 위해서는 값을 직접변화해줘야 하지만 qs 라이브러리를 사용하면 간편하게 값으로 변환 가능하다.

search에서 입력받은 detail 값이 "true"일 때 상세정보를 표시하는 컴포넌트를 작성하면 다음과 같이 작성할 수 있다.

import React from 'react';
import qs from 'qs';

const About = ({ location }) =>{
  const query = qs.parse(location.search, {
    // 첫 단어 ? 를 무시하는 옵션
    ignoreQueryPrefix: true
  });
  
  const detail = query.detail === 'true';
  
  return (
    <div>
      <h2>페이지 소개</h2>
      <p>사용자 정보를 보여주는 페이지 입니다.</p>
      {detail && <p>상세 정보를 표시합니다.</p>}
    </div>
  );
}

export default About;

라우트 안에 서브라우트 추가하기

이번에는 라우트안에 라우트를 포함시켜 서브라우트를 만들어 보자. ProfileList 컴포넌트를 작성하여 Profile 컴포넌트를 서브라우트로 사용하는 코드를 작성한다.

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

const ProfileList = () =>{
  return (
    <div>
      <h3>사용자 목록</h3>
      <ul>
        <li>
          <Link to="/profilelist/kimcoding">김코딩</Link>
        </li>
        <li>
          <Link to="/profilelist/parkhacker">박해커</Link>
        </li>
      </ul>
      <Route 
        path="/profilelist" 
        exact={true} 
        render={()=><div>사용자를 선택해 주세요</div>} />
      <Route path="/profilelist/:userid" component={Profile} />
    </div>
  );
}

export ProfileList;

첫 번째 Route에서 render 값을 사용했는데, 이값을 통해서 인라인으로 JSX를 사용할 수 있으며 라우트의 props를 전달해 줄 수 있다. 이제 위 컴포넌트를 App 컴포넌트에 포함시켜 ProfileList 를 선택할 수 있게 작성한 결과는 다음과 같다.

history 객체를 사용하여 원하는 경로로 이동하기

라우트로 만들어진 컴포넌트는 match, location, history 객체를 props로 가지고 있다. 이중 history를 사용하여 메서드에서 라우터접근에 관한기능들을 사용할 수 있다. 예를들어 뒤로가기나 특정 경로로 이동 및 이탈방지 같은기능을 사용할 수 있다.

history 요소

  • length - (number) 히스토리 스택에 쌓인 요소의 개수
  • action - (string) 현재 액션상태 (PUSH, REPLACE, POP)
  • location - (object) 현재 location 객체값
  • push(path, [state]) - (function) 지정한 경로를 히스토리 스택에 추가
  • replace(path, [state]) - (function) 현재경로를 지정한 경로로 대체한다
  • go(n) - (function) 입력된 값만큼 히스토리 스택의 포인터를 이동시킨다
  • goBack() - (function) go(-1) 과 똑같이 동작 : 뒤로가기
  • goForward() - (function) go(1) 과 똑같이 동작 : 앞으로 가기
  • block(prompt) - (function) 현재경로를 이동하기 전에 입력받은 프롬프트를 실행시켜 페이지 이탈을 방지한다
  • listen - location의 변화를 감지했을 때 특정한 동작을 하기위해 사용한다(참고).

history 객체를 사용하여 간단한 예제를 만들어 보면 다음과 같다.

import React, { useEffect } from 'react';

const HistorySample = ({ history })=>{
  
  const goBack = ()=>{
    history.goBack();
  };
  
  const goMain = () =>{
    history.push('/');
  };
  
  useEffect(()=>{
    console.log(history);
    const unblock = history.block('현재 페이지를 떠나시겠습니까 ?');
    return ()=>{
      unblock();
    };
  },[history]);
  
  return (
    <div>
      <button onClick={goBack}>뒤로가기</button>
      <button onClick={goMain}>메인 페이지로</button>
    </div>
  );
}

export default HistorySample;

각 버튼 클릭 이벤트에 history객체를 사용하여 라우트를 제어하고 useEffect를 사용하여 주소를 이동할 때 알람메시지를 표출하여 사용자의 선택에 따라서 라우트 변경을 제어할 수 있다. 위 예제 코드를 적용시킨 코드는 다음과 같다.

withRouter를 사용하기

withRouter를 사용하면 라우트 컴포넌트가 아닌 컴포넌트도 부모 라우터의 match, location, history를 전달 받을 수 있게 된다.

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

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

export default withRouter(WithRouterSample);

위 컴포넌트를 ProfileList에 적용시키면 다음과 같다.

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

const ProfileList = () => {
  return (
    <div>
      <h3>사용자 목록</h3>
      <ul>
        <li>
          <Link to="/profilelist/kimcoding">김코딩</Link>
        </li>
        <li>
          <Link to="/profilelist/parkhacker">박해커</Link>
        </li>
      </ul>
      <Route
        path="/profilelist"
        exact={true}
        render={() => <div>사용자를 선택해 주세요</div>}
      />
      <Route path="/profilelist/:userid" component={Profile} />
      <WithRouterSample />
    </div>
  );
};

export default ProfileList;

위 코드를 실행시킨 결과 실행시켜 확인해 보면 WithRouterSample 의 부모컴포넌트의 props를 전달받아 보여준다는 것을 알 수 있다.

그 외 라우터 부가기능

  • Switch : 여러 라우트를 감싸서 규칙이 일치하는 하나의 라우트만을 렌더링 시킬 수 있으며, 규칙이 일치하지 않았을때 보여줄 Not Fount 페이지를 구현 할 수 있다.
  • NavLink : 현재 경로와 NavLink에 주어진 경로가 일치하는 경우 특정 클래스나 스타일을 줄수있는 Link 컴포넌트
  • Redirect : 페이지를 리다이렉트 시키는 컴포넌트
  • Prompt : history.block의 컴포넌트 버전

✏️ 정리

  • SPA는 추가적인 로딩과정없이 화면의 요소를 변경할 수 있는 장점이 있다.
  • react-router-dom을 사용하여 SPA를 쉽게 구현할 수 있다.

참고 자료

벨로퍼트 - 리액트 라우터
React Router 공식 홈페이지

profile
웹 개발자

0개의 댓글