오늘은 클라이언트단에서 서버에 페이지를 요청하지 않고 주소를 이동하고 렌더링할 수 있는 history API에 대하여 공부했다.
DOM의 Window 객체는 history 객체를 통해 브라우저의 세션 기록에 접근할 수 있는 방법을 제공합니다. history는 사용자를 자신의 방문 기록 앞과 뒤로 보내고 기록 스택의 콘텐츠도 조작할 수 있는, 유용한 메서드와 속성을 가집니다.
우리는 어떤 링크를 클릭했을 때, 해당 서버의 html로 요청하는 것이 아닌, history API를 이용하여 페이지만 이동시킬 수 있다.
const $btn1 = document.createElement("button");
$btn1.textContent = "1로 이동하기";
$btn1.addEventListener("click", () => {
history.pushState(null, "", "/product/1");
});
const $btn2 = document.createElement("button");
$btn2.textContent = "2로 이동하기";
$btn2.addEventListener("click", () => {
history.pushState(null, "", "/product/2");
});
const $body = document.querySelector("body");
$body?.appendChild($btn1);
$body?.appendChild($btn2);
이 코드를 실행하면 -> 주소창에 주소가 변경되는데, 실제로 새로고침되지 않음을 알 수 있다.
이 상태에서 뒤로가는 버튼을 누를 경우, 아무것도 변경되지 않는다. 우리는 pushstate
를 통해 버튼을 누를 때 이동하도록 했지만, 뒤로가는 이벤트를 감지하도록 설정하지 않았기 때문이다.
window.addEventListener('popstate', () => {
// 다시 화면에 렌더링하도록 반영하기
route();
});
화면에 반영하기 위하여 window 객체에 popstate
이벤트리스너를 등록하고, 뒤로 가는 동작이 감지되면 그 주소에 맞게 다시 렌더링하는 과정을 거쳐야한다. 이것이 기본적인 SPA(Single Page Application) 동작 방식이다.
만약 이 상태에서 새로고침을 하면 어떻게될까? 오류가 나거나 아무것도 나오지 않는다. 기본적으로는 index.html
이 다시 실행되기 때문에, index.js
를 처음 불러올 때 route
과정을 거치도록 설정해 주어야 한다.
// index.js
// psuedocode 입니다.
import { App } from './App.js'
new App()
// App.js
class App {
constructor() {
// 처음 불러올 때 주소에 맞게끔 분기하는 역할
this.router()
// 이벤트 등록
window.addEventListener('pushstate', () => {
this.router()
});
// 이벤트 등록
window.addEventListener('popstate', () => {
this.router()
});
}
route() {
const { pathname } = location
if (pathname ==== '/') {
// render home page
} else if (pathname.indexOf('pathname') > 0) {
// render product page
} else {
// render error 404 page
}
}
그 동안 React의 useHistory
만 사용하여 기본적인 동작 원리에 대해 전혀 모르고 사용하고 있었으며, 바닐라 자바스크립트도 라우팅을 설정하여 SPA를 구성할 수 있음을 알았다. 또, 생각보다 바닐라 자바스크립트로 SPA를 구성하기에는 이것 저것 설정해주어야 될 것이 많았다. 아 이래서 프레임워크를 쓰는구나...?