Nodejs

 

1. Interceptor란?

Interceptor는 gRPC 호출 전/후에 특정 로직을 삽입할 수 있는 기능입니다. 예를 들어:

  • 요청마다 공통으로 필요한 Metadata를 추가

  • 호출 추적(trace-id) 삽입

  • 클라이언트 서비스 이름 전달

이런 기능들을 Interceptor에 정의해두면, 모든 서비스 호출에서 일관되게 적용할 수 있습니다.

 

 

2. traceInterceptor 구현

공통 모듈(@app/common/grpc/interceptor)에 traceInterceptor를 구현합니다. 이 Interceptor는 호출 시 client-service라는 Metadata를 자동으로 추가합니다.

import { InterceptingCall } from '@grpc/grpc-js';

export const traceInterceptor = (service: string) => (options, nextCall) => {
  return new InterceptingCall(nextCall(options), {
    start: function (metadata, listener, next) {
      metadata.set('client-service', service);
      next(metadata, listener);
    },
  });
};

이제 모든 gRPC 호출마다 서비스 이름이 자동으로 붙어 추적과 로깅에 활용할 수 있습니다.

 

 

3. constructMetadata 유틸리티

각 메서드 호출 시 trace-id, 호출 클래스, 호출 메서드 이름 등을 Metadata에 담아주는 유틸리티입니다.

import { Metadata } from '@grpc/grpc-js';
import { v4 } from 'uuid';

export const constructMetadata = (
  callerClass: string,
  callerMethod: string,
  prevMetadata?: Metadata,
) => {
  const metadata = prevMetadata ?? new Metadata();

  // trace-id 없으면 새로 생성
  const traceId = metadata.getMap()['trace-id'] ?? v4();

  metadata.set('trace-id', traceId.toString());
  metadata.set('client-class', callerClass);
  metadata.set('client-method', callerMethod);

  return metadata;
};

이 방식으로 각 요청마다 trace-id가 유지되고, 호출 경로를 쉽게 추적할 수 있습니다.

 

 

4. AppModule에서 Interceptor 적용

NestJS ClientsModule 설정에서 gRPC channelOptions에 Interceptor를 추가합니다.

ClientsModule.registerAsync({
  clients: [
    {
      name: USER_SERVICE,
      useFactory: (configService: ConfigService) => ({
        transport: Transport.GRPC,
        options: {
          channelOptions: {
            interceptors: [traceInterceptor('Gateway')],
          },
          package: UserMicroservice.protobufPackage,
          protoPath: join(process.cwd(), 'proto', 'user.proto'),
          url: configService.getOrThrow('USER_GRPC_URL'),
        },
      }),
      inject: [ConfigService],
    },
    // Product, Order 서비스에도 동일 적용
  ],
  isGlobal: true,
}),

Gateway에서 User, Product, Order 서비스로 호출할 때 자동으로 Interceptor가 적용됩니다.

 

 

5. AuthService와 Metadata 활용

AuthService는 constructMetadata를 활용하여 호출 시 필요한 Metadata를 구성합니다.

@Injectable()
export class AuthService implements OnModuleInit {
  authService: UserMicroservice.AuthServiceClient;

  constructor(
    @Inject(USER_SERVICE)
    private readonly userMicroservice: ClientGrpc,
  ) {}

  onModuleInit() {
    this.authService =
      this.userMicroservice.getService<UserMicroservice.AuthServiceClient>(
        'AuthService',
      );
  }

  register(token: string, registerDto: RegisterDto) {
    return lastValueFrom(
      this.authService.registerUser(
        { ...registerDto, token },
        constructMetadata(AuthService.name, 'register'),
      ),
    );
  }

  login(token: string) {
    return lastValueFrom(
      this.authService.loginUser(
        { token },
        constructMetadata(AuthService.name, 'login'),
      ),
    );
  }
}

이제 register와 login 호출마다 trace-id와 호출 정보가 Metadata에 자동으로 포함됩니다.

 

 

6. AuthController에서 Metadata 확인

Controller에서는 gRPC 요청이 들어올 때 전달된 Metadata를 확인할 수 있습니다.

@Controller('auth')
@UserMicroservice.AuthServiceControllerMethods()
export class AuthController implements UserMicroservice.AuthServiceController {
  constructor(private readonly authService: AuthService) {}

  registerUser(request: UserMicroservice.RegisterUserRequest) {
    console.log(' * microService user registerUser token:', request.token);

    if (request.token === null) {
      throw new UnauthorizedException('토큰을 입력해주세요!!!');
    }

    return this.authService.register(request.token, request);
  }

  loginUser(request: UserMicroservice.LoginUserRequest, metadata: Metadata) {
    console.log('로그인 시도 !!:', request);
    console.log('✅ Metadata !!:', metadata);

    if (request.token === null) {
      throw new UnauthorizedException('토큰을 입력해주세요!');
    }

    return this.authService.login(request.token);
  }
}

로그인 시도 시 Metadata에 포함된 client-service, trace-id 등을 확인할 수 있습니다.

 

 

 

7. 정리

  1. gRPC Interceptor를 구현해 client-service Metadata를 자동 삽입하는 방법

  2. constructMetadata 유틸리티로 trace-id 및 호출 정보 관리하기

  3. AppModule에서 Interceptor를 적용하는 방법

  4. AuthService와 Controller에서 Metadata 활용하기

 

 

about author

PHRASE

Level 60  라이트

남의 오이밭에서는 신을 고쳐 신지 말고, 남의 오얏나무 아래에서는 갓을 고쳐 쓰지 말라. -강태공

댓글 ( 0)

댓글 남기기

작성