ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프로퍼티 getter와 setter
    front-end 2022. 3. 2. 14:45
    728x90

    객체의 프로퍼티는 두 종류로 나뉜다.

     

    첫 번째 종류는 데이터 프로퍼티(data property)이며, 지금까지 사용한 모든 프로퍼티는 데이터 프로퍼티이다.

     

    두 번째 종류는 접근자 프로퍼티(accessor property)라 불리는 새로운 종류의 프로퍼티이다.

    접근자 프로퍼티의 본질은 함수인데, 이 함수의 값을 획득(get)하고 설정(set)하는 역할을 담당한다.

    그런데 외부 코드에서는 함수가 아닌 일반적인 프로퍼티처럼 보인다.

     

     

     

    getter와 setter

    접근자 프로퍼티는 'getter(획득자)'와 'setter(설정자)' 메서드로 표현된다.

    객체 리터랄 안에서 getter와 setter 메서드는 getset으로 나타낼 수 있다.

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

    getter 메서드는 obj.propName을 사용해 프로퍼티를 읽으려고 할 때 실행되고, setter 메서드는 obj.propName = value으로 프로퍼티에 값을 할당하려 할 때 실행된다.

     

    let user = {
      name: "John",
      surname: "Smith"
    };

    이 객체에 fullName이라는 프로퍼티를 추가해 fullName'John Smith'가 되도록 해보자.

    기존 값을 복사-붙여넣기 하지 않고 접근자 프로퍼티를 구현하면 된다.

    let user = {
      name: "John",
      surname: "Smith",
    
      get fullName() {
        return `${this.name} ${this.surname}`;
      }
    };
    
    alert(user.fullName); // John Smith

    바깥 코드에선 접근자 프로퍼티를 일반 프로퍼티처럼 사용할 수 있다. 접근자 프로퍼티는 이런 아이디어에서 출발했다.

    접근자 프로퍼티를 사용하면 함수처럼 호출하지 않고, 일반 프로퍼티에서 값에 접근하는 것처럼 평범하게 user.fullName을 사용해 프로퍼티 값을 얻을 수 있다.

    나머지 작업은 getter 메서드가 뒷단에서 처리해준다.

     

    한편, 위 예시의 fullName은 getter 메서드만 가지고 있기 때문에 user.fullName= 을 사용해 값을 할당하려고 하면 에러가 발생한다.

    let user = {
      get fullName() {
        return `...`;
      }
    };
    
    user.fullName = "Test"; // Error (프로퍼티에 getter 메서드만 있어서 에러 발생)

    user.fullName에 setter 메서드를 추가해 에러가 발생하지 않도록 고쳐보자.

    let user = {
      name: "John",
      surname: "Smith",
    
      get fullName() {
        return `${this.name} ${this.surname}`;
      },
    
      set fullName(value) {
        [this.name, this.surname] = value.split(" ");
      }
    };
    
    // 주어진 값을 사용해 set fullName이 실행
    user.fullName = "Alice Cooper";
    
    alert(user.name); // Alice
    alert(user.surname); // Cooper

    이렇게 getter와 setter 메서드를 구현하면 객체엔 fullName이라는 '가상'의 프로퍼티가 생긴다.

    가상의 프로퍼티는 읽고 쓸 순 있지만 실제로는 존재하지 않는다.

     

     

     

    접근자 프로퍼티 설명자

    데이터 프로퍼티의 설명자와 접근자 프로퍼티의 설명자는 다르다.

    접근자 프로퍼티엔 설명자 valuewritable가 없는 대신에 getset이라는 함수가 있다.

    접근자 프로퍼티는 다음과 같은 설명자를 갖는다.

    • get - 인수가 없는 함수로, 프로퍼티를 읽을 때 동작함
    • set - 인수가 하나인 함수로, 프로퍼티에 값을 쓸 때 호출됨
    • enumerable - 데이터 프로퍼티와 동일함
    • configuration - 데이터 프로퍼티와 동일함

     

    let user = {
      name: "John",
      surname: "Smith"
    };
    
    Object.defineProperty(user, 'fullName', {
      get() {
        return `${this.name} ${this.surname}`;
      },
    
      set(value) {
        [this.name, this.surname] = value.split(" ");
      }
    });
    
    alert(user.fullName); // John Smith
    
    for(let key in user) alert(key); // name, surname

    프로퍼티는 접근자 프로퍼티(get/set 메서드를 가짐)나 데이터 프로퍼티(value를 가짐) 중 한 종류에만 속하고 둘 다에 속할 수 없다는 점을 유의해야한다.

     

    한 프로퍼티에 getvalue를 동시에 설정하면 에러가 발생한다.

    // Error: Invalid property descriptor.
    Object.defineProperty({}, 'prop', {
      get() {
        return 1
      },
    
      value: 2
    });

     

     

     

     

    getter와 setter 똑똑하게 활용하기

    getter와 setter를 '실제' 프로퍼티 값을 감싸는 래퍼(wrapper)처럼 사용하면, 프로퍼티 값을 원하는 대로 통제할 수 있다.

     

    아래 예시에선 name을 위한 setter를 만들어 user의 이름이 너무 짧아지는 걸 방지하고 있다. 실제 값은 별도의 프로퍼티 _name에 저장된다.

    let user = {
      get name() {
        return this._name;
      },
    
      set name(value) {
        if (value.length < 4) {
          alert("입력하신 값이 너무 짧습니다. 네 글자 이상으로 구성된 이름을 입력하세요.");
          return;
        }
        this._name = value;
      }
    };
    
    user.name = "Pete";
    alert(user.name); // Pete
    
    user.name = ""; // 너무 짧은 이름을 할당하려 함

    user의 이름은 _name에 저장되고, 프로퍼티에 접근하는 것은 getter와 setter를 통해 이뤄진다.

     

    기술적으론 외부 코드에서 user._name을 사용해 이름에 바로 접근할 수 있다. 그러나 밑줄 "_" 로 시작하는 프로퍼티는 객체 내부에서만 활용하고, 외부에서는 건드리지 않는 것이 관습이다.

     

     

     

    호환성을 위해 사용하기

    접근자 프로퍼티는 언제 어느 때나 getter와 setter를 사용해 데이터 프로퍼티의 행동과 값을 원하는대로 조정할 수 있게 해준다는 점에서 유용하다.

     

    데이터 프로퍼티 nameage를 사용해서 사용자를 나타내는 객체를 구현한다고 가정해보자.

    function User(name, age) {
      this.name = name;
      this.age = age;
    }
    
    let john = new User("John", 25);
    
    alert( john.age ); // 25

    그런데 요구 사항이 바뀌어서 age 대신에 birthday를 저장하는 것으로 수정되었다.

    function User(name, birthday) {
      this.name = name;
      this.birthday = birthday;
    }
    
    let john = new User("John", new Date(1992, 6, 1));

    이렇게 생성자 함수를 수정하면 기존 코드 중 프로퍼티 age를 사용하고 있는 코드도 수정해 줘야한다.

     

    age가 사용되는 부분을 모두 찾아서 수정하는 것도 가능하지만, 시간이 오래걸린다. 게다가 여러 사람이 age를 사용하고 있다면 모두 찾아 수정하는 것 자체가 힘들다. 그리고 ageuser안에 있어도 나쁠 것이 없는 프로퍼티이기도 하다.

     

    기존 코드들은 그대로 두고 age를 위한 getter를 추가해서 문제를 해결해 보자.

    function User(name, birthday) {
      this.name = name;
      this.birthday = birthday;
    
      // age는 현재 날짜와 생일을 기준으로 계산됩니다.
      Object.defineProperty(this, "age", {
        get() {
          let todayYear = new Date().getFullYear();
          return todayYear - this.birthday.getFullYear();
        }
      });
    }
    
    let john = new User("John", new Date(1992, 6, 1));
    
    alert( john.birthday ); // birthday를 사용할 수 있습니다.
    alert( john.age );      // age 역시 사용할 수 있습니다.
    728x90

    'front-end' 카테고리의 다른 글

    재귀와 스택  (0) 2022.03.07
    코딩 스타일  (0) 2022.03.04
    모듈 내보내고 가져오기  (0) 2022.02.28
    모듈(module) 이란?  (0) 2022.02.25

    댓글

Designed by Tistory.