본문 바로가기

공부/react

라이브러리 없이 라우터(Router) 만들기

반응형

 

오늘은 `react-router-dom` 라이브러리 없이 라우터를 만들어보면서 라우팅 시스템에 대해 공부해보려고 합니다.

 

라우팅 시스템 📖

라이브러리 없이 라우팅 시스템을 개발하기 위한 2가지 방법이 있습니다.

 

첫 번째 Fragment 해시(해시 라우터)를 이용하는 방법과

두 번째 history API(브라우저 라우터)를 이용하는 방법이 있습니다.

 

history API는 `pushState``popState`메서드가 지원되면서 Fragment 해시를 쓰지 않고도 라우팅 시스템을 개발할 수 있지만 모든 브라우저에서 지원되는 라우터를 개발하기 위해선 아직까지 Fragment 해시 라우터를 사용해야 합니다.

 

이 포스팅에선 Fragment 해시를 이용해 라우터 구현 방법에 대해 소개하겠습니다.

 

Fragment 해시(#)

Fragment 해시란 URL에 포함될 수 있는 해시로 시작하는 부분으로, 웹 페이지의 특정 섹션을 식별하는 것이 목적입니다.

 

예를 들어 `www.naver.com#/mingo`에서 `mingo`가 Fragment 해시이고 해시 변경이 일어났을 때 브라우저는 서버에 요청하지 않고 페이지가 새로고침 되지 않습니다.

또한 새로고침이나 리다이렉션이 발생했을 때, 해시 이전의 도메인 주소로 요청을 보내므로, 오류가 발생하지 않고 올바른 entry point로 요청합니다.

 

 

라우터 기능 📖

라우터를 만들기 위해 라우터의 기본적인 기능에 대해 먼저 알아보겠습니다.

  • 애플리케이션의 경로 목록들을 저장 (URL을 DOM 구성 요소에 매핑)
  • 현재 URL이 변경되면 페이지 콘텐츠를 해당 URL에 매핑된 요소로 변경
  • URL에 path parameter나 query string을 식별

 

1. SPA 구축 📖

라우터가 잘 작동하는지 테스트하기 위해 간단한 SPA를 구축합니다.

<body>
   <header>
       <a href="#/">home</a>
       <a href="#/mingo">melon</a>
   </header>
   <main></main>
   <script type="module" src="index.js"></script>
</body>

 

`home`과 `mingo`페이지를 보여줄 간단한 text도 준비합니다.

// index.js
const container = document.querySelector("main")
const pages = {
 home: () => container.innerText = '메인 페이지',
 mingo: () => container.innerText = '밍고 페이지',
}

 

2. 라우터 구현 📖

Fragment 해시랑 pages 함수를 연결하는 라우터 기능을 구현합니다.

// router.js
export default function createRouter() {
  const routes = [];

  const router = {
    addRoute(fragment, component) {
      routes.push({fragment, component});
      return this;
    },
  
    start() {
      const checkRoutes = () => {
        const currentRoute = routes.find(route => route.fragment === window.location.hash);
        currentRoute.component();
      }

      window.addEventListener('hashchange', checkRoutes);
      checkRoutes();
    }
  };

  return router;
}

 

routes

애플리케이션 경로 목록을 저장하고, URL을 구성 요성에 매핑한 객체들을 저장하는 배열입니다.

const routes = [];

 

router

라우터를 구현한 객체입니다.

const router = {
  ...
}

 

addRoute

routes 배열에 URL과 구성 요소들을 매핑해 저장하기 위한 메서드입니다.

addRoute(fragment, component) {
  routes.push({fragment, component});
  return this;
},

 

start

URL 변화를 감지하는 메서드입니다.

start() {
  const checkRoutes = () => {
    const currentRoute = routes.find(route => route.fragment === window.location.hash);
    currentRoute.component();
  }

  window.addEventListener('hashchange', checkRoutes);
  checkRoutes();
}

 

router를 SPA에 연결

// index.js
import createRouter from "./router.js";

const container = document.querySelector("main")
const pages = {
 home: () => container.innerText = "메인 페이지",
 mingo: () => container.innerText = "밍고 페이지",
}

const router = createRouter();

router.addRoute("#/", pages.home)
 .addRoute("#/mingo", pages.mingo)
 .start();

 

3. navigate 📖

기존 `<a>`태그 없이 페이지 이동이 필요한 경우에도 라우터가 이를 감지해 Fragment 해시값을 바꿔줘야 합니다. `<a>`태그를 `<button>`태그로 바꾸고 데이터 속성을 이용해 경로를 추가해 줍니다.

<body>
   <header>
     <button data-navigate="/">home</button>
     <button data-navigate="/mingo">mingo</button>
   </header>
   <main></main>
   <script type="module" src="index.js"></script>
</body>

 

 

버튼을 클릭했을 때, 클릭된 버튼의 데이터 속성에 있는 경로들을 라우터에 전달해 주는 이벤트리스터를 추가하고, 전달받은 Fragment 해시로 URL 해시값을 바꿔주는 `navigate`메서드를 추가합니다.

  // index.js
  ...
  window.addEventListener("click", event => {
    if(event.target.matches("[data-navigate]")) {
      router.navigate(event.target.dataset.navigate);
    }
  });

  // router.js
  const router = {
    ...
    navigate(fragment) {
      window.location.hash = fragment;
    }
  }

 

navigate

 

location replace

navigate 메서드의 `location replace`옵션도 간단하게 추가할 수 있습니다.

  // router.js
const router = {
...
navigate(fragment, replace = false) {
  if (replace) {
    const href = window.location.href.replace(window.location.hash, "#" + fragment);
    window.location.replace(href);
  } else {
    window.location.hash = fragment;
  }
}
}

 

현재 URL의 해시 부분을 클릭한 fragment(해시)로 변경 후 현재 브라우저의 URL을 href로 변경 (이때 브라우저 히스토리에 기존 URL이 기록되지 않음. 뒤로 가기 X)

const href = window.location.href.replace(window.location.hash, "#" + fragment);
window.location.replace(href);

replace

 

회고 🧐

라우터를 직접 구현해 보면서 라우팅 시스템에 대해 이해를 할 수 있어서 좋았습니다.


출처 🏷️

https://fe-developers.kakaoent.com/2022/221124-router-without-library/

 

반응형