백엔드/Node

클래스에 대해서... 알아보자

ZestLee 2024. 7. 15. 11:43

개요

  • 개인 프로젝트를 진행할 때 Nest 를 사용하려 했더니, class 에 대해서 모르면 안 될 것 같았음
  • 따라서 class 에 대해 좀 더 깊게 알아보려 하였다

클래스란

  • OOP 의 중요한 구성 요소로, 객체를 생성하기 위한 템플릿이다
  • 클래스는 속성과 메서드를 포함하여 객체의 상태와 동작을 정의한다

클래스의 주요 특징 (신입 면접 필수 질문이었던...)

  1. 캡슐화: 클래스는 데이터와 메서드를 하나의 단위로 묶어서 외부에서 접근을 제한할 수 있다
  2. 상속: 클래스는 다른 클래스의 특성을 상속받아 재사용할 수 있다
  3. 다형성: 같은 인터페이스를 통해 서로 다른 클래스의 객체를 조작할 수 있다
  4. 추상화: 클래스는 객체의 복잡성을 숨기고 단순한 인터페이스를 제공한다

클래스의 기본 구조

class Car {
  // 속성 (필드)
  private model: string;
  private year: number;

  // 생성자
  constructor(model: string, year: number) {
    this.model = model;
    this.year = year;
  }

  // 메서드
  start() {
    console.log(`${this.model} (${this.year}) started.`);
  }
}

// 객체 생성
const myCar = new Car('붕붕이', 2024);
myCar.start(); // 붕붕이 (2024) started.

생성자(constructor) 란

  • 클래스의 인스턴스를 생성하고 객체를 초기화하는 역할을 한다
  • 필수 초기화를 강제할 수 있다
  • 다른 클래스를 상속받을 경우, 부모 클래스의 생성자를 호출해서 부모 클래스까지 초기화를 진행할 수 있다

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

  • 클래스는 객체를 생성할 수 있는 템플릿이고, 런타임에 실제로 존재하는 js 객체로 변환된다
  • 인터페이스는 ts 문법이고, 객체의 구조를 정의하는 것이고, 런타임에는 아무런 영향도 미치지 않는다. 타입 체크를 위해서 사용된다
// ICar 라는 인터페이스 타입을 선언
interface ICar {
  model: string;
  year: number;
  start(): void;
}

// ICar 를 구현하여 구체적으로 Car 클래스를 만듦
class Car implements ICar {
  constructor(public model: string, public year: number) {}

  start() {
    console.log(`${this.model} (${this.year}) started.`);
  }
}

의존성 주입이란

  • 객체 간의 결합도를 낮추고 객체를 생성하고 관리하는 책임을 나눠 외부에 맡기는 디자인 패턴이다
  • 코드의 유연성과 재사용성을 높이고, 테스트하기 쉬운 구조를 만드는 데에 도움을 준다
class Engine {
  start() {
    console.log('Engine started');
  }
}

class Car {
  private engine: Engine;

  // 생성자를 통해 의존성을 주입받음
  constructor(engine: Engine) {
    this.engine = engine;
  }

  start() {
    this.engine.start();
  }
}

// 외부에서 의존성을 생성하고 주입함
const engine = new Engine();
const car = new Car(engine);
car.start(); // Engine started
  • Car 클래스 내부에서 Engine 객체를 생성하는 것이 아닌 engine 객체를 만들고 car 를 만들 때 주입 시킴
  • 이렇게 하면 car 는 engine 의 변화에 덜 영향 받음

상속이란

  • 클래스가 다른 클래스를 확장하여 부모의 속성과 메서드를 물려받는 관계임
  • 코드 재사용성을 높일 수 있음
class Vehicle {
  constructor(public model: string, public year: number) {}

  start() {
    console.log(`${this.model} (${this.year}) started.`);
  }
}

class Car extends Vehicle {
  constructor(model: string, year: number, public color: string) {
    super(model, year);
  }

  start() {
    super.start();
    console.log(`The car is ${this.color}.`);
  }
}

const car = new Car('Tesla Model 3', 2021, 'red');
car.start();
// 출력:
// Tesla Model 3 (2021) started.
// The car is red.
  • Car 클래스가 Vehicle 클래스를 상속받아서 부모의 속성과 메서드를 물려받았음 그대로 사용이 가능함

의존성 주입 vs. 상속

  1. 의존성 관리:
  • 의존성 주입: 객체가 다른 객체에 의존성을 직접 생성하지 않고 외부에서 주입받음. 이는 객체 간의 결합도를 낮추고, 더 유연하게 관리할 수 있게 함.
  • 상속: 클래스가 다른 클래스를 확장하여 속성과 메서드를 물려받음. 이는 코드 재사용성을 높이고, 클래스 계층 구조를 형성함.
  1. 관계의 표현:
  • 의존성 주입: “has-a” 관계를 나타냄. 예: Car 클래스는 Engine 객체를 가짐.
  • 상속: “is-a” 관계를 나타냄. 예: Car 클래스는 Vehicle 클래스의 일종임.
  1. 유연성:
  • 의존성 주입: 객체의 의존성을 쉽게 교체할 수 있으며, 테스트가 용이함. 예를 들어, Engine 클래스의 다른 구현체를 주입하여 테스트할 수 있음.
  • 상속: 클래스 계층 구조가 깊어질수록 유연성이 떨어질 수 있음. 상속은 다중 상속을 지원하지 않으므로, 한 클래스가 여러 클래스를 상속받을 수 없음.