0. 설치

npm install reflect-metadata

1. 파라미터 데코레이터 정의

import "reflect-metadata";

export const ParamSymbols = {
    query : Symbol("ParamQuery"),
    param : Symbol("ParamParam"),
} 

function InjectSymbol(symbol: Symbol, target: any, key: string | symbol, index: number) {
    const existingParameterDecorators = Reflect.getMetadata(symbol, target, key) || [];
    Reflect.defineMetadata(symbol, [...existingParameterDecorators, index], target, key);
}

/** HTTP Request Query */
export function ReqQuery(target: any, key: string | symbol, index: number) {
    InjectSymbol(ParamSymbols.query, target, key, index);
}

/** HTTP Request Params */
export function ReqParam(target: any, key: string | symbol, index: number) {
    InjectSymbol(ParamSymbols.param, target, key, index);
}

2. 메소드 데코레이터 정의

import { ParamSymbols } from "../decorator/param-decorator";

export function ControllerBody(target: any, name: string, descriptor: PropertyDescriptor) {

    // 원본 함수
    const originFunc = descriptor.value;
    
    // 파라미터 목록
    const paramTypes: any[] = Reflect.getMetadata('design:paramtypes', target, name);
    
    // 데코레이터 적용 인덱스 위치
    const posQueryDeco : number[] = Reflect.getMetadata(ParamSymbols.query, target, name);
    const posParamDeco : number[] = Reflect.getMetadata(ParamSymbols.param, target, name);

    const wrappedFunc = async function (...args: any[]) {

        // 각 파라미터마다 사용된 데코레이터에 알맞게 처리
        paramTypes.forEach((paramType: any, index: number) => {
        
            if(posQueryDeco?.includes(index)) {
                args[index] = {}; // Request Query
            }
            else if(posParamDeco?.includes(index)) {
                args[index] = {}; // Request Param
            }
        });
        
        return await originFunc.apply(this, args);
    };

    descriptor.value = wrappedFunc;
}

3. 사용 예시

@ControllerBody
public static async Func(
    @ReqQuery q: any,
    @ReqParam p: any,
    a1: any,
    b2: any,
) {
    // ...
    return 1;
}