회원가입, 글 작성하기, 댓글 달기 등등 기본적인 기능들은 이제 다 구현을 해보았으니 다크모드 기능을 한 번 구현해보려 한다.

 

state를 통해 다크모드인지 라이트모드인지를 관리하면 된다는 것은 누구나 쉽게 떠올릴 수 있을 것이다. 리액트 사용자였다면 특히 여기서 자연스럽게 useState를 생각하게 될텐데, useState 사용 시 새로고침 시 상태가 초기화되기 때문에 다크모드의 상태를 관리하기에는 적절하지 않다.

상태 데이터를 DB에 저장할 수도 있지만 간단하게 브라우저의 저장공간에 저장해보자.

 

1. localStorage에 저장하기

localStorage.setItem("저장할 이름", "값"); // 저장하기
localStorage.getItem("저장할 이름"); // 가져오기
localStorage.removeItem("저장할 이름"); // 삭제하기
export default function () {
  useEffect(() => {
    if (typeof window != "undefined") {
      let res = localStorage.setItem("이름", "kim");
    }
  }, []);

  return 생략;
}

이와 같이 간단하게 브라우저의 로컬 스토리지에서 데이터를 관리할 수 있다.

다만 useEffect 안에서 코드를 작성해주어야 하기 때문에 이 방식 또한 다크모드 기능을 구현하기에는 적절하지 않아보인다.

❓ useEffect 안의 코드는 HTML이 브라우저에 다 그려진 후에 실행되기 때문에, 로컬 스토리지에 다크모드인 상태가 저장되어 있어도 라이트모드가 먼저 보여지고 곧바로 다크모드로 바뀌게 될 것이다.

 

2. cookie에 저장하기 ✔

로컬 스토리지에 저장된 데이터는 클라이언트 컴포넌트에서만 사용이 가능한 것에 반해, 쿠키는 서버 컴포넌트에서도 쉽게 접근할 수 있다는 점에서 SSR에서 유용하다.

✅ 쿠키 사용 시 유의사항

  • 단순 문자열만 저장 가능
  • GET, POST 요청 시마다 서버에 전달되므로 네트워크 호스팅 비용 증가

 

쿠키를 생성할 때의 코드는 다음과 같다.

document.cookie = "쿠키이름=값; max-age=3600";
  • max-age : 쿠키의 유효기간 (미설정 시 브라우저 종료와 함께 쿠키는 자동 삭제됨)

 

서버 컴포넌트에서 쿠키를 가져올 때는 다음과 같이 키값으로 값을 가져오면 된다.

const result = cookies().get('쿠키이름')

 

 

💻 전체 코드

import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";

const ThemeBtn = () => {
  const [theme, setTheme] = useState("light");

  const router = useRouter();

  useEffect(() => {
    const mode = ("; " + document.cookie).split(`; mode=`).pop().split(";")[0];
    if (mode == "") document.cookie = "mode=light; max-age=" + 60 * 60 * 30;
  }, []);

  const handleDarkMode = () => {
    document.cookie = "mode=dark; max-age=" + 60 * 60 * 30;
    setTheme("dark");
    router.refresh();
  };

  const handleLightMode = () => {
    document.cookie = "mode=light; max-age=" + 60 * 60 * 30;
    setTheme("light");
    router.refresh();
  };

  return (
    <div>
      {theme == "dark" ? (
        <span onClick={handleLightMode}>🌞</span>
      ) : (
        <span onClick={handleDarkMode}>🌛</span>
      )}
    </div>
  );
};

export default ThemeBtn;
import { cookies } from "next/headers";

export default async function RootLayout({ children }) {
  const mode = cookies().get("mode");  // 쿠키에 저장된 값 가져오기
  console.log(mode, "mode");

  return (
    <html lang="en">
      <body>
        {/* 가져온 값(mode)이 'dark'이면 다크모드 css 입히기 */}
        <div className={"p-[20px] " + `${mode.value == "dark" && "dark-mode"}`}>
          <div className="navbar">
            <Link
              href="/"
              className="pr-[20px] font-semibold text-black no-underline"
            >
              Home
            </Link>
            <Link href="/list" className="text-black no-underline">
              List
            </Link>
            {session ? <LogoutBtn /> : <LoginBtn />}
            <ThemeBtn />
          </div>
        </div>
        {children}
      </body>
    </html>
  );
}
.dark-mode .navbar {
  background: #222;
}
.dark-mode .navbar a {
  color: #fff;
}
해안해