import type { Metadata } from "next";
import "./globals.css";
import Header from "@/components/ui/Header/Header";
import { Roboto } from "next/font/google";
import ToastProvider from "@/lib/sonner/ToastProvider";
import SWRProvider from "@/lib/swr/SWRProvider";
import { cookies } from "next/headers";
import { API_ENDPOINTS } from "@/constant/api";
export const metadata: Metadata = {
metadataBase: new URL(
process.env.NEXT_PUBLIC_SITE_URL || "https://conduit-nextjs.com"
),
title: {
template: "%s | conduit",
default: "conduit - 실제 세계의 이야기",
},
description: "실제 세계의 다양한 이야기와 정보를 공유하는 커뮤니티입니다.",
keywords: ["블로그", "커뮤니티", "아티클", "정보 공유"],
authors: [{ name: "conduit Team" }],
openGraph: {
type: "website",
siteName: "conduit",
images: [
{
url: "/images/og-image.jpg",
width: 1200,
height: 630,
alt: "conduit",
},
],
},
twitter: {
card: "summary_large_image",
images: ["/images/og-image.jpg"],
},
icons: {
icon: "/favicon.ico",
},
};
const roboto = Roboto({
weight: ["400", "500", "700"], // 필요한 폰트 굵기 선택
subsets: ["latin"], // 사용할 문자 세트
display: "swap", // 폰트 로딩 전략
variable: "--font-roboto",
});
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const token = cookieStore.get("token")?.value;
let userResponse = { user: null };
if (token) {
try {
userResponse = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/user`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
).then((res) => res.json());
} catch (error) {
console.error(error);
throw new Error("사용자 정보를 불러오는데 실패했습니다.");
}
}
const fallback = {
[API_ENDPOINTS.CURRENT_USER]: userResponse,
};
return (
<html lang="kr" className={roboto.variable} suppressHydrationWarning>
<head>
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
try {
var mode = localStorage.getItem('dark-mode-storage');
if (mode) {
var parsed = JSON.parse(mode);
if (parsed.state && parsed.state.isDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
} else {
// 시스템 설정 확인
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
}
}
} catch (e) {
console.error('다크 모드 초기화 오류:', e);
}
})();
`,
}}
/>
<body>
<SWRProvider fallback={fallback}>
<Header />
<main className="dark:bg-gray-900 min-h-screen">{children}</main>
<ToastProvider />
</SWRProvider>
</body>
</head>
</html>
);
}
다크모드 적용시 렌더링 된 후에 상태를 확인하고 적용하느라
깜빡임이 발생하는데
이렇게 script를 작성해 놓으면
초기 렌더링시에 미리 데이터를 읽어와 변경할 수 있게 해준다
suppressHydrationWarning로 하이드레이션 경고 무효화
좀 찝찝한 감이 있지만 사람들이 많이 사용하는 next-themes도 해당 방법을 사용하고 있기에 괜찮을것이다.
'개발 > NEXTJS' 카테고리의 다른 글
Tailwind CSS와 동적 클래스: safelist로 해결한 스타일 누락 문제 (feat. 컨테이너 쿼리 폰트) (0) | 2025.05.24 |
---|---|
프로필 이미지가 선명하게 나오지 않는 에러 해결 그리고 하이드레이션 에러 해결 (0) | 2025.03.03 |
사용자 이미지가 새로고침을 할 때 이전의 이미지가 보였다가 다시 되돌아 가는 현상 해결 (0) | 2025.03.02 |
nextjs jwt 토큰에서 만료 값 읽어와서 쿠키 설정하기 (0) | 2025.03.02 |
api router에서 params 가져오는 법 (0) | 2025.02.26 |