[TypeScript] 클래스

JINJIN·2023년 6월 12일
1

TypeScript

목록 보기
6/6
post-thumbnail

TypeScript에 대해서 하나씩 차근차근 배우면서 작성하는 글입니다.

✨ 클래스

타입스크립트 클래스는 객체 지향 프로그래밍의 핵심 요소로, 데이터와 함수를 하나의 단위로 묶어 추상화합니다
클래스는 객체의 설계도 역할을 하며, 객체를 생성하기 위한 기본 구조를 정의합니다.
타입스크립트의 클래스는 자바스크립트의 클래스와 유사하지만, 타입스크립트는 정적 타입 검사와 접근 제한자 등 추가 기능을 제공합니다.


🤔 인터페이스와 클래스의 차이점

분명 저번에 인터페이스에 관한 게시물을 작성할 때에도 객체의 설계도 역할을 한다고 했었습니다.
그렇다면 인터페이스와 클래스는 같은 것이라고 봐도 무방할까요?

음... 분명 비슷한 점도 있지만 인터페이스와 클래스는 분명하게 차이점도 있습니다!
클래스에 대해 설명하기 전에 그 차이점부터 설명하고 들어가겠습니다.

클래스(Class)는 객체의 구체적인 행동과 상태를 정의합니다.
클래스는 생성자(constructor)를 가지며, 그 내부에 메서드(method)와 프로퍼티(property)를 정의할 수 있습니다.

또한, 클래스는 객체의 인스턴스를 생성할 수 있는 구체적인 코드 블럭입니다.
객체 지향 프로그래밍에서 클래스는 상태(state)와 동작(behavior)을 모두 포함하는 핵심 개념입니다.

class Car {
  constructor(public make: string, public model: string) { }
  
  startEngine() {
    console.log(`${this.make} ${this.model}의 엔진이 작동하였습니다.`)
  }
}

let myCar = new Car('도요타', '코롤라');
myCar.startEngine(); // 도요타 코롤라의 엔진이 작동했습니다.

반면에 인터페이스(Interface)는 객체의 구조를 정의합니다.
인터페이스는 메서드와 프로퍼티의 이름, 타입, 그리고 해당 메서드나 프로퍼티가 필수인지 아닌지를 명시할 수 있습니다.

하지만 인터페이스는 구현을 포함하지 않으며, 단지 구조와 형태만을 정의하는 "계약"이라고 볼 수 있습니다.

interface Car {
  make: string;
  model: string;
  startEngine(): void;
}

따라서 클래스는 객체의 구체적인 "설계도"라고 할 수 있으며, 인스턴스를 생성하고 동작을 구현하는 데 사용됩니다.
반면에 인터페이스는 객체의 구조와 형태를 정의하는 "규격서"나 "계약서"라고 볼 수 있습니다.

그래서 클래스와 인터페이스는 각기 다른 목적으로 사용됩니다.
클래스는 객체의 구현과 인스턴스 생성에 사용되고, 인터페이스는 코드 간의 규약을 정의하거나 클래스의 구조를 제한하는 데 사용됩니다.


😎 클래스의 정의와 생성

클래스는 class 키워드를 사용하여 정의하며, 클래스 이름은 일반적으로 대문자로 시작합니다.

class Person {
  name: string;
  age: number;
  
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  
  introduce() {
    console.log(`안녕하세요 제 이름은 ${this.name}이고 제 나이는 ${this.age}살 입니다.`)
  }
}

생성자(constructor)는 특별한 종류의 메소드입니다.
이 메소드는 클래스의 객체가 생성될 때 자동으로 호출되며, 객체의 초기 상태를 설정하는데 주로 사용됩니다.

생성자 함수는 constructor 키워드를 사용하여 정의되며, 클래스 내에서 오직 하나만 존재할 수 있습니다. 클래스에서 생성자를 정의하지 않으면, 기본적으로 빈 생성자가 호출됩니다.

클래스를 정의한 후, new 키워드를 사용하여 클래스의 인스턴스를 생성합니다.

const person = new Person('최연진', 26);
person.introduce(); // 안녕하세요 제 이름은 최연진이고 제 나이는 26살 입니다.

😎 접근 제한자

타입스크립트의 클래스에서 사용되는 접근 제한자(Access Modifiers)는 클래스의 프로퍼티와 메소드의 가시성을 제어하는 키워드입니다.
접근 제한자에는 public, private, 그리고 protected 세 가지가 있습니다.

public

public 접근 제한자를 가진 프로퍼티와 메서드는 어디서든지 접근할 수 있습니다. 타입스크립트에서는 기본적으로 모든 멤버가 public으로 선언되어 있습니다.

class Car {
  public brand: string;
  public model: string;
  
  constructor(brand: string, model: string) {
    this.brand = brand;
    this.model = model;
  }
  
  public startEngine() {
    console.log(`${this.brand} ${this.model}의 엔진이 작동하였습니다.`)
  }
}

private

private 접근 제한자를 가진 프로퍼티와 메서드는 해당 클래스 내에서만 접근 가능합니다.
클래스 외부나 파생 클래스에서는 접근할 수 없습니다.

class Car {
  private brand: string;
  private model: string;
  
  constructor(brand: string, model: string) {
    this.brand = brand;
    this.model = model;
  }
  
  private startEngine() {
    console.log(`${this.brand} ${this.model}의 엔진이 작동하였습니다.`)
  }
}

let myCar = new Car("Toyota", "Corolla");
console.log(myCar.brand); // Error: Property 'brand' is private and only accessible within class 'Car'.
myCar.startEngine(); // Error: Property 'startEngine' is private and only accessible within class 'Car'.

protected

protected 접근 제한자를 가진 프로퍼티와 메서드는 해당 클래스와 파생 클래스 내에서만 접근 가능합니다. 클래스 외부에서는 접근할 수 없습니다.

class Car {
  protected brand: string;
  protected model: string;

  constructor(brand: string, model: string) {
    this.brand = brand;
    this.model = model;
  }

  protected startEngine() {
    console.log(`${this.brand} ${this.model}의 엔진이 작동하였습니다.`)
  }
}

class ToyotaCar extends Car {
  start() {
    this.startEngine(); // startEngine 메소드는 파생 클래스 내에서 액세스할 수 있습니다
  }
}

let myCar = new ToyotaCar("Toyota", "Corolla");
console.log(myCar.brand); // Error: Property 'brand' is protected and only accessible within class 'Car' and its subclasses.
myCar.startEngine(); // Error: Property 'startEngine' is protected and only accessible within class 'Car' and its subclasses.

이처럼, 접근 제한자를 사용하면 클래스의 내부 상태와 구현을 외부로부터 잘 보호하고, 클래스를 사용하는 방법을 좀 더 명확하게 할 수 있습니다.


😤 간결한 생성자 구문

접근 제한자는 생성자 파라미터에도 선언할 수 있습니다!
이때 접근 제한자가 사용된 생성자 파라미터는 암묵적으로 클래스 프로퍼티로 선언되고 생성자 내부에서 별도의 초기화가 없어도 암묵적으로 초기화가 수행된다는 점!

class Car {
  public brand: string;
  public model: string;

  constructor(brand: string, model: string) {
    this.brand = brand;
    this.model = model;
  }

  protected startEngine() {
    console.log(`${this.brand} ${this.model}의 엔진이 작동하였습니다.`)
  }
}

위 예시 코드를 생성자 매개변수에 접근 제한자를 사용하면 아래와 같이 간결하게 생략이 가능합니다.

class Car {
  constructor(public brand: string, protected model: string) { }
  
  protected startEngine() {
    console.log(`${this.brand} ${this.model}의 엔진이 작동하였습니다.`)
  }
}

이 구문을 사용하면, 클래스 속성을 명시적으로 선언하고 생성자 내부에서 초기화하는 번거로움을 피할 수 있습니다. 따라서 코드가 더욱 간결해지고, 유지보수도 쉬워집니다.

그러나 모든 상황에서 이 구문을 사용할 수 있는 것은 아닙니다.
예를 들어, 생성자 매개변수에 추가적인 로직이 필요하거나, 생성자 매개변수와 클래스 속성의 이름이 서로 다를 경우에는 이 구문을 사용할 수 없습니다.


😤 클래스 확장(상속)

클래스 또한 인터페이스처럼 확장(상속)을 할 수 있습니다!
클래스 확장이란 한 클래스의 속성과 메서드를 다른 클래스에게 상속할 수 있는 기능입니다.

다음은 클래스 확장의 간단한 예시 코드입니다.

class Vehicle {
  constructor(public color: string) {}
  
  protected honk(): void {
    console.log('beep');
  }
}

class Car extends Vehicle {
  constructor(public wheels: number, color: string) {
    super(color);
  }
  
  private drive(): void {
   console.log('vroom');
  }
  
  startDrivingProcess(): void {
    this.drive();
    this.honk();
  }
}

const car = new Car(4, 'red');
car.startDrivingProcess(); // vroom, beep

이 예제에서 Car 클래스는 Vehicle 클래스를 확장합니다. extends 키워드를 사용하여 Vehicle 클래스의 모든 공개 속성과 메서드를 Car 클래스가 상속받게 만듭니다.

super 키워드는 부모 클래스의 생성자를 호출하는 데 사용됩니다.
이는 자식 클래스에서 필수적으로 호출되어야 합니다.

private vs protected : private 멤버는 해당 클래스에서만 접근할 수 있지만, protected 멤버는 해당 클래스와 그 자식 클래스에서 접근할 수 있습니다.
따라서 상속을 통해 클래스를 확장할 때에는 이 접근 제한자들을 적절하게 사용해야 합니다.

profile
안녕하세요! 배우는 것을 좋아하는 개발자 JINJIN입니다.

0개의 댓글