next.js의 useRouter 훅을 사용하면 router 객체에 접근해 query를 이용할 수 있다. 그런데 검색 페이지에서 이것을 이용하는 과정에서 예상치 못한 문제가 발생했다.
왜 정보를 가지고 이동을 못하니 🤷♀️
내가 만들고 있는 검색 페이지에서 해야할 일은 다음과 같았다.
검색 페이지의 공통 컴포넌트인 SearchInput에서 입력된 검색어를 라우터 query값으로 넣어준다.
탭을 눌러 글, 작가, 픽스토리 페이지로 이동했을 때, 해당 페이지의 pathname(post, writer, picstory)은 변하지만 query값은 유지시킨 상태로 검색을 요청한다.
문제는 router의 query를 이용해 값을 담아놔도 다른 페이지로 이동하는 순간, 값이 깔끔하게 사라졌다. 당연한 일이다. 새로고침 하는 순간 데이터를 유지할 수 없으니까.
이때 사용할 수 있는 도구가 window의 localStorage이다.
localstorage와 sessiongstorage는 브라우저에서 기본적으로 제공하는 저장소(storage)이다. 이 저장소들은 application 전역에서 접근이 가능하다. 하나의 특징은 storage에 저장된 data는 JSON형태여야 하고 저장소에서 막 꺼낸 데이터는 JSON형태라는 것이다.
localstorage와 sessionstorage는 비슷하지만 하나의 차이점이 있다.
localstorage는 우리가 직접 지우기 전까지 저장되는 반면 sessionstorage는 session이 유지되는 한에서만 데이터가 유지된다.
session이란 브라우저의 탭 이라고 생각하면 편하다. 탭을 닫지않는 이상 essiongstorage에 저장된 데이터는 얼마든지 접근이 가능하다(새로고침 포함). 하지만 브라우저 탭을 닫게되면 sessionstorage에 저장된 데이터는 사라진다.
이제 진짜 된다! 싶었을 때 짠 하고 나타나 나를 고통받게 한 에러 녀석😵
next.js는 페이지를 처음 렌더링할 때 서버에서 웹페이지를 렌더링하는데 이때 window, document 같은 브라우저 전역 객체를 가지고 있기 않았기 때문에 발생한 문제다.
처음에는 페이지가 이미 렌더링 상태에서 코드를 수정했기에 문제가 발생하지 않았는데 새로고침하니 나를 반겨주었다.
🔨
next.js 환경은 최초에 서버사이드에서 실행된 이후 클라이언트 사이드에서 실행된다. window 객체는 클라이언트 사이드에서만 존재하기 때문에 다음의 방법을 사용해 해결할 수 있다.
아직 window 객체를 받아오지 못한 SSR 상태에서 조건 처리
useEffect를 사용해 componentDidMount 내에 코드를 작성
next dynamic 모듈을 사용해 모듈을 빌드타임이 아닌 런타임에 불러오기
구글링을 통해 알게 된 이 3가지 방법 중 여러 삽질 끝에 1번을 이용해 간단히 해결했다.
해당하는 브라우저 코드를 다음과 같은 조건문으로 감싸주었다.
const localStorage = typeof window !== "undefined" ? JSON.parse(window.localStorage.getItem("value")) : null;
console.log(`localStorage: ${localStorage}`);
const [value, setValue] = useState(localStorage);