private, protected 프로퍼티와 메서드

17611 단어 JavaScriptJavaScript

내부 / 외부 인터페이스

객체지향 프로그래밍에서 프로퍼티와 메서드는 두 그룹으로 분류된다

  • 내부 인터페이스 - 동일한 클래스 내의 다른 메서드에선 접근할 수 있지만, 클래스 밖에선 접근할 수 없는 프로퍼티와 메서드
  • 외부 인터페이스 - 클래스 밖에서도 접근 가능한 프로퍼티와 메서드

JS 객체 필드의 경우

  • public : 어디서든 접근할 수 있고 외부 인터페이스를 구성
  • private : 클래스 내부에서만 접근할 수 있고 내부 인터페이스를 구성할 때 쓰인다

JS 이외 다수 언어에서 클래스 자신과 자손 클래스에서만 접근을 허용하는 protected 필드를 지원한다
protected 필드는 private과 비슷하지만, 자손 클래스에서도 접근이 가능하다는 점이 다르다
자손 클래스의 필드에 접근해야하는 경우가 많기 때문에 protected 필드는 private 필드보다 조금 더 광범위하게 사용된다



프로퍼티 보호하기

class CoffeeMachine {
  waterAmount = 0; 

  constructor(power) {
    this.power = power;
    alert( `전력량이 ${power}인 커피머신을 만듭니다.` );
  }

}

// 커피머신 생성
let coffeeMachine = new CoffeeMachine(100);

// 물 추가
coffeeMachine.waterAmount = 200;

현재 waterAmountpower 프로퍼티는 public이므로 읽고 원하는 값으로 변경하기 쉬운 상태이다


waterAmount를 protected로 바꿔서 0미만의 값으로는 설정하지 못하도록 만들어보자

  _waterAmount = 0;

  set waterAmount(value) {
    if (value < 0) throw new Error("물의 양은 음수가 될 수 없습니다.");
    this._waterAmount = value;
  }

  get waterAmount() {
    return this._waterAmount;
  }

  constructor(power) {
    this._power = power;
  }

}

// 커피 머신 생성
let coffeeMachine = new CoffeeMachine(100);

// 물 추가
coffeeMachine.waterAmount = -10; // Error: 물의 양은 음수가 될 수 없습니다.

protected 프로퍼티 명 앞엔 밑줄 _이 붙는다
이제 물의 양을 0 미만으로 설정하면 실패한다



읽기 전용 프로퍼티

power프로퍼티를 읽기전용으로 만들어보자
프로퍼티를 생성할 때만 값을 할당할 수 있고, 그 이후에는 값을 절대 수정하지 말아야 할 경우가 종종 있는데, 이럴 때 읽기 전용 프로퍼티를 활용할 수 있다
읽기전용 프로퍼티를 만드려면 setter는 만들지 않고 getter만 만들어야 한다

class CoffeeMachine {
  // ...

  constructor(power) {
    this._power = power;
  }

  get power() {
    return this._power;
  }

}

// 커피 머신 생성
let coffeeMachine = new CoffeeMachine(100);

alert(`전력량이 ${coffeeMachine.power}인 커피머신을 만듭니다.`); // 전력량이 100인 커피머신을 만듭니다.

coffeeMachine.power = 25; // Error (setter 없음)



getter와 setter 함수

위에서는 get, set 문법을 통해 getter, setter 함수를 만들었지만 대부분은 아래 같은 get.../set...형식의 함수가 쓰인다

class CoffeeMachine {
  _waterAmount = 0;

  setWaterAmount(value) {
    if (value < 0) throw new Error("물의 양은 음수가 될 수 없습니다.");
    this._waterAmount = value;
  }

  getWaterAmount() {
    return this._waterAmount;
  }
}

new CoffeeMachine().setWaterAmount(100);

이 방식으로 함수를 선언하면 다수의 인자를 받을 수 있기 때문에 좀 더 유연하다
반면 get, set 문법을 사용하면 코드가 간결해진다는 장점이 있다



protected 필드는 상속된다

class MegaMachine extends CoffeeMachine로 클래스를 상속받으면, 새로운 클래스의 메서드에서 this._waterAmountthis._power를 사용해 프로퍼티에 접근할 수 있다

👉 protected 필드는 아래에서 보게 될 private 필드와 달리, 자연스러운 상속이 가능하다



private 프로퍼티

⚠️ 스펙에 추가된 지 얼마 안 된 문법이므로 폴리필 구현이 필요하다

private 프로퍼티와 메서드는 제안(proposal) 목록에 등재된 문법으로, 명세서에 등재되기 직전의 상태이다

private 프로퍼티와 메서드는 #으로 시작한다
#으로 시작하면 클래스 안에서만 접근 가능하다

물 용량 한도를 나타내는 private 프로퍼티 #waterLimit과 남아있는 물의 양을 확인해주는 private 메서드 #checkWater를 구현해보자

class CoffeeMachine {
  #waterLimit = 200;

  #checkWater(value) {
    if (value < 0) throw new Error("물의 양은 음수가 될 수 없습니다.");
    if (value > this.#waterLimit) throw new Error("물이 용량을 초과합니다.");
  }

}

let coffeeMachine = new CoffeeMachine();

// 클래스 외부에서 private에 접근할 수 없음
coffeeMachine.#checkWater(); // Error
coffeeMachine.#waterLimit = 1000; // Error

#은 JS에서 지원하는 문법으로, private 필드를 의미한다
private 필드는 클래스 외부나 자손 클래스에서 접근 불가능하다
private 필드는 public 필드와 상충하지 않는다
private 프로퍼티 #waterAmount와 public 프로퍼티 waterAmount를 동시에 가질 수 있다


#waterAmount의 접근자 waterAmount를 만들어보자면

class CoffeeMachine {

  #waterAmount = 0;

  get waterAmount() {
    return this.#waterAmount;
  }

  set waterAmount(value) {
    if (value < 0) throw new Error("물의 양은 음수가 될 수 없습니다.");
    this.#waterAmount = value;
  }
}

let machine = new CoffeeMachine();

machine.waterAmount = 100;
alert(machine.#waterAmount); // Error

protected 필드와 달리, private 필드는 언어 자체에 의해 강제된다는 점이 장점이다



요약

객체 지향 프로그래밍에선 내부 / 외부 인터페이스를 구분하는 것을 캡슐화(encapsulation)라는 용어를 사용해 설명한다

캡슐화❔

내부 인터페이스를 엄격하게 구분하면, 클래스 개발자들은 사용자에게 알리지 않고도 자유롭게 내부 프로퍼티와 메서드들을 수정할 수 있다

내부 인터페이스가 엄격히 구분된 클래스를 만지고 있다면, 그 어떤 외부 코드도 내부 private 메서드에 의존하고 있지 않기 때문에 private 메서드의 이름을 안전하게 바꿀 수 있고, 매개변수를 변경하거나 없앨 수도 있다

사용자 입장에서는 새로운 법전이 출시되면서 내부 정비가 전면적으로 이뤄졌더라도 외부 인터페이스만 똑같다면 업그레이드가 용이하다는 장점이 있다


복잡성 은닉
구현 세부사항이 숨겨져 있으면 간단하고 편리해지며 외부 인터페이스에 대한 설명도 문서화하기 쉬워진다

내부 인터페이스를 숨기려면 protected 나 private 프로퍼티를 사용하면 된다

  • protected 필드는 _로 시작하는 것이 관습처럼 사용된다. 개발자는 protected 프로퍼티가 정의된 클래스와 해당 클래스를 상속받는 클래스에서만 _가 붙은 필드에 접근해야 한다
  • private 필드는 #로 시작하며, JS에서 지원하는 문법이고 #로 시작하는 필드는 해당 필드가 정의된 클래스 내부에서만 접근 가능하다

모든 브라우저에서 private 필드를 지원하지는 않지만 폴리필을 구현하여 사용할 수 있다

좋은 웹페이지 즐겨찾기