데코레이터란 (Decorator)?
class, method, accessor, property, parameter에 사용할 수 있는 선언의 한 종류이다.
`@Decorator` 형식으로 사용을 할 수 있다. 이는 런타임에 호출되는 함수이다.
데코레이터 합성
데코레이터를 사용하다보면 호출 순서에 대해 혼란스러운 순간이 있다. 데코레이터는 내부 요소부터 외부 요소로 향하는 순서로 적용된다. 예를 들어 메서드의 매개변수에 대한 데코레이터가 적용된 후 메서드 자체 그리고 마지막에 클래스 전체에 대한 데코레이터가 적용된다.
같은 종류의 데코레이터의 경우도 유사하다. 수학의 합성함수와 동일하게 작동한다.
@f
@g
x
다음과 같이 데코레이터가 선언되어 있다면 f(g(x))와 동일하게 실행된다.
Typescript 와 NestJs에서 사용되는 데코레이터의 차이
typescript의 데코레이터는 메타 프로그래밍을 위한 하나의 도구이다. 하지만 NestJs 데코레이터는 NestJs의 기능을 확장하고 프레임워크의 특정 패턴과 통합을 위해 설계되었다. 예를 들어 NestJS의 데코레이터는 의존성 주입, 라우팅, 미들웨어, 가드, 컨트롤러와 같은 특정 기능을 쉽게 구현하기 위해 사용된다. 또한 프레임워크의 기능과 긴밀하게 통합되어 있다. 반면 Typescript 데코레이터는 함수 형태로 구현되어 커스터마이징할 수 있다. 때문에 Typescript의 데코레이터가 더 넓은 범위에서 다양하게 활용할 수 있다.
커스텀 데코레이터의 활용
- 여러 클래스에서 반복되는 공통 관심사가 있을 때 중복된 코드를 줄이는데 활용이 된다. 예를 들면, Response 형태에 통일성을 부여하거나 개인정보 보호를 위하여 특정 정보에 대하여 마스킹이 필요할 경우 활용할 수 있다.
NestJs에서 데코레이터를 등록하는 과정은 크게 3가지로 나눌 수 있다.
1. SetMetadata() 또는 defineMetadata() 함수로 데코레이터가 적용된 곳에 심볼을 메타데이터 키로 등록한다.
2. 모듈 초기화 시점에 DiscoveryService와 MetadataScanner로 모든 provider 클래스를 순회하며 1번에서 등록한 심볼을 메타데이터로 가지고 있는 대상을 찾는다.
3. 대상을 찾았다면 해당 함수를 실행한다.
**참고
- 기능적으로는 Reflect.setMetadata / Reflect.defineMetadata는 거의 동일하게 작동하고 동일한 인자를 받는다. 하지만 Reflect.defineMetadata는 Reflect API의 확정된 부분으로 추가적인 라이브러리를 프로젝트에 설치해야 사용할 수 있다.
데코레이터 프로젝트 적용 과정
- 특정 데이터를 마스킹을 해야하는 요구사항이 생겼다. 사용자의 권한에 따라 읽을 수 있는 데이터의 범위가 지정되어야했다. 수 많은 api의 response에 데이터 마스킹을 해야하기 때문에 효율적으로 요구 사항을 적용하는 방향을 고민해보았다.
1. 가장 먼저 데이터 마스킹이 필요한 api를 조사하였다. 데이터 마스킹이 필요한 필드들은 크게 이메일 / 이름이었다. 데이터의 타입에 따라 별도의 마스킹 방법이 필요했다. 또한 같은 정보임에도 response 필드가 제각각인 경우가 많았다. 예를 들어 어떤 end-point에서는 userName 다른 end-point에서는 firstName + lastName등등으로 다양한 경우가 많았다.
2. 처음에는 필드 종류 별로 최종 response dto를 return하기전에 데이터를 함수로 넘겨주는 방법을 생각했다. 이 방법은 생각보다 복잡했고 코드를 모두 살펴봐야한다는 문제점이 있었다. 또한 놓치는 부분이 분명 생길 것 같았다.
3. 필자의 프로젝트는 최종 response data에 대하여 DTO를 만들어 작업을하고 있다. 때문에 데코레이터를 사용하여 최종 responseDTO에만 수정을하는 방안을 고민했다. "@MaskingType(DATA_TYPE)" 형태로 custom decorator를 생성하였다.
데코레이터를 통해서 response dto class에 어떤 필드가 어떤 종류의 마스킹이 필요한지를 저장한다. 최종적으로 response dto class를 토대로 객체 인스턴스가 생성될 때 데코레이터를 토대로 관리자 권한에 따라 데이터 마스킹을 진행할 지 판단 후 이에 맞는 결과를 내려준다.
느낀점
- 생각보다 Typescript의 데코레이터의 개념이 혼란스러웠다. Typescript에 Decorator가 정식 기능으로 출시된지 얼마되지 않았어서 java와 같은 다른 언어의 decorator과는 다르게 다루기가 까다롭다는 생각이 들었다. 하지만 지금까지는 구현에 있어 decorator 사용하는 방향을 자주 고민하지는 않았지만, 이번 기능 적용을 통해 데코레이터를 다루면서 보다 다양한 상황에서 이를 응용해보아도 될 것 같았다.
'개발' 카테고리의 다른 글
지속 가능한 소프트웨어 (0) | 2024.08.15 |
---|---|
0. HTTP란? (1) | 2024.07.28 |
Generic Programming and Type Parameter (1) | 2023.11.12 |
Typescript Type narrowing (0) | 2023.11.12 |
Static and non-static(instance) methods (1) | 2023.11.12 |