# Typescript 객체지향프로그래밍

# **Introduction**

최근에 \[헤드퍼스트 디자인패턴\]이라는 책을 읽고 있는데, 객체지향프로그래밍에 대한 개념이 부족한 것을 느꼈습니다... 그래서 여러 디자인패턴들에 대해 공부하기전 캡슐화, 추상화, 상속, 다형성 등 객체지향프로그래밍에 대해 먼저 공부하려고 합니다!

# **Body**

## 객체 지향 프로그래밍(**Object-Oriented Programming) 이란?**

객체 지향 프로그래밍이란 "객체" 개념을 중심으로 하는 프로그래밍 패러다임입니다. 객체는 데이터를(속성) 및 해당 데이터에 작용하는 동작(메서드)을 캡슐화한 독립적인 단위입니다.  
OOP의 주요 원칙은 다음과 같습니다.

* **캡슐화(Encapsulation)**
    
    * 캡슐화는 데이터(속성)와 해당 데이터에 작용하는 메서드를 단일 단위(객체)로 묶는 것을 의미합니다. 이는 객체의 내부 구현 세부 사항을 숨기고, 객체를 블랙박스처럼 다루며, 다른 코드 부분이 객체와 상호 작용할 수 있는 공개 인터페이스만을 노출합니다.
        
* **추상화(Abstraction)**
    
    * 추상화는 객체의 본질적인 측면에 집중하면서 불필요한 복잡성을 숨기는 것을 의미합니다. 이는 인터페이스(객체가 사용되는 방법)와 구현(객체가 내부적으로 정의되는 방법) 사이의 명확한 분리를 만드는 데 도움을 줍니다.
        
* **상속(Inheritance)**
    
    * 상속은 클래스(서브클래스)가 다른 클래스(슈퍼클래스)의 속성과 메서드를 상속받을 수 있게 하는 메커니즘입니다. 이는 코드의 재사용성을 높이고, 클래스 간의 계층적 관계를 촉진합니다.
        
* **다형성(Polymorphism)**
    
    * 다형성은 서로 다른 클래스의 객체를 공통 슈퍼클래스의 객체로 다룰 수 있게 해줍니다. 이는 단일 인터페이스가 다양한 유형의 객체를 표현할 수 있도록 하여 코드베이스의 유연성과 확장성을 제공합니다.
        

TypeScript에서는 객체 지향 프로그래밍 개념을 구현하기 위해 클래스와 인터페이스를 만들 수 있습니다.

```typescript
class Animal {
  private name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log(`${this.name} makes a sound.`);
  }
}

class Dog extends Animal {
  constructor(name: string) {
    super(name);
  }

  makeSound() {
    console.log(`${this.name} barks.`);
  }
}

const dog = new Dog("Buddy");
dog.makeSound(); // Output: "Buddy barks."
```

이 예제에서는 `Animal` 클래스를 정의하고, 그 안에 `makeSound` 메서드를 포함시킵니다. 그 다음, `Animal` 클래스를 상속받는 `Dog` 클래스를 만듭니다. `Dog` 클래스는 `makeSound` 메서드를 오버라이드하여 특화된 구현을 제공합니다.

TypeScript에서 객체 지향 프로그래밍 원칙을 사용하면 더 체계적이고, 유지보수하기 쉬우며, 확장 가능한 애플리케이션을 만들 수 있습니다.

### 클래스와 객체란?

클래스는 객체를 생성하기 위한 설계도 입니다. 클래스는 객체가 생성될 때 기반으로 사용할 구조와 동작을 정의합니다. TypeScript에서 클래스는 속성(필드 또는 속성이라고도 함)과 메서드(클래스와 관련된 함수)를 포함합니다. 클래스는 특정 개념이나 엔티티와 관련된 데이터와 작업을 캡슐화합니다.

다음은 TypeScript에서 클래스를 생성하는 기본 문법입니다

```typescript
class ClassName {
  // Properties (attributes)
  propertyName1: type;
  propertyName2: type;

  // Constructor
  constructor(parameter1: type, parameter2: type) {
    this.propertyName1 = parameter1;
    this.propertyName2 = parameter2;
  }

  // Methods
  methodName1() {
    // Method implementation
  }

  methodName2() {
    // Method implementation
  }
}
```

TypeScript에서 기본적인 자동차를 나타내는 간단한 클래스를 만들어 보겠습니다.

```typescript
class Car {
  // Properties
  make: string;
  model: string;
  year: number;

  // Constructor
  constructor(make: string, model: string, year: number) {
    this.make = make;
    this.model = model;
    this.year = year;
  }

  // Method
  start() {
    console.log(`Starting the ${this.make} ${this.model}.`);
  }
}
```

이제 객체는 클래스의 인스턴스입니다. 클래스를 정의한 후에는 해당 클래스를 기반으로 객체를 생성할 수 있습니다. 객체는 클래스에 의해 설명된 엔티티의 특정 인스턴스를 나타냅니다. 각 객체는 고유한 속성 세트를 가지며, 클래스에서 정의된 메서드를 호출할 수 있습니다.

다음은 우리가 이전에 정의한 Car 클래스를 기반으로 객체를 생성하는 방법입니다:

```typescript
const myCar = new Car("Toyota", "Camry", 2022);
const anotherCar = new Car("Honda", "Accord", 2023);

myCar.start(); // Output: "Starting the Toyota Camry."
anotherCar.start(); // Output: "Starting the Honda Accord."
```

이 예제에서 우리는 `myCar`와 `anotherCar`라는 두 개의 `Car` 클래스의 별도 인스턴스를 생성했습니다. 각각 고유한 속성(`make`, `model`, `year`)을 가지고 있으며, `start` 메서드를 호출할 수 있는 능력을 갖추고 있습니다.

TypeScript에서 클래스와 객체를 사용하면 코드를 더 효율적으로 구조화할 수 있고, 상속을 통해 코드 재사용을 촉진하며, 객체 지향 프로그래밍의 원칙을 활용할 수 있습니다.

**캡슐화 (Encapsulation)**

캡슐화는 객체 지향 프로그래밍(OOP)의 기본 원칙 중 하나입니다. 캡슐화는 데이터를(속성)와 해당 데이터에 작용하는 메서드(함수)를 클래스라는 단일 단위로 묶는 것을 의미합니다. 캡슐화의 주요 목표는 객체의 내부 구현 세부 사항을 숨기고, 다른 코드 부분이 객체와 상호 작용할 수 있는 명확히 정의된 공개 인터페이스만을 노출하는 것입니다. 이 개념은 종종 "데이터 숨기기"라는 문구로 요약됩니다.

**캡슐화의 이점**

* **모듈화**
    
    * 관련된 데이터와 동작을 클래스 내에 캡슐화함으로써, 독립적으로 재사용 및 유지 관리할 수 있는 자급자족형 모듈을 만들 수 있습니다.
        
* **데이터 보호**
    
    * 내부 데이터를 비공개(또는 보호)로 설정함으로써 클래스 외부에서의 무단 접근과 수정을 방지하여 데이터의 무결성과 보안을 보장합니다.
        
* **코드 유연성**
    
    * 캡슐화를 통해 공개 인터페이스가 변경되지 않는 한 클래스의 내부 구현을 변경하더라도 그 클래스를 사용하는 코드에 영향을 미치지 않게 할 수 있습니다.
        

이제 TypeScript에서 캡슐화의 예를 살펴보겠습니다.

```typescript
class BankAccount {
  private accountNumber: string;
  private balance: number;

  constructor(accountNumber: string, initialBalance: number) {
    this.accountNumber = accountNumber;
    this.balance = initialBalance;
  }

  public getAccountNumber(): string {
    return this.accountNumber;
  }

  public getBalance(): number {
    return this.balance;
  }

  public deposit(amount: number): void {
    this.balance += amount;
    console.log(`Deposited ${amount}. New balance: ${this.balance}`);
  }

  public withdraw(amount: number): void {
    if (this.balance >= amount) {
      this.balance -= amount;
      console.log(`Withdrawn ${amount}. New balance: ${this.balance}`);
    } else {
      console.log("Insufficient balance");
    }
  }
}
```

이 예제에서는 간단한 은행 계좌를 나타내는 `BankAccount` 클래스를 가지고 있습니다. 이 클래스에는 `accountNumber`와 `balance`라는 두 개의 비공개 속성이 있습니다. 이 속성들은 `private`으로 표시되어 있어서 클래스 외부에서 접근할 수 없습니다. 이는 코드의 다른 부분에서 이러한 속성에 직접 접근하거나 수정할 수 없도록 보장합니다.

비공개 속성과 상호 작용하기 위해, 클래스는 `getAccountNumber`, `getBalance`, `deposit`, `withdraw`라는 공개 메서드를 제공합니다. 이 메서드들은 다른 코드가 `BankAccount` 객체와 상호 작용할 수 있도록 하는 공개 인터페이스 역할을 합니다.

이제 이 클래스를 사용해보겠습니다.

```typescript
const account = new BankAccount("123456789", 1000);

console.log("Account Number:", account.getAccountNumber()); // Output: "Account Number: 123456789"
console.log("Balance:", account.getBalance()); // Output: "Balance: 1000"

account.deposit(500); // Output: "Deposited 500. New balance: 1500"
account.withdraw(200); // Output: "Withdrawn 200. New balance: 1300"
account.withdraw(1500); // Output: "Insufficient balance"
```

이 사용 예제에서 우리는 공개 메서드를 통해 계좌 번호와 잔액에 접근할 수 있지만, 이를 직접 수정할 수는 없다는 것을 알 수 있습니다. 이는 캡슐화 덕분인데, 이는 `BankAccount` 객체의 내부 상태를 보호하고 정의된 공개 인터페이스를 통해서만 제어된 접근을 허용합니다.

데이터를 클래스 내에 캡슐화하고 상호 작용할 수 있는 잘 정의된 메서드를 제공함으로써 객체의 상태가 일관되고 안전하게 유지되도록 하며, 코드 유지 관리가 더 쉬워지고 버그나 실수로 인한 오용의 가능성을 줄일 수 있습니다.

**추상화 (Abstraction)**

추상화는 객체 지향 프로그래밍(OOP)의 중요한 개념으로, 객체의 필수적인 기능을 제공하면서 불필요한 세부 사항을 숨기는 데 중점을 둡니다. 이를 통해 복잡한 시스템이나 엔티티를 단순화된 방식으로 표현할 수 있어 이해하고 작업하기가 쉬워집니다. 추상화를 통해 객체의 관련된 특성을 캡처하면서 모든 구현 세부 사항을 노출하지 않을 수 있습니다.

OOP에서 추상화는 추상 클래스와 인터페이스를 사용하여 달성됩니다. 이러한 추상적 구조는 다른 클래스가 따라야 할 청사진을 제공하며, 메서드와 속성의 집합을 정의하지만 그 구현을 제공하지 않습니다. 추상 클래스를 확장하거나 인터페이스를 구현하는 구체적인 서브클래스가 이러한 추상 요소들을 구현하는 역할을 맡습니다.

**추상화의 이점**

* **복잡성 단순화**
    
    * 추상화는 객체의 고수준 설계와 동작에 집중하게 하여, 그 수준에서 중요하지 않은 복잡한 세부 사항을 숨깁니다.
        
* **코드 재사용성**
    
    * 추상 클래스나 인터페이스를 통해 공통 인터페이스를 정의함으로써 코드 재사용을 촉진합니다. 구체적인 클래스는 추상 클래스를 상속하거나 인터페이스를 구현하여 그 구조와 동작을 상속받을 수 있습니다.
        
* **유연성**
    
    * 추상화는 추상 인터페이스가 변경되지 않는 한, 구체적인 클래스의 구현 세부 사항을 변경해도 프로그램의 전체 기능에 영향을 미치지 않도록 해줍니다.
        

이제 Typescript에서 추상화 예를 살펴보겠습니다.

```typescript
// Abstract class representing a shape
abstract class Shape {
  abstract getArea(): number;
  abstract getPerimeter(): number;
}

// Concrete subclass representing a Circle
class Circle extends Shape {
  private radius: number;

  constructor(radius: number) {
    super();
    this.radius = radius;
  }

  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }

  getPerimeter(): number {
    return 2 * Math.PI * this.radius;
  }
}

// Concrete subclass representing a Rectangle
class Rectangle extends Shape {
  private width: number;
  private height: number;

  constructor(width: number, height: number) {
    super();
    this.width = width;
    this.height = height;
  }

  getArea(): number {
    return this.width * this.height;
  }

  getPerimeter(): number {
    return 2 * (this.width + this.height);
  }
}
```

이 예제에서 우리는 `Shape`라는 추상 클래스를 가지고 있습니다. 이 클래스는 `getArea()`와 `getPerimeter()`라는 두 개의 추상 메서드를 정의합니다. 이 메서드들은 어떤 도형의 필수적인 특성을 나타내지만, `Shape` 클래스 자체에서는 구현되지 않습니다.

그 다음으로, `Circle`과 `Rectangle`이라는 두 개의 구체적인 서브클래스가 `Shape` 클래스를 확장합니다. 이 서브클래스들은 추상 메서드에 대한 구현을 제공할 책임이 있습니다.

추상화를 사용함으로써, 우리는 다양한 도형을 모아 각 도형의 구체적인 구현 세부 사항에 대해 걱정하지 않고 그들의 면적과 둘레를 계산할 수 있습니다.

```typescript
function printShapeDetails(shape: Shape) {
  console.log("Area:", shape.getArea());
  console.log("Perimeter:", shape.getPerimeter());
}

const circle = new Circle(5);
const rectangle = new Rectangle(4, 6);

printShapeDetails(circle);
printShapeDetails(rectangle);
```

이 코드들 실행하면 아래와 같은 결과가 나옵니다.

```typescript
Area: 78.53981633974483
Perimeter: 31.41592653589793
Area: 24
Perimeter: 20
```

추상화의 장점은 `printShapeDetails` 함수가 `Shape` 인터페이스를 구현하는 모든 도형과 함께 작동할 수 있다는 점입니다. 삼각형, 사각형 등 새로운 도형을 만들어도 `Shape` 클래스나 `printShapeDetails` 함수를 수정할 필요가 없습니다. 이는 추상화를 통해 얻은 유연성과 재사용성을 잘 보여줍니다.

**상속 (Inheritance)**

상속은 객체 지향 프로그래밍(OOP)의 핵심 개념으로, 한 클래스(서브클래스)가 다른 클래스(슈퍼클래스)의 속성과 메서드를 상속받을 수 있게 해줍니다. 상속은 클래스 간의 "is-a" 관계를 형성하며, 서브클래스는 슈퍼클래스의 특수화된 버전이 됩니다. 상속은 공통 속성과 동작을 슈퍼클래스에 정의하고 이를 서브클래스에서 확장하거나 수정할 수 있도록 하여 코드 재사용을 촉진합니다.

상속되는 클래스를 슈퍼클래스 또는 기본 클래스라고 하고, 슈퍼클래스를 상속받는 클래스를 서브클래스 또는 파생 클래스라고 합니다.

**상속의 이점**

1. **코드 재사용성:** 상속을 통해 슈퍼클래스에 정의된 기능을 재사용할 수 있으며, 애플리케이션에서 중복 코드를 줄일 수 있습니다.
    
2. **모듈화:** 상속은 클래스의 계층적 조직을 촉진하여 코드를 더 체계적으로 구성하고 유지 관리하기 쉽게 만듭니다.
    
3. **다형성:** 상속된 메서드는 서브클래스에서 오버라이드되어 특수한 동작을 제공할 수 있으며, 공통 슈퍼클래스 인터페이스를 통해 서로 다른 클래스의 객체를 다룰 때 다형성을 가능하게 합니다.
    

이제 예제를 통해 상속을 설명해 보겠습니다.

```typescript
class Animal {
  private name: string;
  private age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  makeSound() {
    console.log("Some generic sound");
  }

  getInfo() {
    return `Name: ${this.name}, Age: ${this.age}`;
  }
}

// Subclass: Dog (inherits from Animal)
class Dog extends Animal {
  private breed: string;

  constructor(name: string, age: number, breed: string) {
    super(name, age);
    this.breed = breed;
  }

  makeSound() {
    console.log("Woof!");
  }

  getInfo() {
    return `${super.getInfo()}, Breed: ${this.breed}`;
  }
}
```

이 예제에서는 `name`과 `age`라는 속성과 `makeSound()` 및 `getInfo()` 메서드를 가진 기본 클래스 `Animal`이 있습니다. 그런 다음, `Animal`을 상속받는 `Dog`라는 서브클래스를 정의합니다. `Dog` 클래스는 고유한 속성 `breed`를 추가하고, `makeSound()` 및 `getInfo()` 메서드를 오버라이드합니다.

이제 이러한 클래스를 기반으로 객체를 생성해 보겠습니다:

```typescript
const genericAnimal = new Animal("Generic Animal", 5);
console.log(genericAnimal.getInfo()); // Output: "Name: Generic Animal, Age: 5"
genericAnimal.makeSound(); // Output: "Some generic sound"

const dog = new Dog("Buddy", 3, "Labrador");
console.log(dog.getInfo()); // Output: "Name: Buddy, Age: 3, Breed: Labrador"
dog.makeSound(); // Output: "Woof!"
```

이 예제에서, `Dog` 클래스는 `Animal` 클래스로부터 `name`, `age` 속성과 메서드를 상속받는 것을 볼 수 있습니다. `makeSound()` 메서드는 `Dog` 클래스에서 오버라이드되어 개의 특유의 소리("Woof!")를 제공합니다. 또한, `getInfo()` 메서드도 `Dog` 클래스에서 오버라이드되어 `name`과 `age`와 함께 `breed` 속성을 포함합니다.

상속을 사용함으로써, 각 서브클래스가 슈퍼클래스의 기능을 상속하고 확장하는 클래스 계층 구조를 만들 수 있습니다. 이 접근 방식은 공통 코드를 재사용하고, 특화된 동작을 정의하며, 구조적이고 모듈화된 방식으로 복잡한 시스템을 구축할 수 있게 합니다.

**다형성 (Polymorphism)**

다형성은 객체 지향 프로그래밍(OOP)의 또 다른 중요한 개념으로, 서로 다른 클래스의 객체를 공통 슈퍼클래스의 객체로 다룰 수 있게 합니다. 이를 통해 단일 인터페이스(메서드 또는 속성)가 다양한 유형의 객체를 나타낼 수 있어 코드베이스에 유연성과 확장성을 제공합니다. 다형성은 종종 메서드 오버라이딩과 메서드 오버로딩을 통해 구현됩니다.

다형성에는 두 가지 유형이 있습니다:

* **컴파일 타임 다형성 (정적 다형성)**
    
    * 이 유형의 다형성은 컴파일 시에 해결됩니다. 이는 메서드 오버로딩이 사용될 때 발생합니다, 즉, 동일한 이름이지만 다른 매개변수 목록을 가진 여러 메서드가 존재할 때입니다. 컴파일러는 함수 호출 시 전달된 인수와 메서드의 시그니처를 기반으로 적절한 메서드를 결정합니다.
        
* **런타임 다형성 (동적 다형성)**
    
    * 이 유형의 다형성은 런타임 시에 해결됩니다. 이는 서브클래스에서 슈퍼클래스와 동일한 이름과 시그니처를 가진 메서드가 있을 때 발생합니다. 호출될 메서드는 객체의 실제 유형에 따라 런타임 시에 결정됩니다.
        

이제 두 가지 유형의 다형성을 예제로 설명해 보겠습니다.

```typescript
// Compile-time Polymorphism (Method Overloading)
class MathOperations {
  add(a: number, b: number): number;
  add(a: string, b: string): string;
  add(a: any, b: any): any {
    return a + b;
  }
}

const math = new MathOperations();
console.log(math.add(5, 10)); // Output: 15 (number addition)
console.log(math.add("Hello, ", "World!")); // Output: "Hello, World!" (string concatenation)

// Run-time Polymorphism (Method Overriding)
class Animal {
  makeSound() {
    console.log("Some generic sound");
  }
}

class Dog extends Animal {
  makeSound() {
    console.log("Woof!");
  }
}

class Cat extends Animal {
  makeSound() {
    console.log("Meow!");
  }
}

function animalSound(animal: Animal) {
  animal.makeSound();
}

const dog = new Dog();
const cat = new Cat();

animalSound(dog); // Output: "Woof!" (Dog's sound)
animalSound(cat); // Output: "Meow!" (Cat's sound)
```

이 예제에서 먼저 `MathOperations` 클래스에서 메서드 오버로딩을 통한 컴파일 타임 다형성을 보여줍니다. 우리는 `add` 메서드의 두 가지 버전을 정의합니다: 하나는 숫자 덧셈을 위한 것이고, 다른 하나는 문자열 연결을 위한 것입니다. 함수 호출 시 사용된 인수 유형에 따라 적절한 메서드가 컴파일 타임에 선택됩니다.

다음으로, `Animal`, `Dog`, `Cat` 클래스에서 메서드 오버라이딩을 통한 런타임 다형성을 보여줍니다. `Animal` 클래스는 일반적인 소리를 제공하는 `makeSound` 메서드를 가지고 있습니다. `Dog`와 `Cat` 클래스는 각각의 특수한 소리를 제공하기 위해 `makeSound` 메서드를 오버라이드합니다. 우리가 `Dog`와 `Cat`의 객체를 사용하여 `animalSound` 함수를 호출할 때, 적절한 `makeSound` 메서드가 객체의 실제 유형에 따라 런타임에 동적으로 결정됩니다.

다형성을 통해 객체를 특정 유형이 아닌 공통 인터페이스를 기반으로 처리하여 더 유연하고 확장 가능한 코드를 작성할 수 있습니다. 이는 대규모 애플리케이션 설계에서 중요한 역할을 하며, 다양한 클래스와 모듈 간의 상호작용을 단순화합니다.

### Typescript 인터페이스

TypeScript에서 인터페이스는 객체의 구조와 형태를 정의하는 데 사용됩니다. 인터페이스는 객체가 따라야 할 계약을 정의하는 방법을 제공하며, 객체가 가져야 할 속성, 메서드 및 그들의 타입을 지정합니다. 인터페이스는 정적 타입 검사와 코드 문서화를 구현하는 데 중요한 역할을 합니다.

다음은 TypeScript 인터페이스에 대한 개요입니다.

**인터페이스 선언**

인터페이스는 `interface` 키워드를 사용하여 선언하며, 그 뒤에 인터페이스의 이름을 붙입니다.

선언 예시 입니다.

```typescript
interface Person {
  name: string;
  age: number;
}
```

**속성**

인터페이스는 객체가 가져야 할 속성을 정의합니다. 각 속성은 이름과 타입으로 정의됩니다.

속성 예시 입니다.

```typescript
interface Person {
  name: string;
  age: number;
}
```

**선택적 속성**

인터페이스에서 속성 이름 뒤에 `?`(물음표)를 추가하여 속성을 선택적으로 만들 수 있습니다. 이러한 속성은 구현 객체에서 존재할 수도 있고 생략될 수도 있습니다.

**선택적 속성 예시** 입니다.

```typescript
interface Person {
  name: string;
  age?: number;
}
```

**읽기 전용 속성**

`readonly` 수식어를 사용하여 속성을 읽기 전용으로 표시할 수 있습니다. 읽기 전용 속성은 객체가 생성될 때만 값을 할당할 수 있으며, 그 이후에는 수정할 수 없습니다.

**읽기 전용 속성 예시** 입니다.

```typescript
interface Person {
  readonly name: string;
  readonly age: number;
}
```

**메서드**

인터페이스는 객체가 구현해야 하는 메서드도 정의할 수 있습니다. 메서드 시그니처에는 메서드 이름, 매개변수 타입, 반환 타입이 포함됩니다.

**메서드 예시** 입니다.

```typescript
interface Calculator {
  add(a: number, b: number): number;
  subtract(a: number, b: number): number;
}
```

**인터페이스 확장**

인터페이스는 다른 인터페이스를 확장하여 그 속성과 메서드를 상속받으면서 새로운 속성과 메서드를 추가할 수 있습니다. 이를 통해 모듈화되고 재사용 가능한 인터페이스 정의를 만들 수 있습니다.

**인터페이스 확장 예시** 입니다.

```typescript
interface Employee extends Person {
  employeeId: number;
  department: string;
}
```

**인터페이스 구현**

객체가 특정 인터페이스를 따르도록 보장하려면 `implements` 키워드를 사용하여 해당 객체가 특정 인터페이스를 구현한다고 명시할 수 있습니다. TypeScript는 객체가 필요한 속성과 메서드를 제공하도록 강제합니다.

**인터페이스 구현 예시** 입니다.

```typescript
class Person implements Employee {
   name: string;
   age: number;
   employeeId: number;
   department: string;
}
```

TypeScript의 인터페이스는 계약을 정의하고 객체에 대한 타입 검사를 강제하는 방법을 제공합니다. 인터페이스는 코드 재사용성과 유지보수성을 촉진하며, 컴파일 타임에 오류를 잡는 데 도움을 줍니다.

# **Conclusion**

이번에 객체지향 프로그래밍에 대해서 공부한걸 바탕으로 더 좋은 코드를 작성할 수 있다는 자신감이 생겼습니다! 아직 잘 읽히지 않고 있는 디자인패턴 책을 더 깊게 공부하면서 좋은 개발자가 되겠습니다!
