avatar
Published on

자바스크립트의 private

Author
  • avatar
    Name
    yceffort

이 글은 은닉을 향한 자바스크립트의 여정을 요약한 글입니다.

History

자바스크립트에서는 객체에 private 한 속성을 만들 수가 없었다. 그래서 보통 자바스크립트 개발자는 private한 것이다 라는 약속으로 _ prefix를 붙여서 사용하고는 했었다.

function Hello() {
  this.publicProp = 'public'
  this._privateProp = 'private'
}

물론 자바스크립트 개발자들은 _의 존재로 해당 속성을 건들지 말아야겠다는 것을 암묵적으로 공유했지만, 어디까지나 암묵적인 것일 뿐, 실제로는 밖에서 얼마든지 접근 할 수 있다.

좀 더 이 문제를 자바스크립트스럽게 해결하기 위해서는, 클로저를 활용하면 된다.

function Hello() {
  this.publicProp = 'public'
  const privateProp = 'private'

  _doWithPrivateProp = () => {
    // do something
  }
}

비록 thisconst가 짬뽕이 되면서, 가독성이 떨어지긴 하지만, 효과적으로 데이터를 격리 시켰다. 위와 같은 방법을 사용해서 메소드도 숨길 수 있다.

function Hello() {
  const publicProp = 'public'
  const privateProp = 'private'

  _doWithPrivateProp = () => {
    // ...
  }

  const publicMethod = () => {
    _doWithPrivateProp()
    // ...
  }

  return {
    publicProp,
    publicMethod,
  }
}

Symbol을 사용해 볼 수도 있다.

const privateMethodName = Symbol()
const privatePropName = Symbol()

class Hello {
  [privatePropName] = 'private'
  publicProp = 'public';

  [privateMethodName]() {
    // ...
  }

  publicMethod() {
    this[privateMethodName](this[privatePropName])
  }
}

Symbol은 생성될 때 마다 고유의 값을 가지므로, 외부에서는 이를 export하지 않는 이상 접근할 수 없다.

# 의 등장

해당 제안 내용은 여기에서 자세히 확인할 수 있다.

class Hello {
  #message = 'hello'
}

const hello = new Hell()
hello.#message
Uncaught SyntaxError: Private field '#message' must be declared in an enclosing class

private 하기 때문에 접근 할 수 없다는 메시지가 뜬다.

여기에서 #은 prefix이기 때문에 꼭 접근시에 #을 써야 한다.

class Hello {
  #message = 'hello'

  getMessage() {
    return this.message // 안됨
  }
}

상속을 받는다 하더라도 접근이 되지 않는다.

class Hello {
  #message = 'hello'

  getMessage() {
    return this.#message
  }
}

class Hi extends Hello {
  getHiMessage() {
    return this.#message
  }
}

const hi = new Hi()
hi.getHelloMessage() // Uncaught SyntaxError: Private field '#message' must be declared in an enclosing clas

추가로 모든 private 필드는 클래스 별로 독립된 고유한 스코프를 갖는다.

class Hello {
  #message = 'hello'

  getMessage() {
    return this.#message
  }
}

class Hi extends Hello {
  #message = 'hi'

  getHiMessage() {
    return this.#message
  }
}

const hi = new Hi()
const hello = new Hello()
console.log(hello.getMessage()) // hello
console.log(hi.getHiMessage()) // hi

타입스크립트에서는?

https://www.typescriptlang.org/docs/handbook/classes.html#ecmascript-private-fields https://www.typescriptlang.org/docs/handbook/classes.html#understanding-typescripts-private

위에서 언급한 # 문법과 더불어 (3.8부터) private 키워드 도 지원한다.

https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/#ecmascript-private-fields

여기에 좋은 내용이 정리되어 있다.

  • Private 필드는 #으로 시작된다.
  • 모든 private 필드는 속한 클래스에서 고유한 스코프를 가지고 있다.
  • #은 타입스크립트의 public private과 함게 사용할 수 없다.
  • Private 필드는 클래스 밖에서 접근하거나 알아챌 수 없다. (JS도 마찬가지)