프로젝트가 커지면서 나는 같은 값을 여기저기 반복해서 적고 있다는 걸 깨달았다. 브랜드 색 하나를 수십 군데에 똑같이 써두었는데, 그 색을 조금 바꾸려니 그 수십 군데를 일일이 찾아 고쳐야 했다. 간격도, 모서리 둥글기도 마찬가지였다. 이 반복을 한 곳에서 관리할 방법이 절실했고, 그 답이 CSS 변수, 정식 이름으로는 커스텀 프로퍼티였다.
이번 편은 값을 이름으로 관리하는 CSS 변수를 다룬다. 변수를 어떻게 선언하고 불러 쓰는지, 상속과 범위는 어떻게 작동하는지, 실행 중에 값을 바꿔 테마를 전환하는 법, 계산과 결합해 컴포넌트의 조절 장치처럼 쓰는 법, 그리고 변수를 애니메이션하는 최신 기능까지 살펴본다. 변수를 손에 쥐면 스타일이 훨씬 유연하고 유지보수하기 좋아진다.
1. 값을 이름으로 관리하기
CSS 변수는 자주 쓰는 값에 이름을 붙여 저장해두고, 필요한 곳에서 그 이름으로 불러 쓰는 기능이다. 브랜드 색이나 기본 간격 같은 값을 변수로 정의해두면, 그 값을 쓰는 모든 곳이 변수를 참조하게 된다. 나는 이 방식으로 반복되던 값들을 하나의 이름 아래 모았다. 값이 바뀔 때 정의 한 곳만 고치면 참조하는 모든 곳이 따라 바뀐다.
변수를 정의하는 것과 불러 쓰는 것은 방식이 나뉜다. 정의할 때는 이름과 값을 짝지어 적고, 불러 쓸 때는 전용 함수로 그 이름을 참조한다. 나는 이 참조 방식이 처음엔 낯설었지만, 익숙해지고 나니 변수를 쓰지 않던 시절로 돌아갈 수 없게 되었다. 값이 무엇을 의미하는지 이름으로 드러난다는 것만으로도 코드가 훨씬 읽기 좋아진다.
변수를 불러 쓸 때는 폴백 값을 함께 지정할 수 있다. 참조하려는 변수가 정의되어 있지 않으면 대신 쓸 값을 미리 정해두는 것이다. 나는 변수가 항상 정의되어 있으리라 장담할 수 없는 재사용 컴포넌트에서 이 폴백을 유용하게 쓴다. 변수가 없어도 최소한의 기본값으로 안전하게 표시되니, 예상 못 한 빈 값으로 화면이 깨지는 걸 막는다.
변수의 이름은 의미를 담아 짓는 것이 중요하다. 단순히 색의 이름을 그대로 쓰기보다, 그 색이 어떤 역할을 하는지를 이름에 담으면 좋다. 나는 특정 색 이름 대신 강조색이나 표면색처럼 역할 중심의 이름을 쓴다. 그러면 나중에 실제 색을 바꿔도 이름은 여전히 유효하다. 이름이 값이 아니라 역할을 가리키게 하는 것이 좋은 변수 설계의 시작이다.
변수는 값이라면 거의 무엇이든 담을 수 있다. 색이나 크기뿐 아니라 여러 값의 묶음, 심지어 값의 일부만 담아 조립할 수도 있다. 나는 그림자 설정이나 여러 값으로 이루어진 복잡한 속성도 변수로 만들어 재사용한다. 반복되는 것이라면 무엇이든 이름을 붙여 관리할 수 있다는 점이 변수의 폭넓은 쓸모다.
변수의 근본 가치는 단일한 진실의 원천을 만드는 데 있다. 같은 값이 여러 곳에 흩어져 있으면 언젠가 서로 어긋나기 마련이다. 나는 디자인의 핵심 값들을 변수로 모아 한 곳에서 관리함으로써, 값들이 항상 일치하도록 유지한다. 색과 간격이 사이트 전체에서 일관되게 유지되는 것은 이 단일한 원천 덕분이다.
2. 상속과 범위
CSS 변수는 상속된다. 어떤 요소에 정의한 변수는 그 자식들에게 물려 내려가, 자식들도 그 변수를 참조할 수 있다. 나는 이 상속 덕분에 변수를 전역에서 관리할 수 있다는 걸 알았다. 최상위 요소에 변수를 정의하면 문서 전체의 모든 요소가 그 변수에 접근할 수 있다. 상속은 변수를 전역 설정처럼 쓸 수 있게 해주는 토대다.
전역 변수는 보통 문서의 최상위 요소에 정의한다. 여기에 정의한 변수는 어디서든 참조할 수 있어, 사이트 전체의 기본 설정을 담기에 적합하다. 나는 브랜드 색, 기본 간격, 모서리 둥글기 같은 사이트 공통 값들을 이 최상위에 모아둔다. 이곳이 프로젝트의 디자인 설정이 사는 중심 장소가 되는 것이다.
변수는 특정 요소에서 지역적으로 다시 정의할 수도 있다. 전역에 정의한 변수를 어떤 컴포넌트 안에서만 다른 값으로 덮어쓰면, 그 컴포넌트와 자식들에게만 새 값이 적용된다. 나는 이 지역 재정의를 컴포넌트의 변형을 만드는 데 쓴다. 같은 컴포넌트라도 지역 변수 값만 바꾸면 촘촘한 형태나 넉넉한 형태를 손쉽게 만들 수 있다.
이 지역 재정의는 상속과 맞물려 강력하게 작동한다. 변수는 정의된 요소부터 그 아래로만 새 값이 흐르므로, 컴포넌트마다 독립적인 설정을 가질 수 있다. 나는 한 카드는 촘촘하게, 다른 카드는 넉넉하게 만들 때, 각 카드에 간격 변수를 지역적으로 다르게 정의한다. 같은 규칙을 공유하면서도 변수 값만으로 서로 다른 결과를 낸다.
변수의 범위는 상속을 따르므로 예측이 명확하다. 어떤 요소가 참조하는 변수의 값은, 자기 자신이나 가장 가까운 조상에서 정의된 값이다. 나는 변수 값이 예상과 다르게 나오면, 어느 조상에서 그 변수를 재정의했는지 위로 거슬러 올라가며 찾는다. 상속의 흐름을 이해하면 변수의 값이 어디서 왔는지 추적하기 어렵지 않다.
범위를 잘 활용하면 전역과 지역의 균형을 맞출 수 있다. 사이트 공통 값은 전역에 두고, 컴포넌트별 특수한 값은 지역에 둔다. 나는 이 두 층을 구분해서, 전역은 최소한의 핵심 값만 담고 나머지는 각 컴포넌트가 지역에서 관리하게 한다. 모든 걸 전역에 몰아넣으면 관리가 어려워지므로, 값의 성격에 맞는 범위를 골라 배치하는 것이 요령이다.
3. 실행 중에 값을 바꾸기
CSS 변수가 이전의 정적인 값 관리 방식과 결정적으로 다른 점은, 실행 중에 값을 바꿀 수 있다는 것이다. 스크립트로 변수의 값을 새로 지정하면, 그 변수를 참조하던 모든 곳이 즉시 새 값으로 다시 그려진다. 나는 이 동적인 성질을 알고 나서야 CSS 변수의 진정한 힘을 이해했다. 변수는 단순한 값 저장소가 아니라 실행 중에 조작 가능한 설정이었다.
이 성질이 가장 빛나는 곳이 테마 전환이다. 색을 변수로 정의해두고, 사용자가 어두운 테마를 고르면 변수 값들만 어두운 색으로 바꾼다. 그러면 그 변수를 참조하던 모든 요소가 한꺼번에 어두운 화면으로 전환된다. 나는 이 방식으로 밝은 테마와 어두운 테마를 스타일 규칙을 두 벌 쓰지 않고 변수 값 교체만으로 구현한다.
다크 모드를 이렇게 만들면 관리가 놀랍도록 간결해진다. 각 요소마다 밝을 때와 어두울 때의 색을 따로 지정할 필요 없이, 요소는 그저 변수를 참조하고 테마에 따라 변수 값만 달라진다. 나는 앞선 편에서 다룬 사용자 선호 쿼리와 이 변수 교체를 결합해서, 시스템 설정에 맞춰 자동으로 테마가 바뀌게 한다. 두 기능이 만나면 다크 모드가 아주 우아하게 완성된다.
스크립트로 변수를 바꾸는 것은 사용자의 직접 조작에도 활용된다. 사용자가 테마 버튼을 누르거나 강조색을 고르면, 그에 맞춰 변수를 갱신해 화면을 실시간으로 바꾼다. 나는 사용자가 직접 화면의 색조를 고르는 기능을 이 방식으로 만든다. 변수 하나만 바꿔도 사이트 전체가 반응하니, 개인화 기능을 구현하기가 훨씬 수월하다.
실행 중 변경은 상호작용과도 연결된다. 마우스의 위치나 스크롤 정도 같은 값을 변수에 반영하면, 그에 따라 화면이 동적으로 반응하게 만들 수 있다. 나는 마우스를 따라 움직이는 효과나, 스크롤 진행도에 반응하는 표시를 이런 방식으로 구현한다. 변수를 스크립트와 스타일 사이의 다리로 삼으면, 둘의 협업이 매끄러워진다.
이렇게 변수는 정적인 설정과 동적인 조작을 잇는 특별한 위치에 있다. 스타일 안에 살면서도 실행 중에 바뀔 수 있기 때문이다. 나는 화면에서 변할 여지가 있는 값이라면 되도록 변수로 빼둔다. 나중에 그 값을 동적으로 조작하고 싶어질 때, 이미 변수로 되어 있으면 손댈 곳이 정의 한 곳뿐이기 때문이다. 미래의 유연함을 미리 확보하는 셈이다.
4. 계산과 결합해 조절 장치 만들기
변수는 계산 함수와 결합할 때 표현력이 크게 늘어난다. 변수에 담긴 기본값을 바탕으로 그 배수나 일부를 계산해 파생된 값을 만들 수 있다. 나는 기본 간격을 변수로 정해두고, 그 두 배나 절반을 계산해 여러 크기의 간격을 파생시킨다. 기준값 하나만 바꾸면 그에서 파생된 모든 값이 비례해서 함께 조정되는 것이다.
이 조합은 일관된 간격 체계를 만드는 데 특히 유용하다. 하나의 기준 간격에서 여러 크기를 계산으로 파생시키면, 화면 전체의 간격들이 하나의 비율 안에서 조화를 이룬다. 나는 이렇게 만든 간격 체계로 여백을 통일해서, 어디를 봐도 리듬이 맞는 화면을 만든다. 임의의 값을 곳곳에 흩뿌리는 대신, 체계 안에서 값을 고르는 것이다.
변수는 컴포넌트의 조절 장치처럼 쓸 수도 있다. 컴포넌트가 자기 내부에서 참조할 변수들을 정해두면, 그 컴포넌트를 쓰는 쪽에서 변수 값만 지정해 모양을 조절할 수 있다. 나는 버튼 컴포넌트가 색과 크기를 변수로 받게 만들어, 쓰는 곳마다 변수만 바꿔 다양한 버튼을 만든다. 변수가 컴포넌트의 공개된 조절 손잡이 역할을 하는 것이다.
이 방식은 컴포넌트의 내부를 건드리지 않고도 겉모습을 바꿀 수 있게 해준다. 컴포넌트를 쓰는 사람은 내부 규칙을 몰라도, 정해진 변수만 지정하면 원하는 변형을 얻는다. 나는 잘 만든 컴포넌트일수록 조절 가능한 지점을 변수로 명확히 열어둔다. 무엇을 바꿀 수 있고 무엇은 고정인지를 변수로 드러내면, 컴포넌트가 다루기 쉬워진다.
계산과 변수의 결합은 반응형에서도 힘을 발휘한다. 화면 크기에 반응하는 값과 변수를 계산으로 엮으면, 상황에 따라 유연하게 변하는 값을 만들 수 있다. 나는 화면 크기 구간마다 변수 값만 바꾸고, 나머지 계산은 그대로 두는 방식으로 반응형을 간결하게 유지한다. 변수가 변화의 중심점이 되고, 그 주변 값들은 계산으로 자동 조정된다.
다만 계산과 변수를 지나치게 얽으면 값의 출처를 추적하기 어려워진다. 나는 파생 관계를 한두 단계로 얕게 유지하고, 너무 복잡한 계산 사슬은 피한다. 변수의 장점은 명료함인데, 계산이 겹겹이 쌓이면 오히려 어떤 값이 어디서 왔는지 알기 힘들어진다. 유연함과 명료함 사이에서 균형을 잡는 것이 변수를 오래 잘 쓰는 비결이다.
5. 변수를 애니메이션하고 관리하기
일반적인 CSS 변수는 값이 바뀔 때 부드럽게 이어지지 않고 뚝 바뀐다. 브라우저가 변수에 담긴 값의 종류를 알지 못해, 중간값을 계산할 수 없기 때문이다. 나는 변수 값을 애니메이션하려다 뚝뚝 끊기는 결과에 실망한 적이 있다. 변수가 무엇이든 담을 수 있는 유연함의 대가로, 그 값을 부드럽게 보간하지 못하는 것이다.
이 한계를 푸는 최신 기능이 있다. 변수의 값이 어떤 종류인지, 어떤 초기값을 갖는지를 미리 등록해두면, 브라우저가 그 변수를 이해하고 값을 부드럽게 보간할 수 있게 된다. 나는 그라디언트의 각도나 특정 수치를 애니메이션하고 싶을 때 이 등록 기능을 쓴다. 변수에 타입을 알려주면, 그동안 불가능하던 변수 애니메이션이 매끄럽게 작동한다.
이 기능은 예전에는 표현하기 까다롭던 효과들을 열어준다. 회전하는 그라디언트 배경이나, 값에 따라 부드럽게 변하는 진행 표시 같은 것들이다. 나는 변수에 타입을 등록해 이런 동적인 배경 효과를 만든다. 변수의 유연함과 애니메이션의 부드러움을 동시에 얻는 셈이라, 표현의 폭이 한층 넓어진다.
변수를 잘 관리하려면 체계적인 이름 규칙이 필요하다. 변수가 많아질수록 이름이 뒤죽박죽이면 어떤 변수가 무엇인지 알기 어려워진다. 나는 색, 간격, 크기처럼 종류별로 이름의 앞부분을 통일하고, 역할을 뒤에 붙이는 규칙을 세운다. 일관된 이름 규칙은 변수가 수십 개로 늘어나도 필요한 것을 금방 찾을 수 있게 해준다.
변수는 디자인 토큰이라는 더 큰 개념으로 이어진다. 디자인 시스템에서 색, 간격, 글꼴 같은 기본 단위를 토큰이라 부르는데, CSS 변수는 이 토큰을 코드로 구현하는 자연스러운 수단이다. 나는 디자이너와 정한 디자인 값들을 변수로 옮겨, 디자인과 코드가 같은 값을 공유하게 한다. 변수가 디자인과 구현을 잇는 공통 언어가 되는 것이다.
정리하면 CSS 변수는 값에 이름을 붙여 한 곳에서 관리하고, 상속과 범위로 전역과 지역을 넘나들며, 실행 중에 바뀌어 테마와 상호작용을 구현하고, 계산과 결합해 유연한 체계를 만든다. 나는 변수를 스타일의 설정이자 디자인의 공통 언어로 삼는다. 값을 반복하지 않고 이름으로 다루는 이 습관이 유지보수를 근본적으로 바꾼다. 다음 편에서는 요소의 상태와 위치를 겨냥하는 가상 클래스와 가상 요소를 다룬다.