과거 웹 개발자들은 웹사이트 전체의 메인 컬러(예: 브랜드 컬러인 파란색 #1da1f2)를 적용하기 위해 수백 개의 CSS 클래스에 color: #1da1f2;를 복사해서 붙여넣었습니다.
어느 날 기획자가 "브랜드 컬러를 약간 보라색 톤으로 바꿔주세요"라고 요청하면, 개발자는 프로젝트 전체를 뒤져가며 수백 개의 코드를 일일이 찾아 바꾸는(Find & Replace) 지옥 같은 막노동을 해야만 했습니다.
프로그래밍의 제1 원칙인 DRY(Don't Repeat Yourself, 중복 금지)가 완전히 박살 난 상태였죠.
이를 해결하기 위해 Sass(SCSS)나 Less 같은 CSS 전처리기(Preprocessor)들이 등장하여 변수 기능을 제공했지만, 이것들은 컴파일 단계에서 값이 고정되어 버리기 때문에 런타임(브라우저 실행 중)에 자바스크립트로 다크 모드 전환 같은 동적인 변화를 주기가 매우 까다로웠습니다. 그리고 마침내, W3C는 CSS 자체에 프로그래밍 언어의 '변수(Variables)' 개념을 도입한 CSS Custom Properties (사용자 지정 속성)를 표준으로 발표했습니다. 이 혁명적인 기능 덕분에 오늘날 우리는 단 한 줄의 자바스크립트 코드만으로 수만 개의 요소가 얽힌 대형 웹사이트의 테마(다크 모드, 라이트 모드)를 0.01초 만에 스위칭할 수 있게 되었습니다.
이번 09단계 포스팅에서는 CSS 변수의 선언과 스코프(Scope) 원리부터, Sass 변수와의 결정적 차이점, 그리고 자바스크립트와의 완벽한 상호작용까지 다뤄보겠습니다.

1. 변수의 탄생: "--" (더블 대시)의 마법
CSS 변수는 이름표를 붙이는 아주 단순한 문법을 가집니다. 변수 이름을 지을 때는 반드시 앞에 하이픈 두 개(--)를 붙여야 합니다. (예: --main-bg-color). 그리고 이 변수에 저장된 값을 꺼내 쓸 때는 var() 함수를 사용합니다. 프로그래밍 언어를 조금이라도 접해본 사람이라면 너무나도 익숙하고 반가운 패턴입니다.
/* 1. 변수를 선언하고 값을 저장합니다 */
:root {
--primary-color: #3498db;
--base-padding: 16px;
}
/* 2. 저장된 변수를 불러와서 재사용합니다 */
.btn {
background-color: var(--primary-color);
padding: var(--base-padding);
}
2. 지배력의 범위: 스코프(Scope)와 :root의 비밀
자바스크립트에 전역 변수(Global)와 지역 변수(Local)가 있듯이, CSS 변수에도 스코프(Scope)라는 유효 범위가 존재합니다. 어디에 선언하느냐에 따라 그 변수를 쓸 수 있는 자식들의 범위가 달라집니다.
전역 변수: :root 가상 클래스
웹사이트의 전체 테마 색상이나 기본 폰트 사이즈 등 모든 페이지, 모든 요소에서 공통으로 끌어다 써야 하는 값은 무조건 :root 가상 클래스 선택자 안에 선언합니다. :root는 HTML 문서의 최상위 뿌리인 <html> 태그와 동일하며, 특이도(Specificity)가 더 높기 때문에 전역 변수를 보관하는 최적의 금고 역할을 합니다. 실무에서는 보통 variables.css라는 파일을 따로 만들어 수십 개의 :root 변수들을 테마 딕셔너리처럼 중앙 관리합니다.
지역 변수와 섀도잉(Shadowing)
특정 컴포넌트 안에서만 쓸 변수라면 해당 클래스 안에 직접 선언할 수 있습니다. 예를 들어 .card { --card-bg: #fff; }라고 선언하면, 이 --card-bg 변수는 .card 자기 자신과 그 뱃속에 있는 자식 요소들만 꺼내 쓸 수 있습니다. 밖에서는 접근할 수 없습니다.
더욱 놀라운 것은 '변수 덮어쓰기(Shadowing)'가 가능하다는 점입니다. :root에 --text-color: black;이 있더라도, 특정 섹션 .dark-section { --text-color: white; }를 선언해 주면, 그 어두운 섹션 안에 있는 자식 요소들이 var(--text-color)를 호출할 때는 흰색으로 바뀌어 적용됩니다. 이 상속(Inheritance)의 원리를 이용하면 엄청나게 우아하고 확장성 있는 컴포넌트 디자인이 가능해집니다.
3. 안전망 구축: Fallback (대체값) 설정
만약 다른 동료 개발자가 실수로 변수 이름을 오타 냈거나, 특정 상황에서 변수에 값이 할당되지 않았다면 어떻게 될까요? 브라우저는 투명하게 변해버리거나 레이아웃을 망가뜨릴 수 있습니다. 이를 방지하기 위해 var() 함수는 두 번째 인자로 '대체값(Fallback)'을 받을 수 있는 강력한 안전장치를 제공합니다.
/* --main-color 변수가 존재하지 않거나 오타가 났다면, 뒤에 있는 #333(진회색)을 대신 적용해라! */
.title {
color: var(--main-color, #333);
}
이 대체값 기능은 특히 여러 개의 테마 파일을 모듈화해서 불러올 때, 변수가 늦게 로딩되더라도 화면이 깨지지 않도록 막아주는 훌륭한 방패막이가 됩니다.

4. CSS 변수의 진정한 파괴력: 자바스크립트(JS)와의 완벽한 소통
CSS 변수가 Sass(SCSS) 같은 전처리기 변수 압도적으로 뛰어넘는 가장 큰 이유가 바로 여기에 있습니다. 순수 CSS 변수는 브라우저의 DOM 트리에 살아 숨 쉬는 속성이기 때문에, 자바스크립트를 사용해 실시간으로 변수의 값을 빼오거나(Get) 새로운 값으로 바꿔치기(Set) 할 수 있습니다.
예를 들어 사용자가 마우스를 움직이는 좌표(X, Y)에 따라 그림자의 방향이 실시간으로 변하는 인터랙티브 버튼을 만든다고 가정해 봅시다. 과거에는 JS에서 버튼의 style.boxShadow를 통째로 매 프레임마다 조작해야 해서 코드가 더럽고 브라우저 렌더링 성능이 심각하게 저하되었습니다. 하지만 이제는 JS에서 단순히 --mouse-x, --mouse-y CSS 변수의 숫자 값만 틱틱 업데이트해주고, CSS에서는 box-shadow: var(--mouse-x) var(--mouse-y) 10px black; 이라고 받아먹기만 하면 끝입니다. 로직(JS)과 디자인(CSS)이 완벽하게 분리되면서도 환상적인 퍼포먼스를 내는 모던 웹 인터랙션의 핵심 비법입니다.
정리하며
이번 09단계 포스팅에서는 지옥 같은 하드코딩에서 우리를 구원해 주고, 다크 모드와 실시간 테마 변경이라는 모던 웹의 필수 요소를 단 한 줄의 코드로 구현하게 해주는 기적의 기술, CSS Variables에 대해 깊이 있게 살펴보았습니다. 변수를 통해 CSS 코드가 훨씬 스마트해지고 관리가 편해졌다면, 이제 다시 화면 배치(Layout)의 이야기로 돌아갈 시간입니다. 지난 시간에 배운 float라는 기괴한 해킹 기법의 끔찍함을 기억하시나요? W3C는 그 참상을 두고 볼 수 없었고, 오직 '레이아웃'만을 위해 창조된 완벽하고 아름다운 구세주를 하사하셨습니다. 프론트엔드 개발자들의 빛이자 소금! 다음 10단계 포스팅에서는 "CSS Flexbox (1편): 부모(Container)의 규칙, 가로세로 1차원 배치의 마법"에 대해 상세히 파헤쳐 보겠습니다!
'StyleSheet > CSS' 카테고리의 다른 글
| [CSS] Flexbox 컨테이너 속성 (0) | 2026.07.28 |
|---|---|
| [CSS] Flexbox (1편: Container) (0) | 2026.07.28 |
| [CSS] Float와 Clearfix (0) | 2026.07.28 |
| [CSS] Positioning (0) | 2026.07.28 |
| [CSS] Display (0) | 2026.07.28 |