Dapr는 이식 가능한 이벤트 기반 런타임으로, 개발자가 탄력적인 상태 없는(stateless) 및 상태 있는(stateful) 애플리케이션을 쉽게 구축할 수 있게 해줍니다. 클라우드 플랫폼 또는 엣지 컴퓨팅 환경에서 실행될 수 있으며, 다양한 프로그래밍 언어와 개발 프레임워크를 지원합니다. Dapr은 개발자가 분산 시스템 문제에 신경 쓰지 않고 비즈니스 로직에만 집중할 수 있도록 하여 생산성을 크게 향상시킵니다. Dapr은 마이크로서비스 아키텍처 기반의 현대적인 클라우드 네이티브 애플리케이션 구축의 진입 장벽을 낮춥니다.
Dapr JavaScript SDK
JavaScript와 TypeScript에서 Dapr 애플리케이션을 구축하기 위한 클라이언트 라이브러리입니다. 이 클라이언트는 서비스 간 호출, 상태 관리, 발행/구독, 비밀 관리 등 공통 Dapr API를 추상화하며, 애플리케이션 구축을 위한 간단하고 직관적인 API를 제공합니다.
설치
Javascript SDK를 사용하려면 NPM에서 Dapr JavaScript SDK 패키지를 설치하세요:
npm install --save @dapr/dapr
️ dapr-client는 이제 더 이상 사용되지 않습니다. 자세한 내용은 #259를 참조하세요.
구조
Dapr Javascript SDK는 두 가지 주요 구성 요소로 이루어져 있습니다:
- DaprServer: 모든 Dapr 사이드카와 애플리케이션 간 통신을 관리합니다.
- DaprClient: 모든 애플리케이션과 Dapr 사이드카 간 통신을 관리합니다.
위 통신은 gRPC 또는 HTTP 프로토콜을 사용하도록 구성할 수 있습니다.
실전 예제
웹사이트 사용자 행동에 대한 통계 정보를 생성하는 작은 애플리케이션을 만들어 보겠습니다.
환경 및 프로젝트 구조 설정
npm install -g @nestjs/cli
nest new api
mv api nest-dapr
cd nest-dapr
nest generate app page-view
npm install dapr-client
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
decorators.ts 파일(apps/shared/decorators.ts)을 생성하여 모든 마이크로서비스가 우리가 작성할 인프라에서 혜택을 받을 수 있도록 합니다.
Dapr 의존성 주입
DaprClient와 DaprServer를 주입하려면 nest.js에 제공해야 합니다.
app.module.ts에서 DaprClient를 등록합니다:
providers: [
...
{
provide: DaprClient,
useValue: new DaprClient()
}
]
page-view.module.ts에 동일한 방식으로 DaprServer를 추가합니다:
providers: [
...
{
provide: DaprServer,
useValue: new DaprServer()
}
]
Dapr 구성 요소(RabbitMQ) 설정
docker compose를 사용하여 rabbitmq를 시작합니다:
version: '3.9'
services:
pubsub:
image: rabbitmq:3-management-alpine
container_name: 'pubsub'
ports:
- 5674:5672
- 15674:15672 #web port
Dapr 구성 요소를 구성해야 합니다. 루트 폴더에 component/pubsub.yml를 생성합니다:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: default
spec:
type: pubsub.rabbitmq
version: v1
metadata:
- name: host
value: 'amqp://guest:guest@localhost:5674'
각 Dapr 마이크로서비스에는 자체 config가 필요합니다.
api/dapr/config.yml:
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: api
namespace: default
page-view/dapr/config.yml:
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: page-view
namespace: default
API/Gateway 서비스
app.controller.ts에서 간단한 API인 /add-page-view를 공개합니다:
@Post('/add-page-view')
async prderAdd(@Body() pageViewDto: PageViewDto): Promise<void> {
try {
console.log(pageViewDto);
await this.daprClient.pubsub.publish('pubsub', 'page-view-add', pageViewDto);
} catch (e) {
console.log(e);
}
}
내부 구독 마이크로서비스
데이터를 큐에 게시한 후, 이를 수신하고 호출해야 합니다:
page-view.controller.ts에 다음을 추가합니다:
@DaprPubSubSubscribe('pubsub', 'add')
addPageView(data: PageViewDto) {
console.log(`addPageView executed with data: ${JSON.stringify(data)}`);
this.data.push(data);
}
이제 새로 만들어야 하는 데코레이터: @DaprPubSubscribe에 주목하세요.
@DaprPubSubscribe 데코레이터
shared/decorators.ts에서:
import { INestApplication } from '@nestjs/common';
import { DaprServer } from 'dapr-client';
export type PubsubMap = {
[pubSubName: string]: {
topic: string;
target: any;
descriptor: PropertyDescriptor;
};
};
export const DAPR_PUB_SUB_MAP: PubsubMap = {};
export const DaprPubSubSubscribe = (
pubSubName: string,
topic: string,
): MethodDecorator => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
DAPR_PUB_SUB_MAP[pubSubName] = {
topic,
target,
descriptor,
};
return descriptor;
};
};
export const useDaprPubSubListener = async (app: INestApplication) => {
const daprServer = app.get(DaprServer);
for (const pubSubName in DAPR_PUB_SUB_MAP) {
const item = DAPR_PUB_SUB_MAP[pubSubName];
console.log(
`Listening to the pubsub name - "${pubSubName}" on topic "${item.topic}"`,
);
await daprServer.pubsub.subscribe(
pubSubName,
item.topic,
async (data: any) => {
const targetClassImpl = app.get(item.target.constructor);
await targetClassImpl[item.descriptor.value.name](data);
},
);
}
};
애플리케이션 실행
실행:
docker-compose up -d # rabbitmq 시작
npm run dapr:api:dev # api/gateway 시작
npm run page-view:dev # 내부 마이크로서비스 dapr rabbitmq 큐 수신 시작
요청 실행:
curl --location --request POST 'http://localhost:7001/v1.0/invoke/api/method/statistics/add-page-view' \
--header 'Content-Type: application/json' \
--data-raw '{
"pageUrl" : "https://test.com/some-page",
"userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
}'