Decorator 패턴
Decorator 패턴은 객체 지향 디자인 패턴 중 하나로, 객체에 동적으로 새로운 기능을 추가하기 위한 방법이다. 데코레이터 패턴을 사용하면 객체의 수정 없이도 객체의 기능을 확장하고, 코드의 재사용성과 유연성을 높일 수 있다.
타입스크립트에서는 데코레이터 패턴을 사용하여 클래스, 메소드, 프로퍼티 등에 데코레이터를 적용할 수 있다. 데코레이터는 클래스, 메소드, 프로퍼티 앞에 @ 기호를 붙이며, 함수로 구현된다. 데코레이터 함수는 대상이 되는 클래스, 메소드, 프로퍼티를 인자로 받으며, 이들을 수정하거나 대체할 수 있다.
예를 들어, 아래와 같은 클래스가 있다고 가정해보자.
class Car {
drive() {
console.log("Driving");
}
}
이 클래스에 데코레이터를 적용하여 새로운 기능을 추가해보자.
function withTurbo(target: any) {
target.prototype.drive = function() {
console.log("Driving with turbo");
};
}
@withTurbo
class Car {
drive() {
console.log("Driving");
}
}
위 코드에서, withTurbo 함수는 target 인자로 클래스를 받아 해당 클래스의 drive 메소드를 수정하여 새로운 동작을 추가한다. 그리고, @withTurbo 데코레이터를 Car 클래스에 적용하여 기존 drive 메소드를 withTurbo 함수가 수정한 새로운 drive 메소드로 대체한다.
데코레이터 함수는 여러 개를 조합하여 적용할 수도 있다. 예를 들어, withTurbo 데코레이터와 withNitro 데코레이터를 조합하여 Car 클래스에 적용하면, 두 데코레이터 함수가 순차적으로 실행되어 기존 drive 메소드를 수정한 새로운 메소드를 만들 수 있다.
function withTurbo(target: any) {
target.prototype.drive = function() {
console.log("Driving with turbo");
};
}
function withNitro(target: any) {
target.prototype.drive = function() {
console.log("Driving with nitro");
};
}
@withTurbo
@withNitro
class Car {
drive() {
console.log("Driving");
}
}
위 코드에서는, withTurbo 데코레이터가 먼저 실행되어 기존 drive 메소드를 수정한 새로운 메소드를 만들고, withNitro 데코레이터가 그 새로운 메소드를 다시 수정하여 더욱강화된 새로운 메소드를 만든다.
데코레이터를 사용하여 클래스에 새로운 프로퍼티나 메소드를 추가할 수도 있다.
Decorator(데코레이터)
예를 들어, 아래와 같은 클래스가 있다고 가정해보자.
class Car {
speed: number;
constructor(speed: number) {
this.speed = speed;
}
drive() {
console.log(`Driving at ${this.speed} km/h`);
}
}
이 클래스에 @maxSpeed 데코레이터를 적용하여 최대 속도를 설정할 수 있도록 해보자.
function maxSpeed(speed: number) {
return function(target: any) {
target.prototype.maxSpeed = speed;
};
}
@maxSpeed(200)
class Car {
speed: number;
maxSpeed: number;
constructor(speed: number) {
this.speed = speed;
}
drive() {
console.log(`Driving at ${this.speed} km/h`);
}
}
위 코드에서, maxSpeed 함수는 최대 속도를 인자로 받아 target 인자로 클래스를 받아 해당 클래스의 prototype 객체에 maxSpeed 프로퍼티를 추가한다. 그리고, @maxSpeed(200) 데코레이터를 Car 클래스에 적용하여 최대 속도를 200으로 설정한다.
이제, Car 객체를 생성하고 최대 속도를 출력해보자.
const car = new Car(100);
console.log(car.maxSpeed); // 200
위 코드에서는, Car 클래스의 인스턴스인 car 객체를 생성하고, car.maxSpeed 프로퍼티를 출력한다. 이 때, 생성된 car 객체는 @maxSpeed 데코레이터가 추가한 maxSpeed 프로퍼티를 가지게 된다.
데코레이터를 작성할 때는, 함수로 구현되며 대상이 되는 클래스, 메소드, 프로퍼티를 인자로 받는다. 함수의 반환값으로는 대상을 수정하거나 대체한 새로운 대상을 반환할 수 있다.
예를 들어, 아래와 같이 클래스에 @decorator 데코레이터를 적용할 수 있다.
@decorator
class MyClass {
// ...
}
또한, 메소드나 프로퍼티에도 @decorator 데코레이터를 적용할 수 있다.
class MyClass {
@decorator
myMethod() {
// ...
}
@decorator
myProperty = 123;
}
데코레이터는 여러 개를 적용할 수 있으며, 적용 순서는 위에서 아래로, 왼쪽에서 오른쪽으로이다.
예를 들어, 아래와 같이 데코레이터를 여러 개 적용할 수 있다.
@decorator1
@decorator2
class MyClass {
// ...
}
위 코드에서는, 먼저 decorator1 데코레이터가 MyClass 클래스에 적용되고, 그 다음에 decorator2 데코레이터가 MyClass 클래스에 적용된다.
데코레이터를 사용하면, 코드의 가독성과 재사용성을 높일 수 있다.또한, 타입스크립트의 강력한 타입 시스템과 결합하여, 코드의 안정성과 유지보수성을 향상시킬 수 있다.
타입스크립트 제공 데코레이터
타입스크립트에서는 다양한 데코레이터를 기본적으로 제공하며, 이를 사용하여 코드의 가독성과 유지보수성을 향상시킬 수 있다.
@deprecated
해당 메소드나 프로퍼티가 더 이상 사용되지 않는 것을 나타낸다. 이 데코레이터를 사용하면, 해당 메소드나 프로퍼티를 사용하는 코드에서 경고 메시지를 출력할 수 있다.
class MyClass {
@deprecated
oldMethod() {
// ...
}
}
const obj = new MyClass();
obj.oldMethod(); // 경고 메시지 출력
@sealed
해당 클래스의 상속을 금지하는 것을 나타낸다. 이 데코레이터를 사용하면, 해당 클래스를 상속하는 코드에서 컴파일 오류가 발생한다.
@sealed
class MyClass {
// ...
}
class MySubClass extends MyClass { // 컴파일 오류 발생
// ...
}
@readOnly
해당 프로퍼티가 읽기 전용임을 나타낸다. 이 데코레이터를 사용하면, 해당 프로퍼티를 수정하는 코드에서 컴파일 오류가 발생한다.
class MyClass {
@readOnly
prop: string = "hello";
}
const obj = new MyClass();
obj.prop = "world"; // 컴파일 오류 발생
@override
해당 메소드가 부모 클래스에서 정의된 메소드를 오버라이드하는 것임을 나타낸다. 이 데코레이터를 사용하면, 부모 클래스에서 정의된 메소드와 시그니처가 일치하지 않는 경우 컴파일 오류가 발생한다.
class MyBaseClass {
method() {}
}
class MySubClass extends MyBaseClass {
@override
method() {}
}
class AnotherSubClass extends MyBaseClass {
// @override 데코레이터가 없으면, 컴파일 오류가 발생하지 않음
method(arg: string) {} // MyBaseClass의 method와 시그니처가 다름
}
@suppressWarnings
해당 코드에서 발생하는 경고 메시지를 숨기는 것을 나타낸다. 이 데코레이터를 사용하면, 해당 코드에서 발생하는 경고 메시지를 무시할 수 있다.
class MyClass {
@suppressWarnings
method() {
// ...
}
}
@param 및 @return
함수의 매개변수와 반환값에 대한 설명을 제공한다. 이 데코레이터를 사용하면, 함수의 매개변수와 반환값에 대한 문서화를 보다 쉽게 할 수 있다.
function myFunc(
@param {number} x - 매개변수 x에 대한 설명
@param {string} y - 매개변수 y에 대한 설명
): string {
// ...
return result;
}
@return {string} - 반환값에 대한 설명
@namespace
해당 코드가 네임스페이스임을 나타낸다. 이 데코레이터를 사용하면, 코드의 네임스페이스를 명확하게 표시할 수 있다.
@namespace("MyNamespace")
class MyClass {
// ...
}
@abstract
해당 클래스나 메소드가 추상 클래스나 추상 메소드임을 나타낸다. 추상 클래스는 인스턴스를 만들 수 없으며, 추상 메소드는 구현이 누락된 메소드다.
@abstract
class MyBaseClass {
@abstract
method() {}
}
// 추상 클래스는 인스턴스를 만들 수 없습니다.
const obj = new MyBaseClass(); // 컴파일 오류 발생
class MySubClass extends MyBaseClass {
// 추상 메소드를 구현하지 않으면, 컴파일 오류가 발생한다.
method() {
// ...
}
}
위와 같이, 타입스크립트에서는 다양한 데코레이터를 제공하며, 이를 사용하여 코드를 더 가독성 있게 만들고, 문서화할 수 있다.
하지만 데코레이터를 너무 많이 사용하면 코드를 복잡하게 만들 수 있으므로, 적절한 사용이 필요하다.