Tailwind vs Linaria: 성능 조사
원문을 번역하고 사내 발표용으로 정리한 글입니다. 측정 화면과 그래프, 전체 논증은 원문에 있으니 함께 보시길 권합니다. 원문: Tailwind vs Linaria: performance investigation (developerway.com)
무엇을 조사했나
Tailwind는 요즘 “성능이 가장 좋다”는 평을 자주 듣습니다. 빌드 타임 CSS 솔루션들이 여럿 나와 있는데도 정말 그런지, 실제 앱으로 확인해본 조사입니다.
비교 대상은 Tailwind와 Linaria입니다. Linaria는 styled-components가 유지보수 모드로 들어간 뒤 관심을 받고 있는데, 중요한 건 Linaria가 특별해서가 아니라 빌드 타임에 평범한 CSS 파일을 만들어내는 전통적 방식의 대표라서입니다. CSS 모듈로 바꿔도 결론은 비슷합니다.
측정 조건은 이렇습니다.
- 버튼 백만 개 같은 합성 예제가 아니라, 로그인·대시보드·설정 세 페이지를 갖춘 실제 앱
- 원본은 Tailwind로 작성돼 있고, 같은 앱을 Linaria로 다시 구현해 짝을 맞춤
- 프로덕션 빌드, CPU 6배 스로틀링 + 느린 4G, 3회 측정의 중간값
세운 가정
Tailwind는 유틸리티 클래스를 재사용하므로 CSS는 작아지고 className이 길어져 HTML/JS는 커지는 경향이 있습니다. 그리고 초기 로드의 임계 경로는 HTML과 CSS입니다. 그렇다면,
- CSR에서는 CSS가 작은 Tailwind가 조금 유리할 것이다
- SSR에서는 HTML이 커진 만큼 Tailwind가 불리할 것이다
이게 조사 전의 가정이었습니다.
결과 1: 자산 크기는 예상대로 움직였다
빌드 산출물을 재보니 가정대로였습니다.
- CSS: Tailwind가 13% 작음
- JavaScript: Tailwind가 3% 큼
- HTML(SSR로 생성): 페이지마다 편차가 커서 4%, 70%, 162% 증가. 설정 페이지는 두 배 이상 불어남
결과 2: 그런데 초기 로드는 전혀 달라지지 않았다
CSR에서 설정 페이지 LCP를 재봤더니 두 프로젝트가 사실상 동일했습니다. 이유는 성능 프로필을 보면 분명합니다. CSR 앱의 HTML은 빈 div뿐이라, 브라우저는 화면에 그릴 게 없어 JS가 다운로드·컴파일·실행될 때까지 기다립니다. 병목이 CSS가 아니라 JS 크기였고, JS는 3%밖에 차이가 안 나니 결과가 같을 수밖에 없습니다.
SSR로 바꿔도 마찬가지였습니다. 원시 HTML 크기가 162%까지 차이 나는데도 다운로드 시간은 거의 같았습니다. 압축 때문입니다. Tailwind의 HTML은 같은 클래스 이름이 반복되므로 오히려 압축이 잘 듣습니다. 실제 전송량 기준으로 CSS 차이는 1KB 남짓이었고, 네트워크를 3G까지 낮춰도 결과는 바뀌지 않았습니다.
정리하면 이 규모(수십 KB)에서는 CSS·HTML 크기 차이가 초기 로드에 0의 영향을 줍니다. 눈에 띄려면 킬로바이트가 아니라 메가바이트 단위여야 하고, 그 정도로 커졌다면 CSS 프레임워크 교체보다 코드 분할·지연 로딩이 먼저입니다.
결과 3: 예상 못 한 곳에서 차이가 났다 — 인터랙션
초기 로드가 아니라 인터랙션(INP)에서 차이가 나왔습니다. 페이지 이동은 같았는데 드롭다운 열기와 모바일 사이드바 드로어 열기가 Tailwind 쪽에서 눈에 띄게 느렸습니다. 절대값은 불안정했지만 추세는 반복 측정에서도 일관됐습니다.
성능 프로필의 병목은 양쪽 다 Recalculate style이었고, Tailwind 쪽 막대가 약 두 배 길었습니다(85ms + 72ms 대 48ms + 40ms).
원인은 Selector stats에서 드러났습니다. Tailwind 프로젝트가 *, ::before, ::after에 대해 두 배 많은 매칭을 시도하고 있었고, Linaria에는 없는 ::backdrop까지 있었습니다. CSS를 열어보니 이런 규칙이 있었습니다.
*, :before, :after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
/* 다른 --tw- 접두사 스타일들 */
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
/* 다른 --tw- 접두사 스타일들 */
}*는 모든 DOM 노드에 적용되는 유니버설 선택자입니다. 이 블록을 Linaria 프로젝트의 빌드된 CSS에 그대로 붙여넣자 Linaria도 똑같이 느려졌습니다. 원인 확인 완료입니다. 출처는 @tailwind base; 임포트이고, 프레임워크의 필수 구성이라 사용자가 걷어낼 방법은 마땅치 않습니다.
결론
- Tailwind는 실제로 CSS를 줄이고 HTML·JS를 늘린다. 가정은 맞았다.
- 하지만 그 증감은 초기 로드 성능에 아무 영향도 주지 않았다. 압축과 JS 병목이 차이를 다 흡수한다.
- 대신
@tailwind base의 유니버설 선택자 때문에 일부 인터랙션의 스타일 재계산이 느려진다. 이건 예상 밖이었다.
그래서 Tailwind가 성능상 나쁜 선택이냐 하면, 아닙니다. 이 인터랙션 저하는 엣지 케이스에 가깝고, 로우 티어 모바일 시뮬레이션에서도 여전히 “초록” 영역이었습니다. CPU를 20배로 줄여야 빨간색이 됐습니다. 아주 낡은 기기와 나쁜 네트워크를 주 대상으로 하는 앱이라면 주시할 만하지만, 대부분에게는 차단 요소가 되기 어렵습니다.
Tailwind냐 Linaria냐는 개발자 경험, 유지보수 상태, API 안정성 같은 평소의 기준으로 고르면 됩니다. 성능은 둘 다 꽤 오랫동안 문제가 되지 않을 겁니다.
이 글에서 가져갈 진짜 교훈은 Tailwind에 대한 판정이 아니라 조사 방법입니다. 가정을 세우고, 측정하고, 가정이 틀렸을 때 프로필을 열어 왜 틀렸는지까지 따라가는 것. 이 조사에서는 가정이 거의 전부 틀렸고, 그래서 더 배울 게 많았습니다.