@@ -288,6 +290,32 @@ const Sidebar = ({ isMobile = false }) => {
);
+ // 테마 토글 버튼 컴포넌트
+ const ThemeToggle = () => {
+ const getThemeIcon = () => {
+ if (theme === 'system') return
;
+ };
+
+ const getThemeLabel = () => {
+ if (theme === 'system') return '시스템';
+ if (theme === 'dark') return '다크';
+ return '라이트';
+ };
+
+ return (
+
@@ -578,6 +606,9 @@ const Sidebar = ({ isMobile = false }) => {
})}
+ {/* 테마 토글 */}
+
+
{/* 하단: 로그인/유저 정보 */}
{isLoggedIn ? (
diff --git a/frontend/src/contexts/ThemeContext.jsx b/frontend/src/contexts/ThemeContext.jsx
new file mode 100644
index 0000000..0d45151
--- /dev/null
+++ b/frontend/src/contexts/ThemeContext.jsx
@@ -0,0 +1,81 @@
+import React, { createContext, useContext, useState, useEffect } from "react";
+
+// 테마 타입: 'system' | 'dark' | 'light'
+const ThemeContext = createContext();
+
+export const useTheme = () => {
+ const context = useContext(ThemeContext);
+ if (!context) {
+ throw new Error("useTheme must be used within a ThemeProvider");
+ }
+ return context;
+};
+
+export const ThemeProvider = ({ children }) => {
+ // localStorage에서 저장된 테마 가져오기 (기본값: system)
+ const [theme, setTheme] = useState(() => {
+ const saved = localStorage.getItem("theme");
+ return saved || "system";
+ });
+
+ // 실제 적용되는 테마 (시스템 설정 반영)
+ const [resolvedTheme, setResolvedTheme] = useState("dark");
+
+ // 시스템 테마 감지 및 적용
+ useEffect(() => {
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
+
+ const updateResolvedTheme = () => {
+ if (theme === "system") {
+ setResolvedTheme(mediaQuery.matches ? "dark" : "light");
+ } else {
+ setResolvedTheme(theme);
+ }
+ };
+
+ updateResolvedTheme();
+
+ // 시스템 테마 변경 감지
+ const handler = () => updateResolvedTheme();
+ mediaQuery.addEventListener("change", handler);
+
+ return () => mediaQuery.removeEventListener("change", handler);
+ }, [theme]);
+
+ // 테마 클래스를 에 적용
+ useEffect(() => {
+ const root = document.documentElement;
+ root.classList.remove("light", "dark");
+ root.classList.add(resolvedTheme);
+ }, [resolvedTheme]);
+
+ // 테마 변경 및 localStorage 저장
+ const setThemeWithStorage = (newTheme) => {
+ setTheme(newTheme);
+ localStorage.setItem("theme", newTheme);
+ };
+
+ // 테마 순환: system -> dark -> light -> system
+ const cycleTheme = () => {
+ const order = ["system", "dark", "light"];
+ const currentIndex = order.indexOf(theme);
+ const nextIndex = (currentIndex + 1) % order.length;
+ setThemeWithStorage(order[nextIndex]);
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default ThemeContext;
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 7d6936d..f72c753 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -2,14 +2,55 @@
@tailwind components;
@tailwind utilities;
-/* 기본 html, body 스타일 - 다크 배경 & 스크롤바 레이아웃 고정 */
+/* 테마 CSS 변수 */
+:root {
+ /* 다크 테마 (기본) */
+ --bg-primary: #141414;
+ --bg-secondary: #1c1c1c;
+ --bg-tertiary: #252525;
+ --bg-card: #1c1c1c;
+ --bg-hover: rgba(255, 255, 255, 0.05);
+
+ --text-primary: #ffffff;
+ --text-secondary: #a1a1aa;
+ --text-tertiary: #71717a;
+ --text-muted: #52525b;
+
+ --border-color: rgba(255, 255, 255, 0.1);
+ --border-subtle: rgba(255, 255, 255, 0.05);
+
+ --shadow-color: rgba(0, 0, 0, 0.3);
+}
+
+/* 라이트 테마 */
+.light {
+ --bg-primary: #f8fafc;
+ --bg-secondary: #ffffff;
+ --bg-tertiary: #f1f5f9;
+ --bg-card: #ffffff;
+ --bg-hover: rgba(0, 0, 0, 0.05);
+
+ --text-primary: #18181b;
+ --text-secondary: #52525b;
+ --text-tertiary: #71717a;
+ --text-muted: #a1a1aa;
+
+ --border-color: rgba(0, 0, 0, 0.1);
+ --border-subtle: rgba(0, 0, 0, 0.05);
+
+ --shadow-color: rgba(0, 0, 0, 0.1);
+}
+
+/* 기본 html, body 스타일 */
html {
scrollbar-gutter: stable; /* 스크롤바 공간 예약 (세로 스크롤바) */
}
body {
- background: #141414;
+ background: var(--bg-primary);
+ color: var(--text-primary);
min-height: 100vh;
+ transition: background-color 0.3s ease, color 0.3s ease;
}
/* PC 레이아웃 - min-width 적용 */
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 9ccb6db..c2bb725 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -1,6 +1,7 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
+ darkMode: "class", // 클래스 기반 다크 모드
theme: {
extend: {
fontFamily: {