Item 56 정보를 감추는 목적으로 private 사용하지 않기

JS에는 비공개 속성이 없다

JS는 클래스에 비공개 속성을 만들 수 없다. 그 대신 언더스코어(_)를 비공개 속성 앞에 접두사로 붙이는 관례가 있다.

class Foo {
 _privateField = 'secret123'
}

하지만 언더스코어로 쓴다고 해서 자동완성이 안되는 것이 아니며, 여전히 클래스 외부에서 접근할 수 있다.

타입스크립트에는 public, protected, private 접근 제어자가 있으나, 이것들은 타입스크립트 키워드이기 때문에 컴파일 후에는 제거된다.(Item 3) 결국 변환된 JS 코드만 남는다.

해결책 1. 클로저(Closure)

정보를 숨기기 위해 가장 효과적인 방법은 클로저(closure)를 사용하는 것이다.

declare function hash(text: string): number

class PasswordChecker {
  checkPassword: (password: string) => boolean
  constructor(passwordHash: number) {
    this.checkPassword = (password: string) => {
      return hash(password) === passwordHash
    }
  }
}

해결책 2. 비공개 필드 기능(#)

현재 표준화가 진행 중인 비공개 필드 기능을 사용할 수도 있다. 필드 앞에 접두사로 #을 붙여서 타입 체크와 런타임 모두에서 비공개로 만드는 역할을 한다.

class PasswordChecker {
  #passwordHash: number

  constructor(passwordHash: number) {
    this.#passwordHash = passwordHash
  }
  
  checkPassword(password: string) {
    return hash(password) === this.#passwordHash
  }
}

const checker = new PasswordChecker(hash('s3cret'))
checker.checkPassword('secret') // false
checker.checkPassword('s3cret') // true

#passwordHash 속성은 클래스 외부에서 접근할 수 없다. 하지만 클로저 기법과 다르게 클래스 메서드나, 동일한 클래스의 개별 인스턴스끼리는 접근이 가능하다. 비공개 필드를 지원하지 않는 JS 버전으로 컴파일하면, WeapMap 을 사용한 구현으로 대체된다.

어쨌든 구현 방식과 무관하게 데이터는 동일하게 비공개이다. 2021년 기준으로 비공개 필드는 JS 표준화 3단계이며, TS에서 사용이 가능하다.

만약 설계 관점의 캡슐화가 아닌 '보안'에 대해 걱정하고 있다면, 내장된 프로토타입과 함수에 대한 변조 같은 문제를 알고 있어야 한다.

Summary

  • public, protected, private 접근 제어자는 타입 시스템에서만 강제될 뿐이다. 런타임에서는 소용이 없으며 단언문을 통해 우회도 가능하다. 접근 제어자로 데이터를 감추려고 해선 안된다.

  • 확실히 데이터를 감추고 싶다면 클로저를 사용하자.

Last updated