안녕하세요! 오늘은 Tailwind CSS를 사용하면서 겪었던 스타일 누락 문제와 그 해결 과정을 공유해 드리려고 합니다. 특히 커스텀 유틸리티 클래스를 만들고, 이 클래스 이름을 동적으로 적용하려 할 때 발생할 수 있는 문제인데요. 저의 경우, 컨테이너 쿼리(cqi
)를 사용한 반응형 폰트 크기 클래스(text-bubble
)를 적용하는 과정에서 이 문제를 만났습니다.
겪었던 문제: "분명 클래스는 있는데, 스타일이 안 먹어요!"
상황 요약:
tailwind.config.js
에fontSize
를 확장하여bubble
이라는 키로 커스텀 폰트 크기를 정의했습니다. 이 폰트 크기는clamp()
함수와cqi
단위를 사용하여 컨테이너 너비에 따라 동적으로 변하도록 설정했죠. (예:bubble: "clamp(0.875rem, 4.46cqi, 1.25rem)"
)- 이 커스텀 폰트 크기는
.text-bubble
이라는 클래스로 사용될 예정이었습니다. - 실제 컴포넌트에서는 상황에 따라 다른 말풍선 스타일을 적용하기 위해, 클래스 이름을 포함한 스타일 정보를 JavaScript 객체(
speechBubbleMap
)에 저장해두고, 여기서fontSizeClass
라는 키로.text-bubble
값을 가져와 JSX의className
에 동적으로 할당했습니다.
// speechBubbleMap 예시
const speechBubbleMap = {
"1": (name) => ({
// ... 다른 스타일
fontSizeClass: "text-bubble", // ✅ 동적으로 적용될 클래스 이름
}),
// ...
};
// 컴포넌트 사용 예시
const { fontSizeClass } = speechBubbleMap[imageId](userName);
// ...
return <div className={`some-base-styles ${fontSizeClass} other-styles`}>텍스트</div>;
문제 발생:
개발 서버를 실행하고 브라우저에서 확인해보니, 분명 HTML 요소에는 text-bubble
클래스가 적용되어 있는데, 개발자 도구에서 해당 클래스에 대한 font-size
정의가 전혀 보이지 않았습니다. 당연히 글꼴 크기도 변하지 않았고요.
처음에는 clamp()
나 cqi
단위 문제, 혹은 @tailwindcss/container-queries
플러그인 설정 오류를 의심했습니다. 그래서 tailwind.config.js
의 bubble
값을 '1.5rem'
과 같은 아주 단순한 값으로 바꿔보기도 했죠. 하지만 여전히 font-size
속성은 감감무소식... 🤯
단서 발견: "직접 넣으니깐 보인다!"
그러다 혹시나 해서, 동적으로 할당하던 fontSizeClass
변수 대신, JSX에 직접 className="... text-bubble ..."
이라고 클래스 이름을 하드코딩해보았습니다.
결과: 거짓말처럼 font-size
스타일이 적용되었습니다!
이 순간, 문제의 원인이 Tailwind CSS의 작동 방식과 관련이 깊다는 것을 직감했습니다.
원인 분석: Tailwind CSS의 정적 분석과 동적 클래스
Tailwind CSS는 프로덕션 빌드 시 사용되지 않는 스타일을 제거하여 CSS 파일 크기를 최적화하는 트리쉐이킹(Tree-shaking) 과정을 거칩니다. 이를 위해 content
설정에 명시된 파일들을 정적(static)으로 분석하여 코드 내에 실제로 사용된 클래스 이름 문자열을 찾아냅니다.
문제는 저의 경우처럼 클래스 이름이 변수(fontSizeClass
)를 통해 동적으로 할당될 때 발생합니다. Tailwind의 정적 분석기는 JavaScript 코드를 실행하지 않기 때문에, fontSizeClass
변수가 최종적으로 어떤 문자열 값("text-bubble"
)을 가지게 될지 미리 알 수 없습니다. 따라서 스캔 과정에서 "text-bubble"
이라는 문자열 리터럴을 코드에서 직접 발견하지 못하면, "아, 이 클래스는 사용되지 않는구나!"라고 판단하고 최종 CSS에서 해당 클래스 정의를 누락시키는 것입니다.
직접 클래스 이름을 JSX에 하드코딩했을 때 스타일이 적용된 이유는, Tailwind 스캐너가 마침내 "text-bubble"
이라는 문자열을 발견하고 해당 CSS를 생성했기 때문입니다.
해결책: tailwind.config.js
의 safelist
활용
이렇게 Tailwind가 정적 분석으로 찾아내기 어려운 동적 클래스 이름을 안전하게 항상 CSS에 포함시키도록 하는 방법이 바로 safelist
옵션입니다.
tailwind.config.js
파일에 다음과 같이 safelist
배열을 추가하고, 항상 생성되길 원하는 클래스 이름을 문자열로 넣어주면 됩니다.
// tailwind.config.js
import type { Config } from "tailwindcss";
import containerQueries from "@tailwindcss/container-queries"; // 예시 플러그인
export default {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
safelist: [
'text-bubble', // ✅ 여기에 Tailwind가 놓칠 수 있는 클래스 이름을 추가!
// 필요하다면 다른 클래스 이름이나 패턴도 추가 가능
// {
// pattern: /bg-(red|green|blue)-(100|200|300)/, // 정규식을 이용한 패턴 매칭
// },
],
theme: {
extend: {
fontSize: {
// 이제 원래 의도했던 clamp 값 사용 가능
bubble: "clamp(0.875rem, 4.46cqi, 1.25rem)",
},
// ... 기타 테마 확장
},
},
plugins: [containerQueries], // 예시 플러그인
} satisfies Config;
중요: tailwind.config.js
파일을 수정한 후에는 반드시 개발 서버를 재시작해야 변경사항이 적용됩니다.
safelist
에 'text-bubble'
을 추가하고 서버를 재시작한 후, 다시 fontSizeClass
변수를 사용하여 동적으로 클래스를 적용하니... 드디어! text-bubble
클래스에 font-size: clamp(...)
스타일이 정상적으로 적용되고, 컨테이너 너비에 따라 글꼴 크기도 잘 변하는 것을 확인할 수 있었습니다.
safelist
는 언제 유용할까?
- 클래스 이름이 CMS, 데이터베이스, API 응답 등 외부 데이터로부터 오는 경우
- 사용자 인터랙션이나 JavaScript 로직에 의해 클래스 이름이 동적으로 조합되는 경우 (예:
const dynamicClass = \
bg-${color}-500`;`) - 컴포넌트 라이브러리에서 특정 변형(variant) 스타일들이 항상 사용 가능하도록 보장하고 싶을 때
물론, safelist
에 너무 많은 클래스를 등록하면 최종 CSS 파일 크기가 커질 수 있으므로, 꼭 필요한 클래스들만 선별하여 추가하는 것이 좋습니다.
마무리하며
Tailwind CSS의 정적 분석 기반 최적화는 매우 강력하지만, 때로는 동적으로 클래스를 다루어야 하는 상황과 충돌할 수 있습니다. safelist
는 이러한 간극을 메워주는 유용한 기능이며, 왜 특정 스타일이 적용되지 않는지 디버깅하는 과정에서 Tailwind의 작동 원리를 더 깊이 이해하는 계기가 되었습니다.
혹시 저와 비슷한 문제를 겪고 계신 분이 있다면, 이 글이 조금이나마 도움이 되었으면 좋겠습니다!
'개발 > NEXTJS' 카테고리의 다른 글
nextjs 다크모드 적용시 깜빡이는거 해결 (0) | 2025.03.04 |
---|---|
프로필 이미지가 선명하게 나오지 않는 에러 해결 그리고 하이드레이션 에러 해결 (0) | 2025.03.03 |
사용자 이미지가 새로고침을 할 때 이전의 이미지가 보였다가 다시 되돌아 가는 현상 해결 (0) | 2025.03.02 |
nextjs jwt 토큰에서 만료 값 읽어와서 쿠키 설정하기 (0) | 2025.03.02 |
api router에서 params 가져오는 법 (0) | 2025.02.26 |