어떤 스타일링 실수는 개발 환경에서는 멀쩡하다가 조건이 바뀔 때 깨져. Tailwind의 정적 분석과 테마 계약을 거스르는 패턴을 미리 알아두자.
동적으로 만든 클래스 이름
className={'bg-' + color + '-500'}는 자연스럽게 보이지만 Tailwind의 정적 스캐너는 완성된 클래스 문자열을 찾지 못해. { red: 'bg-red-500', blue: 'bg-blue-500' }처럼 가능한 값을 map에 완전한 문자열로 적어 두면 타입 검사와 CSS 생성이 모두 안정적이야.
반복되는 임의값
bg-[#ff8fbe] 같은 임의값은 정말 한 번만 쓸 때 편해. 같은 값이 두세 파일에 반복되면 브랜드 색을 바꿀 때 일부를 놓치기 쉬우니 @theme 토큰으로 올려.
!important로 cascade 우회하기
!text-red-500 같은 ! prefix는 다른 방법으로 건드릴 수 없는 서드파티 CSS를 재정의할 때만 써. 직접 만든 컴포넌트에 필요하다면 보통 레이어나 클래스 병합 계약이 잘못된 거야.
지나치게 긴 className
유틸리티가 너무 길어지면 @layer components의 클래스, 의미가 있는 상수, 조건을 정리하는 cn() 중 하나로 묶어. 긴 문자열을 Tailwind의 숙명처럼 받아들일 필요 없어.
두 스타일 시스템 섞기
Tailwind 프로젝트에 styled-components나 emotion을 함께 넣으면 같은 책임을 두 체계가 나눠 맡고 런타임 비용도 늘어. Tailwind와 @layer를 일관되게 쓰거나 CSS-in-JS를 선택하되, 특별한 이유 없이 둘을 섞지 마.
동적 값은 완전한 클래스 문자열로 매핑해
bg-${color}-500처럼 이름 조각을 이어 붙이면 Tailwind 스캐너가 실제 클래스를 찾지 못해 프로덕션 CSS에서 빠질 수 있어. { red: 'bg-red-500', blue: 'bg-blue-500' }처럼 가능한 문자열을 소스에 완전한 형태로 적어.
정말 동적인 값에는 제한된 safelist를 써
서버 데이터가 정해진 클래스 집합 중 하나를 보낼 수밖에 없다면 Tailwind v4의 @source inline()로 필요한 문자열만 명시할 수 있어. 타입이 있는 map으로 해결할 수 있는지 먼저 확인하고 safelist 범위를 작게 유지해.
프로덕션 CSS에서 결과를 확인해
npm run build 뒤 생성된 CSS에서 기대한 유틸리티를 찾아. 개발 중 우연히 남아 있던 스타일이 배포 빌드에서 사라지는 문제를 잡을 수 있어. 직접 만든 !important와 서로 다른 스타일 시스템을 섞는 선택도 이 시점에 다시 검토해.