Frontend/JS.info 정리

class - private, protected

Creative_Lee 2022. 6. 22. 20:28

1. 객체 지향의 캡슐화

객체 지향 프로그래밍의 중요한 원리 중 하나는

내부 인터페이스와 외부 인터페이스를 구분 짓는 것,

즉 캡슐화 입니다.

 

객체 지향 프로그래밍에서 프로퍼티와 메서드는 2개의 그룹으로 분류됩니다.

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

커피 머신으로 예를 들자면

외형에는 커피를 뽑는 버튼 몇 개, 구멍 몇 개, 화면 하나 정도가 있습니다.

내부에는 디테일한 장치들과 부품들이 전부 들어있습니다.

하지만 내부의 것을 알지 못해도 사용자는 간단한 조작으로 커피 머신을 사용할 수 있습니다. 

커피 머신의 내부를 가려주는 보호 커버를 제거하면

사용법이 복잡해지고 감전 같은 위험한 상황이 발생하기도 합니다.

 

머신 안쪽에 숨어있는 디테일한 장치와 부품들이 내부 인터페이스가 될 수 있습니다.

내부 인터페이스의 세부사항들은 서로의 정보를 이용해서 객체를 동작시킵니다.

 

커피 머신은 보호 커버에 쌓여 있기 때문에 벗기지 않고는 커피머신 외부에서 내부로 접근할 수 없습니다.

밖에서는 내부를 알 수 없고, 접근할 수 없습니다.

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

이런 특징 덕분에 외부 인터페이스만 알아도 객체를 가지고 무언가를 할 수 있습니다.

객체 내부 동작이 어떤지 몰라도 괜찮다는 점은 큰 장점으로 작용합니다.

 

자바스크립트에는 두 가지 타입의 객체 필드가 있습니다.

  • public : 어디서든지 접근할 수 있으며 외부 인터페이스를 구성합니다.
  • private : 클래스 내부에서만 접근할 수 있으며 내부 인터페이스를 구성할 때 사용됩니다.

2. protected 필드

자바스크립트에서는 프로퍼티명 앞에 밑줄 _ ( under score )을 붙여

클래스 자신과 자손 클래스에서만 접근을 허용하는 'protected' 필드를 모방해서 사용할 수 있습니다.

(정식으로 지원하지 않습니다.)

 

protected 필드는

private와 비슷하지만, 자손 클래스에서도 접근이 가능하다는 차이점이 있습니다.

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

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

}

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

// 물 추가
coffeeMachine.waterAmount = 200;

위 코드에서 waterAmount 와 power는 public입니다.

원하는 값으로 쉽게 변경 할 수 있는 상태입니다.

 

waterAmount를 protected 프로퍼티로 바꿔서 통제해보겠습니다.

class CoffeeMachine {
  _waterAmount = 0;

  set waterAmount(value) {
    if (value < 0){
      console.log("물의 양은 음수가 될 수 없습니다.")
      return 
    }

    this._waterAmount = value;
  }

  get waterAmount() {
    return this._waterAmount;
  }

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

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

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

getter와 setter를 활용해서 waterAmount 프로퍼티를 protected 프로퍼티로 바꿨습니다.

코드에 따라 waterAmount 프로퍼티는 조건에 부합할 때에만 수정할 수 있습니다.


읽기 전용 프로퍼티

power 프로퍼티는 인스턴스 생성시에만 값을 할당할 수 있어야 합니다.

커피 머신의 전력량은 항상 같아야 겠죠.

 

이런 경우에 읽기 전용 프로퍼티를 활용할 수 있습니다.

읽기 전용 프로퍼티는 setter를 구현하지 않고,  getter만 구현하면 됩니다.

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

  get power() {
    return this._power;
  }
}

let coffeeMachine = new CoffeeMachine(100);

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

coffeeMachine.power = 25 // 코드가 동작 하지 않습니다.

console.log(coffeeMachine.power) // 100

getter만 구현했으므로

인스턴스 생성시를 제외하고 power 프로퍼티를 수정할 수 없습니다.


대부분의 코드에서는 get, set 문법보다 다음과 같이

get.... set.....식의 함수가 선호됩니다.

다수의 인자를 받을 수 있다는 장점이 있습니다.

class CoffeeMachine {
  _waterAmount = 0;

  setWaterAmount(value) {
    if (value < 0){
      console.log('물의 양은 음수가 될 수 없습니다.')
      return
    }

    this._waterAmount = value;
  }

  getWaterAmount() {
    return this._waterAmount;
  }
}

let coffeeMachine = new CoffeeMachine()
coffeeMachine.setWaterAmount(-1); // '물의 양은 음수가 될 수 없습니다.'
console.log(coffeeMachine.getWaterAmount()) // 0

 


protected 필드는 상속됩니다.

위 예제를 예로 들어

CoffeeMachine 클래스를 상속 받는 자식 클래스가 있다면

자식 클래스의 메서드에서 this._waterAmount, this.power 를 사용해 프로퍼티에 접근 할 수 있습니다.

 

protected 필드는 private 필드와는 다르게 상속이 가능하므로 ,

자손 클래스에서도 접근이 가능합니다!!


3. private 필드

프로퍼티나 메소드의 앞에  # 을 붙여 private 필드로 만들 수 있습니다.

class CoffeeMachine {
  _waterAmount = 0
  #waterLimit = 200;

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

}

let coffeeMachine = new CoffeeMachine();

coffeeMachine.#checkWater(); // Private field '#checkWater' must be declared in an enclosing class
coffeeMachine.#waterLimit = 1000; // Private field '#checkWater' must be declared in an enclosing class

private 필드는 해당 클래스 안에서만 접근 가능하고,

클래스 외부나 자손 클래스에서 접근 할 수 없습니다.


private 프로퍼티와  public 프로퍼티를 동시에 가질 수 있습니다.

class CoffeeMachine {
  #waterAmount = 0;

  get waterAmount() {
    return this.#waterAmount; // 해당 클래스 내에서만 private 프로퍼티에 접근 가능합니다.
  }

  set waterAmount(value) {
    if (value < 0) throw new Error("물의 양은 음수가 될 수 없습니다.");
    this.#waterAmount = value; // 해당 클래스 내에서만 private 프로퍼티에 접근 가능합니다.
  }
}

let machine = new CoffeeMachine();

machine.waterAmount = 100; // --> setter호출
console.log(machine.waterAmount); // 100 --> getter호출
console.log(machine.#waterAmount) // Error: Private field '#waterAmount' must be declared in an enclosing class

waterAmount 는 public 프로퍼티이고,

private 프로퍼티인 #waterAmount 의 접근자 입니다.


class MegaCoffeeMachine extends CoffeeMachine {
  test(){
    return this.#waterAmount // SyntaxError: Private field '#waterAmount' must be declared in an enclosing class
  }
}

자식 클래스에서는 private 프로퍼티에 직접 접근 할 수 없습니다.


class MegaCoffeeMachine extends CoffeeMachine {}

let megaMachine = new MegaCoffeeMachine();

megaMachine.setWaterAmount(100);
console.log(megaMachine.getWaterAmount()); // 100

자식 클래스에서 private 프로퍼티에 접근하려면

getter, setter를 사용하면 됩니다.


private 필드는 this[name]로 사용할 수 없습니다.

private 필드는 computed property를 사용할 수 없습니다.

class User {
  field = 'Bob'

  sayHi() {
    let fieldName = 'field'
    console.log(`Hello, ${this[fieldName]}`);
  }

  #private = 'God'

  test(){
    let fieldName = 'private'
    console.log(`Hello, ${this[fieldName]}`);
  }
}

let user = new User();
user.sayHi() // Hello, Bob
user.test() // Hello, undefined

다양한 시나리오에서 위와 같은 private의 제약사항은 너무 엄격합니다.

자식 클래스에서 부모 클래스의 내부에 접근해야 하는 상황이 있을 수 있기 때문입니다.

 

언어차원에서 지원하지 않는 protected 필드를 더 자주 사용하는 이유입니다.


요약

1. OOP에서 내부 인터페이스와 외부 인터페이스를 구분하는 것을 캡슐화 (encapsulation) 이라고 한다.

2. 캡슐화의 이점은 다음과 같다.

  • 외부 사용자의 내부 로직 조작으로 인한 에러를 방지함
  • 외부 사용자에게 알리지 않고도 자유롭게 내부 프로퍼티와 매서드 수정 가능 , 업그레이드 용이
    (사용자는 외부 인터페이스만 똑같다면 내부 로직이 바뀐걸 알지 못함)
  • 구현 세부 사항을 숨길 수 있어 편리해지고, 외부 인터페이스에 대한 문서화가 쉬워짐.

3. 내부 인터페이스를 숨기려면 protected( 모방 가능 ) , private ( 지원 ) 필드를 사용하면 된다.

  • protected는 _ 로 시작하며, 정의된 클래스와 자손 클래스에서 접근 가능하다.
  • private는 # 으로 시작하며, 정의된 클래스 에서만 접근 가능하다.

 

 

 

많다 많아!

 

 

 

'Frontend > JS.info 정리' 카테고리의 다른 글

class - mixin  (0) 2022.06.27
class - 내장 class의 특성  (0) 2022.06.24
class - static method, property  (0) 2022.06.22
class - class extends (상속)  (0) 2022.06.21
class - class 기본 문법  (0) 2022.06.20