[Next.js]localStorage is not defined

권도훈·2025년 1월 5일

문제 발생 코드

const onClickLogin = async()=>{
	try{
		cosnt result = await loginUser({
			variables:{
					email : email,
					password : password
				}
			})
			const accessToken = result.data?.loginUser.accessToken
			if(accessToken){setAccessToken(accessToken || "" )
				void router.push('/loginsuccess')
				localStorage.setItem("accessToken",accessToken)
			}
		}catch(error){
			Modal.error({content : error.message})
		}
	} 

문제 분석

문제가 발생한 코드는 graphql mutation 요청을 통해 로그인을 하고 AccessToken을 발급받은 후 Global state(Recoil)에 저장하고 localStorage에 저장하는 단순한 코드이다.

위 코드를 그대로 실행시키면 localstorage is not defined 오류가 발생한다.
그러나, localStorage나 alert 기능은 웹(브라우저) API이기때문에 브라우저에서 사용할 수 있다.

만약에 위 코드를 리액트 환경(CSR)에서 실행하면 오류없이 정상적으로 동작한다.
여기서 원인을 찾을 수 있었다. Next.js는 서버사이드렌더링(SSR) 환경에서 동작하기 때문에 브라우저 API인 localStorage에 접근할 수 없었던 것이다.

Next.js 렌더링 방식

Next.js에서는diffinghydration 과정을 거친 후 화면을 렌더링한다.
이 과정은 흔히 말하는 서버사이드 렌더링과는 살짝 다른개념이다.

  • SSR (서버사이드 렌더링)
    서버에서 HTML을 생성(Pre-rendering)하여 클라이언트로 전달한다.
    이때, 이 시점에서의 HTML은 정적이고 동적 기능이 불가능하다.
  • Diffing
    브라우저가 서버한테 받은 HTML(pre-rendering을 통해 생성됨)과 리액트 가상 DOM을 비교
  • hydration
    diffing을 통해 비교한 후 최종적으로 반영해 렌더링하게 되는 과정(이때부터, 동적인 기능이 활성화 됨)

이제 오류의 이유를 알 수 있다.

localStorage는 브라우저에만 있는데, 서버에서 먼저 화면을 그려보기 때문에(pre-rendering) 발생하는 오류이다.

이를 해결하기 위해서는
1. if(typeof window !== "undefined") 사용
2. if (process.browser) 사용
3. useEffect를 사용해 렌더링 이후에 실행하는 방법

profile
🏃🏻

0개의 댓글