[React] SPA 사용하기

🌊·2021년 12월 17일
0

React

목록 보기
13/20
post-custom-banner

SPA란?

Single Page Application의 약어
한 개의 페이지로 이루어진 어플리케이션이다.
리액트 같은 라이브러리 혹은 프레임워크를 사용하여 뷰 렌더링을 사용자의 브라우저가 담당하도록 한다.
새로운 데이터가 필요하면 서버 API를 호출하여 필요한 데이터만 새로 불러와 애플리케이션에서 사용할 수 있다.

기존에는 사용자가 다른 페이지로 이동할 때마다 새로운 html을 받아 오고, 페이지를 로딩할 때마다 서버에서 리소스를 전달받아 해석한 뒤 화면에 보여 주었다.
기존의 방식을 사용하면 트래픽이 너무 많이 나오거나 부하가 쉽게 걸릴 수 있다.

라우팅

다른 주소에 다른 화면을 보여주는 것
리액트 라이브러리 자체에 이 기능이 내장되어 있지는 않다.

리액트 라우팅 라이브러리

  • 리액트 라우터(react-router)
  • 리치 리액트 라우터
  • Next.js 등

SPA의 단점

앱의 규모가 커지면 자바스크립트 파일이 너무 커진다. 페이지 로딩 시 사용자가 실제로 방문하지 않은 수도 있는 페이지의 스크립트도 불러오게 된다.

코드 스플리팅(code splitting)을 사용하면 라우트별로 파일들을 나누어서 트래픽과 로딩 속도를 개선할 수 있다.

일반 크롤러에서는 페이지의 정보를 제대로 수집해 가지 못하는 잠재적 단점이 있다.
검색 엔진의 검색 결과에 잘 나타나지 않을 수도 있다.
자바스크립트가 실행될 때까지 페이지가 비어 있기 때문에 자바스크립트 파일이 로딩되어 실행되는 짧은 시간 동안 흰 페이지가 나타날 수 있다는 단점이 있다.

서버 사이드 렌더링(server-side rendering)을 통해 해결할 수 있다.

라우팅 라이브러리 설치

$yarn add react-router-dom

라우터 적용

src/index.js 파일에서 react-router-dom에 내장되어 있는 BrowserRouter 컴포넌트를 사용하면 된다.

BrowserRouter 컴포넌트는 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로고침하지 않고도 주소를 변경하고, 현재 주소에 관련된 정보를 props로 쉽게 조회하거나 사용할 수 있도록 해준다.

src/index.js

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

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

페이지 생성

Home.js

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

export default Home;

About.js

const About = () => {
  return (
    <div>
      <h1>소개</h1>
      <p>리액트 라우터 기초를 실습해보자.</p>
    </div>
  );
};

export default About;

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

사용자의 경로에 따라 다른 컴포넌트를 보여줄 수 있다.
react-route-dom 라이브러리가 업데이트 됨에 따라 사용방법이 조금 바뀌었다.

App.js

import React from "react";
import { Routes, Route } 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;

책에서 사용하던 exact를 사용하지 않아도 path가 중복되지 않고 각 path마다 하나의 component를 보여준다.
또한 Routes 컴포넌트로 감싸고 Route 컴포넌트를 사용해야 했다. Route 컴포넌트에서 component property가 사라지고 element={<컴포넌트 이름 />}으로 사용해야 해당 컴포넌트를 인식할 수 있다.

여러 라우팅을 매칭하고 싶은 경우에 URL 뒤에 *을 사용한다.

`<Route path="/page/* element={} />

일반 웹 애플리케이션에서는 a 태그를 사용하여 페이지를 전환한다.
리액트 라우터를 사용할 때는 a 태그를 직접 사용해서는 안된다. a 태그를 사용하면 페이지를 새로 불러오기 때문에 애플리케이션이 들고 있던 상태들을 모두 날리게 된다.

Link 컴포넌트를 사용하면 애플리케이션은 그대로 유지한 상태에서 HTML5 History API를 사용하여 페이지의 주소만 변경해준다.

App.js

import React from "react";
import { Routes, Route, Link } 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>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </div>
  );
};

export default App;

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

기존의 v5에서는 path에 배열을 넣어서 사용했다.
v6 버전으로 바뀌면서 Route 컴포넌트를 새로 생성하는 것으로 알고 있다.
추가적으로 v6에 대한 정리가 필요하다.

URL 파라미터와 쿼리

페이지 주소를 정의할 때 유동적인 값을 전달해야 할 때도 있다.
파라미터와 쿼리로 나눌 수 있다.

파라미터 예시: /profile/abel
쿼리 예시: /about?details=true

파라미터를 써야 할지 쿼리를 써야 할지 무조건 따라야 하는 규칙은 없다.
일반적으로 파라미터는 아이디, 이름을 사용하여 조회할 때 사용한다.
쿼리는 키워드를 검색하거나 페이지에 필요한 옵션을 전달할 때 사용한다.

URL 파라미터

Profile.js

import React from "react";
import { useParams } from "react-router-dom";

const data = {
  velopert: {
    name: "김민준",
    description: "리액트를 좋아하는 개발자",
  },
  gildong: {
    name: "홍길동",
    description: "고전 소설 홍길동전의 주인공",
  },
};

const Profile = () => {
  const { username } = useParams();

  const profile = data[username];

  if (!profile) {
    return <div>존재하지 않는 사용자입니다.</div>;
  }

  return (
    <div>
      <h3>
        {username}({profile.name})
      </h3>
      <p>{profile.description}</p>
    </div>
  );
};

export default Profile;

App.js

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

const App = () => {
  return (
    <div>
      <div>
        <ul>
          <li>
            <Link to="/"></Link>
          </li>
          <li>
            <Link to="/about">소개</Link>
          </li>
          <li>
            <Link to="/profile/velopert">velopert 프로필</Link>
          </li>
          <li>
            <Link to="/profile/gildong">gildong 프로필</Link>
          </li>
        </ul>
      </div>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/profile/:username" element={<Profile />} />
      </Routes>
    </div>
  );
};

export default App;

/profile/:username이라고 작성하면 useParams를 통해서 해당 값을 얻어낼 수 있다.
useParams Hook을 통해서 username 값을 얻을 수 있다.

URL 쿼리

useSearchParams Hook을 이용해서 쿼리에 대한 값을 확인할 수 있다.

URL 쿼리: about?detail=true

About.js

import { useSearchParams } from "react-router-dom";

const About = () => {
  const [searchParams, setSearchParams] = useSearchParams();

  const showDetail = searchParams.get("detail") === "true";

  return (
    <div>
      <h1>소개</h1>
      <p>라우터 기초 실습 프로젝트</p>
      {showDetail && <p> detail 값을 true로 설정하셨군요!</p>}
    </div>
  );
};

export default About;

쿼리를 사용할 때는 쿼리 문자열을 파싱하는 과정에서 get한 값은 언제나 문자열이다.
true 값을 가져왔고 비교할 때도 boolean형이 아닌 string 'true'와 값을 비교했다.

서브 라우트

라우트 내부에 라우트를 정의하는 것
App 컴포넌트에서 프로필링크를 제공했다면, 프로필링크를 제공하는 컴포넌트를 따로 만들어서 그 안에 Profile 컴포넌트를 서브 라우트로 사용해서 코드를 작성할 수 있다.

Profiles.js

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

const Profiles = () => {
  return (
    <div>
      <h3>사용자 목록:</h3>
      <ul>
        <li>
          <Link to="/profiles/velopert">velopert</Link>
        </li>
        <li>
          <Link to="/profiles/gildong">gildong</Link>
        </li>
      </ul>

      <Routes>
        <Route path=":username" element={<Profile />} />
      </Routes>
    </div>
  );
};
export default Profiles;

App.js

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/profiles/*" element={<Profiles />} />
</Routes>

path에 대한 기준이 v5와 v6가 많이 달랐다. v5에서는 /profiles/:username으로 사용해야 했고, v6에서는 :username으로 지정해야한다. v5와 v6를 동시에 처음 보다보니 헷갈리는 부분이 너무 많다.

post-custom-banner

0개의 댓글