안녕하세요! 개발이라는 0과 1의 차가운 논리 속에서, 오늘도 어떻게 하면 조금이라도 더 효율적인 로직을 짤 수 있을까 치열하게 고민하는 codeBricks입니다.
오늘은 프론트엔드 개발자라면 누구나 가슴 한구석에 품고 있는 아주 징글징글한 애증의 대상, 바로 'CSS(Cascading Style Sheets)'에 대한 이야기를 해보려고 합니다. 코딩을 갓 시작한 초보자 시절, 화면에 버튼 하나를 예쁘게 띄우고 색깔이 변하는 것을 보며 "와, 내가 마법사가 된 것 같아!" 라며 환호했던 기억이 다들 있으실 겁니다. 하지만 프로젝트의 덩치가 커지고 실무에 투입되는 순간, 그 귀엽던 CSS는 밤잠을 설치게 만드는 거대한 괴물로 변모하곤 하죠.
도대체 웹 개발 생태계에서는 무슨 일이 있었길래, 우리는 과거의 '생짜 CSS'를 버리고 새로운 도구들을 찾아 끝없는 유목민 생활을 하게 된 것일까요? 오늘 그 눈물겨운 진화의 역사를 아주 쉽고 재미있게, 제 뼈아픈 삽질 경험담을 듬뿍 담아 풀어내 보겠습니다.
1. "이름 짓다가 하루가 다 간다" – 순수 CSS와 BEM의 시대
개발자들 사이에서 전해 내려오는 유명한 명언이 하나 있습니다. "컴퓨터 과학에서 가장 어려운 두 가지는 캐시 무효화와 '이름 짓기'다."
저 역시 프론트엔드 개발에 갓 입문했을 때, 이 '이름 짓기'의 저주에 깊게 빠졌습니다. HTML 태그에 스타일을 입히려면 클래스명(className)을 지어줘야 하는데, 이게 정말 고역이거든요. 처음에는 단순하게 갑니다. wrapper, container, box... 그런데 페이지가 늘어날수록 이름이 고갈되기 시작합니다. 나중에는 main-wrapper, real-final-container, header-inner-box 같은 끔찍한 혼종들이 탄생하게 되죠.
가장 큰 문제는 CSS의 치명적인 단점인 '글로벌 스코프(전역 오염)' 현상이었습니다. 메인 페이지에 있는 버튼 색깔을 파란색으로 바꾸려고 .button { color: blue; } 라고 코드를 한 줄 추가했을 뿐인데, 생판 상관없는 결제 페이지의 버튼까지 파랗게 물들어버리는 대참사가 발생합니다. CSS 파일은 프로젝트 전체에 영향을 미치기 때문에, 내가 짠 코드가 어디서 어떻게 충돌할지 예측할 수가 없었던 것이죠.
이 전역 오염을 막기 위해 개발자들은 BEM(Block Element Modifier)이라는 기괴한 이름 짓기 규칙을 만들어냅니다. 어디 소속인지 명확히 하기 위해 언더바 두 개(__)와 하이픈 두 개(--)를 섞어 쓰는 방식이었죠. 그 결과 우리의 HTML 코드는 <div class="main-header__navigation-button--active">처럼 읽기도 힘들 만큼 길고 지저분해졌습니다. 유지보수는 산으로 갔고, 개발자들의 불만은 극에 달했습니다.

2. "자바스크립트 안으로 들어온 구원자" – CSS-in-JS (Styled-components)의 르네상스
CSS 클래스명 짓기의 고통에서 허우적대던 프론트엔드 생태계에, React(리액트)의 부흥과 함께 한 줄기 강력한 빛이 내려옵니다. 바로 Styled-components나 Emotion 같은 'CSS-in-JS' 라이브러리들의 등장이었습니다.
이름 그대로, CSS를 별도의 파일에 작성하는 것이 아니라 아예 자바스크립트(JS) 코드 파일 안에 우겨넣어 버리는 혁명적인 발상이었습니다. 저는 처음 Styled-components를 프로젝트에 도입했던 날의 짜릿함을 아직도 잊지 못합니다.
더 이상 무의미한 클래스명을 고민할 필요가 없어졌습니다. 그냥 JS 변수를 만들듯이 const SubmitButton = styled.button 이라고 선언하고 그 안에 스타일을 적어주면 끝이었으니까요. 라이브러리가 알아서 세상에 하나뿐인 해시값(예: sc-bdfBwQ)으로 클래스명을 암호화해주었기 때문에, 내가 만든 버튼이 다른 페이지의 스타일을 망가뜨리는 '전역 오염' 문제가 100% 완벽하게 해결되었습니다. 게다가 컴포넌트의 상태(State)에 따라 CSS 속성을 변수처럼 다이내믹하게 바꿀 수 있으니, 그야말로 프론트엔드 개발의 르네상스가 열린 듯했습니다. "이제 평생 Styled-components만 안고 가야지!"라고 굳게 다짐했죠.
3. "세상이 또 변했다" – Next.js '서버 컴포넌트'와의 치명적인 불화
하지만 프론트엔드 생태계는 잔인할 만큼 발전 속도가 빠릅니다. 어제의 정답이 오늘의 레거시(낡은 기술)가 되는 일이 비일비재하죠. 영원할 것 같았던 CSS-in-JS의 천하는 최근 Next.js 13 버전 이상의 '앱 라우터(App Router)와 서버 컴포넌트(Server Components)' 패러다임이 등장하면서 산산조각이 나기 시작합니다.
최근 웹 개발의 핵심 트렌드는 "브라우저(클라이언트)가 연산할 자바스크립트의 양을 최소한으로 줄여서, 사용자에게 0.1초라도 더 빨리 화면을 보여주자"는 것입니다. 이를 위해 Next.js는 HTML을 서버에서 미리 다 그려서 던져주는 '서버 컴포넌트'를 기본값으로 채택했습니다.
그런데 여기서 치명적인 모순이 발생합니다. 우리가 사랑했던 CSS-in-JS(Styled-components 등)는 반드시 '브라우저(클라이언트)에서 자바스크립트가 실행(Runtime)되어야만' CSS 코드로 변환되어 화면에 색을 칠할 수 있는 구조였던 것입니다.
서버에서 완성된 HTML이 먼저 도착해서 뼈대를 보여주는데, 정작 색깔과 디자인을 입히는 자바스크립트 파일은 나중에 다운로드되어 실행되다 보니, 사용자의 눈에는 찰나의 순간 동안 뼈대만 있는 흉측한 화면이 번쩍! 하고 나타나게 됩니다. 이를 전문 용어로 FOUC(Flash Of Unstyled Content) 현상이라고 부릅니다.
이 문제를 억지로 해결하려다 보니 서버와 클라이언트 간의 렌더링 결과물이 다르다는 끔찍한 'Hydration(수화) 에러'가 밥 먹듯이 터져 나왔습니다. 결국, 무거운 자바스크립트 연산이 필요한 기존의 CSS-in-JS는 최신 Next.js 환경에서 성능 저하의 주범으로 찍히게 되었고, 전 세계 수많은 기업과 개발자들이 눈물을 머금고 기존 코드를 뜯어내어 새로운 대안을 찾아 대거 이동하는 대탈출극이 벌어지고 있는 것이 현재 2026년 프론트엔드 생태계의 민낯입니다.

마무리하며: 그렇다면 우리의 다음 목적지는 어디인가?
이름 짓기의 고통(생짜 CSS)을 피해 자바스크립트의 품(CSS-in-JS)으로 도망쳤더니, 이제는 성능 문제와 서버 렌더링이라는 거대한 벽에 부딪혀 쫓겨나게 된 프론트엔드 개발자들. 과연 우리는 앞으로 어떤 도구로 화면을 예쁘게 그려내야 할까요?
정답은 '자바스크립트 런타임의 개입 없이, 빌드 단계에서 미리 CSS를 뽑아내면서도 (Zero-runtime), 클래스명 짓기의 고통이 전혀 없는 도구'에 있습니다.
눈치채셨나요? 맞습니다. 전 세계 프론트엔드 개발 생태계를 사실상 통일해 버린 압도적인 1위, 바로 '테일윈드(Tailwind CSS)'가 그 주인공입니다. 처음엔 코드가 너무 더러워 보인다고 쌍욕을 하던 개발자들도, 한 달만 써보면 절대 빠져나올 수 없다는 바로 그 마약 같은 도구죠.
다음 제2편에서는 우리가 왜 그토록 테일윈드에 열광할 수밖에 없는지, 그리고 실무에서 테일윈드를 사용할 때 겪게 되는 문제들을 어떻게 우아하게 해결하는지 그 완벽한 가이드를 들고 찾아오겠습니다. 다음 포스팅도 많은 기대 부탁드립니다!
(지금까지 웹 개발 생태계의 CSS 트렌드 변화에 대해 알아보았습니다. 본 포스팅은 제가 직접 실무를 하며 겪은 기술적 고민과 학습 기록을 바탕으로 작성되었으며, 특정 라이브러리나 기술 스택에 대한 절대적인 우위를 주장하는 것이 아님을 알려드립니다. 프로젝트의 성격과 팀의 상황에 맞는 최적의 기술을 선택하시길 권장합니다.)
[UI/IX 컴포넌트 오픈소스 #02] "처음엔 욕하지만 결국 사랑하게 되는 마약", Tailwind CSS 완벽 가이드
안녕하세요! 매일 쏟아지는 새로운 프론트엔드 기술의 홍수 속에서, 여러분의 퇴근 시간을 1시간이라도 앞당겨드릴 실전 로직을 연구하는 codeBricks입니다.지난 1편에서는 우리가 왜 정들었던 순
code-bricks.tistory.com