Dev./javascript

[JS] 클래스의 private 속성

인쥭 2022. 5. 6. 14:56
반응형

참고

 

Private class fields - JavaScript | MDN

class 의 속성(property)들은 기본적으로 public 하며 class 외부에서 읽히고 수정될 수 있다. 하지만, ES2019 에서는 해쉬 # prefix 를 추가해 private class 필드를 선언할 수 있게 되었다.

developer.mozilla.org

  • 아래와 같은 클래스가 있다고 하자.
class Runner {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    /*
    엄
    청
    나
    게
    길
    고
    가
    독
    성
    이
    떨
    어
    지
    는
    로
    직
     */
    console.log('comments end');
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old!!!`);
  }
}
module.exports = Runner;
  • 이 클래스는 아마도 이렇게 사용될 것이다.
const Runner = require('./runner/Runner');

(() => {
  const runner = new Runner('ingnoh', 3);
  runner.sayHello();
})();

  • 그런데 sayHello 메소드의 로직이 너무 길고 복잡하(다고 가정하)여 가독성이 떨어진다.
  • 때문에 함수를 추출하여 다음과 같이 리팩토링을 적용하였다.
class Runner {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    this.stupidFunction();
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old!!!`);
  }

  stupidFunction() {
    /*
    엄
    청
    나
    게
    길
    고
    가
    독
    성
    이
    떨
    어
    지
    는
    로
    직
     */
    console.log('comments end');
  }
}
module.exports = Runner;

  • 그런데 이 클래스는 설계 의도와는 달리 다음과 같이 사용될 수도 있다.
const Runner = require('./runner/Runner');

(() => {
  const runner = new Runner('ingnoh', 3);

  runner.stupidFunction();
  console.log(runner.name);
  console.log(runner.age);
})();
  • 상술한 방식은 클래스가 적절히 캡슐화되지 않으며, 내부의 세부사항이 그대로 노출된다.
  • 이로 인해 다음과 같은 상황이 발생할 수 있다:
    • sayHello 메소드에서만 사용하기 위해 정의한 stupidFunction 메소드를 외부에서 호출할 수도 있다.
    • name과 age 프로퍼티를 외부에서 마음대로 수정하여 클래스가 오동작할 수도 있다.
  • 상술한 예시는 매우 쉬운 클래스이지만, 충분히 복잡한 기능을 수행하는 클래스에서는 문제가 발생할 수 있다.
    • 특히 다른 개발자가 해당 클래스를 사용하는 경우에 문제가 발생하기 쉬울 것이다.
  • 이를 방지하기 위해 이 클래스는 이렇게 사용하세요, 를 문서화하거나 주석을 달 수도 있지만, 이는 강제성이 없다.

  • 다행히도 ES2019에서 도입된 'private 멤버를 명시하기 위한 #'을 활용하여 내부 정보를 숨길 수 있다.
class Runner {
  // private 필드는 여기에 정의한다.
  #name;
  #age;

  // 생성자에서 평범하게 접근이 가능하다.
  constructor(name, age) {
    this.#name = name;
    this.#age = age;
  }

  sayHello() {
    // 클래스 내부에서 접근하는 경우, #을 함께 명시한다.
    this.#stupidFunction();
    console.log(`Hello, my name is ${this.#name} and I am ${this.#age} years old!!!`);
  }

  // 메소드에도 적용이 가능하다.
  #stupidFunction() {
    /*
    엄
    청
    나
    게
    길
    고
    가
    독
    성
    이
    떨
    어
    지
    는
    로
    직
     */
    console.log('comments end');
  }
}
module.exports = Runner;
  • 이제 Runner 클래스는 sayHello 메소드만을 외부에 노출하게 된다.

주의사항

  • 자신의 런타임이 ES2019를 지원하는지 확인한 후에 사용하도록 하자.
    • 내 경우, Node.js 14에서 동작하는 것을 확인하였다!