Tailwind CSS 사용기
이번에 새롭게 블로그를 만들면서 Tailwind CSS를 사용해봤습니다. Tailwind CSS는 익히 들어서 알고 있었지만 몇 가지 거부감에 그동안 사용하길 꺼렸습니다. 언뜻보기에 인라인 스타일과 다를 바 없어보이는 클래스들을 나열한다는게 어색했습니다. 게다가 기존의 CSS in CSS나 CSS in JS와 달리 이름없이 만들어진 컴포넌트들을 다른 사람이 이해할 수 있을지가 의문이었습니다.
그럼에도 사용하게된 이유#
뒤늦게서야 제 스스로가 Utility-First에 의문을 품으면서 동시에 프로젝트에 적용하고 있었다는걸 깨달았습니다.
import styled from 'styled-components';export const Flex = styled.div`display: flex;`;export const FlexCenter = styled(Flex)`justify-content: center;align-items: center;`;...export const Ellipsis = styled.div`white-space: nowrap;overflow: hidden;text-overflow: ellipsis;`;
이런 식으로 레이아웃을 지정하기도 하고,
const devices = {mobile: '(max-width: 576px)',tablet: '(max-width: 992px)',desktop: '(max-width: 1200px)',};const theme = { devices };export default theme;
이렇게 테마도 지정해서 사용했었습니다. Sass(SCSS)와 같은 CSS-in-CSS를 사용할 때도 마찬가지였습니다.
이렇게 작성했던 유틸리티들이 편하다고 생각한 이유는 무엇일까요?
- 작은 단위로 이루어져서 다른 컴포넌트에서 손쉽게 사용할 수 있다.
- 직관적인 네이밍으로 적용 후에 어떤 일이 일어나는 지 바로 알 수 있다.
유틸리티들의 장점을 저도 모르게 느끼고 있었던 겁니다. 동시에 Tailwind CSS에서 추구하는 Utility-First 컨셉에도 공감이 가서 사용을 결정했습니다.
사용하면서 해소된 몇 가지 오해#
CSS의 의미론적 요소는 어디에?#
CSS 시맨틱
CSS의 경우입니다. 다양한 종류의 과일을 나타내기 위해서는 리스트 태그 li 가 있다고 가정해봅시다. div> ul> li 와 .fruits__item 둘 중 어떤 것이 어떤 DOM부분이 선택되었는지 잘 알려줄까요?
CSS의 이름에 의미를 부여하는 건 당연한 일이었습니다.
// CSS<style>.fruits__item {color: orange;}</style><li class="fruits__item">Orange</li>
// Tailwind CSS<li class="text-orange">Orange</li>
CSS와 Tailwind CSS를 비교해보면 어떤 역할을 하는 엘리먼트인지 직관적으로 알아보기 쉬운 쪽은 당연하게도 CSS입니다.
그렇다면 Tailwind CSS를 사용하면 의미론적인 요소를 포기해야 하는 걸까요? 그렇지 않습니다. 이제는 컴포넌트가 그 역할을 대신해주기 때문입니다.
function FruitItem() {return <li class="text-orange">Orange</li>;}
이렇게 하면 클래스 명이 아니라 컴포넌트의 이름만으로도 충분히 의미가 부여됩니다.
인라인 스타일과 무엇이 다를까?#
인라인 스타일은 해당 스타일이 선언된 요소에만 적용됩니다.
아마도 인라인 스타일의 가장 큰 한계는 그들이 있는 요소에만 영향을 미칠 수 있다는 것입니다. 이것은 유틸리티 클래스와의 중요한 차이점입니다.
No, Utility Classes Aren't the Same As Inline Styles - Sarah Dayan
인라인 스타일은 적용한 엘리먼트에만 영향을 줍니다. 바꿔 말하면 적용한 엘리먼트 외 요소들과 상호작용 할 수 없습니다. 예를 들어 :hover와 같은 의사 클래스나 미디어 쿼리 등은 쓸 수 없습니다.
반면에 Tailwind CSS는 PostCSS와 같은 전처리기를 거쳐서 스타일 시트를 생성합니다. 덕분에 인라인 스타일에서 할 수 없었던 것들이 가능해집니다.
<li class="hover:text-orange">Orange</li>
호버 시에 글자 색이 바뀌는 동작도 이렇게 유틸리티 클래스로 처리하면 됩니다.
소소한 팁#
블로그에서 Tailwind CSS를 사용하면서 유용했던 몇 가지를 정리해보려고 합니다.
IntelliSense#
공식 Tailwind CSS IntelliSense를 사용하면 Visual Studio Code에서 자동 완성, 문법 강조, 린팅 등의 기능이 지원됩니다. 제가 tailwind.config.ts에 정의해놨던 스타일들도 똑같이 지원해줘서 좋았습니다.

Prettier Plugin#
공식 Prettier Plugin은 권장된 클래스 순서대로 자동 정렬을 지원합니다. 스타일링에 필요한 클래스를 쭉 나열해서 쓰다가 나중에 수정할 때도 동일한 순서로 정리해줘서 정말 편리했습니다. 덤으로 중복된 클래스를 적는 일도 사라졌습니다.
yarn add -D prettier-plugin-tailwindcss
{...,"plugins": ["prettier-plugin-tailwindcss"]}
Custom Styles#
Tailwind CSS에서 충분히 많은 기본적인 스타일을 지원하지만 필요에 따라서 커스텀 스타일을 추가해야 하는 경우가 있습니다. 저는 이 블로그를 개발하면서 다양한 색을 사용했는데 arbitrary values 없이 개발하고 싶어서 모든 색을 등록해뒀습니다.
import type { Config } from 'tailwindcss';export default {theme: {...,extend: {colors: {light: {DEFAULT: '#dddddd',mute: '#595959',link: '#004ec7',background: '#eef1f5',line: '#e5e7eb',code: {DEFAULT: '#ea580c',background: '#fb923c1a',},},dark: {DEFAULT: '#191c1f',mute: '#b3b3b3',link: '#10b981',background: '#222527',line: '#3a3e42',code: {DEFAULT: '#a78bfa',background: '#f3e8ff1a',},},dimmed: '#00000066',},},},} satisfies Config;
nested object로 작성하면 코드에서는 -로 이어서 씁니다.
<div class="text-light-mute"></div>
DEFAULT로 작성된 부분은 입력하지 않아도 됩니다.
// light: { DEAFULT: '#dddddd' }<div class="text-light"></div>
애니메이션 등록도 됩니다. 저는 블로그 메인 페이지에서 글 리스트에 마우스를 호버하면 이모지가 회전하도록 구현하는 데 썼습니다.
export default {...,animation: {flip: 'flip 1s linear infinite',},keyframes: {flip: {'0%': { transform: 'rotateY(0)' },'100%': { transform: 'rotateY(360deg)' },},},} satisfies Config;
<div className="group-hover:animate-flip ...">{frontmatter.emoji}</div>
Dark Mode#
다크 모드는 정말 손쉽게 추가했습니다. 아무 설정을 건들지 않아도 클래스 앞에 dark:만 붙여주면 다크 모드에서 동작하는 스타일이 정의됩니다.
<footer className="dark:border-dark-line ...">ⓒ 2024. 김도현. All Rights Reserved.</footer>
기본적으로는 prefers-color-scheme에 맞춰서 동작합니다. 만약 클래스를 통해서 직접 조작하려면 darkMode: 'selector' 설정 한 줄만 추가해주면 됩니다.
import type { Config } from 'tailwindcss';export default {...,darkMode: 'selector',} satisfies Config;
개인적으로는 클래스로 조작하는 설정이 좋았습니다. 코드 상에서 페이지 초기 진입 시에 prefers-color-scheme에 따라서 테마를 지정해주고 헤더의 버튼으로 사용자가 제어하게 만들었기 때문입니다.
Custom Utility#
필요에 따라서 직접 커스텀 유틸리티 클래스를 정의할 수도 있습니다. 블로그에서 자주 사용되는 스타일은 따로 유틸리티 클래스로 만들어서 사용했습니다.
CSS 안에서 정의된 Tailwind CSS의 유틸리티 클래스를 쓰고 싶다면 @apply를 이용하면 됩니다.
@layer components {.box {@apply bg-light-background dark:bg-dark-background rounded-lg;}.link {@apply text-light-link dark:text-dark-link;}.mute {@apply text-light-mute dark:text-dark-mute;}}
느낀점#
Tailwind CSS 사용 후기 글들을 읽어보면 공통적으로 나오는 말이 있습니다.
초반 러닝 커브가 있지만, 사용할수록 편리했다.
직접 사용해본 결과 전적으로 공감가는 말이었습니다. 초반에는 스타일을 지정해줄 때 하나하나 공식 문서에 검색했는데 적응한 이후로는 개발이 훨씬 빨라졌습니다. 문법에 어느정도 일관성이 있기 때문에 나중에는 기억나지 않더라도 유추해서 작성했습니다.
styled-component를 쓸 때는 이름 짓는 데 많은 시간을 쓰면서 고통 받았는데 해방되는 기분도 들었습니다. 네이밍 컨벤션을 결정하거나 이런 작은 컴포넌트까지 이름을 지어줘야 하는지 고민하는 데 드는 시간을 많이 줄였습니다.

정의된 스타일로 개발하기 때문에 일관된 스타일도 유지됐습니다. 스타일링하려고 파일이나 커서를 옮기는 컨텍스트 스위칭 시간도 없어서 편리했습니다.
앞서 Tailwind CSS의 좋은 점만 나열한 것 같은데 꼭 그런 것은 아닙니다. 동적 스타일링을 하기 힘들기 때문에 외부 플러그인에 도움을 받아야 하는 경우가 생깁니다. 아쉽게도 블로그를 개발하면서는 동적 스타일링을 할 경우가 많지 않았습니다. 하지만 나중에 규모가 큰 프로젝트에서 사용할 때는 지나칠 수 없는 문제가 될 것 같습니다.
그리고 개인 프로젝트 외에 현업에서 사용해본 적은 없지만 디자인 시스템이 없거나 있어도 애매한 부분에는 arbitrary values가 점점 쌓이면서 금새 복잡해질 것 같습니다.
마침 FE개발그룹에서는 Tailwind CSS를 왜 도입했고, 어떻게 사용했을까? 라는 좋은 아티클이 있어서 같이 남겨놓습니다.
끝으로 많은 선입견을 갖고 있던 Tailwind CSS를 직접 사용해봤습니다. 기존에 개발하면서 느끼던 불편함들이 다양한 방법으로 해소되는 경험이었습니다. 세상에 완벽한 도구란 없으니 필요한 도구를 적합한 장소에 사용할 줄 아는 개발자가 되어야겠습니다.