# Basics

## Basics

Nest 마이크로 서비스는 HTTP와 다른 **전송** 계층을 사용하는 응용 프로그램 유형입니다.

## Installation

먼저 필요한 패키지를 설치해야 합니다.

```bash
$ npm i --save @nestjs/microservices
```

## Overview

일반적으로 Nest는 여러 내장 전송기를 지원합니다. **요청-응답** 및 **이벤트 기반** 패러다임을 기반으로 하며 전체 통신 로직이 추상화 계층 뒤에 숨겨져 있습니다. 이를 통해 코드 라인을 변경하지 않고도 운송업체(transporter)간에 쉽게 전환 할 수 있습니다. 그러나 요청-응답 패러다임은 다양한 범위의 문제를 해결하도록 설계된 [Kafka](https://docs.confluent.io/3.0.0/streams/) 또는 [NATS 스트리밍](https://github.com/nats-io/node-nats-streaming)과 같은 로그 기반 지속성이 제공되는 스트리밍 플랫폼에서는 그다지 의미가 없습니다. 그럼에도 불구하고 **이벤트 기반** (단방향) 통신 또는 [응용 프로그램 컨텍스트](https://app.gitbook.com/application-context) 기능과 함께 사용할 수 있습니다.

## Getting started

마이크로 서비스를 생성하기 위해 우리는 `NestFactory` 클래스의 `createMicroservice()`메소드를 사용합니다.

```typescript
@@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()`메소드의 두 번째 인수는 옵션 객체입니다. 이 객체에는 두 멤버가 있을 수 있습니다.

| `transport` | Specifies the transporter (for example, `Transport.NATS`) |
| ----------- | --------------------------------------------------------- |
| `options`   | 트랜스포터 동작을 결정하는 트랜스포터별 옵션 객체                               |

&#x20;The `옵션` 개체는 선택한 트랜스포터에 따라 다릅니다. **TCP** 트랜스포터는 아래에 설명된 몇가지 속성을 가져옵니다.

| `host`          | 연결 호스트 이름     |
| --------------- | ------------- |
| `port`          | 연결 포트         |
| `retryAttempts` | 총 연결 시도 횟수    |
| `retryDelay`    | 연결 재시도 지연(ms) |

## Patterns

마이크로 서비스는 패턴을 통해 메시지와 이벤트를 모두 인식합니다. 패턴은 리터럴 객체 또는 문자열과 같은 일반 값입니다. 결국 모든 패턴이 직렬화되므로 데이터와 함께 네트워크를 통해 전송될 수 있습니다. 따라서, 리시버는 들어오는 메시지를 대응하는 핸들러와 쉽게 연관시킬 수있다.

## Request-response

요청-응답 통신 메커니즘은 다양한 외부 서비스간에 메시지를 **교환**해야 할 때 유용합니다. 또한 이 패러다임을 사용하면 서비스가 실제로 메시지를 수신했는지 확인할 수 있습니다.

서비스가 네트워크를 통해 데이터를 교환할 수 있도록 Nest는 두 채널을 생성합니다. 하나는 데이터 전송을 담당하고 다른 하나는 들어오는 응답을 수신합니다. 그러나 항상 그런 것은 아닙니다. 예를 들어 [NATS](https://nats.io/)와 같은 플랫폼은 이러한 기능을 기본적으로 제공하므로 자체적으로 수행할 필요가 없습니다.

기본적으로, 요청-응답 패러다임을 기반으로 하는 메시지 핸들러를 만들기 위해 우리는 `@nestjs/microservices` 패키지에서 가져온 `@MessagePattern()`데코레이터를 사용합니다.

```typescript
@@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` 메소드가 지원됩니다.

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

```

또한 `Observable`을 반환할 수 있으므로 스트림이 완료 될 때까지 값이 방출됩니다.

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

```

위의 메시지 처리기는 배열의 각 항목과 함께 **3 번** 응답합니다.

## Event-based

요청-응답 방법은 서비스간에 메시지를 지속적으로 교환해야 할 때 유용하지만 응답을 기다리지 않고 **이벤트**를 게시하려고 할 때 완전히 쓸모없는 불필요한 오버 헤드가 너무 많이 발생합니다. 예를 들어, 시스템의 이 부분에서 특정 상황이 발생했음을 다른 서비스에 알리려고 합니다. 따라서, 이벤트 기반 통신도 지원합니다.

이벤트 핸들러를 만들기 위해 우리는`@nestjs/microservices` 패키지에서 가져온 `@EventPattern ()`데코레이터를 사용합니다.

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

```

`handleUserCreated()`메소드는 `user_created` 이벤트를 청취하고 있습니다. 이벤트 핸들러는 클라이언트로부터 전달 된 `data`라는 단일 인수를 받습니다 (이 경우 네트워크를 통해 전송된 이벤트 페이로드).

## Client

메시지를 교환하거나 Nest 마이크로 서비스에 이벤트를 게시하기 위해 몇가지 방법으로 생성할 수 있는 `ClientProxy`클래스를 사용합니다. 먼저 정적 `register()`메소드를 노출하는 `ClientsModule`을 가져올 수 있습니다. 이 메소드는 모든 요소가 `name` (마이크로 서비스 식별자의 일종)과 마이크로 서비스 특정 옵션 (이것이`createMicroservice()` 방법에 전달된 것과 동일한 객체)을 갖는 매개 변수로 배열을 취합니다.

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

> info **힌트** `ClientsModule`은 `@nestjs/microservices` 패키지에서 가져옵니다.

모듈을 가져 오면 `@Inject()`데코레이터를 사용하여 `MATH_SERVICE`를 주입할 수 있습니다.

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

> info **힌트** `ClientProxy` 클래스는`@nestjs/microservices` 패키지에서 가져옵니다.

그럼에도 불구하고,이 접근법은 마이크로 서비스 구성을 비동기적으로 가져올 수 없습니다. 이 경우에, 우리는 클라이언트 인스턴스 인 [Custom provider](https://app.gitbook.com/%20techniques%20/%20custom-providers)를 등록하기 위해 `ClientProxyFactory`를 직접 사용할 수 있습니다 :

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

> info **힌트** `ClientProxyFactory`는 `@nestjs/microservices` 패키지에서 가져옵니다.

마지막으로 가능한 해결책은 `@Client()`속성 데코레이터를 사용하는 것입니다.

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

> info **힌트** `@Client()`는 `@nestjs/microservices` 패키지에서 가져옵니다.

그러나 데코레이터를 사용하는 것은 권장되지 않습니다 (테스트하기 어렵고 클라이언트 인스턴스를 공유하기가 어렵습니다).

`ClientProxy`는 **게으른**입니다. 즉시 연결을 시작하지 않습니다. 대신, 첫 번째 마이크로 서비스 호출 전에 설정되고 이후의 각 호출에서 재사용됩니다. 그러나 애플리케이션 부트 스트랩 프로세스를 지연시키고 연결을 수동으로 초기화하려면 `OnModuleInit` 라이프 사이클 후크 내에서 `connect()`메소드를 사용할 수 있습니다.

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

연결을 만들 수 없으면 `connect()`메소드는 해당 오류 객체와 함께 거부됩니다.

## Sending messages

`ClientProxy`는 `send()`메소드를 노출시킵니다. 이 메소드는 마이크로 서비스를 호출하기 위한 것이며 응답과 함께 `Observable`을 반환합니다. 따라서 방출된 값을 쉽게 가입할 수 있습니다.

```typescript
@@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()`입니다. 이 방법의 책임은 이벤트를 메시지 브로커에 공개하는 것입니다.

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

```

`emit()`메소드는 `pattern`과 `payload`라는 두 개의 인수를 취합니다. `pattern`은 `@EventPattern()` 데코레이터에 정의된 것과 동일해야하며, `payload`는 다른 마이크로 서비스로 전송하려는 이벤트 페이로드입니다.
