📘

6장 클래스

6.1 타입스크립트에서 클래스 사용하기

6.1.1 클래스란?

클래스는 객체를 만들기 위한 틀, 혹은 설계도입니다. 타입스크립트의 클래스도 자바스크립트의 클래스와 다르지 않습니다. 다만 타입스크립트에서는 클래스도 사용자가 만드는 하나의 타입이라고 이해하면 좋습니다.

6.1.2 클래스 정의

클래스는 class 키워드를 이용해 작성합니다. 보통 이름은 대문자로 시작하도록 합니다.
class Employee { name: string; age: number; team: string; constructor(name: string, age: number, team: string) { this.name = name; this.age = age; this.team = team; } work() { console.log("열심히 일하는 중!"); } }
클래스는 프로퍼티와 메서드를 가지고 있습니다. 형태를 살펴보면 클래스 정의 자체는 자바스크립트와 다른 게 없으며 타입에 대한 선언만 추가되었다는 것을 알 수 있습니다.

6.1.3 생성자

constructor(name: string, age: number, team: string) { this.name = name; this.age = age; this.team = team; }
constructor은 생성자이고, 객체를 생성하면서 값을 전달할 수 있습니다. 생성자 함수를 작성하지 않을 경우 디폴트 생성자가 불립니다. 타입스크립트의 strict 모드를 사용 중인 경우 프로퍼티를 선언할 때 초깃값을 설정해 주거나 생성자에서 값을 할당해야만 합니다.
const e1: Employee = new Employee('소희', 24, '개발팀'); const e2: Employee = new Employee(); // 오버 로딩 발생
객체를 생성할 때 생성자와 매개변수 타입이 일치하지 않는 경우 타입스크립트에서는 각각의 생성자를 생성하는 오버 로딩이 발생합니다. 자바스크립트에서는 가볍게 무시되었던 부분이니 참고해두면 좋습니다.

6.1.4 Getter & Setter

class Employee { name: string; age: number; team: string; ... // getter get name() { return this._name; } // setter set name(newName:string) { this._name = newName; } }
getter와 setter 함수를 세팅하여 값을 가져오고 변경할 수 있습니다. getter 함수를 선언할 때는 get 키워드를 사용하고, 값을 리턴하는 return문이 없을 경우 에러가 발생합니다. setter 함수는 set 키워드를 이용합니다. 이번엔 변경할 값을 인자로 받아와야 하기 때문에 매개변수 선언이 없을 경우 에러가 발생합니다.
const e1: Employee = new Employee('소희', 24, '개발팀'); console.log(e1.name); // get, '소희' e1.name = '주예'; // set console.log(e1.name); // get, '주예'
getter, setter를 이용하면 마치 객체 필드에 직접 접근하는 것처럼 값을 가져오고 할당할 수 있습니다.

6.1.5 상속

클래스는 다른 클래스를 상속받아 자신만의 프로퍼티를 추가해 사용할 수 있습니다.
class CEO extends Employee { isCEO: boolean; constructor( name: string, age: number, team: string, isCEO: boolean ) { super(name, age, team); // ⭐️ this.isCEO = isCEO; } }
앞서 선언한 Employee 클래스를 상속받아 사용하는 CEO 클래스를 만들었습니다. CEO의 생성자는 Employee의 생성자를 오버라이딩하는 형태로 작성합니다. super()로 부모 클래스의 생성자를 필수로 호출해야 합니다. 놓치면 에러가 발생하니 주의하시길 바랍니다.
const e3: CEO = new Employee('병민', 60, '운영', true);

6.1.6 접근 제어자

클래스 내 필드나 메서드 등 모든 곳에 접근할 수 있는 범위를 설정할 수 있습니다. 접근 제어자는 public, privateprotected로 3가지가 있습니다. 앞선 예시처럼 별다른 접근 제어자를 설정해 주지 않는다면 public 필드가 됩니다. 용도에 맞게 접근 권한을 설정하여 클래스를 수정해 보겠습니다.
class Employee { name: string; private age: number; protected team: string; constructor(name: string, age: number, team: string) { this.name = name; this.age = age; this.team = team; } ... }
name은 public, age는 private, team은 protected로 접근 권한이 설정되었습니다.
class Employee { constructor( public name: string, private age: number, protected team: string ) { this.name = name; this.age = age; this.team = team; } ... }
위와 같이 접근 제어자를 이용해 생성자 함수에서 필드를 작성해 주면, 필드 선언 코드를 생략할 수도 있습니다. 두 코드는 동일한 클래스를 생성합니다.
const employee = new Employee("주예", 25, "개발자"); employee.name; employee.age; // Error employee.team; // Error
이제 객체를 생성해 확인해 보겠습니다. private, protected 필드에 접근하고자 하면 에러가 발생합니다. 생성자에서 볼 수 있듯이 세 가지 필드 전부 클래스 내에서는 자유롭게 접근이 가능하지만 클래스 외부에서는 public 필드만 접근할 수 있다는 것을 알 수 있습니다.
그렇다면 private, protected 접근 권한의 차이는 무엇일까요?
class CEO extends Employee { isCEO: boolean; constructor( name: string, age: number, team: string, isCEO: boolean ) { super(name, age, team); this.isCEO = isCEO; } func() { console.log(this.age); // Error console.log(this.team); } }
Employee를 상속받아 만든 파생 클래스인 CEO에서 차이를 확인할 수 있습니다. private으로 설정해둔 프로퍼티의 경우 상속받은 클래스 내에서도 접근이 불가능합니다.
💡
접근 제어자 : 접근 권한 정리
클래스 내부
클래스 외부
파생 클래스
public
O
O
O
private
O
X
X
protected
O
X
O
 

6.2 인터페이스와 클래스

6.2.1 인터페이스를 클래스에 활용하기

인터페이스는 클래스의 설계도 역할을 해줄 수 있습니다.
interface CarInterface { name: string; speed: number; move(): void; }
name, moveSpeed, move()를 가진 인터페이스가 있습니다.
class Car implements CarInterface { constructor( public name: string, public speed: number, private extra: string) {} move(): void { console.log(`${this.speed}의 속도로 이동 중!`); } }
인터페이스의 타입을 따르는 클래스 인스턴스를 만들기 위해서 implements키워드를 사용합니다.
위처럼 인터페이스로 정의한 필드들은 무조건 public 필드로 정의되기 때문에 private 프로퍼티가 필요할 경우 private extra: string) {} 와 같이 생성자 매개변수에 작성하도록 합니다.