추후 더 나은 방법을 찾아야 할 부분
Next.js 에서 다크모드를 구현하다가
이러한 에러를 만났다.
구글링을 해보니 서버쪽에서 렌더링하는 컨텐츠와 클라이언트쪽에서 렌더링하는 컨텐츠가 서로 불일치 할때 나는 에러라고 한다.
보통 다크모드를 구현 한후 새로고침을 했을때 라이트 모드가 먼저 적용되어서 깜빡임 현상이 일어났다가 다크모드가 적용되는데
페이지 로드가 다 된 후에 다크모드가 적용되기 때문이다.
이 문제를 해결하기 위해서 즉시 실행 함수를 사용해서, 페이지가 로드 될때 즉시 실행함수를 실행시켜주어서 이 깜빡임 현상을 방지시켜준다.
하지만 위의 이러한 단계들은 전부 브라우저쪽에서 페이지가 로드될때 적용되는 것이어서 서버에서 적용되는 것은 아니다. 이 부분을 해결하기 위해선 서버쪽에서 렌더링 이전에 미리 다크모드 설정값을 지정해주어야한다.
구글링을 하다보니 suppressHydrationWarning를 true로 설정해주면 이 에러를 없애준다고 했다.
import "./globals.css";
import localFont from "next/font/local";
import Header from "@/components/Header/Header";
import Card from "../components/Card/Card";
const nanumGothicFont = localFont({
src: [
{
path: "./fonts/NanumGothicCoding-Bold.ttf",
},
],
display: "swap",
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
// 즉시 실행함수로 감싸서 페이지가 렌더링될때 바로 실행되도록함
const themeInitializerScript = `(function() {
${setInitialColorMode.toString()}
setInitialColorMode();
})()
`;
// 초기 테마를 설정하는 함수
function setInitialColorMode() {
function getInitialColorMode() {
// 로컬스토리지에서 'theme' 값 가져오기
const persistedPreferenceMode = window.localStorage.getItem("theme");
const hasPersistedPreference =
typeof persistedPreferenceMode === "string";
if (hasPersistedPreference) {
return persistedPreferenceMode;
}
const preference = window.matchMedia("(prefers-color-scheme: dark)");
const hasMediaQueryPreference = typeof preference.matches === "boolean";
if (hasMediaQueryPreference) {
return preference.matches ? "dark" : "light";
}
return "light";
}
//현재 테마 모드
const currentColorMode = getInitialColorMode();
const element = document.body;
element.style.setProperty("--initial-color-mode", currentColorMode);
// 현재 다크모드라면 다크모드를 바로 적용 시켜줌
if (currentColorMode === "dark")
document.body.setAttribute("data-theme", "dark");
}
return (
<html lang="en" className={nanumGothicFont.className}>
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
// suppressHydrationWarning 를 true로
<body suppressHydrationWarning={true}>
<script
dangerouslySetInnerHTML={{
__html: themeInitializerScript,
}}
></script>
<Card>
<Header />
{children}
</Card>
</body>
</html>
);
}
이렇게 설정해주니 콘솔 에러가 발생하진 않았지만, 서버와 클라이언트쪽의 렌더링 결과물이 일치하지는 않는다.