๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
  • ์žฅ์›์ต ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ
๐Ÿ’Š Java & Kotlin & Spring/- spring framework +

AWS SQS + Spring Boot 3 + kotlin ์ธํ”„๋ผ ๊ตฌ์ถ•ํ•˜๊ธฐ

by Wonit 2024. 7. 27.

๊ด€๋ จ ๊ธ€

 

์ด๋ฒˆ ๊ธ€์˜ ๋ชฉํ‘œ๋Š” Spring Boot ๊ณผ kotlin ์„ ์ด์šฉํ•ด์„œ SQS ๋ฅผ ์—ฐ๋™ํ•˜๋Š” application ์„ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

Spring Boot ๋กœ application ์„ ๋งŒ๋“ค์–ด๋ณด๋ฉฐ ๊ฐ„๋žตํ•˜๊ฒŒ SQS ์˜ ์„ค์ •์ด ์–ด๋–ค ๊ฒƒ์„ ์˜๋ฏธํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž.

 

๋ชฉ์ฐจ

  1. prerequisites
  2. IAM User ์ƒ์„ฑํ•˜๊ธฐ
  3. sqs ์ƒ์„ฑํ•˜๊ธฐ
  4. spring boot ์™€ ์—ฐ๋™
  5. SQS ์˜ ์—ฌ๋Ÿฌ ์„ค์ •๋“ค

1. prerequisities

๋‹ค์Œ ๋ฒ„์ „์œผ๋กœ ์‹ค์Šต์„ ์ง„ํ–‰ํ•  ์˜ˆ์ •์ด๋‹ค.

  1. kotlin (java 21)
  2. Spring Boot 3.3.2
  3. 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 ์—์„œ ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž.

๋Œ“๊ธ€