TS에서는 Java나 C계열 언어에서의 Class와 거의 동일한 Class문법을 명시적으로 지원한다.
하지만 오해하지 말자. JS도 객체지향적 언어이다. class 키워드를 통한 문법 대신 prototype을 통해 객체지향성을 구현했을 뿐.
TS로 Class 를 작성한뒤 컴파일 하면, ES6에서 도입된 JS의 Class 문법으로 코드가 변환되는데, ES6의 Class 문법은 사실 prototype을 활용하면서도, prototype 문법을 감추어 둔 함수일 뿐이다.
public : 클래스의 코드블럭 밖에서도 접근 가능한 프로퍼티 = 인스턴스에서 쓸 수 있음. (기본값)
private : 클래스의 코드블럭 안에서만 접근 가능한 프로퍼티 = 인스턴스에서 쓸 수 없음.
protected : 클래스 및 클래스를 상속받은 자식 클래스의 코드블럭 안에서만 접근 가능한 프로퍼티 = 인스턴스에서 쓸 수 없음.
참고로 private이나 protected를 통해 접근을 차단하는 것을 정보은닉이라고 부른다.
Class에 관해서는 OOP(Object Oriented Programming)에 관해 다룰 때 좀 더 깊게 얘기할 예정.
TS에서는 abstract class(추상적 범주, 추상적 클래스)도 제공된다.
또한, abstract method도 제공된다.
abstract <call signature>
readonly 옵션도 제공된다. 만약 public 메서드에 readonly 옵션을 달아둔다면, 인스턴스에서 접근이 가능하지만, 직접적으로 인스턴스의 값을 수정하지는 못하게 막을 수 있다.
하지만 추상적 범주는 치명적인 단점이 있다.
추상적 범주라는 개념은 JS에는 존재하지 않는 개념이지만, 범주(Class, 클래스)라는 개념은 존재하기 때문에 TS가 JS로 컴파일 되는 과정에서 결국 일반적인 Class로 선언된다는 점이다. 따라서 예기치 못한 문제점들이 발생할 수 있다.
이를 보완하기 위해 쓰이는 것이 바로 interface (혹은 type)이다.
interface는 JS로 컴파일되지 않고 TS내에서만 존재한다. 따라서 abstract class와 달리 JS에서는 TS단계에서 어떤 interface 코드를 작성했는지 알 수 없고 접근할 수도 없게 된다는 점에서 결과적으로 갖게되는 JS코드도 가벼워지고, 예기치 못한 런타임 에러를 방지할 수 있다.
abstract class를 확장 할 때는 extends 를 썼지만, interface 혹은 type를 Class에 상속 받기 위해서는 implements를 쓴다는 것이 다를 뿐이다.
abstract class Snack {
constructor(
public sort: string
) { }
getSort() {
console.log(this.sort)
}
}
class Nacho extends Snack {
constructor(
public sort: string,
public price: number
) { super(sort) }
getPrice() {
console.log(`${this.price}원`)
}
}
const doritos = new Nacho("nacho", 1500)
doritos.getSort() // "nacho"
doritos.getPrice() // "1500원"
참고로 super 문법은 JS 문법이다.
Class의 확장시에는 부모 Class에 존재하는 constructor의 모든 요소를 구현해야하는데(애초에 그래서 상속이다. '부모님의 빚은 안 받고 재산만 받을래요' 가 가능한 상속법은 없다.) 이 구현과정을 도와주는 일종의 short-cut이다.
우리는 super()을 통해서 부모에 있던 코드를 다시 무의미하게 복붙하는 일을 피할 수 있다.
interface Snack {
sort : string
getSort():void
}
class Nacho implements Snack {
constructor(
public sort: string,
public price: number
) { }
getSort() {
console.log(this.sort)
}
getPrice() {
console.log(`${this.price}원`)
}
}
const doritos = new Nacho("nacho", 1500)
doritos.getSort() // "nacho"
doritos.getPrice() // "1500원"
마찬가지로 interface의 경우에도 요소를 생략할 수 없다. 위 예제에서는 Nacho Class에서 sort, getSort()가 반드시 구현되어야 한다.
interface와 같이 TS에서 JS로 컴파일 되지 않는 것이 하나 더 있다.
그것은 바로 이전 포스팅에서 설명한 (그리고 바로 앞에 얘기한) type 키워드를 통해 생성된 Type 이다.
이전 포스팅에서도 언급했듯, type 키워드를 통해서는 결국 특정 Type 및 구조를 특정 변수에 할당하게 되므로, 이는 JS로 컴파일되는 과정에서 사라지게 된다. Type은 형식만 지녀서 그 실질이 없기 때문이다.
한마디로, abstract class 보다는 interface나 type이 좀 더 권장된다는 뜻이다. 이제 그럼 interface와 type을 비교해보자.
type CarType = {
name : string
}
const carTyped : CarType = {
name : 'Camaro'
}
interface CarInterface {
name : string
}
const carInterfaced : CarInterface = {
name : 'Camaro'
}
이처럼 위 두 방법은 매우 유사하지만 표현적인 측면에서 가장 큰 차이점은 바로 = 의 유무이다.
다시 한 번 말하지만, type은 기본적으로 변수 선언 키워드(var, let, const) 와 동일한 작용을 한다는 점을 기억하자. 그러면 변수에 뭔가를 할당해주면서 '=' 을 쓰지 않는 것이 더 이상하다.
또한, 바로 이 점 때문에 type 혹은 interface를 확장할 때의 문법도 달라진다.
type CarType = {
name : string
}
type CarType2 = CarType & {
cc : number
}
const carTyped : CarType2 = {
name : 'Camaro',
cc : 6200
}
CarType은 일종의 Type 전용 변수이므로, 이를 CarType2에 상속 받고 싶다면, 연산자를 통해 합집합으로 처리하는 것이다.
반면, interface는 좀 더 Class 문법스러운 방법을 지원한다.
interface CarInterface {
name : string
}
interface CarInterface2 extends CarInterface {
cc : number
}
const carInterfaced : CarInterface2 = {
name : 'Camaro',
cc : 6200
}
게다가 interface 의 경우 type과 달리 기존에 선언된 interface를 다시 선언하면서 내용을 추가할 수도 있다.
반면, (귀에 피가 날 것 같아도 어쩔 수 없다.) type은 변수 선언 키워드와 동일한 작동 메커니즘을 가지고 있으므로, 사실상 TS차원에만 존재하는 하나의 변수(정확히는 상수 const 개념)이다. 따라서 이미 선언된 변수에 새로운 값을 할당할 수 없기 때문에 아래와 같은 코드는 에러가 뜨게 된다.
자 그렇다면 둘 중에서 도대체 뭘 써야할까?
일반적인 TS프로젝트 그리고, 커뮤니티에서는 객체나 클래스의 Type을 지정할 때는 interface 를 쓸 것을 권장한다.
참고로 TS 공식 문서에서는 자유롭게 선택할 수 있다고 안내되어 있긴 하다.
'Learning-Log > Computer Science' 카테고리의 다른 글
[NestJS] NestJS, 한 번 빠지면 벗어날 수 없는 마성의 늪(1) : 소개 (0) | 2022.07.11 |
---|---|
Tistory의 코드블럭을 이쁘게 만들어보자 : 코드블럭 테마 (highlight.js) 적용방법 (0) | 2022.07.06 |
[TS] 타입스크립트의 기초(1) - type 키워드 및 Type에 대한 이해 (0) | 2022.07.05 |
[TS] 타입스크립트 Generic에 대한 이해와 활용 (0) | 2022.07.01 |
[NPM] 패키지 설치시 더 이상 '--save' 플래그를 입력하지 않아도 되는 이유 (0) | 2022.06.24 |