내가 Tailwind CSS를 사랑하는 이유
들어가는 글
내 이력서에 적혀진 프로젝트 모두 Tailwind CSS를 사용하고 있다. 처음에는 낯설었지만, 지금은 대부분의 개인/사이드 프로젝트에서 사용할 정도로 애정하는 기술이 되었다. 이 글에서는 Tailwind CSS에 대한 간단한 소개와 왜 내가 이 기술을 애정하게 되었는지, 더 효과적으로 사용하는 방법은 무엇인지 적어보고자 한다.
Tailwind CSS란?
마크업에 직접적으로 모든 디자인을 구축할 수 있는 클래스들로 구성된 유틸리티 우선 CSS 프레임워크
사전에 정의되어 있는 여러 CSS 유틸리티 클래스들(ex. justify-content: space-between;
→ justify-between
)을 활용해 스타일링할 수 있도록 제공하는 프레임워크이다. 거두절미하고 예제를 살펴보자.
CSS와 React로 작성한, 간단한 프로필 카드를 만드는 예제이다.
.profile-card-container {
display: flex;
flex-direction: column;
width: fit-content;
row-gap: 0.5rem;
padding: 1rem 1.5rem;
background-color: #f8fafc;
}
.profile-card-name {
color: #06b6d4;
font-size: 1.5rem;
line-height: 2rem;
font-weight: 700;
}
.profile-card-bio {
color: #67e8f9;
font-size: 0.875rem;
line-height: 1.25rem;
}
const ProfileCard = ({ name, bio }) => {
return (
<section className="profile-card-container">
<h1 className="profile-card-name">{name}</h1>
<p className="profile-card-bio">{bio}</p>
</section>
);
};
위의 코드를 Tailwind CSS로 변경해보면 아래와 같다.
const ProfileCard = ({ name, bio }) => {
return (
<section class="flex flex-col w-fit gap-y-2 px-6 py-4 bg-slate-50">
<h1 class="text-cyan-500 text-2xl font-bold">{name}</h1>
<p class="text-cyan-300 text-sm">{bio}</p>
</section>
);
};
display: flex
는 flex
, background-color: #f8fafc
는 bg-slate-50
으로 축약된 것을 볼 수 있다. 위에서 말한 “사전에 정의되어 있는 여러 CSS 유틸리티 클래스들”이 이에 해당된다.
Bootstrap과의 차이점
Bootstrap을 경험해본 사람은 기능 단위의 클래스명이 유사하다고 느낄 수 있으나, 다른 점이 있다. Bootstrap은 btn btn-primary
등의 이미 스타일링된 클래스를 제공하기 때문에 UI 컴포넌트 라이브러리에 가까운 반면, Tailwind CSS는 스타일링을 유저에게 (거의) 온전히 맡긴다. 자유도가 훨씬 높다는 말로 정리할 수 있겠다.
인라인 스타일과의 차이점
마크업에 직접 작성되는 스타일의 형태는 인라인 스타일을 떠올리게 한다. 실제로, “Tailwind CSS는 그냥 인라인 스타일 아닌가요?”와 같은 질문이 올라오기도 한다. 이에 관한 내용은 공식 문서에 자세히 설명되어 있다. 요약하자면 디자인 시스템을 구축할 수 있으며, 반응형 디자인을 지원하고, hover 시 스타일링 등을 쉽게 다룰 수 있는 점에서 인라인 스타일과 구분된다는 내용이다. 관련해서 작성된 좋은 글도 첨부한다. (No, Utility Classes Aren't the Same As Inline Styles)
Tailwind CSS의 장점
생산성
“내가 Tailwind CSS를 사랑하는 이유”라는 제목에 가장 걸맞는 장점이다.
컨텍스트 스위칭 비용 감소
화면을 분할해서 CSS 파일과 마크업 파일을 동시에 보거나, 파일을 왔다갔다하며 시간을 썼던 경험이 있었을 것이다. Tailwind CSS는 마크업 파일에 스타일을 작성하는 방식이기 때문에, 마크업 파일 하나에만 집중해도 된다.
네이밍 고민 X
CSS 클래스명의 네이밍 문제에 대해서 많은 논의들이 있었고, BEM, SMACSS 등의 방법론이 나왔다. 하지만, 다양한 한계점과 더불어 네이밍의 고통에서 벗어날 실버불렛은 아니었다. 이후 CSS-in-JS 라이브러리들의 등장과 함께 클래스명에 대한 고민은 사라지는 줄…. 알았으나, 클래스명 네이밍이 변수명 네이밍으로 바뀌었을 뿐 네이밍은 여전히 개발자의 몫이었다. Tailwind CSS를 사용한다면 스타일에 대한 네이밍은 고민하지 않아도 된다. 그저, 스타일을 적어내리면 된다.
작고 간편한 클래스명
코파일럿이 지배한 요즘엔 큰 장점이 아닐 수도 있겠으나, CSS 속성들을 축약해 작성할 수 있다. 예를 들어, grid-template-rows: repeat(1, minmax(0, 1fr))
를 grid-rows-1
로, text-decoration-line: underline
을 underline
으로 작성할 수 있다. 이는 작성할 때 타이핑이 빨라지는 장점도 있지만, 짧아서 가독성이 향상된다는 장점도 존재한다. (부작용: 기존 CSS 문법을 잊어버림)
기능성
자유로운 커스텀
Bootstrap과의 차이점에서 서술했듯이, Tailwind CSS는 커스터마이즈가 매우 자유롭다. 기본적으로 제공하는 컬러 셋업을 변경하거나, 단위 체계를 변경해 자체적인 디자인 시스템을 구현할 수 있다. (공식 문서)
성능
이전 버전의 Tailwind CSS는 미리 정의된 CSS 파일을 모두 불러오는 방식으로 작동하여, 성능 상 좋지 않았다. 하지만 이후 업데이트에서, 사용하지 않는 CSS는 빌드 시점에 제거하는 기능을 내장하여 실제로 사용되는 CSS만을 불러오는 방식으로 변경되었다. 현 시점에서 Tailwind CSS의 성능과 CSS-in-JS 라이브러리들의 성능을 비교하면, JS 번들 사이즈에 영향을 주지 않으며 CSS를 효과적으로 캐싱하는 Tailwind CSS 쪽이 낫다고 얘기할 수 있다. (관련 아티클: Why We’re Breaking Up with CSS-in-JS | Emotion 메인테이너)
Tailwind CSS의 단점
생산성
러닝 커브
대부분의 Tailwind CSS 클래스명은 CSS를 아는 사람이라면 어느 정도 이해할 수 있게끔 네이밍되어 있다. 다만 (개인적으로) 그렇지 않게 느껴지는 네이밍도 있다. (ex: display: none
→ hidden
) 또, 자체 제공하는 단위 체계가 4px 단위로 작성되어 있어 익숙해지는 데 시간이 필요하다. (ex: width: 0.5rem
→ w-2
) CSS만 알면 바로 코딩할 수 있는 CSS-in-JS와 비교되는 지점이다. 그러므로 다수의 인원이 함께하는 프로젝트에서 Tailwind CSS를 도입할 때에는 충분한 고민이 필요하다.
가독성
Tailwind CSS의 가장 큰 진입 장벽은 역시 길게 늘어진 클래스명이지 않을까 싶다. 사람들이 Tailwind CSS를 싫어하는 대표적인 이유이기도 하다. 간단한 스타일링은 괜찮지만, 다크 모드 지원, hover 등에 따른 동적 스타일링까지 포함된 복잡한 컴포넌트를 보게 되면 굉장히 길쭉해져 읽기 어려움을 느낄 수 있다. 이에 대한 해결책은 클래스명을 변수로 분리해 사용하거나, Tailwind CSS에서 제공하는 @apply로 클래스를 분리하는 방식을 고려해볼 수 있다. 하지만 이 방식들은 유틸리티 클래스의 장점을 무효화시킨다는 치명적 단점이 있다.
기능성
지원되지 않는 일부 기능
Tailwind CSS는 굉장히 활발히 메인테이닝이 진행되고 있는 프로젝트이고, 지금도 유틸리티 클래스들이 추가되고 있지만 CSS의 모든 기능을 커버하지는 못한다. 예를 들어, perspective 등의 3d 관련 속성들을 지원하지 않는다. 지원되지 않는 속성들은 [속성명:값]
형태로 작성하거나, 직접 유틸리티 클래스를 만들어 사용할 수도 있지만 불편한 건 사실이다.
불편한 동적 스타일링
const Container = ({ width }) => {
const color = width < 5 ? "#000" : "#FFF";
return <div className={`w-[${width}px] bg-[${color}]`} />;
};
위의 코드를 실행하면, w-[10px]
와 bg-[#FFF]
가 적용되지 않았음을 확인할 수 있다. CSS-in-JS와 달리 Tailwind CSS는 빌드 시점에 미리 유틸리티 클래스를 감지해 빌딩하는 방식이므로, 런타임에서 작성된 동적 유틸리티 클래스는 적용할 수 없다. 위의 코드를 정상적으로 작동하게 하려면, 이러한 방식을 사용할 수 있다.
const Container = ({ width }) => {
const color = width < 5 ? "bg-[#000]" : "bg-[#FFF]";
return <div className={color} style={{ width }} />;
};
인라인 스타일을 혼용해 사용해야 한다는 점에서 좋지 않다.
Tailwind와 함께 쓰면 좋은 것들
Editor
- Tailwind CSS IntelliSense(VS Code, WebStorm): Tailwind CSS는 공식적으로 에디터 플러그인을 제공한다. 자동완성, 프리뷰(Tailwind CSS의 클래스명에 호버하면 실제 CSS 코드를 보여준다!), 린트 등의 편리한 기능이 있으니 사용이 필수적이다.
- Inline fold(VS Code): 마크업에 집중하고 싶을 때, 길게 늘어진 클래스명을 접어서 가독성을 향상시켜주는 플러그인이다.
Prettier
- prettier-plugin-tailwindcss: 클래스명을 정렬해주는 Prettier 플러그인이다. 순서에 익숙해지면 가독성이 크게 향상되는 효과를 볼 수 있다.
Others
- clsx: React className에서의 조건부 스타일링을 덜 복잡하고, 구조화된 방식으로 작성할 수 있는 유틸 함수이다.
- Headless UI: Tailwind 팀에서 개발한 Headless UI 컴포넌트이다. Tailwind CSS와 궁합이 좋고, 공식 홈페이지의 예제도 모두 Tailwind CSS 기반이어서 편리하다. Modal, Switch 등의 UI가 필요하다면 사용해보는 것을 추천한다.
마치며
Tailwind CSS에는 언급한 단점들이 존재하지만, 내게는 생산성이라는 장점이 압도적으로 크게 다가왔고 보조 라이브러리와 기능들을 잘 이용한다면 단점이 다수 개선된다고 느꼈다. 자주 사용하다 보니 그 생각은 확신이 되었고 많은 사람들이 Tailwind CSS를 써봤으면 좋겠다는 생각이 들어 글을 적게 되었다. Tailwind CSS를 처음 접하는 사람들, 혹은 싫어하는 사람들에게 이 글의 내용이 설득되었을지는 모르겠지만, 애정하는 라이브러리인 만큼 많은 사람에게 쓰여졌으면 좋겠다.
참고 문서
- CSS Utility Classes and "Separation of Concerns" | Adam Wathan (Tailwind 제작자의 글, 코드 예시로 알아보는 CSS 대장정)
- In Defense of Utility-First CSS | frontstuff (유틸리티 우선 CSS에 대한 사실과 오해)