TypeScript 연습 <1> -11-
________________________________________________________________________
TypeScript 로 객체 지향 프로그래밍 -2- Interfaces(2)
< 타입스크립트로 객체 지향 프로그래밍 하기. 클래스와 인터페이스 - 인터페이스(2) >
이번 챕터에서는 인터페이스(interface) 와 클래스(Class) 를 합쳐보는 연습을 해볼거야.
어째서 인터페이스가 type 에 비해 더 객체 지향적인 문법인지 알 수 있는 대목이지.
Class 파트에서 우리는 < 추상 클래스 ( Abstrack Class ) > 라는 걸 배웠지.
추상 클래스를 선언하고, Class 해서는 추상 클래스를 extends, 즉 상속 받아 사용하는 문법이였어.
추상 클래스는 다른 클래스가 가져야 할 프로퍼티랑 메소드를 명시할 수 있도록 도와주지.
1.
________________________________
< 인터페이스와 클래스를 연걸하기에 앞서, 추상 클래스를 다시 한 번 집고 넘어가자 >
클래스에게 상속을 부여해 연결된다는 점에서는 추상 클래스와 인터페이스는 크게 다르지 않아.
다음 코드를 통해서 추상 클래스를 복습해보자.
=> 우리가 추상화를 원할 때와 인터페이스를 사용할 때의 차이점에 대해 알아봐야 해.
——
// 추상 클래스는 단지 다른 클래스가 따라야 할 청사진(설계도) 만을 제시할 뿐. 추상 클래스로는 인스턴스를 만들 수 없어.
abstract class User5 {
constructor (
protected firstName: string,
protected lastName: string
) {}
abstract sayHi(name: string) : string
abstract fullName(): string
}
class Player12 extends User5 {
fullName () {
return `${this.firstName} ${this.lastName}`
}
sayHi (name: string) {
return `Hello ${name}. My name is ${this.fullName()}`
}
}
——
=> 추상 클래스(abstract class) 의 특징을 몇 가지 보여주고 있어.
하나. 추상 클래스로는 인스턴스를 만들 수 없다.
둘. 추상 클래스는 다른 클래스에게 방향을 제시해 준다.
셋. 추상 클래스 안에서 작성된 추상 메소드는, 해당 추상 클래스를 상속받는 클래스가 반드시 작성해야 한다.
** 당연히, Player12 는 User5의 생성자 함수도 상속받는다.
추상 클래스에는 문제점이 하나 있어.
바로 자바스크립트에는 ‘abstract class’ 라는 개념이 없다는 거야.
이거 매우 중요해. 타입스크립트에서 추상 클래스를 만들어 봤자 트랜스파일이 되어버리면 그냥 일반 클래스로 바뀌어 버려.
우리는 추상 클래스를 왜 사용하는가.
다른 클래스들이 표준화된 모양, 표준화된 프로퍼티와 메소드를 갖도록 해주는 청사진을 만들기 위해 추상 클래스를 사용해.
따라서 자바스크립트에서 추상 클래스가 트랜스파일 되어 일반 클래스가 되어버린 다면, 추상 클래스를 만든 의미가 없어져.
지금이 바로, 인터페이스를 사용해야 하는 순간.
인터페이스는 가벼워. 인터페이스는 트랜스파일 하면 js 로 바뀌지 않고 사라지지.
그렇다면, 인터페이스를 사용할 때 클래스가 특정 형태를 따르도록 어떻게 강제할 수 있지? 알아가 보자.
2.
________________________________
< 추상 클래스를 인터페이스로 교체 >
——
interface User6 {
firstName: string,
lastName: string,
sayHi(name: string):string
fullName(): string
}
class Player13 implements User6 {
constructor (
public firstName:string,
public lastName:string
) {}
fullName () {
return `${this.firstName} ${this.lastName}`
}
sayHi (name: string) {
return `Hello ${name}. My name is ${this.fullName()}`
}
}
——
=> 인터페이스에서는 사용해야 하는 프로퍼티와 메소드만 제시하고,
그것들에 어떤 값과 로직을 담을 지는 Player13 클래스에서 정해.
그리고 상속받을 때 키워드가 extends 가 아니라 implements 이지?
인터페이스를 implements 하면, 인터페이스에 명시되어 있는 프로퍼티와 메소드를 클래스에서 작성하지 않을 경우 오류가 발생해.
즉, 추상 메소드 마냥 해당 프로퍼티와 메소드를 사용할 것을 클래스에게 강제할 수 있는거지.
추가로, 인터페이스를 implements 받은 클래스에서는, 인터페이스에 명시 된 프로퍼티를 private 로 작성할 수 없어.
그래서 위 연습에서는 public 키워드를 사용.
이처럼 인터페이스의 사용처는 명확해.
인터페이스는 클래스의 모양을 알려준다는 점에서 매우 유용해.
중요한 것은, 그러면서도 자바스크립트 코드로 컴파일 되지 않아.
다음 코드는 위의 코드가 자바스크립트로 트랜스파일된 모습.
——
class Player13 {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
sayHi(name) {
return `Hello ${name}. My name is ${this.fullName()}`;
}
}
——
=> 보다시피 인터페이스는 사라지고 Player13 클래스만 남았어.
즉, 인터페이스와 Implements 는 매우 가볍다 라는 것.
이에 반해 추상 클랙스는 트랜스파일하면 일반 클래스로 남아 버리지. 즉 추상 클래스는 인터페이스에 비해 무겁다.
인터페이스를 상속하는 것의 문제점 중 하나는, private 프로퍼티를 상속받은 클래스에서는 사용하지 못한다는 것.
그리고 다른 하나는,
추상 클래스를 사용했을 때에는 추상 클래스의 생성자 함수를 그대로 클래스에게 상속시키는데에 반해
인터페이스의 경우 상속받는 클래스의 생성자 함수에서 새롭게 프로퍼티를 정의해 주어야 한다는 것.
=> 왜? 인터페이스에서는 생성자 함수가 없기 때문에 프로퍼티를 제대로 된 ‘클래스 프로퍼티’ 로 확정지어 줄 수가 없어서.
추가로, 하나의 클래스가 다수의 인터페이스에게서 상속받을 수도 있어.
——
interface User6 {
firstName: string,
lastName: string,
sayHi(name: string):string
fullName(): string
}
interface Human {
health: number
}
class Player13 implements User6, Human {
…
}
——
=> 이렇게 두 개 이상의 인터페이스에게서 상속받을 수도 있어.
어쨌든 이를 통해서 인터페이스는 객체 리터럴의 모양을 지정해줄 뿐 아니라 클래스의 모양을 특정지어 주기도 한다는 것을 알 수 있었어.
또한 하나의 클래스가 여러 개의 인터페이스에게서 상속받을 수 있다는 것도 알았어.
다음 챕터에서는 인터페이스와 타입, 그리고 인터페이스와 클래스를 다시 한 번 비교해볼거야.
****
우리는 변수의 타입을 type, interface 로 설정할 수 있어.
또한 클래스로 만든 인스턴스를 메소드의 매개변수의 타입으로 지정할 수도 있어. 즉 매개변수의 타입을 클래스로 지정한 다는 거지.
그렇게 하면 매개변수로서 해당 클래스의 인스턴스만을 받아들일 수 있게 될 것이고.