Contents
see ListCSS-in-JS vs Tailwind CSS 비교
React 프로젝트에서 스타일링 방식 선택은 중요한 결정입니다. CSS-in-JS(styled-components, Emotion)와 Tailwind CSS의 특징, 장단점을 비교하여 프로젝트에 맞는 선택을 도와드립니다.
언제 어떤 것을 선택하나요?
- CSS-in-JS: 동적 스타일링, 컴포넌트 캡슐화 중시
- Tailwind: 빠른 개발, 일관된 디자인 시스템
CSS-in-JS 예시 (styled-components)
import styled from "styled-components";
// 기본 스타일 컴포넌트
const Button = styled.button`
padding: 10px 20px;
background-color: ${props => props.primary ? "#3498db" : "#fff"};
color: ${props => props.primary ? "#fff" : "#333"};
border: 2px solid #3498db;
border-radius: 4px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;
// 확장
const LargeButton = styled(Button)`
padding: 15px 30px;
font-size: 18px;
`;
// 사용
function App() {
return (
<>
<Button>기본</Button>
<Button primary>Primary</Button>
<LargeButton primary>Large</LargeButton>
</>
);
}Tailwind CSS 예시
// 기본 사용
function Button({ primary, children }) {
return (
<button className={`
px-5 py-2.5 rounded border-2 border-blue-500 cursor-pointer
transition-opacity hover:opacity-90
disabled:opacity-50 disabled:cursor-not-allowed
${primary
? "bg-blue-500 text-white"
: "bg-white text-gray-800"
}
`}>
{children}
</button>
);
}
// clsx/classnames로 가독성 향상
import clsx from "clsx";
function Button({ primary, size, disabled, children }) {
return (
<button
className={clsx(
"rounded border-2 border-blue-500 transition-opacity",
"hover:opacity-90 disabled:opacity-50",
{
"bg-blue-500 text-white": primary,
"bg-white text-gray-800": !primary,
"px-3 py-1.5 text-sm": size === "sm",
"px-5 py-2.5": size === "md",
"px-7 py-3 text-lg": size === "lg",
}
)}
disabled={disabled}
>
{children}
</button>
);
}비교표
| 항목 | CSS-in-JS | Tailwind |
|---|---|---|
| 학습 곡선 | 낮음 (CSS 문법) | 중간 (클래스 암기) |
| 번들 크기 | 런타임 증가 | 빌드 시 제거 (작음) |
| 동적 스타일 | 매우 좋음 | 제한적 |
| 성능 | 런타임 오버헤드 | 빠름 (순수 CSS) |
| 캡슐화 | 완벽 | 없음 |
| 디자인 일관성 | 수동 관리 | 자동 (토큰 기반) |
| IDE 지원 | 좋음 | 매우 좋음 |
| SSR | 추가 설정 | 문제 없음 |
Tailwind 장점
// 1. 일관된 스페이싱/색상 시스템
// p-4 = 1rem, p-8 = 2rem (4px 단위)
<div className="p-4 m-2 space-y-4">
// 2. 반응형이 직관적
<div className="text-sm md:text-base lg:text-lg">
// 3. 상태 변형이 간단
<button className="bg-blue-500 hover:bg-blue-600 active:bg-blue-700">
// 4. 다크모드 쉬움
<div className="bg-white dark:bg-gray-800">
// 5. @apply로 재사용
/* styles.css */
.btn-primary {
@apply px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600;
}CSS-in-JS 장점
// 1. 동적 스타일링
const Box = styled.div`
width: ${props => props.width}px;
background: ${props => props.theme.colors.primary};
`;
// 2. 테마 시스템
<ThemeProvider theme={darkTheme}>
<App />
</ThemeProvider>
// 3. CSS 기능 완전 지원
const Animation = styled.div`
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
animation: spin 1s linear infinite;
`;
// 4. 컴포넌트와 스타일 함께 관리
// Button.jsx 파일 하나에 모든 것이 있음하이브리드 접근법
// Tailwind + CSS-in-JS 조합
import styled from "styled-components";
import tw from "twin.macro";
const Button = styled.button`
${tw`px-4 py-2 rounded font-bold`}
${props => props.primary
? tw`bg-blue-500 text-white`
: tw`bg-gray-200 text-gray-800`
}
`;
// CVA (Class Variance Authority) - Tailwind용
import { cva } from "class-variance-authority";
const button = cva("px-4 py-2 rounded font-bold", {
variants: {
intent: {
primary: "bg-blue-500 text-white",
secondary: "bg-gray-200 text-gray-800",
},
size: {
sm: "text-sm",
md: "text-base",
lg: "text-lg px-6 py-3",
},
},
defaultVariants: {
intent: "primary",
size: "md",
},
});
<button className={button({ intent: "primary", size: "lg" })}>추천
- 신규 프로젝트, 빠른 개발: Tailwind CSS
- 복잡한 동적 UI, 테마: CSS-in-JS
- 대규모 팀, 디자인 시스템: Tailwind + CVA
- 기존 CSS 경험 활용: CSS Modules 또는 CSS-in-JS