๊ด๋ จ ๊ธ
- AWS SQS + Spring Boot 3 + kotlin ์ธํ๋ผ ๊ตฌ์ถํ๊ธฐ
- AWS SQS Consumer ์๋ฌ๋ฅผ DLQ ๋ก ์ฒ๋ฆฌํ๊ธฐ
- message converter ๋ฅผ ์ด์ฉํ sqs message serializer
- AWS SDK ๋ก DLQ ์ ์์ธ ๋ฉ์์ง ์ฒ๋ฆฌํ๋ 2๊ฐ์ง ๋ฐฉ๋ฒ
์ด๋ฒ ๊ธ์ ๋ชฉํ๋ Spring Boot ๊ณผ kotlin ์ ์ด์ฉํด์ SQS ๋ฅผ ์ฐ๋ํ๋ application ์ ๊ฐ๋ฐํ๋ ๊ฒ์ด๋ค.
Spring Boot ๋ก application ์ ๋ง๋ค์ด๋ณด๋ฉฐ ๊ฐ๋ตํ๊ฒ SQS ์ ์ค์ ์ด ์ด๋ค ๊ฒ์ ์๋ฏธํ๋์ง ์์๋ณด์.
๋ชฉ์ฐจ
- prerequisites
- IAM User ์์ฑํ๊ธฐ
- sqs ์์ฑํ๊ธฐ
- spring boot ์ ์ฐ๋
- SQS ์ ์ฌ๋ฌ ์ค์ ๋ค
1. prerequisities
๋ค์ ๋ฒ์ ์ผ๋ก ์ค์ต์ ์งํํ ์์ ์ด๋ค.
- kotlin (java 21)
- Spring Boot 3.3.2
- awspring 3.1.1
๋น์ฐํ๊ฒ๋ SQS ๋ฅผ ์์ฑํ AWS ์ ๊ณ์ ๋ ํ์ํ๊ณ local application ์ ํตํด ์ ๊ทผํ๊ธฐ ์ํ IAM user ๋ ํ์ํ๋ค.
์ด๋ฒ ์์ ์์๋ IAM ์ ํตํด ์์ฑํ user ๋ฅผ ํตํด access ํ ๊ฒ์ด๋ค.
์ค์ต์ ํตํด ํ๋์ฉ ์งํํด๋ณด์.
2. IAM user ์์ฑํ๊ธฐ
AWS ๊ณ์ ์ ์์ฑํ๋ฉด root ์ฌ์ฉ์ ๊ณ์ ์ผ๋ก access ๊ถํ์ ๋ฐ๋๋ค.
root access permission ์ด ์ ์ถ๋๋ฉด ์์ฃผ ํฌ๋ฆฌํฐ์ปฌ ํ๋ฏ๋ก ์ผ๋ฐ์ ์ผ๋ก application ์์๋ root ๊ณ์ ์ ์ฌ์ฉํ์ง ์๋๋ค.
๊ฐ์๊ฐ ๋ชจ๋ ํ์ ํน์ ์๋น์ค๋ง ์ ๊ทผ ๊ฐ๋ฅํ ๊ณ์ ์ ๋ถ์ฌ๋ฐ๊ณ programming access ๋ฐฉ์์ ์ํ IAM ๊ณ์ ์ ๋ถ์ฌ๋ฐ๋๋ค.
์ด๋ฒ ์ค์ต์์๋ ๋์ผํ๊ฒ SQS ๋ง ์ ๊ทผํ IAM user ๋ฅผ ์์ฑํด๋ณด๋๋ก ํ์.
๋จผ์ IAM ์ ์ ์ํด์ IAM ์ฌ์ฉ์ ์์ฑํ๊ธฐ
๋ฅผ ํด๋ฆญํ๋ค
์ฌ์ฉ์ ์ด๋ฆ์ ์
๋ ฅํ๊ณ ์ง์ ์ ์ฑ
์ฐ๊ฒฐ
์ ํตํด SQS ์ ํํด์๋ง ์ ๊ทผ ๊ถํ์ ๋ถ์ฌํ์.
SQSFullAccess
role ์ ์ถ๊ฐํ๊ณ ์ฌ์ฉ์๋ฅผ ์์ฑํ๋ฉด ์๋์ ๊ฐ์ ํ๋ฉด์ด ๋ฌ๋ค.
์ก์ธ์ค ํค๋ฅผ ๋ฐ๊ธํ ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ฉ์ผ๋ก ์์ฑํ๋ฉด ACCESS_KEY ์ SECRET_KEY ๋ฅผ ๋ฐ๊ธ๋ฐ์ ์ ์๋ค.
2. SQS ์์ฑํ๊ธฐ
์ด์ aws console ์์ queue ๋ฅผ ์์ฑํด ๋ณผ ๊ฒ์ด๋ค. queue ๋ฅผ ์์ฑํ๋ ๊ฒ์ ์์ฃผ ๊ฐ๋จํ๋ค.
์์ธํ ์ค์ ์ ๊ฐ์ฅ ๋ง์ง๋ง ํํธ์์ ํ๋์ฉ ์์๋ณผ ์์ ์ด๋ ์ฐ์ ์ ์๋ ์คํ ์ ๋ฐ๋ผ์ Standard ํ์ ์ queue ๋ฅผ ์์ฑํด๋ณด์.
3. Spring Boot ์ ์ฐ๋
์์์ ์ฐ๋ฆฌ๋ IAM ์ ์์ฑํ๊ณ accessKey ์ secrets ๋ฅผ ๋ฐ๊ธ ๋ฐ์๋ค. ์ด์ IAM ๊ณผ SQS ๋ฅผ ๊ฐ์ง๊ณ Spring Application ์ผ๋ก ์ฐ๋ํด๋ณผ ์ฐจ๋ก์ด๋ค.
๊ฐ์ฅ ๋จผ์ gradle ์์กด์ฑ์ ์ถ๊ฐํด์ฃผ์.
implementation("io.awspring.cloud:spring-cloud-aws-starter-sqs:3.1.1")
์์ ์์กด์ฑ์ Spring Cloud AWS ์์ ๊ด๋ฆฌํ๋ ํ๋ก์ ํธ๋ก awspring.io ์์ AWS ์ S3, SQS, SNS, DynamoDB ๋ฑ ๋ค์ํ ์๋น์ค์ ํตํฉ์ ์ ๊ณตํ๋ค.
์์ ์์กด์ฑ์ sqs starter ๋ก, ๋ค๋ฅธ starter ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํน์ฑ๊ณผ ๋์ผํ๊ฒ ๋ณต์กํ ์ค์ ์์ด ํต์ฌ bean ๋ค์ auto configuration ์ผ๋ก ์ ๊ณตํ๋ค.
๋ํ์ ์ผ๋ก๋ ์๋์ ๊ฐ์ ํด๋์ค๋ค์ด ์๋๋ฐ, ์ด ๋๋ถ์ ์ฐ๋ฆฌ๋ ๋ณ๋ค๋ฅธ bean configuration ์ค์ ์ ์๋ ๊ตฌ์ฑ์๊ฒ ์์ํ ์์ ์ด๋ค.
- SqsAsyncClient
- SqsTemplate
- @SqsListener
- SqsMessageListenerContainerFactory
3-1. aws sqs conf ๋ฐ pub/sub ๊ตฌ์กฐ
auto-configuration ์ ์ํด ๋ฑ๋ก๋๋ bean ๋ค๊ณผ pub/sub ์ ์ํด ์ํธ์์ฉํ๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ๋จํ๊ฒ ๋์ํ ํ๋ค๋ฉด ์๋ง ์๋์ ๊ฐ์ ๊ฒ์ด๋ค.
SqsListener ์ SqsPublisher ์ฐ์ฐ ๋ชจ๋ AwsClient ๋ฅผ ํตํด AWS ์ ํต์ ์ ์ํํ๋๋ฐ, ์ด๋ ์ ๊ทผ์ ์ํ ์ธ์ฆ ์ ๋ณด๋ค์ด (ACCESS_KEY, SECRET_KEY) ํ์ํ๋ค.
์์ ์ฐ๋ฆฌ๊ฐ aws console ์์ ํ๋ํ๋ ์ธ์ฆ ์ ๋ณด์ ๋์ผํ๋ฐ, ์ด๋ค์ ๋ณดํต ์ธ๋ถ์์ ๊ด๋ฆฌ๋๋ฏ๋ก application.yml ํน์ environment variable ๋ก ์ฃผ์ ๋๋ค.
3-2. credential ์ฐ๋๊ณผ configuration
์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ application ์ ๋๋ถ๋ถ remote git repository ์ ์ฌ๋ผ๊ฐ๋ access-key
์ secret-key
๋ ์ ์ถ์ด ๋์ง ์๋๋ก ์ ๊ด๋ฆฌํด์ผ ํ๋ค.
์ด๊ฑด secret ์ ๋ณด๋ฅผ ์จ๊ธธ ๋ ์ฌ์ฉํ๋ ๋ค์ํ ๋ฐฉ๋ฒ๋ค ์ค ํธํ ๋ฐฉ๋ฒ์ ์ฐพ์ ์์ ๋กญ๊ฒ ํด์ฃผ๋ฉด ๋๋ค.
spring:
cloud:
aws:
credentials:
access-key: ABCDEF
secret-key: xxx
region:
static: ap-northeast-2
๋๋ application-local.yml
์ ๋ง๋ค๊ณ ์ง์ access ์ secret ์ ๋ช
์ํ์ฌ gitignore ๋ฅผ ์ฒ๋ฆฌํด ์ฃผ์๋ค.
์์ ๊ฐ์ด ๋ช
์ํ๊ณ ๋๋ฉด Aws ์ ์ง์ ํต์ ์ ์ํํ AwsClient
์ค์ ์ ํด์ค์ผ ํ๋๋ฐ, ์ฐ๋ฆฌ๋ Spring Boot ์ Auto-Configuration ์ ์ด์ฉํ ๊ฒ์ด๋ฏ๋ก ๋ณ๋ค๋ฅธ Credential ์ค์ ์ด ํ์ํ์ง ์๋ค.
3-3. ๊ฐ๋จ publisher ์ listener ๊ตฌํ
์ฐ๋ฆฌ๊ฐ ๋ง๋ Spring Application ์ ์์ ์์ฑํ queue ์ ๋ฉ์์ง๋ฅผ ๋ฐํํ publisher ์ ์ด๋ฅผ ์์ ํ listener ๋ฅผ ๋ง๋ค์ด์ฃผ๋ฉด ๋๋ค.
publisher ๋ ๊ฐ๋จํ๊ฒ controller ๋ฅผ ํตํด message ๋ฅผ ์์ฒญ์ผ๋ก ๋ฐ์ ์ด๋ฅผ ๊ฒ์ํ๋ ์ญํ ์ ์ํํ ๊ฒ์ด๋ค.
@RestController
class PublishMessageController(
private val sqsTemplate: SqsTemplate,
) {
@PostMapping("/messages")
fun sendMessage(@RequestBody body: Map<String, String>) {
val content = body["content"]!!
sqsTemplate.send {
it.queue("member-event")
.payload(content)
}
}
}
listener ๋ @SqsListener
์ด๋
ธํ
์ด์
์ ์ด์ฉํ๋ฉด ์ฝ๊ฒ ์์ฑํ ์ ์๋ค.
@Component
class MemberEventListener {
@SqsListener("member-event")
fun listen(message: String) {
println("received message ($message)")
}
}
์ด์ api ๋ฅผ ํธ์ถํ์ฌ ์ ์์ ์ผ๋ก message ๊ฐ consume ๋๋์ง ํ์ธํด๋ณผ ์ ์๋ค.
SQS ์ฌ๋ฌ ์ค์ ๋ค
์๊น SQS ๋ฅผ ์์ฑํ ๋ ์์ธ ์ค์ ๋ค์ ๋ํด์๋ ์์ธํ ์์๋ณด์ง ์์๋ค.
์๋ ์ค์ ๋ค์ ๊ฐ๋จํ ์์๋ณด์
queue type
queue type ์ standard ํ์ ๊ณผ FIFO ํ์ ์ด ์กด์ฌํ๋ค.
standard type ์ ๊ฐ์ฅ simple ํ ํํ์ queue ์ด๋ค. ์ผ๋ฐ์ ์ธ ์ํฉ์์ ๋ง์ด ์ฌ์ฉํ๊ณ ์์์ ์ค๋ณต์ ์ต๋ํ ๋ณด์ฅํ๋ค.
FIFO ๋ ๊ฐ์ฅ ๋ฌ์ฑํ๊ธฐ ์ด๋ ต๋ค๋ message delivery semantics ์ exactly once ๋ฅผ ํ๋ฐฉํ๋ค.
FIFO ํํ์ queue ๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ ๊ฐ์ฅ ์์ ์ ์ด๋ ์ฒ๋ฆฌ๋์ ๋ํ ๊ณ ๋ฏผ๊ณผ error handling ์ blocking ๋๋ ๋ค์ํ ์ด์๋ค์ ๋์ํด์ผ ํ๋ค. (์ด๋ ๋ค์ DLQ ์์ ์์ธํ ์์๋ณด์)
visibility timeoue ๊ณผ retention period
๋ฉ์์ง ๋ณด์กด ๊ธฐ๊ฐ(Message Retention Period)๊ณผ ๊ฐ์์ฑ ํ์์์(Visibility Timeout) ์ด๋ผ๊ณ ๋ถ๋ฅด๋ ์์ฑ์ ์ค์ํ ์์ฑ์ด๋ค.
๋ฉ์์ง ๋ณด์กด ๊ธฐ๊ฐ (Message Retention Period)
์ฒ๋ฆฌ๋์ง ์์ ๋ฉ์์ง๋ฅผ ์ผ๋ง๋ SQS ์ ๋ณด์กดํ ๊ฒ์ธ๊ฐ? ๋ฅผ ๊ฒฐ์ ํ๋ ์์ฑ์ด๋ค.
๋ง์ฝ ์ฌ๊ธฐ์ ๋ช ์๋ ์๊ฐ ๋งํผ ์ฒ๋ฆฌ๋์ง ์์ผ๋ฉด ์๋์ผ๋ก ๋ฉ์์ง๋ฅผ ํ์์ ์ ๊ฑฐํ๋ค.
๊ฐ์์ฑ ํ์์์ (Visibility Timeout)
์ด ์์ฑ์ ์ค๋ณต ์ฒ๋ฆฌ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์ ์ฌ์ฉ๋๋ ์ผ์ข ์ hold and wait ๋ฐฉ์์ locking ์ด๋ค.
ํน์ consumer ์๊ฒ ์๋น๋์์ ๋ ์ผ์ ์๊ฐ ๋์ ๋ค๋ฅธ consumer ์๊ฒ ๋ณด์ด์ง ์๋๋ก ํ๋ ์๊ฐ์ด๊ณ ํด๋น ์๊ฐ์ด ๋ชจ๋ ์๋น๋์ด์ผ ๋ค๋ฅธ ์๋น์๊ฐ ์ด๋ฅผ ๋ค์ ์ ์ ํ ์ ์๋ค.
ํ์ง๋ง ์์ ํ ์๋ฏธ์์ locking ์ด๋ atomic ์ ๋ณด์ฅํ์ง๋ ์๋๋ค๋ ๊ฒ์ ๋ช ์ฌํ์.
DLQ ์ Redriving
DLQ ๋ฅผ ํตํด ์คํจ๋ฅผ ํธ๋ค๋งํ ์ ์๋ค. ์ด์ ๊ด๋ จ๋ ์์ฑ๋ค์ ๋ค์ ์๊ฐ์ ๋ฐฐ์ธ DLQ ์์ ์์ธํ ์์๋ณด์.
๋๊ธ