Customization
프로젝트의 콘텐츠 소스를 설정하는 방법.
tailwind.config.js
파일의 content
섹션은 HTML 템플릿, 자바스크립트 컴포넌트, 그리고 Tailwind 클래스 이름이 포함된 모든 소스 파일의 경로를 설정하는 곳입니다.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
// ...
}
이 가이드는 여러분의 프로젝트에 필요한 모든 CSS를 Tailwind가 생성하도록 하는 데 필요한 모든 것을 다룹니다.
Tailwind CSS는 여러분의 HTML, JavaScript 컴포넌트, 그리고 기타 템플릿 파일을 모두 스캔하여 클래스 이름을 찾고, 해당 스타일에 맞는 CSS를 생성하는 방식으로 동작합니다.
Tailwind가 필요한 모든 CSS를 생성하려면, 프로젝트 내에서 Tailwind 클래스 이름을 포함하는 모든 파일을 알아야 합니다.
설정 파일의 content
섹션에서 콘텐츠 파일들의 경로를 설정할 수 있습니다:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}'
],
// ...
}
경로는 glob 패턴으로 설정되며, 이를 통해 많은 설정 없이도 프로젝트 내의 모든 콘텐츠 파일을 쉽게 매칭할 수 있습니다:
*
를 사용하여 슬래시와 숨김 파일을 제외한 모든 것을 매칭**
를 사용하여 0개 이상의 디렉토리를 매칭{}
사이에 쉼표로 구분된 값을 사용하여 옵션 목록에 대해 매칭Tailwind는 내부적으로 fast-glob 라이브러리를 사용합니다. 다른 지원되는 패턴 기능에 대해서는 해당 라이브러리의 문서를 참고하세요.
경로는 tailwind.config.js
파일이 아닌 프로젝트 루트를 기준으로 합니다. 따라서 tailwind.config.js
파일이 커스텀 위치에 있더라도, 경로는 프로젝트 루트를 기준으로 작성해야 합니다.
최상의 성능을 위해 그리고 잘못된 탐지를 방지하려면, 여러분의 콘텐츠 설정을 가능한 한 구체적으로 해야 합니다.
아래와 같이 너무 광범위한 패턴을 사용하면, Tailwind가 node_modules
까지 스캔하게 되어 원하지 않는 결과를 초래할 수 있습니다.
너무 광범위한 패턴은 사용하지 마세요
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./**/*.{html,js}',
],
// ...
}
프로젝트 루트에 있는 파일(주로 index.html
파일)을 스캔해야 한다면, 해당 파일을 독립적으로 나열하여 다른 패턴을 더 구체적으로 설정할 수 있습니다.
콘텐츠 패턴을 구체적으로 설정하세요
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./components/**/*.{html,js}',
'./pages/**/*.{html,js}',
'./index.html',
],
// ...
}
일부 프레임워크는 주요 HTML 진입점을 다른 템플릿과 다른 위치(주로 public/index.html
)에 숨겨두는 경우가 있습니다. 따라서 Tailwind 클래스를 해당 파일에 추가한다면, 설정에 포함시켜야 합니다.
HTML 진입점을 포함시키는 것을 잊지 마세요
module.exports = {
content: [
'./public/index.html',
'./src/**/*.{html,js}',
],
// ...
}
HTML에 클래스를 추가하기 위해 조작하는 JavaScript 파일이 있다면, 해당 파일도 포함시켜야 합니다.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
// ...
'./src/**/*.js',
],
// ...
}
// ...
menuButton.addEventListener('click', function () {
let classList = document.getElementById('nav').classList
classList.toggle('hidden')
classList.toggle('block')
})
// ...
또한, CSS 파일을 스캔하지 않는 것이 중요합니다. Tailwind가 생성하는 CSS 파일이 아니라, 클래스 이름이 사용되는 템플릿을 스캔하도록 설정해야 합니다.
콘텐츠 설정에 CSS 파일을 포함시키지 마세요
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.css',
],
// ...
}
Tailwind가 여러분의 소스 코드에서 클래스를 탐지하는 방식은 의도적으로 매우 단순합니다. 실제로 코드를 파싱하거나 실행하지 않고, 단순히 정규 표현식을 사용해 클래스 이름일 가능성이 있는 모든 문자열을 추출합니다.
예를 들어, 다음은 모든 잠재적인 클래스 이름 문자열을 개별적으로 강조한 HTML 예제입니다:
<div class="md:flex">
<div class="md:flex-shrink-0">
<img class="rounded-lg md:w-56" src="/img/shopping.jpg" alt="Woman paying for a purchase">
</div>
<div class="mt-4 md:mt-0 md:ml-6">
<div class="uppercase tracking-wide text-sm text-indigo-600 font-bold">
Marketing
</div>
<a href="/get-started" class="block mt-1 text-lg leading-tight font-semibold text-gray-900 hover:underline">
Finding customers for your new business
</a>
<p class="mt-2 text-gray-600">
Getting a new business off the ground is a lot of hard work.
Here are five ideas you can use to find your first customers.
</p>
</div>
</div>
Tailwind는 class="..."
속성에만 탐색을 제한하지 않습니다. 클래스는 어디에서나 사용될 수 있기 때문입니다. 예를 들어, 메뉴를 토글하기 위해 JavaScript에서 클래스를 사용할 수 있습니다:
<script>
menuButton.addEventListener('click', function () {
let classList = document.getElementById('nav').classList
classList.toggle('hidden')
classList.toggle('block')
})
</script>
이러한 매우 단순한 접근 방식을 통해 Tailwind는 JSX와 같은 어떤 프로그래밍 언어에서도 매우 안정적으로 작동합니다:
const sizes = {
md: 'px-4 py-2 rounded-md text-base',
lg: 'px-5 py-3 rounded-lg text-lg',
}
const colors = {
indigo: 'bg-indigo-500 hover:bg-indigo-600 text-white',
cyan: 'bg-cyan-600 hover:bg-cyan-700 text-white',
}
export default function Button({ color, size, children }) {
let colorClasses = colors[color]
let sizeClasses = sizes[size]
return (
<button type="button" className={`font-bold ${sizeClasses} ${colorClasses}`}>
{children}
</button>
)
}
Tailwind가 클래스 이름을 추출하는 방식에서 가장 중요한 점은, 소스 파일에 완전한 문자열로 존재하는 클래스만 찾는다는 것입니다.
문자열 보간을 사용하거나 부분적인 클래스 이름을 연결하면, Tailwind는 이를 찾지 못하고 해당 CSS를 생성하지 않습니다.
클래스 이름을 동적으로 생성하지 마세요
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
위 예제에서 text-red-600
과 text-green-600
문자열은 존재하지 않기 때문에, Tailwind는 해당 클래스를 생성하지 않습니다.
대신, 사용하는 모든 클래스 이름이 완전한 형태로 존재하도록 해야 합니다.
항상 완전한 클래스 이름을 사용하세요
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
React나 Vue와 같은 컴포넌트 라이브러리를 사용하는 경우, props를 사용해 클래스를 동적으로 생성하지 않도록 주의해야 합니다.
props를 사용해 클래스 이름을 동적으로 생성하지 마세요
function Button({ color, children }) {
return (
<button className={`bg-${color}-600 hover:bg-${color}-500 ...`}>
{children}
</button>
)
}
대신, props를 빌드 시점에 정적으로 감지할 수 있는 완전한 클래스 이름에 매핑하세요.
항상 props를 정적 클래스 이름에 매핑하세요
function Button({ color, children }) {
const colorVariants = {
blue: 'bg-blue-600 hover:bg-blue-500',
red: 'bg-red-600 hover:bg-red-500',
}
return (
<button className={`${colorVariants[color]} ...`}>
{children}
</button>
)
}
이 방식은 예를 들어, 다른 prop 값을 다른 색조에 매핑할 수 있는 추가적인 이점도 있습니다.
function Button({ color, children }) {
const colorVariants = {
blue: 'bg-blue-600 hover:bg-blue-500 text-white',
red: 'bg-red-500 hover:bg-red-400 text-white',
yellow: 'bg-yellow-300 hover:bg-yellow-400 text-black',
}
return (
<button className={`${colorVariants[color]} ...`}>
{children}
</button>
)
}
코드에서 항상 완전한 클래스 이름을 사용한다면, Tailwind는 매번 모든 CSS를 완벽하게 생성할 것입니다.
서드파티 라이브러리(예: Select2)를 사용하고 해당 라이브러리에 커스텀 CSS로 스타일을 적용할 때, Tailwind의 @layer
기능을 사용하지 않고 스타일을 작성하는 것을 권장합니다:
@tailwind base;
@tailwind components;
.select2-dropdown {
@apply rounded-b-lg shadow-md;
}
.select2-search {
@apply border border-gray-300 rounded;
}
.select2-results__group {
@apply text-lg font-bold text-gray-900;
}
/* ... */
@tailwind utilities;
이렇게 하면 Tailwind가 항상 해당 스타일을 CSS에 포함시켜 주기 때문에, 서드파티 라이브러리의 소스 코드를 Tailwind가 스캔하도록 설정하는 것보다 훨씬 간편합니다.
Tailwind로 스타일을 적용한 재사용 가능한 컴포넌트 세트를 만들고 여러 프로젝트에서 이를 가져와 사용한다면, Tailwind가 해당 컴포넌트의 클래스 이름을 스캔하도록 설정해야 합니다:
module.exports = {
content: [
'./components/**/*.{html,js}',
'./pages/**/*.{html,js}',
'./node_modules/@my-company/tailwind-components/**/*.js',
],
// ...
}
이렇게 하면 Tailwind가 해당 컴포넌트에 필요한 모든 CSS를 생성할 수 있습니다.
워크스페이스가 있는 모노레포에서 작업하는 경우, Tailwind가 콘텐츠 파일을 인식할 수 있도록 require.resolve
를 사용해야 할 수도 있습니다:
const path = require('path');
module.exports = {
content: [
'./components/**/*.{html,js}',
'./pages/**/*.{html,js}',
path.join(path.dirname(require.resolve('@my-company/tailwind-components')), '**/*.js'),
],
// ...
}
기본적으로 Tailwind는 절대 경로가 아닌 콘텐츠 경로를 현재 작업 디렉토리를 기준으로 해석합니다. 이는 tailwind.config.js
파일이 아닌 다른 디렉토리에서 Tailwind를 실행할 경우 예상치 못한 결과를 초래할 수 있습니다.
tailwind.config.js
파일을 기준으로 경로를 항상 해석하려면, content
설정을 객체 표기법으로 작성하고 relative
속성을 true
로 설정하세요:
module.exports = {
content: {
relative: true,
files: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
},
// ...
}
이 방식은 프레임워크의 다음 주요 버전에서 기본 동작이 될 가능성이 높습니다.
어떤 이유로든 파일 내용 대신 원시 콘텐츠를 스캔하도록 Tailwind를 설정해야 한다면, 경로 대신 raw
키가 있는 객체를 사용하세요.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
{ raw: '<div class="font-bold">', extension: 'html' },
],
// ...
}
이 기능을 사용할 만한 유효한 사례는 많지 않습니다. 대부분의 경우 안전 목록이 더 적합한 선택입니다.
가장 작은 파일 크기와 최상의 개발 경험을 위해, 가능한 한 content
설정을 통해 Tailwind가 어떤 클래스를 생성할지 지정하는 것을 적극 권장합니다.
화이트리스트는 최후의 수단이며, 특정 콘텐츠에서 클래스 이름을 스캔할 수 없는 상황에서만 사용해야 합니다. 이러한 상황은 드물며, 거의 필요하지 않은 기능입니다.
만약 콘텐츠 파일에 존재하지 않는 특정 클래스 이름을 Tailwind가 생성하도록 해야 한다면, safelist
옵션을 사용하세요:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
safelist: [
'bg-red-500',
'text-3xl',
'lg:text-4xl',
]
// ...
}
이 기능이 유용한 예시 중 하나는, 사이트에서 사용자 생성 콘텐츠를 표시하고, 사용자가 자신의 콘텐츠에 Tailwind 클래스를 제한적으로 사용할 수 있도록 허용하려는 경우입니다. 이때 사용자가 사용할 클래스가 사이트 소스 파일에는 존재하지 않을 수 있습니다.
Tailwind는 많은 클래스를 안전 목록에 추가해야 하는 상황을 위해 패턴 기반의 안전 목록(safelist) 기능을 지원합니다:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
safelist: [
'text-2xl',
'text-3xl',
{
pattern: /bg-(red|green|blue)-(100|200|300)/,
},
],
// ...
}
패턴은 /bg-red-.+/
와 같은 기본 유틸리티 이름에만 매칭되며, /hover:bg-red-.+/
와 같이 변형 수정자(variant modifier)가 포함된 패턴은 매칭되지 않습니다.
매칭된 클래스에 대해 Tailwind가 변형을 강제로 생성하도록 하려면, variants
옵션을 사용하여 포함시킬 수 있습니다:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
safelist: [
'text-2xl',
'text-3xl',
{
pattern: /bg-(red|green|blue)-(100|200|300)/,
variants: ['lg', 'hover', 'focus', 'lg:hover'],
},
],
// ...
}
Tailwind는 콘텐츠 내의 클래스 이름을 매우 단순한 방식으로 감지하기 때문에, 실제로 필요하지 않은 클래스가 생성될 수 있습니다.
예를 들어, 아래 HTML 코드는 container
클래스가 실제로 사용되지 않더라도 해당 클래스를 생성합니다:
<div class="text-lg leading-8 text-gray-600">
우리가 디자인하는 모든 커스텀 수영장은 사용된 선적 컨테이너로 시작하며,
최신 기술과 마감재를 적용하여 여름 내내 손님들을 즐겁게 할 수 있는
아름답고 기능적인 공간으로 변모시킵니다.
</div>
또한, 기존 CSS와 충돌할 수 있는 특정 클래스의 생성을 방지하고 싶지만, 모든 Tailwind 클래스에 접두사를 붙이기까지는 원하지 않을 수 있습니다.
이런 경우, blocklist
옵션을 사용하여 Tailwind가 콘텐츠에서 감지한 특정 클래스를 무시하도록 지시할 수 있습니다:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
blocklist: [
'container',
'collapse',
],
// ...
}
blocklist
옵션은 Tailwind가 생성하는 CSS에만 영향을 미치며, 여러분이 직접 작성하거나 다른 라이브러리에서 가져온 커스텀 CSS에는 영향을 주지 않습니다.
safelist
와 달리, blocklist
옵션은 문자열만 지원하며, 정규식을 사용하여 클래스를 제외할 수는 없습니다.
HTML로 컴파일 되는 형식(예: 마크다운)으로 콘텐츠를 작성하는 경우, 클래스 이름을 스캔하기 전에 해당 콘텐츠를 HTML로 컴파일하는 것이 종종 합리적입니다.
특정 파일 확장자와 일치하는 콘텐츠를 클래스 추출 전에 변환하려면 content.transform
옵션을 사용하세요:
const remark = require('remark')
module.exports = {
content: {
files: ['./src/**/*.{html,md}'],
transform: {
md: (content) => {
return remark().process(content)
}
}
},
// ...
}
content.transform
을 사용할 때는 content
아래의 최상위 배열이 아닌 content.files
를 사용하여 소스 경로를 제공해야 합니다.
특정 파일 확장자에 대한 클래스 이름을 감지하는 Tailwind의 로직을 재정의하려면 extract
옵션을 사용하세요:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: {
files: ['./src/**/*.{html,wtf}'],
extract: {
wtf: (content) => {
return content.match(/[^<>"'`\s]*/g)
}
}
},
// ...
}
이 기능은 고급 기능이며 대부분의 사용자는 필요하지 않습니다. Tailwind의 기본 추출 로직은 거의 모든 프로젝트에서 매우 잘 작동합니다.
변환과 마찬가지로 content.extract
를 사용할 때는 content
아래의 최상위 배열 대신 content.files
를 사용하여 소스 경로를 제공해야 합니다.
Tailwind에서 클래스가 생성되지 않는다면, content
설정이 올바른지 확인하고 모든 소스 파일과 일치하는지 점검하세요.
흔히 발생하는 실수는 파일 확장자를 누락하는 것입니다. 예를 들어 React 컴포넌트에서 js
대신 jsx
를 사용하는 경우:
module.exports = {
content: [
'./src/**/*.{html,js}',
'./src/**/*.{html,js,jsx}'
],
// ...
}
또는 프로젝트 중간에 새로운 폴더를 생성했는데, 이를 설정에 추가하지 않은 경우:
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
'./util/**/*.{html,js}'
],
// ...
}
동적 클래스 이름을 사용하려고 시도하는 경우에도 문제가 발생할 수 있습니다. Tailwind는 실제로 소스 코드를 평가하지 않으며, 오직 정적이고 끊어지지 않은 클래스 문자열만 감지할 수 있습니다.
동적으로 클래스 이름을 생성하지 마세요
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
항상 코드에서 완전한 클래스 이름을 사용하도록 주의하세요:
항상 완전한 클래스 이름을 사용하세요
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
더 자세한 내용은 동적 클래스 이름에 대한 문서를 참고하세요.
CSS가 무한 루프로 재빌드되는 것처럼 보인다면, 이는 빌드 도구가 PostCSS 의존성을 등록할 때 glob
옵션을 지원하지 않기 때문일 가능성이 높습니다.
많은 빌드 도구(예: webpack)는 이 옵션을 지원하지 않으며, 결과적으로 특정 파일이나 _전체 디렉토리_만 감시하도록 설정할 수 있습니다. 예를 들어, webpack에게 디렉토리 내의 *.html
파일만 감시하라고 지시할 수는 없습니다.
이는 CSS를 빌드할 때 해당 디렉토리 내의 어떤 파일이 변경되더라도, 변경된 파일이 glob에 지정된 확장자와 일치하지 않더라도 재빌드가 트리거될 수 있음을 의미합니다.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
// 일부 빌드 도구를 사용할 경우, `src` 디렉토리 내의
// 어떤 파일이 변경되더라도 CSS가 재빌드됩니다.
'./src/**/*.{html,js}',
],
// ...
}
따라서 src/**/*.html
파일의 변경을 감시하고 있지만, CSS 출력 파일을 src/css/styles.css
에 작성하는 경우, 일부 도구에서는 무한 재빌드 루프가 발생할 수 있습니다.
이상적으로는 콘솔에서 이 문제에 대해 경고를 표시할 수 있지만, 많은 도구(우리의 CLI 도구 포함)가 이를 완벽하게 지원하며, 사용 중인 빌드 도구를 감지할 수 있는 신뢰할 만한 방법이 없습니다.
이 문제를 해결하려면 content
설정에서 더 구체적인 경로를 사용하여 CSS가 빌드될 때 변경되지 않는 디렉토리만 포함하도록 해야 합니다:
module.exports = {
content: [
'./src/**/*.{html,js}',
'./src/pages/**/*.{html,js}',
'./src/components/**/*.{html,js}',
'./src/layouts/**/*.{html,js}',
'./src/index.html',
],
// ...
}
필요한 경우, 실제 프로젝트 디렉토리 구조를 조정하여 CSS 파일이나 매니페스트 파일과 같은 빌드 산출물을 실수로 포함하지 않도록 템플릿 파일을 대상으로 할 수 있도록 해야 합니다.
content
설정이나 디렉토리 구조를 변경할 수 없는 경우, 완전한 glob 지원을 제공하는 도구를 사용하여 CSS를 별도로 컴파일하는 것이 최선의 방법입니다. Tailwind CLI를 사용하는 것을 권장하며, 이는 Tailwind로 CSS를 컴파일하기 위한 빠르고 간단한 목적에 맞춰진 도구입니다.
출력 결과가 이상하거나 설명하기 어려운 문제가 발생하거나, 아예 작동하지 않는 것처럼 보인다면, 빌드 도구가 PostCSS 의존성 메시지를 제대로 지원하지 않아서 생기는 문제일 가능성이 높습니다. 현재 알려진 예시 중 하나는 Stencil입니다.
이런 문제가 발생할 때는, 기존 도구에 Tailwind를 통합하려고 하기보다는 Tailwind CLI를 사용해 CSS를 별도로 컴파일하는 것을 권장합니다.
npm-run-all
이나 concurrently
같은 패키지를 사용해 일반적인 개발 명령어와 함께 CSS를 컴파일할 수 있습니다. 프로젝트에 다음과 같은 스크립트를 추가하면 됩니다.
// package.json
{
// ...
"scripts": {
"start": "concurrently \"npm run start:css\" \"react-scripts start\"",
"start:css": "tailwindcss -o src/tailwind.css --watch",
"build": "npm run build:css && react-scripts build",
"build:css": "NODE_ENV=production tailwindcss -o src/tailwind.css -m",
},
}
어떤 방법을 선택하든, 기존 이슈를 확인하거나 새 이슈를 열어 문제를 파악하고 사용 중인 도구와의 호환성을 개선할 수 있도록 해주세요.