๐ Tailwind 7์ฅ: ๋คํฌ ๋ชจ๋ ๊ตฌํ
๐ ๊ฐ์
Tailwind dark variant ๋ก ๋คํฌ ๋ชจ๋ ์๋ฒฝ ๊ตฌํ โ class ์ ๋ต๋ถํฐ ์์คํ ์ค์ ์ฐ๋๊น์ง
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐ Tailwind ๋คํฌ ๋ชจ๋ ๋ ๊ฐ์ง ์ ๋ต
- ๐จ ๋คํฌ ๋ชจ๋ ์์ ์ค๊ณ ์์น
- ๐ป ์ค์ : ์์๋ค ์ปค๋ฎค๋ํฐ ๋คํฌ ๋ชจ๋ ์ ์ฉ
- โ๏ธ Next.js ์์ ๋คํฌ ๋ชจ๋ ํ ๊ธ ๊ตฌํ
- ๐จ ํํ ์ค์์ ์ฃผ์์ฌํญ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- [๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ](#-์์ฒ ์ด์-ํด๊ทผ ์ผ๊ธฐ)
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 18๋ถ
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
-
dark:variant ๋ฅผ ์์ ๋กญ๊ฒ ์จ์ ๋คํฌ ๋ชจ๋ ์คํ์ผ์ ์ ์ํ ์ ์๋ค -
class์ ๋ต์ผ๋ก JS ๊ธฐ๋ฐ ๋คํฌ ๋ชจ๋ ํ ๊ธ์ ๊ตฌํํ ์ ์๋ค - ๋คํฌ ๋ชจ๋์์ ์์ ๊ณ์ธต์ ์ฌ๋ฐ๋ฅด๊ฒ ์ค๊ณํ ์ ์๋ค
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ๐จ ์์ (๋์์ด๋): "์์ฒ ๋, ์ ํผ๊ทธ๋ง์ ๋คํฌ ๋ชจ๋๋ ๋ค ๊ทธ๋ ค๋จ์ด์. ์ด์ ๊ตฌํ ๋ถํ๋๋ ค๋ ๋ ๊น์?"
- ๐ฃ ์์ฒ : "...๋คํฌ ๋ชจ๋์? ๊ทธ๊ฒ ์ผ๋ง๋ ๊ฑธ๋ฆด์ง..."
- ๐ฆ ์ํธ (๋ฆฌ๋): "์์ฒ ๋, Tailwind ์ฐ๊ณ ์์ผ๋ฉด ์๊ฐ๋ณด๋ค ๋นจ๋ฆฌ ๋ผ์.
bg-white dark:bg-gray-900์ด๋ฐ ์์ผ๋กdark:์ ๋์ฌ๋ง ๋ถ์ด๋ฉด ๋๊ฑฐ๋ ์." - ๐ฃ ์์ฒ : "์, ๋ฐ์ํ์ฒ๋ผ ์ ๋์ฌ ๋ฐฉ์์ด๊ตฐ์?"
- ๐ฆ ์ํธ: "์ ํํด์. ์ด๋ฏธ ๋ฐ์ํ ํ์ผ๋ฉด ๋คํฌ ๋ชจ๋๋ ๊ธ๋ฐฉ์ด์์."
๐ค ์ ์์์ผ ํ๋๊ฐ
๋คํฌ ๋ชจ๋๋ ์ด์ ์ ํ์ด ์๋ ํ์์ผ. iOS, Android, macOS, Windows ๋ชจ๋ ์์คํ ๋คํฌ ๋ชจ๋๋ฅผ ์ง์ํ๊ณ , ์ฌ์ฉ์๋ค์ ์์ ์ด ์ ํธํ๋ ๋ชจ๋๋ฅผ ๊ธฐ๋ํด.
๋จ์ํ "๋ฐฐ๊ฒฝ์ ์ด๋ก๊ฒ ํ๋ ๊ฒ" ์ด ์๋์ผ. ๋์ ํผ๋ก๋ ๊ฐ์, ๋ฐฐํฐ๋ฆฌ ์ ์ฝ (OLED ํ๋ฉด), ์ผ๊ฐ ์ฌ์ฉ ํธ์์ฑ ๋ฑ ์ค์ง์ ์ธ UX ์ด์ ์ด ์์ด.
์์๋ค ์ปค๋ฎค๋ํฐ์ ๋คํฌ ๋ชจ๋๊ฐ ์์ผ๋ฉด? ์ผ๊ฐ์ ์คํฐ๋ ๊ฒ์ํ๋ค๊ฐ ๋์ด ํฐ์ง๋ ์ฌ์ฉ์๋ค์ด ์ดํํด.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๋คํฌ ๋ชจ๋ = ๋ฃธ ๋ผ์ดํธ ์ค์์น
๋ฐฉ์ ์ผ๋ฐ ์กฐ๋ช
๊ณผ ๋ฌด๋ ๋ฑ ๋ ๊ฐ์ง๊ฐ ์๋ค๊ณ ์๊ฐํด. dark: ํด๋์ค๋ "๋ฌด๋ ๋ฑ ์ผ์ก์ ๋ ์ด ์์์ผ๋ก ๋ฐ๊ฟ" ๋ผ๋ ์ง์์ผ.
๋ผ์ดํธ ๋ชจ๋ (์ผ๋ฐ ์กฐ๋ช
): bg-white text-gray-900
๋คํฌ ๋ชจ๋ (๋ฌด๋ ๋ฑ): dark:bg-gray-900 dark:text-white
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
dark:๋hover:,md:์ ๊ฐ์ variant. "๋คํฌ ๋ชจ๋์ผ ๋ ์ด ์คํ์ผ์ ์ ์ฉํด."
๐ Tailwind ๋คํฌ ๋ชจ๋ ๋ ๊ฐ์ง ์ ๋ต
์ ๋ต 1: media ๋ฐฉ์ (์์คํ
์ค์ ์๋ ๊ฐ์ง)
/* tailwind.config ์์ด ๊ธฐ๋ณธ๊ฐ: prefers-color-scheme ์๋ ๊ฐ์ง */
/* Tailwind v4 ์์๋ @import "tailwindcss" ๋ง ํ๋ฉด ๋ฏธ๋์ด ๊ธฐ๋ฐ์ด ๊ธฐ๋ณธ */<!-- ์์คํ
์ด ๋คํฌ ๋ชจ๋์ผ ๋ ์๋์ผ๋ก dark: ์ ์ฉ -->
<div class="bg-white dark:bg-gray-900">
<p class="text-gray-900 dark:text-white">ํ
์คํธ</p>
</div>์ฅ์ : ๋ณ๋ JS ๋ก์ง ์์ด ์์คํ
์ค์ ์ ์๋ ์ฐ๋
๋จ์ : ์ฌ์ฉ์๊ฐ ์ฑ ์์์ ์ง์ ํ ๊ธ ๋ถ๊ฐ
์ ๋ต 2: class ๋ฐฉ์ (์๋ ์ ์ด โ ๊ถ์ฅ)
/* CSS ํ์ผ์ ์ค์ */
@import "tailwindcss";
@variant dark (&:where(.dark, .dark *));๋๋ tailwind.config.js:
// tailwind.config.js
module.exports = {
darkMode: 'class', // 'media' ๋์ 'class' ์ฌ์ฉ
// ...
}<!-- html ๋๋ body ์ .dark ํด๋์ค๊ฐ ๋ถ์ผ๋ฉด dark: ์ ์ฉ -->
<html class="dark">
<body>
<div class="bg-white dark:bg-gray-900">๋คํฌ ๋ชจ๋ ์ ์ฉ๋จ</div>
</body>
</html>์ฅ์ : ์ฌ์ฉ์๊ฐ ์ฑ ๋ด์์ ํ ๊ธ ๊ฐ๋ฅ, localStorage ์ ์ฅ์ผ๋ก ์ค์ ์ ์ง
๋จ์ : JS ๋ก .dark ํด๋์ค ํ ๊ธ ๋ก์ง ํ์
๐ ์ค๋ฌด์์๋
class๋ฐฉ์์ด ํ์ค์ด์ผ. ์ฌ์ฉ์์๊ฒ ์ ํ๊ถ์ ์ฃผ๋ ๊ฒ ๋ ์ข์ UX ๊ฑฐ๋ .
๐จ ๋คํฌ ๋ชจ๋ ์์ ์ค๊ณ ์์น
๋คํฌ ๋ชจ๋๋ ๋จ์ํ ์์์ ๋ฐ์ ์ํค๋ ๊ฒ ์๋์ผ. ๋๋น(Contrast)์ ๊ณ์ธต(Hierarchy) ์ ์ ์งํด์ผ ํด.
๋ผ์ดํธ ๋ชจ๋ vs ๋คํฌ ๋ชจ๋ ์์ ๋์ํ
๋ผ์ดํธ ๋ชจ๋ ๋คํฌ ๋ชจ๋
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
bg-white โ dark:bg-gray-900 (ํ์ด์ง ๋ฐฐ๊ฒฝ)
bg-gray-50 โ dark:bg-gray-800 (์น์
๋ฐฐ๊ฒฝ)
bg-gray-100 โ dark:bg-gray-700 (์นด๋ ๋ฐฐ๊ฒฝ)
border-gray-200 โ dark:border-gray-700 (ํ
๋๋ฆฌ)
text-gray-900 โ dark:text-white (์ฃผ์ ํ
์คํธ)
text-gray-600 โ dark:text-gray-300 (๋ณด์กฐ ํ
์คํธ)
text-gray-400 โ dark:text-gray-500 (ํํธ ํ
์คํธ)
bg-blue-600 โ dark:bg-blue-500 (CTA ๋ฒํผ โ ๋ฐ๊ฒ)
text-blue-600 โ dark:text-blue-400 (๋งํฌ ์์ โ ๋ฐ๊ฒ)
์๋ชป๋ ๋คํฌ ๋ชจ๋ vs ์ฌ๋ฐ๋ฅธ ๋คํฌ ๋ชจ๋
{/* โ ์๋ชป๋ ๋ฐฉ์: ๋จ์ ๋ฐ์ โ ๊ณ์ธต ๊ตฌ์กฐ๊ฐ ์ฌ๋ผ์ง */}
<div className="bg-white dark:bg-black text-black dark:text-white">
{/* ๋ฐฐ๊ฒฝ์ด ์์ ๊ฒ์ โ ์นด๋๊ฐ ๋ฐฐ๊ฒฝ์์ ๋ถ๋ฆฌ ์ ๋จ */}
<div className="bg-gray-100 dark:bg-gray-900">์นด๋</div>
</div>
{/* โ
์ฌ๋ฐ๋ฅธ ๋ฐฉ์: ๊ณ์ธต ์ ์ง */}
<div className="bg-gray-50 dark:bg-gray-900"> {/* ํ์ด์ง ๋ฐฐ๊ฒฝ */}
<div className="bg-white dark:bg-gray-800"> {/* ์นด๋ ๋ฐฐ๊ฒฝ (๋ฐฐ๊ฒฝ๋ณด๋ค ๋ฐ๊ฒ) */}
<div className="bg-gray-50 dark:bg-gray-700"> {/* ๋ด๋ถ ์น์
(์นด๋๋ณด๋ค ๋ฐ๊ฒ) */}
์นด๋ ๋ด์ฉ
</div>
</div>
</div>๐ป ์ค์ : ์์๋ค ์ปค๋ฎค๋ํฐ ๋คํฌ ๋ชจ๋ ์ ์ฉ
๊ธฐ๋ณธ ๋ ์ด์์
// ํ์ด์ง ๋ฃจํธ: ๋ผ์ดํธ/๋คํฌ ๋ชจ๋ ๋ฐฐ๊ฒฝ ์ค์
function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
{/* body ์ ๋คํฌ ๋ชจ๋ ๋ฐฐ๊ฒฝ์ ์ ์ฉ */}
<body className="bg-gray-50 text-gray-900 dark:bg-gray-900 dark:text-white">
<Navbar />
<main>{children}</main>
<Footer />
</body>
</html>
);
}์คํฐ๋ ์นด๋ ๋คํฌ ๋ชจ๋
function StudyCard({ title, description, tag, memberCount }: Props) {
return (
<div className="
group rounded-xl border p-5 shadow-md transition-all
hover:-translate-y-1 hover:shadow-lg
{/* ๋ผ์ดํธ ๋ชจ๋ */}
bg-white border-gray-200
{/* ๋คํฌ ๋ชจ๋ */}
dark:bg-gray-800 dark:border-gray-700
">
{/* ํ๊ทธ ๋ฐฐ์ง */}
<span className="
mb-3 inline-block rounded-full px-3 py-0.5 text-xs font-semibold
bg-blue-100 text-blue-700
dark:bg-blue-900/50 dark:text-blue-300
">
{tag}
</span>
{/* ์ ๋ชฉ */}
<h3 className="
mb-2 text-lg font-bold transition-colors
text-gray-900 group-hover:text-blue-600
dark:text-white dark:group-hover:text-blue-400
">
{title}
</h3>
{/* ์ค๋ช
*/}
<p className="mb-4 line-clamp-2 text-sm text-gray-500 dark:text-gray-400">
{description}
</p>
{/* ํ๋จ */}
<div className="
flex items-center justify-between border-t pt-4
border-gray-100 dark:border-gray-700
">
<span className="text-xs text-gray-400 dark:text-gray-500">
๐ฅ {memberCount}๋ช
</span>
<button className="
rounded-lg px-3 py-1.5 text-xs font-medium transition-colors
bg-blue-600 text-white hover:bg-blue-700
dark:bg-blue-500 dark:hover:bg-blue-400
">
์ฐธ์ฌํ๊ธฐ
</button>
</div>
</div>
);
}๋ด๋น๊ฒ์ด์ ๋ฐ ๋คํฌ ๋ชจ๋
function Navbar() {
return (
<nav className="
sticky top-0 z-10 border-b
bg-white/80 backdrop-blur-sm border-gray-200
dark:bg-gray-900/80 dark:border-gray-700
">
<div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-4">
{/* ๋ก๊ณ */}
<a href="/" className="font-bold text-blue-600 dark:text-blue-400">
๐ ์์๋ค ์ปค๋ฎค๋ํฐ
</a>
{/* ๋ฉ๋ด */}
<div className="flex items-center gap-4">
<a className="
text-sm transition-colors
text-gray-600 hover:text-blue-600
dark:text-gray-300 dark:hover:text-blue-400
">
์คํฐ๋ ์ฐพ๊ธฐ
</a>
<DarkModeToggle />
</div>
</div>
</nav>
);
}โ๏ธ Next.js ์์ ๋คํฌ ๋ชจ๋ ํ ๊ธ ๊ตฌํ
next-themes ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด Next.js ํ๋ก์ ํธ์ ํ์ค์ด์ผ.
์ค์น
npm install next-themes์ค์
// app/providers.tsx
'use client';
import { ThemeProvider } from 'next-themes';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider
attribute="class" // html ์์์ class="dark" ๋ก ์ ์ฉ
defaultTheme="system" // ๊ธฐ๋ณธ๊ฐ: ์์คํ
์ค์ ๋ฐ๋ฆ
enableSystem={true} // ์์คํ
์ค์ ๊ฐ์ง ํ์ฉ
>
{children}
</ThemeProvider>
);
}// app/layout.tsx
import { Providers } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko" suppressHydrationWarning> {/* suppressHydrationWarning ํ์! */}
<body className="bg-gray-50 dark:bg-gray-900">
<Providers>
{children}
</Providers>
</body>
</html>
);
}๋คํฌ ๋ชจ๋ ํ ๊ธ ๋ฒํผ
// components/DarkModeToggle.tsx
'use client';
import { useTheme } from 'next-themes';
import { useEffect, useState } from 'react';
export function DarkModeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
// Hydration ๋ถ์ผ์น ๋ฐฉ์ง โ ๋ฐ๋์ ํ์!
useEffect(() => setMounted(true), []);
if (!mounted) return null;
return (
<button
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className="
rounded-lg p-2 transition-colors
text-gray-600 hover:bg-gray-100
dark:text-gray-400 dark:hover:bg-gray-800
"
aria-label="๋คํฌ ๋ชจ๋ ํ ๊ธ"
>
{theme === 'dark' ? 'โ๏ธ' : '๐'}
</button>
);
}โ ๏ธ
suppressHydrationWarning๊ณผmounted์ฒดํฌ ์ด์ : Next.js ๋ ์๋ฒ์์ HTML ์ ์์ฑํ ๋ ๋คํฌ ๋ชจ๋ ์ํ๋ฅผ ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ์๋ฒ/ํด๋ผ์ด์ธํธ ๋ถ์ผ์น๊ฐ ๋ฐ์ํด.suppressHydrationWarning์ผ๋ก ๊ฒฝ๊ณ ๋ฅผ ์ต์ ํ๊ณ ,useEffect๋ก ๋ง์ดํธ ํ์๋ง ๋ ๋๋งํด์ ์์ ํ๊ฒ ์ฒ๋ฆฌํด.
๐จ ํํ ์ค์์ ์ฃผ์์ฌํญ
์ค์ 1: ์ด๋ฏธ์ง์ ๋คํฌ ๋ชจ๋ ์ ์ฉ
{/* โ ๋คํฌ ๋ชจ๋์์ ์ด๋ฏธ์ง๊ฐ ๋๋ฌด ๋ฐ๊ฒ ๋ณด์ */}
<img src="/logo.png" alt="๋ก๊ณ " />
{/* โ
๋คํฌ ๋ชจ๋์์ ์ด๋ฏธ์ง ๋ฐ๊ธฐ ์ฝ๊ฐ ๊ฐ์ */}
<img src="/logo.png" alt="๋ก๊ณ " className="dark:opacity-80" />
{/* โ
๋๋ ๋คํฌ ๋ชจ๋์ฉ ๋ณ๋ ์ด๋ฏธ์ง */}
<img
src="/logo-light.png"
alt="๋ก๊ณ "
className="dark:hidden"
/>
<img
src="/logo-dark.png"
alt="๋ก๊ณ "
className="hidden dark:block"
/>์ค์ 2: ํ๋์ฝ๋ฉ๋ ํฐ์ ๋ฐฐ๊ฒฝ ๋ฌธ์
{/* โ ๋คํฌ ๋ชจ๋์์ ํฐ ๋ฐฐ๊ฒฝ์ ํฐ ๊ธ์ โ ์ ๋ณด์ */}
<p className="text-white">ํฐ ๊ธ์</p>
{/* โ
๋ผ์ดํธ์์ ์ด๋์ด ๊ธ์, ๋คํฌ์์ ๋ฐ์ ๊ธ์ */}
<p className="text-gray-900 dark:text-white">์ฌ๋ฐ๋ฅธ ๊ธ์</p>์ค์ 3: ์์ ๋ฐ๊ธฐ ๋ฐฉํฅ ํผ๋
{/* โ ๋คํฌ ๋ชจ๋์์ ๋ ์ด๋์ด ์์ ์ฐ๋ ์ค์ */}
<button className="bg-blue-600 dark:bg-blue-800"> {/* ๋คํฌ์์ ๋ ์ด๋์์ ์ ๋ณด์ */}
{/* โ
๋คํฌ ๋ชจ๋์์๋ ๋ฐฐ๊ฒฝ์ด ์ด๋ก๊ธฐ ๋๋ฌธ์ ๋ฒํผ์ ์คํ๋ ค ๋ฐ๊ฒ */}
<button className="bg-blue-600 dark:bg-blue-400">๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
| ๊ฐ๋ | ์ค๋ช |
|---|---|
dark: variant | ๋คํฌ ๋ชจ๋์ผ ๋ ์ ์ฉ๋๋ ์คํ์ผ |
| media ์ ๋ต | ์์คํ ์ค์ ์๋ ๊ฐ์ง |
| class ์ ๋ต | html.dark ํด๋์ค๋ก ์๋ ์ ์ด (๊ถ์ฅ) |
| ์์ ๊ณ์ธต | ๋ผ์ดํธ: ๋ฐ์โ์ด๋ , ๋คํฌ: ์ด๋ โ๋ฐ์ |
| next-themes | Next.js ๋คํฌ ๋ชจ๋ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ |
| Hydration ์ฃผ์ | suppressHydrationWarning + mounted ์ฒดํฌ |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ์์ฒ ์ด๊ฐ ์นด๋ ๋ฐฐ๊ฒฝ์์ "๋ผ์ดํธ ๋ชจ๋์์ ํฐ์, ๋คํฌ ๋ชจ๋์์ ์ด๋์ด ํ์" ์ผ๋ก ๋ง๋ค๊ณ ์ถ๋ค. ์ฌ๋ฐ๋ฅธ ํด๋์ค๋?
โ
์ ๋ต: bg-white dark:bg-gray-800
๐ก ์์ธ ํด์ค:
bg-white: ๊ธฐ๋ณธ(๋ผ์ดํธ ๋ชจ๋) ํฐ์ ๋ฐฐ๊ฒฝ.dark:bg-gray-800: ๋คํฌ ๋ชจ๋์์ ์ด๋์ด ํ์(#1f2937) ๋ฐฐ๊ฒฝ.- ์
gray-900์ด ์๋gray-800์ธ๊ฐ? ํ์ด์ง ์ ์ฒด ๋ฐฐ๊ฒฝ์ดdark:bg-gray-900์ด๋ฉด, ์นด๋๋ ๋ฐฐ๊ฒฝ๋ณด๋ค ์ฝ๊ฐ ๋ฐ์gray-800์ ์จ์ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ์ ์งํด์ผ ํด. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "๋คํฌ ๋ชจ๋ ์์ ๊ณ์ธต: ํ์ด์ง ๋ฐฐ๊ฒฝ
900โ ์นด๋800โ ๋ด๋ถ700."
Q2. Next.js ์์ next-themes ๋ฅผ ์ฌ์ฉํ ๋ suppressHydrationWarning ์ <html> ํ๊ทธ์ ์ถ๊ฐํด์ผ ํ๋ ์ด์ ๋?
โ ์ ๋ต: ์๋ฒ์์ HTML ๋ ๋๋ง ์ ๋คํฌ ๋ชจ๋ ์ํ๋ฅผ ์ ์ ์์ด ์๋ฒ/ํด๋ผ์ด์ธํธ ๋ถ์ผ์น(Hydration Mismatch)๊ฐ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์, ์ด ๊ฒฝ๊ณ ๋ฅผ ์ต์ ํ๊ธฐ ์ํด ํ์ํ๋ค.
๐ก ์์ธ ํด์ค:
- Next.js ๋ ์๋ฒ์์ HTML ์ ๋ฏธ๋ฆฌ ์์ฑํด. ์ด๋ ๋คํฌ ๋ชจ๋ ์ค์ (localStorage ์ ์ ์ฅ๋)์ ์ฝ์ ์ ์์ด์ ํญ์ ๊ธฐ๋ณธ ํ ๋ง๋ก ๋ ๋๋งํด.
- ํด๋ผ์ด์ธํธ์์ ๋ง์ดํธ ํ localStorage ๋ฅผ ์ฝ์ด ๋คํฌ ๋ชจ๋๋ฅผ ์ ์ฉํ๋ฉด, ์๋ฒ HTML ๊ณผ ํด๋ผ์ด์ธํธ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ ธ Hydration ์ค๋ฅ ๋ฐ์.
suppressHydrationWarning์ ์ด ํน์ ์์(<html>)์์์ Hydration ๋ถ์ผ์น๋ฅผ React ๊ฐ ๋ฌด์ํ๊ฒ ํด.class์์ฑ์ด ์๋ฒ/ํด๋ผ์ด์ธํธ์์ ๋ฌ๋ผ๋ ๊ฒฝ๊ณ ์์.- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "์๋ฒ๋ ๋คํฌ ๋ชจ๋ ๋ชจ๋ฅธ๋ค โ
suppressHydrationWarning์ผ๋ก ํํ๋กญ๊ฒ."
Q3. ๋คํฌ ๋ชจ๋์์ CTA ๋ฒํผ(bg-blue-600)์ ์์ ์ ๋ต์ผ๋ก ์ฌ๋ฐ๋ฅธ ๊ฒ์?
โ
์ ๋ต: bg-blue-600 dark:bg-blue-500 ์ฒ๋ผ ๋คํฌ ๋ชจ๋์์ ๋ ๋ฐ์ ๊ฐ์ ์ฌ์ฉํ๋ค.
๐ก ์์ธ ํด์ค:
- ๋คํฌ ๋ชจ๋์์ ๋ฐฐ๊ฒฝ์ด ์ด๋์์ง๊ธฐ ๋๋ฌธ์, ๋ฒํผ๋ ๋์ผํ ๋ฐ๊ธฐ๋ฅผ ์ ์งํ๋ ค๋ฉด ์คํ๋ ค ๋ ๋ฐ์ ์์์ ์จ์ผ ๋๋น(Contrast)๊ฐ ์ ์ง๋ผ.
blue-600(๋ผ์ดํธ) โblue-500(๋คํฌ): ์ซ์๊ฐ ๋ฎ์์๋ก ๋ฐ์.- ๋ฐ๋๋ก ์๊ฐํ๋ฉด ์ ๋ผ: ๋คํฌ ๋ชจ๋์
blue-700,blue-800์ ์ฐ๋ฉด ๋ฐฐ๊ฒฝ์ด ์ด๋์ด๋ฐ ๋ฒํผ๋ ์ด๋์์ ๊ตฌ๋ถ์ด ์ ๋ผ. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "๋คํฌ ๋ชจ๋ = ๋ฐฐ๊ฒฝ์ด ์ด๋์ = ์์ ์ค๋ ์์๋ ๋ ๋ฐ๊ฒ."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
๋คํฌ ๋ชจ๋๊ฐ ์ด๋ ๊ฒ ๊ฐ๋จํ ์ค ๋ชฐ๋๋ค. dark: ์ ๋์ฌ ํ๋๋ก ๋๋๋๋ฐ, ๊ทธ๋์ ์ ์ด๊ฑธ ์ด๋ ต๊ฒ ์๊ฐํ๋์ง ๋ชจ๋ฅด๊ฒ ๋ค.
next-themes ์จ์ ํ ๊ธ ๋ฒํผ ๋ง๋ ๊ฒ๋ ์๊ฐ๋ณด๋ค ๊ธ๋ฐฉ ๋๋ค. suppressHydrationWarning ๋๋ฌธ์ ์ข ํค๋งธ๋๋ฐ, ์ํธ ๋์ด "์๋ฒ์์๋ ๋คํฌ ๋ชจ๋๋ฅผ ๋ชจ๋ฅด๋๊น ํด๋ผ์ด์ธํธ์์ ๋ง์ดํธ ๋ ํ ์ฒ๋ฆฌํด์ผ ํ๋ค" ๊ณ ์ค๋ช
ํด์ฃผ๋๊น ๋ฐ๋ก ์ดํด๋๋ค.
๐ก ์ค๋์ ๊ตํ: "๋คํฌ ๋ชจ๋๋ ์์ ๋ฐ์ ์ด ์๋๋ผ ๊ณ์ธต ์ ์ง๋ค. ๋ฐ์ ๋ฐฐ๊ฒฝ์์ ์ด๋์ด ํ ์คํธ โ ์ด๋์ด ๋ฐฐ๊ฒฝ์์ ๋ฐ์ ํ ์คํธ, ๊ทธ ๊ณ์ธต์ ๊ทธ๋๋ก ๋ค์ง์ด."
์ค๋ ์์ ๋์ด ํผ๊ทธ๋ง ๋คํฌ ๋ชจ๋ ์์ ๋ค ๋ณด๋ด์ฃผ์ จ๋๋ฐ, ์ด์ ๋ณด๋๊น ์์ ๋๋ ๊ฐ์ ์์น์ผ๋ก ์์ ๊ณ์ธต์ ์ค๊ณํ์ จ๋๋ผ. ๋์์ด๋์ ๊ฐ๋ฐ์์ ์ธ์ด๊ฐ ๊ฐ์์ง๋ ์๊ฐ์ด ์ด๋ฐ ๊ฑฐ๊ตฌ๋. ๋ฟ๋ฏํ๋ค! ์ค๋ ์ ๋ ์ ์ผ๊ฒน์ด ๋จน์ผ๋ฌ ๊ฐ์ผ๊ฒ ๋ค. ๐ฅฉ