Tailwind CSS에서 동적 클래스명 사용 시 트러블슈팅

oversleep·2025년 4월 18일
0

troubleshooting

목록 보기
18/19

문제 상황

최근 React 프로젝트에서 체크박스 컴포넌트를 개발하던 중, 체크 표시의 색상을 동적으로 변경하고 싶었습니다. 그래서 다음과 같이 코드를 작성했습니다:

<div className={`text-${checkColor} opacity-0 peer-checked:opacity-100`}>
  <Check className="h-3 w-3" />
</div>

하지만 페이지를 확인해보니 색상이 전혀 적용되지 않았습니다. 왜 그럴까요?

원인 파악

알고 보니 Tailwind CSS의 핵심 작동 방식과 관련된 문제였습니다.

Tailwind CSS는 빌드 타임 최적화를 합니다

  • Tailwind는 성능 최적화를 위해 실제 코드에서 사용된 클래스만 최종 CSS에 포함시킵니다
  • 개발 시점에 확인할 수 있는 클래스만 생성합니다
  • 런타임에 동적으로 생성된 클래스명은 CSS에 포함되지 않습니다

간단히 말해, text-red-500처럼 직접 작성한 클래스는 포함되지만, text-${변수} 형태로 동적 생성된 클래스는 CSS에 포함되지 않기 때문에 스타일이 적용되지 않는 것입니다.

헷갈리는 부분: 모든 동적 코드가 문제인 것은 아닙니다

흥미롭게도, 다음과 같은 코드는 잘 작동합니다:

<div
  className={`h-4 w-4 rounded ${
    variant === 'default'
      ? 'peer-checked:border-primary-50 peer-checked:bg-primary-50 border border-gray-300 bg-white'
      : 'border-transparent bg-transparent'
  }`}
></div>

이는 많은 개발자들을 혼란스럽게 합니다. "왜 어떤 동적 코드는 되고, 어떤 것은 안될까?"

동작하는 것과 동작하지 않는 것의 차이:

  1. 동작하지 않는 예: 클래스명 자체가 동적으로 생성되는 경우

    className={`text-${checkColor}`}
    • 빌드 시점에 실제 클래스명을 알 수 없음
    • checkColor가 'red-500'이면 'text-red-500'이 되어야 하지만, 빌드 시점에는 알 수 없음
  2. 동작하는 예: 정적 클래스명 중 조건부로 선택하는 경우

    className={isRed ? 'text-red-500' : 'text-blue-500'}
    • 빌드 시점에 'text-red-500'과 'text-blue-500' 모두 인식됨
    • 런타임에는 단지 둘 중 하나를 선택할 뿐

핵심 차이점: 동적으로 새로운 클래스명을 생성하는 것은 안 되지만, 정적으로 정의된 클래스명들 중에서 선택하는 것은 가능합니다.

팀원의 코드 리뷰: clsx는 도움이 될까?

이 문제에 대해 팀원이 다음과 같은 코드 리뷰를 남겼습니다:

"동적으로 지정하신 클래스 명은 코드래빗 리뷰처럼 빌드시 안먹힐 수도 있어서, clsx로 처리해보심은 어떨까요?"

clsx란 무엇인가?

clsx는 CSS 클래스를 조건부로 결합하는 데 사용되는 유틸리티 라이브러리입니다. 복잡한 조건부 클래스 처리를 더 깔끔하게 할 수 있게 해줍니다.

import clsx from 'clsx';

// 조건부 클래스 적용
<div className={clsx(
  'base-class',
  isActive && 'active-class',
  error ? 'error-class' : 'normal-class'
)}>
  Hello
</div>

clsx는 해결책일까?

하지만 중요한 점은 clsx도 Tailwind의 동적 클래스 생성 문제를 해결해주지 않는다는 것입니다:

// 이렇게 해도 여전히 문제가 있음
<div className={clsx(`text-${checkColor}`, 'opacity-0')}>
  Hello
</div>

clsx는 클래스 결합을 깔끔하게 관리하는 도구일 뿐, 빌드 타임에 인식되지 않는 동적 클래스 문제 자체는 해결해주지 않습니다.

실제 해결 방법

이 문제를 해결할 수 있는 네 가지 방법이 있습니다:

1. 인라인 스타일 사용하기 (가장 간단한 방법)

<Check 
  className="h-3 w-3" 
  style={{ color: checkColor }} // 직접 색상 지정
/>

2. Tailwind Safelist 설정하기

사용할 모든 가능한 클래스를 미리 등록하는 방법입니다.

// tailwind.config.js
module.exports = {
  // 기존 설정...
  safelist: [
    'text-white',
    'text-red-500',
    'text-blue-500',
    // 사용할 가능성 있는 모든 클래스들
  ]
}

3. CSS 변수 활용하기

<div 
  className="text-[var(--check-color)]" 
  style={{ '--check-color': checkColor }}
>
  <Check className="h-3 w-3" />
</div>

4. clsx와 미리 정의된 클래스 조합 사용하기

모든 가능한 색상을 조건부로 적용합니다:

<div className={clsx({
  'text-red-500': checkColor === 'red-500',
  'text-blue-500': checkColor === 'blue-500',
  'text-green-500': checkColor === 'green-500',
  'opacity-0': true,
  'peer-checked:opacity-100': true,
})}>
  <Check className="h-3 w-3" />
</div>

이 방법은 가능한 모든 케이스를 미리 알고 있을 때만 사용 가능합니다.

실제 적용 사례

우리 프로젝트에서는 체크박스 컴포넌트를 다음과 같이 수정했습니다:

// 수정 전 (작동하지 않음)
<div className={`text-${checkColor} opacity-0 peer-checked:opacity-100`}>
  <Check className="h-3 w-3" />
</div>

// 수정 후 (제대로 작동함)
<div className="opacity-0 peer-checked:opacity-100">
  <Check 
    className="h-3 w-3" 
    style={{ color: checkColor }}
  />
</div>

자주 묻는 질문

Q: 왜 text-${checkColor}는 작동하지 않는데, 조건부 클래스 선택은 작동하나요?

// 작동하지 않음
className={`text-${checkColor}`}

// 작동함
className={`${variant === 'default' ? 'text-white' : 'text-gray-300'}`}

A: 이는 Tailwind의 빌드 프로세스 때문입니다:

  1. 첫 번째 예제에서는 빌드 시점에 text-${checkColor}라는 텍스트만 보입니다. Tailwind는 checkColor에 어떤 값이 들어갈지 모르기 때문에 필요한 CSS를 생성할 수 없습니다.

  2. 두 번째 예제에서는 빌드 시점에 문자열 'text-white''text-gray-300'이 모두 인식됩니다. 실제 어떤 값이 선택될지는 런타임에 결정되지만, 두 클래스 모두 빌드 시점에 인식되어 CSS에 포함됩니다.

간단한 규칙: 클래스 이름 자체가 완전한 문자열로 코드에 있어야 합니다. 변수나 표현식으로 클래스 이름의 일부를 만들면 작동하지 않습니다.

배운 점

Tailwind CSS와 함께 작업할 때 알아두면 좋은 내용들:

  1. 동적 클래스명 생성은 피하기 (text-${dynamicValue} 피하기)
  2. 동적 스타일이 필요하면 인라인 스타일이나 CSS 변수 활용하기
  3. 미리 알 수 있는 모든 클래스는 safelist에 등록하기
  4. clsx는 조건부 클래스 처리를 깔끔하게 해주지만, 동적 클래스 이름 문제 자체는 해결해주지 않음
  5. 가능한 모든 경우의 수가 제한적이라면 clsx와 조건부 클래스를 함께 사용하기

결론

Tailwind CSS는 강력한 도구이지만, 동적 클래스 생성에는 주의해야 합니다. 팀원의 코드 리뷰에서 언급된 clsx는 코드를 더 깔끔하게 만들어주는 좋은 도구이지만, Tailwind의 동적 클래스 문제 자체를 해결해주지는 않습니다.

이 문제를 해결하려면 인라인 스타일, CSS 변수, safelist, 또는 미리 정의된 클래스들을 조건부로 적용하는 방법 중 하나를 선택해야 합니다. 각 프로젝트의 상황과 필요에 맞는 방법을 선택하면 됩니다.

Tailwind CSS와 React를 함께 사용할 때 이러한 특성을 이해하면, 스타일링 오류를 예방하고 더 효율적으로 개발할 수 있습니다!

profile
궁금한 것, 했던 것, 시행착오 그리고 기억하고 싶은 것들을 기록합니다.

0개의 댓글