vanilla js를 제외하고 vue, react, angular와 같은 프레임워크를 사용할 땐 우리는 vue-route, react-router와 같은 Third Party 라이브러리를 많이 사용한다. 그렇기 때문에 어떤 프로젝트를 하더라도 SPA (Single Page Application)에서 라우팅을 구현할 일이 없다. 그러므로 인해 라우팅을 구현해 보라는 과제를 준다면 해결하기 쉽지 않다. 애플리케이션에서 일부를 차지하는 컴포넌트가 아닌 애플리케이션 전체를 구동, 조작되어야하는 하나의 코어이기 때문이다. 이런 Third Party 시스템을 구현할 때에는 개념과 설계, 구현의 3박자가 정확하게 일치해야 하므로 쉬운 문제는 아닐 것이다.
이번 포스팅에서는 이런 경험을 바탕으로 앞으로 같은 문제를 겪는 개발자에게 도움이 되고자 Vanilla js를 사용하여 SPA (Single Page Application)에서 Routing 시스템을 구현하는 방법을 적어보자 한다.
SPA Routing
SPA는 Single Page Application으로 Front-End 개발자라면 대부분 아는 아키텍쳐이다. 또한 SPA하면 빠질 수 없는 게 라우팅 개념이다. SPA가 널리 퍼지기 전에는 사용자가 새 페이지를 탐색하기 위해서는 해당 페이지의 문서를 서버에 요청해야 한다. 이로 인해서 웹 사이트가 다시 로드되어 요청된 콘텐츠가 최종적으로 페이지에 렌더링 된다. 이런 과정에서 페이지가 렌더링 되기 전에 사용자는 항상 몇 초 동안 빈 화면을 응시할 수밖에 없었다.
최신 웹 페이지는 이런 빈 화면에 대한 시간을 줄이기 위해 라우팅과 함께 SPA (Single Page Application)를 사용한다. 위에서 언급했듯이 vue, react, angular와 같은 많은 프레임워크가 SPA가 사용자 경험을 가져다주는 이점으로 인해 라우팅을 권장하고 많은 Third Party 용 라우팅이 개발되었다.
이 라우팅으로 인해 사용자는 새 콘텐츠가 필요할 때마다 서버에 요청할 필요가 없다. 초기 애플리케이션을 로드할 때 모든 웹 사이트 콘텐츠를 로드하고 URL 경로 이름에 따라 페이지에 올바른 콘텐츠를 동적으로 표현한다. 애플리케이션은 URL 경로의 이름을 분석하고 이 이름과 관련된 콘텐츠를 분석한다. 콘텐츠는 서버가 아닌 메모리에 저장되기 때문에 애플리케이션 내에서 페이지를 스와핑하기 때문에 사용자는 빈 화면을 볼 수 없는 것이다.
SPA 라우팅을 구현하는 방법에 있어서는 두 가지 방법이 있다.
1. history (Browser History)를 사용한 방법
2. hash (Hash History)를 사용한 방법
두 차이점은 아래와 같다.
history (BrowserHistory) - history.pushState API를 활용하여 페이지를 다시 로드하지 않고 URL을 탐색 할 수 있다.
hash (HashHistory) - url 해쉬를 사용하여 전체 url을 시뮬레이트하게되며, url이 변경될 때 페이지가 다시 로드되지 않는다. 보통 url에 #이 붙는다.
history를 사용하는 방법 (Browser History Mode)
history를 사용한 방법은 history라는 API를 사용하는 방법이며, 가장 보편적인 방법이다.
history API의 pushstate와 window 객체의 popstate 이벤트를 이용하는데 history.pushState를 통하여 새 데이터 전달을 위한 상태, 제목, url을 지정할 수 있다.
window.history.pushState({ data: 'some data' },'Some history entry title', '/some-path')
Browser History의 url의 형태는 site/some-path와 같이 표현되지만 이 방법은 서버 측 지원이 일부 필요하다. 예를 들어 http://domain.com/site/another-path와 같이 존재하지 않는 경로로 접속할 경우 오류를 출력한다. 이런 문제를 해결하고 대체할 url은 서버에서 지정해야 한다.
hash를 사용하는 방법 (Hash History Mode)
hash를 사용하는 방법은 # 앵커를 통해 이동하는 방법으로 site/#some-path와 같이 url이 표현된다. 보통 정적 페이지에서 사용되며 블로그의 주 제목을 클릭 후 앵커 이동 시 url에 #이 붙는 모습을 볼 수 있다.
현재 url의 hash는 window.location.hash를 통하여 확인 할 수 있으며, 라우팅 시스템을 구축할 경우 이 window.location.hash를 이용하여 라우팅을 변경할 수 있다. hash가 변경될 때마다 popstate와 같이 hashchange 이벤트가 발생하기 때문에 hashchange를 통하여 라우팅을 변경할 수 있다.
history 일 경우엔 historyRouterPush를 통해서 history PushState API를 사용하고 있으며 이후 template를 렌더링하고 있으며, onpopstate를 통하여 브라우저 뒤로 가기 또는 앞으로 가기에 따라 히스토리를 관리할 수 있다. hash인 경우 hashchange 이벤트를 통하여 hash가 변경되는 것을 감지하고 hash에 따라 페이지를 렌더링하고 있다.
index.js에서는 각 html 태그에 이벤트를 생성하고 이벤트에 따라 라우팅을 변경하는 코드가 존재한다. 또한, 최초에 initialRoutes를 통해 기본 페이지를 렌더링하는 것을 볼 수 있다.
실행
작성이 모두 끝났으면 개발 모드로 실행을 해보자.
npm
script
npm run start
yarn
script
yarn start
빌드
빌드는 아래와 같이 실행할 수 있다.
npm
script
npm run build
yarn
script
yarn build
빌드를 실행하면 dist 폴더가 생기고 빌드 결과를 확인할 수 있다. 이 빌드 결과를 실행해 보기 위해서는 http-server와 같은 모듈을 설치하여 실행이 가능하다.
npm
script
npm install -g http-server
yarn
script
yarn add global http-server
이 후 dist 경로에서 http-server를 입력하면 가상 서버를 실행 할 수 있다.
사실 라우팅이라는 개념은 단순하다.
특정 window 객체를 잘 활용하고 개념만 안다면 누구나 만들 수 있는 시스템이지만 우리는 보통 만들어져있는 모듈을 많이 쓰기 때문에 처음에는 약간 생소할 수도 있다. 생각해보면 Front-End 개발에 있어서 라우팅은 이제 중요한 부분을 차지하고 있는데도 불구하고 그저 자연스럽게 필요하니 가져다 쓰는 모듈에 불과했었다. 개념을 알지만 실제로 구현해보는 것과는 차이가 있다는 것을 다시 한 번 느끼는 기회였다.
라우팅을 만들어 볼 수 있는 기회가 생긴 것에 감사하고 필요한 이에게 도움이 되었으면 한다.