개발새발 로그

[2024-02-20] 팀 프로젝트 개인 회고 - 다크모드 플래시 문제 해결 2(쿠키) 본문

TIL

[2024-02-20] 팀 프로젝트 개인 회고 - 다크모드 플래시 문제 해결 2(쿠키)

이즈흐 2024. 2. 20. 00:53

1. 여전히 찝찝한 다크모드..

https://ydoag2003.tistory.com/442

 

[2024-02-09] 팀프로젝트 개인 회고 - SSG에서 코드블록 무시

1. 서버에서 렌더링한 것과 클라이언트에서 렌더링한 결과가 다르다고?다크모드를 위해 다크모드 버튼을 만들면서 생긴 상황이다.기존에 리액트에서 구현해서 그대로 사용하면 되지않을까? 했

ydoag2003.tistory.com

이전에 다크모드를 구현하면서 플래시 현상을 해결하기 위해 html 내부에 즉시실행 스크립트를 넣어서 현재 브라우저의 테마상태와 일치시켜서 중간에 플래시 현상이 없도록 했다.

서버사이드에서 html을 파싱할 때 스크립트를 만나면 파싱이 중단되고 스크립트를 실행하는 점을 이용했다.

 

일치시켰다고는 하지만 클라이언트 사이드에서는 아직 다크모드 테마가 적용안된 html이기 때문에 class가 일치하지 않는다는 오류가 떴었다.

이를 해결하려고 했지만 개발환경에서만 나타나는 오류라고 해서 임시로 오류를 안보이게하는 속성을 이용했다.

<html lang="en" suppressHydrationWarning={true}>

이를 통해 해결은 했지만 여전히 찝찝했다.

 

그래서 멘토님께 조언을 받았고, 쿠키를 이용하는 방향으로 바꾸었다.

 

쿠키는 서버사이드에서도 문제없이 접근 가능하기 때문이었다.

Next.js의 서버사이드 렌더링(SSR)에서 쿠키에 접근이 가능한 이유는 HTTP 요청에 쿠키가 포함되어 있기 때문입니다.
Next.js에서 서버사이드 렌더링을 수행할 때, 클라이언트로부터의 초기 페이지 로드 요청은 서버에서 처리됩니다. 이 때, 클라이언트는 HTTP 요청 헤더에 쿠키를 포함하여 서버에 전송합니다.
서버는 이 요청을 받아 HTTP 요청 헤더에서 쿠키를 읽을 수 있습니다. 따라서, 서버사이드 렌더링이 수행되는 동안에는 서버가 쿠키에 접근하여 필요한 데이터를 읽어올 수 있습니다.

 

그래서 아래와 같이 쿠키에 접근했다.

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const cookieStore = cookies();
  const savedDarkMode = cookieStore.get("theme");

  const initialDarkMode =
    savedDarkMode === undefined ? "dark" : savedDarkMode.value;
  return (
    <html lang="en" className={initialDarkMode}>
      <body className={inter.className}>
        <main className="relative mx-auto h-[100dvh] max-w-[360px] overscroll-y-none px">
          {children}
        </main>
      </body>
    </html>
  );
}

쿠키에 접근해서 만약 해당되는 값이 없으면 "dark"테마로 고정시키고 아니라면 쿠키에있는 값을 넣어주었다.

그리고 클라이언트 컴포넌트에서는 아래와 같이 기능하게 했다.

"use client";

import { getCookie, setCookie } from "@/utils/cookie";
import { useEffect, useState } from "react";

const useDarkMode = () => {
  const [dark, setDark] = useState(() => {
    if (typeof window !== "undefined") {
      const savedDarkMode = getCookie({ name: "theme" });
      const prefersDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
      return savedDarkMode === null ? prefersDarkMode : savedDarkMode === "dark";
    }
  });

  const toggleDarkMode = () => {
    const updatedDarkMode = !dark;
    setCookie({ name: "theme", value: updatedDarkMode ? "dark" : "light" });
    setDark(updatedDarkMode);
  };

  useEffect(() => {
    if (dark === undefined) return;
    document.documentElement.classList.toggle("dark", dark);
  }, [dark]);

  return { dark, toggleDarkMode };
};

export default useDarkMode;

 

이를 통해서서버에서 생성된 컴포넌트와 CSR로 클라이언트에서 생성된 컴포넌트의 클래스명이 서로 같아지게된다.

 

 

서버사이드 렌더링할 때 쿠키가 없으면 쿠키를 set해주는 방법?

처음에는 서버사이드렌더링할 때 쿠키에 테마를 바로 저장해주는 것이 어떨까? 했지만
그럼 cookies.set()을 사용하기 위해 라우터핸들러나 서버액션을 해야했고,

라우터핸들러를 이용해서 구축했지만 해당 api/route.ts 서버에서만 쿠키가 저장되고, 클라이언트에서는 저장이 안됐다.

나중에 쿠키만 set하는 방법을 찾아봐야할 것 같다.\

 

728x90
반응형
LIST