fromm์˜ SQS์™€ DLQ - 2. Throughput์„ ํšจ๊ณผ์ ์œผ๋กœ ๋Š˜๋ฆฌ๊ธฐ ์œ„ํ•œ Partial Batch ์‚ฌ์šฉ

yeon
  • #AWS
  • #SQS
  • #DLQ
  • #event-driven
  • #partial batch

์•ˆ๋…•ํ•˜์„ธ์š”. ๋…ธ๋จธ์Šค ๋ฐฑ์—”๋“œํŒ€์—์„œ fromm ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๊น€์—ฐํƒœ์ž…๋‹ˆ๋‹ค.

๋“ค์–ด๊ฐ€๋ฉฐ

์ง€๋‚œ fromm์˜ SQS์™€ DLQ - 1. DeadLetterQueue๋ฅผ ๋„์ž…ํ•˜๊ธฐ๊นŒ์ง€ ์—์„œ fromm ์ด SQS ๋ฅผ ์„ ํƒํ•œ ์ด์œ ์™€ DeadLetterQueue๋ฅผ ์‚ฌ์šฉํ•ด ์•ˆ์ •์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ํ•ญ๋ชฉ์ด ๊ถ๊ธˆํ•˜์‹  ๋ถ„๋“ค์ด๋ผ๋ฉด ์ง€๋‚œ๊ธ€์„ ์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

EventDriven Architecture๋ฅผ ์„ ํƒํ•˜๊ณ  SQS๋ฅผ ์‚ฌ์šฉํ•ด ์‹œ์Šคํ…œ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ์œ ์—ฐ์„ฑ์„ ๋†’์˜€๋‹ค๋ฉด ์ดํ›„ ๊ด€์‹ฌ์‚ฌ๋Š” ์ด๋Ÿฐ ํ™˜๊ฒฝ์—์„œ ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ž˜ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฐ€ ์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” SQS์˜ throughput์„ ์–ด๋–ป๊ฒŒ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

SQS์˜ throughput ์ฆ๊ฐ€์‹œํ‚ค๊ธฐ

SQS๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งค์šฐ ๋†’์€ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  AWS๋Š” ๋งํ•ฉ๋‹ˆ๋‹ค. ํ‘œ์ค€ ๋Œ€๊ธฐ์—ด์˜ ๊ฒฝ์šฐ API ํ˜ธ์ถœ์ˆ˜๋Š” ๊ฑฐ์˜ ๋ฌด์ œํ•œ ์ด๋ผ๊ณ  ํ‘œํ˜„ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ํ‘œ์ค€ ๋Œ€๊ธฐ์—ด๋ณด๋‹ค ์ฒ˜๋ฆฌ๋Ÿ‰์ด ์ข€๋” ๋‚ฎ์€ FIFO ๋Œ€๊ธฐ์—ด์˜ ๊ฒฝ์šฐ์—๋„ ๋ฆฌ์ „๋ณ„ ์ฐจ์ด๋Š” ์žˆ์ง€๋งŒ ํ•œ๊ตญ ๋ฆฌ์ „์€ ์ตœ๋Œ€ ์ดˆ๋‹น 90000๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋•Œ ์ตœ๋Œ€ ํ˜ธ์ถœ์„ ์œ„ํ•ด์„œ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ API๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

SQS throughput์„ ์ตœ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ๋‹ค์Œ ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Horizontal Scaling
  • Action Batching

Horizontal Scaling

Horizontal Scaling์˜ ๊ฒฝ์šฐ SQS์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ๊ฐฏ์ˆ˜ ํ˜น์€ ์“ฐ๋ ˆ๋“œ ์ˆ˜๋ฅผ ๋Š˜๋ ค ๋ฉ”์‹œ์ง€์˜ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

EC2, ECS, EKS ๋“ฑ์œผ๋กœ SQS ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ ์ธ์Šคํ„ด์Šค๋ฅผ ๋Š˜๋ ค ๋ฉ”์‹œ์ง€์˜ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ์„ ํ˜•์ ์œผ๋กœ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ Auto Scaling์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ ์ˆ˜๋ฅผ ๋™์ ์œผ๋กœ ์กฐ์ •ํ•˜์—ฌ SQS์˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋งŽ์„๋•Œ๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ ์„๋•Œ๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ค„์—ฌ ๋น„์šฉ์„ ์ค„์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ fromm ์„œ๋น„์Šค์˜ ๊ฒฝ์šฐ SQS์˜ ๋ฉ”์‹œ์ง€ ํ•ธ๋“ค๋Ÿฌ ํด๋ผ์ด์–ธํŠธ๋กœ Lambda๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ณ  Lambda์˜ ์ด๋ฒคํŠธ ์†Œ์Šค๊ฐ€ SQS์ผ ๊ฒฝ์šฐ Lambda๋Š” ๋ถ„๋‹น 300๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๊นŒ์ง€ ์˜คํ† ์Šค์ผ€์ผ๋ง ๋ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ AWS ์ž์ฒด์ ์œผ๋กœ ๋ฉ”์‹œ์ง€ ํด๋ผ์ด์–ธํŠธ์˜ ์˜คํ† ์Šค์ผ€์ผ๋ง์„ ์ง€์›ํ•ด์ค€๋‹ค๋Š” ์ ์ด SQS๋ฅผ ์„ ํƒํ•œ ์ฃผ์š”ํ•œ ์ด์œ ์ค‘ ํ•˜๋‚˜์˜€๊ณ  ๋”ฐ๋ผ์„œ fromm ์„œ๋น„์Šค์—์„œ๋Š” SQS Client์˜ horizontal scaling์„ ํฌ๊ฒŒ ์‹ ๊ฒฝ์“ฐ๊ณ  ์žˆ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

Action Batching

Action Batching์€ SQS์˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ throughput์„ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ค„์ด๊ณ  ํšจ์œจ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๊ณ  Lambda์˜ Concurrency๋ฅผ ์ตœ์ ํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ‘œ์ค€ ๋Œ€๊ธฐ์—ด์˜ ๊ฒฝ์šฐ ํ•œ๋ฒˆ์— ์ตœ๋Œ€ 10000๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐฐ์น˜๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

AWS Document ์— SQS Batch size์˜ max value๊ฐ€ 10, 10000 ์œผ๋กœ ํ˜ผ์šฉ๋˜์–ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š”๋ฐ aws-lambda-now-supports-batch-windows-of-up-to-5-minutes-for-functions ์— ๋”ฐ๋ฅด๋ฉด 10์—์„œ 10000์œผ๋กœ ์ฆ๊ฐ€๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Action Batch ๊ตฌ์„ฑํ•˜๊ธฐ

action batch ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ serverless ๋ฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ ์˜ˆ์‹œ๋ฅผ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

serverless YAML config

serverless.yml

service: hello-fromm

provider:
  name: aws
  runtime: nodejs20.x
  region: ap-northeast-2
  memorySize: 1024
  timeout: 60
  tracing:
    lambda: true

functions:
  HelloFromm:
    handler: src/lambda.handler
    events:
      - sqs:
          arn:
            Fn::GetAtt:
              - HelloFrommQueue
              - Arn
          batchSize: 100
          maximumBatchingWindow: 60
          functionResponseType: ReportBatchItemFailures

.
.
.
(์ง€๋‚œ ๊ธ€์˜ resource config์™€ ๊ฐ™์Œ)

functions์˜ handler ์„ค์ •์— batchSize, maximumBatchingWindow, functionResponseType ์„ ์ถ”๊ฐ€ํ•ด ๋ฉ”์‹œ์ง€ ๋ฐฐ์น˜์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • batchSize : ํ•œ๋ฒˆ์— ๋ฐฐ์น˜๋กœ ์ฒ˜๋ฆฌํ•  ์ตœ๋Œ€ ๋ฉ”์‹œ์ง€ ์ˆ˜๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • maximumBatchingWindow : ๋ฉ”์‹œ์ง€๋ฅผ ํด๋งํ•˜๋Š” ์ตœ๋Œ€ ์‹œ๊ฐ„์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ ์ด ์‹œ๊ฐ„๋™์•ˆ ๋ฐ˜๋“œ์‹œ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋‹ค๋ฆฐ๋‹ค๋Š” ๋ณด์žฅ์€ ์—†์œผ๋ฉฐ ์„ค์ •ํ•œ batchSize ๊นŒ์ง€ ๋ฉ”์‹œ์ง€ ๊ฐฏ์ˆ˜๊ฐ€ ๋˜์ง€ ์•Š๊ฑฐ๋‚˜ batchWindow ๋งŒํผ ์‹œ๊ฐ„์ด ๋˜์ง€ ์•Š๋”๋ผ๋„ ๋ฉ”์‹œ์ง€๊ฐ€ ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ex. batchSize 50, maximumBatchingWindow 60์œผ๋กœ ์„ค์ •๋˜์—ˆ์œผ๋‚˜ 10์ดˆ๋งŒ์— 7๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋งŒ ๋ฐฐ์น˜์ฒ˜๋ฆฌ, ์‹ค์ œ๋กœ ์ด๋ ‡๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ›จ์”ฌ ๋งŽ์Šต๋‹ˆ๋‹ค.
  • functionResponseType : Response Type์„ ์ง€์ •ํ•ด ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ์ค‘ ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€๋งŒ์„ ์žฌ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋•Œ maximumBatchingWindow, functionResponseType ๋Š” ์ƒ๋žต๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Partial Batch Failure

๋ฉ”์‹œ์ง€๋ฅผ ๋ฐฐ์น˜๋กœ ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋‹ค ๋ณด๋ฉด ์ค‘๊ฐ„์ค‘๊ฐ„ ์ฒ˜๋ฆฌ์— ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€๊ฐ€ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ functionResponseType ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋ƒฅ ๋ฐฐ์น˜๋งŒ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด SQS handler Lambda๋Š” ๊ฐ€์ ธ์˜จ ๋ฉ”์‹œ์ง€๋“ค์ด ์ฒ˜๋ฆฌ์— ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ์‹คํŒจํ–ˆ๋Š”์ง€๋งŒ์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ์ค‘๊ฐ„์— ์ฒ˜๋ฆฌํ•œ ๋ฉ”์‹œ์ง€๊ฐ€ ํ•œ๊ฐœ๋ผ๋„ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๋ฉด ๊ฐ€์ ธ์˜จ ๋ฉ”์‹œ์ง€ ์ „์ฒด๋ฅผ ์žฌ์‹œ๋„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฏธ ์„ฑ๊ณตํ•œ ๋ฉ”์‹œ์ง€๊ฐ€ ์žฌ์ฒ˜๋ฆฌ ๋˜๋”๋ผ๋„ ์ „ํ˜€ ์ƒ๊ด€์ด ์—†๋‹ค๋ฉด ์ฝ”๋“œ์˜ ๊ฐ„๊ฒฐํ•จ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋ณธ ์„ค์ • ๊ทธ๋Œ€๋กœ action batch๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋„ ๋งŽ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž์—๊ฒŒ Push ์ „์†ก์„ ์œ„ํ•ด SQS๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ด๋ฅผ ๋ฐฐ์น˜์ฒ˜๋ฆฌ ํ•˜๊ณ ์žˆ๋‹ค๋ฉด ์„ฑ๊ณตํ•œ ๋ฉ”์‹œ์ง€๊ฐ€ ์—ฌ๋Ÿฌ๋ฒˆ ์ฒ˜๋ฆฌ๋˜์–ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์ค‘๋ณต๋œ Push๊ฐ€ ์—ฌ๋Ÿฌ๋ฒˆ ๋ฐœ์†ก๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ReportBatchItemFailures ๋ผ๋Š” handler Lambda์˜ Response Type์„ ์ง€์ •ํ•ด ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€๋งŒ์„ ์žฌ์ฒ˜๋ฆฌํ•˜๋Š” partial batch failure๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Lambda Handler Source Code

lambda.ts

export const handler = async event => {
  const app = await NestFactory.create(AppModule);

  const helloFrommService = app.get(HelloFrommService);
  
  const batchItemFailures: SQSBatchItemFailure[] = [];

  for (const record of event.Records) {
        try {
            await helloFrommService.executeTestData(JSON.parse(record.body));

        } catch (e) {
            batchItemFailures.push({ itemIdentifier: record.messageId });
            
            // logging, alerting, etc...
        }
    }

    return { batchItemFailures };
}

์ง€๋‚œ๋ฒˆ ์ฝ”๋“œ์™€ ๋‹ฌ๋ผ์ง„ ์ ์œผ๋กœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • 1๊ฐœ๊ฐ€ ์•„๋‹Œ ์—ฌ๋Ÿฌ๊ฐœ์˜ record๋ฅผ ๊ฐ€์ ธ์˜ค๋ฏ€๋กœ ๊ฐ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฅผ ํ•œ๋‹ค๋Š” ๊ฒƒ
  • ๊ฐ ๋ ˆ์ฝ”๋“œ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด batchItemFailures ๋ผ๋Š” ๋ฐฐ์—ด์— ํ•ด๋‹น ๋ ˆ์ฝ”๋“œ message ID๋ฅผ ๋‹ด์•„ ์‹คํŒจํ•œ ๋ ˆ์ฝ”๋“œ๋งŒ์„ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋Š” ๊ฒƒ
  • ๋ฆฌํ„ด์ด ๋ช…์‹œ์ ์ด๋ฏ€๋กœ handler ์ฝ”๋“œ์—์„œ error throw๊ฐ€ ์—†์–ด์กŒ๋‹ค๋Š” ๊ฒƒ

์œ„ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด SQS์˜ Action Batch๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€๋งŒ ์•ˆ์ „ํ•˜๊ฒŒ ์žฌ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

์—ฌ๊ธฐ๊นŒ์ง€ ๊ธ€์„ ๊ด€์‹ฌ์žˆ๊ฒŒ ์ฝ์–ด์ฃผ์‹  ๋ชจ๋“ ๋ถ„๊ป˜ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค๐Ÿ™‡โ€

๋‹น์‹ ์˜ ์˜ˆ์ˆ ์ด ์„ธ์ƒ์„ ๋ฐ”๊พผ๋‹ค.

์ €ํฌ์™€ ํ•จ๊ป˜ ์„œ๋น„์Šค์˜ ํ’ˆ์งˆ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๊ณ ๋ฏผํ•˜๊ณ  ํ•จ๊ป˜ ์„ฑ์žฅํ•ด๋‚˜๊ฐˆ ๊ฐœ๋ฐœ์ž๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. ๊ด€์‹ฌ์žˆ์œผ์‹ ๋ถ„๋“ค์€ ์–ธ์ œ๋“ ์ง€ ์ง€์›๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค๐Ÿ˜€

๋…ธ๋จธ์Šค ์ง€์›ํ•˜๊ธฐ

์ฐธ๊ณ ์ž๋ฃŒ

Serverless Framework Documentation

Increasing throughput using horizontal scaling and action batching with Amazon SQS

Best practices for implementing partial batch responses

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ