들어가며...
useNavigation훅을 통해서 상태를 저장할 수 있습니다. 해당 상태는 uri에 직접적으로 반영이 되지 않는 상태입니다.
따라서 새로고침 시 막연하게 초기화 될 거라고 생각을 했었습니다. 하지만 실제 테스트 해 본 결과 상태가 초기화가 되지 않았습니다. 심지어 ChagGPT 조차 상태가 초기화 된다고 하더군요..
import { useNavigate } from 'react-router';
const Home = () => {
const navigate = useNavigate();
return (
<div>
<h1>Home</h1>
<button
onClick={() => navigate('/test', { state: { message: '저장됨' } })}
>
Test 페이지로 이동
</button>
</div>
);
};
export default Home;
message에 저장됨이라는 상태를 여전히 가지고 있었습니다. useNavigate훅은 결국 History API 기반의 라이브러리 이기에 혹시 HTML 파일에서도 확인해보았고 여전히 초기화되지 않음을 확인하였습니다.
따라서 History API의 state의 스펙에 대해서 알아보기로 생각을 하였습니다.
결론은 브라우저에서는 Session History에 상태를 기록을 하는데, 해당 상태는 복원이 가능해야 합니다. 따라서 메모리에 기록이 되어야 하기 때문에, 새로고침을 하여도 다른 탭을 열거나 기존 탭을 닫는 방식이 아니면 상태가 유지가 됩니다.
History에 대해서
시작점은 History였습니다. 당연히 MDN에는 상태의 생명주기에 대한 언급이 있지 않을까라는 막연한 기대감에 접근하게 되었습니다.
History API는 history 전역 객체를 통해 브라우저 세션 히스토리(웹 익스텐션 히스토리와 혼동해서는 안 됩니다.)에 대한 접근을 제공합니다. 사용자의 방문 기록을 앞뒤로 탐색하고, 방문 기록 스택의 내용을 조작할 수 있는 유용한 메서드와 속성을 노출합니다.
해당 문서에서 History State에 대해 찾아보니 "History.state 속성은 현 history에 해당하는 state값을 나타냅니다." 라는 내용이 끝이더군요.
따라서 더 깊게 파고들기 위해서는 HTML의 History 스펙을 확인할 필요가 있었습니다.
history.state : Returns the classic history API state of the active session history entry, deserialized into a JavaScript value.
이제 Session history에 대해서 뭔저 알 필요가 생겼네요.
세션 히스토리 엔트리는 여러 가지 데이터를 포함하는 구조체(struct)로, 다음과 같은 요소를 가집니다.
- step: 방문 순서를 나타내는 0 이상의 정수 또는 "pending"(초기값 "pending").
- URL: 해당 히스토리 엔트리에 저장된 페이지의 URL.
- document state: 해당 페이지의 문서 상태.
- classic history API state: window.history.pushState()나 window.history.replaceState()를 통해 저장된 직렬화된 상태 데이터. 기본적으로 StructuredSerializeForStorage(null)로 초기화됨.
- navigation API state: 네비게이션 API 관련 직렬화된 상태 데이터. 초기값은 StructuredSerializeForStorage(undefined).
- navigation API key: 네비게이션 API에서 사용하는 UUID(고유 식별자).
- navigation API ID: 개별 네비게이션 요청을 구분하는 또 다른 UUID(고유 식별자).
- scroll restoration mode: 스크롤 복원 방식 ("auto"가 기본값).
- scroll position data: 문서에서 스크롤이 복원될 위치 데이터.
- persisted user state: 사용자가 입력한 데이터 등 브라우저가 유지할 상태 (기본값 null).
history의 state의 경우에 Session history에 보관되고 있습니다. 이때 직렬화를 거치는데 직렬화를 2가지 목적으로 사용됩니다.
1. 미리 처리된(preparsed) 상태를 URL에 저장하는 것
- 간단한 경우에는, URL에 상태를 저장하면 개발자가 따로 파싱(parsing) 하지 않아도 된다.
- 다만, URL이 사용자들 사이에서 공유될 수도 있으므로, 어떤 경우든 결국에는 파싱이 필요하다.
- 하지만 URL에 저장된 상태를 빠르게 가져올 수 있으므로, 약간의 최적화 효과가 있다.
2. URL에 저장하지 않고, 현재 문서(Document)에서만 필요한 상태를 저장하는 것
- 특정 상태는 새로운 문서가 열리면 다시 생성해야 하므로 URL에 저장하기 적절하지 않다.
- 이런 경우, 직렬화된 상태를 이용해 브라우저 히스토리에서 해당 상태를 보존하고 복원할 수 있다.
이후 공식스펙에는 7.4.6.5 Persisted history entry state 이 존재하는데, 유저의 편의를 위해 히스토리를 복원할 수 있는 방법이 존재해야 합니다.
공식스펙을 확인했지만,, 아직까지 왜 새로고침 시에도 값이 유지되는지에 대해서는 명확하지 않습니다.
이를 위해서 크롬에서는 세션 히스토리를 어떻게 사용하는지에 대해서 확인해보았습니다.
크롬의 History Session
브라우저의 세션 히스토리는 각 탭에서 발생한 탐색을 추적하여 뒤로 가기/앞으로 가기 탐색과 세션 복원을 지원한다. 이는 chrome://history 같은 기록(history)과는 다른데, 기록은 프로필의 수명 동안 사용자가 방문한 주요 프레임의 URL을 모든 탭에 걸쳐 저장한다.
위에서 스펙에서 확인한대로 복원 기능을 가지고 있네요.
그리고 복원 기능에 대해서 확인하면 아래와 같습니다.
탭의 공동 세션 히스토리는 유지되므로, Chromium을 다시 시작하거나, 탭을 닫은 후, 또는 다른 기기에서 탭을 복원할 수 있다. 이를 위해 각 NavigationEntry와 그 내부 FrameNavigationEntries 트리의 상태를 PageState 객체 및 기타 메타데이터를 사용하여 직렬화해야 한다. 새로운 값을 안전하게 저장하고 복원하는 방법에 대해서는 Modifying Session History Serialization을 참고하라.
마지막으로 Modifying Session History Serialization 에서 직렬화 방법을 변경시 디스크에 남아있는 데이터에 의한 호완 문제가 생길 수 있다는 점을 경고하고 있는데, 이를 통해서 크롬에서 disk에 Session History가 저장되어있음을 알 수 있습니다. 따라서 새로고침시에도 History.state가 유지가 됩니다.
Note that changing the serialization format is high risk and should be approached carefully. Mistakes or missed steps can cause backwards compatibility problems, because the effects can continue to live on disk between different versions of Chromium
느낀점
사실 처음에는 History.state의 생명주기 라고 검색하면 금방 해결할 수 있는 지식이라 생각했습니다. 하지만 관련 키워드로는 검색했을 때 나오지 않더군요. 공식 스펙을 보는 것도 제로초님이 영상에서 보시는 걸 본적은 있지만 직접 본 적은 없어서 많이 어렵게 느껴졌네요. 해당 스펙이 존재한다는 것이 명시되었을 뿐 어떻게 구현할 지는 브라우저마다 다를 수 있다는 사실도 어려웠습니다. 영어를 번역하면서 열심히 읽었는데, 혹시 오역이 있었을까에 대한 막연한 걱정도 조금 남아있네요. 혹시 잘못된 부분이 있으면 지적부탁드립니다 ㅠ
그래도 덕분에 history가 트리구조가 아니라 리스트 구조가 되었다는 등 많은 정보도 확인할 수 있었네요..
또한 ChatGpt 를 사용할 떄 좀 더 유의할 필요성을 느꼈습니다. 사실 새로고침 시 history.state가 초기화 되는지 여부 정보는 고차원적인 지식은 아니라 생각했는데 ChatGpt가 잘못 알려주더군요.
그리고 확실하게 새로고침에서 history.state가 사라지진 않는다는 지식을 얻어가네요.
참고링크
Chromium Docs - Modifying Session History Serialization
Modifying Session History Serialization Note: Please expand these steps as needed. See also NavigationEntryImpl comments for how to save and restore values outside of PageState, which is less common. Overview The following (non-exhaustive) steps are requir
chromium.googlesource.com
https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
HTML Standard
html.spec.whatwg.org
https://developer.mozilla.org/ko/docs/Web/API/History
History - Web API | MDN
History 인터페이스는 브라우저의 세션 기록, 즉 현재 페이지를 불러온 탭 또는 프레임의 방문 기록을 조작할 수 있는 방법을 제공합니다.
developer.mozilla.org
https://chromium.googlesource.com/chromium/src/+/master/docs/session_history.md#persistencehttps://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/modifying_session_history_serialization.md
Chromium Docs - Modifying Session History Serialization
Modifying Session History Serialization Note: Please expand these steps as needed. See also NavigationEntryImpl comments for how to save and restore values outside of PageState, which is less common. Overview The following (non-exhaustive) steps are requir
chromium.googlesource.com