Tailwind 클래스 병합하기: tailwind-merge

soheekim·2025년 1월 4일

트러블슈팅

목록 보기
2/3

Tailwind 클래스 병합하기: tailwind-merge

문제 발생 배경부터 해결까지

  • clsx 라이브러리를 통해 컴포넌트 내부에 지정한 기본 스타일과 커스텀 스타일(props로 받아온 스타일)을 병합해 사용하고 있었다.
  • 그런데 내가 원하는 방식과 반대로, 기본 스타일이 커스텀 스타일을 덮어쓰는 문제가 생겼다.
  • 나는 clsx가 중복된 클래스 이름을 처리한다고 해서 이런 문제도 해결해주는 줄 알았는데, clsx는 단순히 클래스명을 문자열로 연결만 할 뿐, 클래스의 우선순위를 제어하지 않는다고 한다. (clsx는 단순히 입력받은 인자들을 순회하며 유효한 클래스명들을 찾아서 공백으로 구분된 하나의 문자열로 연결하는 기능을 제공) css는 선택자 우선 순위 점수가 동일하다는 전제 하에 동일한 속성을 여러 번 지정할 경우 뒤에 오는 값이 앞에 오는 값을 덮어쓰기 때문에 아래 코드의 경우에도 커스텀 스타일이 기본 스타일을 덮어쓸 줄 알았다. 그러나 안 됨.
    className={clsx(
        "bg-blue", // 기본 스타일
        className // 커스텀 스타일
    )}
  • 내가 원하는 결과를 얻으려면 tailwind-merge 라이브러리를 이용해야 했다.
  • 더 알아보니 많은 프로젝트에서 clsx와 tailwind-merge의 장점을 결합한 cn 유틸리티 함수를 사용한다고 한다. 이 함수는 clsx의 조건부 클래스 적용 기능과 tailwind-merge의 클래스 충돌 해결 기능을 모두 활용할 수 있게 해준다. 그래서 나도 프로젝트에 cn 유틸리티 함수를 추가했다.
  • 하지만 tailwind-merge에도 한계가 있었는데, 그것은 바로 background 관련 속성을 여러 번 사용할 경우 일부 background 속성만 적용되는 문제가 있다는 것.
  • 이 문제의 해결책은 복잡한 background 속성 사용 시 className 속성이 아닌 style 속성을 사용하는 것이었다.

더 정리된 내용 👇

문제 상황

  • 컴포넌트에서 기본 스타일과 커스텀 스타일을 병합할 때 Tailwind 클래스 충돌 발생
  • 기본 스타일이 커스텀 스타일을 덮어쓰거나, 의도하지 않은 스타일이 적용되는 문제

해결 방법

  1. tailwind-merge와 clsx 설치:

    pnpm add tailwind-merge clsx
  2. 유틸리티 함수 생성:

    // utils/cn.ts
    import { ClassValue, clsx } from 'clsx';
    import { twMerge } from 'tailwind-merge';
    
    export const cn = (...inputs: ClassValue[]) => {
      return twMerge(clsx(inputs));
    };
  3. 컴포넌트에서 사용:

    import { cn } from '@/utils/cn';
    
    export default function Button({ className, ...props }) {
      return (
        <button 
          className={cn(
            "px-4 py-2 bg-button-primary", // 기본 스타일
            className // 커스텀 스타일
          )} 
          {...props}
        />
      );
    }

tailwind-merge의 한계와 해결책

  1. 다중 배경 속성 충돌 문제

    • background-colorbackground-image 등을 함께 사용할 때 클래스가 제대로 적용되지 않음
    • 예: bg-input-bgbg-[image:var(--svg-bg)] 충돌
  2. 해결책: style 속성 사용

    const backgroundStyle = {
      backgroundColor: '#FFFBEB', // 배경색
      backgroundImage: 'url(...)', // 배경 이미지
    } as React.CSSProperties;
    
    return (
      <div
        style={backgroundStyle}
        className={cn(
          'other-classes',
          className
        )}
      />
    );

불편하다. 정말로 이게 최선인가?

결론

1. 클래스 병합 전략

  • cn 유틸리티 함수를 사용하여 기본적인 클래스 충돌 해결
    className={cn(
      'base-styles',
      condition && 'conditional-styles',
      className
    )}```

2. Background 속성 처리

  • tailwind-merge는 background 관련 속성을 처리할 때 제한이 있으므로, 복잡한 background 스타일링이 필요한 경우 style 속성 사용을 권장
  • background-color만 사용하는 경우 -> tailwind 클래스 사용
  • background-image나 여러 background 속성을 함께 사용하는 경우 -> style 속성 사용

다만 이는 tailwind-merge의 현재 버전(작성 시점 기준)에서의 제한사항이므로, 추후 라이브러리가 업데이트되면 더 나은 해결책이 제시될 수 있다.

참고

0개의 댓글