본문 바로가기

문제 => 해결

[Nextjs] Nextjs 프로젝트에 localStorage 적용하기

 

문제 상황

 

만들고 있는 프로젝트의 사이드바를 열고 닫게 할 수 있게 만들어놨는데, 닫아 놓으면 새로고침 시 풀려서 불편함이 발생했다. 

이럴 때는 클라이언트의 localStorage에 상태를 저장해주면 된다. 

 

아래와 같이 localStorage에 값을 추가하는 코드를 작성하고 실행해보았는데, 적용이 되지 않았다. 

'use client'

export default function ClientLayout({ children }: { children: React.ReactNode }) {
  const [isOpen, setIsOpen] = useState(true);

  useEffect(() => {
    const stored = localStorage.getItem('sidebarOpen')
    if (stored) {
      setIsOpen(JSON.parse(stored))
    }
  }, [])

  useEffect(() => {
    localStorage.setItem('sidebarOpen', JSON.stringify(isOpen))
  }, [isOpen])

  return (
	// ...
  )
}

 

 

코드에는 문제가 없는 것 같은데, 왜 실행되지 않는 것일까?

 

찾아보니 해당 문제는 Next.js의 화면 렌더링과 관련이 있었다.

 


Next.js의 화면 렌더링 원리

Next.js는 Pre-rendering과 Hydration 이렇게 두 단계로 렌더링이 이루어진다. 

 

1) Pre-rendering

  • 사용자가 브라우저에 접속하려 할 때, 프론트엔드 서버에서는 브라우저에 접속하기 전에 먼저 html, css, js 파일의 소스코드를 둘러보며 정적 HTML을 생성한다.
  • 이 때 hook과 이벤트 핸들러 함수는 제외한다.

2) Hydration

  • Pre-rendering 단계를 거치며 생성된 정적 HTML 파일을 브라우저로 전송한다.
  • 그러면 브라우저가 기존 HTML과 비교(diffing)하여 업데이트한다. 
  • diffing 이후 업데이트 될 때 hook, 이벤트 핸들러 등이 반영되며, 이 과정을 hydration이라고 한다.

 

더보기

✨ hydration이란 :

hydration이 사전적 의미로 수화, 수분 공급 이런 뜻인데, 마치 마른 HTML에 물(기능)을 부어 생동감 있게 만드는 것처럼

적인 데이터(HTML)에 상태 관리, 이벤트 핸들러(onClick 등) 등이 연결되며 동적 기능을 활성화시키는 과정을 말한다. 

 


 

문제 원인

 

그러면 이제 문제의 원인을 알 수 있다. 

localStorage, SessionStorage 등은 브라우저 API이므로, 브라우저에 있는 것이다.
Pre-rendering 단계에서 브라우저 API를 사용하려 하면 프론트엔드 서버는 API를 찾지 못하기 때문에, 에러가 발생한 것이다. 

 


문제 해결

그러면 hydration 단계가 완료되고 나서 localStorage 값을 적용하도록 해야 한다.

 

mounted 상태를 추가함으로써 컴포넌트가 클라이언트 사이드에서 완전히 마운트된 후에만 localStorage와 상호작용하도록 보장한다.

이렇게 하면 서버 사이드 렌더링 중에 발생하는 localStorage 접근 오류를 방지할 수 있다.

export default function ClientLayout({ children }: { children: React.ReactNode }) {
  const [isOpen, setIsOpen] = useState(true);
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    const stored = localStorage.getItem('sidebarOpen');
    if (stored) {
      setIsOpen(JSON.parse(stored));
    }
    setMounted(true);
  }, []);

  useEffect(() => {
    if (mounted) {
      localStorage.setItem('sidebarOpen', JSON.stringify(isOpen));
    }
  }, [isOpen, mounted]);
  
  return (
  	// ...
  )
}

 

 

마침내 개발자 도구 > 애플리케이션 > 로컬 스토리지 항목에서 sidebarOpen이 true/false로 잘 저장되고 있는 것을 확인했다. 

 

 

 

출처 :

https://dev.to/collegewap/how-to-use-local-storage-in-nextjs-2l2j