getter & setter !!! (타입스크립트 접근 제어자)

DatQueue·2022년 9월 27일
1
post-thumbnail

이번 포스팅에서 다뤄볼 내용은 “접근자(Accessor)”이다.

처음부터 다룰 생각은 없었지만 타입스크립트 문법에서 사용되는 “데코레이터”를 공부하는 도중 “접근자 데코레이터(Accessor Decorator)”에 관해 접하게 되었다. 해당 내용을 알아보기 전, “접근자”에 관해, 조금 더 자세히 말하면 자바스크립트와 타입스크립트에서 사용되는 “접근자”에 관해 정리를 먼저 해볼 필요가 있다 생각하여 이번 포스팅을 작성해본다.

아마 자바스크립트, 혹은 자바의 클래스 문법에 대해 공부해 본 사람이라면 익숙하게 접해보았을 것이라 생각이 든다.


객체의 프로퍼티

접근자에 관해 알기 전 먼저 큰 범주에서 “객체의 프로퍼티”에 관해 간단히 알아볼 필요가 있다.

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

데이터 프로퍼티(data property)

첫 번째 종류는 “데이터 프로퍼티(data property)”이다. 아마 평소에 접하는 모든 프로퍼티들은 거의 데이터 프로퍼티일 것이다. 일반적으로 객체에 속해있는 프로퍼티를 데이터 프로퍼티라 한다.

아주 간단 예시로 보면 다음과 같다.

const obj = {
  id: 1,  // data property
  name: "Obj" // data property
}

class Obj {
  static id: number = 123; //data property
}
new Obj();
Obj().id // 123

우리는 해당 프로퍼티를 불러오는데 obj.id, [obj.name](http://obj.name)로, 또한 클래스에서도 Obj().id와 같이 작성해 불러왔다. 다음과 같은 형식으로 불러오는 것이 흔히 데이터 프로퍼티라 말할 수 있다.

접근자 프로퍼티(Accessor Property)

객체의 프로퍼티 종류 중 새로운 종류의 프로퍼티이다. 함수처럼 정의되며 호출은 데이터 프로퍼티에 접근하는 것 처럼 호출할 수 있다.

접근자 프로퍼티는 한번쯤 들어보았을법한 “getter”“setter”를 활용한 구문이다. 코드를 알아보기 전 해당 접근자를 “왜” 사용하는지를 먼저 알고가는 것이 중요한데 그것은 바로 객체지향(OOP)의 “Encapsulation(캡슐화)”와 연관이 있다. (캡슐화에 관해선 구글링 참조바랍니다.)

그리고 “캡슐화”는 곧 “정보 은닉(hiding)”과 관련이 있다는 것을 잘 알 것이다. 이러한 정보은닉(은닉화)는 객체에 대한 구체적인 정보를 노출시키지 않도록 하는 기법이다.

모두 알다시피 OOP에는 Access Modifiers(접근 제한자)가 존재한다. 클래스속 멤버 변수와 메소드에 적용될 수 있는 키워드로 클래스 외부로부터 접근을 제한하여 코드의 안정성및 보안을 향상 시킬 수 있다는 장점이 있다.

자바(JAVA)와 같은 다른 객체지향 언어들도 마찬가지이겠지만 “타입스크립트”에서는 대표적으로 3가지의 접근제한자가 있다. 바로 “Public, Private, Protected”이다.

Private은 클래스 내에서만 접근이 가능하고 외부에서 접근이 불가하다. 하지만 우리는 불가피하게 private으로 지정한 프로퍼티를 수정해야할 경우가 발생할 것이다. 하지만 외부에서 ClassName.property와 같이 작성해 수정하면 당연히 에러가 발생할 것이다. 그렇다고 직접 클래스 본체 내부를 고치는 것은 객체지향에서 어긋난다고 볼 수 있다. 누구나 함부로 프라이빗한 속성을 고친다는 것은 은닉성을 무시하기 때문이다.

하지만 이렇게 private으로 정의된 프로퍼티나 메서드를 사용하기 위해서 OOP에서는 “getter”“setter”라는 키워드를 제공하고 있다.

예시 코드는 클래스 문법과 타입스크립트를 이용하여 작성해보겠다. 물론 자바스크립트에서도 “getter”“setter”를 유용하게 사용할 수 있다. 하지만 타입스크립트로 작성했을 때 조금 더 유연한 코드로써 이해할 수 있을 것이다. 조금 더 큰 단락으로 넘어가 진행해보자.

Getter & Setter

getter와 setter를 사용하는 몇 가지 방법이 있다. 해당 방법은 “방법 1, 방법 2” 라기 보단 “방법1 → 방법2”라 보는 것이 맞을 것이다. 즉, 방법 1의 단점을 보완해 방법 2의 구문을 작성하는 것이다.

(혹은, 단계 1과 단계 2로 생각하셔도 좋습니다.)

방법 1) 메서드를 이용한 접근법

첫 번째 방법은 접근 제어자를 사용할때, “메서드”를 노출시키는 것이다. 코드를 통해 알아보자.

class User {
  private _age: number = 24;

  getAge() {
    console.log('Age getter called');
    return this._age;
  }

  setAge(value: number) {
    console.log('Age setter called');
    this._age = value;
  }
}

const user = new User();
user.setAge(25); // Age setter called
console.log(user.getAge());

결과는 어떨까? (브라우저 콘솔 창에 출력)

해당 결과를 어떻게 도출하였는지 코드를 해석해보자.

먼저 우린 User클래스내에 age라는 프로퍼티를 둠과 동시에 해당 멤버를 프라이빗으로 설정하였다. 참고로 age앞에 붙인 “언더스코어 프리픽스 (_)”는 일종의 프라이빗 멤버 변수에게 붙이는 “컨벤션”이다. 즉, 붙이지 않아도 문법적으로 오류는 전혀없다. 단지 저렇게 언더스코어를 붙여줌으로써 해당 멤버가 “프라이빗”하다는 것을 제시할 수 있다.

우리는 기존 클래스 내부에서 설정한 유저의 나이를 수정하고자 한다. 프라이빗으로 설정하였기 때문에 외부에서 프로퍼티에 직접 접근해(즉, 데이터 프로퍼티처럼 접근) 수정하는 것은 불가하다.

여기서, 우린 getAge, setAge를 이용해 해당 프라이빗 age를 수정할 수 있다.

코드에선 getAge가 먼저 작성되었지만 작동되는 순서는 setAgegetAge이다. 우리는 setAge메서드의 매개변수 value를 클래스 내부에서 작성한 _age로써 받아오도록 한다. 그리고 클래스 외부에서 user.setAge(25)와 같이 value의 값을 런타임에서 받아와준다.

조금 더 간단히 말하면 기존의 24였던 유저의 나이를 우리는 setAge라는 클래스 내부의 메서드를 이용하여 25로 수정할 수 있게 된 것이다. 직접적 프로퍼티 접근으로는 불가능하니 이러한 방법을 사용한 것이다. 최종적으로 getAge를 통해 this._age의 값을 리턴받게 되고 해당 메서드를 클래스 외부에서 user.getAge()를 통해 호출시키면 수정된 값 25를 받을 수 있게 된다.

참고로 “getter”에는 어떠한 파라미터도 받지 않는 것이 원칙이다.

이처럼 메서드를 이용한 접근법은 잘 동작하고, 코드 논리상으로도 아무 문제가 없다. 하지만 평소처럼(즉, 데이터 프로퍼티에 접근하는것 처럼__ intance.prop)속성에 직접 접근해도 내부적으로 관련 로직이 실행되도록 정의할 수 있다면 훨씬 간결하고 동시에 직관적인 코드가 될 것이다.

사실 위의 getAge(), setAge()는 getter와 setter의 형태를 띄는, 단지 우리가 설정해 준 “메서드”일 뿐이다. 자바스크립트와 타입스크립트에선 getset을 활용한 접근자를 제공해주는데 바로 아래서 만나보자.

방법 2) 접근자 게터(get), 세터(set)

위의 코드를 통해 즉, 메서드로써 표현한 접근자를 통해서도 알았겠지만 “getter”는 “읽기 접근을 위한” 키워드이고, “setter”는 “쓰기 접근을 위한” 키워드이다.

앞 전의 코드를 타입스크립트의 접근자 getter, setter를 활용해 변형시켜보자.

class User {
  private _age: number = 24;

  get age() {
    console.log('Age getter called');
    return this._age;
  }

  set age(value: number) {
    console.log('Age setter called');
    this._age = value;
  }
}

const user = new User();
user.age = 25;  // 마치 데이터 프로퍼티처럼 접근

const newAge = user.age;
console.log(newAge);

결과를 확인해보자. (브라우저 콘솔에 출력)

크게 달라진 것은 없다. 단지, getAge(), setAge()와 같이 메서드로 정의해주었던 접근자를 타입스크립트에서 제공해주고 있는 getset키워드를 사용하여 표현할 수 있다.

이렇게 함으로써 우린 프라이빗한 프로퍼티의 값(_age)를 수정해주는데 있어, 메서드로써 접근하는 것보다 마치 “데이터 프로퍼티(data property)”에 접근하는 것처럼 작성해 수정해 줄 수 있게 되었다. 또한 값을 불러올때도 user.ageuser.getAge()보다 훨씬 간결하고 직관적인 의미로 작성할 수 있다.


생각정리

이번 포스팅에선 그 유명한 "getter(게터)""setter(세터)"를 "타입스크립트 (or 자바스크립트)"에선 어떻게 사용하고 또한 어떤 의미를 가지는가에 관해 알아보았다. 객체지향 클래스 문법을 다루는 언어 (Java, C#, Python, TS)에선 해당 개념은 굉장히 중요하다. 다음 포스팅들에서 다룰 "의존성 주입(_DI)"을 이해하기에 앞서 꼭 알고가면 좋을 키워드이기도 하다.

profile
You better cool it off before you burn it out / 티스토리(Kotlin, Android): https://nemoo-dev.tistory.com

0개의 댓글