Study JavaScript 0714 - private, protected 프로퍼티와 메소드

변승훈·2022년 7월 14일
0

Study JavaScript

목록 보기
43/43

private, protected 프로퍼티와 메서드

객체 지향 프로그래밍 중에서 가장 중요한 원리 중 하나는 '내부 인터페이스'와 '외부 인터페이스'를 구분 짓는 것이다.

프로그래밍에서 특별한 문법과 컨벤션을 사용해 안쪽 세부사항을 숨긴다.

1. 내부 인터페이스와 외부 인터페이스

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

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

내부 인터페이스의 기능은 외부 인터페이스를 통해야만 사용할 수 있다.

이런 특징 때문에 외부 인터페이스만 알아도 객체를 가지고 무언가를 할 수 있다. 객체 안이 어떻게 동작하는지 알지 못해도 괜찮다는 점은 큰 장점이다.

javascript에는 아래와 같은 두 가지 타입의 객체 필드(프로퍼티와 메소드)가 있다.

  • public: 어디에서든지 접근이 가능하며 외부 인터페이스를 구성, 지금까지 다룬 프로퍼티와 메소드는 모두 public이다.
  • private: 클래스 내부에서만 접근이 가능하며 내부 인터페이스를 구성할 때 쓰인다.

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

자바스크립트는 protected 필드를 지원하지 않지만, protected를 사용하면 편리한 점이 많기 때문에 이를 모방해서 사용하는 경우가 많다.

지금까지 배운 내용으로 커피 머신을 만들어보자!

2. 프로퍼티 보호하기

먼저 간단한 커피 머신 클래스를 만들어 보자

class CoffeeMachine {
  waterAmount = 0; // 물통에 차 있는 물의 양

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

}

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

// 물 추가
coffeeMachine.waterAmount = 200;

현재 프로퍼티 waterAmountpower는 public이며, 원하는 값으로 변경하기 쉬운 상태이다.

이제 waterAmount를 protected로 바꿔서 waterAmount를 통제해 보자.

protected 프로퍼티 명 앞엔 밑줄 _이 붙는다.

javascript에서 강제한 사항은 아니지만, 밑줄은 프로그래머들 사이에서 외부 접근이 불가능한 프로퍼티나 메서드를 나타낼 때 쓴다.

class CoffeeMachine {
  _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: 물의 양은 음수가 될 수 없습니다.

예시로 waterAmount를 0 미만의 값으로는 설정하지 못하도록 만들어 보았다.

3. 읽기 전용 프로퍼티

읽기 전용 프로퍼티를 활용하면 프로퍼티를 생성할 때만 값을 할당하고, 그 이후에는 값을 수정할 수 없다.

우리의 예시에선 전력이 이에 해당한다고 볼 수 있다.
읽기 전용 프로퍼티를 만드려면 getter만 만들어야 한다.

power 프로퍼티를 읽기만 가능하도록 만들어보자.

class CoffeeMachine {
  // ...

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

  get power() {
    return this._power;
  }

}

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

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

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

※ getter와 setter는 get...(arg1, arg2, ...) / set...(arg1, arg2, ...)으로 선언하여 다수의 인자를 받을수 있게 사용할 수 있다!

※ protected 필드는 private 필드와 달리 상속이 가능하다!

4. private 프로퍼티

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

#은 javascript에서 지원하는 문법으로, 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;
console.log(machine.#waterAmount); // Error

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

그런데 CoffeeMachine을 상속받는 클래스에선 #waterAmount에 직접 접근할 수 없다. waterAmountgettersetter를 통해야 #waterAmount에 접근이 가능하다!

class MegaCoffeeMachine extends CoffeeMachine {
  method() {
    console.log( this.#waterAmount ); // Error: CoffeeMachine을 통해서만 접근할 수 있습니다.
  }
}

private 필드는 this[name]로 사용할 수 없다.
이런 문법적 제약은 필드의 보안을 강화하기 위해 만들어졌다.

class User {
  ...
  sayHi() {
    let fieldName = "name";
    console.log(`Hello, ${this[fieldName]}`);
  }
}
profile
잘 할 수 있는 개발자가 되기 위하여

0개의 댓글