Basics

Basics

Nest 마이크둜 μ„œλΉ„μŠ€λŠ” HTTP와 λ‹€λ₯Έ 전솑 계측을 μ‚¬μš©ν•˜λŠ” μ‘μš© ν”„λ‘œκ·Έλž¨ μœ ν˜•μž…λ‹ˆλ‹€.

Installation

λ¨Όμ € ν•„μš”ν•œ νŒ¨ν‚€μ§€λ₯Ό μ„€μΉ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

$ npm i --save @nestjs/microservices

Overview

일반적으둜 NestλŠ” μ—¬λŸ¬ λ‚΄μž₯ 전솑기λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€. μš”μ²­-응닡 및 이벀트 기반 νŒ¨λŸ¬λ‹€μž„μ„ 기반으둜 ν•˜λ©° 전체 톡신 둜직이 좔상화 계측 뒀에 숨겨져 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 μ½”λ“œ 라인을 λ³€κ²½ν•˜μ§€ μ•Šκ³ λ„ μš΄μ†‘μ—…μ²΄(transporter)간에 μ‰½κ²Œ μ „ν™˜ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μš”μ²­-응닡 νŒ¨λŸ¬λ‹€μž„μ€ λ‹€μ–‘ν•œ λ²”μœ„μ˜ 문제λ₯Ό ν•΄κ²°ν•˜λ„λ‘ μ„€κ³„λœ Kafka λ˜λŠ” NATS 슀트리밍과 같은 둜그 기반 지속성이 μ œκ³΅λ˜λŠ” 슀트리밍 ν”Œλž«νΌμ—μ„œλŠ” 그닀지 μ˜λ―Έκ°€ μ—†μŠ΅λ‹ˆλ‹€. κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³  이벀트 기반 (단방ν–₯) 톡신 λ˜λŠ” μ‘μš© ν”„λ‘œκ·Έλž¨ μ»¨ν…μŠ€νŠΈ κΈ°λŠ₯κ³Ό ν•¨κ»˜ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Getting started

마이크둜 μ„œλΉ„μŠ€λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•΄ μš°λ¦¬λŠ” NestFactory 클래슀의 createMicroservice()λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

@@filename(main)
import { NestFactory } from '@nestjs/core';
import { Transport } from '@nestjs/microservices';
import { ApplicationModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice(ApplicationModule, {
    transport: Transport.TCP,
  });
  app.listen(() => console.log('Microservice is listening'));
}
bootstrap();

info 힌트 마이크둜 μ„œλΉ„μŠ€λŠ” 기본적으둜 TCP ν”„λ‘œν† μ½œμ„ 톡해 λ©”μ‹œμ§€λ₯Ό λ“£κ³  μžˆμŠ΅λ‹ˆλ‹€.

createMicroservice()λ©”μ†Œλ“œμ˜ 두 번째 μΈμˆ˜λŠ” μ˜΅μ…˜ κ°μ²΄μž…λ‹ˆλ‹€. 이 κ°μ²΄μ—λŠ” 두 멀버가 μžˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

The μ˜΅μ…˜ κ°œμ²΄λŠ” μ„ νƒν•œ νŠΈλžœμŠ€ν¬ν„°μ— 따라 λ‹€λ¦…λ‹ˆλ‹€. TCP νŠΈλžœμŠ€ν¬ν„°λŠ” μ•„λž˜μ— μ„€λͺ…λœ λͺ‡κ°€μ§€ 속성을 κ°€μ Έμ˜΅λ‹ˆλ‹€.

Patterns

마이크둜 μ„œλΉ„μŠ€λŠ” νŒ¨ν„΄μ„ 톡해 λ©”μ‹œμ§€μ™€ 이벀트λ₯Ό λͺ¨λ‘ μΈμ‹ν•©λ‹ˆλ‹€. νŒ¨ν„΄μ€ λ¦¬ν„°λŸ΄ 객체 λ˜λŠ” λ¬Έμžμ—΄κ³Ό 같은 일반 κ°’μž…λ‹ˆλ‹€. κ²°κ΅­ λͺ¨λ“  νŒ¨ν„΄μ΄ μ§λ ¬ν™”λ˜λ―€λ‘œ 데이터와 ν•¨κ»˜ λ„€νŠΈμ›Œν¬λ₯Ό 톡해 전솑될 수 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ, λ¦¬μ‹œλ²„λŠ” λ“€μ–΄μ˜€λŠ” λ©”μ‹œμ§€λ₯Ό λŒ€μ‘ν•˜λŠ” ν•Έλ“€λŸ¬μ™€ μ‰½κ²Œ μ—°κ΄€μ‹œν‚¬ μˆ˜μžˆλ‹€.

Request-response

μš”μ²­-응닡 톡신 λ©”μ»€λ‹ˆμ¦˜μ€ λ‹€μ–‘ν•œ μ™ΈλΆ€ μ„œλΉ„μŠ€κ°„μ— λ©”μ‹œμ§€λ₯Ό κ΅ν™˜ν•΄μ•Ό ν•  λ•Œ μœ μš©ν•©λ‹ˆλ‹€. λ˜ν•œ 이 νŒ¨λŸ¬λ‹€μž„μ„ μ‚¬μš©ν•˜λ©΄ μ„œλΉ„μŠ€κ°€ μ‹€μ œλ‘œ λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν–ˆλŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ„œλΉ„μŠ€κ°€ λ„€νŠΈμ›Œν¬λ₯Ό 톡해 데이터λ₯Ό κ΅ν™˜ν•  수 μžˆλ„λ‘ NestλŠ” 두 채널을 μƒμ„±ν•©λ‹ˆλ‹€. ν•˜λ‚˜λŠ” 데이터 전솑을 λ‹΄λ‹Ήν•˜κ³  λ‹€λ₯Έ ν•˜λ‚˜λŠ” λ“€μ–΄μ˜€λŠ” 응닡을 μˆ˜μ‹ ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 항상 그런 것은 μ•„λ‹™λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ NATS와 같은 ν”Œλž«νΌμ€ μ΄λŸ¬ν•œ κΈ°λŠ₯을 기본적으둜 μ œκ³΅ν•˜λ―€λ‘œ 자체적으둜 μˆ˜ν–‰ν•  ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€.

기본적으둜, μš”μ²­-응닡 νŒ¨λŸ¬λ‹€μž„μ„ 기반으둜 ν•˜λŠ” λ©”μ‹œμ§€ ν•Έλ“€λŸ¬λ₯Ό λ§Œλ“€κΈ° μœ„ν•΄ μš°λ¦¬λŠ” @nestjs/microservices νŒ¨ν‚€μ§€μ—μ„œ κ°€μ Έμ˜¨ @MessagePattern()λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

@@filename(math.controller)
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';

@Controller()
export class MathController {
  @MessagePattern({ cmd: 'sum' })
  accumulate(data: number[]): number {
    return (data || []).reduce((a, b) => a + b);
  }
}

accumulate() ν•Έλ“€λŸ¬λŠ” cmd:'sum' νŒ¨ν„΄μ„ μΆ©μ‘±μ‹œν‚€λŠ” λ©”μ‹œμ§€λ₯Ό λ“£κ³  μžˆμŠ΅λ‹ˆλ‹€. νŒ¨ν„΄ ν•Έλ“€λŸ¬λŠ” ν΄λΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„° μ „λ‹¬λœ dataλΌλŠ” 단일 인수λ₯Ό μ·¨ν•©λ‹ˆλ‹€. 이 경우, λ°μ΄ν„°λŠ” λˆ„μ λ˜μ–΄μ•Ό ν•˜λŠ” 숫자의 λ°°μ—΄μž…λ‹ˆλ‹€.

Asynchronous responses

각 λ©”μ‹œμ§€ ν•Έλ“€λŸ¬λŠ” 동기식 λ˜λŠ” λΉ„λ™κΈ°μ‹μœΌλ‘œ 응닡할 수 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ async λ©”μ†Œλ“œκ°€ μ§€μ›λ©λ‹ˆλ‹€.

@@filename()
@MessagePattern({ cmd: 'sum' })
async accumulate(data: number[]): Promise<number> {
  return (data || []).reduce((a, b) => a + b);
}

λ˜ν•œ Observable을 λ°˜ν™˜ν•  수 μžˆμœΌλ―€λ‘œ 슀트림이 μ™„λ£Œ 될 λ•ŒκΉŒμ§€ 값이 λ°©μΆœλ©λ‹ˆλ‹€.

@@filename()
@MessagePattern({ cmd: 'sum' })
accumulate(data: number[]): Observable<number> {
  return from([1, 2, 3]);
}

μœ„μ˜ λ©”μ‹œμ§€ μ²˜λ¦¬κΈ°λŠ” λ°°μ—΄μ˜ 각 ν•­λͺ©κ³Ό ν•¨κ»˜ 3 번 μ‘λ‹΅ν•©λ‹ˆλ‹€.

Event-based

μš”μ²­-응닡 방법은 μ„œλΉ„μŠ€κ°„μ— λ©”μ‹œμ§€λ₯Ό μ§€μ†μ μœΌλ‘œ κ΅ν™˜ν•΄μ•Ό ν•  λ•Œ μœ μš©ν•˜μ§€λ§Œ 응닡을 기닀리지 μ•Šκ³  이벀트λ₯Ό κ²Œμ‹œν•˜λ €κ³  ν•  λ•Œ μ™„μ „νžˆ μ“Έλͺ¨μ—†λŠ” λΆˆν•„μš”ν•œ μ˜€λ²„ ν—€λ“œκ°€ λ„ˆλ¬΄ 많이 λ°œμƒν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ‹œμŠ€ν…œμ˜ 이 λΆ€λΆ„μ—μ„œ νŠΉμ • 상황이 λ°œμƒν–ˆμŒμ„ λ‹€λ₯Έ μ„œλΉ„μŠ€μ— μ•Œλ¦¬λ €κ³  ν•©λ‹ˆλ‹€. λ”°λΌμ„œ, 이벀트 기반 톡신도 μ§€μ›ν•©λ‹ˆλ‹€.

이벀트 ν•Έλ“€λŸ¬λ₯Ό λ§Œλ“€κΈ° μœ„ν•΄ μš°λ¦¬λŠ”@nestjs/microservices νŒ¨ν‚€μ§€μ—μ„œ κ°€μ Έμ˜¨ @EventPattern ()λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

@@filename()
@EventPattern('user_created')
async handleUserCreated(data: Record<string, unknown>) {
  // business logic
}

handleUserCreated()λ©”μ†Œλ“œλŠ” user_created 이벀트λ₯Ό μ²­μ·¨ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 이벀트 ν•Έλ“€λŸ¬λŠ” ν΄λΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„° 전달 된 dataλΌλŠ” 단일 인수λ₯Ό λ°›μŠ΅λ‹ˆλ‹€ (이 경우 λ„€νŠΈμ›Œν¬λ₯Ό 톡해 μ „μ†‘λœ 이벀트 νŽ˜μ΄λ‘œλ“œ).

Client

λ©”μ‹œμ§€λ₯Ό κ΅ν™˜ν•˜κ±°λ‚˜ Nest 마이크둜 μ„œλΉ„μŠ€μ— 이벀트λ₯Ό κ²Œμ‹œν•˜κΈ° μœ„ν•΄ λͺ‡κ°€μ§€ λ°©λ²•μœΌλ‘œ 생성할 수 μžˆλŠ” ClientProxy클래슀λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. λ¨Όμ € 정적 register()λ©”μ†Œλ“œλ₯Ό λ…ΈμΆœν•˜λŠ” ClientsModule을 κ°€μ Έμ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€. 이 λ©”μ†Œλ“œλŠ” λͺ¨λ“  μš”μ†Œκ°€ name (마이크둜 μ„œλΉ„μŠ€ μ‹λ³„μžμ˜ 일쒅)κ³Ό 마이크둜 μ„œλΉ„μŠ€ νŠΉμ • μ˜΅μ…˜ (이것이createMicroservice() 방법에 μ „λ‹¬λœ 것과 λ™μΌν•œ 객체)을 κ°–λŠ” 맀개 λ³€μˆ˜λ‘œ 배열을 μ·¨ν•©λ‹ˆλ‹€.

ClientsModule.register([
  { name: 'MATH_SERVICE', transport: Transport.TCP },
]),

info 힌트 ClientsModule은 @nestjs/microservices νŒ¨ν‚€μ§€μ—μ„œ κ°€μ Έμ˜΅λ‹ˆλ‹€.

λͺ¨λ“ˆμ„ κ°€μ Έ 였면 @Inject()λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•˜μ—¬ MATH_SERVICEλ₯Ό μ£Όμž…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

constructor(
  constructor(
    @Inject('MATH_SERVICE') private readonly client: ClientProxy,
  ) {}
)

info 힌트 ClientProxy ν΄λž˜μŠ€λŠ”@nestjs/microservices νŒ¨ν‚€μ§€μ—μ„œ κ°€μ Έμ˜΅λ‹ˆλ‹€.

κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³ ,이 접근법은 마이크둜 μ„œλΉ„μŠ€ ꡬ성을 λΉ„λ™κΈ°μ μœΌλ‘œ κ°€μ Έμ˜¬ 수 μ—†μŠ΅λ‹ˆλ‹€. 이 κ²½μš°μ—, μš°λ¦¬λŠ” ν΄λΌμ΄μ–ΈνŠΈ μΈμŠ€ν„΄μŠ€ 인 Custom providerλ₯Ό λ“±λ‘ν•˜κΈ° μœ„ν•΄ ClientProxyFactoryλ₯Ό 직접 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€ :

{
  provide: 'MATH_SERVICE',
  useFactory: (configService: ConfigService) => {
    const mathSvcOptions = configService.getMathSvcOptions();
    return ClientProxyFactory.create(mathSvcOptions);
  },
  inject: [ConfigService],
}

info 힌트 ClientProxyFactoryλŠ” @nestjs/microservices νŒ¨ν‚€μ§€μ—μ„œ κ°€μ Έμ˜΅λ‹ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ κ°€λŠ₯ν•œ 해결책은 @Client()속성 λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

@Client({ transport: Transport.TCP })
client: ClientProxy;

info 힌트 @Client()λŠ” @nestjs/microservices νŒ¨ν‚€μ§€μ—μ„œ κ°€μ Έμ˜΅λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•˜λŠ” 것은 ꢌμž₯λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ (ν…ŒμŠ€νŠΈν•˜κΈ° μ–΄λ ΅κ³  ν΄λΌμ΄μ–ΈνŠΈ μΈμŠ€ν„΄μŠ€λ₯Ό κ³΅μœ ν•˜κΈ°κ°€ μ–΄λ ΅μŠ΅λ‹ˆλ‹€).

ClientProxyλŠ” 게으λ₯Έμž…λ‹ˆλ‹€. μ¦‰μ‹œ 연결을 μ‹œμž‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λŒ€μ‹ , 첫 번째 마이크둜 μ„œλΉ„μŠ€ 호좜 전에 μ„€μ •λ˜κ³  μ΄ν›„μ˜ 각 ν˜ΈμΆœμ—μ„œ μž¬μ‚¬μš©λ©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λΆ€νŠΈ 슀트랩 ν”„λ‘œμ„ΈμŠ€λ₯Ό μ§€μ—°μ‹œν‚€κ³  연결을 μˆ˜λ™μœΌλ‘œ μ΄ˆκΈ°ν™”ν•˜λ €λ©΄ OnModuleInit 라이프 사이클 후크 λ‚΄μ—μ„œ connect()λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

@@filename()
async onModuleInit() {
  await this.client.connect();
}

연결을 λ§Œλ“€ 수 μ—†μœΌλ©΄ connect()λ©”μ†Œλ“œλŠ” ν•΄λ‹Ή 였λ₯˜ 객체와 ν•¨κ»˜ κ±°λΆ€λ©λ‹ˆλ‹€.

Sending messages

ClientProxyλŠ” send()λ©”μ†Œλ“œλ₯Ό λ…ΈμΆœμ‹œν‚΅λ‹ˆλ‹€. 이 λ©”μ†Œλ“œλŠ” 마이크둜 μ„œλΉ„μŠ€λ₯Ό ν˜ΈμΆœν•˜κΈ° μœ„ν•œ 것이며 응닡과 ν•¨κ»˜ Observable을 λ°˜ν™˜ν•©λ‹ˆλ‹€. λ”°λΌμ„œ 방좜된 값을 μ‰½κ²Œ κ°€μž…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

@@filename()
accumulate(): Observable<number> {
  const pattern = { cmd: 'sum' };
  const payload = [1, 2, 3];
  return this.client.send<number>(pattern, payload);
}

send()λ©”μ†Œλ“œλŠ” patternκ³Ό payloadλΌλŠ” 두 개의 인수λ₯Ό μ·¨ν•©λ‹ˆλ‹€. pattern은 @MessagePattern() λ°μ½”λ ˆμ΄ν„°μ— μ •μ˜λœ 것과 동일해야 ν•˜λ©°, payloadλŠ” λ‹€λ₯Έ 마이크둜 μ„œλΉ„μŠ€λ‘œ μ „μ†‘ν•˜λ €λŠ” λ©”μ‹œμ§€μž…λ‹ˆλ‹€.

Publishing events

또 λ‹€λ₯Έ 방법은 emit()μž…λ‹ˆλ‹€. 이 λ°©λ²•μ˜ μ±…μž„μ€ 이벀트λ₯Ό λ©”μ‹œμ§€ λΈŒλ‘œμ»€μ— κ³΅κ°œν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

@@filename()
async publish() {
  this.client.emit<number>('user_created', new UserCreatedEvent());
}

emit()λ©”μ†Œλ“œλŠ” patternκ³Ό payloadλΌλŠ” 두 개의 인수λ₯Ό μ·¨ν•©λ‹ˆλ‹€. pattern은 @EventPattern() λ°μ½”λ ˆμ΄ν„°μ— μ •μ˜λœ 것과 λ™μΌν•΄μ•Όν•˜λ©°, payloadλŠ” λ‹€λ₯Έ 마이크둜 μ„œλΉ„μŠ€λ‘œ μ „μ†‘ν•˜λ €λŠ” 이벀트 νŽ˜μ΄λ‘œλ“œμž…λ‹ˆλ‹€.

Last updated