[React] global location과 react location 비교하기

장동균·2024년 3월 17일
0
const App = () => {
  if (location.pathname === 'home') return <Home />
  return <Other />
}

export default App

해당 코드에서 이상한 점을 느끼셨나요?

사실 이상한 점은 없습니다. 다만 조건부에서 사용하는 location 객체가 react-router-dom이 아닌, window 객체에서 가져온 점이 특징이죠.

window.location.pathnameconst {pathname} = useLocation.

이 둘은 여러가지 면에서 동일해 보입니다. 덕분에 둘의 차이로 인해 발생하는 문제는 찾기 어렵습니다. 둘은 어떤 차이를 가지고 있는지 알아보고자 합니다.


Location

두 가지를 비교하기 이전에 헷갈릴 수 있는 location의 특징을 소개합니다.

https://developer.mozilla.org/en-US/docs/Web/API/Document/location

location 객체는 read only입니다. (아마도 보안상의 이유로) 하지만 문자열 할당이 가능합니다.

location = 'www.example.com'location.href='www.example.com'과 동일한 동작을 수행합니다.

location과 뗄레야 뗄 수 없는 histoty api에 대해서도 확인해봅니다.

https://stackoverflow.com/questions/24425885/failed-to-execute-pushstate-on-history-error-when-using-window-history-pushs

history API의 pushState 함수를 통해, URL 이동과 state 전달이 동시에 가능합니다. 다만 size에 대한 제한이 존재합니다. 640k characters 까지의 전달이 가능하다하며, 그 이상의 전달이 필요한 경우 localStorage나 sessionStorage의 사용을 추천합니다.

A 에서 B 페이지로 이동하면서 { code: test }라는 state를 넘겼다고 가정합니다.

B 에서 C 페이지로 이동했다가, 다시 history.go(-1) 혹은 history.back() 을 통해 B로 돌아오는 경우. 기존에 전달받았던 state는 유지 됩니다.


immutable mutable

https://www.geeksforgeeks.org/react-router-hooks/

Note: history.location also represents the current location, but it is mutable, on the other hand, the location returned by useLocation() is immutable. So, if you want to use the location instance, it is recommended to use the useLocation() hook

window.locationmutable, useLocation의 locationimmutable한 객체입니다.

useEffect 훅의 dependency로 location 객체 자체를 넣을 때, 이 차이는 큰 혼란을 가져오게 됩니다. (location 객체 자체를 deps에 추가하는건 피해야 합니다.)

// location 객체의 property가 변경되어도 훅은 호출되지 않는다.
useEffect(() => {}, [location])

// location 객체의 property가 변경되면 훅은 재실행된다.
const location = useLocation()
useEffect(() => {}, [location])

useEffect cleanup

useEffect cleanup 함수 내부에서 pathname을 찍어보면 둘의 차이가 존재한다는 점을 알 수 있습니다.

// A 페이지에서 B 페이지로 이동하는 경우
useEffect(() => {
  return () => {
    console.log(useLocation().pathname) // A 페이지의 pathname
    console.log(window.location.pathname) // B 페이지의 pathname
  }   
}, [])

useLocation().pathname은 기존 페이지의 pathname을

window.location.pathname은 이동하는 페이지의 pathname을 출력합니다.

https://velog.io/@dongkyun/React-useLocation-vs-Global-location

이는 해당 글과 관련이 있습니다. useLocation은 리액트 내부의 코드이기 때문에 리액트의 의도와 동일하게 동작하게 됩니다. (useEffect의 cleanup 함수는 기존 페이지 lifecycle의 마지막 요소이다.)

반면 window.location.pathname은 리액트와 전혀 관련이 없기 때문에, 호출 시점에 노출되는 pathname 그대로를 보여주고 있습니다.


고민

Location은 굉장히 편리하면서도 다루기 어려운 존재입니다. 실제 현업을 하다보면 다음과 같은 니즈가 생기는 순간이 있습니다.

  • 진입 경로에 따라 다른 컴포넌트를 보여주고 싶어! 진입 경로를 확인하기 위해서 직전 location 객체가 필요해!
  • state 객체를 일회성으로만 사용하고 싶어! 뒤로가기를 통해 다시 해당 페이지로 진입했을 때는 기존에 사용했던 state 객체가 제거되었으면 좋겠어!

이러한 니즈를 충족하기 위해서는 Loation 객체로 이루어진 배열이 필요합니다.

이러한 배열을 제공해주는 곳은 존재하지 않기 때문에 프로젝트 자체에서 구축할 필요가 있습니다. 라우팅 이벤트가 발생하는 순간마다 location 객체를 커스텀하게 관리하는 배열에 추가하는 식으로의 처리가 가능할 것 같습니다. 다만 이런 식으로 커스텀한 배열을 만들어 사용하는 경우 실제 location 객체 배열과 동기화되지 못한 데이터가 존재할 가능성이 높습니다.

https://www.youtube.com/watch?v=pEPOGDPDU-U&t=331s

당근에서도 동일한 문제를 커스텀한 배열로 처리하였고 많은 문제점들을 겪었다고 한다. Location 배열을 정확하게 관리할 수 있는 방법에 대한 고민이 필요합니다.

profile
프론트 개발자가 되고 싶어요

2개의 댓글

comment-user-thumbnail
2024년 3월 20일

원딜할때 본인 딜량보고 이상한 점을 느끼셨나요?
사실 이상한 점은 많습니다.

1개의 답글