본문으로 건너뛰기
피드

Tailwind를 떠나며 다시 CSS 구조를 배운 이야기

frontend 약 10분
vote
0
댓글
북마크

Julia Evans가 몇 년간 써온 Tailwind를 걷어내고, 의미 있는 HTML과 순수 CSS로 사이트를 다시 정리한 경험을 공유했다. 핵심은 Tailwind를 단순히 버린 게 아니라, Tailwind가 줬던 제약과 시스템을 CSS 코드베이스 안에서 직접 재구성했다는 점이다.

  • 1

    Tailwind의 리셋, 색상 팔레트, 폰트 스케일 같은 시스템은 그대로 배울 만한 자산으로 남음

  • 2

    컴포넌트별 CSS 파일, 전역 색상 변수, 제한된 유틸리티 클래스로 유지보수 범위를 줄임

  • 3

    반응형 처리는 미디어 쿼리보다 CSS Grid, auto-fit, grid-template-areas 같은 웹 표준 기능을 더 활용하는 쪽으로 이동

  • 4

    Tailwind 최신 버전의 빌드 시스템 의존성과 2.8MB짜리 CSS 파일이 마이그레이션의 현실적 동기가 됨

Tailwind를 그냥 버린 게 아니라, 배운 걸 자기 CSS로 옮긴 이야기

  • 글쓴이는 8년 전 Tailwind를 처음 만났을 때 꽤 반가웠다고 함

    • 당시에는 CSS를 어떻게 구조화해야 할지 감이 없었고, 선택지는 거의 ‘완전한 혼돈’ 아니면 Tailwind였음
    • Tailwind 덕분에 작은 사이트를 많이 만들 수 있었고, 그 과정에서 리셋, 색상 팔레트, 폰트 스케일 같은 시스템의 가치를 배움
  • 이번에는 몇몇 사이트에서 Tailwind를 걷어내고, 의미 있는 HTML과 순수 CSS 쪽으로 옮겨봄

    • 본인은 풀타임 프론트엔드 개발자가 아니라 CSS를 띄엄띄엄 배워왔다고 밝힘
    • 그래서 이번 작업은 ‘Tailwind가 싫어서 탈출’이라기보다, Tailwind가 대신 잡아주던 규칙을 직접 설계해보는 실험에 가까움

중요

> 핵심은 Tailwind의 장점을 부정하는 게 아님. Tailwind가 제공하던 제약과 체계를 가져와서, 필요한 부분만 자기 CSS 코드베이스에 다시 심는 쪽에 가까움.

CSS를 나누는 기준

  • 가장 큰 변화는 CSS를 컴포넌트 단위로 나누는 방식임

    • 각 컴포넌트는 고유한 클래스를 갖고, 다른 컴포넌트의 CSS를 덮어쓰지 않는다는 규칙을 둠
    • 각 컴포넌트마다 별도 CSS 파일을 두면, 100줄짜리 컴포넌트를 고칠 때 그 100줄만 생각하면 됨
    • Vue나 React 컴포넌트와 정신적으로는 비슷하지만, 실제로 자바스크립트가 꼭 필요한 건 아님
  • 예를 들어 .zine 같은 클래스를 하나의 컴포넌트로 보고, 그 안에서 .horizontal, .vertical, :hover 같은 변형을 중첩 선택자로 관리함

    • @scope나 웹 컴포넌트처럼 강제 격리를 쓰지는 않음
    • 그래도 “한 컴포넌트의 CSS가 다른 컴포넌트를 건드리지 않는다”는 컨벤션만으로도 유지보수 난이도가 꽤 내려갔다고 함
  • 전역 색상은 colors.css에 CSS 변수로 모아둠

    • --pink, --red, --orange 같은 변수들을 정의해두고 필요한 곳에서 가져다 씀
    • 색상은 워낙 어려운 영역이라 이번 리팩터링에서는 크게 손대지 않았고, 모든 색상이 이 파일에 있어야 한다는 규칙만 둠
  • 폰트 크기는 Tailwind의 스케일을 거의 그대로 가져옴

    • Tailwind에서는 text-lg, text-xl, text-2xl처럼 “좀 크게”라는 감각으로 고를 수 있었음
    • 순수 CSS에서는 --size-lg, --line-height-lg 같은 변수를 만들고 font-size: var(--size-lg)처럼 씀
    • Tailwind보다 조금 장황하지만, px, em, rem을 매번 고민하지 않아도 되는 장점은 유지됨

리셋, 유틸리티, 베이스 스타일

  • CSS 리셋은 Tailwind의 preflight 스타일 앞부분 약 200줄을 복사해서 가져옴

    • 대표적으로 모든 요소에 box-sizing: border-box를 적용하는 부분이 있음
    • 이 설정 덕분에 요소의 너비가 패딩까지 포함해서 계산되는데, 글쓴이는 이미 이 동작에 꽤 익숙해져 있었다고 함
    • html { line-height: 1.5; } 같은 기본값도 무의식적으로 기대하고 있었을 가능성이 큼
  • 유틸리티 클래스는 아주 작게 유지함

    • 여러 컴포넌트에서 반복되는 버튼 같은 요소나, 스크린리더 전용 텍스트를 위한 .sr-only 정도가 여기에 들어감
    • Tailwind식 유틸리티를 전부 재현하려는 게 아니라, 정말 자주 쓰고 조심해서 관리할 것만 남기는 방식임
  • 베이스 스타일은 최대한 적게 시작함

    • 예를 들어 모든 section 안쪽을 950px 중앙 컬럼으로 맞추는 스타일, 링크 색상을 --orange로 지정하는 정도만 둠
    • 처음부터 전역 규칙을 많이 만들면 다시 혼돈이 될 수 있으니, 컴포넌트에서 공통 패턴이 보일 때 아래에서 위로 올리는 방식을 택함

여백과 반응형은 더 CSS답게 접근

  • 여백은 아직 완전히 정답을 찾은 상태는 아니지만, Tailwind 때보다 원칙적으로 다루려는 중임

    • 예전에는 화면이 마음에 들 때까지 패딩과 마진 클래스를 여기저기 붙이는 식이었다고 함
    • 지금은 바깥 레이아웃 컴포넌트가 자식 간격을 책임지게 하려 함
    • 예를 들어 section > * + * { margin-top: 1rem; }처럼 형제 요소 사이에만 여백을 주는 패턴을 실험함
  • 반응형 디자인은 미디어 쿼리보다 CSS Grid를 더 쓰는 방향으로 감

    • Tailwind에서는 md:text-xl처럼 특정 브레이크포인트 이상에서 스타일을 바꾸는 패턴을 많이 썼음
    • 지금은 auto-fitminmax를 써서 넓은 화면에서는 2열, 좁은 화면에서는 1열이 자동으로 되게 만드는 식을 배우는 중임
    • grid-template-areas도 많이 썼는데, 글쓴이는 이 기능을 Tailwind만으로 쓰기 어렵다고 봄

💡

> 반응형을 전부 브레이크포인트로 해결하지 않아도 됨. CSS Grid를 잘 쓰면 레이아웃 자체가 화면 크기에 맞춰 자연스럽게 접히는 구조를 만들 수 있음.

빌드 시스템을 피하고 싶었지만, esbuild는 괜찮았던 이유

  • 개발 중에는 빌드 시스템 없이도 충분히 작업 가능하다고 봄

    • CSS에는 이제 @import가 있고, 중첩 선택자도 표준으로 들어왔음
    • 그래서 reset.css, typography.css, colors.css 같은 파일을 직접 import하며 개발할 수 있음
  • 배포용으로 묶을 때만 esbuild를 쓰는 정도는 받아들일 만하다고 함

    • 예시 명령은 CSS를 번들링하고, SVG는 data URL로, WOFF2 폰트는 파일로 처리하는 식임
    • esbuild는 웹 표준 위에 얹혀 있고, Go로 된 정적 바이너리라서 부담이 적다고 봄

왜 Tailwind에서 멀어졌나

  • 현실적인 이유 중 하나는 Tailwind 최신 버전이 빌드 시스템에 더 강하게 의존하게 됐다는 점임

    • 글쓴이는 빌드 시스템을 거의 쓰지 않는 방식으로 Tailwind를 써왔고, 그래서 몇 년 동안 Tailwind v2에 머물러 있었음
    • 여러 프로젝트에 tailwind.min.css가 2.8MB, gzip 후 270KB짜리로 들어가 있었는데, 본인도 좀 우스운 상태라고 느낌
  • 본인이 CSS를 더 잘하게 된 것도 큰 이유임

    • 처음에는 Tailwind의 제한이 도움이 됐지만, 이제는 이상한 CSS 실험이나 Grid 고급 기능처럼 Tailwind로 표현하기 애매한 것들을 직접 쓰고 싶어짐
    • 어떤 사이트는 순수 CSS와 Tailwind가 섞여 있었고, 이 조합은 유지보수하기 별로였다고 함
    • 의미 있는 HTML을 직접 써보면 어떤 느낌일지도 궁금했다고 함
  • 단순한 도구 선택을 넘어, CSS 전문성을 어떻게 바라볼지에 대한 얘기도 나옴

    • 글쓴이는 예전에는 “CSS는 쉬워 보이는데 왜 안 되지?”라는 식의 태도를 갖고 있었을 수 있다고 돌아봄
    • 하지만 지난 10년 동안 CSS를 진지한 기술로 존중하게 됐고, “CSS가 어렵다”는 건 CSS가 어려운 문제를 풀고 있기 때문이라고 봄
    • 특히 대규모 언어 모델(LLM) 시대에는 사람의 전문성을 더 소중히 봐야 한다는 문제의식도 언급함
  • 이 글의 결론은 꽤 현실적임

    • Tailwind는 많은 걸 가르쳐줬고 실제로 유용했음
    • 하지만 어느 시점부터는 프레임워크가 제공하는 제약을 그대로 쓰는 대신, 필요한 제약을 직접 고르고 설계하는 쪽이 더 맞을 수 있음
    • CSS를 피해야 할 대상으로 보는 대신, 제대로 배워볼 만한 기술로 보는 태도 변화가 글 전체의 핵심임

기술 맥락

  • 이 글에서 중요한 선택은 Tailwind를 완전히 부정하는 게 아니라, Tailwind가 주던 구조화 장치를 순수 CSS로 옮긴 거예요. 리셋, 폰트 스케일, 색상 변수 같은 건 개발자가 매번 새로 고민하면 금방 흐트러지거든요.

  • 컴포넌트별 CSS 파일을 둔 이유는 변경 범위를 줄이기 위해서예요. 한 컴포넌트의 스타일을 고칠 때 사이트 전체 CSS를 머릿속에 올릴 필요가 없으면, 작은 개인 사이트나 문서형 사이트에서도 유지보수가 훨씬 편해져요.

  • 반응형에서 CSS Grid를 더 쓰려는 것도 같은 맥락이에요. 브레이크포인트를 많이 만들면 화면 크기마다 예외가 늘어나는데, auto-fit이나 minmax를 쓰면 레이아웃 규칙 자체가 더 유연해져요.

  • esbuild를 배포 단계에만 쓰는 건 꽤 실용적인 절충이에요. 개발 경험은 웹 표준 CSS에 가깝게 유지하면서, 실제 배포 파일은 하나로 묶어 네트워크 비용을 줄일 수 있거든요.

이 글은 Tailwind 비판이라기보다, 프레임워크가 가르쳐준 규칙을 자기 CSS 설계로 흡수하는 과정에 가깝다. 프론트엔드 개발자라면 ‘유틸리티 우선’과 ‘의미 있는 구조’ 사이에서 어디까지 자동화하고 어디부터 직접 설계할지 다시 생각하게 됨.

댓글

댓글

댓글을 불러오는 중...

frontend

요즘 픽셀 폰트가 그냥 복고 감성이 아닌 이유

1990년대 기기 화면 느낌을 현대 폰트 시스템으로 재해석한 픽셀 폰트 몇 가지를 소개한 글이다. 핵심은 예쁜 복고풍 글자 모양만이 아니라, 실제 제품에서 쓸 수 있게 기준선, 자간, 메타데이터, 세로 메트릭까지 챙기는지가 중요하다는 점이다.

frontend

HTML의 `<dl>`이 생각보다 쓸모 많은 이유

이 글은 HTML의 description list, 즉 `<dl>`, `<dt>`, `<dd>`가 단순 용어 사전용 태그가 아니라 이름-값 쌍 UI를 표현하는 꽤 강력한 시맨틱 도구라고 설명한다. 숙소 편의시설, 요금 내역, 기술 용어 설명, 게임 능력치표처럼 흔한 패턴을 중첩 `<div>` 대신 의미 있는 HTML로 만들 수 있다는 얘기다.

frontend

HTML을 캔버스 안에 넣는 데모 모음이 등장함

구글 크롬 랩스 저장소에 HTML-in-Canvas 관련 데모와 프레임워크 지원 목록이 정리됐다. Duck Hunt 스타일 폼, 흔들리는 버튼, 셰이더 기반 페이지 전환, 천처럼 매달린 폼 같은 실험적 예제가 포함돼 있고 Three.js와 PlayCanvas 쪽 샘플도 연결돼 있다.

frontend

싱글 페이지 앱이 웹을 너무 비싸게 만들었다는 불평

이 글은 싱글 페이지 앱(SPA)이 사용자 경험을 좋게 만든다는 명분 아래 웹의 초기 로딩 비용, 도구 복잡도, 개발 진입 장벽을 키웠다고 비판한다. 페이스북 로그인 페이지의 CSS 3.8MB, 레딧 몇 개 클릭 후 33MB 다운로드 같은 숫자를 들며, 지금의 프론트엔드 생태계가 사람보다 대기업의 요구에 맞춰져 있다고 주장한다.

frontend

네이티브로 끝까지 가려다 텍스트에서 막힌 macOS 개발자의 고백

20년 가까이 macOS와 iOS 네이티브 개발을 해온 작성자가 SwiftUI, AppKit, TextKit 2로 마크다운 채팅 UI를 만들다 결국 WebKit과 Electron 쪽이 훨씬 낫다는 결론에 도달한 글이다. 문제는 성능 하나가 아니라 선택, 스트리밍, 스크롤, 접근성, 텍스트 상호작용 같은 ‘사용자가 당연히 기대하는 기본기’가 네이티브 조합에서 계속 깨진다는 점이다.