CSS 변수와 계산식
지난 장에서 우리는 CSS 트랜지션과 애니메이션을 통해 웹 페이지에 생동감을 불어넣는 방법을 배웠습니다.
이제 CSS 코드를 더 효율적이고 유지보수하기 쉽게 만드는 고급 기법, CSS 변수(Custom Properties)와 계산식(calc() 함수)를 다룹니다.
이 두 기능은 CSS의 유연성을 극대화하여 대규모 프로젝트나 재사용 가능한 컴포넌트 개발에서 중요한 역할을 합니다.
기존 CSS는 변수나 동적 계산 기능이 부족해, 특정 값을 여러 곳에서 써야 할 때마다 일일이 수정해야 하는 번거로움이 있었습니다. 또한 두 가지 이상의 값을 조합해 새로운 값을 계산하는 것도 어려웠습니다.
CSS 변수와 계산식은 이런 불편함을 해소하고, 더 깔끔하고 강력한 CSS 코드를 작성할 수 있도록 돕습니다.
이 장에서는 CSS 변수를 선언하고 사용하는 방법, 변수의 스코프(범위), 그리고 calc() 함수를 이용한 동적 값 계산 방법을 상세히 학습합니다.
이 지식은 CSS 작성 생산성과 코드 재활용성을 크게 향상시킵니다.
CSS 변수 (Custom Properties) 이해하기
CSS 변수는 말 그대로 CSS 파일 내에서 재사용할 수 있는 사용자 정의 속성입니다. 변수를 한 번 정의해두면, 이 변수 값을 여러 CSS 속성에서 반복적으로 사용할 수 있으며, 필요할 때 변수 값만 변경하면 해당 변수를 사용하는 모든 곳에 자동으로 반영됩니다. 이는 웹사이트의 디자인 일관성을 유지하고, 변경 사항을 적용하는 시간을 크게 단축시켜 줍니다.
변수 선언 및 사용 방법
CSS 변수는 두 개의 하이픈(--)으로 시작하는 사용자 정의 속성 이름으로 선언합니다.
/* 변수 선언 */
:root { /* 문서 전체에 적용되는 전역 변수 */
--primary-color: #3498db; /* 메인 색상 */
--secondary-color: #2ecc71; /* 보조 색상 */
--font-size-base: 16px; /* 기본 폰트 크기 */
--spacing-unit: 10px; /* 간격 단위 */
}
/* 변수 사용 */
body {
font-size: var(--font-size-base);
color: var(--primary-color);
}
.button {
background-color: var(--secondary-color);
padding: var(--spacing-unit) calc(var(--spacing-unit) * 2); /* calc() 함수와 함께 사용 */
}
.header {
border-bottom: 1px solid var(--primary-color);
}--변수이름: 값;: 변수를 선언하는 문법입니다. 변수 이름 앞에는 반드시--를 붙여야 합니다.var(--변수이름): 선언된 변수 값을 가져와 사용합니다.
변수의 스코프 (Scope)
CSS 변수는 일반적인 CSS 속성처럼 캐스케이드(Cascade)되고 상속(Inheritance)됩니다. 이는 변수가 선언된 위치에 따라 적용되는 범위, 즉 스코프가 달라진다는 의미입니다.
-
전역(Global) 스코프:
html또는:root선택자 내에 선언된 변수는 문서 전체에서 접근 가능합니다.:root가 더 일반적입니다.:root { --global-bg: #f0f0f0; } body { background-color: var(--global-bg); /* 어디서든 사용 가능 */ } -
지역(Local) 스코프: 특정 선택자 내에 선언된 변수는 해당 선택자와 그 자식 요소 내에서만 유효합니다.
.card-container { --card-border-color: #ddd; /* .card-container와 그 자식에만 유효 */ border: 1px solid var(--card-border-color); } .card-container .card-item { border: 1px solid var(--card-border-color); /* 부모로부터 상속받아 사용 */ } .another-element { /* background-color: var(--card-border-color); /* 이곳에서는 사용할 수 없음 */ }스코프 우선순위: 더 구체적인(나중에 선언된 또는 더 깊은 스코프의) 변수가 상위 스코프의 변수보다 우선합니다. 이를 활용하여 특정 컴포넌트 내에서 전역 변수를 오버라이드(재정의)할 수 있습니다.
:root { --text-color: #333; /* 전역 텍스트 색상 */ } .dark-theme { --text-color: #eee; /* .dark-theme 내부에서는 텍스트 색상이 변경됨 */ background-color: #333; } p { color: var(--text-color); /* 상황에 따라 다른 색상 적용 */ }
아래 다이어그램은 CSS 변수가 값을 찾는 우선순위와 폴백 흐름을 정리한 것입니다.
변수의 장점
- 유지보수 용이: 디자인 변경 시 변수 값 하나만 수정하면 전체 웹사이트에 일괄 적용됩니다.
- 일관성 유지: 동일한 값을 여러 번 입력하는 실수를 줄여 디자인의 일관성을 높입니다.
- 가독성 향상:
--primary-color와 같이 의미 있는 이름으로 변수를 선언하여 코드 이해도를 높입니다. - 테마 변경 용이: 다크 모드, 라이트 모드 등 테마 기능을 구현할 때 HTML 요소에 클래스만 토글하여 쉽게 테마를 변경할 수 있습니다.
- JavaScript와의 상호작용: JavaScript로 CSS 변수 값을 읽고 쓸 수 있어 동적인 스타일 변경에 유용합니다. (다음 장에서 다룰 예정)
CSS 계산식 (calc()) 이해하기
CSS 변수와 계산식에서는 스타일 규칙, 레이아웃 계산, 화면 반영 결과를 정리합니다.
calc() 함수는 CSS 속성 값 내에서 사칙연산(+, -, *, /)을 수행하여 동적인 값을 계산할 수 있게 해줍니다. 이는 특히 다양한 단위(px, %, em, rem, vw, vh)가 혼합될 때 유용하며, 유연하고 반응형인 레이아웃을 만드는 데 필수적입니다.
calc() 함수 기본 문법
calc() 함수 안에 계산식을 작성합니다.
.element {
width: calc(100% - 40px); /* 부모 너비에서 40px을 뺀 너비 */
height: calc(50vh + 2em); /* 뷰포트 높이의 절반에 2em을 더한 높이 */
font-size: calc(16px + 0.5vw); /* 뷰포트 너비에 따라 폰트 크기 조절 */
margin-left: calc(var(--spacing-unit) / 2); /* 변수와 함께 사용 */
}- 연산자 주의사항:
- 덧셈(
+)과 뺄셈(-) 연산자 주위에는 반드시 공백이 있어야 합니다. (100% - 20px은 가능하지만,100%-20px은 오류) - 곱셈(
*)과 나눗셈(/) 연산자 주위에는 공백이 필수는 아니지만, 가독성을 위해 권장됩니다. - 곱셈과 나눗셈은 단위가 통일되지 않아도 가능합니다. (예:
100px * 2또는100px / 2) - 나눗셈은 두 번째 피연산자가
0이 될 수 없습니다. (오류 발생)
- 덧셈(
calc() 활용 예시
다양한 상황에서 calc() 함수를 활용해 봅시다.
.main-content {
width: calc(100% - 40px); /* 좌우 20px씩 총 40px의 패딩을 제외한 너비 */
margin: 20px auto;
padding: 20px;
}.grid-item {
/* 3개의 아이템을 나란히 배치하면서 20px의 간격을 고려한 너비 */
width: calc((100% - (20px * 2)) / 3); /* (전체 너비 - 총 간격) / 아이템 수 */
/* 또는 Grid의 gap 속성 사용 시: grid-template-columns: repeat(3, 1fr); gap: 20px; */
}h1 {
font-size: calc(2em + 2vw); /* 최소 2em에서 뷰포트 너비에 따라 커짐 */
min-font-size: 2em; /* calc() 자체는 min/max를 제공하지 않으므로 추가 속성 필요 */
/* 또는 clamp() 함수 (최신 CSS) 사용: clamp(최소값, 권장값, 최대값) */
/* font-size: clamp(2em, 2em + 2vw, 4em); */
}:root {
--base-padding: 15px;
}
.card {
padding: var(--base-padding);
margin-bottom: calc(var(--base-padding) * 2); /* 기본 패딩의 2배 마진 */
}calc()의 장점
- 유연한 레이아웃: 고정된 값과 유동적인 값을 혼합하여 더 복잡하고 유연한 레이아웃을 쉽게 구현할 수 있습니다.
- 단위 혼합: 서로 다른 CSS 단위를 함께 사용하여 계산할 수 있습니다.
- 반응형 디자인: 미디어 쿼리 없이도 어느 정도 반응형 동작을 구현할 수 있습니다.
- 유지보수성: 동적인 계산이 필요한 부분을 코드 내에 명확히 표현하여 가독성을 높이고, 값 변경 시 계산이 자동으로 업데이트됩니다.
아래 다이어그램은 CSS 변수와 calc()를 함께 써서 컴포넌트 값을 설계하는 흐름을 정리한 것입니다.
실습: 웹 컴포넌트 만들기
CSS 변수와 calc() 함수를 활용하여 재사용 가능한 UI 컴포넌트를 만들어 봅시다. 이번 실습에서는 공통된 스타일과 유연한 너비를 가진 카드 컴포넌트를 구현합니다.
variables_calc.html 파일 작성<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS 변수와 calc() 실습</title>
<link rel="stylesheet" href="css/component.css">
</head>
<body>
<header>
<h1>CSS 변수 & calc()</h1>
<p>더 스마트한 CSS를 위한 도구</p>
</header>
<main>
<section class="section-container">
<h2>제품 카드 목록</h2>
<div class="product-grid">
<div class="product-card">
<h3>스마트폰</h3>
<p>최신 기술이 집약된 고성능 스마트폰입니다.</p>
<span class="price">990,000원</span>
<button class="buy-button">구매하기</button>
</div>
<div class="product-card">
<h3>무선 이어폰</h3>
<p>선명한 음질과 편안한 착용감을 제공합니다.</p>
<span class="price">159,000원</span>
<button class="buy-button">구매하기</button>
</div>
<div class="product-card">
<h3>스마트 워치</h3>
<p>건강 관리에 필수적인 스마트 워치.</p>
<span class="price">299,000원</span>
<button class="buy-button">구매하기</button>
</div>
<div class="product-card">
<h3>노트북</h3>
<p>강력한 성능과 휴대성을 겸비한 노트북.</p>
<span class="price">1,500,000원</span>
<button class="buy-button">구매하기</button>
</div>
</div>
</section>
<section class="section-container dark-theme">
<h2>다크 테마 예시</h2>
<div class="theme-box">
<p>이 박스는 다크 테마가 적용되어 있습니다. 변수의 스코프를 확인해보세요.</p>
<button class="theme-button">테마 전환 (JS로 구현 가능)</button>
</div>
</section>
</main>
<footer>
<p>© 2025 변수와 계산식 실습. 모든 권리 보유.</p>
</footer>
</body>
</html>css/component.css 파일 작성/* 뷰포트 메타 태그가 HTML에 설정되어 있는지 확인! */
/* 기본 설정 */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f0f2f5;
color: var(--text-color, #333); /* 변수 사용, 폴백(fallback) 값 지정 */
line-height: 1.6;
padding-bottom: 50px;
}
/* CSS 변수 선언 (전역 스코프) */
:root {
--primary-color: #3498db; /* 파란색 */
--secondary-color: #2ecc71; /* 초록색 */
--accent-color: #e67e22; /* 주황색 */
--background-light: #f0f2f5; /* 밝은 배경색 */
--background-card: white; /* 카드 배경색 */
--text-color: #333; /* 기본 텍스트 색상 */
--border-color: #ddd; /* 테두리 색상 */
--box-shadow-light: 0 2px 10px rgba(0,0,0,0.1); /* 밝은 그림자 */
--base-padding: 15px; /* 기본 패딩 단위 */
--card-gap: 20px; /* 카드 간 간격 */
--button-padding-vertical: 10px; /* 버튼 세로 패딩 */
--button-padding-horizontal: 20px; /* 버튼 가로 패딩 */
}
/* 다크 테마 변수 오버라이드 (지역 스코프) */
.dark-theme {
--background-light: #2c3e50;
--background-card: #34495e;
--text-color: #ecf0f1;
--border-color: #4a6581;
--box-shadow-light: 0 4px 12px rgba(0,0,0,0.3);
}
/* 헤더 스타일 */
header {
background-color: var(--primary-color);
color: white;
text-align: center;
padding: calc(var(--base-padding) * 2) var(--base-padding);
box-shadow: var(--box-shadow-light);
margin-bottom: 30px;
}
header h1 {
font-size: 2.8em;
margin-bottom: calc(var(--base-padding) / 2);
}
header p {
font-size: 1.2em;
}
/* 섹션 공통 스타일 */
.section-container {
background-color: var(--background-light);
padding: calc(var(--base-padding) * 2);
margin-bottom: 30px;
border-radius: 8px;
box-shadow: var(--box-shadow-light);
max-width: 1200px;
margin: 0 auto 30px auto;
color: var(--text-color);
}
.section-container h2 {
color: var(--primary-color);
text-align: center;
margin-bottom: calc(var(--base-padding) * 2);
font-size: 2em;
}
/* 제품 카드 그리드 */
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); /* 반응형 그리드 */
gap: var(--card-gap);
justify-content: center;
}
.product-card {
background-color: var(--background-card);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: var(--base-padding);
text-align: center;
box-shadow: var(--box-shadow-light);
display: flex; /* 내부 콘텐츠를 위한 Flexbox */
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.product-card img {
max-width: 100%;
height: auto;
border-radius: 5px;
margin-bottom: var(--base-padding);
}
.product-card h3 {
font-size: 1.4em;
color: var(--primary-color);
margin-bottom: calc(var(--base-padding) / 2);
}
.product-card p {
font-size: 0.95em;
color: var(--text-color);
margin-bottom: var(--base-padding);
flex-grow: 1; /* 단락이 남은 공간 차지 */
}
.product-card .price {
font-size: 1.3em;
font-weight: bold;
color: var(--accent-color);
margin-bottom: var(--base-padding);
}
.buy-button {
background-color: var(--secondary-color);
color: white;
padding: var(--button-padding-vertical) var(--button-padding-horizontal);
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
transition: background-color 0.3s ease;
}
.buy-button:hover {
background-color: #27ae60;
}
/* 다크 테마 박스 */
.theme-box {
background-color: var(--background-card);
border: 1px solid var(--border-color);
padding: calc(var(--base-padding) * 2);
border-radius: 8px;
text-align: center;
box-shadow: var(--box-shadow-light);
margin: 0 auto;
width: 80%;
max-width: 600px;
}
.theme-box p {
margin-bottom: var(--base-padding);
color: var(--text-color);
}
.theme-button {
background-color: var(--primary-color);
color: white;
padding: var(--button-padding-vertical) var(--button-padding-horizontal);
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
transition: background-color 0.3s ease;
}
.theme-button:hover {
background-color: #2980b9;
}
/* 푸터 스타일 */
footer {
background-color: var(--primary-color);
color: white;
text-align: center;
padding: calc(var(--base-padding) + 5px) 0;
margin-top: 50px;
font-size: 0.9em;
}- Live Server를 통해
variables_calc.html파일을 열어보세요. --primary-color와 같은 변수들이 웹 페이지 전반에 걸쳐 어떻게 사용되었는지 확인하세요.header의padding이나product-grid의gap에서calc()함수가 어떻게 사용되었는지 CSS 코드를 확인해 보세요.- 브라우저의 개발자 도구(F12)를 열어 Elements 탭에서
.product-card나.theme-box같은 요소들을 선택하고 Styles 탭을 확인해 보세요. Computed 탭에서calc()로 계산된 최종 값이 어떻게 적용되었는지 볼 수 있습니다. - 특히
.dark-theme클래스가 적용된 섹션의 배경색, 텍스트 색상, 테두리 색상 등이 전역 변수와 다르게 적용되는 것을 확인하세요. 이는 지역 스코프 변수가 전역 변수를 오버라이드한 결과입니다. CSS 파일에서:root의--primary-color값이나--base-padding값을 변경해 보면, 전체 페이지에 어떻게 즉시 반영되는지 확인할 수 있습니다.
이번 장에서는 CSS 코드의 효율성과 유지보수성을 극대화하는 두 가지 고급 기법인 CSS 변수(Custom Properties)와 계산식(calc() 함수)에 대해 학습했습니다.
- CSS 변수는 재사용 가능한 값을
--변수이름: 값;형태로 선언하고var(--변수이름)으로 사용하여, 디자인 일관성을 유지하고 대규모 프로젝트의 스타일 변경을 용이하게 합니다. 또한, 변수의 스코프를 이해하여 전역/지역적으로 값을 제어할 수 있습니다. calc()함수는 CSS 속성 값 내에서 사칙연산을 수행하여width: calc(100% - 40px);와 같이 동적이고 유연한 값을 계산할 수 있게 해줍니다. 이는 서로 다른 단위를 혼합하여 사용하거나, 복잡한 레이아웃에서 여백 등을 자동으로 계산하는 데 매우 유용합니다.
이 두 가지 기능을 활용하면 여러분의 CSS 코드는 훨씬 더 강력하고 유연하며, 관리하기 쉬워질 것입니다. 이는 복잡한 웹 애플리케이션이나 디자인 시스템을 구축할 때 필수적인 역량입니다.
아래 다이어그램은 CSS 변수를 이해할 때 필요한 레이아웃 조건, 스타일 선언, 화면 확인 순서로 정리합니다.
CSS 변수와 계산식에서 남길 기준과 다음 확인 순서를 정리했습니다.
다음 학습으로 넘어가기 전, CSS 변수와 계산식에서 남은 개념 경계와 실습 확인 포인트를 점검합니다.