관리 메뉴

거니의 velog

(5) 리액트 라우터로 SPA 개발하기 1 본문

React_리액트 응용

(5) 리액트 라우터로 SPA 개발하기 1

Unlimited00 2023. 12. 8. 17:39

1. SPA란?

* react-route v6 이후를 보길 원하는 분들은 아래 주소 링크를 참고하길 바란다.

https://velog.io/@velopert/react-router-v6-tutorial

 

React Router v6 튜토리얼

리액트 라우터 v6를 새로 접하시는 분들을 위한 튜토리얼을 작성했습니다. 리액트 라우터 v6 의 기본적인 사용법, 그리고 이 라이브러리에서 제공하는 다양한 유용한 기능들에 대해서 알아봅시

velog.io

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

* 기존에는 사용자가 다른 페이지로 이동할 때마다 새로운 html을 받아 오고, 페이지를 로딩할 때마다 서버에서 리소스를 전달받아 해석한 뒤 화면에 보여 주었다. 이렇게 사용자에게 보이는 화면은 서버 측에서 준비했다. 사전에 html 파일을 만들어서 제공하거나, 데이터에 따라 유동적인 html을 생성해 주는 템플릿 엔진을 사용하기도 했다.

* 요즘은 웹에서 제공하는 정보가 정말 많기 때문에 새로운 화면을 보여 주어야 할 때마다 서버 측에서 모든 뷰를 준비한다면 성능상의 문제가 발생할 수 있다. 예를 들면 트래픽이 너무 많이 나올 수도 있고, 사용자가 몰려 서버에 높은 부하가 쉽게 걸릴 수도 있다. 속도와 트래픽 측면에서는 캐싱과 압축을 해서 서비스를 제공하면 어느 정보 최적화될 수 있겠지만, 사용자와의 인터랙션이 자주 발생하는 모던 웹 애플리케이션에서는 적당하지 않을 수도 있다. 애플리케이션 내에서 화면 전환이 일어날 때마다 html을 계속 서버에 요청하면 사용자의 인터페이스에서 사용하고 있던 상태를 유지하는 것도 번거롭고, 바뀌지 않는 부분까지 새로 불러와서 보여 주어야 하기 때문에 불필요한 로딩이 있어서 비효율적이다.

* 그래서 리액트 같은 라이브러리 혹은 프레임워크를 사용하여 뷰 렌더링을 사용자의 브라우저가 담당하도록 하고, 우선 애플리케이션을 브라우저에 불러와서 실행시킨 후에 사용자와의 인터랙션이 발생하면 필요한 부분만 자바스크립트를 사용하여 업데이트해 준다. 만약 새로운 데이터가 필요하다면 서버 API를 호출하여 필요한 데이터만 새로 불러와 애플리케이션에서 사용할 수도 있다.

* 싱글 페이지라고 해서 화면이 한 종류일까? 꼭 그렇지만은 않다. 예를 들어 블로그를 개발한다면 홈, 포스트 목록, 포스트, 글쓰기 등의 화면이 있을 것이다. SPA의 경우 서버에서 사용자에게 제공하는 페이지는 한 종류이지만, 해당 페이지에서 로딩된 자바스크립트와 현재 사용자 브라우저의 주소 상태에 따라 다양한 화면을 보여 줄 수 있다.

* 다른 주소에 다른 화면을 보여 주는 것을 라우팅이라고 한다. 리액트 라이브러리 자체에 이 기능이 내장되어 있지는 않다. 그 대신 브라우저의 API를 직접 사용하여 이를 관리하거나, 라이브러리를 사용하여 이 작업을 더욱 쉽게 구현할 수 있다.

* 리액트 라우팅 라이브러리는 리액트 라우터(react-router), 리치 라우터(reach-router), Next.js 등 여러 가지가 있다. 우리는 그 중 역사가 가장 길고 사용 빈도가 가장 높은 리액트 라우터를 사용할 것이다.

* 리액트 라우터는 클라이언트 사이드에서 이루어지는 라우팅을 아주 간단하게 구현할 수 있도록 해 준다. 더 나아가서 나중에 서버 사이드 렌더링을 할 때도 라우팅을 도와주는 컴포넌트들을 제공해 준다.


(1) SPA의 단점

* SPA의 단점은 앱의 규모가 커지면 자바스크립트 파일이 너무 커진다는 것이다. 페이지 로딩 시 사용자가 실제로 방문하지 않을 수도 있는 페이지의 스크립트도 불러오기 때문이다. 하지만 걱정하지 말자. 나중에 배울 코드 스플리팅(code splitting)을 사용하면 라우트별로 파일들을 나누어서 트래픽과 로딩 속도를 개선할 수 있다.

* 리액트 라우터처럼 브라우저에서 자바스크립트를 사용하여 라우팅을 관리하는 것은 자바스크립트를 실행하지 않는 일반 크롤러에서는 페이지의 정보를 제대로 수집해 가지 못한다는 잠재적인 단점이 따른다. 그렇기 때문에 구글, 네이버, 다음 같은 검색 엔진의 검색 결과에 페이지가 잘 나타나지 않을 수도 있다.  구글 검색 엔진에서 사용하는 크롤러의 경우, 자바스크립트를 실행해 주는 기능이 탑재되어 있기는 하지만, 크롤링하는 모든 페이지에서 자바스크립트를 실행하고 있지는 않다(2019년 기준). 또한, 자바스크립트가 실행될 때까지 페이지가 비어 있기 때문에 자바스크립트 파일이 로딩되어 실행되는 짧은 시간 동안 흰 페이지가 나타날 수 있다는 단점도 있다. 이러한 문제점들은 다행히 나중에 배우게 될 서버 사이드 렌더링(server-side rendering)을 통해 모두 해결할 수 있다.


2. 프로젝트 준비 및 기본적인 사용법

* 이제 SPA가 무엇인지 알았고, 리액트 라우터의 역할도 알았으니 본격적으로 리액트 라우터를 사용해 보자!

* 이번 실습은 다음 흐름대로 진행된다.


(1) 프로젝트 생성 및 라이브러리 설치

* 우선 리액트 라우터를 적용해 볼 리액트 프로젝트를 새로 생성해 보자.

$ yarn create react-app router-tutorial

* 그리고 해당 프로젝트 디렉터리로 이동하여 리액트 라우터 라이브러리를 설치하자. 리액트 라우터를 설치할 때는 yarn을 사용하여 react-router-dom이라는 라이브러리를 설치하면 된다.

$ cd router-tutorial
$ yarn add react-router-dom

// v5 설치 용도
$ yarn add react-router-dom@5
yarn add react-router-dom@5 명령어를 사용하여 특정 버전의 react-router-dom 라이브러리를 설치하는 경우, 해당 버전의 다른 의존성 패키지들도 자동으로 설치됩니다. 그러나 때로는 특정 프로젝트나 라이브러리가 다른 버전의 의존성을 필요로 할 수 있습니다. 이 경우, 일반적으로는 yarn이 자동으로 충돌을 해결하려고 노력하지만, 때로는 명시적인 작업이 필요할 수 있습니다.

다른 라이브러리의 버전을 맞추는 데 필요한 단계는 다음과 같습니다:

의존성 확인: 프로젝트의 package.json 파일을 확인하여 다른 의존성 라이브러리와 버전을 파악합니다.

// package.json

{
  "dependencies": {
    "react": "^16.8.0",
    "react-dom": "^16.8.0",
    "react-router-dom": "^5.0.0",
    // 다른 의존성들...
  }
}

버전 일치 설정: 필요한 다른 라이브러리의 버전을 명시적으로 설정하려면 package.json 파일에서 해당 라이브러리의 버전을 직접 수정합니다.

// package.json

{
  "dependencies": {
    "react": "^16.8.0",
    "react-dom": "^16.8.0",
    "react-router-dom": "^5.0.0",
    // 다른 의존성들...
  }
}

의존성 설치: 변경된 package.json 파일을 저장한 후, 터미널에서 yarn install 명령어를 실행하여 의존성을 업데이트합니다.

yarn install

이제 react-router-dom 및 다른 의존성 라이브러리의 버전이 맞추어진 상태여야 합니다. 만약 어떤 의존성이 여전히 충돌하면, 해당 라이브러리의 버전을 수동으로 조정해야 할 수 있습니다. 이때는 yarn why [package] 명령어를 사용하여 왜 특정 버전이 설치되었는지 확인하고 문제를 해결할 수 있습니다.

(2) 프로젝트에 라우터 적용

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

[src/index.js]

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom/cjs/react-router-dom.min";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

[3] 페이지 만들기

* 이제 라우트로 사용할 페이지 컴포넌트를 만들 차례이다. 사용자가 웹 사이트에 들어왔을 때 맨 처음 보여 줄 Home 컴포넌트와 웹 사이트를 소개하는 About 컴포넌트를 만들어 보자. src 디렉터리에 다음 파일을 만들자.

[Home.js]

import React from "react";

const Home = () => {
  return (
    <div>
      <h1>홈</h1>
      <p>홈, 그 페이지는 가장 먼저 보여지는 페이지</p>
    </div>
  );
};

export default Home;

[About.js]

import React from "react";

const About = () => {
  return (
    <div>
      <h1>소개</h1>
      <p>
        이 프로젝트는 리액트 라우터 기초를 실습해 보는 예제 프로젝트 입니다.
      </p>
    </div>
  );
};

export default About;

* 이제 페이지로 사용할 모든 컴포넌트가 완성되었다.


(4) Route 컴포넌트로 특정 주소에 컴포넌트 연결

* Route 라는 컴포넌트를 사용하여 사용자의 현재 경로에 따라 다른 컴포넌트를 보여 주자. Route 컴포넌트를 사용하면 어떤 규칙을 가진 경로에 어떤 컴포넌트를 보여 줄지 정의할 수 있다.

* 사용 방식은 다음과 같다.

<Route path="주소규칙" component={보여 줄 컴포넌트} />

* App.js를 열어서 기존 코드를 모두 제거하고, Route 컴포넌트를 사용하여 방금 만든 Home 컴포넌트 혹은 About 컴포넌트를 보여 주도록 설정해 보자.

import React from "react";
import { Route } from "react-router-dom/cjs/react-router-dom";
import Home from "./Home";
import About from "./About";

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

export default App;

* React-Router v6 이후는 아래 접은 글을 참조하자.

더보기

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

const App = () => {
  return (
    <div>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </div>
  );
};

export default App;


* 여기까지 코드를 작성한 뒤 터미널에서 yarn start를 입력하여 개발 서버를 시작해 보자. 첫 화면에 다음과 같이 Home 컴포넌트가 나타날 것이다.

- http://localhost:3000/

* 다음으로 주소 창에 아래 경로를 입력하여 들어가 보자.

- http://localhost:3000/about

* /about 경로로 들어가면 About 컴포넌트만 나오기를 기대했지만, 예상과 다르게 두 컴포넌트가 모두 나타난다. /about 경로가 / 규칙에도 일치하기 때문에 발생한 현상이다. 이를 수정하려면 Home을 위한 Route 컴포넌트를 사용할 때 exact라는 props를 true로 설정하면 된다.

[App.js]

import React from "react";
import { Route } from "react-router-dom/cjs/react-router-dom";
import Home from "./Home";
import About from "./About";

const App = () => {
  return (
    <div>
      <Route path="/" component={Home} exact={true} />
      <Route path="/about" component={About} />
    </div>
  );
};

export default App;

* 다시 브라우저를 확인해 보자.

- http://localhost:3000/about

* 컴포넌트가 하나만 잘 나타난다!


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

* Link 컴포넌트는 클릭하면 다른 주소로 이동시켜 주는 컴포넌트이다. 일반 웹 애플리케이션에서는 a 태그를 사용하여 페이지를 전환하는데, 리액트 라우터를 사용할 때는 이 태그를 직접 사용하면 안 된다. 이 태그는 페이지를 전환하는 과정에서 페이지를 새로 불러오기 때문에 애플리케이션이 들고 있던 상태들을 모두 날려 버리게 된다. 렌더링된 컴포넌트들도 모두 사라지고 다시 처음부터 렌더링하게 된다.

* Link 컴포넌트를 사용하여 페이지를 전환하면, 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 상태에서 HTML5 History API를 사용하여 페이지의 주소만 변경해 준다. Link 컴포넌트 자체는 a 태그로 이루어져 있지만, 페이지 전환을 방지하는 기능이 내장되어 있다.

* Link 컴포넌트는 다음과 같이 사용한다.

<Link to="주소">내용</Link>

* 이제 App 컴포넌트에서 "/", "/about" 경로로 이동하는 Link 컴포넌트를 만들어 보자.

[App.js]

import React from "react";
import { Link, Route } from "react-router-dom/cjs/react-router-dom";
import Home from "./Home";
import About from "./About";

const App = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">홈</Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
      </ul>
      <Route path="/" component={Home} exact={true} />
      <Route path="/about" component={About} />
    </div>
  );
};

export default App;

* React-Router v6 이후는 아래 접은 글을 참조하자.

더보기

import React from "react";
import { Link, Route, Routes } from "react-router-dom";
import About from "./About";
import Home from "./Home";

const App = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">홈</Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
      </ul>
      <hr />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </div>
  );
};

export default App;


* 페이지 상단에 있는 링크를 눌러 보자. 페이지가 잘 전환되는가?

- http://localhost:3000/