본문으로 건너뛰기

© 2026 Molayo

CSS-T헤드라인2026. 06. 03. 22:04

@function

요약

CSS의 새로운 @function at-rule을 통해 사용자 정의 함수를 정의하는 방법을 설명합니다. 인자를 받고 복잡한 로직을 포함하여 값을 반환할 수 있는 재사용 가능한 CSS 블록의 구문과 구성 요소를 다룹니다.

핵심 포인트

  • @function은 인자를 받고 값을 반환하는 재사용 가능한 CSS 블록입니다.
  • 함수 이름은 반드시 두 개의 대시(--)로 시작해야 합니다.
  • Sass의 @function과는 기능이 다르므로 혼동에 주의해야 합니다.
  • 반환 타입(returns)을 명시하여 브라우저의 로직 검증을 도울 수 있습니다.

@function at-rule은 CSS 사용자 정의 함수 (CSS custom functions)를 정의합니다. 이러한 사용자 정의 함수는 인자 (arguments)를 받을 수 있고, 복잡한 로직을 포함하며, 해당 로직을 바탕으로 값을 반환할 수 있는 재사용 가능한 CSS 블록입니다. 이 기능은 사용자 정의 속성 (custom properties (CSS variables))의 보다 동적인 버전과 본질적으로 유사합니다.

참고: Sass에도 목적은 유사하지만 네이티브 CSS @function과는 기능이 다른 @function at-rule이 존재합니다. Sass가 기술 스택의 일부이거나 자료를 검색할 때 이 둘을 혼동하기 쉬우므로 주의하십시오.

구문 (Syntax)

@function at-rule은 다음 구문을 사용하여 사용자 정의 함수를 정의합니다:

@function --function-name(<function-parameter>#?) [returns <css-type>]? {
  <declaration-rule-list>
}
...

다시 말해, 함수의 이름을 대시 식별자 (dashed ident, --my-function)로 정의하고, 일치시키고자 하는 조건(<function-parameter>)을 제공하며, 반환하고자 하는 값의 종류(예: CSS [<length>] 값)를 명시합니다. 그리고 해당 조건이 일치하면 스타일(<declaration-rule-list>)을 적용합니다.

이 요소들이 실제로 무엇을 의미하는지 더 자세히 살펴보겠습니다.

인자 (Arguments) 및 기술자 (Descriptors)

@function 구문에는 기능의 다양한 부분을 처리하기 위한 여러 구성 요소가 있습니다. 매우 복잡해 보일 수 있지만(실제로 그렇습니다), 나중에 예제를 살펴보면 더 명확해질 것입니다.

--function-token

사용자가 정의하는 식별자로, 사용자 정의 속성의 dashed-ident와 유사하게 반드시 두 개의 대시(--)로 시작해야 합니다. 사용자 정의 속성과 마찬가지로 이름은 대소문자를 구분합니다. 예를 들어, --conversion--Conversion은 서로 다른 사용자 정의 함수 정의를 가리킵니다.

@function --progression()

<function-parameter> (선택 사항)

다음 항목을 포함할 수 있는 선택적인 쉼표 구분 입력 목록입니다:

  • --param-name: 인자(argument)의 이름 (반드시 --로 시작해야 함).
  • <css-type> (선택 사항): 일치하는 조건에 도달했을 때 함수가 어떤 종류의 입력 또는 결과를 반환하는지 알려주는 키워드 또는 타입 (예: <length>, <color>).
  • <default-value> (선택 사항): 함수 호출 시 인자가 생략되는 등 결과가 유효하지 않을 경우 반환되는 대체 값(fallback value). 기본값을 제공하는 경우, 앞서 언급한 <css-type>에 유효해야 합니다 (예: <length>는 유효한 CSS 길이로 기본값이 설정되어야 함). 이는 콜론(:)을 통해 나머지 매개변수 정의와 구분됩니다.
  • returns <css-type> (선택 사항): 함수의 예상 출력 타입(output type)을 정의합니다. 이는 브라우저가 렌더링 전에 로직을 검증하는 데 도움을 줍니다. 타입을 지정하지 않으면 모든 값이 유효하게 처리됩니다 (예: returns type(*)와 같이 작성하는 것과 같음).
  • <declaration-rule-list>: 함수의 본문(body)과 로직을 구성하는 CSS 선언(declarations) 및 At-rules입니다. 루트(root) 수준 또는 At-rule 내부에 중첩된 형태로 커스텀 속성(custom properties)과 result 디스크립터(descriptor)를 포함할 수 있습니다.
@function --progression(--current <number>, --total <number>) returns <percentage> {
  result:
}

커스텀 함수가 무엇을 반환할지 정의하는 result 디스크립터입니다. 만약 커스텀 함수가 result 디스크립터를 생략하면, 고장 난 커스텀 속성과 마찬가지로 항상 guaranteed-invalid을 반환합니다.

@function --progression(--current <number>, --total <number>) returns <percentage> {}

기본 사용법 (Basic Usage)

만들 수 있는 가장 기본적인 함수의 예로, 제공된 값(예: 20px)을 절반(예: 10px)으로 계산하여 길이 단위(예: px)로 반환하는 함수가 있습니다:

@function --half(--size <length>) {
  result: calc(var(--size) / 2);
}

여기서 우리는 function-token--half로 설정함으로써 함수에 '이름'을 붙이고 있습니다. 그다음 --size라는 function-parameter (함수 매개변수)를 생성하고, css-type (CSS 타입)을 <length>로 설정하여 길이 값만 허용하도록 합니다. 결과 기술자(result descriptor)는 calc(--size / 2)로 설정되었으며, 이는 CSS 계산 함수 (CSS Calculating Function)를 사용하여 size function-parameter로부터 가져온 값을 절반으로 줄입니다.

그다음 다음과 같이 함수를 사용합니다:

.container {
  margin-inline: --half(20px); /* 이는 10px로 계산됩니다 */
}

타입 체크 (Type Checking)

JavaScript나 다른 언어를 작성할 때와 마찬가지로, 때로는 함수가 특정 인자(argument)만 허용하도록 보장하고 싶을 때가 있습니다. 예를 들어, 숫자만 입력될 수 있고 결과값으로는 퍼센트(percentage)만 출력되도록 보장하고 싶다면 어떻게 해야 할까요?

@function --progression(--current <number>, --total <number>) returns <percentage> {
  result: calc(var(--current) / var(--total) * 100%);
}
...

<css-type>@property를 통해 사용자 정의 속성(custom property)의 타입을 체크하는 방식과 동일하게 꺾쇠괄호 안에 작성됩니다. 만약 인자가 선언된 타입(예: <color>)과 일치하지 않으면 함수 호출은 무효화되며, 이는 대규모 코드베이스에서 버그를 조기에 발견하는 데 매우 유용합니다.

<syntax-combinator> (구문 결합자)를 사용하여 타입을 type()로 감싸고 |를 구분자로 사용함으로써 여러 타입을 허용할 수 있습니다. 예를 들어, 여기서 --alpha<number><percentage>를 모두 허용합니다:

@function --transparent(--color <color>, --alpha type(<number> | <percentage>));

쉼표로 구분된 리스트 (Comma-Separated Lists)

CSS는 사용자 정의 함수의 입력을 구분하기 위해 쉼표를 사용합니다. 여기서 한 가지 의문이 생깁니다. 만약 값의 리스트를 제공하고 싶다면 어떻게 해야 할까요? 여러 개의 개별 입력 대신 하나의 입력으로서 값의 리스트를 제공하려면, 먼저 함수가 리스트를 기대하도록 표시해야 합니다.

이를 수행하려면 <css-type> 뒤에 # 문자를 접미사로 붙입니다. 함수를 호출할 때 값의 리스트를 중괄호 {}로 감싸면, 브라우저는 중괄호 안의 모든 내용을 하나의 인자 (argument)로 취급하게 됩니다.

예를 들어:

/* 리스트의 최댓값과 최솟값 사이의 거리와 또 다른 입력값을 계산합니다 */
@function --get-range(--list <length>#, --n <length>) {
  result: calc(max(var(--list)) - min(var(--list)) + var(--n));
...

생성자 (Constructs)와 CSS 캐스케이드 (CSS Cascade)

result 디스크립터 (descriptor)는 CSS 캐스케이드 (CSS Cascade) 규칙을 따릅니다. 즉, 다른 속성들과 마찬가지로 여러 개의 result 값을 선언할 수 있으며, 마지막으로 일치하는 유효한 값이 적용됩니다. 따라서 조건부 그룹 규칙 (@media, @container, @supports) 및 if()와 같은 다른 함수들은 훨씬 더 많은 추가적인 가능성을 제공합니다.

이 사례에서는 화면이 1000px 미만일 때 기본값으로 16px를 반환하는 --suitable-font-size를 반환합니다. 만약 화면이 1000px보다 크다면, @media 블록 안에 있는 스타일이 "최종 승리"하는 스타일이 됩니다.

@function --suitable-font-size() returns <length> {
  result: 16px;

...

마지막에 정의된 값이 항상 승리한다는 점을 명심하세요. 따라서 아래와 같이 작성한다면, 미디어 쿼리 (media query)의 트리거 여부와 상관없이 결과는 항상 16px가 될 것입니다.

@function --suitable-font-size() returns <length> {
  @media (width > 1000px) {
    result: 20px;
...

확립된 캐스케이드 (cascade)를 준수하기 때문에 함수 내에서 사용자 정의 속성 (custom properties)을 사용할 수도 있습니다. 이러한 사용자 정의 속성들은 지역 범위 (locally scoped)를 가지므로, 해당 사용자 정의 함수와 이를 참조하는 다른 사용자 정의 함수 내에서만 접근할 수 있습니다. 따라서 전역적으로 예기치 않게 유출되어 나머지 CSS와 상호작용하는 일은 발생하지 않습니다.

@function --spacing-scale(--multiplier) {
  --base-unit: 8px;
  result: calc(var(--base-unit) * var(--multiplier));
...

또한 커스텀 함수 내에서 다른 커스텀 함수를 사용할 수 있으며, 이는 본질적으로 하나의 함수를 다른 함수 안에 중첩(nesting)하는 것입니다. 이를 통해 각 섹션이 하나의 작업만 수행하고 함수를 널리 재사용할 수 있는 매우 깔끔한 코드를 작성할 수 있습니다.

@function --square(--n) {
  result: calc(var(--n) * var(--n));
}
...

기본값 (Defaults)

함수는 여러 개의 인자(arguments)를 처리할 수 있으며 기본값(default values)을 제공할 수 있습니다. 기본값은 함수 매개변수(parameter)의 끝에 콜론(:)으로 구분하여 포함함으로써 정의됩니다.

/* 함수 정의 */
@function --brand-glass(--opacity <number>: 0.5) returns <color> {
  result: rgb(10 120 255 / var(--opacity));
...

부수 효과 없음 (No Side Effects)

CSS @function은 오직 값(value)만을 반환할 수 있으며, 그 외의 다른 작업은 수행할 수 없습니다. 예를 들어, 함수 내부에서 속성(property)을 변경하거나 함수를 사용하여 여러 개의 선언(declarations)을 생성할 수는 없습니다. 이러한 기능이 필요하다면, 여러 줄의 CSS 속성과 기타 복잡한 로직을 허용하는 방식으로 기능을 제공할 예정인 제안된 @mixin at-rule을 살펴보아야 합니다.

순환 의존성 (Circular Dependencies)

CSS는 순환 로직(circular logic)에 대해 매우 엄격합니다. 만약 함수 A가 함수 B를 호출하고, 함수 B가 다시 함수 A를 호출하면, 브라우저는 이러한 순환 의존성(cyclic dependency)을 감지하여 즉시 두 함수 모두를 무효(invalid)로 표시합니다.

이는 CSS 사용자 정의 속성(Custom Properties) 및 커스텀 함수 자체를 참조하는 경우에도 적용됩니다. 만약 함수가 특정 사용자 정의 속성이나 함수에 의존하는데, 그 속성이나 함수가 다시 동일한 함수에 의해 계산된다면, 브라우저는 무한 재귀(infinite recursion)를 방지하기 위해 계산을 중단합니다.

명세 (Specification)

@function at-rule은 CSS Custom Functions and Mixins Module Level 1 명세에 정의되어 있습니다.

브라우저 지원 (Browser Support)

지원되지 않는 브라우저는 @function을 무시하므로, 폴백 선언(fallback declarations) 및 점진적 향상(progressive enhancement) 전략을 사용하는 것이 유리할 수 있습니다. 다음과 같이 @supports를 사용하여 사용자의 브라우저에서 @function이 지원되는지 확인할 수 있습니다:

@supports (at-rule(@function)) {
  /* ... */
}

하지만 아이러니하게도, 이 글을 쓰는 시점에는 @supports at-rule 평가 기능이 브라우저 전반에 걸쳐 완전히 지원되지 않습니다 (Chrome 148만 지원)이므로, 사용자의 경우에 지원되는지 확인이 필요할 것입니다. 이에 대한 논의는 CSS Drafts Issue #2463에서 확인할 수 있습니다.

추가 정보


AI 자동 생성 콘텐츠

본 콘텐츠는 CSS-Tricks의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0