[Typescript] Abstract Class

Jennifer Heejin Kang·2022년 10월 21일
0

Typescript

목록 보기
4/4

Typescript 시리즈를 작성하면서 계속 나오는 말이 뭐냐, 바로 "Javascript와는 다르다!"이다. 이번에 언급해볼 차이점은 바로 객체 지향 프로그래밍 (Object oriented programming)을 가능하게 해준다는 것이다. 여기서 객체 지향 프로그램이 무엇이냐? 나중에 따로 게시글로 언급해보겠다만 뭐 자바랑 C++이 객체 지향 언어 중 일부이다.

객체 지향 프로그래밍의 가장 큰 특징은 바로 객체의 재사용이다. 어떻게 재사용 하는지 하나하나 알아가보자

Class

객체 지향 언어를 배워본 사람이라면 class는 너무 익숙한 개념일 것이다. class는 메소드의 집합이라고 생각하면 되는데 자바스크립트에도 존재하기는 한다. 대신 타입스크립트에서의 class는 자바스크립트의 class보다 확장된 기능을 가지고 있다.

constructor (생성자)

class에는 constructor가 필요하다. class에서 사용한 프로퍼티들을 초기화하는 역할을 하는데 JS의 문법과 다르다.

// 자바스크립트
class Person {
    constructor(name) {
        this.name = name;
    }
}

자바스크립트에서는 프로퍼티를 constructor안에서 생성해야한다. 하지만 타입스크립트에서는 이렇게 하면 "그런 프로퍼티는 존재하지 않아"라면서 에러를 보내버린다. 그러면 타입스크립트에서는 어떻게 해야하나? 생성자 외부에서 프로퍼티를 생성해줘야한다.

// 타입스크립트
class Person {
    private name: string
    constructor(name: string) {
        this.name = name
    }
}

이렇게 생성자 외부에서 프로퍼티를 생성해주고 생성자 안에서 초기화를 해줘야 class 안에 구현된 메소드들에서 사용이 가능하다. 여기서 또 다른점을 확인했는가? 바로 타입스크립트에는 접근 제한자를 지원한다는 것이다. 이것이 자바스크립트와의 차이이고 객체지향프로그래밍을 지원하면서 가능한 일이다.
또 추가적으로 타입을 클래스로 정할 수 있다는 특징이 있다. 이 부분은 뒤에 예시로 더 살펴보도록 하겠다.

접근 제한자

기존 객체 지향 프로그래밍과 동일하다.

  • public: 클래스 내부, 자식 클래스, 외부 클래스에서 모두 사용 가능하다
  • private: 클래스 내부에서만 사용 가능하다
  • protected: 클래스 내부와 자식 클래스에서만 사용이 가능하다

이런 특징이 있어 상황에 따라 맞는 접근 제한자를 지정해줘야 한다.

Abstract class (추상 클래스)

추상클래스는 이름과 같이 추상적이기 때문에 직접적인 사용은 불가능하다. 대신 사용하고자 하는 class에서 추상클래스를 상속받아서 사용이 가능하다.

프로퍼티 구현 방법

추상클래스도 동일하게 프로퍼티와 메소드가 존재하고 클래스에서 추상클래스를 상속받아와서 그 클래스에서 구현하면 된다. 예시를 보자.

abstract class Name{
    constructor(
        public firstName: string,
        public lastName: string,
    ){}
}
class Person extends Name{
}
const heejin: Person = new Person("Heejin", "Kang")

Name이라는 추상클래스 안에 firstname과 lastname을 프로퍼티로 생성했다. 그리고 Person이라는 클래스가 Name을 상속받았다. 즉, Person class에서 상속받은 프로퍼티들을 사용할 수 있게 되었다.
heejin이라는 Person 타입을 가진 객체를 만들었다. 이 객체는 class 초기화 시에 필요한 프로퍼티들을 모두 정해줘야한다. firstName = "Heejin", lastName = "Kang"

메소드 구현 방법

프로퍼티들을 알아봤으니, 메소드 구현에 대해서 알아보자. 앞서 말한 바와 같이 추상클래스에서 메소드 구현이 가능하다.

abstract class Name{
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string,
        private age: number
    ){}
    getNickname(){
        return this.nickname
    }
    getFullname(){
        return `${this.firstName} ${this.lastName}`
    }
    private getAge(){
        return this.age
    }
}
class Person extends Name{
}
const heejin: Person = new Person("Heejin", "Kang", "Jennifer", 1)
console.log(heejin.getNickname()) // "Jennifer"
console.log(heejin.getAge()) // ERROR
console.log(heejin.getFullname()) // "Heejin Kang"

나는 "내 닉네임만 사람들한테 알려주고 싶고 나머지는 개인정보니깐 알려주기 싫어!"해서 nickname은 public으로 설정하고 이외 정보는 모두 private으로 설정했다. 그러고 Name 추상클래스에서 닉네임과 풀네임, 나이를 불러오는 메소드를 각각 만들어줬다.
이전과 같이 heejin이라는 객체를 만들어줬고 Name클래스에서 만든 메소드들의 리턴값을 읽어보았다. getNickname()메소드는 public 메소드에 public 값을 리턴하니깐 닉네임을 리턴하는 것은 오케이. 근데 그 뒤에 애들은 뭐지 싶을 수 있다.
"분명 firstNamelastName을 private으로 설정해줬는데 getFullname()메소드로 읽어올 수 있는거냐고! 근데 나이는 왜 또 못 읽어오는건데!!"라고 생각할 수 있는데 생각해보면 당연하다. 구조도로 보면 다음과 같다.

heejinPerson 타입이므로 부모 클래스인 Name 클래스 중 protected나 public인 프로퍼티나 메소드에 접근이 가능하다. 그렇기 때문에 getNickname()getFullname()에는 접근이 가능하지만 getAge()에는 접근이 불가능한 것이다.
그럼 이제 getFullname()에 접근한 뒤에는 어떻게 되는가? Name 클래스에 이미 접근했기 때문에 해당 클래스에서만 사용할 수 있도록 설정한 private 프로퍼티들에 접근이 가능한 것이다. 따라서 외부에서 getFullname()을 사용하면 문제없이 이름을 리턴할 수 있다.

Abstract Methods

추상 클래스가 있듯이 Abstract Method(추상메소드)도 존재한다.
추상메소드가 무엇이냐면 추상클래스를 상속받은 클래스들과 "이 이름으로 된 메소드를 꼭 구현하렴!" 하고 약속하는 것이다. 그렇기 때문에 이름은 존재하지만 실체는 존재하지 않는다.
무슨 말이냐하면 추상클래스 안에 추상메소드 구현이 가능한데, 추상적이기 때문에 직접 작성은 불가능하고 call signature을 이용해서 구현해야한다는 것이다. 아까 구현했던 메소드들을 추상메소드로 바꿔보자.

abstract class Name{
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string,
    ){}
    abstract getNickname(): string
    abstract getFullname(): string
}
class Person extends Name{
}

이러면 오류가 나타나는데 무슨 오류냐면 "Person에 getNickname, getFullname가 없어!! 빨리 구현해내!"라고 구현 안했다고 뭐라하는 오류가 나타난다. 알았어 그럼 만들어줄게 하고 만들어봤다.

abstract class Name{
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string
    ){}
    abstract getNickname(): string
    abstract getFullname(): string
}
class Person extends Name{
    public getNickname(){
        return this.nickname
    }
    private getFullname(){
        return `${this.firstName} ${this.lastName}` // ERROR
    }

또 오류가 나는데 이번에는 두가지 오류가 있다.
첫 번째, firstNamelastName 프로퍼티를 찾지 못하는 것! 두 프로퍼티가 private으로 되어있기 때문에 자식클래스에서는 읽어오지 못해서 에러가 발생한다.
두번째, getFullname 메소드를 private으로 만든것! 추상메소드나 추상클래스는 반드시 노출이 되어야한다. 자식클래스한테만 노출이 되어도 상관은 없다. 그렇기 때문에 추상메소드/클래스는 꼭 public이나 protected여야만 한다.
그럼 마지막으로 수정을 해본다면

abstract class Name{
    constructor(
        protected firstName: string,
        protected lastName: string,
        public nickname: string,
    ){}
    abstract getNickname(): string
    abstract getFullname(): string
}
class Person extends Name{
    getNickname(){
        return this.nickname
    }
    getFullname(){
        return `${this.firstName} ${this.lastName}`
    }
}
const heejin: Person = new Person("Heejin", "Kang", "Jennifer")
console.log(heejin.getNickname())
console.log(heejin.getFullname())

이러면 문제없이 실행이 가능한 코드가 완성이 된다.

profile
초짜에서 벗어나 개발 전문가가 되고 싶은 블로그 삐약이 🐥

0개의 댓글