History API와 Next Router의 beforePopState

Y·2023년 5월 28일
0

History API

DOM의 Window 객체는 history 객체를 통해 브라우저의 세션 기록에 접근할 수 있는 방법을 제공한다. history는 사용자를 자신의 방문 기록 앞과 뒤로 보내고 기록 스택의 콘텐츠도 조작할 수 있는, 유용한 메서드와 속성을 가진다.

history API는 브라우저 세션 기록을 조작할 수 있는 메서드를 담고 있는 객체이다.

브라우저에 내장된 히스토리는 스택의 형태로 구현되어있다.

속성

  • length (read-only): 현재 로드된 페이지를 포함한 히스토리 스택에 저장된 개수

  • scrollRestoration: history 이동 시 페이지가 원래 스크롤되었던 위치로 돌아갈지의 여부를 설정할 수 있다

    • auto: 기본값 / 스크롤한 페이지의 위치로 복원된다

    • manual: 페이지의 위치는 복원되지 않는다. 해당 위치로 수동으로 스크롤해야한다.

  • state (read-only): 히스토리 스택의 최상단의 state를 반환한다.

메서드

history.back()

history.go(-1)과 동일하다. 이전으로 돌아갈 수 있는 메서드로, 브라우저 백버튼을 누른것과 동작이 동일하다.

history.forward()

history.go(1)과 동일하다. 앞으로 갈 수 있는 메서드로, 브라우저 앞으로가기 버튼을 누른것과 동작이 동일하다.

history.go(index)

특정 index로 갈 수 있다. 존재하지 않는 index를 명시한 경우, 아무 효과가 없다. index 없이 go()만 하면 현재 페이지를 리로드한다.

history.pushState(stateObj, title, URL)

주어진 데이터, 지정한 제목 및 URL을 기록 스택에 넣는다.

  • stateObj : 상태 값을 나타내는 것으로 브라우저에서 앞 뒤로 갈 때, 넘겨줄 데이터 / 직렬화 가능한 객체 사용

  • title : 변경할 브라우저 제목 (변경을 원하지 않으면 null) / 사파리 브라우저에서만 동작

  • url : 변경할 브라우저 URL

history.replaceState(stateObj, title, URL)

스택의 제일 최근 항목을 주어진 데이터, 지정한 제목 및 URL로 대체한다.

history.pushStatehistore.replaceState는 페이지 URL을 변경할 때 HTTP 요청을 보내지 않고, 페이지를 새로고침하지 않는다.
반면에 window.location.href = “url”로 이동하는 경우에는 HTTP 요청을 하고, 새로고침을 한다.
( 만약 url이 hash fragment이면 새 HTTP요청을 하지 않고 연관된 앵커로 스크롤된다. )

popstate event

Window 인터페이스의 popstate 이벤트는 사용자의 세션 기록 탐색으로 인해 현재 활성화된 기록 항목이 바뀔 때 발생한다. 만약 활성화된 엔트리가 history.pushState() 메서드나 history.replaceState() 메서드에 의해 생성되면, popstate 이벤트의 state 속성은 히스토리 엔트리 state 객체의 복사본을 갖게 된다.

history.pushState() 또는 history.replaceState()는 popstate 이벤트를 발생시키지 않는 것에 유의한다. popstate 이벤트는 브라우저의 백 버튼이나 (history.back() 호출) 등을 통해서만 발생된다.

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

// addEventListener("popstate", (event) => {}); 과 동일
history.pushState({page: 1}, "title 1", "?page=1");
history.pushState({page: 2}, "title 2", "?page=2");
history.replaceState({page: 3}, "title 3", "?page=3");
history.back(); // Logs "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // Logs "location: http://example.com/example.html, state: null
history.go(2);  // Logs "location: http://example.com/example.html?page=3, state: {"page":3}

next/router의 beforePopState

브라우저의 뒤로가기 또는 앞으로 가기 버튼을 클릭할 떄 발생하는 popstate 이벤트 전에 실행된다.
주로 router가 작동하기 전에 작업이 필요한 경우 사용된다.

router.beforePopState(cb)
  • cb : 들어오는 popstate 이벤트에서 실행되는 함수이다. 함수는 다음 props를 사용하여 event의 state를 객체로 받는다.

    • url: String - 전환하려는 페이지의 URL을 나타낸다. 보통 page의 이름이다. ( ex. /mypage/contents/[id])
    • as: String - 전환된 페이지의 URL을 나타낸다.
    • options: Object - router.push 를 통해 전달된 추가 옵션
      • shallow
      • scroll
      • locale
  • cb의 리턴값

    • false: Next.js router는 popstate를 유발하지 않을 것이고, 우리는 이 경우에 대해서 핸들링할 수 있다.
    • true: 브라우저의 기본 동작대로 뒤로가기를 수행한다.
  • 특정 요청을 조작하거나, SSR refresh를 강제하는 등의 경우에서 사용할 수 있다.

import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
export default function Page() {
  const router = useRouter()
 
  useEffect(() => {
    router.beforePopState(({ url, as, options }) => {
      // I only want to allow these two routes!
      if (as !== '/' && as !== '/other') {
        // Have SSR render bad routes as a 404.
        window.location.href = as
        return false
      }
 
      return true
    })
  }, [router])
 
  return <p>Welcome to the page</p>
}

router object

  • pathname: String - /pages 뒤에 나오는 파일의 경로이다. basePath, locale및 trailing slash(trailingSlash:true)는 포함되지 않는다.

  • query: Object - 객체 형태로 파싱된 쿼리 스트링이다. (동적 라우팅 파라미터도 포함)
    SSR을 사용하지 않는 페이지의 경우 prerendering 동안에는 빈 객체가 될 것이다. (디폴트 값은 {})

  • asPath: String - search params를 포함하고 trailingSlash 구성을 준수하는 브라우저에 표시된 경로이다. basePath와 locale은 포함되지 않는다.

  • isFallback: boolean - 현재 페이지가 fallback mode인지 여부

  • basePath: String - basePath (활성화된 경우).

  • locale: String - locale (활성화된 경우).

  • locales: String[] - 지원하는 모든 locales (활성화된 경우).

  • defaultLocale: String - 현재 디폴트 locale (활성화된 경우).

  • domainLocales: Array<{domain, defaultLocale, locales}> - 설정된 모든 domain locales.

  • isReady: boolean - router field가 클라이언트 측에서 업데이트되고, 사용할 준비가 되었는지의 여부이다. useEffect내에서만 사용되어야하고 서버에서 조건부로 렌더링하는데에 사용되면 안된다. automatically statically optimized pages 참고

  • isPreview: boolean - 앱이 preview mode인지의 여부

Using the asPath field may lead to a mismatch between client and server if the page is rendered using server-side rendering or automatic static optimization. Avoid using asPath until the isReady field is true.
=> 초기 서버 사이드 렌더링 시점에서 asPath가 서버에서 렌더링되는 동안의 경로를 나타내는 반면, 클라이언트 사이드에서는 클라이언트가 브라우저에서 페이지를 로드하고 클라이언트 사이드 라우팅이 발생한 이후의 실제 경로를 나타낼 수 있다. 따라서 서버에서 생성된 초기 페이지와 클라이언트에서 동적으로 변경된 페이지 간에 asPath값이 일치하지 않을 수도 있다.
따라서 SSR에서는 asPath값을 사용하기 전에 isReady 속성을 확인하여 브라우저와 서버 간의 일관성을 보장하는 것을 권장한다.

상세 페이지(1)-https://www.example.com/contents/2/#목차_제목상세 페이지 (2)-https://www.example.com/contents/?id=333
router.query{id: '2'}{id: '333' }
router.asPath/contents/2/#목차_제목/contents/?id=333
router.pathname/contents/[id]/contents
window.location.hrefhttps://www.example.com/contents/2/#목차_제목https://www.example.com/contents/?id=333
window.location.pathname/contents/2//contents/
indow.location.hash#목차_제목
profile
기록중

0개의 댓글