๋ฒ์
Spring Boot 3
AWS SDK for java v2
firebase-admin 6.5.0
0. ๋์ ๋ฐฐ๊ฒฝ
๊ธฐ์กด ๊ตฌ์กฐ
๊ธฐ์กด์๋ ํธ์ ์๋ฆผ ์์ฒญ์ ๋ฐ๋ผ์ ์๋ฒ ๋ด์์ ๋ฉ์ธ์ง๋ฅผ ์์ฑํ๊ณ Firebase๋ก ๋ฉ์ธ์ง๋ฅผ ์ ์กํ๋ค.
ํ์ง๋ง, ์ฌ๊ธฐ์ ํธ์ ์๋ฆผ์ ๋ณด๋ผ ์ฌ์ฉ์๊ฐ ๋ง๋ช , 10๋ง๋ช , 100๋ง๋ช ์ด ๋๋ค๋ฉด...
๋ชจ๋ ์ฌ์ฉ์์ ๋ํ ์๋ฆผ์ ์ผ๊ด์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํ๋ค.
์ผ๊ด ์ฒ๋ฆฌ๋ฅผ ์ํ Firebase์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณด์.
๋จผ์ ํ๊บผ๋ฒ์ ๋ณด๋ด๋ sendAll์ Deprecate๋์๊ณ , ๋์ sendEach๋ฅผ ์จ์ผํ๋ค.
sendEach()๋ ๊ฐ ํ ํฐ๋ณ ์ฌ๋ฌ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ผ ์ ์์ง๋ง, ๊ฐ ๋ฉ์ธ์ง๋ง๋ค ๋ณ๋์ HTTP ์์ฒญ์ ๋ง๋ค์ด๋ธ๋ค.
10๋ง๊ฐ์ ์์ฒญ์ด๋ฉด 10๋ง๊ฐ์ HTTP ์์ฒญ์ด ์์ฑ๋๋ค๋ ๊ฒ์ด๋ค.
์ฌ์ง์ด, message์ ์์ 500๊ฐ๊ฐ ์ ํ์ด๊ธฐ ๋๋ฌธ์ 500๊ฐ์ฉ ๋๋ ์ ํธ์ถํด์ผ ํ๋ค.
sendEachAsync()๋ ๋น๋๊ธฐ๋ก ๋์ํ์ง๋ง ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ง์ฐฌ๊ฐ์ง์ด๋ค.
๊ฐ์ ํ ๊ตฌ์กฐ
๋ฐ๋ผ์ ๋๋ ํ ํฐ๊ณผ ํธ์ ์๋ฆผ ๋ด์ฉ์ ๋ด์ ๋ฉ์ธ์ง๋ฅผ SQS์ ์ ์ฌํ๊ณ ,
SQS์ ๋ฉ์ธ์ง๊ฐ ๋์ฐฉํ๋ฉด Lambda๊ฐ ํธ๋ฆฌ๊ฑฐ๋์ด ํ์ด์ฌ ํจ์๊ฐ ์คํ๋๋ ๊ตฌ์กฐ๋ก ๊ฐ์ ํด ๋ณด๋ ค๊ณ ํ๋ค.
firebase_admin ๋ชจ๋์ด ์ค์น๋์ด ์๋ AWS Lambda์์ ํธ์ ์๋ฆผ์ ์ฒ๋ฆฌํ๋ค.
๊ธฐ์กด ๊ตฌ์กฐ์๋ ๋ค๋ฅด๊ฒ, ์ ์กํ๋ ค๋ token์ ์์ด ๋ง๋๋ผ๋ ์ ํ์ด ์๊ณ , token์์ ์๊ด ์์ด ์๋ฒ ์ ์ฅ์์๋ ๋ฌด์กฐ๊ฑด 1๋ฒ์ ์์ฒญ์ผ๋ก๋ง ํด๊ฒฐํ๋ ค๊ณ ํ๋ค.
๋ง ๋ช ์ ์ฌ์ฉ์์๊ฒ ํธ์์๋ฆผ์ ์ ์กํด๋ ํ ๋ฒ์ HTTP ์์ฒญ์ผ๋ก ํด๊ฒฐํ๋ค๋ ๊ฒ์ด๋ค.
ํด๋น ๊ตฌ์กฐ๋ ํธ์ ์๋ฆผ ์ ์ก์ด ์์ ํ ๋ถ๋ฆฌ๋์ด, ๋ค์๊ณผ ๊ฐ์ ํจ๊ณผ๋ฅผ ๊ธฐ๋ํด๋ณผ ์ ์๋ค.
- ํ์ฅ์ฑ (Scalability)
- AWS SQS๋ ๋์ ์ฒ๋ฆฌ๋๊ณผ ํ์ฅ์ฑ์ ์ ๊ณตํ๋ค. ๋ฉ์์ง ์์ด ์ฆ๊ฐํด๋ ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
- Lambda ํจ์๋ ์๋์ผ๋ก ์ค์ผ์ผ๋ง๋์ด ๋์์ ๋ง์ ํธ์ ์๋ฆผ์ ์ฒ๋ฆฌํ ์ ์๋ค.
- ๋ถ์ฐ ์ฒ๋ฆฌ (Decoupling)
- ๋ฉ์ธ ์๋ฒ์ ํธ์ ์๋ฆผ ์ฒ๋ฆฌ ๋ก์ง์ ๋ถ๋ฆฌํจ์ผ๋ก์จ ์์คํ ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๊ณ ์ ์ง๋ณด์์ฑ์ ๋์ธ๋ค.
- ์๋ฒ์ ๋ถํ๋ฅผ ์ค์ด๊ณ , ํธ์ ์๋ฆผ ์คํจ๊ฐ ์๋ฒ์ ๋ค๋ฅธ ๊ธฐ๋ฅ์ ์ํฅ์ ์ฃผ์ง ์๋๋ค.
- ์์ ์ฑ๊ณผ ๋ด๊ตฌ์ฑ (Reliability and Durability)
- SQS๋ ๋ฉ์์ง๋ฅผ ์์ ํ๊ฒ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ๋ค. ์ผ์์ ์ธ ๋คํธ์ํฌ ๋ฌธ์ ๋ ์๋น์ค ์ค๋จ ์์๋ ๋ฉ์์ง ์์ค ์์ด ์ฌ์๋ํ ์ ์๋ค.
- ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊น
(Monitoring and Logging)
- AWS ์๋น์ค๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ CloudWatch ๋ฑ์ ํตํด ์ฝ๊ฒ ๋ชจ๋ํฐ๋งํ๊ณ ๋ก๊ทธ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค.
- ๋น๋๊ธฐ ์ฒ๋ฆฌ (Asynchronous Processing)
- ํธ์ ์๋ฆผ ์ ์ก์ ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํจ์ผ๋ก์จ ๋ฉ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ต ์๊ฐ์ ๊ฐ์ ํ ์ ์๋ค.
- ์ฌ์๋ ๋งค์ปค๋์ฆ (Retry Mechanism)
- SQS์ ๊ฐ์์ฑ ํ์์์๊ณผ DLQ๋ฅผ ์ด์ฉํด ์คํจํ ๋ฉ์์ง๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฒ๋ฆฌํ ์ ์๋ค.
1. IAM ์์ฑ
๋จผ์ SQS์ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ผ ๊ถํ์ด ํ์ํ๋ฐ, ์ด๋ฅผ IAM์์ ๋ง๋ค์ด์ค๋ค.
์๋ก์ด ์ฌ์ฉ์๋ฅผ ์์ฑํ๋ฉด์, AmazonSQSFullAccess ๊ถํ์ ๊ฐ์ง๊ณ ์๋ ์ฌ์ฉ์๋ฅผ ๋ง๋ค์ด์ค๋ค.
์ฌ์ฉ์๊ฐ ์์ฑ๋๋ค๋ฉด, ์์ธ์ค ํค๋ฅผ ๋ง๋ค์ด์ค๋ค.
์์ธ์ค ํค๋ ํ ์คํธ ์์๋ง "AWS ์ธ๋ถ์์ ์คํ๋๋ ์ ํ๋ฆฌ์ผ์ด์ " ์ผ๋ก ๋ง๋ค์ด์ฃผ๊ณ ,
์ค์ ๋ฐฐํฌํ ํ๊ฒฝ์ด AWS EC2 ๋ด๋ผ๋ฉด "AWS ์ปดํจํ ์๋น์ค์์ ์คํ๋๋ ์ ํ๋ฆฌ์ผ์ด์ "์ ํค๋ฅผ ์ฌ์ฉํด์ฃผ๋๊ฒ ์ข๋ค.
์์ธ์ค ํค๋ฅผ ์์ฑํ๋ฉด 1ํ์ฑ์ผ๋ก ์์ธ์ค ํค์ ๋น๋ฐ ์์ธ์ค ํค๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ณ ์ด ๋ชจ์ ๋๋ค.
2. SQS (Simple Queue Service) ์ธ์คํด์ค ์์ฑ
์ด์ SQS ์์ ๋๊ธฐ์ด์ ์๋ก ์์ฑํด์ฃผ๋ฉด ๋๋ค.
ํ์ ๋ค์ด๊ฐ ๋ฉ์ธ์ง์ ์ฑ๊ฒฉ์ ๋ฐ๋ผ ๊ตฌ์ฑ์ ํด์ฃผ๋ฉด ๋๋๋ฐ,
๋ด ์๋น์ค์์ ์ฌ์ฉํ ๋ฉ์ธ์ง๋ 1์๊ฐ์ด ์ง๋๋ฉด ์๋ฏธ ์๋ ๋ฉ์ธ์ง์ด๊ธฐ ๋๋ฌธ์ ๋ณด์กด ๊ธฐ๊ฐ์ 1์๊ฐ์ผ๋ก ์ค์ ํ๋ค.
์ฌ๊ธฐ์ ๋๊ธฐ์ด์ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ผ ์ ์๋ ์ฌ์ฉ์์ ๋ฐฉ๊ธ ๋ง๋ค์๋ IAM์ Role์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
ARN Role์ IAM ์ฌ์ฉ์์ ๋ค์ด๊ฐ์ ๋ณต์ฌํ ์ ์๋ค.
3. Lambda ํจ์ ์์ฑ
๋ค์์ผ๋ก Lambda ํจ์๋ฅผ ์์ฑํ๋ฉด ๋๋๋ฐ, ๋ฐํ์ ์ธ์ด๋ ํ์ด์ฌ์ผ๋ก ํ๋๊ฒ ๊ฐ์ฅ ์ข๋ค.
ํ์ด์ฌ์ firebase_admin ๋ชจ๋์ด ๊ฐ์ฅ ๊ฐ๋ณ๊ณ ์ค์ ํ๊ธฐ ๊ฐํธํ๊ธฐ ๋๋ฌธ์ด๋ค.
firebase-admin ๋ชจ๋ ์ค์น
๋ค์์ผ๋ก firebase admin ๋ชจ๋์ ํจ์์ ์ค์นํด๋ณด์.
๋จผ์ python์ผ๋ก ๋ ๋น ๋๋ ํ ๋ฆฌ๋ฅผ ์์ฑํ๋ค.
โผ๏ธ Lambdaํจ์์์ ๋ชจ๋๋ก ์ธ์ํ๊ธฐ ์ํด์๋ ์ต์์ ํด๋์ ์ด๋ฆ์ด ๊ผญ python์ด์ด์ผ ํ๋ค!
pip์ ์ด์ฉํ์ฌ firebase-admin ์์ค๋ฅผ ๊ฐ์ ธ์จ๋ค.
$ pip install firebase-admin -t {๊ฒฝ๋ก}/python
์์ค๊ฐ ์ค์น๋์๋ค๋ฉด, ๊ทธ๋๋ก ์์ถํด์ค๋ค.
AWS Lambda๋ก ๋์์ ๊ณ์ธต์ ์๋ก ์์ฑํด์ค๋ค.
๊ณ์ธต์๋ .zip ํ์ผ์ ์ ๋ก๋ํ์ฌ ์์ฑํ ์ ์๋ค.
๋ฐฉ๊ธ ์์ถํ๋ zipํ์ผ์ ์ ๋ก๋ํ๊ณ , Lambdaํจ์์ ํธํ ์ํคํ ์ฒ์ ์ธ์ด๋ฅผ ์ ํํด์ฃผ๋ฉด ๋๋ค.
์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋์๋ค๋ฉด ๋ฒ์ ์ ํ์ธํ๋ค. (์ฒ์ ๋ง๋ค์๋ค๋ฉด 1์ผ ๊ฒ์ด๋ค.)
์ด์ ํจ์๋ก ๋์์, ์๋ก์ด ๊ณ์ธต์ ์์ฑํด์ค๋ค.
๋ฐฉ๊ธ ๋ง๋ค์๋ ๊ณ์ธต์ ์ถ๊ฐํด์ค๋ค.
์ด๋ก์จ ํด๋น ํจ์์ firebase_admin ๋ชจ๋์ด ์ค์น๋์๋ค.
ํธ๋ฆฌ๊ฑฐ ์ถ๊ฐ
์ด์ ํด๋น ํจ์๊ฐ ์คํ๋๊ธฐ ์ํ ํธ๋ฆฌ๊ฑฐ๋ก, SQSํ ๋นํด์ฃผ๋ฉด ๋๋ค.
๊ทธ ์ ์ ๊ถํ์ ์ฐ๊ฒฐํด์ผ ํ๋๋ฐ,
ํด๋น ํจ์์ ๊ตฌ์ฑ-๊ถํ์ ๋ค์ด๊ฐ์ Role ๋ก ๋ค์ด๊ฐ๋ค.
์ ์ฑ ์ฐ๊ฒฐ์ ์ ํํ๋ค.
AWSLambdaSQSQueueExecutionRole๋ฅผ ์ถ๊ฐํด์ค๋ค.
์ ์ฑ ์ด ์ถ๊ฐ๋์๋ค๋ฉด ํจ์๋ก ๋์์ ํธ๋ฆฌ๊ฑฐ๋ฅผ ์ถ๊ฐํ๋ค.
์ ์ ๋ง๋ค์๋ SQS๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ฉด ๋๋ค.
ํธ๋ฆฌ๊ฑฐ๊ฐ ์ฐ๊ฒฐ๋์๋ค.
์ด์ ํด๋น SQS ํ์ ๋ฉ์ธ์ง๊ฐ ๋ค์ด์ค๋ฉด Lambda ํจ์๋ก ๋ฉ์ธ์ง๊ฐ ์ ๋ฌ๋ ๊ฒ์ด๋ค.
ํจ์ ์ฝ๋ ์์ฑ
์ด์ ํด๋น ํจ์๋ก ๋ฉ์ธ์ง๊ฐ ์ ๋ฌ๋๋ฉด ์คํ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๋๋ค.
๋จผ์ ํด๋น ํด๋์ firebase ํ๋ก์ ํธ์์ ๋ฐ์ ํค ์ ๋ณด (json)ํ์ผ์ ๋ฃ์ด๋์ด์ผ ํ๋ค.
import firebase_admin as admin
from firebase_admin import credentials
from firebase_admin import messaging
# Firebase ๊ณ์ ์ ๋ณด ๋ก๋
credential = credentials.Certificate('waitherAccountServiceKey.json')
admin.initialize_app(credential)
# ๋ฉ์ธ ํจ์
def lambda_handler(event, context):
parsed_payload = parse_sqs_message(event)
return send_notification(parsed_payload)
# ํธ์ ์๋ฆผ ์ ์ก
def send_notification(payload):
responses = []
for token in payload['tokens']:
message = messaging.Message(
notification=messaging.Notification(
title=payload['title'],
body=payload['content']
),
token=token
)
response = messaging.send(message)
responses.append(response)
return responses
# SQS ๋ฉ์์ง ํ์ฑ
def parse_sqs_message(sqs_message):
title = sqs_message['Records'][0]['messageAttributes']['title']['stringValue']
tokens = sqs_message['Records'][0]['messageAttributes']['tokens']['stringValue']
tokens = tokens[1:-1].split(", ")
content = sqs_message['Records'][0]['body']
return {"title": title, "content": content, "tokens": tokens}
๋ฉ์ธ์ง๊ฐ ์ ์ก๋๋ฉด lambda_handler ํจ์๊ฐ ์คํ๋๋๋ฐ, ๋ฐ์ ๋ฉ์ธ์ง(event)๋ฅผ ํ์ฑํด์ firebase_admin ๋ชจ๋๋ก ๋ฉ์ธ์ง๋ฅผ ์ ์กํ๋ค.
์์ ๋ด์ฉ ์ค tokens๋ฅผ ์ฌ๋ผ์ด์ฑํด์ split ํ๋ ๊ณผ์ ์ด ์๋๋ฐ, ๊ทธ ์ด์ ๋ ๋ค์์ ์ค๋ช ํ๊ฒ ๋ค.
SQS๋ก ๋ฐ๋ ์ด๋ฒคํธ๋ ๋ณดํต ๋ค์๊ณผ ๊ฐ์ ํ์์ด๋ค.
{
"Records": [
{
"messageId": "{๋ฉ์ธ์ง ์์ด๋}",
"receiptHandle": "{SQS ๋ฉ์ธ์ง ๊ณ ์ ์๋ณ์}",
"body": "{๋ฉ์ธ์ง ๋ฐ๋}",
"attributes": {
"ApproximateReceiveCount": "10",
"SentTimestamp": "1724855187392",
"SenderId": "AIDAYS2NS75TM4GSXUFYB",
"ApproximateFirstReceiveTimestamp": "1724855189392"
},
"messageAttributes": {
"title": {
"stringValue": "ํ
์คํธ ์ ๋ชฉ์
๋๋ค.",
"binaryValue": null,
"stringListValues": [],
"binaryListValues": [],
"dataType": "String"
},
"token": {
"stringValue": "[token1, token2, token3]",
"binaryValue": null,
"stringListValues": [],
"binaryListValues": [],
"dataType": "String"
}
},
"md5OfMessageAttributes": "{์์ฑ ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ์ฉ MD5 ํด์๊ฐ}",
"md5OfBody": "{๋ฐ๋ ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ์ฉ MD5 ํด์๊ฐ}",
"eventSource": "aws:sqs",
"eventSourceARN": "{Role ARN}",
"awsRegion": "ap-northeast-2"
}
]
}
ํฐ ํ๋ก ๋๋๋ฉด ๋ฉ์ธ์ง ์์ฑ(messageAttributes)๊ณผ body๋ก ๋๋๋๋ฐ,
๋๋ ๋ฉ์ธ์ง ์์ฑ์ ์ฌ์ฉ์ ํ ํฐ ๊ฐ๋ค๊ณผ ํธ์์๋ฆผ ์ ๋ชฉ์ ๋ฃ์ ๊ฒ์ด๊ณ , body์๋ ํธ์์๋ฆผ ๋ด์ฉ์ ๋ฃ์ ๊ฒ์ด๋ค.
ํด๋น ๋ด์ฉ์ ํ์ฑํ๋๊ฒ parse_sqs_message ํจ์์ ๋ด์ฉ์ด๋ค.
4. ์๋น์ค ๋ก์ง ๊ตฌํ
์ด์ ํธ์์๋ฆผ์ ์ ์กํ๋ ์๋ฒ์ชฝ์ ๊ตฌํํด๋ณด์.
Depdency(gradle)
implementation platform('software.amazon.awssdk:bom:2.21.20')
implementation 'software.amazon.awssdk:sqs'
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ aws-sdk for java ๋ฒ์ 2๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค.
application.yml
cloud:
aws:
credentials:
access-key: [access key]
secret-key: [secret access key]
stack:
auto: false
sqs:
queue:
message-delay-seconds: [message-delay-seconds]
url: [SQS Queue URL]
AWSConfig
@Configuration
public class AwsConfig {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
//Credential
private StaticCredentialsProvider createAwsCredentialsProvider() {
AwsBasicCredentials basicAWSCredentials = AwsBasicCredentials.create(this.accessKey, this.secretKey);
return StaticCredentialsProvider.create(basicAWSCredentials);
}
//SQS Async Client
@Bean
public SqsAsyncClient sqsAsyncClient() {
return SqsAsyncClient.builder()
.region(Region.AP_NORTHEAST_2) //์์ธ Region
.credentialsProvider(createAwsCredentialsProvider())
.build();
}
}
AWSConfig์๋ SqsAsyncClient๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํด์ค๋ค.
SqsClient๋ ๋๊ธฐ ๋ฐฉ์, SqsAsyncClient๋ ๋น๋๊ธฐ ๋ฐฉ์์ ํด๋ผ์ด์ธํธ์ด๋ค.
์์ ์๊ฒฉ ์ฆ๋ช ๊ฐ์ฒด(StaticCredentialProvider)๋ ์ฒ์ IAM์์ ์์ฑํ๋ ์์ธ์ค ํค์ access key, secret access key๋ก๋ถํฐ ์์ฑํ๋ค.
AWSSqsProperties
@Getter
@Component
public class AwsSqsProperties {
@Value("${cloud.aws.sqs.queue.url}")
private String queueUrl;
@Value("${cloud.aws.sqs.queue.message-delay-seconds}")
private Integer messageDelaySecs;
}
SQS์ ์ฐ๊ฒฐํ ์ค์ ๋ค์ ๋ฏธ๋ฆฌ ์ปดํฌ๋ํธ๋ก ๋ฑ๋กํด ๋์๋ค.
AwsSqsUtils
@Slf4j
@RequiredArgsConstructor
@Component
public class AwsSqsUtils {
private final SqsAsyncClient sqsAsyncClient;
private final AwsSqsProperties awsSqsProperties;
public void sendMessage(SqsMessageDto messageDto) {
Map<String, MessageAttributeValue> attributes = createAttributes(messageDto.tokens(), messageDto.title());
SendMessageRequest request = createRequest(messageDto, attributes);
try {
CompletableFuture<SendMessageResponse> future = sqsAsyncClient.sendMessage(request);
future.whenComplete((sendMessageResponse, throwable) -> {
if (throwable == null) {
log.info("[SQS Async Client] ๋ฉ์ธ์ง ์ ์ก ์ฑ๊ณต Status ---> {}", sendMessageResponse.sdkHttpResponse().statusCode());
log.info("[SQS Async Client] ๋ฉ์ธ์ง ์ ์ก ์ฑ๊ณต ID ---> {}", sendMessageResponse.messageId());
} else {
log.error("[SQS Async Client] ๋ฉ์ธ์ง ์ ์ก ์คํจ ---> {}", throwable.getMessage());
}
});
} catch (SqsException sqsException) {
log.error("[SQS] SQS ๋ฉ์ธ์ง ์ ์ก ์คํจ --> {}", sqsException.getMessage());
}
}
private SendMessageRequest createRequest(SqsMessageDto messageDto, Map<String, MessageAttributeValue> attributes) {
return SendMessageRequest.builder()
.queueUrl(awsSqsProperties.getQueueUrl())
.delaySeconds(awsSqsProperties.getMessageDelaySecs())
.messageAttributes(attributes)
.messageBody(messageDto.content())
.build();
}
public void sendMessages(List<String> tokens, String title, String content) {
sendMessage(new SqsMessageDto(tokens, title, content));
}
private static Map<String, MessageAttributeValue> createAttributes(List<String> tokens, String title) {
Map<String, MessageAttributeValue> attributes = new HashMap<>();
attributes.put("tokens", MessageAttributeValue.builder().stringValue(tokens.toString()).dataType("String").build());
attributes.put("title", MessageAttributeValue.builder().stringValue(title).dataType("String").build());
return attributes;
}
}
์ด์ ๋ฉ์ธ์ง๋ฅผ ์ ์กํ๋ ํจ์๋ฅผ ๊ตฌํํ๋ฉด ๋๋ค.
๋น๋๊ธฐ ๋ฐฉ์์ AsyncSqsClient๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์๋ต์ CompletableFuture๋ก ๋ฐ์์ผํ๋ค.
catchํ๋ SqsException์ AWS์์ ์ง์ํ๋ ์ ์ก ์คํจ์ ์์ธ๋ค์ด ๋ด๊ฒจ์๊ณ , throwable์ ๋ค์ด์ค๋ ์์ธ๋ค์ ์ ์ก ํ ์คํจ ์์ธ๋ค์ ๋ด๊ณ ์๋ค.
์ฌ๊ธฐ์ ์ฌ๋ฌ token์ด ๋ด๊ฒจ์๋ ๋ฆฌ์คํธ๋ฅผ ๋ฃ๊ธฐ ์ํด์ Attribute builder์ ๋ฆฌ์คํธ๋ฅผ ์ถ๊ฐํ๋ stringListValues ํจ์๊ฐ ์์ง๋ง
StringListValues๋ ์์ง ๊ตฌํ๋์ง ์์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ์ง ๋ชปํ๋ค ๐ซ
๋ฐ๋ผ์ ๋ฆฌ์คํธ๋ฅผ toString์ผ๋ก ๋ฃ๊ณ ํ์ด์ฌ์ผ๋ก ํ์ฑํ๋ ๊ณผ์ ์ผ๋ก ํ ์ ๋ฐ์ ์์๋ค.
์ด์ ํด๋น Util์ ์ฌ์ฉํด์ ๋ฉ์ธ์ง๋ฅผ ์ ์กํ๋ฉด ๋๊ฒ ๋ค.
5. ์ถ๊ฐ
AWS CloudWatch์์ Lambda ํจ์์ ์คํ ๋ก๊ทธ๋ฅผ ํ์ธํด๋ณผ ์ ์๋ค.
Lambda์์ ์ด๋ฒคํธ๋ฅผ ์ง์ ์์ฑํ์ฌ ํ ์คํธํด๋ณผ ์ ์๋ค.
์ถ๊ฐ๋ก, ๋์ฑ ์์ ์ฑ ์๋ ์๋น๋ฅผ ์ํด SQS์ DLQ(Dead Letter Queue)๋ฅผ ์ด์ฉํ ์๋ ์๊ฒ ๋ค.
6. ์ฑ๋ฅ ๊ฐ์ ํ ์คํธ
์ ๊ตฌ์กฐ์ ์ฑ๋ฅ์ ๋์ ๋๊ฒ ๊ฐ์ ๋๋ค.
ํ ํฐ์ด ๋ช ๊ฐ๋ผ๋ ์์ฒญ์ด ํ ๋ฒ ์คํ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฆฌ์์ค ์ฌ์ฉ ์ฐจ์ด๋ฅผ ๋ณด๊ธฐ ์ํด ๋๊ธฐ ๋ฐฉ์์ผ๋ก ๋ฐ๊พธ๊ณ ํ ์คํธ๋ฅผ ์งํํ๋ค.
์ฌ์ฉ์ 1000๋ช ์๊ฒ ํธ์์๋ฆผ์ ์ ์กํ๋ ํ ์คํธ๋ฅผ ์งํํ์ ๋, ์ฝ 98%(2620ms -> 36ms)๊ฐ ํฅ์๋์๋ค.
1000๋ช ์ ํ ์คํธ ํด๋ณด์์ง๋ง, ์ฌ์ฉ์๊ฐ ๋ง์์ง๋ฉด ๋ง์์ง์๋ก ์ฑ๋ฅ ์ฐจ์ด๋ ํ์คํ๊ฒ ๋ ๋ฒ์ด์ง ๊ฒ์ด๋ค.
์ด๋ก์จ ์ฌ์ฉ์ ์์ ๋ฌด๊ดํ ํธ์์๋ฆผ ์ ์ก ํ๊ฒฝ์ ๊ตฌ์ถํด ๋ณด์๋ค.
์ฐธ๊ณ
https://cabi.oopy.io/1d1010b2-b287-43af-a2c1-bab35cdf1dd8
'BackEnd > Spring Boot' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] JPA ์ฟผ๋ฆฌ DTO ์กฐํ ์ฟผ๋ฆฌ ์ต์ ํ (0) | 2024.12.24 |
---|---|
[Redis] ๋ถ์ฐ ๋น๊ด์ ๋ฝ์ผ๋ก ๋์์ฑ ์ด์ ํด๊ฒฐํ๊ธฐ (0) | 2024.09.09 |
[Spring Security] JWT ๊ธฐ๋ฐ ์๋ฒ ์ธ๊ฐ ๊ฐ์ฒด ์ ๋ฌ ๋ฐฉ๋ฒ (0) | 2024.08.18 |
[Spring] Spring Event๋ก ํธ๋์ญ์ , ์์กด์ฑ ๋ถ๋ฆฌํ๊ธฐ (0) | 2024.08.11 |
JPA/Hibernate Exception๋ค๊ณผ ์์ธ ์ฒ๋ฆฌ (0) | 2024.08.04 |