시작은 2014년의 발표 하나였다
2014년 11월, Facebook의 Christopher Chedeau, 일명 vjeux가 NationJS에서 "CSS in JS"라는 발표를 했다. 내용은 단순했다. 대규모 코드베이스에서 CSS가 일으키는 문제를 일곱 가지로 정리한 것이다. 전역 네임스페이스, 의존성 관리, 죽은 코드 제거, 코드 압축, 상수 공유, 비결정적인 우선순위 해석, 격리. 하나하나가 Facebook 규모에서는 실제로 돈이 나가는 문제였고, 발표의 결론은 이 가운데 앞의 다섯 개는 JavaScript 안에서 스타일을 관리하면 풀리고 나머지 둘은 아직 미해결로 남는다는 것이었다.

지금 보면 당연한 얘기 같지만, 그때 기준으로는 꽤 세게 나간 말이었다. CSS는 CSS 파일에 쓰고 JavaScript는 건드리지 않는 게 상식이던 시절이다. 말하자면 관심사 분리를 파일 확장자로 하고 있었다. vjeux는 그걸 대놓고 뒤집었다. 관심사는 파일 종류가 아니라 컴포넌트 단위로 나눠야 한다고.
React가 퍼지면서 이 말이 먹히기 시작했다. 화면을 컴포넌트로 쪼개다 보니, 마크업과 로직은 한 파일에 있는데 스타일만 멀리 떨어진 CSS 파일에 있는 게 오히려 어색해졌다.
styled-components의 시대
2016년, Max Stoiber와 Glen Maddern이 styled-components를 공개했다. 그해 10월에 1.0이 나왔다. API는 이렇게 생겼다.
const Button = styled.button`
background: ${props => props.primary ? 'palevioletred' : 'white'};
color: ${props => props.primary ? 'white' : 'palevioletred'};
`;이 코드가 그 시절에 왜 그렇게 매력적이었을까. 크게 세 가지다.
첫째, 클래스 이름을 지을 필요가 없어졌다. BEM처럼 block__element--modifier를 고민하며 이름 짓던 시간이 통째로 사라졌다. 스타일은 컴포넌트에 붙고, 스코프는 자동으로 격리된다.
둘째, props로 스타일을 바꿀 수 있었다. 위 코드처럼 primary 값에 따라 색이 바뀌는 걸 JavaScript 표현식 하나로 끝낸다. 상태에 따라 클래스를 붙였다 뗐다 하는 코드가 필요 없다.
셋째, 쓰지 않는 스타일이 저절로 정리됐다. 컴포넌트를 지우면 스타일도 같이 지워진다. vjeux가 짚은 죽은 코드 문제가 이걸로 풀렸다.
2017년에 emotion이 나오면서 경쟁이 붙었고, 이후 몇 년은 CSS-in-JS의 전성기였다. Material-UI를 비롯한 주요 컴포넌트 라이브러리가 runtime CSS-in-JS를 채택했고, React 프로젝트를 새로 시작하면 styled-components냐 emotion이냐가 자연스러운 첫 질문이었다.
문제는 런타임 비용이었다
그런데 이 편리함에는 대가가 있었다. runtime CSS-in-JS는 이름 그대로 런타임에 동작한다. 컴포넌트가 렌더링될 때마다 스타일 객체를 직렬화하고, 해시를 계산해 클래스 이름을 만들고, 그 결과를 <style> 태그로 문서에 주입한다. 브라우저 입장에서는 렌더링 중에 스타일시트가 계속 바뀌는 셈이라, 그때마다 스타일 재계산이 일어난다.
앱이 작을 때는 티가 안 난다. 문제는 규모가 커진 다음이다. 수백 개의 컴포넌트가 각자 스타일을 직렬화하고 주입하면 그 비용이 쌓인다. 서버 사이드 렌더링에서는 또 다른 비용이 붙는다. 서버에서 생성한 스타일을 HTML에 심어 보내고, 클라이언트에서 하이드레이션을 하면서 같은 작업을 한 번 더 하는 구조가 되기 때문이다.
이 문제를 가장 아프게 건드린 건 정작 내부 사람이었다. 2022년 10월, emotion의 핵심 메인테이너 중 한 명이던 Sam Magura가 "Why We're Breaking Up with CSS-in-JS"라는 글을 올렸다. 자기 회사 Spot의 실제 컴포넌트로 측정해 보니 emotion으로 구현한 버전의 렌더링 시간이 Sass Modules 버전의 두 배 가까이 나왔다는 내용이었다. 평균 54.3ms 대 27.7ms. emotion을 유지보수하던 사람이 emotion을 걷어냈다고 공개적으로 밝힌 셈이니, 이 글은 커뮤니티에서 한참 화제가 됐다.

돌아보면 이때부터 분위기가 꺾였다. 성능 문제는 전부터 알려져 있었지만, 그걸 가장 잘 아는 사람이 결론을 내려버리니 다들 자기 선택을 다시 따져보게 됐다.
RSC에서는 아예 안 돌아간다
성능 논쟁은 그래도 득실을 따져 고를 수 있는 문제였다. 팀에 따라 개발 편의를 얻는 대신 성능을 조금 내주기로 할 수도 있다. 그런데 React Server Components는 득실의 문제가 아니라 구조의 문제였다.
runtime CSS-in-JS는 렌더링 중에 스타일을 만들어 문서에 주입한다. 그런데 서버 컴포넌트에는 주입할 문서가 없다. 브라우저가 없는 곳에서 실행되기 때문이다. 그래서 styled-components나 emotion을 쓰는 컴포넌트는 전부 'use client'를 붙여 클라이언트 컴포넌트로 만들어야 한다. 서버 컴포넌트의 이점을 살리려는 프로젝트일수록 runtime CSS-in-JS가 발목을 잡았다.
2023년 Next.js App Router가 안정화되면서 이건 남의 이야기가 아니게 됐다. 새 프로젝트를 App Router로 시작하는 순간 스타일링 선택지에서 runtime CSS-in-JS가 사실상 빠졌다.
2025년 3월, styled-components의 메인테이너 Evan Jacobs가 유지보수 모드 전환을 선언했다. 새 기능은 더 넣지 않고, 치명적인 버그 수정과 보안 패치만 정해진 기간까지 제공한다는 내용이었다. 선언문에는 이런 문장이 있다.
For new projects, I would not recommend adopting styled-components or most other css-in-js solutions.
만든 쪽이 신규 도입을 말리는 라이브러리가 됐다. 2016년에 나온 지 9년 가까이 지난 시점이다.

아이디어는 지금 도구들에 그대로 남아 있다
그러면 CSS-in-JS는 실패한 실험이었나. 그렇게 보면 절반만 본 거다. 지금 스타일링 도구들을 보면 CSS-in-JS가 주장했던 것 대부분이 그대로 살아남아 있기 때문이다.
zero-runtime 계열이 대표적이다. Linaria, vanilla-extract, Panda CSS, MUI의 Pigment CSS, Meta의 StyleX. 이 도구들로 스타일을 쓰는 느낌은 CSS-in-JS와 거의 같다. TypeScript 안에서 스타일을 쓰고, 토큰에 타입이 붙고, 스타일을 컴포넌트 바로 옆에 둔다. 다른 건 실행 시점 하나다. 런타임에 하던 일을 전부 빌드 타임으로 옮겨서, 결과물은 평범한 정적 CSS 파일로 나온다. 런타임 비용도 없고 서버 컴포넌트와의 충돌도 없다. StyleX가 이 계열에서 어디까지 갔는지는 따로 쓴 글에서 다뤘다.
Tailwind도 여기 놓고 보면 결이 같다. 문법만 보면 CSS-in-JS와 정반대 같다. 하지만 클래스 이름 짓기를 없애고, 스타일을 마크업 옆에 두고, 지우면 같이 지워지게 만든다는 점은 똑같다. vjeux가 꼽은 문제들에 내놓은 또 다른 답인 셈이다. 실제로 runtime CSS-in-JS를 떠난 팀들이 가장 많이 간 곳이 Tailwind였다.
CSS 자체가 좋아진 것도 빼놓을 수 없다. CSS 변수가 모든 브라우저에 깔렸고, 네이티브 중첩 문법이 2023년에 주요 브라우저에 다 들어왔고, @layer로 우선순위를 직접 제어할 수 있게 됐다. 2014년에는 JavaScript를 동원해야 풀리던 문제 중 상당수가 이제 CSS만으로 해결된다. JavaScript를 끌어들일 이유 자체가 줄어든 것이다.
그래서 이 10여 년은 뭐였나
정리하면 이렇다. CSS-in-JS가 꺼낸 문제는 전부 진짜였다. 스타일은 컴포넌트 단위로 관리해야 하고, 스코프는 격리해야 하고, 죽은 스타일은 지워야 한다. 이제 와서 이걸 두고 다투는 사람은 없다. 어떤 도구를 고르든 기본으로 깔려 있다.
틀렸던 건 실행 시점이었다. 이 모든 걸 런타임에 하기로 한 탓에 성능 문제가 생겼고, 서버 컴포넌트와는 아예 같이 쓸 수 없게 됐고, 결국 이 방식을 대표하던 라이브러리가 유지보수 모드까지 왔다.
그래서 이 10여 년을 실패로 부르기는 어렵다. runtime CSS-in-JS는 밀려났지만, 그게 증명한 요구사항 위에서 지금의 도구들이 만들어졌다. styled-components로 짠 코드는 언젠가 다 걷어내겠지만, 클래스 이름을 고민하던 시절로 돌아갈 사람은 없다.
참고 자료
- Christopher Chedeau (vjeux), React: CSS in JS (NationJS, 2014)
- Sam Magura, Why We're Breaking Up with CSS-in-JS (2022)
- Evan Jacobs, styled-components 유지보수 모드 선언 (2025)