이제 더 깔끔하고 스마트한 코드를 작성하세요
TypeScript는 JavaScript를 기반으로 하는 강력한 정적 타입 프로그래밍 언어로서, 대규모 프로젝트에서 더욱 효율적인 도구 사용을 가능하게 합니다. JavaScript로 코드를 작성할 때 발생할 수 있는 여러 문제점을 해결하기 위해 개발되었으며, 타입 시스템을 통해 JavaScript의 약점을 보완합니다.
TypeScript 소스 코드에서 모든 값은 특정 타입을 가집니다. TypeScript는 각 값이 해당 타입의 규칙을 준수하는지 확인하며, 코드를 실행하지 않고도 소스 코드 내 오류를 검사합니다.
이러한 정적 타입 검사는 프로그램에서 사용되는 값의 타입을 기준으로 개발 단계에서 오류를 탐지하는 과정을 포함합니다.
코드의 명확성 및 가독성을 높이고 정적 타입 검사를 제공하는 것 외에도, TypeScript는 코드의 가독성, 재사용성, 유지보수성을 향상시키는 다양한 추가 기능을 제공합니다. 그 중 하나가 TypeScript 데코레이터입니다.
TypeScript 데코레이터

TypeScript 데코레이터는 런타임 시 코드의 동작을 수정하거나 개선하고, 코드에 메타데이터를 추가할 수 있는 기능입니다. 이를 통해 TypeScript에서 메타프로그래밍이 가능해집니다. 메타프로그래밍은 프로그램이 다른 프로그램을 데이터로 취급하여 자신의 동작을 변경할 수 있는 프로그래밍 기법입니다.
기본적으로 데코레이터는 특정 요소가 접근되거나 수정될 때 런타임에 실행되는 함수입니다. 데코레이터를 통해 해당 요소에 추가 기능을 부여할 수 있습니다. TypeScript 데코레이터는 클래스 선언, 메서드, 속성, 접근자(getter 및 setter), 그리고 메서드 매개변수에 적용할 수 있습니다.
TypeScript에서 데코레이터는 `@` 기호로 시작하며, 런타임 시 호출될 함수를 평가하는 표현식을 포함하는 `@expression` 형태를 가집니다. 데코레이터 사용의 일반적인 구문은 다음과 같습니다.
@decoratorName itemToDecorate
다음은 간단한 클래스 데코레이터의 예시입니다.
function logClass(target: Function) {
console.log("The Log Class Decorator has been called");
console.log("Class:", target);
}
@logClass
class MyClass {
constructor() {
console.log("An instance of MyClass has been created");
}
}
const myInstance = new MyClass();
위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
출력 결과:
The Log Class Decorator has been called Class: [class MyClass] An instance of MyClass has been created

`logClass()` 함수는 `Function` 타입의 `target`이라는 단일 인자를 받습니다. 여기서 `target`은 데코레이터가 적용되는 클래스의 생성자를 나타냅니다.
`MyClass` 클래스에 `logClass()`를 데코레이터로 적용하려면 `MyClass` 선언 바로 앞에 `@logClass`를 입력합니다. 데코레이터는 적용하려는 함수와 동일한 이름을 가져야 합니다.
`MyClass` 인스턴스가 생성되면, 출력에서 볼 수 있듯이 클래스 생성자 실행 외에도 데코레이터의 동작이 실행됩니다.
데코레이터는 현재 TypeScript에서 실험적인 기능으로 제공됩니다. 따라서 TypeScript에서 데코레이터를 사용하려면 `tsconfig.json` 파일의 컴파일러 옵션에서 `experimentalDecorators`를 활성화해야 합니다.
이를 위해 프로젝트 디렉토리에서 터미널을 열고 다음 명령을 실행하여 TypeScript 프로젝트 폴더에 `tsconfig.json` 파일을 생성합니다.
tsc --init
`tsconfig.json` 파일이 생성되면 파일을 열고 아래와 같이 `experimentalDecorators`의 주석을 제거합니다.

또한 JavaScript 대상 버전을 ES2015 이상으로 설정해야 합니다.
TypeScript 데코레이터의 중요성
좋은 코드는 가독성, 재사용성, 유지보수성을 기준으로 판단할 수 있습니다. 가독성이 높은 코드는 이해하기 쉽고, 개발자의 의도를 명확하게 전달하는 코드입니다.
반면 재사용 가능한 코드는 특정 기능 또는 컴포넌트를 수정 없이 애플리케이션의 다른 부분이나 새로운 프로젝트에서 재활용할 수 있도록 하는 코드입니다.
유지보수성이 뛰어난 코드는 수명 주기 동안 쉽게 수정, 업데이트, 수정할 수 있는 코드입니다.

TypeScript 데코레이터를 사용하면 코드의 가독성, 재사용성 및 유지보수성을 향상시킬 수 있습니다. 데코레이터를 통해 선언적인 구문으로 코드의 동작을 개선할 수 있습니다. 데코레이터 안에 로직을 캡슐화하고 필요한 코드 요소에 적용하여 로직을 실행할 수 있습니다.
이를 통해 코드를 더 쉽게 읽고 무슨 일이 일어나는지 파악할 수 있습니다. 데코레이터는 개발자의 의도를 명확히 전달하는 데 도움이 됩니다.
데코레이터는 일회성으로 사용되는 요소가 아니라 본질적으로 재사용이 가능합니다. 데코레이터를 한 번 생성하면 여러 곳에서 여러 번 사용할 수 있습니다.
데코레이터를 정의하고 가져와서 코드 동작을 수정하려는 곳 어디든 사용할 수 있습니다. 이는 코드베이스 내 논리 중복을 방지하여 코드 재사용성을 높입니다.
데코레이터는 코드에 높은 유연성과 모듈성을 제공하여, 다양한 기능을 독립적인 컴포넌트로 분리할 수 있게 합니다. 가독성과 재사용성이 높은 코드 작성을 가능하게 함으로써, TypeScript 데코레이터를 사용하면 유지보수하기 쉬운 코드를 만들 수 있습니다.
TypeScript 데코레이터의 유형
앞서 언급했듯이 TypeScript 데코레이터는 클래스, 클래스 속성, 클래스 메서드, 클래스 접근자, 그리고 클래스 메서드 매개변수에 적용할 수 있습니다. 데코레이터가 적용되는 요소에 따라 다양한 타입의 데코레이터가 존재합니다. 이러한 데코레이터에는 다음이 포함됩니다.
#1. 클래스 데코레이터
클래스 데코레이터는 클래스 정의를 관찰, 수정하거나 대체하는 데 사용됩니다. 클래스 데코레이터는 꾸미고 있는 클래스 선언 바로 앞에 위치합니다. 클래스 데코레이터는 데코레이팅 대상 클래스의 생성자에 적용됩니다. 런타임 시, 클래스 데코레이터는 데코레이팅 대상 클래스의 생성자를 유일한 인자로 하여 호출됩니다.
클래스 확장을 방지하는 데 사용되는 클래스 데코레이터 예시는 다음과 같습니다.
function frozen(target: Function) {
Object.freeze(target);
Object.freeze(target.prototype);
}
@frozen
class Vehicle {
wheels: number = 4;
constructor() {
console.log("A vehicle has been created");
}
}
class Car extends Vehicle {
constructor() {
super();
console.log("A car has been created");
}
}
console.log(Object.isFrozen(Vehicle));
클래스 확장을 방지하기 위해 `Object.freeze()` 함수를 사용하고, 고정할 클래스를 인자로 전달합니다. 데코레이터는 클래스에 이 기능을 추가하는 데 사용됩니다. `isFrozen()` 함수에 클래스를 전달하여 런타임 시 `Vehicle` 클래스가 고정되었는지 확인할 수 있으며, 코드의 출력은 아래와 같습니다.
true

#2. 속성 데코레이터
속성 데코레이터는 클래스 속성을 꾸미는 데 사용되며, 속성 선언 바로 앞에 위치합니다. 속성 데코레이터를 사용하여 속성 정의를 수정하거나 관찰할 수 있습니다.
런타임 시 데코레이터가 호출되며 두 개의 인자를 받습니다. 첫 번째 인자는 멤버가 정적인 경우 클래스의 생성자 함수이거나 인스턴스 멤버인 경우 클래스의 프로토타입입니다. 두 번째 인자는 멤버의 이름, 즉 데코레이팅 중인 속성입니다.
TypeScript에서 정적 멤버 앞에는 `static` 키워드가 붙습니다. 정적 멤버는 클래스를 인스턴스화하지 않고 접근할 수 있습니다. 인스턴스 멤버는 `static` 키워드가 없으며, 클래스 인스턴스가 생성된 후에만 접근할 수 있습니다.
속성 데코레이터의 예시는 다음과 같습니다.
function wheelsDecorator(target: any, propertyName: string) {
console.log(propertyName.toUpperCase());
}
class Vehicle {
@wheelsDecorator
wheels: number = 4;
constructor() {
console.log("A vehicle has been created");
}
}
위 코드를 실행한 결과는 다음과 같습니다.
WHEELS

#3. 메서드 데코레이터
메서드 선언 바로 앞에 위치하는 메서드 데코레이터는 메서드 정의를 관찰, 수정, 또는 대체하는 데 사용됩니다. 메서드 데코레이터는 세 가지 인자를 받습니다. 멤버가 정적인 경우 클래스의 생성자 함수, 인스턴스 멤버인 경우 클래스의 프로토타입이 첫 번째 인자로 전달됩니다.
두 번째 인자는 멤버의 이름이며, 마지막 인자는 멤버의 속성 설명자입니다. 속성 설명자는 객체 속성과 관련된 객체이며, 속성의 특성 및 동작에 대한 정보를 제공합니다.
메서드 데코레이터는 메서드 호출 전후에 특정 작업을 수행하고자 할 때 유용합니다. 또한 호출되는 메서드에 대한 정보를 기록하는 데에도 사용될 수 있습니다. 메서드가 더 이상 사용되지 않음을 사용자에게 알리고자 할 때 유용하게 활용할 수 있습니다.
메서드 데코레이터의 예시는 다음과 같습니다.
const logDeprecated = (target: any, methodName: string, descriptor: PropertyDescriptor) => {
console.log(`${methodName} has been deprecated`);
console.log(descriptor);
};
class Vehicle {
wheels: number = 4;
constructor() {
console.log("A vehicle has been created");
}
@logDeprecated
reFuel(): void {
console.log("Your vehicle is being refuelled");
}
}
출력 결과:
reFuel has been deprecated
{
value: [Function: reFuel],
writable: true,
enumerable: false,
configurable: true
}

#4. 접근자 데코레이터
TypeScript에는 `get`과 `set`이라는 두 가지 유형의 접근자 메서드가 있습니다. 접근자 메서드는 클래스 속성에 대한 접근을 제어하는 데 사용됩니다. 접근자 데코레이터는 이 두 접근자 메서드를 꾸미는 데 사용되며, 접근자 선언 바로 앞에 위치합니다. 접근자는 메서드와 동일하게 동작하므로 접근자 데코레이터는 메서드 데코레이터와 동일하게 동작합니다.
접근자 데코레이터의 예시는 다음과 같습니다.
const logWheels = (target: any, accessorName: string, descriptor: PropertyDescriptor) => {
console.log(`${accessorName} used to get the number of wheels`);
console.log(descriptor);
};
class Vehicle {
private wheels: number = 4;
constructor() {
console.log("A vehicle has been created");
}
@logWheels
get numWheels(): number {
return this.wheels;
}
}
출력 결과:
numWheels used to get the number of wheels
{
get: [Function: get numWheels],
set: undefined,
enumerable: false,
configurable: true
}

접근자 데코레이터를 사용할 때, 동일한 이름의 여러 `get`/`set` 접근자에 데코레이터를 적용할 수 없다는 점을 유의해야 합니다. 예를 들어, 위 코드 샘플에서 `set numWheels`라는 setter를 생성하는 경우 `logWheels` 데코레이터를 사용할 수 없습니다.
#5. 매개변수 데코레이터
매개변수 데코레이터는 매개변수가 메서드에 선언되었는지 여부를 관찰하는 데 사용되며, 매개변수 선언 앞에 위치합니다. 매개변수 데코레이터는 세 가지 인자를 받습니다. 첫 번째 인자는 정적 멤버의 경우 클래스의 생성자 함수, 인스턴스 멤버의 경우 클래스의 프로토타입입니다.
두 번째 인자는 멤버 이름(즉, 매개변수 이름)입니다. 세 번째 인자는 함수 매개변수 목록에서 해당 매개변수의 인덱스입니다. 즉, 매개변수 목록에서 첫 번째 매개변수의 위치가 0일 때, 해당 매개변수의 위치는 몇 번째인지 나타냅니다.
매개변수 데코레이터의 예시는 다음과 같습니다.
const passengerLog = (target: Object, propertyKey: string, parameterIndex: number) => {
console.log(`Decorator on ${propertyKey}'s parameter index ${parameterIndex}`);
};
class Vehicle {
private wheels: number = 4;
constructor() {
console.log("A vehicle has been created");
}
pickPassenger(location: string, numPassengers: string, @passengerLog driver: string) {
console.log(`${numPassengers} picked at ${location} by ${driver}`);
}
dropPassenger(driver: string, @passengerLog location: string, numPassengers: string) {
console.log(`${numPassengers} dropped at ${location} by ${driver}`);
}
}
출력 결과:
Decorator on pickPassenger's parameter index 2 Decorator on dropPassenger's parameter index 1

결론
TypeScript 데코레이터는 코드 가독성을 높이고 재사용 가능한 모듈식 코드를 작성하는 데 매우 유용하며, 데코레이터를 한 번 선언하여 여러 번 재사용할 수 있다는 장점이 있습니다. 또한 데코레이터는 코드 전반의 유지보수에 기여합니다.
아직 실험적인 기능이지만 데코레이터는 매우 유용하므로 반드시 숙지하는 것이 좋습니다.
TypeScript에서 문자열을 숫자로 변환하는 방법에 대해서도 읽어보실 수 있습니다.