프로퍼티 플래그와 접근자 프로퍼티

345·2023년 6월 29일

모던 JavaScript

목록 보기
18/23

프로퍼티 플래그

객체의 프로퍼티는 플래그 (flag) 라는 속성 세 가지를 갖습니다.
각 플래그는 true 아니면 false 의 값을 갖고, 이에 따라 프로퍼티의 속성이 정해집니다.

  1. writable: 값을 수정할 수 있는지
  2. enumerable: 반복문을 사용하여 열거할 수 있는지 (false 면 반복문에 포함 ❌)
  3. configurable: 프로퍼티 삭제나 플래그 수정이 가능한지

일반적인 방법으로 생성한 프로퍼티의 플래그는 모두 true 의 값을 가집니다.

플래그 수정하기

Object.defineProperty(obj, propertyName, descriptor)

defineProperty 를 사용하여 프로퍼티 플래그, 값 등을 설정할 수 있습니다.

let user = {
  name: "John"
};

Object.defineProperty(user, "name", {
  writable: false
});

user.name = "Pete"; // Error: Cannot assign to read only property 'name'


defineProperties 를 사용하면 여러 개의 프로퍼티를 한 번에 설정 가능합니다.

Object.defineProperties(obj, {
  prop1: descriptor1,
  prop2: descriptor2
  // ...
});

🔔 접근자 프로퍼티

객체 프로퍼티는 두 종류로 나뉩니다.

  1. 데이터 프로퍼티: 일반적으로, 값을 담은 프로퍼티
  2. 접근자 프로퍼티: 본질은 함수로, getter, setter 역할을 하나 외부에서는 일반적인 프로퍼티처럼 사용

접근자 프로퍼티를 사용하여 객체의 프로퍼티에 접근하는 방법을 알아봅시다.


getter 와 setter

접근자 프로퍼티는 객체 리터럴 안에 getset 을 붙여 나타냅니다.
일반 메서드처럼 () 을 붙여 실행하지 않고, 이름만 가져다 사용해도 내부의 코드를 실행합니다.

let obj = {
  get propName() {
    // getter, obj.propName을 실행할 때 실행되는 코드
  },

  set propName(value) {
    // setter, obj.propName = value를 실행할 때 실행되는 코드
  }
};

getter 는 프로퍼티를 반환하고, setter 는 인수를 넘겨받아 프로퍼티에 값을 설정합니다.
인수는 obj.propName = value 처럼 값을 할당하는 모양새로 넘겨줍니다.


Maximum call stack size exceeded 에러 ❓

student 라는 객체에 이름을 설정하고 가져오는 getter, setter 를 만들었습니다.
둘은 호출한 객체의 name 프로퍼티를 반환하거나 인수를 받아 값으로 설정합니다.

let student = {
	get name(){
		return this.name;
    },

	set name(value){
		this.name = value;
    }
}

student.name = "JJ";

그런데, 위 코드를 실행하면 Maximum call stack size exceede 라는 에러가 발생합니다.


이 에러는 콜 스택의 최대 사이즈를 초과하여 발생하는데요, 콜 스택은 실행중인 함수의 정보가 위치하는 공간입니다.
이런 콜 스택이 꽉 찼다는 건 실행중인 함수가 너무 많다는 뜻이겠죠??
여기 존재하는 함수라고는 getter, setter 두 개밖에 없는데...

그러나 하나의 함수는 여러 번 재차 호출될 수 있고, 호출마다 새로운 실행 컨텍스트를 생성하여 콜 스택의 공간을 차지합니다.
따라서, gettersetter 가 계속해서 재귀호출 되고있다 볼 수 있습니다❗


❓ 재귀호출하는 코드가 없는데??

물론 코드 내부에서 재귀를 수행하는 명시적인 부분은 없지만, gettersetter 의 등장 자체가 재귀를 야기합니다.

우리가 접근자 프로퍼티를 사용하면, student.nameget name() 을 실행하고 student.name = "JJ"set name(value) 를 실행합니다.
근데, 이와 같은 동작이 접근자 프로퍼티 내의 코드인 this.name, this.name = value 에서도 동일하게 일어납니다.

즉, gettersetter 내부에서 다시 gettersetter 를 호출하는 재귀가 발생하는 겁니다.
그렇게 부른 함수는 또 자기 자신을 호출하는 무한 재귀 현상에 빠집니다.
콜 스택 사이즈 초과 에러가 나는 이유는 그 때문입니다.


🦕 오류는 어떻게 해결할까?

근본적으로 이 오류는 접근자 프로퍼티 내의 this.name 의 이 name 이 일반적인 데이터 프로퍼티가 아니라 접근자 프로퍼티로 취급된다는 점에서 발생합니다.

따라서, 접근자 프로퍼티로 취급되지 않고 데이터 프로퍼티로 취급되도록 고쳐주면 됩니다.

let student = {
	get name(){
		return this._name;
    },

	set name(value){
		this._name = value;
    }
}

위처럼... name 이 아니라 _name 을 사용합니다.
이러면 접근자 프로퍼티 내부에서는 재귀가 일어나지 않고 _name 이라는 데이터 프로퍼티에 접근하는 방식으로 gettersetter 의 동작을 수행합니다.

_name 에 값을 저장하고, 접근은 gettersetter 를 이용하는 것이죠.
이러면 student 에는 _name 이라는 프로퍼티가 생기고, student._name 으로 직접 접근도 가능합니다.
그러나 _ 등이 붙은 프로퍼티는 내부에서만 활용하고 외부에서는 접근하지 않는 게 관습입니다.

profile
기록용 블로그 + 오류가 있을 수 있습니다🔥

0개의 댓글