| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- input
- html
- Eclipse
- 스프링부트
- 문자열
- SpringBoot
- Array
- 인텔리제이
- 자바문법
- list
- string
- math
- vscode
- junit5
- ArrayList
- 자바스크립트
- IntelliJ
- js
- Java
- CSS
- Visual Studio Code
- 자바
- 배열
- javascript
- java테스트
- 단위테스트
- HashMap
- junit
- 테스트자동화
- 정규식
- Today
- Total
어제 오늘 내일
tailwind-merge로 Tailwind 클래스 충돌 깔끔하게 해결하기 본문
1. 들어가며: 어떤 문제를 해결하는가
Tailwind CSS로 컴포넌트를 만들다 보면 이런 상황을 자주 마주칩니다.
function Box({ className }) {
return <div className={`p-4 bg-white rounded-lg ${className}`}>내용</div>
}
// 사용하는 쪽에서 padding을 다르게 주고 싶어서
<Box className="p-8" />
이렇게 문자열을 단순히 이어 붙이면 최종 className은 p-4 bg-white rounded-lg p-8이 됩니다. 문제는 CSS에서 p-4와 p-8이 같은 우선순위를 가진 클래스라서, 어떤 게 이길지는 CSS가 실제로 로드되는 순서에 달려있다는 점입니다. Tailwind가 빌드 시 클래스를 어떤 순서로 정렬하느냐에 따라 p-4가 이길 수도, p-8이 이길 수도 있어서 예측이 어렵고 버그로 이어지기 쉽습니다.
tailwind-merge는 바로 이 문제, 즉 같은 CSS 속성을 건드리는 Tailwind 클래스끼리 충돌할 때 뒤에 오는 값으로 안전하게 덮어쓰는 역할을 해주는 라이브러리입니다.
2. 설치하기
npm install tailwind-merge
3. 기본 사용법
핵심 함수는 twMerge 하나입니다. 문자열로 된 클래스 목록을 인자로 넘기면, 충돌하는 클래스를 정리한 하나의 문자열을 반환합니다.
import { twMerge } from 'tailwind-merge'
twMerge('p-4 bg-white', 'p-8')
// → 'bg-white p-8'
p-4와 p-8은 둘 다 padding을 지정하는 클래스라서 충돌로 인식되고, 뒤에 온 p-8이 살아남습니다. 반면 서로 다른 속성을 건드리는 클래스는 충돌 없이 그대로 유지됩니다.
twMerge('p-4 text-black', 'bg-white')
// → 'p-4 text-black bg-white' (충돌 없음, 그대로 합쳐짐)
단순 문자열 템플릿과 비교하면 차이가 명확합니다.
// 문자열 템플릿: 충돌 클래스가 그대로 둘 다 남음
`p-4 bg-white rounded-lg ${className}`
// → 'p-4 bg-white rounded-lg p-8' (어느 쪽이 이길지 예측 어려움)
// twMerge: 충돌 클래스를 인식해서 뒤엣것만 남김
twMerge('p-4 bg-white rounded-lg', className)
// → 'bg-white rounded-lg p-8' (항상 p-8이 적용됨)
4. React 컴포넌트에서 활용하기
가장 흔한 활용 패턴은, 재사용 가능한 컴포넌트를 만들 때 기본 스타일은 컴포넌트가 갖고, 사용하는 쪽에서 필요하면 일부를 덮어쓸 수 있게 열어두는 것입니다.
import { twMerge } from 'tailwind-merge'
function Card({ className, children }) {
return (
<div className={twMerge('p-4 bg-white rounded-lg shadow-md', className)}>
{children}
</div>
)
}
// 기본 스타일 그대로 사용
<Card>기본 카드</Card>
// padding과 그림자만 다르게 덮어쓰기
<Card className="p-8 shadow-xl">강조 카드</Card>
이렇게 하면 Card 컴포넌트를 쓰는 쪽에서 className을 넘기기만 하면, 나머지 스타일은 유지한 채 원하는 부분만 안전하게 교체됩니다. props로 스타일 오버라이드를 허용하는 디자인 시스템 컴포넌트를 만들 때 특히 유용합니다.
5. clsx와 함께 쓰기
조건부로 클래스를 조합해야 하는 경우가 많기 때문에, 실무에서는 clsx와 tailwind-merge를 함께 쓰는 경우가 대부분입니다. clsx로 조건에 따른 클래스 조합을 만들고, 그 결과를 twMerge로 한 번 더 정리하는 방식입니다.
import clsx from 'clsx'
import { twMerge } from 'tailwind-merge'
function cn(...inputs) {
return twMerge(clsx(...inputs))
}
이렇게 만든 cn 헬퍼 함수는 shadcn/ui를 비롯한 많은 React 컴포넌트 라이브러리에서 표준처럼 사용되는 패턴입니다.
function Button({ variant, className, disabled, children }) {
return (
<button
className={cn(
'px-4 py-2 rounded-lg font-medium transition',
variant === 'primary' && 'bg-blue-500 text-white',
variant === 'secondary' && 'bg-gray-200 text-gray-800',
disabled && 'opacity-50 cursor-not-allowed',
className // 외부에서 넘긴 클래스가 항상 안전하게 마지막에 병합됨
)}
disabled={disabled}
>
{children}
</button>
)
}
조건부 로직은 clsx가 처리하고, 그 결과에서 발생할 수 있는 클래스 충돌은 twMerge가 정리해주기 때문에 두 라이브러리의 역할이 명확하게 나뉩니다.
6. 동작 원리 간단히 살펴보기
tailwind-merge는 Tailwind의 클래스 이름을 분석해서 어떤 CSS 속성 그룹에 속하는지 내부적으로 매핑하고 있습니다. 예를 들어 p-*, px-*, pt-*는 모두 padding 관련 그룹으로 인식되기 때문에, 서로 겹치는 범위라면 충돌로 판단합니다.
twMerge('px-4 py-2', 'p-8')
// → 'p-8' (px, py를 모두 포함하는 p로 통합)
twMerge('p-4', 'px-8')
// → 'p-4 px-8' (다른 축이므로 병합해서 유지: 세로는 p-4, 가로는 px-8 적용)
또한 반응형 접두사(md:, lg:)나 상태 접두사(hover:, focus:)가 붙은 클래스는 접두사가 같을 때만 충돌로 판단하기 때문에, 아래처럼 브레이크포인트별로 다른 값을 안전하게 유지할 수 있습니다.
twMerge('p-4 md:p-8', 'p-6')
// → 'md:p-8 p-6' (md: 접두사가 없는 p-6만 기본 p-4를 덮어씀)
7. 마무리
tailwind-merge는 화려한 기능을 가진 라이브러리는 아니지만, Tailwind로 재사용 가능한 컴포넌트를 만들 때 발생하는 클래스 충돌 문제를 근본적으로 해결해줍니다. 컴포넌트 라이브러리를 만들거나, className prop으로 스타일 오버라이드를 지원하는 컴포넌트가 하나라도 있다면 도입을 고려해볼 만합니다. 특히 clsx와 함께 cn 헬퍼로 묶어서 쓰는 패턴은 실무에서 사실상 표준처럼 자리 잡았으니, 프로젝트 초기에 유틸 함수로 미리 만들어두는 것을 추천합니다.
'IT > React' 카테고리의 다른 글
| [ React ] Zustand 사용법 : React 상태 관리 가볍게 시작하기 (1) | 2026.08.08 |
|---|---|
| clsx로 React 조건부 클래스 깔끔하게 관리하기 (0) | 2026.08.07 |
| React에서 Tailwind CSS 제대로 활용하기 (설치, 활용) (0) | 2026.08.07 |
| [ React ] useReducer 사용법: 복잡한 상태 로직을 체계적으로 관리하기 (0) | 2026.08.07 |
| [ React ] useCallback 사용법: 함수를 메모이제이션해 리렌더링 최적화하기 (0) | 2026.08.07 |