CSS에 함수가? 네이티브 @function 시대가 열렸다
CSS 전처리기(Sass, Less) 없이도 CSS만으로 함수를 정의할 수 있는 시대가 왔습니다. 바로 @function at-rule 덕분이죠. 이 기능은 CSS Custom Functions and Mixins Module Level 1 명세에 정의되어 있으며, 우리가 Sass에서 쓰던 @function과 비슷해 보이지만 완전히 다른 네이티브 기능입니다.
핵심 차이점: Sass
@function은 컴파일 타임에 동작하지만, CSS 네이티브@function은 런타임에 브라우저가 직접 평가합니다. 즉, 동적 환경(뷰포트 크기, 컨테이너 쿼리 등)에 반응하는 함수를 만들 수 있습니다.
이 글에서는 CSS @function의 문법부터 실전 활용, 주의사항, 그리고 브라우저 지원 현황까지 모두 다룹니다. 함께 보면 좋은 글로 Azure IaaS 방어 심층(DiD) 아키텍처, SFI 원칙으로 완성하는 클라우드 보안 실전 가이드도 참고하세요.
기본 문법: 함수 이름부터 결과 반환까지
함수 이름은 반드시 --(대시 두 개)로 시작해야 합니다. 이는 CSS 커스텀 프로퍼티와 동일한 네이밍 규칙이며, 대소문자를 구분합니다.
/* 가장 기본적인 함수: 절반 값을 반환 */
@function --half(--size) {
result: calc(var(--size) / 2);
}
.container {
margin-inline: --half(20px); /* 10px로 평가됨 */
}
함수 구성 요소
--function-token:--half처럼 두 개의 대시로 시작하는 식별자(): 인자 목록 (선택 사항, 여러 개 가능)returns: 반환 타입 명시 (선택 사항, 명시하지 않으면 모든 타입 허용)result: 실제 반환값을 정의하는 디스크립터 (필수)
타입 체크: 안전한 함수를 위한 첫걸음
JavaScript처럼 CSS 함수도 인자와 반환값의 타입을 지정할 수 있습니다. @property와 유사한 방식으로 꺾쇠괄호(< >)를 사용합니다.
/* 숫자만 입력받고, 퍼센트를 반환하는 함수 */
@function --progression(--current, --total) returns {
result: calc(var(--current) / var(--total) * 100%);
}
.progress-bar {
width: --progression(3, 5); /* 60% */
}
여러 타입을 허용하려면 type() 함수와 | 구분자를 사용합니다.
@function --transparent(--color, --alpha type(|)) {
result: rgb(from var(--color) r g b / var(--alpha));
}
실무 팁: 타입 체크는 대규모 프로젝트에서 버그를 조기에 발견하는 데 매우 유용합니다. 특히 디자인 시스템의 토큰 값을 계산할 때 강력합니다.
고급 기능: 리스트, 기본값, 중첩, 캐스케이드
쉼표로 구분된 리스트 처리
CSS 함수 호출 시 쉼표는 인자 구분자로 사용됩니다. 리스트 자체를 하나의 인자로 전달하려면 중괄호 {}로 감싸고, 매개변수에 # 접미사를 붙입니다.
@function --get-range(--list#, --n) {
result: calc(max(var(--list)) - min(var(--list)) + var(--n));
}
div {
padding-block: --get-range({10px, 100px, 50px, 25px}, 200px); /* 290px */
}
기본값 설정
함수 인자에 기본값을 지정할 수 있습니다. 콜론(:) 뒤에 기본값을 작성합니다.
@function --brand-glass(--opacity: 0.5) returns {
result: rgb(10 120 255 / var(--opacity));
}
.header {
background: --brand-glass(); /* 0.5 적용 */
}
.header:hover {
background: --brand-glass(0.8); /* 0.8 오버라이드 */
}
함수 중첩 및 지역 변수
함수 안에서 다른 함수를 호출하거나 지역 스코프의 커스텀 프로퍼티를 사용할 수 있습니다. 지역 변수는 함수 밖으로 새지 않아 안전합니다.
@function --square(--n) {
result: calc(var(--n) * var(--n));
}
@function --circle-area(--radius) {
--pi: 3.14159;
result: calc(var(--pi) * --square(var(--radius)));
}
.blob {
width: calc(--circle-area(10) * 1px); /* 314.159px */
}
CSS 캐스케이드와 조건부 로직
result 디스크립터는 CSS 캐스케이드 규칙을 따릅니다. 따라서 @media, @container, @supports 안에 result를 중첩하여 조건부 반환값을 만들 수 있습니다.
@function --suitable-font-size() returns {
result: 16px;
@media (width > 1000px) {
result: 20px;
}
}
body {
font-size: --suitable-font-size(); /* 1000px 초과면 20px, 아니면 16px */
}
주의: 마지막에 선언된
result가 우선합니다. 위 예제에서@media블록 밖에result: 16px를 먼저 쓰고, 미디어 쿼리 안에result: 20px를 쓰면 항상 16px만 반환됩니다.
브라우저 지원 현황 및 폴백 전략
현재(2025년) @function은 **Chrome 148+**에서만 실험적으로 지원됩니다. Firefox, Safari, Edge는 아직 미지원입니다. 따라서 프로덕션에서 사용하려면 폴백이 필수입니다.
/* @supports로 기능 감지 */
@supports (at-rule(@function)) {
.container {
margin-inline: --half(20px);
}
}
/* 폴백: @function이 지원되지 않으면 기본값 사용 */
.container {
margin-inline: 10px; /* 폴백 */
}
아이러니:
@supports자체도 모든 브라우저에서 완전히 지원되지 않습니다. CSS Drafts Issue #2463에서 논의가 진행 중이니, 실제 사용 전에 Can I Use 등을 반드시 확인하세요.
CSS @function의 한계와 주의사항
- 부수 효과(Side Effect) 불가: 함수는 오직 하나의 값만 반환할 수 있습니다. 여러 속성을 한 번에 생성하거나 스타일을 변경하는 것은 불가능하며, 이는 제안된
@mixinat-rule이 해결할 예정입니다. - 순환 참조 금지: 함수 A가 B를, B가 A를 호출하면 브라우저가 감지하고 두 함수를 무효화합니다. 커스텀 프로퍼티를 통한 간접 순환도 마찬가지입니다.
- 현재 브라우저 지원 협소: 프로덕션 사용은 아직 시기상조입니다. 학습 목적으로 미리 익혀두되, 실제 서비스에는 폴백을 반드시 포함하세요.
한국 개발 생태계에서의 적용 맥락
국내에서는 아직 CSS 전처리기(Sass, Less) 의존도가 높은 편입니다. 하지만 네이티브 @function이 브라우저 지원을 확대하면, 다음과 같은 상황에서 강력한 도구가 될 수 있습니다.
- 디자인 시스템 토큰 계산: 반응형 타이포그래피 스케일, 간격 시스템 등을 CSS 함수로 정의하여 빌드 도구 없이 동적 계산 가능
- 런타임 테마 전환: 사용자 선호도(다크모드 등)에 따라 함수 결과가 달라지도록 설계
- CDN 기반 CSS 라이브러리: 빌드 단계 없이 바로 사용할 수 있는 경량 CSS 유틸리티
다음 단계 학습 방향
- 공식 명세서 CSS Custom Functions and Mixins Module Level 1 읽기
@mixinat-rule 제안 따라가기 (함수와 달리 여러 스타일을 생성 가능)- Chrome Canary에서 직접 실험해보고, CSS Tricks의 @function 알마낙 참고 (해당 글의 원문)
이 글이 도움이 되셨다면, 전통적인 소프트웨어 테스트는 끝났다 에이전틱 개발 시대의 JiTTest 혁명 글도 함께 읽어보세요. CSS의 패러다임 변화와 소프트웨어 개발 방법론의 변화는 생각보다 연결되어 있습니다.

실전 예제: 반응형 그리드 시스템을 CSS 함수로 만들기
다음은 @function을 활용한 반응형 그리드 시스템 예제입니다. 컨테이너 너비와 컬럼 수를 인자로 받아 각 컬럼의 너비를 계산합니다.
/* 그리드 컬럼 너비 계산 함수 */
@function --grid-column-width(--container-width, --columns) returns {
result: calc(var(--container-width) / var(--columns));
}
/* 기본값을 가진 갭 계산 함수 */
@function --grid-gap(--base-gap: 16px) returns {
result: var(--base-gap);
}
.grid {
display: grid;
grid-template-columns:
--grid-column-width(1200px, 12)
var(--grid-gap())
--grid-column-width(1200px, 12);
/* 1200px / 12 = 100px, gap은 16px */
/* 결과: 100px 16px 100px */
}
/* 반응형: 컨테이너 쿼리와 조합 */
@container (min-width: 800px) {
.grid {
grid-template-columns: repeat(3, --grid-column-width(100%, 3));
}
}
이 예제는 현재 브라우저에서 동작하지 않지만, 미래의 CSS 아키텍처가 어떻게 변할지 보여줍니다. 지금부터 개념을 익혀두면, 브라우저 지원이 확대되었을 때 바로 활용할 수 있습니다.

CSS @function vs Sass @function 비교표
| 항목 | CSS @function | Sass @function |
|---|---|---|
| 평가 시점 | 런타임 (브라우저) | 컴파일 타임 |
| 동적 환경 반응 | 가능 (미디어 쿼리, 컨테이너 쿼리 등) | 불가능 (정적) |
| 타입 체크 | returns 디스크립터로 명시적 타입 검증 | 없음 (JavaScript 함수처럼 동적) |
| 부수 효과 | 불가능 (값만 반환) | 불가능 (값만 반환) |
| 순환 참조 감지 | 브라우저가 자동 감지 및 무효화 | 컴파일러가 감지 (에러 발생) |
| 브라우저 지원 | Chrome 148+ (실험적) | 모든 환경 (전처리기 사용 시) |
| 생태계 | 명세 초기 단계, 라이브러리 부족 | 성숙함 (Bourbon, Compass 등) |
주의사항 요약
- 프로덕션 사용 금지: 현재는 Chrome 148+에서만 동작합니다. 학습 목적으로만 사용하세요.
- 폴백 필수:
@supports로 감지하거나, 전처리기와 병행 사용을 고려하세요. - 순환 참조 조심: 함수 간 호출 그래프를 항상 체크하세요.
- 이름 충돌:
--로 시작하는 이름은 커스텀 프로퍼티와 동일한 네임스페이스를 공유합니다. 의도치 않은 오버라이드에 주의하세요.

결론: CSS의 미래는 지금부터 준비하자
CSS @function은 단순한 문법 추가 이상입니다. 이는 CSS가 정적 선언 언어에서 동적 계산이 가능한 프로그래밍 언어로 진화하는 신호탄입니다. 비록 지금은 브라우저 지원이 제한적이지만, 1~2년 안에 주요 브라우저에 탑재될 가능성이 높습니다.
지금부터 @function의 개념과 문법을 익혀두면, 미래의 CSS 아키텍처 설계에 큰 도움이 될 것입니다. 특히 디자인 시스템, 반응형 컴포넌트, 테마 관리 등에서 강력한 무기가 될 수 있습니다.
한 줄 요약: CSS
@function은 아직 실험 단계이지만, 'CSS로 함수를 작성한다'는 패러다임 변화를 이해하는 것이 중요합니다. 폴백 전략과 함께 천천히 학습을 시작해보세요.
함께 보면 좋은 글: