<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tailwind CSS on kenji.blog</title><link>http://kenji.blog/ko/tags/tailwind-css/</link><description>Recent content in Tailwind CSS on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ko</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 03:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ko/tags/tailwind-css/index.xml" rel="self" type="application/rss+xml"/><item><title>Hugo 테마 커스터마이징: Tailwind CSS 도입 방법</title><link>http://kenji.blog/ko/p/hugo-tailwind-css-customization-guide/</link><pubDate>Sun, 13 Sep 2026 03:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/hugo-tailwind-css-customization-guide/</guid><description>&lt;img src="http://kenji.blog/p/hugo-tailwind-css-customization-guide/img/eyecatch.jpg" alt="Featured image of post Hugo 테마 커스터마이징: Tailwind CSS 도입 방법" />&lt;h1 id="들어가며-정적-사이트-생성기-hugo와-tailwind-css의-강력한-시너지">들어가며: 정적 사이트 생성기 Hugo와 Tailwind CSS의 강력한 시너지
&lt;/h1>&lt;p>현대 웹 프론트엔드 개발에서 성능과 개발자 경험(DX: Developer Experience)의 양립은 모든 프로젝트에서 가장 중요한 과제 중 하나입니다. 정적 사이트 생성기(SSG) 중에서도 세계 최고 수준의 빌드 속도를 자랑하는 &lt;strong>Hugo&lt;/strong>와, 유틸리티 퍼스트라는 혁신적인 패러다임을 도입한 &lt;strong>Tailwind CSS&lt;/strong>를 결합하는 것은 이 과제에 대한 하나의 궁극적인 해답이라고 할 수 있습니다.&lt;/p>
&lt;p>Hugo는 Go 언어로 작성되어 있어, 수천 페이지의 사이트라도 단 몇 초 혹은 밀리초 단위로 빌드를 완료하는 경이로운 성능을 가지고 있습니다. 한편, Tailwind CSS는 사전에 정의된 무수히 많은 유틸리티 클래스(&lt;code>flex&lt;/code>, &lt;code>text-center&lt;/code>, &lt;code>mt-4&lt;/code> 등)를 HTML에 직접 작성함으로써, CSS 파일과 HTML 파일 사이를 오가는 컨텍스트 스위칭을 없애고 디자인 이터레이션을 가속화합니다.&lt;/p>
&lt;p>본 기사에서는 Hugo 테마에 Tailwind CSS를 도입하고, 나아가 PostCSS를 이용한 고도화된 에셋 파이프라인(Hugo Pipes)을 구축하는 절차를 아키텍처의 근간부터 수학적인 성능 최적화 관점에 이르기까지 철저하고 상세하게 해설합니다.&lt;/p>
&lt;hr>
&lt;h2 id="1-유틸리티-퍼스트-css와-컴포넌트-지향의-변천">1. 유틸리티 퍼스트 CSS와 컴포넌트 지향의 변천
&lt;/h2>&lt;p>Tailwind CSS의 도입 절차에 들어가기 전에, 왜 우리가 Tailwind CSS를 사용해야 하는지, 그 배경에 있는 CSS 설계 사상의 역사와 진화에 대해 깊이 이해해 두는 것은 매우 유익합니다.&lt;/p>
&lt;h3 id="기존-css-설계bem이나-oocss의-한계">기존 CSS 설계(BEM이나 OOCSS)의 한계
&lt;/h3>&lt;p>과거의 웹 개발에서는 시맨틱한 클래스명을 부여하는 것이 베스트 프랙티스로 여겨졌습니다. 예를 들어, 카드 컴포넌트를 작성할 경우 다음과 같이 HTML과 CSS를 분리했습니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__image&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image.jpg&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;thumbnail&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__content&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__title&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>제목&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__description&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>설명문이 이곳에 들어갑니다.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">border-radius&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">box-shadow&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="nb">rgba&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mf">0.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">overflow&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">hidden&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">card__title&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-size&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">1.5&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-weight&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">bold&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#333333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 이후 상세한 스타일이 계속됨 */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이러한 BEM(Block Element Modifier) 기반 설계는 프로젝트 규모가 작을 때는 기능하지만, 다음과 같은 문제를 일으키기 쉽습니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>이름 짓기의 고갈과 피로&lt;/strong>: 비슷한 컴포넌트를 만들 때마다 새로운 클래스명을 고민해야 합니다(예: &lt;code>card-news&lt;/code>, &lt;code>card-featured&lt;/code> 등).&lt;/li>
&lt;li>&lt;strong>CSS의 비대화&lt;/strong>: 새로운 기능을 추가할 때마다 CSS의 줄 수가 계속 늘어나고, 한 번 작성된 CSS는 &amp;ldquo;어디서 사용되는지 모른다&amp;quot;는 두려움 때문에 삭제되는 일이 적어 데드 코드가 축적됩니다.&lt;/li>
&lt;li>&lt;strong>컨텍스트 스위칭&lt;/strong>: HTML의 구조와 CSS의 스타일을 별도의 파일로 관리하기 때문에 에디터 상에서 탭을 오가는 횟수가 기하급수적으로 증가합니다.&lt;/li>
&lt;/ol>
&lt;h3 id="tailwind-css에-의한-패러다임-시프트">Tailwind CSS에 의한 패러다임 시프트
&lt;/h3>&lt;p>Tailwind CSS는 이러한 문제들을 &amp;lsquo;유틸리티 클래스의 조합&amp;rsquo;이라는 접근법으로 해결합니다. 위의 카드 컴포넌트는 Tailwind CSS를 사용하면 다음과 같이 됩니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;rounded-lg shadow-md bg-white overflow-hidden&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;w-full h-48 object-cover&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image.jpg&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;thumbnail&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;p-6&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-2xl font-bold text-gray-800&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>제목&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-2 text-gray-600&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>설명문이 이곳에 들어갑니다.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>클래스명 자체가 스타일의 구체적인 값(&lt;code>p-6&lt;/code>는 &lt;code>padding: 1.5rem;&lt;/code> 등)을 나타내고 있기 때문에, HTML을 보는 것만으로도 최종적인 렌더링 결과를 예측할 수 있습니다. 게다가 Tailwind의 JIT(Just-In-Time) 컴파일러에 의해 실제로 사용된 클래스만이 프로덕션용 CSS 파일로 추출되기 때문에 CSS의 파일 크기는 극한까지 작아집니다.&lt;/p>
&lt;hr>
&lt;h2 id="2-hugo-pipes와-postcss의-아키텍처">2. Hugo Pipes와 PostCSS의 아키텍처
&lt;/h2>&lt;p>Hugo에 Tailwind CSS를 통합하기 위해서는 &lt;strong>Hugo Pipes&lt;/strong>라고 불리는 에셋 처리 파이프라인을 이해할 필요가 있습니다. Hugo Pipes는 Sass/SCSS 컴파일, JavaScript 번들링 및 Minify, 그리고 이번에 사용하는 &lt;strong>PostCSS&lt;/strong> 실행 등 에셋과 관련된 모든 처리를 Hugo 내부에서 완결시키는 강력한 기능입니다.&lt;/p>
&lt;p>PostCSS는 JavaScript 플러그인을 사용하여 CSS를 변환하기 위한 도구입니다. Tailwind CSS 자체도 실은 PostCSS의 플러그인으로서 동작하고 있습니다.&lt;/p>
&lt;h3 id="postcss에-의한-ast추상-구문-트리-변환-메커니즘">PostCSS에 의한 AST(추상 구문 트리) 변환 메커니즘
&lt;/h3>&lt;p>PostCSS가 어떻게 CSS를 처리하고 있는지를 이해하는 것은 트러블슈팅을 할 때 크게 도움이 됩니다. 다음의 Mermaid 다이어그램은 PostCSS가 CSS 파일을 읽어 들이고, 플러그인을 통해 변환하여 최종적인 CSS를 출력하기까지의 파이프라인을 보여줍니다.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;원본 CSS (styles.css)&amp;#34;] --&amp;gt;|Parser| B[&amp;#34;AST (추상 구문 트리)&amp;#34;]
B --&amp;gt; C[&amp;#34;Plugin 1: Tailwind CSS&amp;#34;]
C --&amp;gt; D[&amp;#34;Plugin 2: Autoprefixer&amp;#34;]
D --&amp;gt; E[&amp;#34;Plugin N: cssnano&amp;#34;]
E --&amp;gt;|Stringifier| F[&amp;#34;컴파일 및 최적화된 CSS&amp;#34;]
&lt;/pre>
&lt;ol>
&lt;li>&lt;strong>Parser (파서)&lt;/strong>: 입력된 원본 CSS 문자열을 분석하여, 프로그램으로 조작 가능한 데이터 구조인 AST(추상 구문 트리)로 변환합니다.&lt;/li>
&lt;li>&lt;strong>Plugins (플러그인들)&lt;/strong>:
&lt;ul>
&lt;li>&lt;strong>Tailwind CSS&lt;/strong>: 템플릿 파일(HTML이나 Markdown)을 스캔하여, 사용되고 있는 유틸리티 클래스를 AST 상에 노드로 추가합니다. 또한 &lt;code>@tailwind&lt;/code> 지시어를 전개합니다.&lt;/li>
&lt;li>&lt;strong>Autoprefixer&lt;/strong>: &lt;code>Can I Use&lt;/code> 데이터베이스를 참조하여, 필요에 따라 벤더 프리픽스(&lt;code>-webkit-&lt;/code>, &lt;code>-moz-&lt;/code> 등)를 AST의 속성에 추가합니다.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Stringifier (스트링기파이어)&lt;/strong>: 변환이 완료된 AST를 다시 브라우저가 해석 가능한 CSS 문자열로 변환하여 출력합니다.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-환경-구축과-전제-조건">3. 환경 구축과 전제 조건
&lt;/h2>&lt;p>그러면 실제 도입 절차로 들어가 보겠습니다. 먼저 필요한 소프트웨어가 설치되어 있는지 확인합니다.&lt;/p>
&lt;h3 id="필수-요건">필수 요건
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Hugo Extended Version&lt;/strong>:
일반적인 Hugo가 아닌, Sass/SCSS 처리 기능이나 네이티브 PostCSS 연동 기능이 포함된 &lt;strong>Extended 버전&lt;/strong>이 필수입니다. 터미널에서 다음 명령어를 실행하여 버전 정보에 &lt;code>extended&lt;/code>라는 문자열이 포함되어 있는지 확인해 주세요.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 예상되는 출력 예시:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># hugo v0.121.2-4146... windows/amd64 BuildDate=... VendorInfo=gohugoio +extended&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Node.js와 npm&lt;/strong>:
Tailwind CSS나 PostCSS 등의 의존성 패키지는 Node.js 상에서 동작합니다. Node.js(LTS 버전 권장)가 설치되어 있는지 확인합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">node -v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm -v
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="npm-패키지-설치">npm 패키지 설치
&lt;/h3>&lt;p>프로젝트 루트 디렉토리(Hugo 설정 파일 &lt;code>hugo.toml&lt;/code>이 있는 위치)에서 npm을 초기화하고, 필요한 패키지를 설치합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># package.json 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm init -y
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 개발 의존성 패키지로 Tailwind CSS, PostCSS, Autoprefixer 설치&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install -D tailwindcss postcss postcss-cli autoprefixer
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>[!IMPORTANT]
&lt;code>postcss-cli&lt;/code>가 설치되어 있지 않으면, Hugo 내부에서 PostCSS를 호출할 때 에러가 발생할 수 있습니다. Hugo Pipes는 내부적으로 &lt;code>postcss-cli&lt;/code>를 사용하므로 반드시 설치해 두어야 합니다.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="4-설정-파일-구축-postcss--tailwind-css">4. 설정 파일 구축 (PostCSS &amp;amp; Tailwind CSS)
&lt;/h2>&lt;p>패키지 설치가 완료되면, 프로젝트의 동작을 제어할 2개의 중요한 설정 파일을 작성합니다. 프로젝트 루트 디렉토리에 배치해 주세요.&lt;/p>
&lt;h3 id="tailwindconfigjs-작성">tailwind.config.js 작성
&lt;/h3>&lt;p>터미널에서 다음 명령어를 실행하면 기본 설정 파일이 생성됩니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npx tailwindcss init
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>생성된 &lt;code>tailwind.config.js&lt;/code>를 에디터로 열고, &lt;code>content&lt;/code> 속성을 설정합니다. 이 부분은 매우 중요합니다. Tailwind는 여기서 지정된 경로의 파일을 분석하여 사용되고 있는 클래스를 추출합니다. Hugo의 프로젝트 구조에 맞춰 레이아웃 파일이나 콘텐츠 파일을 정확하게 지정합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/** @type {import(&amp;#39;tailwindcss&amp;#39;).Config} */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Hugo의 디렉토리 구조에 맞춰 스캔 대상을 지정
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">content&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./content/**/*.md&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./content/**/*.html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./layouts/**/*.html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./assets/**/*.js&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 테마를 사용하는 경우에는 테마의 디렉토리도 포함해야 합니다
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// &amp;#34;./themes/my-theme/layouts/**/*.html&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">theme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">extend&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 커스텀 색상이나 폰트 확장을 이곳에서 진행합니다
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">colors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;brand-primary&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#3490dc&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;brand-secondary&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#ffed4a&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fontFamily&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;sans&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Helvetica Neue&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Arial&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Hiragino Kaku Gothic ProN&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Meiryo&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sans-serif&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 필요에 따라 공식 플러그인 추가 (예: Typography 플러그인)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// require(&amp;#39;@tailwindcss/typography&amp;#39;),
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="postcssconfigjs-작성">postcss.config.js 작성
&lt;/h3>&lt;p>다음으로, PostCSS가 어떤 플러그인을 어떤 순서로 실행할지를 정의하는 &lt;code>postcss.config.js&lt;/code>를 프로젝트 루트에 작성합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tailwindcss&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoprefixer&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이 설정을 통해 Hugo가 PostCSS를 호출할 때, 먼저 Tailwind CSS 처리가 수행되고 그 다음에 Autoprefixer에 의한 벤더 프리픽스 부여가 진행됩니다.&lt;/p>
&lt;hr>
&lt;h2 id="5-hugo에서의-css-에셋-파이프라인-구축">5. Hugo에서의 CSS 에셋 파이프라인 구축
&lt;/h2>&lt;p>설정이 완료되면 드디어 Hugo 테마 측에 Tailwind CSS를 통합합니다.&lt;/p>
&lt;h3 id="5-1-엔트리-포인트가-되는-css-파일-작성">5-1. 엔트리 포인트가 되는 CSS 파일 작성
&lt;/h3>&lt;p>&lt;code>assets/css/&lt;/code> 디렉토리(존재하지 않는다면 만들어주세요)에 엔트리 포인트가 될 CSS 파일을 작성합니다. 여기서는 &lt;code>main.css&lt;/code>로 하겠습니다.&lt;/p>
&lt;p>&lt;strong>파일 경로: &lt;code>assets/css/main.css&lt;/code>&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Tailwind의 기본 스타일(리셋 CSS 등) 불러오기 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">base&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 컴포넌트 클래스 불러오기 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">components&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 유틸리티 클래스 불러오기 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">utilities&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 독자적인 커스텀 CSS가 필요한 경우 여기에 추가할 수 있지만,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> 가능한 한 tailwind.config.js의 extend로 대응하는 것을 권장합니다 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">layer&lt;/span> &lt;span class="nt">components&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">btn-primary&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">@apply&lt;/span> &lt;span class="err">bg-blue-500&lt;/span> &lt;span class="n">hover&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">bg-blue-700&lt;/span> &lt;span class="kc">text&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">white&lt;/span> &lt;span class="k">font&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">bold&lt;/span> &lt;span class="n">py-2&lt;/span> &lt;span class="n">px-4&lt;/span> &lt;span class="n">rounded&lt;/span> &lt;span class="k">transition&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">colors&lt;/span> &lt;span class="n">duration-300&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="5-2-레이아웃-파일headhtml-편집">5-2. 레이아웃 파일(head.html) 편집
&lt;/h3>&lt;p>다음으로, Hugo의 템플릿에서 위에서 만든 CSS 파일을 불러와 PostCSS로 처리하는 파이프라인을 작성합니다. 일반적으로 &lt;code>&amp;lt;head&amp;gt;&lt;/code> 태그 내부를 정의하고 있는 부분 템플릿(예: &lt;code>layouts/partials/head.html&lt;/code>)을 편집합니다.&lt;/p>
&lt;p>&lt;strong>파일 경로: &lt;code>layouts/partials/head.html&lt;/code>&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go-html-template" data-lang="go-html-template">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;viewport&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;width=device-width, initial-scale=1&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Title&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span> | &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Site.Title&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- assets/css/main.css 가져오기 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">:=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">resources&lt;/span>&lt;span class="na">.Get&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;css/main.css&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- PostCSS의 옵션 정의 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$options&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">:=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">dict&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;inlineImports&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">true&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">resources&lt;/span>&lt;span class="na">.PostCSS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$options&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 프로덕션 환경(Production)을 위한 에셋 최적화 파이프라인 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">hugo&lt;/span>&lt;span class="na">.IsProduction&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 1. Minify (압축) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">minify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 2. Fingerprint (캐시 버스팅을 위한 해시 부여) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">fingerprint&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;sha512&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 3. SRI(Subresource Integrity)를 포함하여 태그 출력 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.RelPermalink&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span> &lt;span class="na">integrity&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.Data.Integrity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span> &lt;span class="na">crossorigin&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;anonymous&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 개발 환경(Development)에서는 압축하지 않고 그대로 출력 (빌드 속도 우선) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.RelPermalink&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="파이프라인-해설-및-mermaid-다이어그램">파이프라인 해설 및 Mermaid 다이어그램
&lt;/h4>&lt;p>위의 Go 템플릿 코드가 어떻게 CSS 파일을 처리해 나가는지, 일련의 파이프라인 처리를 다이어그램으로 설명합니다.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;assets/css/main.css&amp;#34;] --&amp;gt;|resources.Get| B[&amp;#34;Hugo 메모리 공간&amp;#34;]
B --&amp;gt;|resources.PostCSS| C[&amp;#34;PostCSS + Tailwind JIT 처리&amp;#34;]
C --&amp;gt; D{&amp;#34;hugo.IsProduction ?&amp;#34;}
D -- &amp;#34;Yes (프로덕션 환경)&amp;#34; --&amp;gt; E[&amp;#34;minify (공백·줄바꿈 삭제)&amp;#34;]
E --&amp;gt; F[&amp;#34;fingerprint (해시 계산)&amp;#34;]
F --&amp;gt; G[&amp;#34;public/css/main.[hash].css (SRI 포함)&amp;#34;]
D -- &amp;#34;No (개발 환경)&amp;#34; --&amp;gt; H[&amp;#34;public/css/main.css&amp;#34;]
&lt;/pre>
&lt;ol>
&lt;li>&lt;strong>&lt;code>resources.Get&lt;/code>&lt;/strong>: &lt;code>assets&lt;/code> 디렉토리 내의 지정된 파일을 찾아 메모리 상의 리소스 객체로 로드합니다.&lt;/li>
&lt;li>&lt;strong>&lt;code>resources.PostCSS&lt;/code>&lt;/strong>: 프로젝트 루트의 &lt;code>postcss.config.js&lt;/code>를 참조하여, CSS 소스 코드에 Tailwind CSS와 Autoprefixer 처리를 적용합니다. 개발 환경(&lt;code>hugo server&lt;/code>)에서는 JIT 모드가 작동하여, 파일 변경 시 필요한 클래스만을 빠르게 생성합니다.&lt;/li>
&lt;li>&lt;strong>&lt;code>minify&lt;/code>&lt;/strong>: 프로덕션 환경 빌드 시(&lt;code>hugo --environment production&lt;/code> 등)에 불필요한 공백이나 주석을 삭제하여 파일 크기를 최소화합니다.&lt;/li>
&lt;li>&lt;strong>&lt;code>fingerprint&lt;/code>&lt;/strong>: 파일의 내용에 기반하여 SHA 해시를 계산하고 파일명에 부여합니다(예: &lt;code>main.ab12cd...css&lt;/code>). 이를 통해 브라우저의 강력한 캐시를 활용하면서 CSS 업데이트 시에는 확실하게 새로운 파일을 불러오게 하는 &amp;lsquo;캐시 버스팅&amp;rsquo;을 실현합니다.&lt;/li>
&lt;li>&lt;strong>&lt;code>integrity&lt;/code>&lt;/strong>: Fingerprint로 계산된 해시값을 사용하여 CDN 등에서의 위변조를 방지하는 SRI 속성을 출력합니다.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="6-css-최적화에서의-수학적-성능-분석">6. CSS 최적화에서의 수학적 성능 분석
&lt;/h2>&lt;p>Tailwind CSS를 도입하는 가장 큰 장점 중 하나는 전송되는 CSS 파일 크기의 극소화입니다. 이것이 웹 성능(특히 First Contentful Paint: FCP)에 어떤 영향을 미치는지 수학적인 모델을 사용하여 정량적으로 분석해 보겠습니다.&lt;/p>
&lt;h3 id="css-파일-크기-감소-모델">CSS 파일 크기 감소 모델
&lt;/h3>&lt;p>기존 CSS 프레임워크(Bootstrap 등)에서는 사용하지 않는 스타일까지 포함하여 전체가 로드되기 때문에 파일 크기 $S_{original}$이 커지기 쉽습니다(약 150KB〜200KB).
Tailwind CSS의 JIT 컴파일러에 의한 불필요한 클래스 제거(Purge) 적용 후의 크기를 $S_{purged}$라고 하면, 감소율 $R_{purge}$를 사용하여 다음과 같이 나타낼 수 있습니다.&lt;/p>
$$
S_{purged} = S_{original} \times (1 - R_{purge})
$$&lt;p>일반적인 프로젝트에서 $R_{purge}$는 $0.9$(90% 감소) 가까이에 도달하며, $S_{purged}$는 불과 10KB〜20KB 정도 내에 머뭅니다.&lt;/p>
&lt;p>게다가 전송 시에는 서버 측에서 Brotli나 Gzip에 의한 압축이 이루어집니다. 압축률을 $R_{compress}$(통상적으로 약 0.7〜0.8)라고 하면 네트워크를 흐르는 최종 페이로드 크기 $S_{final}$은 다음 공식으로 계산됩니다.&lt;/p>
$$
S_{final} = S_{purged} \times (1 - R_{compress})
$$&lt;h3 id="중요-렌더링-경로critical-rendering-path와-네트워크-지연">중요 렌더링 경로(Critical Rendering Path)와 네트워크 지연
&lt;/h3>&lt;p>브라우저가 화면에 첫 콘텐츠를 그릴 때까지의 시간(FCP)은 HTML 다운로드 시간, CSS 다운로드 시간, 그리고 렌더링 시간의 합계로 근사할 수 있습니다.&lt;/p>
$$
T_{FCP} \approx RTT + \frac{S_{HTML}}{BW} + RTT + \frac{S_{final}}{BW} + T_{render}
$$&lt;p>여기서,&lt;/p>
&lt;ul>
&lt;li>$RTT$: Round Trip Time (서버와의 왕복 통신 지연 시간)&lt;/li>
&lt;li>$BW$: 네트워크 대역폭 (Bandwidth)&lt;/li>
&lt;/ul>
&lt;p>모바일 환경 등 $BW$가 좁고 $RTT$가 큰(지연이 긴) 환경에서, $S_{final}$을 수 킬로바이트 단위까지 깎아낼 수 있는 Tailwind CSS의 접근 방식은 $\frac{S_{final}}{BW}$ 항을 극한까지 0에 가깝게 만들어, 경이로운 점수(Google PageSpeed Insights 등)를 내는 원동력이 됩니다.&lt;/p>
&lt;hr>
&lt;h2 id="7-개발-서버-실행과-핫-리로드-확인">7. 개발 서버 실행과 핫 리로드 확인
&lt;/h2>&lt;p>모든 설정이 완료되었다면 Hugo 개발 서버를 실행하고, Tailwind CSS가 올바르게 작동하는지 확인합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo server -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>브라우저에서 &lt;code>http://localhost:1313/&lt;/code>에 접속하여 사이트가 표시되는지 확인합니다.
Markdown 콘텐츠 파일이나 Hugo 템플릿(&lt;code>layouts/&lt;/code> 이하 파일)을 열어 클래스를 추가해 보세요.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- 테스트용 Tailwind 클래스 적용 예시 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;bg-gradient-to-r from-blue-500 to-purple-600 text-white p-8 rounded-xl shadow-2xl text-center transform transition duration-500 hover:scale-105&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-4xl font-extrabold tracking-tight&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Tailwind CSS + Hugo is Awesome!&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-4 text-lg font-medium&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>핫 리로드가 즉각적으로 반영되는 것을 확인해 보세요.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>파일을 저장하는 순간, Hugo의 강력한 파일 감시자와 Tailwind의 JIT 컴파일러가 연동하여 밀리초 단위로 CSS가 재구축되고, 브라우저가 자동으로 새로고침되는(핫 리로드) 쾌감을 맛볼 수 있을 것입니다.&lt;/p>
&lt;h3 id="트러블슈팅-스타일이-반영되지-않을-경우">트러블슈팅: 스타일이 반영되지 않을 경우
&lt;/h3>&lt;p>만약 변경 사항이 반영되지 않는다면 다음 사항들을 체크해 보세요.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>&lt;code>tailwind.config.js&lt;/code>의 &lt;code>content&lt;/code> 경로 설정&lt;/strong>
스캔 대상 파일 경로가 잘못되어 있으면 Tailwind는 해당 파일 내에서 사용 중인 클래스를 감지하지 못하여 CSS에 출력하지 않습니다. 특히 테마를 사용하는 경우 테마 디렉토리의 경로가 누락되지 않았는지 확인하세요.&lt;/li>
&lt;li>&lt;strong>PostCSS 에러&lt;/strong>
터미널의 Hugo 서버 로그에 &lt;code>Error: failed to transform resource: PostCSS not found&lt;/code> 같은 에러가 출력된다면, &lt;code>npm install&lt;/code>이 제대로 실행되지 않았거나 &lt;code>postcss-cli&lt;/code>가 부족할 가능성이 있습니다.&lt;/li>
&lt;li>&lt;strong>Hugo 캐시 삭제&lt;/strong>
드물게 Hugo의 캐시 문제로 오래된 CSS가 남는 경우가 있습니다. 서버를 끄고 &lt;code>hugo server --ignoreCache&lt;/code>로 다시 실행하거나, OS의 임시 디렉토리(&lt;code>/tmp/hugo_cache/&lt;/code> 등)를 삭제해 보세요.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-프로덕션-환경용-빌드와-더욱-고도화된-설정">8. 프로덕션 환경용 빌드와 더욱 고도화된 설정
&lt;/h2>&lt;p>사이트를 프로덕션 서버(Netlify, Vercel, GitHub Pages, Cloudflare Pages 등)에 배포할 때는, 환경 변수를 설정하여 프로덕션용 최적화 파이프라인을 실행해야 합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 프로덕션 빌드 명령어 예시&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">NODE_ENV&lt;/span>&lt;span class="o">=&lt;/span>production hugo --minify --environment production
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>--environment production&lt;/code> 플래그를 붙임으로써 &lt;code>head.html&lt;/code> 안의 &lt;code>{{ if hugo.IsProduction }}&lt;/code> 블록이 실행되며, CSS의 Minify화와 Fingerprint 부여가 수행됩니다.&lt;/p>
&lt;h3 id="typography-플러그인을-활용한-markdown-스타일링">Typography 플러그인을 활용한 Markdown 스타일링
&lt;/h3>&lt;p>Hugo 같은 블로그나 문서 사이트에서는 Markdown에서 생성된 순수 HTML 요소(&lt;code>&amp;lt;h1&amp;gt;&lt;/code>, &lt;code>&amp;lt;p&amp;gt;&lt;/code>, &lt;code>&amp;lt;ul&amp;gt;&lt;/code> 등)에 직접 클래스를 추가할 수 없습니다. 이러한 경우에 매우 유용한 것이 Tailwind 공식 &lt;strong>Typography 플러그인&lt;/strong>입니다.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>플러그인 설치&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install -D @tailwindcss/typography
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;code>tailwind.config.js&lt;/code>에 추가&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;@tailwindcss/typography&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>템플릿에 적용
글 본문을 출력하는 컨테이너 요소에 &lt;code>prose&lt;/code> 클래스(및 취향에 맞게 색상이나 크기의 변형)를 부여하는 것만으로 아름다운 기본 스타일이 적용됩니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go-html-template" data-lang="go-html-template">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">article&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;prose prose-lg prose-blue mx-auto mt-10&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Content&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">article&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>이것으로 손수 복잡한 CSS 선택자(&lt;code>.article-content h2 { ... }&lt;/code>)를 작성할 필요가 전혀 없어지며, 컴포넌트의 모듈성이 완벽하게 유지됩니다.&lt;/p>
&lt;hr>
&lt;h2 id="9-요약-유지-보수성이-뛰어난-프론트엔드-생태계-완성">9. 요약: 유지 보수성이 뛰어난 프론트엔드 생태계 완성
&lt;/h2>&lt;p>수고하셨습니다. 이것으로 Hugo의 초고속 정적 사이트 생성 엔진과 Tailwind CSS의 모던한 스타일링 기능, 그리고 PostCSS의 확장성을 갖춘 완벽한 웹 개발 에셋 파이프라인이 완성되었습니다.&lt;/p>
&lt;p>이 아키텍처의 뛰어난 점은 **&amp;ldquo;설정은 처음 한 번으로 끝난다&amp;rdquo;**는 것입니다. 한 번 파이프라인을 구축해 두면, 개발자는 CSS 파일을 열 필요 없이 직관적인 유틸리티 클래스를 HTML이나 Markdown 템플릿에 작성하는 것만으로 복잡한 UI를 놀라운 속도로 조립해 나갈 수 있습니다.&lt;/p>
&lt;p>또한 출력되는 CSS 크기가 항상 최소화되기 때문에 Core Web Vitals 점수 향상으로도 직결되며 SEO 관점에서도 매우 유리하게 작용합니다.&lt;/p>
&lt;p>Hugo와 Tailwind CSS의 조합은 개인 기술 블로그부터 대규모 기업 사이트까지 모든 프로젝트에서 &amp;lsquo;최고의 선택지&amp;rsquo; 중 하나로 계속 남을 것입니다. 부디 이 강력한 툴체인을 활용하여 쾌적한 웹 개발 라이프를 즐기시길 바랍니다!&lt;/p></description></item><item><title>블로그의 다크 모드 지원을 위한 CSS 설계의 포인트</title><link>http://kenji.blog/ko/p/dark-mode-css-design-patterns/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/dark-mode-css-design-patterns/</guid><description>&lt;img src="http://kenji.blog/p/dark-mode-css-design-patterns/img/eyecatch.jpg" alt="Featured image of post 블로그의 다크 모드 지원을 위한 CSS 설계의 포인트" />&lt;p>현대 웹 개발에서 다크 모드(Dark Mode) 지원은 단순한 &amp;lsquo;있으면 좋은 기능(Nice to have)&amp;lsquo;에서 사용자 경험(UX)을 향상시키기 위한 &amp;lsquo;필수 요건(Must have)&amp;lsquo;으로 변화하고 있습니다. 특히 블로그나 문서 사이트처럼 장시간 텍스트 읽기를 전제로 하는 미디어에서는 사용자의 눈의 피로를 덜어주고 기기의 배터리 소모를 줄이는 효과가 있기 때문에, 다크 모드 지원의 중요성은 매우 높다고 할 수 있습니다.&lt;/p>
&lt;p>본 기사에서는 블로그의 다크 모드 지원에 있어 피할 수 없는 기술적인 과제와 유지보수성이 높은 CSS 설계의 핵심을 프런트엔드 엔지니어의 관점에서 매우 깊이 파고들어 해설합니다. CSS Custom Properties(CSS 변수)의 활용, FOUC(Flash of Unstyled Content)를 방지하기 위한 고급 JavaScript 제어와 SSR 연동, 접근성(WCAG 2.1 AAA)을 보장하기 위한 색채 설계(RGB, HSL, 그리고 최신 OKLCH), 나아가 Tailwind CSS를 활용한 실전 코드 예시까지 다크 모드 구현의 모든 것을 망라합니다.&lt;/p>
&lt;hr>
&lt;h2 id="1-css-custom-propertiescss-변수를-이용한-테마-설계의-기초">1. CSS Custom Properties(CSS 변수)를 이용한 테마 설계의 기초
&lt;/h2>&lt;p>다크 모드를 구현하는 데 있어 현재 가장 표준적이고 강력한 방법이 &lt;strong>CSS Custom Properties(CSS 변수)&lt;/strong> 를 이용한 접근법입니다. Sass 같은 CSS 전처리기의 변수(&lt;code>$color&lt;/code>)가 컴파일 시점에 정적으로 해결되는 반면, CSS 변수는 브라우저의 런타임에서 동적으로 해결 및 덮어쓰기 됩니다. 이를 통해 JavaScript에서 클래스를 전환하는 것만으로 페이지 전체의 색조를 순식간에 변경하는 것이 가능해집니다.&lt;/p>
&lt;h3 id="11-기본적인-컬러-테마-정의">1.1 기본적인 컬러 테마 정의
&lt;/h3>&lt;p>먼저, &lt;code>:root&lt;/code> 가상 클래스를 사용하여 라이트 모드(기본값)의 컬러 팔레트를 정의합니다. 그리고 &lt;code>[data-theme='dark']&lt;/code>와 같은 속성(또는 &lt;code>.dark&lt;/code> 클래스)이 부여되었을 때, 해당 변수들을 덮어쓰는 설계 패턴이 정석입니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 라이트 모드(기본값)의 변수 정의 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f3f4f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#4b5563&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#3b82f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#e5e7eb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 다크 모드 시의 변수 덮어쓰기 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 실제 적용 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bg&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">text&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">background-color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">a&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">accent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이렇게 레이아웃이나 타이포그래피 지정과 색상(테마) 지정을 완전히 분리함으로써 CSS의 유지보수성은 비약적으로 향상됩니다.&lt;/p>
&lt;h3 id="12-media-prefers-color-scheme-dark-의-활용">1.2 @media (prefers-color-scheme: dark) 의 활용
&lt;/h3>&lt;p>OS 레벨에서 다크 모드가 설정된 경우, 웹사이트 첫 방문 시부터 자동으로 다크 테마를 적용하는 것이 UX 관점에서 바람직합니다. 이를 실현하는 것이 &lt;code>@media (prefers-color-scheme: dark)&lt;/code> 라는 미디어 쿼리입니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* OS의 환경 설정이 다크 모드인 경우의 폴백 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">media&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="nt">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nt">dark&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이 표기법에서는 사용자가 명시적으로 라이트 모드를 선택(&lt;code>data-theme='light'&lt;/code>)하지 않는 한, OS의 다크 모드 설정을 존중하여 변수를 덮어씁니다.&lt;/p>
&lt;hr>
&lt;h2 id="2-색-공간의-이해와-접근성wcag-21-aaa">2. 색 공간의 이해와 접근성(WCAG 2.1 AAA)
&lt;/h2>&lt;p>다크 모드의 색채 설계에서 단순히 &amp;lsquo;배경을 검게, 글자를 하얗게&amp;rsquo; 하는 것만으로는 불충분합니다. 대비가 너무 강하면 헐레이션이 일어나 오히려 읽기 힘들어지고, 대비가 너무 낮으면 시인성이 떨어집니다. Web Content Accessibility Guidelines (WCAG)에서는 시인성을 확보하기 위한 대비율(명암비)이 엄격하게 정의되어 있습니다.&lt;/p>
&lt;h3 id="21-wcag-대비율-계산식">2.1 WCAG 대비율 계산식
&lt;/h3>&lt;p>WCAG에서의 대비율(Contrast Ratio) $CR$ 은 배경색과 전경색의 상대 휘도(Relative Luminance)를 이용하여 다음과 같이 정의됩니다.&lt;/p>
$$CR = \frac{L_{lighter} + 0.05}{L_{darker} + 0.05}$$&lt;p>여기서 $L_{lighter}$는 밝은 쪽 색의 상대 휘도, $L_{darker}$는 어두운 쪽 색의 상대 휘도입니다(값의 범위는 0.0부터 1.0까지). WCAG 2.1의 레벨 AAA를 달성하기 위해서는 일반 텍스트에서 &lt;strong>7:1 이상&lt;/strong>, 큰 텍스트에서 &lt;strong>4.5:1 이상&lt;/strong>의 대비율이 요구됩니다.&lt;/p>
&lt;p>상대 휘도 $L$ 은 sRGB 색 공간의 RGB 값으로부터 아래의 복잡한 수식으로 계산됩니다.&lt;/p>
$$L = 0.2126 \times R + 0.7152 \times G + 0.0722 \times B$$&lt;p>각 성분($R, G, B$)은 원래의 8비트 값($R_{sRGB}$)을 255로 나눈 정규화 값을 사용하여 감마 보정을 풀기 위한 다음 변환을 수행합니다.&lt;/p>
$$
R, G, B =
\begin{cases}
\frac{C_{sRGB}}{12.92} &amp; \text{if } C_{sRGB} \le 0.03928 \\
\left( \frac{C_{sRGB} + 0.055}{1.055} \right)^{2.4} &amp; \text{otherwise}
\end{cases}
$$&lt;p>이 계산을 수동으로 하는 것은 어렵지만, 색채 설계 도구를 활용하면 대비율이 7:1 ($CR \ge 7.0$)을 만족하는 색을 기계적으로 선정할 수 있습니다.&lt;/p>
&lt;h3 id="22-hsl-vs-rgb-vs-oklch">2.2 HSL vs RGB vs OKLCH
&lt;/h3>&lt;p>컬러 팔레트를 만들 때, 예전에는 RGB나 HSL이 주류였습니다. 하지만 이것들에는 &amp;lsquo;지각적 균일성&amp;rsquo;이라는 관점에서 큰 결함이 있습니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>RGB&lt;/strong>: 기계적인 빛의 삼원색이며, 사람이 직관적으로 &amp;lsquo;밝게 한다&amp;rsquo;, &amp;lsquo;어둡게 한다&amp;rsquo;와 같은 조정을 하기가 어렵습니다.&lt;/li>
&lt;li>&lt;strong>HSL&lt;/strong>: 색상(Hue), 채도(Saturation), 명도(Lightness)를 사용하지만, HSL의 &amp;lsquo;명도(L)&amp;lsquo;는 사람 눈의 지각적인 밝기와 일치하지 않습니다. 예를 들어 HSL에서 명도 50%인 순수한 노란색과 순수한 파란색은 수치상으로는 같은 밝기지만, 사람의 눈에는 노란색이 압도적으로 밝게 보입니다.&lt;/li>
&lt;li>&lt;strong>OKLCH&lt;/strong>: 최근 CSS Color Module Level 4에서 도입된 최신 색 공간입니다. Lightness(지각적 명도), Chroma(채도), Hue(색상)로 구성되어 있으며, **인간의 시각 특성과 완전히 일치(지각적 균일)**합니다.&lt;/li>
&lt;/ul>
&lt;p>OKLCH를 사용하면 색상(Hue)을 변경해도 동일한 지각적 명도(Lightness)를 유지할 수 있기 때문에 다크 모드용 컬러 팔레트 생성이 극도로 예측 가능하고 안전해집니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* OKLCH를 사용한 CSS 변수의 정의 예 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 라이트 모드의 기본 명도를 높게, 채도를 억제하여 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.98&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.25&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.65&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;dark&amp;#34;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 다크 모드에서는 명도를 반전시키는 것만으로, 지각적 대비를 유지하기 쉬움 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.20&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.95&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.75&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c">/* 다크 모드용으로 조금 밝게 하여 시인성을 확보 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이와 같이 OKLCH를 도입함으로써 여러 테마 간에 일관된 대비율(WCAG AAA 수준)을 보장하는 로직을 심플하게 구축할 수 있습니다.&lt;/p>
&lt;hr>
&lt;h2 id="3-foucflash-of-unstyled-content-방지와-ssr-하이드레이션">3. FOUC(Flash of Unstyled Content) 방지와 SSR 하이드레이션
&lt;/h2>&lt;p>다크 모드 지원에서 개발자를 가장 괴롭히는 것이 &lt;strong>FOUC(Flash of Unstyled Content)&lt;/strong> 라고 불리는 화면 깜빡임 문제입니다.&lt;/p>
&lt;h3 id="31-클라이언트-사이드-js를-통한-테마-전환의-함정">3.1 클라이언트 사이드 JS를 통한 테마 전환의 함정
&lt;/h3>&lt;p>React나 Vue 같은 SPA(혹은 SSG를 통한 정적 사이트)에서 사용자의 설정을 &lt;code>localStorage&lt;/code> 에 저장하고, JavaScript로 불러와 테마를 전환하는 방식이 일반적입니다. 하지만 이 처리를 React의 &lt;code>useEffect&lt;/code> 등에서 수행하면 다음과 같은 문제가 발생합니다.&lt;/p>
&lt;ol>
&lt;li>브라우저가 라이트 모드의 HTML/CSS를 렌더링한다.&lt;/li>
&lt;li>JS 번들이 로드되고 실행된다.&lt;/li>
&lt;li>&lt;code>localStorage&lt;/code> 에서 &lt;code>dark&lt;/code> 설정을 읽어온다.&lt;/li>
&lt;li>HTML에 &lt;code>dark&lt;/code> 클래스가 부여되고 화면이 갑자기 어두워진다 (깜빡임).&lt;/li>
&lt;/ol>
&lt;h3 id="32-완벽한-fouc-방지책-cookie와-ssr의-활용">3.2 완벽한 FOUC 방지책: Cookie와 SSR의 활용
&lt;/h3>&lt;p>FOUC를 완전히 방지하고 하이드레이션 오류를 막기 위한 모범 사례는 &lt;strong>사용자의 테마 설정을 &lt;code>document.cookie&lt;/code> 에 저장하고, 서버 사이드 렌더링(SSR) 단계에서 적절한 클래스를 부여한 HTML을 반환&lt;/strong> 하는 것입니다.&lt;/p>
&lt;p>아래 시퀀스 다이어그램은 Cookie를 이용한 테마 초기화의 이상적인 흐름을 보여줍니다.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant User as &amp;#34;사용자&amp;#34;
participant Browser as &amp;#34;브라우저 OS&amp;#34;
participant Server as &amp;#34;서버 SSR&amp;#34;
participant DOM as &amp;#34;클라이언트 DOM&amp;#34;
User-&amp;gt;&amp;gt;Browser: &amp;#34;페이지 요청&amp;#34;
Browser-&amp;gt;&amp;gt;Server: &amp;#34;Cookie를 포함한 GET 요청 (theme=dark)&amp;#34;
Server--&amp;gt;&amp;gt;Browser: &amp;#34;&amp;lt;html data-theme=&amp;#39;dark&amp;#39;&amp;gt;가 포함된 HTML 반환&amp;#34;
Browser-&amp;gt;&amp;gt;DOM: &amp;#34;초기 HTML 렌더링 (FOUC 없음)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;하이드레이션 스크립트 실행&amp;#34;
alt &amp;#34;Cookie 없음 (첫 방문)&amp;#34;
DOM-&amp;gt;&amp;gt;Browser: &amp;#34;OS prefers-color-scheme 확인&amp;#34;
Browser--&amp;gt;&amp;gt;DOM: &amp;#34;matchMedia 결과 (예: dark)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;인라인 차단 스크립트를 통한 data-theme=&amp;#39;dark&amp;#39; 설정&amp;#34;
end
&lt;/pre>
&lt;h3 id="33-인라인-스크립트를-통한-방어선cookie를-사용할-수-없는-정적-사이트의-경우">3.3 인라인 스크립트를 통한 방어선(Cookie를 사용할 수 없는 정적 사이트의 경우)
&lt;/h3>&lt;p>SSG(정적 사이트 생성)만 지원되어 SSR이 불가능한 블로그(Hugo나 Gatsby, Astro의 정적 익스포트 등)의 경우, &lt;code>&amp;lt;head&amp;gt;&lt;/code> 태그 내부에 렌더링을 차단하며 실행되는 인라인 JavaScript를 배치하고 DOM이 렌더링 되기 직전에 클래스를 부여하는 방식이 필수적입니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- &amp;lt;head&amp;gt; 안의 마지막에 배치한다 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;theme&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">osTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matchMedia&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;(prefers-color-scheme: dark)&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">matches&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">theme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">osTheme&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">documentElement&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setAttribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data-theme&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">theme&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이 작은 스크립트는 브라우저의 렌더링을 차단하고 즉시 실행되기 때문에 화면이 렌더링 되는 시점에는 이미 &lt;code>data-theme&lt;/code> 속성이 설정되어 있어 화면 깜빡임(FOUC)을 완벽하게 방지할 수 있습니다.&lt;/p>
&lt;hr>
&lt;h2 id="4-tailwind-css-와-순수-scsscss에서의-구현-접근법">4. Tailwind CSS 와 순수 SCSS/CSS에서의 구현 접근법
&lt;/h2>&lt;p>다크 모드를 실제 프로젝트에 통합할 때, 각 도구별 접근 방식을 이해해 둘 필요가 있습니다.&lt;/p>
&lt;h3 id="41-tailwind-css-에서의-다크-모드">4.1 Tailwind CSS 에서의 다크 모드
&lt;/h3>&lt;p>Tailwind CSS 는 기본적으로 &lt;code>dark:&lt;/code> 변형(variant)을 제공하고 있어 매우 쉽게 다크 모드를 구현할 수 있습니다. 설정 파일(&lt;code>tailwind.config.js&lt;/code>)에서 &lt;code>darkMode&lt;/code> 속성을 설정합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// tailwind.config.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// &amp;#39;media&amp;#39; (OS 설정 의존) 또는 &amp;#39;class&amp;#39; (수동 전환 가능)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">darkMode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;class&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">theme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">extend&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">colors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* CSS 변수를 이용하여 Tailwind의 컬러 팔레트를 확장 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">primary&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-primary) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">background&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-background) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>HTML 측에서는 아래와 같이 클래스를 부여하기만 하면 됩니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-2xl font-bold&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Hello World&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-2&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Tailwind makes dark mode incredibly easy.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>하지만 모든 요소에 &lt;code>dark:bg-xxx&lt;/code> 라고 작성하는 것은 컴포넌트가 비대해지는 원인이 되기도 합니다. 대규모 블로그나 앱에서는 &lt;strong>CSS 변수를 기반으로 하고 Tailwind에서는 그 CSS 변수를 참조하는&lt;/strong> 하이브리드 설계(시맨틱 컬러 설계)가 권장됩니다.&lt;/p>
&lt;p>아래는 CSS 변수의 상속과 적용 계층을 보여주는 클래스 다이어그램입니다.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class GlobalCSSVariables {
&amp;#34;--color-brand-500&amp;#34;
&amp;#34;--color-gray-900&amp;#34;
}
class SemanticVariables {
&amp;#34;--bg-primary&amp;#34;
&amp;#34;--text-base&amp;#34;
&amp;#34;--accent&amp;#34;
}
class TailwindConfig {
&amp;#34;theme.colors.background&amp;#34;
&amp;#34;theme.colors.primary&amp;#34;
}
class UIComponents {
&amp;#34;class=&amp;#39;bg-background text-primary&amp;#39;&amp;#34;
}
GlobalCSSVariables &amp;lt;|-- SemanticVariables : &amp;#34;:root &amp;amp; .dark&amp;#34;
SemanticVariables &amp;lt;|-- TailwindConfig : &amp;#34;tailwind.config.js&amp;#34;
TailwindConfig &amp;lt;.. UIComponents : &amp;#34;유틸리티 클래스 적용&amp;#34;
&lt;/pre>
&lt;h3 id="42-raw-scsscss-에서의-구현mixin-활용">4.2 Raw SCSS/CSS 에서의 구현(Mixin 활용)
&lt;/h3>&lt;p>Tailwind를 사용하지 않고 자체적으로 SCSS를 작성하는 프로젝트에서는 &lt;code>@mixin&lt;/code> 을 활용하여 다크 모드의 스타일을 캡슐화합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* SCSS Mixin 정의 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">@mixin&lt;/span>&lt;span class="nf"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* [data-theme=&amp;#39;dark&amp;#39;] 속성, 또는 OS 설정 양쪽을 모두 지원 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@media&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">dark&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">:root:not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 사용 예 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#333333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="ni">solid&lt;/span> &lt;span class="mh">#eeeeee&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@include&lt;/span>&lt;span class="nd"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#1a202c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#e2e8f0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#2d3748&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이 방법은 직관적이지만 컴파일 후의 CSS 파일 크기가 비대해지기 쉽기(미디어 쿼리가 각 선택자마다 복제됨) 때문에, 역시 CSS 변수(Custom Properties)를 중심으로 한 설계로의 전환이 현재의 트렌드입니다.&lt;/p>
&lt;hr>
&lt;h2 id="5-이미지image와-svg의-다크-모드-최적화">5. 이미지(Image)와 SVG의 다크 모드 최적화
&lt;/h2>&lt;p>텍스트와 배경의 색채 설계가 완료되었더라도, 콘텐츠로 배치된 이미지나 아이콘(SVG)이 라이트 모드 그대로라면 다크 모드 시에 너무 눈부시게 떠 보이게 됩니다. 이들에 대한 최적화도 필수적입니다.&lt;/p>
&lt;h3 id="51-이미지의-밝기를-낮추는-css-필터">5.1 이미지의 밝기를 낮추는 CSS 필터
&lt;/h3>&lt;p>사진 등의 비트맵 이미지는 다크 모드 시에 그대로 표시하면 너무 눈부실 수 있습니다. CSS의 &lt;code>filter&lt;/code> 속성을 사용하여 이미지의 명도(brightness)와 대비(contrast)를 약간 낮춤으로써 다크 테마의 UI에 자연스럽게 녹아들게 할 수 있습니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">src&lt;/span>&lt;span class="o">*=&lt;/span>&lt;span class="s2">&amp;#34;.svg&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 밝기를 낮추고, 약간 대비를 올림 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.8&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">filter&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">hover&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 호버 시에는 원래 밝기로 되돌림 (사용자가 자세히 보고 싶은 경우) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-picture-태그를-통한-이미지-분기-처리">5.2 &lt;code>&amp;lt;picture&amp;gt;&lt;/code> 태그를 통한 이미지 분기 처리
&lt;/h3>&lt;p>로고 이미지나 설명용 도해(배경이 흰색으로 고정된 JPEG 등)는 필터 처리만으로는 대응할 수 없습니다. 이 경우 HTML의 &lt;code>&amp;lt;picture&amp;gt;&lt;/code> 요소와 미디어 쿼리를 사용하여 다크 모드용의 다른 이미지 파일을 분기 처리하여 보여주는 것이 정답입니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 다크 모드 OS 설정의 사용자에게는 이곳을 표시 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">source&lt;/span> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-dark.png&amp;#34;&lt;/span> &lt;span class="na">media&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;(prefers-color-scheme: dark)&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 기본(라이트 모드) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-light.png&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;사이트 로고&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>※단, 이 방법은 &lt;code>localStorage&lt;/code> 등을 통한 수동 토글과는 연동되지 않기(OS 설정에만 의존) 때문에, 수동 전환을 구현한 경우에는 JS로 이미지의 &lt;code>src&lt;/code>를 동적으로 변경하거나 CSS 클래스로 &lt;code>display: none&lt;/code>을 전환할 필요가 있습니다.&lt;/p>
&lt;h3 id="53-svg-아이콘의-currentcolor-대응">5.3 SVG 아이콘의 &lt;code>currentColor&lt;/code> 대응
&lt;/h3>&lt;p>아이콘 등에 사용하는 인라인 SVG는 칠하기 색상을 부모 요소의 텍스트 컬러와 연동시키는 가장 스마트한 방법입니다. SVG의 &lt;code>fill&lt;/code> 이나 &lt;code>stroke&lt;/code> 속성에 &lt;code>currentColor&lt;/code> 를 지정합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- CSS의 color 속성 값(var(--text-primary) 등)이 자동으로 적용된다 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">svg&lt;/span> &lt;span class="na">viewBox&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;0 0 24 24&amp;#34;&lt;/span> &lt;span class="na">fill&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;currentColor&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">path&lt;/span> &lt;span class="na">d&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;M12 2L2 22h20L12 2z&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">svg&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이를 통해 다크 모드로 전환되어 부모 요소의 글자색이 흰색 계통이 되면, SVG 아이콘도 자동으로 흰색 계통으로 변합니다.&lt;/p>
&lt;hr>
&lt;h2 id="6-요약-지속-가능한-다크-모드-설계를-향해">6. 요약: 지속 가능한 다크 모드 설계를 향해
&lt;/h2>&lt;p>블로그나 웹 애플리케이션에서 고품질의 다크 모드를 구현하기 위해서는 아래의 포인트를 망라한 CSS 설계가 필수적입니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>CSS Custom Properties 활용하기&lt;/strong>: 색상 지정의 하드코딩을 피하고, 시맨틱한 변수명(예: &lt;code>--bg-primary&lt;/code>)으로 추상화한다.&lt;/li>
&lt;li>&lt;strong>OKLCH 색 공간 채택하기&lt;/strong>: WCAG 2.1 AAA를 만족하는 접근성 높은 대비율(7:1 이상)을 지각적으로 균일한 색 공간에서 논리적으로 설계한다.&lt;/li>
&lt;li>&lt;strong>FOUC 대책 철저히 하기&lt;/strong>: SSR과 Cookie의 연동, 혹은 &lt;code>&amp;lt;head&amp;gt;&lt;/code> 내의 렌더링 차단 인라인 스크립트를 통해 초기 로드 시의 화면 깜빡임을 완전히 배제한다.&lt;/li>
&lt;li>&lt;strong>미디어와 자산의 최적화&lt;/strong>: &lt;code>filter: brightness()&lt;/code> 나 &lt;code>currentColor&lt;/code>, &lt;code>&amp;lt;picture&amp;gt;&lt;/code> 태그를 구사하여 텍스트 이외의 요소도 다크 테마와 조화시킨다.&lt;/li>
&lt;/ol>
&lt;p>단순한 &amp;lsquo;색상 반전&amp;rsquo;을 넘어서는 이러한 세심한 배려야말로 사용자에게 오랫동안 사랑받고 눈이 피로하지 않은 훌륭한 독서 경험(리딩 익스피리언스)을 제공하는 모던 블로그의 조건이라 할 수 있겠습니다. 앞으로 다크 모드를 도입할 개발자 분들은 꼭 이 글의 설계 패턴을 참고해 보시기 바랍니다.&lt;/p></description></item></channel></rss>