JavaScript Property Attribute

gittidev·2025년 5월 13일
0

Nodejs

목록 보기
6/6

JavaScript 객체의 프로퍼티(Property)는 값뿐만 아니라 여러 가지 속성(Attribute)을 가지고 있습니다. 이 속성들을 이해하면 객체를 더 정교하게 다루고, 의도치 않은 변경을 막거나 커스텀 엑세서(getter/setter)를 활용하는 데 큰 도움이 됩니다.


1. 프로퍼티 디스크립터(Property Descriptor)란?

  • 객체의 프로퍼티 하나를 묘사하는 메타데이터 묶음
  • ES5부터 Object.getOwnPropertyDescriptor, Object.defineProperty API로 다룰 수 있음
const obj = { foo: 123 };
console.log(Object.getOwnPropertyDescriptor(obj, 'foo'));
// {
//   value: 123,
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

디스크립터의 기본 속성

  • value: 프로퍼티의 실제 값
  • writable: 값 변경 가능 여부 (true면 재할당 허용)
  • enumerable: 열거(for…in, Object.keys)에 포함될지 여부
  • configurable: 삭제 및 어트리뷰트 변경 가능 여부

2. 데이터 디스크립터(Data Descriptor) vs 액세서 디스크립터(Accessor Descriptor)

프로퍼티 디스크립터는 크게 두 종류로 나뉩니다.

구분데이터 디스크립터액세서 디스크립터
주요 속성value, writableget, set
읽기 방식실제 저장된 값을 반환getter 함수를 실행한 결과 반환
쓰기 방식재할당 (=) 시 value 변경setter 함수를 호출
함께 사용할 수 없는 속성get, setvalue, writable

2.1. 데이터 디스크립터 예제

const user = {};
Object.defineProperty(user, 'name', {
  value: 'Alice',
  writable: false,    // 재할당 금지
  enumerable: true,   // for...in, Object.keys에 나타남
  configurable: false // 삭제 및 어트리뷰트 변경 금지
});

console.log(user.name); // Alice
user.name = 'Bob';
console.log(user.name); // 여전히 Alice
delete user.name;       // 실패 (configurable: false)

2.2. 액세서 디스크립터 예제

const counter = { _count: 0 };

Object.defineProperty(counter, 'count', {
  get() {
    console.log('get 호출!');
    return this._count;
  },
  set(val) {
    console.log('set 호출!', val);
    this._count = val;
  },
  enumerable: true,
  configurable: true,
});

console.log(counter.count); // get 호출! → 0
counter.count = 5;          // set 호출! 5
console.log(counter.count); // get 호출! → 5

3. Object.defineProperty vs Object.defineProperties

  • Object.defineProperty(obj, prop, descriptor)
    → 단일 프로퍼티를 정의하거나 수정

  • Object.defineProperties(obj, descriptors)
    → 여러 프로퍼티를 한 번에 정의

const obj = {};
Object.defineProperties(obj, {
  a: { value: 1, writable: true, enumerable: true, configurable: true },
  b: {
    get() { return this.a * 2; },
    enumerable: true,
    configurable: true
  }
});

4. Reflect API 활용하기

ES6부터 추가된 Reflect.defineProperty, Reflect.getOwnPropertyDescriptor 등은 Object API와 동작은 동일하지만, 실패 시 예외가 아닌 false를 반환합니다.

const o = {};
const success = Reflect.defineProperty(o, 'foo', {
  value: 99,
  writable: false,
  enumerable: false,
  configurable: false
});
console.log(success); // true
console.log(Reflect.getOwnPropertyDescriptor(o, 'foo'));

5. 실제 활용 시나리오

  1. Immutable 객체

    • writable: false, configurable: false로 설정해 변경을 방지
  2. 라이브러리 내부 구현

    • 외부에서 값을 직접 건드리지 못하도록 어트리뷰트 잠금
  3. 가상 프로퍼티(Virtual Property)

    • 액세서 디스크립터로 동적인 계산 결과 제공
  4. Enum-like 상수

    • enumerable: false로 숨기고, configurable: false로 삭제 방지

6. 주의사항

  • defineProperty로 만든 프로퍼티는 기본 어트리뷰트가 모두 false이므로, value만 지정하면 나머지는 모두 비활성화된 상태입니다.
  • 생성자 함수나 클래스 내에서 this.prop = value로 선언한 프로퍼티는 기본적으로 모두 true로 설정됩니다.
  • 성능을 고려할 때, 일반 할당(obj.prop = …)보다 defineProperty 호출이 조금 느릴 수 있으므로, 반복적으로 정의할 경우 주의하세요.

참고 자료

0개의 댓글