react를 다루는 기술

13. 리액트 라우터로 SPA 개발하기

crab. 2022. 6. 15. 08:47

SPA란?

  • SPA는 Single Page Application(싱글 페이지 애플리케이션)의 약어입니다.
  • 말 그대로 한 개의 페이지로 이루어진 애플리케이션이라는 의미입니다.
  • 전통적인 웹 페이지는 다음과 같이 여러 페이지로 구성되어 있습니다.

기존방식

  • 다른 페이지로 이동할 때 마다 새로운 html, 페이지 로딩 시 서버에서 리소스 전달받아 해석한 뒤 화면 보여줌
  • 사용자에게 보이는 화면은 서버 측에서 준비.
  • 유동적인 html 생성해주는 템플릿 엔진 사용
  • 서버 측에서 모든 뷰를 준비한다면 성능상의 문제가 발생
  • 바뀌지 않은 부분까지 새로 불러와 불필요한 로딩이 있어 비효율적

Single Page Application: 말 그대로 한 개의 페이지로 이루어진 애플리케이션

  • 뷰 렌더링을 사용자의 브라우저가 담당.
  • 애플리케이션을 브라우저에 불러와서 실행시킨 후 사용자와의 인터랙션이 발생하면 필요한 부분만 자바스크립트를 이요하여 업데이트
  • 새로운 데이터가 필요하다면 서버 API를 호출하여 필요한 데이터만 새로 불러와 애플리케이션을 사용할 수 있다.

라우팅

  • 다른 주소에 다른 화면을 보여 주는 것
  • 싱글 페이지라고 해서 화면이 한 종류는 아니다. 해당 페이지에서 로딩된 자바스크립트와 현재 사용자 브라우저의 주소 상태에 따라 다양한 화면을 보여 줄 수 있다.
  • 리액트 라우팅 라이브러리
    • 리액트 라우터
    • Next.js

SPA의 단점

  • SPA의 단점은 앱의 규모가 커지면 자바스크립트 파일이 너무 커진다는 것입니다.
    • 페이지 로딩 시 사용자가 실제로 방문하지 않을 수도 있는 페이지의 스크립트도 불러오기 때문
  • 리액트 라우터처럼 브라우저에서 자바스크립트를 사용하여 라우팅을 관리하는 것은 자바스크립트를 실행하지 않는 일반 크롤러에서는 페이지의 정보를 제대로 수집해 가지 못한다는 잠재적인 단점이 있다.
    • 구글, 네이버, 다음 같은 검색 엔진의 검색 결과에 페이지가 잘 나타나지 않을 수도 있습니다.
    • 자바스크립트가 실행될 때까지 페이지가 비어 있기 때문에 자바스크립트 파일이 로딩되어 실행되는 짧은 시간 동안 흰 페이지가 나타날 수 있다는 단점도 있습니다.
    • 이러한 문제점들은 다행히 나중에 배우게 될 서버 사이드 렌더링(server-side rendering)을 통해 모두 해결할 수 있습니다.

프로젝트 설치, 라이브러리 설치

  • 해당 디렉토리에 yarn add react-router-dom

프로젝트에 적용

  • 프로젝트에 리액트 라우터를 적용할 때는 src/index.js 파일에서 react-router-dom에 내장되어 있는 BrowserRouter라는 컴포넌트를 사용하여 감싸면 됩니다.
  • 이 컴포넌트는 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로고침하지 않고도 주소를 변경하고, 현재 주소에 관련된 정보를 props로 쉽게 조회하거나 사용할 수 있도록 해 줍니다.

라우팅 구현

  • 처음 보여줄 Home 컴포넌트 생성, 웹 사이트를 소개하는 About 컴포넌트 생성
  • Router 컴포넌트 사용해 현재 경로에 따라 다른 컴포넌트 렌더링
    • 규칙 : <Route path="주소 규칙" component={보여 줄 컴포넌트}>
// App.js
import About from './About'
import Home from './Home'
import {Route} from 'react-router-dom'
...
return (
    <div>
        <Route path='/' component={HOME}>
        <Route path='/about' component={ABOUT}>
    </div>
)

/가 겹치는 문제

  • 이 경우 about 페이지에 HOME 컴포넌트도 함께 렌더링 된다.
    • /about 경로가 / 규칙에도 일치하기 때문
    • Home 위한 Route 컴포넌트를 사용할 때 exact라는 props를 true로 설정
    • <Route path='/' component={HOME} exact={true}>

Link 컴포넌트를 사용하여 다른 주소로 이동하기

  • Link 컴포넌트는 클릭하면 다른 주소로 이동시켜 주는 컴포넌트입니다.
  • 일반 웹 애플리케이션에서는 a 태그를 사용하여 페이지를 전환하는데요.
  • 리액트 라우터를 사용할 때는 이 태그를 직접 사용하면 안 됩니다.
  • 이 태그는 페이지를 전환하는 과정에서 페이지를 새로 불러오기 때문에 애플리케이션이 들고 있던 상태들을 모두 날려 버리게 됩니다.
  • 렌더링된 컴포넌트들도 모두 사라지고 다시 처음부터 렌더링하게 되죠.
  • Link 컴포넌트를 사용하여 페이지를 전환하면, 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 상태에서 HTML5 History API를 사용하여 페이지의 주소만 변경해 줍니다.
  • Link 컴포넌트 자체는 a 태그로 이루어져 있지만, 페이지 전환을 방지하는 기능이 내장되어 있습니다.
<Link to="주소">내용</Link>
return (
    <div>
        <ul>
            <li>
                <Link to="/">홈</Link>
            </li>
            <li>
                <Link to="/about">소개</Link>
            </li>
        </ul>
        <Route path='/' component={Home}>
        <Route path='/about' component={About}>
    </div>
)

Route 하나에 여러 개의 path 설정하기

// Route 하나당 컴포넌트 설정
<Route path='/' component={Home} exact={true}>
<Route path='/about' component={About}>
<Route path='/info' component={About}>

// Route 하나에 여러 path 설정
<Route path='/' component={Home} exact={true}>
<Route path={['/about', '/info']} component={About}>

URL 파라미터와 쿼리

  • 페이지 주소를 정의할 때 가끔은 유동적인 값을 전달해야 할 때도 있습니다.
  • 이는 파라미터와 쿼리로 나눌 수 있습니다.
    • 파라미터 예시: /profiles/velopert
    • 쿼리 예시: /about?details=true
  • 유동적인 값을 사용해야 하는 상황에서 파라미터를 써야 할지 쿼리를 써야 할지 정할 때, 무조건 따라야 하는 규칙은 없습니다.
  • 다만 일반적으로 파라미터는 특정 아이디 혹은 이름을 사용하여 조회할 때 사용하고, 쿼리는 우리가 어떤 키워드를 검색하거나 페이지에 필요한 옵션을 전달할 때 사용합니다.

URL 파라미터

  • 뒷부분에 유동적인 username 값을 넣어 줄 때 해당 값을 props로 받아 와서 조회하는 방법
  • App.js
    • Route path의 /:username을 통해 Profile 컴포넌트에 props를 전달한다.
    • 이 때 Link에 있는 /velopert, /dongwon이 username 값으로 Profile 컴포넌트에 props에 전달
  • <li><Link to="/profile/velopert">velopert 프로필</Link></li><li><Link to="/profile/dongwon">dongwon 프로필</Link></li><Route path="/profile/:username" component={Profile} />
  • Profile.js
    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>
      );
    };
    
    • App.js 에서 전달받은 props : match.params.username = velopert 혹은 dongwon
  • const data = { velopert: { name: '김민준', description: '리액트 저자', }, dongwon: { name: '김동원', description: '공부 중', }, };

URL 쿼리

  • location 객체
    • location 객체 안의 search값에서 쿼리 조회 가능
    • 라우트로 사용된 컴포넌트에게 props로 전달.
    • 웹 애플리케이션의 현재 주소에 대한 정보를 가지고 있다.
    • localhost:3000/about?detail=true 주소 일 때
    {
        "pathname": "/about",
        "search": "?detail=true"
        "hash" : ""
    }
    
    • 문자열 형태로 이루어져 있다. 특정 값을 불러오기 위해 문자열을 객체 형태로 변환
      • qs 라이브러리.
      • yarn add qs
  • About.js
    • localhost:3000/about?detail=true 로 들어갔을 때
    const query = qs.parse(location.search, {
        ignoreQueryPrefix: true,
    });
    
    const showDetail = query.detail === 'true';
    
    • ignoreQueryPrefix: true, 를 통해 앞의 ?를 제거
    • query는 "detail=true"

서브 라우트

  • 서브 라우트는 라우트 내부에 또 라우트를 정의하는 것을 의미합니다.
  • 이 작업은 그렇게 복잡하지 않습니다.
  • 그냥 라우트로 사용되고 있는 컴포넌트의 내부에 Route 컴포넌트를 또 사용하면 됩니다.

리액트 라우터 부가 기능

history - 클래스형 컴포넌트

  • history 객체
    • 컴포넌트에 match, location과 함께 전달되는 props 중 하나
    • 컴포넌트 내에 구현하는 메서드에서 라우터 API 호출 가능
      • ex) 뒤로가기, 화면 전환, 다른 페이지 이탈 방지

withRouter 컴포넌트

  • 라우터로 사용된 컴포넌트가 아니어도 match, location, history 사용 가능
    • 주소 경로가 없는 컴포넌트도 사용이 가능하다.
    • 라우트 기능이 없지만 withRouter를 사용하여 location, match, history 사용
    • textarea를 통해 location, match를 확인. 버튼을 이용해 histroy 기능 확인 가능하다.
    • export 할 때 withRouter(WithRouterSample)로 감싸준다.
    • match객체의 param은 현재 자신을 보여 주고 있는 라우트 컴포넌트를 기준으로 match가 전달.
      • Profile과 Profiles에 넣어보고 비교

Switch 컴포넌트

  • 여러 Route를 감싸서 그 중 일치하는 단 하나의 라우터만 렌더링
    • 모든 규칙과 일치하지 않을 때 Not Found 페이지도 구현 가능

NavLink

  • 현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일, CSS 클래스를 적용할 수 있는 컴포넌트
  • NavLink가 활성화되어 스타일을 적용할 때는 activeStyle을, CSS 클래스를 적용할 때는 activeClassName을 props로 넣어 준다.