history stack

2025. 6. 26. 00:09·Develop/Frontend

히스토리 스택은 완전한 스택은 아니다.

여기서 완전한 스택이란, 중간에 껴있는 데이터를 보기 위해서 위에 쌓인 모든 데이터를 pop해야만 하는 스택을 의미한다.

 


History API 

히스토리 API는 기본 중의 기본이다. 

실제로 리액트나 Next.js 에서 제공하는 히스토리 관련 함수들도 결국 아래의 3가지 API를 래핑해서 제공하는 것 뿐이다.

 

해당 API를 잘만 이용하면,

fetch만 이용하여 SPA를 구성할 경우 발생하는 앞으로가기/뒤로가기가 제대로 구현이 안되는 상황을 해결할 수 있다.

 

한번 히스토리 이력을 관리해보면서 해당 API에 대한 이해를 높여보자.

 


1. history.pushState 

세션 기록에 새 항목을 추가한다. 즉 히스토리 스택을 쌓는다. 

document.addEventListener("click", async (event) => {
  const creature = event.target.getAttribute("data-creature");
  if (creature) {
    event.preventDefault();
    try {
      const response = await fetch(`creatures/${creature}.json`);
      const json = await response.json();
      displayContent(json);
      // 새로운 역사 항목을 추가합니다.
      // 이는 새 페이지를 로드하는 것을 시뮬레이션합니다.
      history.pushState(json, "", creature);
    } catch (err) {
      console.error(err);
    }
  }
});

 

history.pushState의 인자에 대한 설명은 다음과 같다.

  • json: 방금 가져온 콘텐츠. 이는 역사 항목과 함께 저장되며, 나중에 사용자가 A로 네비게이션할 때 popstate 이벤트 핸들러에 전달되는 인수의 state 속성으로 포함된다.
  • "": 이는 레거시 사이트와의 하위 호환성을 위해 필요하며, 항상 빈 문자열이어야 한다.
  • creature: 이는 항목의 URL로 사용됩니다. 브라우저의 URL 표시줄에 표시되며, 페이지가 수행하는 모든 HTTP 요청에서 Referer 헤더의 값으로 사용된다. 이는 페이지와 동일 출처 정책이어야 한다.

 

2. popstate 이벤트

브라우저의 뒤로가기/ 앞으로가기 혹은 JS의 history.back() , history.forward(), history.go() 를 실행하면 발생하는 이벤트다.

// 앞으로/뒤로 버튼 처리
window.addEventListener("popstate", (event) => {
  // 상태가 제공된 경우, "시뮬레이션된" 페이지가 있으며 현재 페이지를 업데이트합니다.
  if (event.state) {
    // 이전 페이지 로드를 시뮬레이션합니다.
    displayContent(event.state);
  }
});

 

history.pushState에서 첫번째 인자로 전달한 콘텐츠를 이벤트 객체의 state 속성으로 가져와 사용하는 것을 볼 수 있다.

 

3. history.replaceState 

 현재 페이지(현재 참조하고 있는 히스토리)의 세션 기록 항목을 업데이트한다.

// 페이지 로드 시 상태를 생성하고 현재 기록을 교체합니다.
const image = document.querySelector("#photo");
const initialState = {
  description: document.querySelector("#description").textContent,
  image: {
    src: image.getAttribute("src"),
    alt: image.getAttribute("alt"),
  },
  name: "홈",
};
history.replaceState(initialState, "", document.location.href);

 

이는 당장 필요 없어 보이지만 root 세션을 위해 필요하다.

만약 사용자가 특정 url로 접속한 뒤, 다른 링크로 이동해서 pushState가 발생했다고 치자.

그 후 뒤로 가기를 바로 한다면 첫 번째 세션에는 state가 null이기 때문에 화면을 정상적으로 렌더링하기 어렵다 (2페이지에 남음)

 

이를 해결하기 위해 초기에 현재 페이지의 상태를 업데이트 해두는 것이다. (나중에 돌아올 것을 대비하여)

 


popstate 이벤트에 대한 간단한 고찰

popstate는 뒤로가기 뿐만 아니라, 앞으로 가기에도 발생한다고 했다. 

또한 직전 세션의 상태를 객체의 state 속성으로 받아온다고 했다.  

이는 즉, 실제로는 완전한 스택으로 동작하는 것이 아니라 단지 포인터가 움직이며 현재의 히스토리 세션을 관리하는 것으로 파악할 수 있다.

 

만약 뒤로가기를 하고, 이전과 다른 행위(이전에는 /page2를 방문했는데 이번에는 /page97를 방문)를 하면

영영 뒤로 가기와 앞으로 가기로는 /page2 를 방문하지 못하기 때문이다. 히스토리 스택의 세션 기록이 달라지기 때문이다.

그리고 그 세션 기록이 달라지는 방법은 간단하다.

 

앞으로가기로 이동한 게 아니라면, 현재 참조하고 있는 세션 위에 새로운 세션을 덮어써버리기만 하면 된다. 

 


아주 간단하고 명확하다.

history.push 나 history.pop, history.replace 같은 건 존재하지 않는다.

(라이브러리나 프레임워크에서 제공하는 기능과 혼동하지 말라)

 

참고로, SPA를 동작시키는 라우터는 위의 이벤트가 API를 활용하여 구현한다.

replaceState는 http 요청을 보내지는 않으면서 url을 변경시키는데, SPA 라우터는 내부적으로 replaceState로 url을 변경시키면서 이 변경된 url에 맞는 컴포넌트를 불러와 렌더링하는 방식이다. (마치 하나의 트랜잭션처럼 동작시킨다.)

만약 사용자가 브라우저에 url을 입력한다면 http 요청을 피할 수는 없는데, 이때 SPA 라우터는 요청이 들어온 path 파라미터를 인식하고 해당 path에 맞는 컴포넌트를 렌더링 시켜준다. 

'Develop > Frontend' 카테고리의 다른 글

타입스크립트의 재귀는 몇번까지 허용될까?  (0) 2025.11.10
구글 태그 매니저, GTM의 원리  (0) 2025.09.26
브라우저 캐싱과 헤더 필드  (0) 2025.06.23
Tanstack Query (React Query) 필수 지식  (0) 2025.05.11
하이드레이션 에러(Hydration Error)  (0) 2025.05.07
'Develop/Frontend' 카테고리의 다른 글
  • 타입스크립트의 재귀는 몇번까지 허용될까?
  • 구글 태그 매니저, GTM의 원리
  • 브라우저 캐싱과 헤더 필드
  • Tanstack Query (React Query) 필수 지식
ocahs
ocahs
개발 내용을 담습니다.
  • ocahs
    ocahs 개발 블로그
    ocahs
  • 전체
    오늘
    어제
    • 분류 전체보기 (47)
      • Develop (47)
        • Frontend (25)
        • Javascript (7)
        • Algorithm (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    JS
    재귀타입
    요청의 역사
    line sweeping
    promise reject
    번들러
    비트 연산자 활용 예시
    Working Set Model
    vite
    Promise
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
ocahs
history stack
상단으로

티스토리툴바