Front End/React

React SPA

SPA (Single Page Application)

웹페이지에서는 페이지를 유저에게 보여줄 때, 즉 페이지를 로딩할 때마다 서버에 미리 준비된 페이지를 전달받아 렌더링 했지만 규모가 커질수록 유저와의 상호작용이 많아지고 이에 따른 속도 저하 등의 문제가 발생했다. 이는 서버에 필요 이상의 트래픽을 발생시키는 것은 물론 유저 입장에서도 반응성이 떨어지는, 나쁜 사용자 경험을 제공하게 되었다.

이러한 문제를 SPA에서는 전환 후에 중복되는 부분은 불러오지 않고 필요한 부분의 데이터로 불러옴으로서 브라우저에서 해당되는 부분만 업데이트 하는 방식으로 해결했다. 말 그대로 한 페이지에서 동작하는 어플리케이션이 된 것이다.

SPA의 장점

  • 필요한 부분의 데이터만 받아 화면을 업데이트하기 때문에 사용자와의 Interaction에 빠르게 반응한다.
  • 서버에서는 요청받은 데이터만 넘겨주면 되기 때문에 서버 과부하 문제가 현저히 줄어든다.
  • 전체 페이지를 렌더링 할 필요가 없기 때문에 더 나은 유저경험을 제공한다.

SPA의 단점

  • 대부분의 코드가 JavaScript 파일에 있다보니 파일이 무거워 첫 화면의 로딩 시간이 길다.
  • 검색엔진 최적화(SEO)가 좋지 않다. 검색 엔진은 HTML 파일에 있는 자료를 분석하는 방식으로 검색 기능을 구동하는데, SPA는 HTML 파일에 별다른 데이터가 없기 때문에 동작하지 못한다. 단, SPA에서도 검색 엔진 최적화에 대응할 수 있도록 검색 엔진이 발전하는 추세라 점차 이 단점은 사라지고 있다.

React에서의 Wireframe (컴포넌트 나누기)

React를 이용해 유튜브를 개발한다 가정해보자. 검색창과 영상 목록, 사이드바 등 다양한 기능을 구현해야 한다. React는 컴포넌트 기반이기 때문에, 페이지 보다는 어떤 컴포넌트를 만들고 조합할지를 구상해야 한다.

먼저 화면 상단의 헤더의 경우 검색창과 설정이 들어가기 때문에 각각의 기능을 컴포넌트화 하고, 다른 페이지로 이동해도 늘 최상단에 같은 모습을 유지하고 있기 때문에 한 번만 만들어서 모든 페이지에서 재사용하는 것으로 설계한다.

영상 목록은 어떨까. 동일한 형태를 가진 영상물이 반복적인 형태로 화면을 구성하기 때문에 컨텐츠 역시 컴포넌트화 하여 반복하되 서버에서 데이터를 받아 출력할 수 있도록 설계한다.

뿐만 아니라 영상을 클릭했을 때 이 영상을 출력해야 하고 대기 목록에 있을 때도, 재생 중일 때도 동일한 내용이 화면에 출력된다. 같은 데이터지만, 어떤 상태에 있느냐에 따라 출력되는 위치와 모습만 조금씩 달라진다.

컴포넌트가 UI의 필수요소, 또는 각자 고유의 기능을 가지고 있다는 정의도 맞지만 어플리케이션의 기능이 다양해질수록 안에서 다뤄지는 데이터를 컴포넌트끼리 보다 유기적으로 주고받을 수 있도록 설계되어야 한다.

Routing?

SPA는 하나의 페이지를 가지고 있지만 사실 한 종류의 화면만 사용하진 않는다. 어플리케이션에서 제공하는 기능에 따라 이에 맞춰 여러 페이지를 보여줄 수 있고, 이 화면에 따라 주소 역시 달라진다. 이렇게 다른 주소에 따라 다른 뷰를 보여주는 과정을 경로에 따라 변경한다는 의미로 라우팅(Routing)이라고 한다.

React Router

하지만 React 자체에는 라우팅 기능이 내장되어 있지 않기 때문에, 우리가 직접 주소마다 다른 뷰를 보여주어야 한다. React SPA에서는 이러한 라우팅을 위해 React Router라는 라이브러리를 가장 많이 사용한다.

React Router 주요 컴포넌트

React Router의 주요 컴포넌트는 크게 라우터 역할을 하는 BrowserRouter, 경로를 매칭해주는 RoutesRoute, 그리고 경로를 변경하는 역할을 하는 Link로 나눌 수 있다. 이 컴포넌트들을 사용하기 위해서는 React Router 라이브러리를 따로 불러와야 한다.

React Router 환경 세팅

1. react-router 라이브러리 설치

create-react-app으로 React 프로젝트 환경을 세팅했다면, 해당 환경에 npm을 이용해 React Router 라이브러리를 설치한다.

npm install react-router-dom@^6.3.0

2. App.js에 react-router 컴포넌트 꺼내오기

라이브러리의 컴포넌트를 꺼내오기 위해 App.jsimport 명령어를 이용해 컴포넌트를 꺼내온다.

import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

React Router 구현방법

<BrowserRouter>

<BrowserRouter> 컴포넌트는 새로고침하지 않고도 주소를 변경할 수 있게 해준다. React Router DOM이 사용될 부분의 가장 상위에 작성하여 해당 컴포넌트 내에서 React Router의 컴포넌트들을 사용한다.

function App() {
  return (
    <BrowserRouter>
      <div>
        <nav>
          <ul>
            <li>Home</li>
            <li>MyPage</li>
            <li>Dashboard</li>
          </ul>
        </nav>
      </div>
    </BrowserRouter>
  );
}

export default App;

아래와 같이 ReactDOM의 렌더 단계인 index.js<BrowserRouter>를 넣어서 사용할 수도 있다.

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

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

<Routes>, <Route>

경로를 매칭해주고 역할을 한다. <Routes>가 위치한 곳에 실제로 연결한 컴포넌트가 표시된다.

  • <Routes> : 여러 <Route>컴포넌트를 감싸서 그중 경로가 일치하는 단 하나의 라우터만 렌더링시켜준다. <Routes>를 사용하지 않으면 매칭되는 모든 요소를 렌더링하게 된다.
  • <Route> : path속성(URL)을 지정하여 해당 path에서 어떤 컴포넌트를 보여줄지 element속성으로 정한다. URL은 <Link>컴포넌트가 정해주는 경로와 일치한 경우에만 작동한다.
function App() {
  return (
    <BrowserRouter>
      <div>
        <nav>
          <ul>
            <li>Home</li>
            <li>MyPage</li>
            <li>Dashboard</li>
          </ul>
        </nav>
				{// 해당 위치에 Router가 불러온 컴포넌트가 표시된다}
        <Routes>
					{// 경로는 path, 컴포넌트는 element로 연결한다}
          <Route path="/" element={<Home />} />
          <Route path="/mypage" element={<MyPage />} />
          <Route path="/dashboard" element={<Dashboard />} />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;
☝🏼
만약 사용자가 지정된 주소(위의 코드에서는 /, /mypage, /dashboard) 외에 주소로 접근하게 되면 의도한 화면이 보이지 않을 수 있는데, 이 때 사용할 수 있는 속성이 path='*'이다. 지정되지 않은 주소로 접근할 시 이 속성이 적용된 컴포넌트를 보여주기 때문에 오류메세지를 보여주는 등으로 활용할 수 있다.

<Link>

경로를 연결해주는 역할을 하는 컴포넌트로, HTML의 <a>태그를 생각하면 이해하기 쉽다. 페이지 전환을 통해 페이지를 새로 불러오지 않고 어플리케이션을 그대로 유지한 채로 HTML5 History API를 이용해 페이지의 주소만 변경한다. ReactDOM으로 렌더를 시키면 <Link>컴포넌트는 <a>태그로 렌더링한다. to속성을 이용해 URL을 연결해주면 된다.

function App() {
  return (
    <BrowserRouter>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/mypage">MyPage</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>
{// ... 중략}

useNavigate

경로를 연결해주는 역할은 <Link>와 비슷하지만 태그가 아닌 함수 호출로 활용할 수 있기 때문에 state, prop 상태에 따라 조건문을 사용해 이동할 위치를 결정시킬 수 있다. 변수(일반적으로는 navigate라 명명)에 함수를 할당해서 사용하는 것이 일반적이며, 전달인자 내에 경로를 입력하면 해당 컴포넌트로 연결된다.

전달인자에 -1이나 1을 넣으면 window.history 내의 값을 참조하여 이전, 다음 페이지로 이동시켜 줄 수 있다.

import { useNavigate } from 'react-router-dom';

function App (){
	const navigate = useNavigate();
	return (
		<button onclick={navigate(-1)}> 뒤로가기 </button>
	)
}

Uploaded by N2T