11. 클래스 (2)

2021. 11. 10. 23:02재주껏 하는 Front-End/타입스크립트 (준비중)

반응형

이번 글에서는 타입스크립트의 클래스에 대해 알아보자. 타입스크립트의 클래스는 ES6의 클래스와 문법이 같으며, 캡슐화에 대한 접근 제한자와 멤버에게 타입을 정의하는 정도의 차이를 가지고 있다. 아래에서 타입스크립트의 클래스에 대해 알아보자.

 

1. 접근 제한자

 

접근 제한자는 객체 지향 언어에서 제공하는 기능으로 클래스 멤버의 접근 유무를 설정하는 기능이다. 이 기능을 통해 클래스 내부를 안전하게 보호하는 캡슐화를 구현한다. 접근 제한자는 public, private, protected, readonly가 있다. 접근 제한자에 대한 설명은 아래와 같다.

 

  • Public : Default 설정으로 클래스로 생성한 객체의 내 / 외부에서 접근할 수 있다.
  • Private : 대상 클래스 내부에서만 접근할 수 있다. 대상 클래스로 생성한 객체도 밖에서는 접근할 수 없다. Private 클래스 필드의 값을 변경하려면 클래스 내부의 멤버 함수를 사용하거나 Get / Set 프로퍼티를 사용한다.
  • Protected : Private와 동일하지만, Protected는 대상 클래스를 상속 받은 자식 클래스 내부에서도 접근할 수 있다.
  • Readonly : 수정할 수 없는 멤버를 정의한다. 생성자에 파라미터에 사용할 경우 클래스 필드를 선언과 동시에 초기화할 수 있다.

 

아래의 코드는 타입스크립트의 클래스에서 정의가 가능한 옵션들을 나타낸 예제이다.

 

class Car {
    public maker: string = ''
    private price: number = 0
    protected modelName: string = ''
    readonly tireCount: number = 4

    constructor (maker, price) {
        this.maker = maker
        this.price = price
    }

    getCarInfo () {
        return {
            maker: this.maker,
            price: this.price,
            tireCount: this.tireCount
        }
    }

    setCarInfo (maker: string, price: number, model: string) {
        this.maker = maker
        this.price = price
        this.modelName = model
    }
}

 

Public은 접근 제한자를 붙이지 않았을 경우 Default로 설정되는 접근 제한자이다. 대상 클래스로 만들어진 객체로 안 / 밖에서 멤버에 접근할 수 있다. 위의 클래스를 객체로 생성하여 Public 클래스 필드인 maker의 값을 수정하고 출력해보았다.

 

const malibu = new Car('chevy', 2895)

malibu.maker = 'kia'
console.log('Malibu Maker > ', malibu.maker)

 

실행 결과는 아래와 같다.

 

 

위와 같이 Public 멤버의 경우 외부에서 마음대로 값을 바꿀 수 있고 수정할 수 있다. 따라서, 객체 내부의 중요한 데이터를 Public으로 선언하면 문제가 발생할 것이다. 이때, 사용하는 접근 제한자가 Private이다. 아래의 코드를 작성해서 실행해보자.

 

const malibu = new Car('chevy', 4000)

malibu.price = 3100
console.log('Malibu Maker > ', malibu.price)

 

실행을 하기도 전에 VS Code에서 이미 에러가 발생하는 것을 볼 수 있다.

 

 

Private로 지정된 멤버는 외부에서 접근하거나 값을 변경할 수 없다. 이 경우 클래스 내부의 메서드를 사용해야만 한다. 아래의 코드를 작성하고 실행해보자.

 

const malibu = new Car('chevy', 4000)

malibu.setCarInfo('Chevy', 3100, 'Malibu Prime Safety')
console.log('Malibu > ', malibu.getCarInfo())

 

실행 결과는 아래와 같다.

 

 

Protected는 Private와 동일하지만 상속 받은 자식 클래스에서도 접근할 수 있다. 아래의 코드를 작성하고 실행해보자.

 

class Car {
    public maker: string = ''
    private price: number = 0
    protected modelName: string = ''

    constructor (maker, price, model) {
        this.maker = maker
        this.price = price
        this.modelName = model
    }
}

class Sedan extends Car {
    constructor (maker, price, model) {
        super(maker, price, model)
    }

    showModelName () {
        console.log('This model > ', this.modelName)
    }
}

const malibu = new Sedan('chevy', 3100, 'Prime Safety')
malibu.showModelName()

 

실행 결과는 아래와 같다.

 

 

Readonly는 오로지 접근만 가능한 멤버를 선언한다. 선언과 동시에 초기화되기 때문에 클래스 내부나 밖에서 값을 변경할 수 없다. 위의 코드에 Readonly 클래스 필드인 country를 추가했다. 그다음 country 코드를 안과 밖에서 접근하여 값을 변경해보았다.

 

class Car {
    public maker: string = ''
    private price: number = 0
    protected modelName: string = ''
    readonly country: string = 'korean'

    constructor (maker, price, model) {
        this.maker = maker
        this.price = price
        this.modelName = model
    }
}

class Sedan extends Car {
    constructor (maker, price, model) {
        super(maker, price, model)
    }

    setCountry (country: string) {
        this.country = console
    }

    showModelName () {
        console.log('This model > ', this.modelName)
    }
}

const malibu = new Sedan('chevy', 3100, 'Prime Safety')
malibu.country = 'US'

 

위의 코드를 실행하기도 전에 아래와 같이 에러가 발생한다.

 

 

Readonly 키워드는 선언과 동시에 초기화 되는 특징을 가지고 있기 때문에, 생성자 함수의 파라미터에 Readonly를 사용하면 클래스 필드를 선언하지 않아도 클래스 필드로 등록된다. 아래의 코드에는 어떠한 클래스 필드도 선언하지 않았지만, 실행하면 maker라는 클래스 필드에 이미 값이 초기화되어 출력되는 것을 확인할 수 있다.

 

class Car {
    constructor (readonly maker: string = 'chevy') {
    }

    getMaker () {
        console.log('Maker > ', this.maker)
    }
}

const malibu = new Car()
malibu.getMaker()

 

실행한 결과는 아래와 같다.

 

 

2. Accessor

 

접근 제한자 키워드와 Getter / Setter를 이용하면 객체의 특정 속성의 접근과 할당을 제어할 수 있다. 아래의 코드는 평범한 타입스크립트 문법으로 어떻게 동작할지 쉽게 예상할 수 있다. Car라는 클래스를 사용하여 malibu 객체를 생성하였다. 이후에 클래스 필드 maker를 "chevy"라는 값으로 초기화하고 있다.

 

class Car {
    maker: string
}

const malibu = new Car()
malibu.maker = 'chevy'

 

만약, 클래스 필드 maker에 제약 사항을 걸고 싶다면 아래와 같이 Getter와 Setter를 선언한다.

 

class Car {
    maker: string

    get getMaker () {
        return this.maker
    }

    set setMaker (maker: string) {
        if (maker === 'kia') {
            console.error('Malibu는 Kia차가 아닙니다...')
        } else {
            this.maker = maker
        }
    }
}

 

위의 클래스를 기준으로 아래와 같이 코드를 작성해보자.

 

const malibu = new Car()
malibu.setMaker = 'kia'

console.log('Malibu maker > ', malibu.getMaker)

 

위의 코드를 실행하면 아래와 같다. Setter에 설정된 조건이 False일 경우, 클래스 필드의 값을 설정하지 않은 것을 알 수 있다.

 

 

참고로 Accessor는 타입스크립트의 변환 대상 버전이 ES5 이상일 경우에만 사용할 수 있다. 기본 값인 ES3로 설정되어 있는 상태에서 Accessor를 사용하면 아래와 같은 에러가 발생한다.

 

 

이 에러를 해결하기 위해서는 tsconfig.json의 대상 언어를 ES5 이상으로 설정하거나 아래와 같이 tsc 명령어에 옵션으로 변환 대상 버전을 지정하면 된다.

 

tsc -t es5 .\class.ts

 


 

지금까지 타입스크립트의 클래스에 대해 살펴보았다. 타입스크립트의 클래스는 객체 지향 언어의 클래스와 거의 동일했으며 객체 지향 언어를 경험한 개발자도 쉽게 이해할 수 있도록 설계되었다. 다만, 자바스크립트의 본래 목적이 객체 지향 언어가 아닌 프로토 타입 지향 언어이기 때문에 실제로는 클래스를 활용한 코드보다는 생성자 함수로 프로토타입을 제어하여 코딩을 하는 경우가 훨씬 많다.

 

다음 글에서는 타입스크립트에서 추가된 중요한 문법 중 하나인 제네릭에 대해서 살펴보겠다. 오늘은 여기까지~

반응형

'재주껏 하는 Front-End > 타입스크립트 (준비중)' 카테고리의 다른 글

12. 제네릭 (1)  (0) 2021.11.11
10. 클래스 (1)  (0) 2021.11.10
9. 기타 타입 (2)  (0) 2021.11.10
8. 기타 타입 (1)  (0) 2021.11.09
7. 인터페이스 (2)  (0) 2021.11.07