웹 개발자에게 인기 있는 테일윈드 CSS가 중대형 프로젝트에서 오히려 독이 될 수 있는 네 가지 이유를 분석한 글입니다. 수많은 유틸리티 클래스의 학습 부담, 구조와 디자인의 분리 원칙 훼손, 일관성 없는 네이밍, 그리고 디자인 시스템의 강제성 부재 등 실무적인 한계점을 조목조목 짚어냅니다. 프레임워크의 이점에도 불구하고 코드의 가독성과 유지보수성이 중요한 환경에서는 신중한 도입이 필요함을 시사합니다.
번역된 본문
테일윈드 CSS(Tailwind CSS)는 웹 개발자들 사이에서 매우 인기 있는 CSS 프레임워크입니다. 사전 그래픽 디자인 지식이 없어도 여백, 색상, 크기의 표준화를 해결해 줍니다. 게다가 약간이라도 체계적이지 못한 성향이 있다면, 단순한 유틸리티(Utility)로 큰 구조를 만드는 작업 방법론(유틸리티 퍼스트 기본 원칙)을 따르도록 강제합니다. 여기에 완벽한 검색 기능과 이해하기 쉬운 공식 문서까지 더해집니다. 저 역시 과거 여러 프로젝트에서 이를 사용하며 빠르게 사용자 인터페이스를 구축하는 데 도움을 받았습니다.
하지만 그럼에도 불구하고, 이것은 모든 상황에서 제가 추천하고 싶은 도구는 아닙니다. 중형 또는 대형 프로젝트에서 테일윈드 CSS를 사용하기 전에 두 번 생각해 봐야 하는 제 관점과 이유를 말씀드리려 합니다. 제가 여러분께 유일하게 요청하는 것은 열린 마음으로 자기 비판을 하고 학습하려는 의지를 갖는 것뿐입니다.
수많은 클래스를 학습해야 합니다
테일윈드에는 수천 개의 유틸리티 클래스가 있습니다. 문서가 아주 훌륭하고 에디터의 자동 완성 기능이 큰 도움이 되긴 하지만, 여러분이 내면화해야 할 기본 클래스에는 한계(한계점)가 존재합니다. 아마도 처음에는 브라우저 탭을 띄워놓고 검색창을 쉬지 않고 사용하며 개발하게 될 것입니다. 각 클래스를 적용하는 가장 적절한 방법을 이해하는 데 시간을 할애해야 하므로 초기에는 진척이 더딘 과정입니다. 게다가 테일윈드의 클래스와 섞이도록 직접 커스텀 클래스를 만들었다면 난이도는 더욱 커집니다.
구조와 디자인의 분리를 깨뜨립니다
테일윈드 CSS는 특정 역할을 하는 원자적(Atomic) 클래스들로 이루어진 거대한 유틸리티 라이브러리입니다. @apply를 사용해 클래스를 그룹화하지 않는 이상, 결국 HTML 내에 수십 개의 클래스를 포함하게 됩니다. 이는 구조(HTML)와 디자인(CSS)을 분리해야 한다는 클래식한 규칙을 깨뜨립니다. HTML이 비대해지고 가독성이 떨어지며, 코드의 재사용성을 잃게 됩니다.
그렇긴 하지만, 이 부분은 잠시 짚고 넘어갈 가치가 있습니다. 이 주제는 테일윈드를 옹호하는 측에서 가장 많이 반박하는 비판이기 때문입니다. 테일윈드의 창시자인 아담 와단(Adam Wathan)은 이에 대한 방어 글을 작성했는데, 그의 주장은 꽤 타당합니다. '관심사의 분리(Separation of concerns)'가 사라지는 것이 아니라 방향이 바뀐다는 것입니다. '의미론적(Semantic)' CSS에서는 CSS가 HTML에 의존하므로 HTML의 스타일은 바꿀 수 있지만 CSS는 재사용할 수 없습니다. 반면 유틸리티 방식에서는 HTML이 CSS에 의존하므로 CSS는 재사용 가능하지만, HTML 자체가 저절로 스타일을 바꾸지는 않습니다. 두 경우 모두 결합도(Coupling)는 존재하며, 다만 화살표의 방향만 달라질 뿐입니다. 이러한 주장은 React나 Vue처럼 논리, 마크업, 스타일을 이미 동일한 파일에 두는 컴포넌트 기반의 세계에서는 잘 들어맞습니다. 거기서는 이미 파일 유형에 따른 분리를 개발자 본인이 깨뜨렸기 때문입니다. 하지만 서버 사이드 렌더링을 사용하고 템플릿과 전통적인 CSS를 사용하는 프로젝트에서는 여전히 구조와 디자인의 분리가 완벽한 의미를 지닙니다. 이는 절대적인 보편적 법칙이 아니라 아키텍처에 따른 결정 사항입니다.
네이밍 일관성이 부족합니다
네이밍이 항상 일관적이지 않으며, 때로는 불투명하여 혼동을 주기도 합니다. 정렬 클래스를 살펴볼까요? items-center, justify-center, text-center, 그리고 place-content-center의 차이점은 무엇일까요? 이름이 직관적이지 않고 명확한 패턴을 따르지 않습니다. 호기심이 생기신다면 말씀드리자면, items-center는 플렉스(Flex) 컨테이너 내의 항목을 정렬하고, justify-center는 주축(Main axis)을 기준으로 정렬하며, text-center는 텍스트를 가운데 정렬하고, place-content-center는 그리드(Grid)에서 콘텐츠를 가운데 정렬합니다. 일관성을 추구하며 이들의 이름을 바꿔본다면 다음과 같을 것입니다: flex-align-items-center, flex-justify-content-center, text-align-center, 그리고 grid-place-items-center 입니다. 차이를 느끼시겠나요? 더 많은 예시도 있습니다: justify-center는 있지만 (align-center가 아닌) items-center를 사용합니다. border는 1px을 주지만 border-2는 2px을 줍니다. 이러한 작은 마찰들이 계속 쌓이게 됩니다.
디자인 시스템이 보이는 것만큼 강력하게 강제되지 않습니다
테일윈드를 찬성하는 큰 논리 중 하나는 여백과 색상 스케일이 일관성을 유지하도록 강제한다는 것입니다. 이는 사실이지만, 함정이 있습니다. 바로 '임의 값(Arbitrary values)'입니다. 아무런 경고 없이 w-[347px], text-[#1a2b3c], 또는 p-[5.5rem]을 작성할 수 있습니다. 이는 시스템을 깨뜨리는 탈출구와도 같습니다. 동일한 프로젝트에서 sky-400과 blue-400을 함께 사용하는 것을 막을 아무런 장치도 없습니다. 궁극적으로 일관성은 프레임워크가 아닌 여전히 개발자의 자제력과 규율에 달려 있습니다.
Tailwind CSS is a very popular CSS framework among web developers. With no prior graphic design knowledge, it solves the standardization of spacing, colors, and sizes for you. And on top of that, if you are even slightly disorganized, it forces you into a working methodology based on building large structures out of simple utilities (Utility-First Fundamentals). Add to all that a documentation that is easy to understand with a flawless search. In the past I used it on several projects, it helped me build user interfaces quickly. And despite all of that, it is a tool I would not recommend in every context. I am going to give you my point of view, my reasons, why you should think twice before using Tailwind CSS on medium or large projects. The only thing I ask of you is an open mind, self-criticism, and a willingness to learn. You have to learn dozens of classes Tailwind has thousands of utility classes. Even though the documentation is very good and the editor autocomplete helps a lot, there is a ceiling of basic classes you have to internalize. Most likely, at first you will develop with a browser tab open and the search box working nonstop. You spend time understanding the most appropriate way to apply each class. A slow process at the start. And if you have created your own classes that mix with Tailwind's, the difficulty grows. It breaks the separation between structure and design Tailwind CSS is a gigantic utility library, atomic classes with specific jobs. Unless you group classes with @apply , you end up including dozens of them in your HTML. This breaks the classic rule of separating structure from design, or HTML from CSS. It bloats the HTML, readability suffers, and you lose reusability. That said, this one is worth pausing on, because it is the criticism that gets rebutted the most. Adam Wathan, the creator of Tailwind, wrote the foundational text of the defense, and his argument is a good one. The separation of concerns does not disappear, it changes direction. With "semantic" CSS, your CSS depends on the HTML; you can restyle the HTML, but your CSS is not reusable. With utilities, your HTML depends on the CSS; the CSS is reusable, but your HTML does not restyle itself. The coupling exists in both cases, only the direction of the arrow changes. The argument holds in a world of components, where React or Vue already put logic, markup, and style in the same file. There, the separation by file type was already broken by you beforehand. But in a server-rendered project, with templates and classic CSS, the separation still makes complete sense. It is not a universal law, it is a decision that depends on your architecture. Its names are not consistent Its naming is not always coherent, at times it is opaque and tends to confuse. Look at the alignment classes. What is the difference between items-center , justify-center , text-center , and place-content-center ? The name is not intuitive and does not follow a clear pattern. In case you are curious, items-center aligns items in a flex container, justify-center aligns them on the main axis, text-center centers text, and place-content-center centers content in a grid. Let me rename them looking for coherence: flex-align-items-center , flex-justify-content-center , text-align-center , and grid-place-items-center . Do you notice the difference? There are more examples: justify-center but items-center (not align-center ); border gives 1px but border-2 gives 2px. Small frictions that add up. The design system is not enforced as much as it seems One of the big arguments in favor of Tailwind is that its spacing and color scale forces you to be consistent. And that is true, but with a catch, the arbitrary values. You can write w-[347px] , text-[#1a2b3c] , or p-[5.5rem] without anything warning you. They are an escape hatch that breaks the system. Nothing stops you from using sky-400 and blue-400 in the same project. Consistency still depends on your discipline, not on the framework. The system helps you, but it does not save you from yourself. It is not a good gateway to learning CSS Tailwind is easy to use if you already master styling. But for those with little CSS experience, it creates a false sense of learning. It conveys that it is a minimalist way of writing CSS, taking away the effort of keeping your code tidy. Using so many pre-built classes hinders a developer's ability to truly master CSS. The time you save naming classes you lose learning to use the framework in the most efficient way. On the other hand, if you go deep into Tailwind, you drift away from learning the fundamentals of the platform. pt-4 forces you to mentally translate to padding-top: 1rem . You gain fluency in the abstraction, not in CSS. To my students and coworkers I recommend learning CSS before Tailwind. And if you are already an experienced developer, you might not even need the framework. It is hard to read Let me show you a button with an indigo background, white text, semibold font, rounded corners, hover, and focus. First in vanilla CSS. I will take the liberty of using variables for colors and spacing, and nesting for hover and focus, something compatible today with every browser. :root { --color-white: #fff; --color-black: #000; --color-indigo: #4f46e5; --color-indigo-hover: #4338ca; --gap-s: 0.5rem; --gap-m: 1rem; } .button { padding: var(--gap-s) var(--gap-m); background-color: var(--color-indigo); color: var(--color-white); font-weight: 700; border-radius: var(--gap-s); &:hover, &:focus { background-color: var(--color-indigo-hover); } } Now in Tailwind CSS using @apply . .button { @apply py-2 px-4 bg-indigo-500 text-white font-semibold rounded-lg hover:bg-indigo-700 focus:bg-indigo-700; } Vanilla CSS is self-explanatory, whereas with Tailwind you have to check the documentation. What color is bg-indigo-500 ? How round is rounded-lg ? Is bg-indigo-700 darker than bg-indigo-500 ? The first snippet is easier to maintain, the learning curve for a new developer is low, and the CSS tends to be modular. You do put in more effort with vanilla CSS, yes, but only at the start. Afterward you have full control of variables, cascade, and naming. A curious detail. The example above uses @apply , and Adam Wathan himself has admitted that @apply "basically only exists to trick people" and that if he started Tailwind from scratch he would not include it . In other words, the route you use to recover the readability of a normal class contradicts Tailwind's philosophy. And if you do not use @apply , then you swallow the class soup in your HTML. The HTML lies to you about priority You may have heard that Tailwind "ignores the cascade". That is an imprecise way to put it, because the real problem is more subtle and interesting. Being able to control which selector wins based on its position in the stylesheet is fundamental to any architecture. A typical mistake among inexperienced web designers is to believe that the order of the classes in the HTML determines which one wins. It does not. Look at this code. <p class="color-red color-green">I am some text</p> What color is it? You do not know. You need to see the CSS cascade. .color-green { color: green; } .color-red { color: red; } Now yes, it is red, because .color-red is defined later. So far, normal CSS. The problem with Tailwind is that you cannot resolve this ambiguity even by reordering your classes. Watch. <p class="text-red-500 text-green-500">I am some text</p> You might think green wins, since it comes last in the attribute. Well, no, you get red. What wins is decided by the order in which Tailwind generates those classes in the final stylesheet, not the order in which you write them in the HTML. The markup lies to you. Reading the HTML you cannot know the result. And you do not fix it by changing the order of the classes, because that order is controlled by the compiler. You are left with !important or with avoiding conflicting utilities. This is what is called a leaky abst