Javascript를 이용하여 route 구현하기 with pjax

sun202x·2022년 9월 5일
1

Javascript

목록 보기
1/2

routing 라이브러리의 도움 없이 javascript만으로 routing 기능을 구현해보자. 여러가지 방법이 있겠지만 그중에서도 pjax 방식을 사용하여 routing 기능을 구현해 볼것이다.

pjax

pjax(pushState + ajax)는 몇가지 routing 방식의 개선을 거듭하여 이전 방식인 hash 방식의 단점인 SEO를 개선하기 위해 나온 방식이다. HTML5의 history API인 pushState와 popstate 이벤트를 이용하여 구현할 수 있다. pushState와 popstate는 IE10 이상부터만 작동한다고 한다.

<nav>
  <ul>
    <li><a href="/home"></a></li>
    <li><a href="/service"></a></li>
  </ul>
</nav>

위 html 코드를 보면 정통적 링크 방식과 동일한 형태로 href 속성의 값이 구성되는 것을 확인할 수 있다.
풀 소스는 참조한 페이지인 poiemaweb에서 확인하는 것으로 하고 핵심 로직만 추려봤다.

const routes = [
  { path: '/', component: Home },
  { path: '/service', component: Service },
  { path: '/about', component: About },
];

const render = async path => {
  try {
    const component = routes.find(route => route.path === path)?.component || NotFound;
    root.replaceChildren(await component());
  } catch (err) {
    console.error(err);
  }
};

navigation.addEventListener('click', e => {
  // server 요청을 막기위해 preventDefault 호출
  e.preventDefault();

  const path = e.target.getAttribute('href');
  window.history.pushState({}, null, path);
  render(path);
});

window.addEventListener('popstate', () => {
  render(window.location.pathname);
});

render(window.location.pathname);

pjax 방식은 navigation의 클릭 이벤트를 캐치하고 preventDefault()를 호출하여 서버요청을 방지한다. 그리고 history api의 pushState 메서드를 호출하여 url을 변경하게 된다.

pushState

pushState 메서드는 브라우저의 세션 기록 스택에 상태를 추가한다. interface는 아래와 같다.

history.pushState(/* state */, /* title */, /* url */);
  • state
    • 세션 기록 항목에 연결할 상태 객체이며, popstate 이벤트 발생시 전달되어진다.
  • title
    • 현재는 브라우저 상에서 쓰지 않는 정보이며(현재날짜 기준), 상태에 대한 짧은 제목정도로 쓰인다.
  • url(optional)
    • 새로운 세션 기록 항목의 URL이며, pushState를 통해 주어진 URL은 탐색되지 않는다.
      • 단, 새로고침 시에는 탐색되어진다.
    • 새 URL은 같은 출처를 가져야 하며, 그렇지 않다면 예외가 발생한다.
    • 지정하지 않은 경우 현재 문서의 URL을 사용한다.

새로운 세션 기록 항목을 생성하고 활성화 한다는 점이 hash 방식과 비슷하지만 pushState는 몇가지 장점을 가지고 있다.

hashpushState
URL 변경 방식hash만 변경 가능동일 출처만 같다면 URL 변경 자유
새 세션 기록 생성을 위한 URL 변경 방식현재 hash 값과 다른 hash 값 설정pushState 호출(URL 변경 필수아님)
임의의 데이터 전달 방식데이터를 인코딩 하여 짧은 문자열로 전달세션 상태 항목을 통해 전달

pjax 방식은 서버 렌더링 방식과 ajax 방식이 혼재되어 있으므로 서버의 지원이 필요하다.

const express = require('express');
const path = require('path');

const app = express();
const port = 5004;

app.use(express.static(path.join(__dirname, 'public')));

// 브라우저 새로고침을 위한 처리 (다른 route가 존재하는 경우 맨 아래에 위치해야 한다)
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'public/index.html'));
});

app.listen(port, () => {
  console.log(`Server listening on http:/localhost:${port}`);
});

브라우저 새로고침 시 서버는 index.html을 전달하고 클라이언트는 window.location.pathname를 참조해 다시 라우팅한다.

react-router-dom은 어떻지?

react-router-dom은 정말 pushState를 사용하여 SPA를 구현하는지 궁금했다.
본격적으로 파고 들기 위해 react-router-dom 프로젝트를 cloning 해보았는데, 재미있게도 하나의 프로젝트로 묶여 있는 것을 확인할 수 있었다.

npmjs에서 Github Repository를 따라가보면,

react-router라는 프로젝트로 이동된다.

안에 내용을 분석해 보니 이 react-router 프로젝트는 packages라는 폴더를 가지고 있고, 대외적으로 패키지 매니저를 통해 설치되는 라이브러리 명들을 이 pakcages 폴더 아래에서 찾아 볼 수 있었다.

위 이미지에서 살펴보면, react-router-dom, react-router-native, react-router 등이 있는 것을 확인할 수 있다. 해당 패키지들이 실제로 번들링 되어서 올라가는 것으로 확인된다.

pushState를 사용한 로직을 검색해 보니, packages 폴더 아래 router 라는 패키지 아래에 history 파일 내에서 존재 하는 것을 확인할 수 있었다.

Reference

profile
긍정적으로 살고 싶은 개발자

0개의 댓글