AWS CDK+SQS를 통한 비동기 처리하기

Intro

백엔드 서버에서 빠르게 처리할 것과 아닌 것을 구분하는 것은 중요하다.

예를 들면 유저가 회원가입을 했을 때 가입처리는 빠르게 해주고 당장 급하지 않은 가입환영 메일을 비동기로 보내주는게 좋다.

이번에 message queue 사용을 고민한건 로그를 남기는 코드들 때문이었다.
로그 저장하는데 시간이 오래 걸리진 않지만 매번 동기로 로직을 처리한다는게 마음에 걸렸다. 어쨌든 매 api마다 조금씩 시간을 잡아먹고 그걸 합치면 절대 적지는 않기 때문이다.

현재 일부 기능에서 RabbitMQ로 메세지 큐를 사용하고 있는데 소프트웨어를 설치부터 서버를 구축하고 메세지 설정 등 해줘야 하는 작업이 꽤 많고 복잡하다.
그래서 이번에 꽤나 구축하기도 간단하고 기존에 사용중인 aws 서비스와 호환도 좋은 sqs를 적용했다.


AWS SQS Queue

AWS SQS(Simple Queue Service)는 AWS에서 제공하는 메세지 큐 서비스이다.
(SQS가 AWS에서 가장 오래된 서비스라고 함!)

AWS 아키텍쳐 구조

람다의 코드 중 비동기로 처리할 부분은 SQS Queue로 메세지를 보낸다.
Queue에서는 받은 메세지를 곧바로 다른 람다로 트리거를 해주는 방식이다.

참고로 Queue에서 메세지를 처리할 수 있는 방법은 다양한데 일반적으로 sdk를 사용해서 queue 메세지들을 Pull로 받고 처리해준다.


SQS Queue(Lambda trigger) vs Lambda invoke 비교

Lambda invoke를 사용해서 비동기를 처리하는 방법도 있다.
람다에서 또 다른 람다를 곧바로 호출하는 것으로 구현하기는 훨씬 간단한 방법이다.

SQS Queue 장점

  1. 안정성 (메세지 유실 우려 없음)
  2. 대량의 트래픽 처리 가능
  3. 확장성
  4. 메세지 보관 (메세지가 처리될 때까지 보관)

Lambda invoke 장점

  1. 단순성 (구현하기 쉬움)
  2. 비용 저렴

2가지 특징을 비교해보고 상황에 따라 선택하여 쓰면 된다.
트래픽이 많거나 중요한 데이터를 다루거나 관리를 정확하게 하고 싶으면 SQS Queue가 좋고, 트래픽이 적고 혹시나 유실된다해도 큰 문제가 없다면 lambda invoke만 써도 충분할 것이다.


Lambda에서 SQS로 메세지 보내기

import * as AWS from 'aws-sdk'

const sqs = new AWS.SQS()
const messageBody = {...}
const params = {
    MessageBody: JSON.stringify(messageBody),
    QueueUrl: 'YourQueueUrl', // 필수
}
await sqs.sendMessage(params).promise()

aws-sdk를 사용해서 람다에서 sqs queue로 메세제를 보냈다.
QueueUrl은 필수로 넣어줘야하는데 ‘https://sqs.ap-northeast-2.amazonaws.com/계정ID/큐이름’ 이런식으로 region, 계정ID, 큐이름 등의 조합으로 만들어지기 때문에 큐를 만들지 않더라도 예측 가능한 값이다.

messageBody에는 비동기로 처리할 때 필요한 데이터를 넣어주면 된다.


CDK(Cloud Development Kit)를 사용해서 SQS Queue 및 람다 트리거 설정

export class MyStack extends cdk.Stack {
    // 생략
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
        // 생략

        const lambda2 = new lambda.Function(this, "Lambda2", {
            ...this.defaultFnProps,
            code: lambda.Code.fromAsset(path.join("lambda/functions/lambda2")),
            handler: "index.handler",
            functionName: "MyStack-lambda2",
        });

        const myQueue = new sqs.Queue(this, "myQueue", {
            queueName: "myQueue1",
        });

        Lambda2.addEventSource(
            new SqsEventSource(myQueue, {
                batchSize: 1,
            })
        );
        // 생략
    }
}

CDK로 2번째 람다(트리거를 받는 람다)와 SQS Queue를 생성했다.
그리고 Lamdba의 addEventSource 메서드를 이용해서 큐에서 람다로 트리거를 설정했다.

CDK를 사용하지 않고 AWS 웹콘솔이나 CLI를 사용할 수도 있다.
하지만 CDK를 사용하면 버전이나 환경변수에 따라 구분되는 스택별로 쉽게 관리 가능하다.
무엇보다 모든 인프라를 코드로 관리하는 것이 가장 큰 장점이다.

댓글남기기