기술 포스트

Tailwind v4로 옮기면서 배운 것들


Article

이 블로그는 처음부터 Tailwind v4로 만들었지만, 회사 프로젝트 하나를 v3에서 v4로 옮기는 작업도 같이 하고 있다. 두 경험을 겹쳐 보니 v4에서 달라진 게 뭔지 선명해져서 정리한다.

설정 파일이 사라졌다

v4의 가장 큰 변화는 CSS-first 설정이다. tailwind.config.js가 없어지고 CSS 파일 안에서 모든 걸 정의한다.

/* v3: tailwind.config.js에서 JS 객체로 정의 */
/* v4: CSS에서 바로 정의 */
@import "tailwindcss";

@theme {
  --color-bg: #0a0a0a;
  --color-fg: #ffffff;
  --color-muted: #b5b5b5;
  --color-surface: #171717;
  --color-accent: #f87171;
  --font-sans: var(--font-noto-sans-kr);
  --font-serif: var(--font-gowun-batang);
}

@theme에 선언한 토큰은 자동으로 유틸리티 클래스가 된다. --color-bg를 선언하면 bg-bg, text-bg, border-bg가 전부 생긴다. 처음엔 어색했는데, 써보니 디자인 토큰과 유틸리티가 한 몸이 되는 구조라 오히려 자연스럽다.

CSS 변수라서 얻는 것

v3에서도 CSS 변수 색상을 쓸 수 있었지만, rgb(var(--bg) / <alpha-value>) 같은 우회가 필요했다. v4는 토큰 자체가 CSS 변수라 그냥 쓰면 된다. 불투명도 변형도 바로 된다.

<p class="text-fg/50">보조 텍스트</p>
<div class="border border-fg/15 bg-fg/18">배지</div>

런타임에 변수 값만 바꾸면 테마 전환도 되니까, 다크모드 대응이 클래스 토글이 아니라 변수 교체 문제로 단순해진다.

postcss 설정도 한 줄

빌드 파이프라인도 가벼워졌다. autoprefixerpostcss-import가 내장돼서 플러그인이 하나만 남는다.

// postcss.config.mjs
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

v3 프로젝트에서 이 부분 옮길 때 기존 autoprefixer를 지우지 않아서 잠깐 헤맸다. v4는 브라우저 접두사를 알아서 처리하니 남겨두면 중복이다.

마이그레이션에서 걸린 것들

공식 업그레이드 툴(npx @tailwindcss/upgrade)이 대부분을 처리해주지만, 손으로 고친 게 몇 가지 있다.

  • @layer components에 정의한 커스텀 클래스에서 @apply가 참조하는 유틸리티가 바뀐 경우. 특히 shadow-smshadow-xs처럼 스케일 이름이 밀린 것들
  • theme() 함수를 쓰던 곳. v4에서는 그냥 var(--color-accent)로 바꾸는 게 맞다
  • arbitrary value로 색을 박아둔 곳(bg-[#171717]). 이 참에 전부 토큰으로 회수했다

세 번째가 사실 제일 오래 걸렸는데, 제일 잘한 일이기도 하다. 마이그레이션은 흩어진 하드코딩을 토큰으로 모으는 강제 이벤트로 쓰는 게 남는 장사다.

정리

v4의 방향은 분명하다. 설정을 JS에서 CSS로, 색상을 문자열에서 변수로. CSS의 원래 능력에 가까워지는 쪽이다. 새 프로젝트라면 고민할 이유가 없고, v3 프로젝트도 업그레이드 툴 덕에 부담이 크지 않다. 다만 커스텀 클래스가 많은 프로젝트라면 @apply 주변은 손으로 검수할 각오는 하는 게 좋다.

Tailwind v4로 옮기면서 배운 것들 — 디코드랩(DCODELAB)