Javascript

[Javascript] ES13 클래스(Class)의 보안성을 높여주는 프라이빗 지시자 "#" 과 스테틱(static) 지시자 기초

apost 2023. 1. 23. 22:42

1. 클래스 프라이빗 지시자 #

 

 

자바스크립트의 클래스는 클래스(Class) 객체의 모습을 하고 있지만, 다른 언어의 클래스에 비해 많이 부족합니다.

가장 큰 단점은 클래스 객체 안의 모든 속성과 메서드가 퍼블릭이어서 객체 외부에서 모든 속성가 메서드에 제한 없이 접근이 가능합니다.

보안성이 없다 보니, 사이드 이펙트가 발생하기도 하고, 게터/세터(get/set) 같은 속성 접근 방식이 별 의미가 업게 되기도 합니다.

ES13에서부터 드디어 프라이빗 속성이 클래스에서 정식으로 지원되기 시작했습니다.

 

기존에도 클래스에 프라이빗 속성을 표시하는 지시자가 있었습니다.

속성 앞에 언더바(_)를 표시하면 프라이빗 속성으로 선언을 하는 것으로 프라이빗 속성을 구분했습니다.

다만, 이 언더바로 표시하는 프라이빗 속성이 속성 이름을 구분하기 위한 일종의 약속일뿐 실제로 기능은 없었습니다.

 

class Account {
    _balanceMoney = 0

    constructor(name) {
        this.name = name
    }
    
    getName() {
      return this.name
    }
    get balance(){
        return this._balanceMoney
    }
    set balance(balance){
        this._balanceMoney = balance
    }
}

let bankAccount = new Account('라이언')
bankAccount.balance = 1000
console.log(bankAccount.balance) // 1000
bankAccount._balanceMoney=2000;
console.log(bankAccount._balanceMoney); // 2000

 

게터/세터 메서드인 balance로 프라이빗 속성으로 설정한 _balanceMoney 속성에 값을 저장하거나 가져올 수 있습니다.

그리고, _balanceMoney 속성에 직접 접근해서 값을 읽거나 변경할 수 있습니다.

게터/세터, 또는 프라이빗 속성의 보안성을 유지하기 위해 사용하는 메서드들이 의미가 없습니다.

 

ES13의 새로운 프라이빗 지시자인 "#"을 사용해서 위 코드를 재작성 해보겠습니다.

 

class Account {
    #balanceMoney = 0

    constructor(name) {
        this.name = name
    }
    
    getName() {
      return this.name
    }
    get balance(){
        return this.#balanceMoney
    }
    set balance(balance){
        this.#balanceMoney = balance
    }
}

let bankAccount = new Account('라이언')
bankAccount.balance = 1000
console.log(bankAccount.balance) // 1000
bankAccount.#balanceMoney=2000;
console.log(bankAccount.#balanceMoney); // Uncaught SyntaxError: Private field '#balanceMoney' must be declared in an enclosing class

 

프라이빗 지시자로 선언한 프라이빗 속성에 접근하려고 하면 다음과 같은 에러 메시지가 표시됩니다. 전처리 단계에서 문법 에러가 발생하기 때문에 전체 자바스크립트 코드가 실행되지 않습니다.

게터/세터로는 #balanceMoney 속성 값을 읽고 쓸 수 있지만, 속성에 직접 접근하는 것은 차단됩니다.

 

 

프라이빗 지시자는 속성뿐만 아니라 멤버 메서드에도 사용할 수 있습니다. 속성과 마찬가지로 클래스 내부에서만 접근할 수 있습니다.

프라이빗 메서드로 선언한 calcMoney() 메서드는 balanceMoney 속성에 실제 값을 읽고 쓰는 메서드입니다.

그리고 공개된 deposit()/widhdraw()를 이용해서 기능을 사용하게 됩니다.

프라이빗으로 선언한 calcMoney() 메서드를 직접 호출하면 에러가 발생하고 자바스크립트 코드 실행이 멈추게 됩니다.

 

class Account {
    #balanceMoney = 0

    constructor(name) {
        this.name = name
    }
    
    getName() {
      return this.name
    }
    #calcMoney(money) {
        this.#balanceMoney += money;
    }
    deposit(money) {
        this.#calcMoney(Math.abs(money))
    }
    withdraw(money) {
        this.#calcMoney(-Math.abs(money))
    }
}

let bankAccount = new Account('라이언')
bankAccount.deposit(1000)
bankAccount.#calcMoney(-500) // Uncaught SyntaxError: Private field '#balanceMoney' must be declared in an enclosing class

 

 

 

 

2. 정적 호출을 위한 스테틱(Static) 지시자

 

자바스크립트의 내장 정적 함수(Static Function)와 동일한 기능을 클래스의 속성이나 메서드에서 구현해 주는 지시자입니다.

변경되지 않는 상수 값, 또는 클래스의 다른 기능이 필요 없이 단독으로 기능을 할 수 있는 메서드를 정의 수 있으며, 클래스 객체를 생성하지 않기 때문에 불필요한 메모리 공간을 차지하지 않고, 빠르게 실행됩니다.

 

Account 클래스에서 상속을 받은 vipAccount 클래스에는 #vip 속성과 #vip 속성이 있는지 확인해서 true/false를 반환하는 isVIP() 메서드가 추가로 정의되어 있습니다.

isVIP() 메서드는 static 지시자로 선언되었기 때문에 클래스 객체 생성 없이 호출할 수 있는 정적 메서드이고, 인자로 객체를 전달받습니다.

객체 속성에 #vip가 있으면 true를 반환합니다.

 

생성한 클래스 객체인 bankAccount를 isVIP 메서드의 인자로 넣어서 호출하면 false를 반환합니다.

 

class Account {
    #balance = 0

    constructor(name) {
        this.name = name
    }
    
    getName() {
      return this.name
    }
    get balance(){
        return this.#balance
    }
    set balance(balance){
        this.#balance = balance
    }
}

class vipAccount extends Account{
    #vip
    static isVIP(obj){
        return #vip in obj
    }
}

const bankAccount = new Account('라이언');
console.log(vipAccount.isVIP(bankAccount)) // false