개요
- 개인 프로젝트를 진행할 때 Nest 를 사용하려 했더니, class 에 대해서 모르면 안 될 것 같았음
- 따라서 class 에 대해 좀 더 깊게 알아보려 하였다
클래스란
- OOP 의 중요한 구성 요소로, 객체를 생성하기 위한 템플릿이다
- 클래스는 속성과 메서드를 포함하여 객체의 상태와 동작을 정의한다
클래스의 주요 특징 (신입 면접 필수 질문이었던...)
- 캡슐화: 클래스는 데이터와 메서드를 하나의 단위로 묶어서 외부에서 접근을 제한할 수 있다
- 상속: 클래스는 다른 클래스의 특성을 상속받아 재사용할 수 있다
- 다형성: 같은 인터페이스를 통해 서로 다른 클래스의 객체를 조작할 수 있다
- 추상화: 클래스는 객체의 복잡성을 숨기고 단순한 인터페이스를 제공한다
클래스의 기본 구조
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. 상속
- 의존성 관리:
- 의존성 주입: 객체가 다른 객체에 의존성을 직접 생성하지 않고 외부에서 주입받음. 이는 객체 간의 결합도를 낮추고, 더 유연하게 관리할 수 있게 함.
- 상속: 클래스가 다른 클래스를 확장하여 속성과 메서드를 물려받음. 이는 코드 재사용성을 높이고, 클래스 계층 구조를 형성함.
- 관계의 표현:
- 의존성 주입: “has-a” 관계를 나타냄. 예: Car 클래스는 Engine 객체를 가짐.
- 상속: “is-a” 관계를 나타냄. 예: Car 클래스는 Vehicle 클래스의 일종임.
- 유연성:
- 의존성 주입: 객체의 의존성을 쉽게 교체할 수 있으며, 테스트가 용이함. 예를 들어, Engine 클래스의 다른 구현체를 주입하여 테스트할 수 있음.
- 상속: 클래스 계층 구조가 깊어질수록 유연성이 떨어질 수 있음. 상속은 다중 상속을 지원하지 않으므로, 한 클래스가 여러 클래스를 상속받을 수 없음.