react-router-dom(v6)에서 state를 공유하는 원리에 대한 탐구

김하연·2024년 4월 11일
0

TIL: Today I Leaned

목록 보기
26/27

얼마 전 과제전형을 치루면서, 리액트의 SPA 내부의 페이지 이동으르 가능하도록 해주는 라이브러리인 react-router-dom을 오랜만에 사용하게 되었다.

다음 페이지 혹은 이전페이지로 이동할 때에 navigate 메서드를 사용했고 이 때, 다음 페이지 혹은 이전 페이지에 특정 데이터를 넘겨주기 위해 navigate('url', { state: { 변수명: 값 }}) 과 같은 방식으로 state를 전달하였다.

근데 여기서 든 궁금증...
무슨 원리로 현재 url에서 전달한 state가 새로운 url까지 전달되는 것일까?
직접적으로 컴포넌트에 props를 넘겨주는 것도 아닐테고, 어떻게 state가 공유되는건지 궁금해서 원리에 대해 간단히 파헤쳐보았다.

react-router-dom에서 페이지 이동 시 state를 전달하는 방법

  • Link 태그 이용 시
<Link
  to={'/test'}
  state={{ test: 'test' }}>
    이동버튼
</Link>
  • navigate 메서드 활용 시
const navigate = useNavigate();
navigate('/test', { state: { test: 'test' }}

react-router-dom에서 state를 사용하는 방법

  • location 메서드를 활용해 state 값 확인 가능
const location = useLocation();
const { state } = location;

console.log(state); // { test: 'test' }

그래서 state는 어떻게 전달되는 것일까?

react-router-dom의 state에 대한 설명에 의하면
The state property can be used to set a stateful value for the new location which is stored inside history state
state 속성을 사용하면 History State에 저장되는 새로운 location에 저장된 상태 값을 설정할 수 있다.

여기까지는 대충 새로운 location에서 사용할 상태값을 저장할 수 있다는 뜻으로 보이는데, 이 부분은 이미 이해하고 있는 내용이니 해당 상태값 설정이 저장되는 History 상태라는 것에 대해 더욱 알아보았다.


History API

🔗 MDN - History API
History API는 브라우저의 세션 히스토리, 즉 브라우저의 방문 페이지들에 대한 히스토리에 액세스 할 수 있도록 한다. 사용자의 히스토리를 앞뒤로 탐색하고 히스토리 스택의 내용을 조작할 수 있는 유용한 방법과 속성을 제공한다.

그리고 History API의 History 인터페이스를 사용하면 브라우저 세션 기록, 즉 현재 페이지가 로드된 탭이나 프레임에서 방문한 페이지를 조작할 수 있다.
사용자의 히스토리를 활용해 페이지를 조작하는 방법은 아래와 같은 메서드들을 활용할 수 있다.

history.back();
history.forward();
history.go(1);
history.go(-1);

History 인터페이스의 속성들

  • length
    현재 로드된 페이지를 포함한 session history의 수를 반환한다. 예를 들어 새 탭에 로드된 페이지의 경우 1을 반환한다.
  • scrollRestoration
    애플리케이션이 기록을 탐색할 때 기본 스크롤 복원 동작에 대해 명시하는 옵션
  • state
    History 스택 최상단에서 state를 나타내는 모든 값을 리턴한다.

History 인터페이스가 SPA에서 사용하는 메서드

pushState, replaceState

그러나 History API의 back, forward, go 메서드들은 페이지를 다시 불러오기 때문에 SPA에서는 사용되지 않는다. 대신, pushState, replaceState라는 메서드들이 있는데 이 메서드들은 브라우저가 서버로 페이지를 다시 요청하지 않고 브라우저 주소 표시줄의 URL만 변경시키는 역할을 한다.

pushState, replaceState의 메서드에 전달되는 인자는 아래와 같다.
1. 해당 URL에 연관된 상태 객체
2. 하위 호환성을 위해 존재, 현재 사용되지 않으므로 빈 문자열 사용
3. 변경할 URL

// 1, 2, 3번 인자를 아래와 같이 작성할 수 있다.
pushState({ test: 'test' }, '', '/test');

따라서 위와 같이 pushState, replaceState를 활용해 <Link/> 컴포넌트로 페이지를 이동하면서 state를 넘겨줄 경우 혹은 navigate 메서드를 통해 페이지를 이동하면서 state를 넘겨줄 경우 페이지 새로고침 없이 URL 이동과 함께 state 전달이 가능한 것이다.

popState

버튼, 링크 등을 클릭해 페이지를 이동할 경우 해당 이벤트를 감지해 pushSate, replaceState의 메서드로 페이지 이동을 구현할 수 있지만 사용자가 웹 환경에서 브라우저의 뒤로가기나 앞으로가기 버튼을 클릭할 경우에는 해당 이벤트를 캐치할 수 없다.
이를 해결해주는 것이 popStateEvent이다.

popState 이벤트는 사용자가 브라우저의 뒤로가기, 앞으로 가기 버튼을 클릭해 히스토리가 바뀔 때마다 실행된다.

그리고 popState 이벤트가 pushState, replaceState 로 생성된 히스토리로부터 실행되었을 경우에는 해당 메서드들의 state의 복사본을 그대로 전달받는다.

window.addEventListener('popstate', (event) => {
	console.log(event.state) // pushState, replaceState로부터 복제해온 state값
  	// 원하는 이벤트 실행
});

History API 체험해보기!

History API를 활용해 버튼 클릭 시 다른 URL로 페이지의 로딩 없이 브라우저 URL을 변경하는 코드를 작성해 직접 테스트해보았다.

window.addEventListener('popstate', (event) => {
  console.log('popState 이벤트 ✨', event)
});

const BUTTON_IDS = ["first", "second"];

BUTTON_IDS.forEach((buttonId) => {
  const button = document.getElementById(buttonId);
  const newLocation = buttonId.toUpperCase();

  button.addEventListener('click', () => {
    const state = { newLocation };
    history.pushState(state, '', newLocation);
  })
})

버튼 두 개를 생성해 각 버튼을 클릭했을 때 newLocation이라는 이름으로 state를 넘기고 새로운 URL로 변경하는 코드이다. window에는 전역으로 popstate 이벤트를 추가해 해당 이벤트가 발생했을 때 콘솔에 로그가 찍히도록 하였다.

그리고 위 코드를 실행해 버튼을 클릭하면, 아래와 같이 popstate가 캐치되어 로그가 찍힘을 확인할 수 있다!
아래 로그는 index > second 버튼 클릭하여 second 페이지로 이동 > 뒤로가기를 통해 index 페이지로 돌아왔을 경우에 대한 로그이다.

state로 전달한 newLocation의 값이 잘 들어옴을 확인할 수 있었다.


이번 탐구를 마치며...

단순히 react-router-dom을 활용한 페이지 이동 시, state는 어떻게 전달되는 것일까? 라는 의문에 시작된 탐구였는데, 생각보다 더 재미있고 유익했던 것 같다.
근본적 질문이었던 state가 어떻게 전달되는 것인가? 에 대한 해답을 얻었을 뿐만 아니라, SPA에서 사용되는 라우팅 라이브러리의 근본이 History API 였다는 것도 처음 알게됐고, 이 API가 어떤 방식으로 작동해서 SPA 내부에서 브라우저의 리로딩 없이 페이지 이동이 가능한지 파악할 수 있는 기회가 되었다.
마치 막연하게만 생각하던 미지의 세계를 내가 다녀와본 경험의 세계로 바꿔놓은듯한 기분이 들어 뿌듯하다.


참고한 블로그 & 문서

History API를 쉽게 설명해주셔서 매우 유익했던, 많은 부분을 참고한 Dale님의 블로그
🔗 자바스크립트의 History API와 클라이언트 단 라우팅

MDN 공식문서
🔗 History API

0개의 댓글