2025. 3. 1. 21:42ㆍNodejs
지난 글에서는 RSS 피드를 슬랙으로 알림을 보내는 기능을 구현해 보았습니다. 이번 글에서는 DynamoDB를 활용하여 중복 알림을 방지하는 방법을 살펴보겠습니다.
- 프로젝트 설정
- RSS 피드 가져오기
- 슬랙에 메시지 전송하기
- 중복 알림 방지하기
- 배포
1. DynamoDB 생성하기
AWS 콘솔에서 생성
DynamoDB를 생성하기 위해 AWS 콘솔에 접속한 후, DynamoDB 서비스로 이동합니다. 좌측 메뉴에서 ‘테이블’ 탭을 클릭한 뒤, 우측 상단의 ‘테이블 생성’ 버튼을 클릭합니다.
테이블 세부 정보는 아래와 같이 입력합니다.
- 테이블 이름 : 원하는 이름으로 하시면 됩니다. 저는 ‘feed-items’로 하겠습니다.
- 파티션 키 : 테이블에서 각 아이템을 고유하게 식별하는 기본 키입니다. 여기서는 파티션 키 이름을 ‘id’로 하고 타입은 문자열로 선택합니다.
- 정렬 키 : 파티션 키와 함께 사용되며, 동일한 파티션 키 내에서 데이터를 정렬하는 역할을 합니다. 여기서는 필요하지 않기 때문에 따로 입력하지 않겠습니다.
테이블 설정은 "기본 설정"을 사용하겠습니다.
- 테이블 클래스 : 두 가지 테이블 클래스 중 선택할 수 있습니다. “기본 설정”은 DynamoDB Standard를 사용합니다.
- DynamoDB Standard : 기본 테이블 클래스이며, 자주 엑세스하는 데이터에 최적화되어 있습니다. 읽기/쓰기 요청 비용이 저렴합니다. 주로, API 백엔드 서비스나 트랜잭션이 자주 발생하는 서비스에 사용합니다.
- DynamoDB Standard-IA(Infrequent Access) : 자주 엑세스되지 않는 데이터에 최적화된 테이블 클래스입니다. 저장 비용이 저렴합니다. 주로, 로그 데이터와 같이 백업 및 기록 보관을 위해 사용합니다.
- 용량 모드 : 테이블의 처리량을 관리하는 방식으로 두 가지 용량 모드 중 선택할 수 있습니다. “기본 설정”은 온디맨드 모드를 사용합니다.
- 온디맨드 모드 : 사전 용량 설정 없이, 요청이 발생할 때마다 자동으로 처리량을 조정하는 방식입니다. 사용한 만큼 비용을 지불하는 모델입니다. 자동 확장을 지원하기 때문에 트래픽이 예측 불가능한 경우 적합합니다.
- 프로비저닝 모드 : 미리 읽기/쓰기 처리량을 설정하고, 그 한도 내에서 운영하는 방식입니다. 일정한 트래픽이 예상되는 서비스에서 비용을 절감할 수 있습니다.
- 최대 읽기 용량 단위, 최대 쓰기 용량 단위 : 프로비저닝 모드를 사용할 경우 설정할 수 있습니다. “기본 설정”에서는 사용하지 않습니다.
- 최대 읽기 용량 단위 : 초당 처리할 수 있는 읽기 요청량을 의미합니다. DynamoDB에서는 RCU(Read Capacity Unit)라는 단위를 사용하며, 1 RCU는 4KB 크기의 데이터를 강한 일관성으로 읽을 때 초당 1회 처리할 수 있는 용량을 의미합니다. 강한 일관성 외에도 결과적 일관성과 트랜잭션 읽기를 제공합니다.
- 최대 쓰기 용량 단위 : 초당 처리할 수 있는 쓰기 요청량을 의미합니다. DynamoDB에서는 WCU(Write Capacity Unit)라는 단위를 사용하며, 1 WCU는 4KB 크기의 데이터를 초당 1회 처리할 수 있는 용량을 의미합니다. 일반 쓰기와 트랜잭션 쓰기를 제공하는데 각각의 처리량은 아래와 같습니다.
- 로컬 보조 인덱스 : 같은 파티션 키 내에서 추가적인 정렬 키를 설정할 수 있는 인덱스입니다. “기본 설정”에서는 사용하지 않습니다.
- 글로벌 보조 인덱스 : 기존 테이블과 다른 파티션 키와 정렬 키를 설정할 수 있는 인덱스입니다. “기본 설정”에서는 사용하지 않습니다.
- 암호화 키 관리 : 데이터를 저장할 때 암호화에 사용할 키를 설정하는 옵션입니다. “기본 설정”에서는 Amazon DynamoDB가 소유합니다.
- Amazon DynamoDB가 소유 : DynamoDB가 AWS KMS 키를 소유하고 관리합니다. 이 키의 사용에 대한 추가 요금이 부과되지 않습니다.
- AWS 관리형 키 : AWS가 소유하고 관리합니다. 콘솔에서 직접 확인 및 삭제가 불가능합니다. AWS KMS 요금이 적용됩니다.
- 고객 관리형 KMS 키 : 사용자가 직접 생성한 KMS 키를 사용합니다. 사용자의 계정에 저장되며, 사용자가 직접 소유하고 관리합니다. AWS KMS 요금이 적용됩니다.
Amazon DynamoDB가 소유하는 키는 DynamoDB에서만 사용할 수 있지만, AWS 관리형 키는 다른 서비스에서도 사용할 수 있습니다.
- 삭제 방지 : 테이블이 실수로 삭제되지 않도록 보호하는 기능입니다. 이 기능을 활성화하면 테이블을 삭제하려 할 때 AWS 콘솔이나 API에서 삭제 요청을 차단합니다. “기본 설정”은 비활성화입니다.
- 리소스 기반 정책 : 리소스 기반 정책은 특정 리소스(DynamoDB, S3 등)에 대한 엑세스 권한을 직접 설정하는 정책입니다. 여기서는 이 테이블에 접근할 수 있는 역할이나 계정을 설정할 수 있습니다. “기본 설정”에서는 사용하지 않습니다.
모든 설정을 마쳤다면, '테이블 생성'을 클릭합니다.
서버리스 프레임워크로 생성하고 관리하기
AWS 콘솔에서는 앞서 설명한대로 테이블을 생성하면 됩니다. 하지만 저는 서버리스 프레임워크를 사용하여 코드로 관리하려고 합니다. serverless.yml 파일의 resources 부분에 아래 설정을 추가하겠습니다. 더 많은 옵션과 세부 사항은 AWS CloudFormation DynamoDB 공식 문서를 참고하세요.
[serverless.yml]
...
resources:
Resources:
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
// 과금 정책
BillingMode: PAY_PER_REQUEST
// 테이블 클래스
TableClass: STANDARD
// 테이블 이름
TableName: feed-items
// 파티션 키
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
마지막으로, Lambda가 DynamoDB에 접근하기 위해 권한을 추가합니다. serverless.yml 파일의 provider 섹션에 DynamoDB에 대한 권한을 추가합니다. 이렇게 설정한 후 배포하면, DynamoDB 테이블이 정상적으로 생성된 것을 확인할 수 있습니다.
[serverless.yml]
...
provider:
...
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:DeleteItem
Resource:
- !GetAtt DynamoDBTable.Arn
2. 중복 알림 방지하기
이제 프로젝트로 돌아와서 DynamoDB를 통해 알림 내역을 관리하는 코드를 작성해보겠습니다.
먼저, Nodejs에서 DynamoDB를 사용하기 위한 라이브러리를 설치합니다. 여기서는 AWS의 공식 라이브러리인 @aws-sdk/client-dynamodb와 @aws-sdk/lib-dynamodb를 사용합니다.
$ pnpm add @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
- @aws-sdk/client-dynamodb : DynamoDB를 직접 호출할 수 있는 AWS SDK입니다.
- @aws-sdk/lib-dynamodb : JSON 데이터 처리가 용이한 DynamoDB Document Client를 제공하는 AWS SDK입니다.
라이브러리 설치를 완료했다면, 이제 DynamoDB의 데이터를 조회하고, 생성, 삭제하는 기능을 담당하는 서비스를 만듭니다. src/service/dynamodb.service.ts 파일을 생성한 후, 아래와 같이 작성합니다.
[src/service/dynamodb.service.ts]
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
DeleteCommand,
DynamoDBDocumentClient,
GetCommand,
PutCommand,
} from "@aws-sdk/lib-dynamodb";
import { isNil } from "@lib/validator";
class DyanmoDBService {
private readonly _client: DynamoDBDocumentClient;
private readonly _tableName: string;
constructor() {
this._client = DynamoDBDocumentClient.from(
new DynamoDBClient({
region: process.env.AWS_DYNAMODB_REGION!,
})
);
this._tableName = process.env.AWS_DYNAMODB_TABLE!;
}
async get(key: Record<string, any>): Promise<Record<string, any> | null> {
const getCommand = new GetCommand({
TableName: this._tableName,
Key: key,
});
const result = await this._client.send(getCommand);
const item = result.Item;
if (isNil(item)) {
return null;
}
return item;
}
async put(item: Record<string, any>): Promise<void> {
const putCommand = new PutCommand({
TableName: this._tableName,
Item: item,
});
await this._client.send(putCommand);
}
async delete(key: Record<string, any>): Promise<void> {
const deleteCommand = new DeleteCommand({
TableName: this._tableName,
Key: key,
});
await this._client.send(deleteCommand);
}
}
export const dynamodbService = new DyanmoDBService();
이제 GitHubReleaseNotificationService에서 DynamoDBService를 활용하여 알림 내역을 조회하고, 중복된 알림을 제거한 후, 슬랙에 메시지를 전송한 알림 내역을 저장하는 기능을 추가합니다.
[src/service/github-release-notification.ts]
...
// add
import { DyanmoDBService } from './dynamodb.service';
export class GitHubReleaseNotificationService {
...
// add
private async excludeSentFeeds(items: FeedItem[]): Promise<FeedItem[]> {
const newItems: FeedItem[] = [];
for await (const item of items) {
const found = await dynamodbService.get({ id: item.id });
if (isNil(found)) {
newItems.push(item);
}
}
return newItems;
}
// add
private async storeSentFeeds(items: FeedItem[]) {
for await (const item of items) {
await dynamodbService.put({ id: item.id });
}
}
async run(): Promise<void> {
const feedSources = await this.loadFeedSources();
for await (const feedSource of feedSources) {
const { packageName, sourceUrl, slackChannelId } = feedSource;
// RSS 피드 조회
const items = await this.parseRSS(sourceUrl);
// 중복 알림 제거
// add
const newItems = await this.excludeSentFeeds(items);
// 알림 전송
const slackMessages = this.generateMessages({
packageName,
// modify
items: newItems,
});
await slackService.sendAll({
channel: slackChannelId,
texts: slackMessages
});
// 알림 내역 저장
// add
await this.storeSentFeeds(newItems);
}
}
}
DynamoDB에서 사용하는 환경 변수를 추가하기 위해서 serverless.yml 파일에 아래 부분을 추가합니다.
[serverless.yml]
...
functions:
notifyAll:
handler: src/handler.notifyAll
events:
- schedule: rate(1 hour)
environment:
# add
AWS_DYNAMODB_REGION: ${env:AWS_DYNAMODB_REGION}
# add
AWS_DYNAMODB_TABLE: ${env:AWS_DYNAMODB_TABLE}
SLACK_OAUTH_TOKEN: ${env:SLACK_OAUTH_TOKEN}
지금까지 작성한 코드를 테스트 하기 위해 sls invoke 명령어를 사용하여 함수를 실행합니다. 여러 번 실행해보면, 이미 전송된 알림은 다시 전송되지 않는 것을 확인할 수 있습니다.
$ sls invoke local --function notifyAll
다음 글에서는
이번 글에서는 서버리스 프레임워크를 사용하여 DynamoDB 테이블을 생성하였습니다. 그리고 @aws-sdk/client-dynamodb, @aws-sdk/lib-dynamodb를 활용하여 DynamoDB에 접근하고, 중복 알림을 방지하는 기능을 구현하였습니다. 다음 글에서 완성된 코드를 배포하고, 배포된 함수를 실행하여 정상적으로 동작하는지 확인해보겠습니다.
2025.02.15 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (1) - 개요
2025.02.19 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (2) - 프로젝트 설정
2025.02.23 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (3) - RSS 피드 가져오기
2025.02.26 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (4) - 슬랙에 메시지 전송하기
2025.03.01 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (5) - 중복 알림 방지하기
'Nodejs' 카테고리의 다른 글
NestJS - @Res() 데코레이터와 Interceptor를 함께 사용하면서 겪었던 이슈 (0) | 2025.03.05 |
---|---|
슬랙으로 RSS 알림 보내기 (6) - 배포 (0) | 2025.03.03 |
슬랙으로 RSS 알림 보내기 (4) - 슬랙에 메시지 전송하기 (0) | 2025.02.26 |
슬랙으로 RSS 알림 보내기 (3) - RSS 피드 가져오기 (0) | 2025.02.23 |
슬랙으로 RSS 알림 보내기 (2) - 프로젝트 설정 (0) | 2025.02.19 |