본문 바로가기

프론트엔드/React

[ React ] [Chapter 9] 단일 웹 SPA

728x90

단일 웹 애플리케이션 SPA

 

SPA(Single Page Application)은 한 개의 페이지로 이루어진 어플리케이션이다.

전통 적인 웹 페이지랑 다른 구성이다.

전통적인 웹

기존에 사용자가 다른 페이지로 이동할 때마다 새로운 html을 받아오고 페이지를 로딩할 때 마다 서버에 리소스를 전달 받아 해석 한 뒤에 화면에 보여주었다.
그런데 요즘 웹은 제공되는 정보가 많기 때문에 새로운 화면을 보여 주어야 할 때마다 서버측에서 모든 뷰를 준비한다면 성능상의 문제가 발생할 수 있다.

그래서 React는 View랜더링을 사용자 브라우져가 담당하고, 사용자와의 인터랙션이 발생하면 필요한 부분만 자바스크립트를 사용하여 업데이트 해준다. 

바뀐 웹 SPA

 

단일 페이지 SPA 주의점

 

1.자바스크립트에서 브라우저로 페이지 전환 요청을 보낼 수 있어야 한다.
   - 단, 브라우저는 서버로 요청을 보내지 않아야 한다.

2.브라우져의 뒤로가기와 같은 사용자의 페이지 전환 요청을 자바스크립트에서 처리할 수 있어야 한다.
    -단, 브라우져는 서버로 요청을 보내지 않아야 한다.

이 조건을 만족 시켜주는 API가 있다.

1.pushState 함수
2.replaceState 함수
3.popstate 이벤트

단일 페이지 SPA 코드 작성

 

필자는 VScode로 코드를 작성하였고, create-react-app로 프로젝트를 만들었다.

버전 16이상 부터는 function을 제공 하기 시작했다 버전16맞나? 쨌든 필자는 펑션으로 했다.

 

import React, { useEffect } from 'react';
import './App.css';

function App() {
  useEffect(() => {
    window.onpopstate = function (e) {
      console.log(`location: ${document.location}, state: ${e.state}`);
    };
  }, []);
  return (
    <div className="App">
      <button onClick={() => window.history.pushState('v1','','/page1')}>
        page1
      </button>
      <button onClick={() => window.history.pushState('v2','','/page2')}>
        page2
      </button>
    </div>
  );
}

export default App;

코드 설명
버튼이 두개가 있는데 page1,page2로 라우팅 하려고 하는 코드이고, pushState 함수로 브라우져에게 알려주는 것이다.
브라우져에서 페이지 전환 요청이 왔을 때 자바스크립트가 알게 하기 위해서
onpopstate 이벤트 핸들러를 사용 했다.
onpopstate 이벤트를 등록한 useEffect는 이벤트 핸들러를 등록하거나, API를 호출하는 등의 부수효과를 발생 시킬 때 호출 되는 리액트 함수이다. 자세한 설명은 나중에 알고 넘어가자
여기서는 onpopstate 이벤트 핸들러를 사용 하기 위해 사용 했다는 정도만 이해하고 넘어가자

pushState 함수중 매개변수들이 있는데
첫번째 매개변수는 데이터를 의미하는 값인데 onpopstate 이벤트 객체에서 state 라는게 있다(e.state) 이 state가 pushState에서 첫번째 매개변소에서 넘겨주는 값(v1)이다.
두번째 매개변수는 title인데 중요하지 않다
세번째 매개변수는 URL이다. 버튼을 클릭하면 브라우져 주소창에 page1or2이 보일거다
로고도 보면 해당 페이지가 찍힌게 보일거다
또 한 서버를 가지 않고 브라우져와 자바스크립로만 통신 하는걸 알 수 있을거다.

단일 페이지 SPA react-router-dom

react-router-dom 을 이용해 더욱 간단하게 편리하게 개발을 해보자

react-router-dom 라이브러리는
여러가지 편의 기능을 제공 해주는데 그 중, 현재 페이지 정보에 따라서 어떤 컴포넌트를 랜더링 할지 해주는 기능도 있고, 현재 상태 정보를 관리를 자동으로도 해준다.

또 한 코드 스플리팅(분할)이라는 기능도 제공 해준다.
보통 코드를 분할을 하지 않고 SPA을 개발을 하면 자바스크립트 하나의 파일에 모두 뭉쳐 있어서 초기 요청을 할때 랜더링이 오래 걸릴 수 있다. 그래서 분할을 하는건데 분할을 쉽게 관리 해준다

먼저 react-router-dom 설치 부터 한다.

npm install react-router-dom

예시 홈이라는 페이지가 있고 홈에서 page1 , page2 를 갈수있고, page2 에서 test1, test2 갈수 있으며 test1, test2 갈때는 파라미터를 사용해서 이동 하는 라우터를 해보겠습니다.

먼저 App.js에 BrowserRouter를 사용해 현재 페이지 상태값을 관리 해줍시다.

import React from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';
import Home from './Home';
import Page1 from './Page1';
import Page2 from './Page2';

function App() {
  return (
    <BrowserRouter>
      <div>
        <Link to="/">홈</Link>
        <br />
        <Link to="/page1">page1</Link>
        <br />
        <Link to="/page2">page2</Link>
        <br />
        <Route exact path="/" component={Home} />
        <Route path="/page1" component={Page1} />
        <Route path="/page2" component={Page2} />
      </div>
    </BrowserRouter>
  );
}

export default App;

<BrowserRouter> 컴포넌트에서 현재 페이지의 상태값을 관리 해줍니다.

<Link> 컴포넌트를 사용을 하면 이것을 클릭 했을 때 to="url"에 해당 하는 경로로 이동을 시켜줍니다.

<Route> 컴포넌트를 사용 하면 path="url" 정보에 따라서 어떤 컴포넌트를 렌더링 할지 결정을 해줍니다.
현재 페이지가 /page1 일 때는 Page1 컴포넌트를 랜더링 할거고 /page2 일 때는 Page2 컴포넌트를 랜더링 할거라는 이야기이다.
근데 path 앞에 exact 라는게 있는데 만약 exact가 없다고 가정 하고(path="/") 현재 페이지가 /page1 일 때 Page1도 랜더링 되고 Home도 랜더링이 된다. 그래서 Home 컴포넌트를 랜더링 하지 않게 하기 위해서 exact를 넣어준다.

랜더링 해줄 홈 컴포넌트 작성

import React from 'react';

function Home(props) {
    return (
        <div>
            <h2>여기는 홈페이지입니다. 원하는 페이지 클릭</h2>
        </div>
    );
}

export default Home;

랜더링 해줄 Page1 컴포넌트 작성

import React from 'react';

function Page1(props) {
    return (
        <div>
            <h2>여기는 page1</h2>
        </div>
    );
}

export default Page1;

마지막으로 여기서는 색다르게 랜더링할 Page2 컴포넌트 작성

import React from 'react';
import { Route, Link } from 'react-router-dom';

function Page2({ match }) {
    return (
        <div>
            <h2>여기는 Page2</h2>
            <Link to={`${match.url}/test1`}>test1</Link>
            <br />
            <Link to={`${match.url}/test2`}>test2</Link>
            <br />
            <Route path={`${match.url}/:testId`} component={Test} />
        </div>
    );
}

function Test({ match }) {
    return <h2>{`${match.params.testId} <- testId`}</h2>
}

export default Page2;

보면 Page({ match }) match 속성값을 사용 하고 있습니다.
Route 컴포넌트로 랜더링을 하면 해당 컴포넌트의 속성값으로 match라는 속성값을 넣어준다.
match 안에는 url 속성이 있는데 이게 Page2 컴포넌트가 랜더링 될 당시에 매치되었던 url의 일부분을 의미합니다.

Page 컴포넌트의 Route태그의 path 보면 :testId 가 있는데 :은 파라미터를 사용하겠다고 선언 한것입니다.

Test 함수형 컴포넌트를 보면 match.params.testId 가 보일겁니다. params 객체 안에 모든 파라미터가 들어갑니다.

   관련 글

 

저의 글을 읽어 주셔서 감사합니다. 오늘도 즐거운 하루 보내세요.

저의 글이 조금이나마 도움이 되셨다면 로그인이 필요 없는 공감♥ 한번 꾸욱 눌러주세요 하하~

728x90