본문 바로가기
web/react

[React] SPA, Routing, React-router, History, Hooks

by fien 2021. 6. 16.

SPA(Single Page Application)

SPA는 단어 그대로 한 개의 페이지로 이루어진 애플리케이션을 의미합니다. SPA 이전의 애플리케이션은 페이지 전환이 발생할 때마다 서버에 해당 페이지를 요청하는 SSR(Server-side Rendering)방식을 사용했습니다. 이러한 방식은 페이지가 다시 렌더링 될 때까지 사용자가 빈 화면을 보며 대기해야 하므로 사용자 경험에도 좋지 않을 뿐더러 변하지 않는 Header나 Footer도 다시 렌더링하기 때문에 비효율적입니다.

SPA는 페이지 전환이 일어날 때마다 서버에 요청을 보내지 않습니다. 대신, 최초로 애플리케이션을 로드하는 시점에 모든 정적 리소스를 받습니다. 이후 사용자 요청에 따라 필요한 데이터만 서버에 요청하고 동적으로 페이지를 변경합니다. CSR(Clinet-side Rendering)

Routing

일반적으로 라우팅은 네트워크 상에서 요청(URL)에 알맞은 경로를 찾는 과정입니다. 라우팅 과정을 통해 서버에 페이지 요청을 하고 응답을 받아 페이지를 보여주는 것입니다.

SPA에서는 URL에 따라 서버에 요청을 보내는 대신 브라우저가 초기에 받은 스크립트를 브라우저가 실행하여 페이지를 로드하고 HTML5의 Hisotry API를 이용해 history 관리를 합니다.

브라우저의 히스토리
사용자가 브라우저 상에서 오고 간 기록을 의미한다. 이력을 기록되어있어 뒤로가기 버튼을 눌렀을 때 빠르게 페이지가 로드된다.

React Router (react-router)

React는 공식 라우팅 라이브러리가 존재하지 않는 대신 써드파트 라이브러리가 존재합니다. 그중에서 react-router는 가장 많이 사용되는 라이브러리입니다. react-router는 CSR를 간단하게 구현할 수 있는 네비게이션 컴포넌트를 제공할 뿐 아니라 SSR를 위한 기능도 포함하고 있습니다.

# react-router 설치
npm install react-router-dom  // web 용

주요 컴포넌트 (BrowserRouter, Switch, Route, Link)

react-router가 제공하는 BrowserRouter, Switch, Route, Link 컴포넌트를 이용하면 기본적인 라우팅 기능을 구현 할 수 있습니다.

BroswerRouter는 라우팅 기능을 위한 핵심 컴포넌트로써 내부적으로 History API를 사용합니다. BrowerRouter가 애플리케이션의 루트 컴포넌트(App.js)를 감싸게 해줍니다.

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

import App from './App';

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

SwitchRouter 를 이용해 URL과 컴포넌트를 매핑 할 수 있습니다.

Switch는 순차적으로 route를 찾기 때문에 "/" 가 최상위에 위치하면 모든 요청이 "/"에 매핑됩니다. 따라서 Route의 순서를 바꾸거나 exact 속성을 추가해 pathname과 정확히 일치 할 때만 Route를 렌더링 하도록 합니다.

// App.js
import React from "react";
import ReactDOM from "react-dom";
import { Switch, Route } from "react-router-dom";

import Homefrom './Home';
import About from './About';
import Contact from './Contact';

const App = () => {
  return (
    <>
      <Switch>
          <Route exact path="/" component={Home} />
        <Route exact path="/about" component={About} />
        <Route exact path="/contact/:id" component={Contact} />
      </Switch>
    </>
  );
}

Link 컴포넌트를 통해 페이지 네비게이션이 가능합니다. 화면에는 a 태그로 렌더링 됩니다.

// Home.js
import React from "react";
import ReactDOM from "react-dom";
import { Link } from "react-router-dom";

const Home = () => {
  return (
    <>
      <Link to="/about">about</Link>  // <a href="/about">about</>
    </>
  );
}

history

react-router는 세션 히스토리를 관리하기 위해 history 패키지에 의존하고 있습니다. 그리고 개발자가 history에 접근할 수 있도록 Route 컴포넌트에 history, location, match props를 전달해줍니다. 이 props를 통해 히스토리를 확인하거나 변경할 수 있습니다.

history 객체는 아래와 같은 속성과 메소드를 가지고 있습니다.

- length: session history stack에 있는 항목 수
- action: 현재의 action (PUSH, REPLACE, POP)
- location: 현재 주소
    pathname: URL path
    search: URL query string
    hash: URL hash fragment
    state: 현재 주소(페이지)와 관련된 사용자 정의 정보
- push(path, [state]): 브라우저의 세션 히스토리 스택에 상태를 추가
- replace(path, [state]): 세션 히스토리 스택의 최신 항목을 변경
- go(n): 현재 페이지를 기준으로 상대 위치로 이동한다.
- goback(): = go(-1)
- goForward(): = go(1)
- block

pop은 사용자가 뒤로가기 버튼을 누르는 것과 같이 페이지를 이동할 때 발생합니다.

location

location 객체는 현재 페이지의 정보로 history.location에 해당합니다.

url : localhost:3000/contact/id123?name=fien#title
- pathname: URL path # contact/id123
- search: URL query string  # ?name=fien
- hash: URL hash fragment  # #title
- state: 현재 주소(페이지)와 관련된 사용자 정의 정보

match

match 객체는 Route 컴포넌트의 path와 실제 URL에 대한 정보를 담고 있습니다.

<Route exact path="/contact/:id" component={Contact} />
- path: route에서 정의한 path # /contact/:id
- url: 실제 URL # /contact/id1234
- params: url에 담긴 파라미터를 json object로 반환 # { id: "id1234"}
- isExact: 전체 URL이 일치하는지 여부를 true/false로 반환 # true

Hooks (useHistory, useLocation, useParams, useRouteMatch)

컴포넌트가 계층화 되면 하위 컴포넌트에 history 객체를 계속 넘겨주거나 withRouter HoC 로 컴포넌트를 감싸줘야 했습니다. 대신 관련 hook을 이용하면 어디서든 관련 객체에 쉽게 접근할 수 있습니다.

history ⇒ useHistory

import React from "react";
import ReactDOM from "react-dom";
import { useHistory } from "react-router-dom";

const ContactInfo = () => {
  const history = useHistory();
  return (
    <>
      <button 
        onClick={() => {
            history.push('/');
          }}
      > 
        home 가기 
      </button>  
    </>
  );
}

location ⇒ useLocation

match.params ⇒ useParams

match ⇒ useRouteMatch

useRouteMatch 같은 경우는

  1. 파라미터를 전달하지 않으면 현재 match 객체를 반환
  2. Route 컴포넌트의 프로퍼티(path, strict, sensitive, exact)를 전달하면 현재 위치와 일치하면 해당 match 객체를 반환하고 아니면 null 반환 → Route를 렌더링 하기 전에 데이터를 접근하는데 사용한다.
const match = useRouteMatch({
    path: "/contact/:id",
    strict: true,
    sensitive: true,
});

const match = useRouteMatch("/contact/:id");

댓글