본문 바로가기
JS

Vanilla JS로 SPA router 기능 만들기

by 사과넹 2024. 4. 14.
반응형

 

정적 웹페이지를 배포해야 할 일이 생겼고, 내용도 굉장히 적기 때문에 프레임워크는 고사하고, 모듈러 사용도 과하다고 판단했다.
항상 프레임워크에서 개발하거나 최근 프레임워크없이 vanillaJS에 모듈러만 사용하여 개발한 경험이 있는데, 이번엔 모듈러까지 없이 하려니 도저히 가닥이 잡히지 않았다.

 

일단 내가 배포에 대한 지식이 전무하다보니 배포를 하려면 프로젝트에 무엇이 필요한지 몰랐다. 그래서 일전에 정적 페이지를 배포해준 동료에게 질문을 했다.

 

정적 페이지를 AWS S3에 배포하셨을 때, 프로젝트를 직접 S3에 업로드하셨나요?

아니요, 적어주신 build 명령어로 build했고, build 결과물을 S3에 업로드했어요.

 

이 답변을 듣고 나니 단순히 컴파일하는 과정이라고 생각했었던 build의 정의가 모호해졌다.

 

그리고 build의 정의를 찾아봤다.

 

소스 코드를 컴퓨터가 실제로 실행할 수 있도록 독립적인 파일 형태로 변환하는 과정이며 그 결과이다.

 

build의 정의를 찾아보자 순수 html, css, js는 브라우저 환경에서 동작하기 때문에 컴파일하는 과정을 굳이 만들지 않으면 build를 할 필요가 없지 않나라는 생각을 했다.

 

나의 가설을 바로 적용하기엔 확신이 없었기 때문에 또다른 동료에게 질문했다.

 

html, css, js는 빌드없이도 브라우저에서 동작하니 S3에 빌드를 하지 않은 상태로 올린다면 정상적으로 배포가 될까요?

빌드했을 때처럼 경로가 잘 설정되어 있고, 도메인이 index.html로 연결만 잘되면 문제 없지 않을까요?

 

의문형이긴 했지만 뭔가 긍정적인 답변이였기 때문에 자신감이 생겼고, 갑자기 예전에 모듈러없이 정적 페이지를 github에 올려서 github에서 제공하는 배포 서비스로 페이지를 띄웠던 것이 생각나서 충분히 가능할 것 같다는 확신을 했다. 그리고 github에서 간단하게나마 테스트를 하기로 했다.

 

테스트는 성공적이였고, 동료의 말대로 최상위에 index.html 만 잘 연결해주면 AWS 배포 때도 문제 없다고 판단했다.

 

일단 프로젝트 내에서 path가 다른 페이지들을 몇 개 만들어야 했기 때문에 vanillaJS로 router 기능을 만들어줘야 했다.

 

이 때 절실히 느낀 프레임워크의 편리함.. 거기선 그냥 라우트 배열에 객체 하나만 생성해주면 끝나는 거였는데..

 

그러다가 검색해보며 얻은 방법은..

 

window 객체의 location 속성에는 hash라는 값을 가진다.

window.location.hash

 

흔히 anchor 태그의 id 속성을 활용해 스크롤 이동을 하곤 하는데, 이 때 스크롤만 이동되는 것이 아닌 url에 #id 형식으로 url path가 붙는다. 이 것이 바로 hash 값이 된다.

 

그래서 url 상에 #뒤에 값이 없으면 빈 문자열을 반환한다.

 

따라서 id가 있는 anchor을 누르면 url path가 추가 및 변경될 것이고, 이 때 innerHTML 를 원하는 내용을 바꿔주면 마치 경로가 이동하는 듯한 효과를 낸다.

 

A anchor를 누르면 innerHTML = A template 이런 식으로 구현하였다.

 

<ul class="menu">
  <li><a href="#/first-page">첫번째 페이지</a></li>
	...
</ul>
import { firstPage } from '../firstPage.js'; // 렌더링할 내용은 다른 파일로 분리했다.

const body = document.querySelector('body');
const savedBody = body.innerHTML; // 메인을 기억하기 위해 저장해놓는다.

const pages = {
  init: () => body.innerHTML = savedBody,
  firstPage: () => body.innerHTML = firstPage,
	...
}

 

hash에 맞는 페이지를 보여주는 함수는 switch문으로 작성했고, default를 빈 문자열로 둬 메인 페이지는 따로 hash 주소 구성을 하지 않았다.

 

const renderingPage = () => {
  const currentPage = window.location.hash.replace('#/', '');

  switch (currentPage) {
    case 'first-page':
      pages.firstPage();
      break;
		...
    default:
      pages.init();
  }
}

 

주소의 hash의 변경은 아래의 이벤트 리스너를 통해 감지했다.

 

window.addEventListener(’hashchage’, renderingPage);

 

최초 진입 시엔 메인으로 잘 이동하지만 뒤로가기, 새로고침을 통한 렌더링을 하기 위해 페이지 렌더링 함수를 호출해야 한다.

 

 

728x90
반응형