2025-09-09 11:07

서론: 왜 클래스명이 그대로 보일까?

테일윈드 Next.js 프로젝트에서 개발자 도구(F12)를 열었을 때 HTML에 flex, text-xl, bg-blue-500 같은 Tailwind 클래스명이 그대로 보이는 이유는, Tailwind CSS가 전통적인 CSS 작성 방식과 다르게 작동하기 때문입니다.

결론부터 말씀드리리면, Tailwind CSS는 빌드 시점(Build Time)에 PostCSS라는 도구를 통해 작동하며, 프로젝트의 모든 파일을 스캔해서 실제로 사용된 클래스명에 해당하는 CSS 규칙들만 모아 최종 CSS 파일을 생성합니다. 브라우저는 이 최종적으로 생성된 CSS 파일과 클래스명이 적힌 HTML을 함께 받아 화면을 그리는 것입니다.

동작 방식을 개발 과정프로덕션 빌드 과정으로 나누어 자세히 살펴보겠습니다.

1. 개발 과정 (npm run dev) - JIT(Just-In-Time) 컴파일러의 마법

개발 중에는 속도가 매우 중요합니다. 코드를 수정할 때마다 즉시 브라우저에 반영되어야 하죠. 최신 Tailwind CSS는 이 경험을 위해 JIT(Just-In-Time) 컴파일러를 사용합니다.

작동 순서:

  1. 파일 감시(Watching Files): tailwind.config.js 파일의 content 배열에 지정된 모든 파일들(예: pages/**/*.js, components/**/*.jsx 등)을 실시간으로 감시합니다.

  2. 클래스명 스캔: 개발자가 JSX 파일에 새로운 Tailwind 클래스(예: text-red-500)를 추가하고 저장하는 순간, JIT 컴파일러가 이 변경을 감지하고 해당 클래스명을 스캔합니다.

  3. CSS 즉시 생성: JIT 컴파일러는 text-red-500에 해당하는 CSS 규칙(.text-red-500 { color: #ef4444; })을 즉시 생성합니다.

  4. 스타일 주입: 생성된 CSS는 Next.js 개발 서버를 통해 브라우저에 동적으로 주입(inject)됩니다. 이 과정이 매우 빨라서 거의 실시간처럼 느껴집니다.

이 방식 덕분에 개발 중에는 실제로 사용한 스타일에 대한 CSS만 메모리에 로드되므로, 모든 유틸리티 클래스를 다 불러오는 구버전 방식에 비해 개발 서버가 매우 가볍고 빠릅니다.

2. 프로덕션 빌드 과정 (npm run build) - 최적화의 시간

애플리케이션을 실제 사용자에게 배포하기 위해 빌드할 때는 최종 결과물이 최대한 작고 빨라야 합니다.

작동 순서:

  1. 전체 프로젝트 스캔: next build 명령을 실행하면, Tailwind는 JIT 컴파일러를 이용해 tailwind.config.jscontent 경로에 있는 모든 파일을 처음부터 끝까지 스캔합니다.

  2. 사용된 클래스 목록화: 프로젝트 전체에서 사용된 모든 Tailwind 클래스명(예: flex, p-4, md:grid-cols-2 등)의 고유한 목록을 만듭니다.

  3. 최종 CSS 파일 생성: 이 목록에 있는 클래스들에 해당하는 CSS 규칙들만 모아서 하나의 CSS 파일(예: styles.css)을 생성합니다.

  4. 최적화 (Purging & Minification):

    • 퍼지(Purge): 목록에 없는, 즉 프로젝트에서 단 한 번도 사용되지 않은 수천 개의 나머지 Tailwind 클래스들은 최종 CSS 파일에서 모두 제거됩니다. 이것이 Tailwind가 프로덕션에서 매우 작은 파일 크기를 자랑하는 핵심 이유입니다.

    • 압축(Minify): 생성된 CSS 파일에서 불필요한 공백, 주석 등을 모두 제거하여 파일 크기를 최소화합니다.

이 과정을 거쳐 생성된 작고 최적화된 CSS 파일이 .next 폴더에 포함되고, 사용자가 웹사이트에 접속할 때 이 CSS 파일을 다운로드하여 스타일을 적용받게 됩니다.

핵심 기술: PostCSS

이 모든 과정의 중심에는 PostCSS가 있습니다. PostCSS는 자바스크립트 플러그인을 사용하여 CSS를 변환하는 도구입니다.

  • Next.js는 기본적으로 PostCSS를 지원합니다.

  • tailwindcss는 사실 PostCSS의 플러그인 중 하나입니다.

  • Next.js의 빌드 과정에 PostCSS가 포함되어 있고, 우리는 PostCSS 설정 파일(postcss.config.js)에 tailwindcss 플러그인을 등록함으로써 위에서 설명한 모든 과정이 자동으로 처리되도록 하는 것입니다.

// postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {}, // 바로 이 부분이 Tailwind를 PostCSS 플러그인으로 사용하겠다는 의미입니다.
    autoprefixer: {}, // 공급업체 접두사(vendor prefix)를 자동으로 붙여줍니다.
  },
}

요약

구분개발 (npm run dev)프로덕션 (npm run build)
목표빠른 개발 경험, 실시간 피드백최종 사용자를 위한 성능 최적화
프로세스JIT 컴파일러가 파일을 감시하며 필요한 CSS만 즉시 생성하여 주입프로젝트 전체를 스캔하여 사용한 CSS만으로 최종 파일 생성
결과물브라우저 메모리 상의 동적인 스타일매우 작고 압축된 단일 CSS 파일

이처럼 Tailwind CSS는 개발 편의성과 프로덕션 성능이라는 두 마리 토끼를 모두 잡기 위해 정교한 빌드 프로세스를 내부적으로 사용하고 있습니다. 그래서 우리는 수많은 유틸리티 클래스를 마음껏 사용하면서도, 최종 결과물의 용량에 대해서는 걱정하지 않아도 되는 것입니다.

보충: 만약 개발자가 직접 CSS를 작성한다면?

결론부터 말씀드리면, 개발자가 직접 작성한 CSS와 Tailwind가 생성한 CSS는 둘 다 최종 결과물에 포함됩니다. 이는 코드 중복과 번들 크기 증가의 원인이 될 수 있으므로, 기존 Tailwind 클래스와 동일한 효과를 내는 CSS를 직접 작성하는 것은 피해야 합니다.

@apply 기능 상세 설명: 언제, 어떻게 사용해야 할까?

@apply는 Tailwind의 유틸리티 우선(utility-first) 접근법을 유지하면서도 코드의 재사용성을 높일 수 있는 강력한 기능입니다.

1. @apply란 무엇인가?

@apply는 CSS 파일 내에서 Tailwind의 유틸리티 클래스들을 직접 가져와 사용할 수 있게 해주는 지시어(directive)입니다. 이를 통해 여러 유틸리티 클래스의 조합을 하나의 새로운 사용자 정의 클래스로 묶을 수 있습니다.

2. 왜 사용하는가? (Before & After)

프로젝트 곳곳에 동일한 스타일의 버튼이 사용된다고 상상해 봅시다.

Before: @apply 사용 전

HTML/JSX 파일에 매번 긴 클래스 목록을 반복해서 작성해야 합니다.

<button class="py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700">
  Click me
</button>

<button class="py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700">
  Submit
</button>

이 방식은 코드를 복잡하게 만들고, 나중에 버튼 스타일을 한 번에 변경하기 어렵게 만듭니다.

After: @apply 사용 후

globals.css 같은 CSS 파일에 버튼 스타일을 하나의 클래스로 추출합니다.

/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

.btn-primary {
  @apply py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md;
  @apply transition-colors duration-300; /* 여러 줄에 걸쳐 작성 가능 */
}

.btn-primary:hover {
  @apply bg-blue-700;
}

이제 HTML/JSX에서는 이 클래스 하나만 사용하면 됩니다.

<button class="btn-primary">
  Click me
</button>

<button class="btn-primary">
  Submit
</button>

HTML이 훨씬 깔끔해지고, 버튼 스타일을 변경하고 싶을 때는 globals.css 파일의 .btn-primary 클래스만 수정하면 모든 버튼에 일괄 적용됩니다.

3. @apply의 장점

  • HTML 가독성 향상: 길고 반복적인 클래스 목록을 제거하여 마크업을 깔끔하게 유지합니다.

  • 유지보수 용이성: 공통 컴포넌트의 스타일을 한곳에서 중앙 관리할 수 있습니다.

  • 디자인 시스템 일관성 유지: tailwind.config.js에 정의된 디자인 토큰(색상, 간격 등)을 그대로 활용하므로 일관성을 해치지 않습니다.

4. 언제 사용하는 것이 좋을까?

@apply는 매우 유용하지만 남용해서는 안 됩니다. Tailwind의 창시자인 Adam Wathan도 ‘유틸리티 우선’이 기본 원칙임을 강조합니다.

권장 사용 사례:

  • 버튼, 폼 요소, 카드 등 작고 반복적으로 사용되는 UI 컴포넌트를 추상화할 때.

  • CSS의 특정 기능(예: 자식 선택자, ::before/::after 가상 요소)과 Tailwind 유틸리티를 함께 사용해야 할 때.

지양해야 할 사례:

  • 웹사이트의 전체 레이아웃처럼 한 번만 사용되는 스타일을 위해 클래스를 만드는 것. 이런 경우에는 유틸리티 클래스를 HTML에 직접 적용하는 것이 더 직관적입니다.

결론적으로 @apply는 “이 스타일 조합이 여러 곳에서 재사용될 것인가?”라는 질문에 “예”라고 답할 수 있을 때 사용하는 것이 가장 좋습니다.