성장, 그리고 노력

부족하더라도 어제보다 더 잘해지자. 노력은 절대 배신하지 않는다.

Typescript

[Typescript] 데코레이터(Decorator)

제이콥(JACOB) 2019. 12. 9. 23:50

(간단히 데레코이터 정의부터 살펴보자)

데코레이터

→ 새로운 프로그래밍 개념이 아니지만, 파사드 패턴(facade pattern : facade는 건물의 정면을 의미한다. 파사드 패턴은 소프트웨어 디자인 패턴의 하나로 통일된 인터페이스를 통해 복잡한 서브 시스템을 간단히 사용하도록 만든 패턴이다)이나 플라이웨이트 패턴(flyweight pattern : 객체 내부에서 참조하는 객체를 매번 만드는 것이 아니라, 없다면 만들고 있으면 기존 객체를 공유하여 메모리 사용을 최소화하는 패턴이다)과 같은 구조적 디자인 패턴의 하나이다.

→ 데코레이터 디자인 패턴은 클래스를 수정하지 않고 클래스의 기능을 확장할 수 있는 구조적 패턴의 하나이다. 이것은 별도의 클래스를 생성하고 이 새로운 클래스로 메인 클래스를 래핑(wrapping)하여 새로운 기능을 추가할 수 있게 함으로써 가능하다.

→ 이 패턴은 클래스에서 단일 책임 원칙을 지킬 수 있는지 확인하도록 한다. 

→ Java 또는 C#과 같은 언어로 작업한 경우 어노테이션이나 속성 형식으로 데코레이터를 사용했을 것이다. (@requestMapping(), @bean 이런걸 말하는 건가.. )

→ 데코레이터 클래스의 로직은 객체 생성시 실행되기 때문에 데코레이터 패턴을 사용하면 전체 기능에 신경 쓰지 않고 특정 인스턴스에 초점을 맞출 수 있다. 

→ Typescript는 데코레이터 패턴을 래핑하여 영향을 받는 클래스에 어노테이션을 추가하여 기능을 추가할 수 있다. 


처음 데코레이터를 접하게된 계기는 `Angular`를 공부하면서부터이다. 앵귤러에서는 데코레이터는 정말 너무 많이 나와서 익숙하다. 그러던 어느날 갑자기 궁금해졌다고나 할까. 스프링에서 쓰던 애노테이션(@, annotation) 같은 느낌인건가 궁금해져서 찾아봤다. 여튼 서론은 이러하고 이제 `데코레이터`에 대해 알아보자.

 

→ Typescript에서는 데코레이터가 1.5 버전에서 도입되었다.

→ 타입스크립트에서 데코레이터는 함수로 정의되며 클래스, 프로퍼티, 메서드에 추가하여 기능을 더할 수 있다. 

→ 다만 데코레이터는 실험적인 기능이기 때문에 tsconfig.json파일에서 옵션을 수정해야 한다.

 

뭐 간단한 특징 정도이다. 한 번 읽어보면 괜찮고 아래처럼 `tsconfig.json` 파일을 수정하자.

"compilerOptions": {
...,
	"experimentalDecorators": true,
...
}

옵션 설정을 안하면, 아래와 같은 오류가 나온다. 그런데 버그인지 설정해도 아래와 같은 오류가 나올때도 있다. (but 컴파일에는 문제는 없다.)

안녕 Warning!

타입스크립트의 데코레이터는 함수 정의일 뿐이므로 이론상으로 단지 Typescript 함수를 작성하기만 하면 된다. 유일한 차이점은 데코레이터가 받는 파라미터의 타입이다.

// 데코레이터 정의
function logger(target: Function) {
    // 데코레이터 로직 구현
}

// 데코레이터 Class에 적용
@logger
class Book {
    constructor(private title: string){}
}

→ 함수를 하나 정의하면서 파라미터 타입은 Function으로 한다.

→ @ (at) 기호는 타입스크립트에게 이것이 데코레이터임을 알려주고, 타입스크립트는 클래스 실행 시 플러그인 형태로 실행한다. 

→ 타입스크립트가 클래스를 실행할 때 클래스의 생성자를 데코레이터의 파라미터로 전달하므로, 위 예처럼 데코레이터를 사용할 때마다 파라미터를 명시적으로 전달하지 않아도 된다.

 

데코레이터 팩토리

위와 같은 방법이 데코레이터 구현의 유일한 방법이라면 매우 제한적으로 밖에 사용할 수 없을 것이다. 실제 애플레케이션에서는 클래스 또는 함수의 데코레이터에 일부 사용자 정의 값을 전달할 수 있는 유연성이 필요하다. 이게 데코레이터 팩토리가 필요한 이유다.

→ 데코레이터 팩토리를 사용하면 클래스의 값을 데코레이터로 전달할 때 추가로 파라미터를 정의할 수 있다. 위 코드를 리펙토링 해보자. 

// 데코레이터 정의
function logger(naeme: string) {
    return function(target: Function){
        console.log(`클래스: ${name}`);
    }
}

// 데코레이터 Class에 적용
@logger('Jacob')
class Book {
    constructor(private title: string){}
}

→ 클래스가 데코레이터에 전달해야 하는 파라미터가 있다면 원하는 만큼 파라미터를 추가로 정의하면 된다.

→ 이 함수는 클래스 데코레이터를 정의하는데 필요한 서명이 있는 함수를 반환하기 때문에 팩토리 함수라고 한다. 

반응형