[TypeScript] 인터페이스와 클래스 언제 사용할까?

인터페이스(Interface)를 사용해야 하는 경우

인터페이스는 객체의 구조를 타입 검사 목적으로 정의할 때 유용합니다.

  • 객체의 형태를 강제: 인터페이스를 통해 객체가 반드시 가져야 하는 속성과 메서드를 명세할 수 있습니다.
  • 경량 타입 정의: 인터페이스는 컴파일된 JavaScript 코드에는 포함되지 않으므로, 코드 용량에 영향을 주지 않습니다.
  • 구조적 계약: 함수의 매개변수나 반환 타입 등, 특정 객체의 구조를 명확히 하여 코드의 안정성을 높입니다.
interface Order {
  id: number;
  item: string;
  quantity: number;
}

function processOrder(order: Order): string {
  return `${order.item} ${order.quantity}개, 주문 ID: ${order.id}`;
}

const myOrder: Order = {
  id: 101,
  item: "책",
  quantity: 2,
};

console.log(processOrder(myOrder)); // 출력: 책 2개, 주문 ID: 101

클래스를 사용해야 하는 경우

클래스는 객체 생성과 관련된 구체적인 로직동작을 구현할 때 사용합니다.

  • 객체 팩토리 역할: 클래스는 데이터를 캡슐화하고, 관련 메서드를 함께 정의하여 객체를 생성합니다.
  • 상속과 다형성: 클래스는 상속을 통해 공통 기능을 재사용하고, 오버라이딩을 통해 특화된 동작을 구현할 수 있습니다.
  • 상태 관리: 내부 상태를 저장하고, 이를 조작하는 메서드를 포함하므로 복잡한 로직을 가진 객체를 만들 때 유용합니다.
class Customer {
  private _name: string;
  private _points: number;

  constructor(name: string, points: number = 0) {
    this._name = name;
    this._points = points;
  }

  earnPoints(amount: number): void {
    this._points += amount;
    console.log(`${this._name}님의 포인트: ${this._points}`);
  }
}

const customer = new Customer("지민");
customer.earnPoints(50); // 출력: 지민님의 포인트: 50

인터페이스와 클래스 함께 사용하기

인터페이스로 타입을 정의하고, 클래스로 이를 구현하는 방식입니다.

interface CartItem {
  name: string;
  price: number;
  getTotal(): number;
}

class ShoppingCart implements CartItem {
  constructor(public name: string, public price: number) {}

  getTotal(): number {
    return this.price;
  }
}

const cart = new ShoppingCart("이어폰", 30000);
console.log(`항목: ${cart.name}, 총액: ${cart.getTotal()}원`);
// 출력: 항목: 이어폰, 총액: 30000원

언제 인터페이스를, 언제 클래스를 사용해야 할까?

  • 구조 정의와 타입 검사
    • 인터페이스를 사용하여 객체의 형태를 선언하고, 함수 매개변수나 리턴 값에 타입을 명확하게 지정합니다.
    • 인터페이스는 컴파일 후 사라지므로, 런타임 성능에 영향을 주지 않습니다.
  • 객체 생성 및 동작 구현
    • 클래스를 사용하여 객체를 생성하고, 데이터와 메서드를 캡슐화합니다.
    • 상속, 메서드 오버라이딩 등 OOP의 다양한 기능을 활용할 때 적합합니다.

두 개념은 서로 보완적이므로, 보통 인터페이스로 타입을 정의하고, 이를 클래스에서 구현하는 방식으로 함께 사용할 수 있습니다.