자바스크립트의 은닉

김윤진·2022년 3월 14일
0

JavaScript

목록 보기
2/10

reference
https://ui.toast.com/weekly-pick/ko_20200312


언더스코어 프리픽스

자바스크립트는 프라이빗한 속성을 줄 수 있는 속성이 없었다

그래서 컨벤션을 이용한 방법으로 _ 언더스코어 프리픽스를 속성 명에 관용적으로 많이 사용했다

class SomeConstructor {
  constructor() {
    this._privateProp = 'dont touch this';
    this.publicProp = 'you can touch this';  
    }
}

이 방법은 컨벤션을 이용해 private로 취급할 뿐이지 당연히 실제로는 public으로 동작하기 때문에 외부에서 얼마든지 접근 할 수 있다


클로저

근본적으로 접근이 불가능한 private 속성을 만드는 방법으로는 클로저를 이용하는 방법이 있다

class SomeConstructor {
  constructor() {
    const privateProp = 'dont touch this';
    this.publicProp = 'you can touch this';

    this.doSomethingWithPrivateProp = () => { };
  }
}

this를 사용해 데이터에 접근하는 문법과 모양새가 달라지기 때문에 this 컨텍스트와 혼용할 때는 코드의 일관성이 잃어 가독성이 떨어질 수 있지만 효과적으로 데이터를 격리할 수 있었다

인스턴스 컨텍스트와 차원이 분리될 정도의 격리다

이러한 접근 방법은 데이터를 숨기는 것에도 유용하지만 메서드를 숨기는데도 유용하다
모듈패턴

function SomeModule() {
  const privateProp = 'dont touch this';
  const publicProp = 'you can touch this';

  _doSomethingWithPrivateProp = () => { ... }

  const publicMethod = () => {
    _doSomethingWithPrivateProp();
  }

  return {
    publicProp,
    publicMethod
  }   
}

모듈패턴은 ES6의 등장으로 점차 사라져갔다


Symbol

Symbol을 사용해 private 속성을 만들 수 있다

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

class SomeClass {
  [privatePropName] = 'dont touch this';;
  publicProp = 'you can touch this';

  [privateMethodName]() {
    console.log('private method');
  }

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

모듈 스코프 안에서 symbol을 사용할 수 있어 해당 필드나 메서드에 접근할 수 있지만 symbol을 export하지 않는 한 외부에서 접근할 수 없다

접근할 필드의 이름을 무엇인지 모르기 때문이다


샵(#)

자바스크립트에서도 언어가 제공하는 정상적인 방법으로 클래스에 private 속성을 만들 수 있게 되었다
private와 같은 키워드를 사용하는 것이 아니라 #, 샵 프리픽스를 사용한다
속성 명 앞에 #이 붙으면 Private 필드로 동작한다

class Student {
 #age = 10; 
}

const student = new Student();
console.log(lee.#age); //Error TS18013: Property '#age' is not accessible outside class 'Student' because it has a private 

#age라는 속성에 접근하려고 하면 에러 메시지가 출력된다

class Student {
 #age = 10;
 
 getAge() {
  return this.#age; 
 }
}

const student = new Student();
console.log(student.getAge()); // 10

외부에 getAge()라는 getter를 노출해 #age값에 접근할 수 있다
private로 만든 속성은 해당 private 속성이 정의된 클래스를 제외하고 접근이 불가능하다
상속 받은 클래스도 마찬가지이다

하지만 독특한 특징이 있다
private 속성의 독특함이 아니라 자바스크립트이기 때문에 유달리 독특해 보이는 특징이 있다
이 특징은 모든 Private 필드는 소속된 클래스에 고유한 스코프를 갖는다라는 내용에 의해 발생한다

class Student {
 age = 10;

 getAge() {
  return this.age;
 }
}

class Lee extends Student {
  age = 20;

 getFakeAge() {
  return this.age; 
 }
}

const lee = new Lee();
console.log(lee.ageAge()); // 20
console.log(lee.getFakeAge()); // 20

위 예제는 private를 사용하지 않았다
Student를 상속한 Lee 객체에서 age를 중복으로 선언하고 다른 이름의 게터 getFakeAge()를 정의했다
public 속성이라면 this 컨텍스트에는 age 속성이 하나이기 때문에 age의 값이 20이다
이는 StudentgetAge()룰 실행하던 LeegetFakeAge()를 실행하던 동일하게 20이다
왜냐면 애초에 this가 가리키는 인스턴스 컨텍스트에 age는 하나밖에 없기 때문이다


age에 private 속성을 #age로 바꾼다면

class Student {
 #age = 10;

 getAge() {
  return this.#age;
 }
}

class Lee extends Student {
  #age = 20;

 getFakeAge() {
  return this.#age; 
 }
}

const lee = new Lee();
console.log(lee.ageAge()); // 10
console.log(lee.getFakeAge()); // 20

#age, private 속성은 그동안 우리가 알고 있던 this의 그 컨텍스트와는 다른 방식으로 저장된다
기존처럼 인스턴스별로 독립적인 공간을 갖지만, 주가로 클래스별로 독립적인 공간을 갖는 것이다

쉽게 말하면 Student클래스 스코프의 #ageLee클래스 스코프의 #age는 다른 것이다

그러므로 Student클래스에 속한 getAge()가 실행될때는 Student#age접근하고
LeegetFakeAge()가 실행될 때는 Lee#age에 접근하는 것이다

이것이 바로 모든 Private 필드는 소속된 클래스에 고유한 스코프를 갖는다는 것이다

0개의 댓글