Properties and values API/Registering custom properties in CSS

김동현·2026년 3월 21일

mdn 학습 번역 - CSS

목록 보기
140/190

안녕하세요! 와, 벌써 CSS의 가장 모던하고 강력한 기능 중 하나인 CSS Houdini(후디니)의 @property 영역까지 오셨네요! 프론트엔드 개발을 깊이 있게 파고드시는 모습이 정말 훌륭합니다. 이 문서를 완벽하게 이해하시면, 남들이 CSS로는 절대 불가능하다고 생각했던 복잡하고 화려한 애니메이션을 구현할 수 있는 무기를 얻게 되실 거예요.

오늘도 원본 문서의 내용 하나도 빠짐없이, 제 실무 팁을 팍팍 섞어서 친근한 구어체로 완벽하게 번역해 드리겠습니다!


CSS에서 사용자 지정 속성(Custom properties) 등록하기 (Registering custom properties in CSS)

@property at-규칙은 CSS Houdini API 세트의 일부로, 개발자가 CSS 사용자 지정 속성(CSS custom properties, CSS 변수)을 명시적으로 정의할 수 있게 해줍니다. 이 가이드에서는 @property를 사용하여 타입 검사(type checking)와 제약을 설정하는 방법, CSS에서 사용자 지정 속성의 기본값을 설정하는 방법, 그리고 사용자 지정 속성이 값을 상속할지 말지를 정의하는 방법에 대해 알아보겠습니다.

💡 강사의 실무 팁 1: CSS Houdini가 뭔가요?
기존에는 브라우저가 CSS를 해석해서 화면에 그리는 과정(렌더링 파이프라인)에 개발자가 개입할 수 없었어요. 브라우저가 주는 기능만 써야 했죠. 하지만 Houdini(후디니) API가 등장하면서 개발자가 브라우저의 CSS 엔진(CSSOM)에 직접 접근할 수 있게 되었습니다. @property는 이 마법 같은 Houdini의 기능 중 현재 가장 널리 쓰이고 실용적인 기능입니다!


이 문서의 목차 (In this article)


왜 사용자 지정 속성을 등록해야 할까요? (Why register custom properties?)

흔히 CSS 변수라고 불리는 CSS 사용자 지정 속성은 기본 선언 방식을 통해 정의할 수 있습니다. 일반적으로는 :root에 선언하며, 그 뒤에 이어지는 규칙 집합(rulesets)에서 다른 값으로 덮어쓸(override) 수 있습니다. UI 상태에 따른 규칙이나 컨테이너 쿼리, 미디어 쿼리 내에서도 마찬가지로 덮어쓸 수 있죠.

아래 예제를 보면, :root에서 --myColor#bada55라는 값을 할당한 뒤, main에서 다른 값으로 변경했습니다. 이 변경된 값은 main의 모든 자손 요소에 상속됩니다. 또한 main에 마우스를 올렸을 때(hover)나 뷰포트의 너비가 750px 미만일 때 그 값이 다시 한번 덮어씌워집니다. 그리고 circles라는 클래스를 가진 요소 내부에서는 --myColor45deg라는 완전히 다른 데이터 타입의 값으로 설정되고 있습니다.

:root {
  --myColor: #bada55;
}
main {
  --myColor: #cacaca;
}
main:hover {
  --myColor: #aabbbbaa;
}
@media (width < 750px) {
  main {
    --myColor: #aabbbbaa;
  }
}

.circles {
  --myColor: 45deg;
}

CSS 속성 및 값 API (CSS properties and values API)는 사용자 지정 속성을 '등록(registering)'하는 메서드를 제공합니다. CSS 사용자 지정 속성을 등록하면 변수의 타입, 상속 여부, 기본값을 명시적으로 설정할 수 있어, 변수를 훨씬 더 예측 가능하고 성능이 좋게 만들 수 있습니다.

등록된 속성들은 파싱될 때가 아니라 계산(computed)될 때 검증됩니다. 즉, 다음과 같은 의미를 가집니다:

  • 요소의 속성을 검사(inspect)할 때, 잘못된(invalid) 값이 유효하지 않은 것으로 나타나지 않습니다.
  • 유효한 속성 뒤에 유효하지 않은 속성이 포함되어 있다고 해서 유효한 속성으로 대체(fallback)되지 않습니다. 대신, 유효하지 않은 속성은 등록될 때 설정한 기본값(default)으로 돌아가게 됩니다.

기본적으로 모든 CSS 변수는 상속(inherit)됩니다. 등록되지 않은 변수의 값을 변경하면, 브라우저는 DOM 트리의 어떤 요소들이 영향을 받는지 확인하기 위해 DOM을 다시 파싱합니다. 하지만 등록된 속성과 함께 inherits: false; 값을 설정하면, 브라우저에게 "이 값이 변하더라도 자식 요소들을 다시 파싱할 필요가 없어!"라고 알려주게 되므로 스타일 재계산의 범위를 좁혀 성능을 향상시킬 수 있습니다.

일반적으로 변수를 애니메이션 하거나, 타입 검사(type checking)를 구현하거나, 예측 가능한 상속 동작을 보장하고 싶을 때 사용자 지정 속성을 등록하게 됩니다. 만약 CSS 프레임워크를 만들고 있다면, 사용자 지정 속성에 네임스페이스를 지정하고 등록해 두는 것이 좋습니다. 이렇게 하면 항상 기본값을 가지게 되며, 프레임워크 사용자가 값을 변경할 때 잘못된 데이터 타입을 할당하는 것을 방지할 수 있습니다.

CSS에서 속성은 @property at-규칙을 사용하여 등록됩니다. 아래 예제에서 --myColor#bada55 값을 가지며 상속 가능한 색상(color) 타입으로 전역 등록되었고, main 요소에 마우스를 올렸을 때(hover) 사용됩니다. 이렇게 등록을 해두면 브라우저가 값을 애니메이션 하기 전에 파싱할 필요가 없고 타입이 올바르다는 것을 알기 때문에 애니메이션 성능이 향상됩니다. .circles에 할당한 값은 속성의 데이터 타입이 <angle>이 아니라 <color>로 등록되었기 때문에 무시됩니다.

@property --myColor {
  syntax: "<color>";
  inherits: true;
  initial-value: #bada55;
}

main:hover {
  color: var(--myColor);
  animation: colorChange 2s linear forwards;
}

@keyframes colorChange {
  to {
    color: red;
  }
}

.circles {
  --myColor: 45deg;
}

💡 강사의 실무 팁 2
방금 설명이 @property를 사용하는 가장 큰 이유입니다!
기존 CSS 변수(--myColor: red)는 브라우저 입장에서 그냥 단순한 '문자열(String)'입니다. 그래서 이 변수를 그라데이션의 각도나 색상 등에 넣고 transition이나 animation을 주면 브라우저가 이게 색상인지, 숫자인지 몰라서 부드러운 전환(Interpolation)을 해주지 못하고 딱! 딱! 끊기면서 변합니다.
하지만 @propertysyntax: "<color>"라고 딱 정해주면, 브라우저가 "아! 이건 색상이구나!"하고 인식해서 엄청나게 부드러운 애니메이션을 만들어줍니다. (특히 그라데이션 애니메이션 만들 때 필수예요!)


@property와 설명자들 (The @property and descriptors)

@property 규칙은 자바스크립트 없이도 스타일시트 안에서 직접 사용자 지정 속성을 등록할 수 있게 해줍니다. 유효한 @property 규칙은 등록된 사용자 지정 속성을 생성하며, 이는 자바스크립트에서 동일한 매개변수로 registerProperty()를 호출하는 것과 같은 효과를 냅니다.

사용자 지정 속성의 이름은 --로 시작하고 그 뒤에 유효한 사용자 정의 식별자가 오는 <dashed-ident> 형태입니다. 이는 대소문자를 구분합니다. 선언부에는 최대 3개의 설명자(descriptors)가 포함될 수 있습니다: syntax, inherits, 그리고 initial-value 입니다.

이 예제에서는 --rotation이라는 사용자 지정 속성을 생성해 보겠습니다.

@property --rotation {
  syntax: "<angle>";
  inherits: false;
  initial-value: 45deg;
}

@property 설명자들 (@property descriptors)

@property 규칙이 유효하려면 반드시 syntaxinherits 설명자를 포함해야 합니다. initial-value 설명자는 선택 사항입니다.

syntax

syntax 설명자는 등록된 사용자 지정 속성에 허용되는 값의 타입을 설명하는 문자열입니다. 이 설명자의 값은 <color>, <length>, <number> 같은 데이터 타입 이름에 승수(+, #)와 결합자(|)를 붙이거나, 사용자 지정 식별자(custom ident)가 될 수 있습니다. 우리 예제에서 --rotation 속성은 <angle>로 설정되었으므로, <angle> 값만 할당할 수 있다는 뜻입니다.

inherits

inherits 설명자는 사용자 지정 속성이 기본적으로 상속될지 여부를 제어하는 불리언(boolean) 값입니다. 상속되게 하려면 true, 상속되지 않게 하려면 false로 설정하세요. 두 값을 사용하는 예제는 아래 사용자 지정 속성 등록 및 사용하기 섹션에서 볼 수 있습니다.

initial-value

@property at-규칙은 initial-value 설명자도 받습니다. 이 값은 해당 속성의 시작 값(기본값)을 정의합니다. 이 설명자는 syntax 설명자의 값이 보편적 구문 정의(즉, syntax: "*")일 때만 생략이 가능합니다. syntax가 그 외의 다른 값을 가진다면 필수적으로 작성해야 하며, 이 값은 반드시 계산적으로 독립적인(computationally independent) 값이어야 합니다. 즉, %em 단위처럼 다른 값에 의존하는 데이터 타입의 값이 올 수 없습니다.

만약 syntaxinherits 설명자 중 하나라도 누락되면, 전체 @property 규칙은 유효하지 않은 것으로 간주되어 무시됩니다. initial-value 설명자가 필요한 상황인데 생략된 경우에도, 전체 @property 규칙이 무효화되고 무시됩니다. 알 수 없는(Unknown) 설명자를 적은 경우 해당 줄은 무시되지만, 전체 @property 규칙 자체가 무효화되지는 않습니다.


사용자 지정 속성 등록 및 사용하기 (Registering and using custom properties)

이 예제에서는 두 개의 사용자 지정 속성인 --item-size--item-color를 정의해 보겠습니다. 그런 다음 이 속성들을 사용하여 아래의 세 가지 아이템에 대한 크기(너비와 높이)와 배경색을 정의할 것입니다.

HTML

MDN Playground에서 실행하기 (Play)

<div class="container">
  <div class="item one">Item one</div>
  <div class="item two">Item two</div>
  <div class="item three">Item three</div>
</div>

여기서는 CSS @property at-규칙을 사용하여 --item-size 사용자 지정 속성을 정의합니다. 초기값을 40%로 설정하고 유효한 값을 오직 <percentage>로만 제한합니다. 즉, 이 속성이 요소의 크기를 지정하는 데 사용될 때, 그 크기는 항상 부모의 크기에 비례하게 됩니다. 또한 이 속성은 상속 가능합니다(inherits: true).

CSS

MDN Playground에서 실행하기 (Play)

@property --item-size {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 40%;
}

JavaScript를 사용하여 속성을 등록할 수도 있습니다. 자바스크립트의 CSS.registerProperty() 메서드는 @property at-규칙과 동일한 역할을 합니다. 여기서는 두 번째 사용자 지정 속성인 --item-color를 정의해 보겠습니다. 초기값은 aqua로 설정하고, <color> 값만 허용하도록 제한하며, 상속되지 않도록(inherits: false) 설정합니다.

JS

MDN Playground에서 실행하기 (Play)

window.CSS.registerProperty({
  name: "--item-color",
  syntax: "<color>",
  inherits: false,
  initialValue: "aqua",
});

자세한 내용은 사용자 지정 속성 등록을 위한 JavaScript API 가이드를 참조하세요.

💡 강사의 부연 설명 3: React나 Next.js 환경에서는 어떨까요?
실무에서 프레임워크를 쓰실 때 window.CSS.registerProperty()를 컴포넌트 마운트 시점(useEffect 등)에 호출할 수도 있습니다. 하지만 요즘은 순수 CSS나 글로벌 CSS 파일(globals.css) 최상단에 @property로 미리 쭉 선언해 놓고 사용하는 방식이 훨씬 유지보수가 편하고 코드가 깔끔해집니다!


등록된 사용자 지정 속성 사용하기 (Using registered custom properties)

이제 등록된 속성에 새로운 값을 할당할 수 있습니다. 여기서는 먼저 부모 컨테이너(container)에 사용자 지정 속성 값을 설정한 후, 개별 아이템들(two, three)에 값을 다시 설정해 보겠습니다. two에 설정된 값은 유효하지만, three에 설정된 값은 유효하지 않게 고의로 작성했습니다.

.container {
  --item-size: 20%;
  --item-color: orange;
}
.two {
  --item-size: initial;
  --item-color: inherit;
}
.three {
  --item-size: 1000px;
  --item-color: xyz;
}

이제 이 두 사용자 지정 속성을 아이템들의 width, height, background-color 값으로 사용해 봅시다:

.item {
  width: var(--item-size);
  height: var(--item-size);
  background-color: var(--item-color);
}

(실행 결과: 첫 번째 아이템은 너비/높이 20%에 배경색 aqua, 두 번째 아이템은 너비/높이 40%에 배경색 orange, 세 번째 아이템은 너비/높이 20%에 배경색 aqua로 표시됩니다.)


상속 (Inheritance)

inherits 설명자는 등록된 CSS 사용자 지정 속성이 기본적으로 상속될지 말지를 제어합니다.

우리 예제에서 사이즈(--item-size) 속성은 상속되도록 설정(true)했지만, 컬러(--item-color)는 상속되지 않게 설정(false)했습니다. 부모인 container에는 --item-size: 20%--item-color: orange;가 설정되었고, 이는 속성을 처음 정의할 때 지정했던 40%aqua라는 기본값을 덮어씁니다.

첫 번째 아이템(item one)을 볼까요? 여기엔 아무런 속성도 직접 설정하지 않았습니다. --item-size는 상속 가능한 속성이기 때문에 부모 container에 설정된 20%를 물려받아 사용합니다. 반면 --item-color는 상속 불가능한 속성이므로 부모에 설정된 orange를 물려받지 못합니다. 대신, 맨 처음에 설정해둔 기본 초기값인 aqua가 사용됩니다.

inherits 설명자는 필수입니다. 만약 누락되거나 값이 잘못되었다면 @property 규칙 전체가 무효화되어 무시되었을 것입니다.


유효한 값과 유효하지 않은 값 (Valid and invalid values)

일반적인 변수 선언 방식 대신 사용자 지정 속성을 '등록'했을 때의 큰 장점은, 변수가 오직 유효한 값으로만 재할당되도록 통제할 수 있다는 것입니다. syntax 설명자가 어떤 값이 유효한지 정의해주기 때문이죠. 이 제약 조건을 충족하지 못하는 값이 들어오면 그 선언은 무시됩니다.

두 번째 아이템(item two)을 살펴보면, 두 속성 모두 CSS 전역 키워드(global keywords)가 설정되어 있습니다. CSS 전역 키워드인 initial, inherit, unset, revert, revert-layer는 모든 값 타입에 대해 유효한 값입니다. 따라서 syntax가 무엇이든 무조건 사용할 수 있습니다.
--item-sizeinitial로 설정되었으므로 @property 선언 시 작성했던 initial-value: 40%;가 사용됩니다. --item-color는 비록 상속 불가능하게 등록되었더라도, 이번엔 명시적으로 inherit 키워드를 사용했기 때문에 부모의 orange 값을 강제로 물려받게 됩니다. 이것이 아이템 2번이 주황색(orange)이 된 이유입니다.

세 번째 아이템(item three)의 경우, --item-size 값을 1000px로 설정했습니다. 1000px은 엄연한 <length> 값이지만, 우리가 @property에서 이 변수는 무조건 <percentage> 타입이어야 한다고 제약을 걸었죠? 따라서 이 선언은 유효하지 않아 무시됩니다. 대신 상속 가능한 속성이므로 부모에 설정된 20%가 적용됩니다.
--item-color에 할당한 xyz라는 값 역시 색상 값이 아니므로 무효입니다. 게다가 registerProperty()에서 --item-color는 상속되지 않게 설정했으므로, 부모의 orange 값도 물려받지 않고 기본 초기값인 aqua가 사용됩니다.


사용자 지정 속성 값 애니메이션 하기 (Animating custom property values)

등록된 CSS 사용자 지정 속성은 성능 면에서 엄청난 우위를 제공합니다. 이들은 단순한 문자열이 아니라 '타입이 지정된 데이터(typed data)'로 취급되기 때문에, 브라우저의 렌더링 엔진에게 명확한 구문과 상속 규칙을 제공합니다. 브라우저는 정의된 구문을 단 한 번만 파싱하면 되고, 이후 변수가 다른 곳에서 쓰일 때는 이미 그 타입과 내부 형태를 알고 있으므로 다시 파싱할 필요가 없습니다.

등록되지 않은 일반 사용자 지정 속성은 단순한 문자열에 불과하므로, 브라우저가 이 값들 사이의 중간값을 계산하는 보간(interpolate)을 수행할 수 없어 부드러운 애니메이션을 만들 수 없습니다. (값이 뚝딱 하고 불연속적으로 바뀌죠).
반면 등록된 변수들은 보간이 가능하므로 애니메이션을 적용할 수 있습니다! 게다가 이 보간 계산은 GPU로 넘겨(offload) 처리되기 때문에, 메인 스레드에서 돌아가는 애니메이션보다 훨씬 성능이 뛰어납니다.

애니메이션 예제는 사용자 지정 속성 값 애니메이션 하기 문서를 참고하세요.


함께 보기 (See also)


MDN 향상에 도움 주기 (Help improve MDN)


이 문서를 통해 @property가 얼마나 유용한지 감이 확 오셨을 거예요. 이제 포트폴리오 만드실 때 화려한 그라데이션 전환 애니메이션이나 커스텀 속성들을 안심하고 사용할 수 있으시겠죠?

학습 중 언제든 막히는 부분이 생기면 주저 말고 질문해 주세요! 계속해서 프론트엔드 개발 공부 응원하겠습니다!

profile
프론트에_가까운_풀스택_개발자

0개의 댓글