2026-02-06 10:43:16 +09:00
|
|
|
/**
|
|
|
|
|
* @file app/layout.tsx
|
|
|
|
|
* @description 애플리케이션의 최상위 루트 레이아웃 (RootLayout)
|
|
|
|
|
* @remarks
|
|
|
|
|
* - [레이어] Infrastructure/Layout
|
|
|
|
|
* - [역할] 전역 스타일(Font/CSS), 테마(Provider), 세션 관리(Manager) 초기화
|
|
|
|
|
* - [데이터 흐름] Providers -> Children
|
|
|
|
|
* - [연관 파일] globals.css, theme-provider.tsx
|
|
|
|
|
*/
|
|
|
|
|
|
2026-02-03 10:51:22 +09:00
|
|
|
import type { Metadata } from "next";
|
2026-02-06 10:43:16 +09:00
|
|
|
import { Geist, Geist_Mono, Outfit } from "next/font/google";
|
2026-02-04 09:34:54 +09:00
|
|
|
import { QueryProvider } from "@/providers/query-provider";
|
2026-02-06 10:43:16 +09:00
|
|
|
import { ThemeProvider } from "@/components/theme-provider";
|
|
|
|
|
import { SessionManager } from "@/features/auth/components/session-manager";
|
2026-02-10 11:16:39 +09:00
|
|
|
import { Toaster } from "sonner";
|
2026-02-03 10:51:22 +09:00
|
|
|
import "./globals.css";
|
|
|
|
|
|
|
|
|
|
const geistSans = Geist({
|
|
|
|
|
variable: "--font-geist-sans",
|
|
|
|
|
subsets: ["latin"],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const geistMono = Geist_Mono({
|
|
|
|
|
variable: "--font-geist-mono",
|
|
|
|
|
subsets: ["latin"],
|
|
|
|
|
});
|
|
|
|
|
|
2026-02-06 10:43:16 +09:00
|
|
|
const outfit = Outfit({
|
|
|
|
|
variable: "--font-heading",
|
|
|
|
|
subsets: ["latin"],
|
|
|
|
|
display: "swap",
|
|
|
|
|
});
|
|
|
|
|
|
2026-02-03 10:51:22 +09:00
|
|
|
export const metadata: Metadata = {
|
2026-02-11 14:06:06 +09:00
|
|
|
title: "Jurini - 감이 아닌 전략으로 시작하는 자동매매",
|
|
|
|
|
description:
|
|
|
|
|
"주린이를 위한 자동매매 파트너 Jurini. 손실 방어 규칙, 데이터 신호 분석, 실시간 자동 실행으로 초보의 첫 수익 루틴을 만듭니다.",
|
2026-02-03 10:51:22 +09:00
|
|
|
};
|
|
|
|
|
|
2026-02-06 10:43:16 +09:00
|
|
|
/**
|
|
|
|
|
* RootLayout 컴포넌트
|
|
|
|
|
* @param children 렌더링할 자식 컴포넌트
|
|
|
|
|
* @returns HTML 구조 및 전역 Provider 래퍼
|
|
|
|
|
* @see theme-provider.tsx - 다크모드 지원
|
|
|
|
|
* @see session-manager.tsx - 세션 타임아웃 감지
|
|
|
|
|
*/
|
2026-02-03 10:51:22 +09:00
|
|
|
export default function RootLayout({
|
|
|
|
|
children,
|
|
|
|
|
}: Readonly<{
|
|
|
|
|
children: React.ReactNode;
|
|
|
|
|
}>) {
|
|
|
|
|
return (
|
2026-02-06 10:43:16 +09:00
|
|
|
<html lang="en" className="scroll-smooth" suppressHydrationWarning>
|
2026-02-03 10:51:22 +09:00
|
|
|
<body
|
2026-02-06 10:43:16 +09:00
|
|
|
className={`${geistSans.variable} ${geistMono.variable} ${outfit.variable} antialiased`}
|
2026-02-03 10:51:22 +09:00
|
|
|
>
|
2026-02-06 10:43:16 +09:00
|
|
|
<ThemeProvider
|
|
|
|
|
attribute="class"
|
|
|
|
|
defaultTheme="system"
|
|
|
|
|
enableSystem
|
|
|
|
|
disableTransitionOnChange
|
|
|
|
|
>
|
|
|
|
|
<SessionManager />
|
|
|
|
|
<QueryProvider>{children}</QueryProvider>
|
2026-02-10 11:16:39 +09:00
|
|
|
<Toaster
|
|
|
|
|
richColors
|
|
|
|
|
position="top-right"
|
|
|
|
|
toastOptions={{
|
|
|
|
|
duration: 4000,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2026-02-06 10:43:16 +09:00
|
|
|
</ThemeProvider>
|
2026-02-03 10:51:22 +09:00
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
);
|
|
|
|
|
}
|