๐Ÿ”ง Tailwind 10์žฅ: ์ปค์Šคํ…€ ์Šคํƒ€์ผ๊ณผ ๋””๋ ‰ํ‹ฐ๋ธŒ

2026๋…„ 3์›” 5์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

@apply, @layer, @source ์˜ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ๋ฒ• โ€” Tailwind ์™€ ์ปค์Šคํ…€ CSS ์˜ ์กฐํ™”๋กœ์šด ๊ณต์กด

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 12๋ถ„

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • @layer base, @layer components, @layer utilities ์˜ ์—ญํ• ๊ณผ ์ฐจ์ด๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค
  • @apply ์˜ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค์™€ ๋‚จ์šฉ ๊ธˆ์ง€ ์ผ€์ด์Šค๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค
  • Tailwind ๊ฐ€ ์†Œ์Šค๋ฅผ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•  ๋•Œ @source ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ๐Ÿฃ ์˜์ฒ : "์˜์ˆ˜ ๋‹˜, react-markdown ๋ถ™์˜€๋”๋‹ˆ ๋งˆํฌ๋‹ค์šด ๊ธ€์ด ๋„ˆ๋ฌด ๋ฐ‹๋ฐ‹ํ•˜๊ฒŒ ๋‚˜์™€์š”. globals.css ์— CSS ์“ฐ๋ฉด ๋˜๋Š” ๊ฑด๊ฐ€์š”?"
  • ๐Ÿฆ ์˜ํ˜ธ (๋ฆฌ๋“œ): "๊ทธ๋ƒฅ ์“ฐ๋ฉด Tailwind ๋ž‘ ์ถฉ๋Œ ๋‚  ์ˆ˜ ์žˆ์–ด์š”. @layer ์•ˆ์— ๋„ฃ์–ด์•ผ Tailwind ๊ฐ€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ด€๋ฆฌํ•ด์ค˜์š”."
  • ๐Ÿฃ ์˜์ฒ : "@layer์š”? @apply ๋ž‘์€ ๋‹ค๋ฅธ ๊ฑด๊ฐ€์š”?"
  • ๐Ÿฆ ์˜ํ˜ธ: "๋‹ค๋ฅธ ๊ฑฐ์˜ˆ์š”. @layer ๋Š” CSS ์šฐ์„ ์ˆœ์œ„ ๊ณ„์ธต์„ ์ •์˜ํ•˜๊ณ , @apply ๋Š” Tailwind ํด๋ž˜์Šค๋ฅผ CSS ์•ˆ์— ์“ฐ๋Š” ๊ฑฐ์˜ˆ์š”. ์˜ค๋Š˜ ๋‘˜ ๋‹ค ์ •ํ™•ํžˆ ๋ฐฐ์›Œ๋ด์š”."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€

์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ Tailwind ๋งŒ์œผ๋กœ ํ•ด๊ฒฐ์ด ์•ˆ ๋˜๋Š” ์ผ€์ด์Šค๋“ค์ด ์ƒ๊ฒจ๋‚ฌ์–ด:

  • ์จ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์Šคํƒ€์ผ: react-markdown, react-datepicker ๋“ฑ ์™ธ๋ถ€ ์ปดํฌ๋„ŒํŠธ์˜ HTML ์—๋Š” ์ง์ ‘ Tailwind ํด๋ž˜์Šค๋ฅผ ๋ถ™์ผ ์ˆ˜ ์—†์–ด
  • ์ „์—ญ Base ์Šคํƒ€์ผ: ํฐํŠธ, ๋ฐ•์Šค ์‚ฌ์ด์ง•, ์Šคํฌ๋กค ๋™์ž‘ ๊ฐ™์€ ์ „์—ญ ์„ค์ •์€ CSS ๋ ˆ์ด์–ด๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒŒ ๋” ์ ํ•ฉํ•ด
  • ๋ณต์žกํ•œ CSS ์…€๋ ‰ํ„ฐ: :not() ์ฒด์ธ์ด๋‚˜ ์†์„ฑ ์„ ํƒ์ž๋Š” Tailwind ํด๋ž˜์Šค๋งŒ์œผ๋กœ ํ‘œํ˜„ํ•˜๊ธฐ ์–ด๋ ค์›Œ

์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ Tailwind ์™€ ์ปค์Šคํ…€ CSS ๋ฅผ ์ถฉ๋Œ ์—†์ด ํ•จ๊ป˜ ์“ฐ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„์•ผ ํ•ด.


๐Ÿ—‚๏ธ @layer: CSS ๊ณ„์ธต ๊ตฌ์กฐ ๊ด€๋ฆฌ

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • @layer base, @layer components, @layer utilities ๊ฐ๊ฐ์ด ์–ธ์ œ ์ ์šฉ๋˜๋Š”์ง€ ์ดํ•ดํ•œ๋‹ค
  • CSS ์šฐ์„ ์ˆœ์œ„ ์ถฉ๋Œ ์—†์ด ์ปค์Šคํ…€ ์Šคํƒ€์ผ์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•ˆ๋‹ค
  • ์จ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์Šคํƒ€์ผ์„ Tailwind ์™€ ๊ณต์กด์‹œํ‚ค๋Š” ํŒจํ„ด์„ ์ตํžŒ๋‹ค

์˜์ฒ ์ด๊ฐ€ react-markdown ์„ ๋ถ™์ด๋ฉด์„œ ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์–ด. ๋งˆํฌ๋‹ค์šด ๋ณธ๋ฌธ ์•ˆ์˜ <h1>, <p>, <a> ํƒœ๊ทธ๋“ค์ด Tailwind Preflight ์— ์˜ํ•ด ๊ธฐ๋ณธ ์Šคํƒ€์ผ์ด ์ œ๊ฑฐ๋˜์–ด์„œ ๋ฐ‹๋ฐ‹ํ•˜๊ฒŒ ๋ณด์ด๋Š” ๊ฑฐ์•ผ.

๐Ÿฃ ์˜์ฒ : "์•„, globals.css ์— .prose-custom h1 { font-size: 2rem; } ์ด๋ ‡๊ฒŒ ์“ฐ๋ฉด ๋˜๋Š” ๊ฑฐ ์•„๋‹Œ๊ฐ€์š”?"

๐Ÿฆ ์˜ํ˜ธ: "๊ทธ๋ ‡๊ฒŒ ์“ฐ๋ฉด Tailwind ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋ž‘ ์ถฉ๋Œํ•  ์ˆ˜ ์žˆ์–ด์š”. CSS ์šฐ์„ ์ˆœ์œ„(Specificity)๊ฐ€ ๋’ค์—‰์ผœ์„œ ์–ด๋–ค ๊ฒŒ ์ด๊ธธ์ง€ ์˜ˆ์ธก์ด ์–ด๋ ค์›Œ์ ธ์š”. @layer ๋ฅผ ์“ฐ๋ฉด Tailwind ๊ฐ€ ์šฐ์„ ์ˆœ์œ„ ๊ณ„์ธต์„ ์ง์ ‘ ๊ด€๋ฆฌํ•ด์ค˜์„œ ์ถฉ๋Œ์ด ์—†์–ด์š”."

Tailwind ๋Š” CSS ๋ฅผ ์„ธ ๊ณ„์ธต(layer) ์œผ๋กœ ๋‚˜๋ˆ ์„œ ๊ด€๋ฆฌํ•ด. ์ธต์˜ ์ˆœ์„œ = ์šฐ์„ ์ˆœ์œ„ ์ˆœ์„œ:

/* ๊ณ„์ธต ์ˆœ์„œ: base โ†’ components โ†’ utilities (์œ„์—์„œ ์•„๋ž˜๋กœ ์šฐ์„ ์ˆœ์œ„ ๋†’์•„์ง) */
 
@layer base {
  /* ๐Ÿ›๏ธ ๊ฐ€์žฅ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ */
  /* HTML ์š”์†Œ ๊ธฐ๋ณธ ์Šคํƒ€์ผ, ์ „์—ญ ํฐํŠธ, CSS reset ๋“ฑ */
  /* ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค ํ•˜๋‚˜๋กœ๋„ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋Š” ์Šคํƒ€์ผ */
}
 
@layer components {
  /* ๐Ÿงฉ ์ค‘๊ฐ„ ์šฐ์„ ์ˆœ์œ„ */
  /* ์žฌ์‚ฌ์šฉ ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ (.btn, .card, .prose ๋“ฑ) */
  /* @apply ๋ฅผ ์ฃผ๋กœ ์“ฐ๋Š” ๊ณต๊ฐ„ */
}
 
@layer utilities {
  /* โšก ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ */
  /* ๋‹จ์ผ ์†์„ฑ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋“ค */
  /* ์ง์ ‘ ์ถ”๊ฐ€ํ•  ์ผ์ด ๊ฑฐ์˜ ์—†์Œ โ€” Tailwind ๊ฐ€ ์ž๋™ ๊ด€๋ฆฌ */
}

์‹ค์ „: @layer base ํ™œ์šฉ

/* globals.css */
@import "tailwindcss";
 
@layer base {
  /* ์ „์—ญ ๊ธฐ๋ณธ ์Šคํƒ€์ผ */
  html {
    scroll-behavior: smooth;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
 
  /* ๋ชจ๋“  ์ด๋ฏธ์ง€ ๊ธฐ๋ณธ๊ฐ’ */
  img, video {
    max-width: 100%;
    height: auto;
  }
 
  /* ์ปค์Šคํ…€ ํฌ์ปค์Šค ์Šคํƒ€์ผ ์ „์—ญ ์ ์šฉ */
  *:focus-visible {
    outline: 2px solid theme('colors.brand.500');
    outline-offset: 2px;
  }
 
  /* ํ•œ๊ตญ์–ด ํ…์ŠคํŠธ ์ตœ์ ํ™” */
  body {
    word-break: keep-all;
    overflow-wrap: break-word;
  }
}

์‹ค์ „: @layer components ํ™œ์šฉ

@layer components {
  /* ๐ŸŽฏ ์˜ฌ๋ฐ”๋ฅธ @apply ์‚ฌ์šฉ์ฒ˜ โ€” ์จ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์Šคํƒ€์ผ ์˜ค๋ฒ„๋ผ์ด๋“œ */
 
  /* react-markdown ๋ณธ๋ฌธ ์Šคํƒ€์ผ */
  .prose-custom {
    @apply text-base leading-relaxed text-gray-700 dark:text-gray-300;
  }
 
  .prose-custom h1 { @apply text-3xl font-bold text-gray-900 dark:text-white mb-4; }
  .prose-custom h2 { @apply text-2xl font-semibold text-gray-800 dark:text-gray-100 mb-3; }
  .prose-custom p  { @apply mb-4; }
  .prose-custom a  { @apply text-brand-600 hover:text-brand-700 underline; }
 
  .prose-custom code {
    @apply rounded bg-gray-100 px-1.5 py-0.5 text-sm font-mono text-gray-800 dark:bg-gray-700 dark:text-gray-200;
  }
 
  .prose-custom pre {
    @apply rounded-xl bg-gray-900 p-4 overflow-x-auto;
  }
}

๐ŸŽฏ @apply: ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ CSS ๋กœ ์ถ”์ถœ

@apply ๋Š” Tailwind ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋ฅผ CSS ๊ทœ์น™ ์•ˆ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜.

์˜ฌ๋ฐ”๋ฅธ @apply ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค

์‹œ๋‚˜๋ฆฌ์˜ค 1: ์จ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์Šคํƒ€์ผ๋ง

/* react-select, react-datepicker ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” HTML ์„ ์ง์ ‘ ์ œ์–ด ๋ถˆ๊ฐ€ */
@layer components {
  /* DatePicker ์ปค์Šคํ…€ ์Šคํƒ€์ผ */
  .react-datepicker__day--selected {
    @apply bg-brand-600 text-white;
  }
 
  .react-datepicker__day:hover {
    @apply bg-brand-100 text-brand-700;
  }
}

์‹œ๋‚˜๋ฆฌ์˜ค 2: ์ „์—ญ input ์Šคํƒ€์ผ (๋ฐ˜๋ณต ๋ฐฉ์ง€)

@layer base {
  /* ๋ชจ๋“  input/select/textarea ์— ์ผ๊ด€๋œ ์Šคํƒ€์ผ ์ ์šฉ */
  input[type="text"],
  input[type="email"],
  input[type="password"],
  select,
  textarea {
    @apply block w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm
           placeholder:text-gray-400
           focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/20
           dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:placeholder:text-gray-500;
  }
}

์‹œ๋‚˜๋ฆฌ์˜ค 3: ๋งˆํฌ๋‹ค์šด / ์ฝ˜ํ…์ธ  ์˜์—ญ

@layer components {
  /* CMS ์ฝ˜ํ…์ธ  ์˜์—ญ โ€” HTML ํƒœ๊ทธ์— ์ง์ ‘ ํด๋ž˜์Šค๋ฅผ ๋ถ™์ผ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ */
  .content-area h1 { @apply text-3xl font-bold mb-6; }
  .content-area h2 { @apply text-2xl font-semibold mb-4 mt-8; }
  .content-area p  { @apply text-base leading-relaxed text-gray-700 mb-4; }
  .content-area ul { @apply list-disc list-inside mb-4 space-y-1; }
  .content-area li { @apply text-gray-700; }
}

๐Ÿ“ก @source: ์†Œ์Šค ํŒŒ์ผ ๋ช…์‹œ

Tailwind v4 ๋Š” ์ž๋™์œผ๋กœ ์†Œ์Šค ํŒŒ์ผ์„ ๊ฐ์ง€ํ•˜์ง€๋งŒ, ๋ช‡ ๊ฐ€์ง€ ๊ฒฝ์šฐ์— @source ๋กœ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ด์•ผ ํ•ด.

@import "tailwindcss";
 
/* node_modules ์•ˆ์— ์žˆ๋Š” UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ํด๋ž˜์Šค๋„ ๋ฒˆ๋“ค์— ํฌํ•จ */
@source "../node_modules/@my-company/ui-components/dist";
 
/* ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ํด๋ž˜์Šค ํŒŒ์ผ */
@source "./templates/**/*.html";

Tailwind ๊ฐ€ ํด๋ž˜์Šค๋ฅผ ๊ฐ์ง€ ๋ชปํ•  ๋•Œ

// โŒ ๋ฌธ์ œ: ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ ํด๋ž˜์Šค๋Š” Tailwind ๊ฐ€ ๊ฐ์ง€ ๋ชปํ•ด!
const color = 'blue';
const className = `bg-${color}-500`;  // Tailwind ๊ฐ€ ์ด ํด๋ž˜์Šค๋ฅผ ๋ฒˆ๋“ค์—์„œ ์ œ๊ฑฐ!
 
// โœ… ํ•ด๊ฒฐ๋ฒ• 1: ์ „์ฒด ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ •์ ์œผ๋กœ ์„ ์–ธ
const colorMap = {
  blue: 'bg-blue-500',
  red:  'bg-red-500',
  green: 'bg-green-500',
};
const className = colorMap[color];
 
// โœ… ํ•ด๊ฒฐ๋ฒ• 2: safelist (v3) ๋˜๋Š” ๊ฐ•์ œ ํฌํ•จ
// tailwind.config.js
module.exports = {
  safelist: [
    'bg-blue-500',
    'bg-red-500',
    'bg-green-500',
    // ๋˜๋Š” ํŒจํ„ด์œผ๋กœ
    { pattern: /bg-(blue|red|green)-(500|600|700)/ },
  ],
};

๐Ÿ’ป ์‹ค์ „: Tailwind ์™€ ์ปค์Šคํ…€ CSS ์˜ ๊ณต์กด

์™„์„ฑ๋œ globals.css ์˜ˆ์‹œ

/* app/globals.css */
@import "tailwindcss";
 
/* ===== ๋””์ž์ธ ํ† ํฐ ===== */
@theme {
  --color-brand-500: #8b5cf6;
  --color-brand-600: #7c3aed;
  --color-brand-700: #6d28d9;
  --font-sans: 'Pretendard Variable', -apple-system, sans-serif;
}
 
/* ===== Base Layer: ์ „์—ญ ๊ธฐ๋ณธ ์Šคํƒ€์ผ ===== */
@layer base {
  html { scroll-behavior: smooth; }
  body { word-break: keep-all; }
 
  /* ์ „์—ญ input ์Šคํƒ€์ผ */
  input[type="text"],
  input[type="email"],
  input[type="password"],
  textarea {
    @apply block w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm
           focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/20;
  }
}
 
/* ===== Components Layer: ์žฌ์‚ฌ์šฉ ์ปดํฌ๋„ŒํŠธ ===== */
@layer components {
  /* ๋งˆํฌ๋‹ค์šด ์ฝ˜ํ…์ธ  ์Šคํƒ€์ผ */
  .prose-korean {
    @apply text-base leading-relaxed text-gray-700 dark:text-gray-300;
  }
  .prose-korean h1 { @apply text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-0; }
  .prose-korean h2 { @apply text-2xl font-semibold text-gray-800 dark:text-gray-100 mb-4 mt-8; }
  .prose-korean p  { @apply mb-4; }
  .prose-korean code {
    @apply rounded bg-gray-100 px-1.5 py-0.5 text-sm font-mono dark:bg-gray-700;
  }
}

๐Ÿšจ @apply ๋‚จ์šฉ ๊ฒฝ๊ณ 

์ ˆ๋Œ€ ํ•˜๋ฉด ์•ˆ ๋˜๋Š” @apply ํŒจํ„ด

/* โŒ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Tailwind ๋ฅผ ์“ฐ๋Š” ์ด์œ ๊ฐ€ ์—†์–ด์ง */
 
/* ์นด๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ @apply ๋กœ ๋งŒ๋“ ๋‹ค? */
.card {
  @apply flex flex-col gap-3 rounded-xl border border-gray-200 bg-white p-5 shadow-md;
}
 
/* ๋ฒ„ํŠผ์„ @apply ๋กœ ๋งŒ๋“ ๋‹ค? */
.btn-primary {
  @apply rounded-xl bg-brand-600 px-6 py-3 font-semibold text-white hover:bg-brand-700;
}

์™œ ๋‚˜์œ๊ฐ€?

1. Tailwind ์˜ ์ตœ๋Œ€ ์žฅ์ ์ธ co-location (์Šคํƒ€์ผ์ด HTML ์˜†์—) ์ด ์‚ฌ๋ผ์ง
2. CSS ํŒŒ์ผ์ด ๋‹ค์‹œ ๊ธธ์–ด์ง€๊ณ , BEM ์ฒ˜๋Ÿผ ํŒŒ์ผ ๊ฐ„ ์ด๋™์ด ์ƒ๊น€
3. ์ด๋Ÿด ๊ฑฐ๋ฉด ๊ทธ๋ƒฅ CSS Modules ์“ฐ๋Š” ๊ฒŒ ๋‚˜์Œ
4. hover:, md:, dark: ๊ฐ™์€ variant ๋ฅผ @apply ์—์„œ ์“ฐ๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋™์ž‘ ๊ฐ€๋Šฅ

์˜ฌ๋ฐ”๋ฅธ ๋Œ€์•ˆ:

// โœ… React ์ปดํฌ๋„ŒํŠธ๋กœ ์ถ”์ถœ โ€” ์ด๊ฒŒ ์ง„์งœ ์ •๋‹ต
function Card({ children }: Props) {
  return (
    <div className="flex flex-col gap-3 rounded-xl border border-gray-200 bg-white p-5 shadow-md">
      {children}
    </div>
  );
}
 
function Button({ children }: Props) {
  return (
    <button className="rounded-xl bg-brand-600 px-6 py-3 font-semibold text-white hover:bg-brand-700">
      {children}
    </button>
  );
}

@apply ์‚ฌ์šฉ ์ ํ•ฉ์„ฑ ํŒ๋‹จํ‘œ

์ƒํ™ฉ@apply ์ ํ•ฉ?๋Œ€์•ˆ
์จ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜ค๋ฒ„๋ผ์ด๋“œโœ…์—†์Œ
CMS/๋งˆํฌ๋‹ค์šด ์ฝ˜ํ…์ธ  ์Šคํƒ€์ผโœ…์—†์Œ
์ „์—ญ HTML ์š”์†Œ ์Šคํƒ€์ผโœ…์—†์Œ
๋ฐ˜๋ณต๋˜๋Š” ์ปดํฌ๋„ŒํŠธ ํด๋ž˜์ŠคโŒReact ์ปดํฌ๋„ŒํŠธ ์ถ”์ถœ
๋ฒ„ํŠผ/์นด๋“œ ๊ฐ™์€ UI ์ปดํฌ๋„ŒํŠธโŒReact ์ปดํฌ๋„ŒํŠธ ์ถ”์ถœ

๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

๋””๋ ‰ํ‹ฐ๋ธŒ์—ญํ• ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
@layer baseHTML ๊ธฐ๋ณธ ์Šคํƒ€์ผ์ „์—ญ ํฐํŠธ, CSS reset, input ์Šคํƒ€์ผ
@layer components์žฌ์‚ฌ์šฉ ์ปดํฌ๋„ŒํŠธ์จ๋“œํŒŒํ‹ฐ ์˜ค๋ฒ„๋ผ์ด๋“œ, ๋งˆํฌ๋‹ค์šด ์Šคํƒ€์ผ
@layer utilities๋‹จ์ผ ์†์„ฑ ํด๋ž˜์Šค์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ (๊ฑฐ์˜ ์‚ฌ์šฉ ์•ˆ ํ•จ)
@applyCSS ์•ˆ์—์„œ Tailwind ์‚ฌ์šฉ์œ„์˜ ์˜ฌ๋ฐ”๋ฅธ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋งŒ
@source์†Œ์Šค ํŒŒ์ผ ๋ช…์‹œnode_modules, ๋™์  ๊ฒฝ๋กœ

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ์˜์ฒ ์ด๊ฐ€ react-markdown ์œผ๋กœ ๋ Œ๋”๋ง๋œ HTML ์„ ์Šคํƒ€์ผ๋งํ•ด์•ผ ํ•œ๋‹ค. Tailwind ํด๋ž˜์Šค๋ฅผ ์ง์ ‘ ๋ถ™์ผ ์ˆ˜ ์—†๋Š”๋ฐ, ์˜ฌ๋ฐ”๋ฅธ ์ ‘๊ทผ๋ฒ•์€?

โœ… ์ •๋‹ต: @layer components ์— .prose-custom h1 { @apply ... } ์ฒ˜๋Ÿผ ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ CSS ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์จ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(react-markdown, react-quill ๋“ฑ)๊ฐ€ ์ƒ์„ฑํ•œ HTML ์—๋Š” ์ง์ ‘ className ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†์–ด.
  • @layer components ์— ๋ž˜ํผ ํด๋ž˜์Šค(.prose-custom)๋ฅผ ๋งŒ๋“ค๊ณ , ๊ทธ ์•ˆ์—์„œ ํ•˜์œ„ HTML ํƒœ๊ทธ๋“ค์— @apply ๋กœ Tailwind ํด๋ž˜์Šค๋ฅผ ์ ์šฉํ•ด.
  • @tailwindcss/typography ํ”Œ๋Ÿฌ๊ทธ์ธ๋„ ๊ฐ™์€ ์›๋ฆฌ์•ผ โ€” prose ํด๋ž˜์Šค๋ฅผ ๋ž˜ํผ์— ๋ถ™์ด๋ฉด ๋‚ด๋ถ€ HTML ์ด ์•„๋ฆ„๋‹ต๊ฒŒ ์Šคํƒ€์ผ๋ง๋ผ.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "์ง์ ‘ ๋ชป ๊ฑด๋“œ๋ฆฌ๋ฉด @layer components ์—์„œ ๋ถ€๋ชจ-์ž์‹ ์„ ํƒ์ž๋กœ."

Q2. @layer base, @layer components, @layer utilities ์ค‘ ์–ด๋А ๋ ˆ์ด์–ด๊ฐ€ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๊ฐ€์žฅ ๋†’์€๊ฐ€?

โœ… ์ •๋‹ต: @layer utilities โ€” ๋‚˜์ค‘์— ์ •์˜๋œ ๋ ˆ์ด์–ด์ผ์ˆ˜๋ก ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • CSS Cascade Layers ์˜ ์›์น™: ๋™์ผํ•œ ํŠน์ด๋„(specificity)์—์„œ ๋‚˜์ค‘์— ์ •์˜๋œ ๋ ˆ์ด์–ด๊ฐ€ ์ด๊ฒจ.
  • Tailwind ์˜ ๋ ˆ์ด์–ด ์ˆœ์„œ: base < components < utilities.
  • ์ด ๋•๋ถ„์— @layer components ์˜ .card ์Šคํƒ€์ผ์„ @layer utilities ์˜ ๋‹จ์ผ ํด๋ž˜์Šค๋กœ ๋ฎ์–ด์”Œ์šธ ์ˆ˜ ์žˆ์–ด. ํด๋ž˜์Šค ์ถฉ๋Œ ๊ฑฑ์ • ์—†์ด!
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "base โ†’ components โ†’ utilities. ๋’ค๋กœ ๊ฐˆ์ˆ˜๋ก ๊ฐ•ํ•˜๋‹ค."

Q3. ์˜์ฒ ์ด๊ฐ€ const cls = bg-$-500`` ์ฒ˜๋Ÿผ ๋™์  ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ ์Šคํƒ€์ผ์ด ์ ์šฉ ์•ˆ ๋œ๋‹ค. ์™œ ๊ทธ๋Ÿฐ๊ฐ€?

โœ… ์ •๋‹ต: Tailwind ๋Š” ๋นŒ๋“œ ์‹œ ์†Œ์Šค ํŒŒ์ผ์„ ์ •์  ๋ถ„์„ํ•ด์„œ ์‚ฌ์šฉ๋œ ํด๋ž˜์Šค๋งŒ ๋ฒˆ๋“ค์— ํฌํ•จํ•œ๋‹ค. ๋™์ ์œผ๋กœ ์กฐํ•ฉ๋œ ํด๋ž˜์Šค ์ด๋ฆ„์€ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•ด์„œ ๋ฒˆ๋“ค์—์„œ ์ œ๊ฑฐ๋œ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • Tailwind ์˜ ๋นŒ๋“œ ์ตœ์ ํ™”(Tree-Shaking)๋Š” ์†Œ์Šค ํŒŒ์ผ์—์„œ ์™„์ „ํ•œ ํด๋ž˜์Šค ์ด๋ฆ„ ๋ฌธ์ž์—ด์„ ์ฐพ์•„. `bg-${color}-500` ์€ ์™„์ „ํ•œ ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ ๋™์  ํ‘œํ˜„์‹์ด๋ผ ๊ฐ์ง€ ๋ชปํ•ด.
  • ํ•ด๊ฒฐ์ฑ…: ์ „์ฒด ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ •์ ์œผ๋กœ ๋ฏธ๋ฆฌ ์„ ์–ธ (const map = { blue: 'bg-blue-500', ... }) ํ•˜๊ฑฐ๋‚˜, safelist ์— ์ง์ ‘ ๋“ฑ๋ก.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "Tailwind ๋Š” ์™„์ „ํ•œ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ฐพ์•„. ๋ฐ˜๋งŒ ์žˆ์–ด์„  ์•ˆ ๋ณด์ธ๋‹ค."

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜ @apply ์–ธ์ œ ์“ฐ๊ณ  ์–ธ์ œ ์“ฐ์ง€ ๋ง์•„์•ผ ํ•˜๋Š”์ง€๊ฐ€ ๋“œ๋””์–ด ๋ช…ํ™•ํ•ด์กŒ๋‹ค. ์จ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฑด๋“œ๋ฆด ๋•Œ๋ž‘ CMS ์ฝ˜ํ…์ธ  ์Šคํƒ€์ผ๋ง ํ•  ๋•Œ ๋ง๊ณ ๋Š” ์“ฐ์ง€ ๋ง์ž โ€” ์ด๊ฒŒ ์˜ค๋Š˜ ํ•ต์‹ฌ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋™์  ํด๋ž˜์Šค ์ด๋ฆ„์ด ์™œ ์•ˆ ๋˜๋Š”์ง€๋„ ์ดํ•ด๋๋‹ค. Tailwind ๊ฐ€ ๋นŒ๋“œ ํƒ€์ž„์— ์ •์  ๋ถ„์„์„ ํ•˜๋Š”๋ฐ, `bg-${color}-500` ์€ ๋ถ„์„์„ ํ”ผํ•ด๊ฐ€๋Š” ๊ฑฐ๋ผ์„œ. ๋‚˜๋„ ๋ชจ๋ฅด๊ฒŒ ์ด๊ฑธ ์“ด ๊ณณ์ด ๋ช‡ ๊ตฐ๋ฐ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์œผ๋‹ˆ๊นŒ ๋‚ด์ผ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋•Œ ํ•œ๋ฒˆ ์ ๊ฒ€ํ•ด๋ด์•ผ๊ฒ ๋‹ค.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "@apply ๋Š” '์–ด์ฉ” ์ˆ˜ ์—†์„ ๋•Œ์˜ ํƒˆ์ถœ๊ตฌ'๋‹ค. React ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋ฉด ๊ทธ๊ฒŒ ํ•ญ์ƒ ๋จผ์ €๋‹ค."

์ฃผ๋ง์— ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ธ€๋กœ๋ฒŒ ์Šคํƒ€์ผ ํ•œ๋ฒˆ ์ •๋ฆฌํ•ด์•ผ๊ฒ ๋‹ค. ์ง€๊ธˆ globals.css ๊ฐ€ ์ข€ ์ง€์ €๋ถ„ํ•œ๋ฐ, @layer ๋กœ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•ด์„œ ์˜ํ˜ธ ๋‹˜ ์นญ์ฐฌ ์ข€ ๋ฐ›์•„์•ผ์ง€. ์˜ค๋Š˜์€ ์ผ์ฐ ํ‡ด๊ทผ ์„ฑ๊ณต! ์ง‘์—์„œ ํŽธํ•˜๊ฒŒ ์‰ฌ์–ด์•ผ์ง€. ๐Ÿ›‹๏ธ


๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ