javascript Get, Set을 이용한 객체 속성 기술자 정의

속성 기술자 정의

어려운 용어를 먼저 알아둘 필요가 있습니다.

게터(Getter)/세터(Setter) 라고도 하고, 속성 기술자(Property Descriptor)라고도 합니다. 

자바스크립트에서는 속성 기술자라는 용어를 좀 더 많이 사용합니다.

자바스크립트는 객체의 속성을 만들고 관리할 수 있는 표준화된 메서드 틀(구조)을 제공합니다.

또, 속성을 만들고 관리할 수 있는 전용의 단일 메서드인 Object.defineProperty() 를 제공합니다.

최상위 객체인 Object 객체의 스테틱 메서드이며, 어디서나 접근이 가능합니다.

속성 기술자는 모두 이 메서드를 사용해 정의하고, 속성 기술자를 정의한다고 하면 Object.defineProperty() 메서드를 사용한다고 이해하면 됩니다.

이해를 돕기 위해 아주 간단하게 가상으로 만든 속성 기술자 구현체를 보겠습니다.

let friends = {}
Object.defineProperty( friends, 'age', {  get: function(){    return this._age;  },  set: function(age){    this._age = age;  }});

요약하면 

Object.defineProperty( 객체명, 속성명, {get: function(){//게터 구현 내용}, set: function(변수){//세터 구현 내용}};

이런 구조를 기본적으로 가집니다. 

실제 코딩시에 여러개의 속성을 객체에 부여할 경우, 속성 한개를 한줄로 타이트하게 기술해서 여러개의 속성을 한꺼번에 정의하기도 합니다.

get, set 메서드 안에는 내부 변수에 값을 저장하거나 가져오는 코드와 함께 값을 가공하거나, 조건 체크를 하는 내용들이 들어갑니다.

위 속성 기술자를 통해 friends 객체는 age라는 속성이 생기고, age 속성을 읽고, 쓸 수 있게 됩니다.

속성 기술자를 사용하는 이유

자바스크립트에서 객체에 속성을 부여하는 방법은 여러가지가 있습니다.

객체 리터럴을 예를들면

let obj = {name: '라이언', age: 5, play: function(toy){}, getName: function(){console.log(this.name);}};

객체 리터럴은 obj.name 으로 속성값을 즉시 얻을 수 있습니다.

속성을 직접 처리하지 않고 간접적으로 처리하고 싶다면 추가의 객체 리터럴 메서드를 구현해 속성을 간접적으로 관리할 수도 있습니다.

이런 간편한 구현 방법이 있지만 속성을 구현할 때, 특히 클래스 속성을 구현할 때는 속성 기술자의 사용을 권장합니다.

처음 접하면 상당히 번거로워 보이는 이런 속성 구현 방식을 사용하는 가장 큰 이유는 객체 사용의 안정성을 위해서입니다.

객체리터럴로 구현할 경우 객체의 속성을 변경하는 것이 자유롭습니다.

age 라는 속성이 있다면, 이미 알고 있는 속성에 접근을 하지 번거롭게 setAge() 와 같은 별도로 만든 메서드를 사용하지는 않습니다.

속성에 직접 접근을 하게되면 들어가면 안되는 값이 임의로 입력될 수 있는 가능성이 항상 존재하게 됩니다.

예를 들어 나이는 0 ~ 100까지, 이름은 2글자 이상이어야 하는 조건이 있는 속성의 제한이 있는 경우

객체 리터럴로는 이 제한을 넘는 속성값이 입력되는 것을 막기가 어렵습니다.

속성 기술자를 사용하면 속성 값에 임의로 접근하는 것을 우회시키기 때문에, 속성값의 제한 사항들을 지킬 수 있습니다. 

객체 내부의 속성(변수)은 내부에서만 사용하기 때문에 임의의 속성값 적용으로 인한 에러를 최대한 막을 수 있습니다.

속성 기술자는 속성을 메서드로 구현하기 때문에 메서드 안에서 다양한 조건 체크 및 수정을 할 수 있습니다.

입력된 속성값이 조건 밖이면 다양한 에러 메시지를 내보낼 수도 있습니다.

주의할 점
내부 속성 변수를 속성 기술자로 정의해 사용하는 경우, 이 내부 변수를 직접 접근해 속성값을 변경할 수 있습니다.

관리상의 편의를 위해 속성명 앞에 언더바를 붙여 내부 변수명으로 사용하는 경우, "객체명._속성명" 으로 내부 속성 변수에 직접 접근할 수 있습니다.

속성 기술자를 사용하는 것은 알려진 속성명으로 속성값을 읽고, 쓰도록 함으로써 속성을 직접 접근함으로써 발생하는 문제를 줄이고, 에러를 발생시키는 값을  메서드에 의해 체크할 수 있는 방법을 제공하는 것이 목적입니다.

협업을 위해 클래스나 모듈을 제작해 공유하는 경우 알려진 속성명으로 속성에 접근하도록 제어를 함으로써, 모듈이나 클래스를 사용하면서 발생하는 많은 문제를 피할 수 있습니다.

속성 기술자 선언

속성 기술자를 사용해 실제 속성을 구현하고 사용하는 방법을 알아봅니다.

age 속성을 속성 기술자로 friend 객체에 추가합니다.

내부 변수는 _age를 사용합니다.

let friend = {name:'라이언'};
Object.defineProperty( friend, 'age', {
  get: function(){
    return this._age;//_age는 로컬 변수로 객체 외부에서 접근이 안됨.
  },
  set: function(age){
    if(age < 0){
      console.log(age+' >>> 0 보다 작은 나이.')
    }else if(age >= 100){
      console.log(age+' >>> 100 이상 나이.')
    }else{
      this._age = age;
    }
  },
  enumerable: true, // 기본값 false - Object.key()로 속성 나열시 해당 속성 표시 여부
  configurable: true // 기본값 false - delete 연산자로 속성 키 삭제 가능 여부 설정
});
friend.age = 10;
friend.age = -1;
console.log(friend.age);
console.log(friend._age);
console.log(Object.keys(friend));

delete friend.age;
console.log(Object.keys(friend));

속성 기술자 옵션

앞서 속성 기술자 정의 예에서 2가지 옵션 설정을 사용했습니다. 빈번하게 사용하는 옵션 값이므로 알아둘 필요가 있습니다.

옵션

기본값

설명

enumerable

false

Object.key() 메서드로 객체 키 리스트를 표시할 때, 포함할 지를 설정

configurable

false

속성 설정을 수정할 수 있는지를 결정. 속성 키 삭제 가능한지도 함께 결정 됨

기본값, 또는 고정값 설정

속성 기술자 옵션 설정을 이용해 속성의 기본값을 지정하거나, 변경할 수 없는 고정 속성값을 지정할 수 있습니다.

valeu, writable 2개의 옵션으로 기본값, 또는 고정값 설정을 할 수 있습니다. get, set 과는 함께 사용할 수 없으므로 주의해야 합니다.

옵션 

기본값

설명

value

undefined

기본값 지정

writable

true

false 면 read-only 상태가 되며, value 값을 수정할 수 없게됨

수정하려고 시도해도 오류는 발생하지 않음

let friend = {name:'라이언'};
Object.defineProperty(
  friend,
  'gender',
  {
    value: 'male',
    writable: false
  }
)
console.log(friend.gender);
friend.gender='female';
console.log(friend.gender);