๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
@sconos 2026. 3. 13. 11:11

๋™์‹œ์„ฑ ๋ฌธ์ œ ๋ฐœ๊ฒฌํ•œ ํ›„ ๊ณต๋ถ€ํ•˜๊ฒŒ ๋œ ACID ์ •๋ฆฌ.

๋น„์ •์ƒ ํŠธ๋ž˜ํ”ฝ ์ฐจ๋‹จ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๋ฉด์„œ Rate Limit์ด๋ž‘ Bot ํƒ์ง€ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋ถ™์ด๋Š” ์ž‘์—…์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

slowapi๋ฅผ ์“ฐ๋ ค๋‹ค๊ฐ€ ๋ฌธ์„œ๋ฅผ ๋ณด๋‹ˆ in-memory ๊ธฐ๋ฐ˜์ด๋ผ๋Š” ๊ฑธ ์•Œ๊ฒŒ ๋๋‹ค. ๋ฉ€ํ‹ฐ ์›Œ์ปค ํ™˜๊ฒฝ์—์„œ๋Š” ์›Œ์ปค๋งˆ๋‹ค ์นด์šดํ„ฐ๊ฐ€ ๋”ฐ๋กœ

๋Œ์•„๊ฐ€๋‹ˆ๊นŒ Rate Limit์ด ์‚ฌ์‹ค์ƒ ๋ฌด์˜๋ฏธํ•ด์ง„๋‹ค๋Š” ๊ฒƒ ๋˜ํ•œ ๋ฐฐ์šฐ๊ฒŒ ๋˜์—ˆ์œผ๋ฉฐ, Redis์˜ ์ž์ฒด ๊ธฐ๋Šฅ์„ ํ†ตํ•ด

ํ•ด๋‹น ๋ถ€๋ถ„์„ ํ•ด์†Œํ•  ์ˆ˜ ์žˆ์Œ์„ ๊นจ๋‹ซ๊ณ  Redis ์ ์šฉ ํ•„์š”์„ฑ์„ ๋А๊ผˆ๋‹ค.

Request
  ↓
Bot ํƒ์ง€ ๋ฏธ๋“ค์›จ์–ด
  ↓
Rate Limit (Redis ์นด์šดํ„ฐ)
  ↓
Endpoint
  โ”œโ”€โ”€ ํฌ๋ ˆ๋”ง ์ฐจ๊ฐ (Redis DECR)
  โ”œโ”€โ”€ Job ์ƒ์„ฑ → PostgreSQL
  โ””โ”€โ”€ SQS ๋ฐœํ–‰

๊ทธ๋ž˜์„œ ์ „์ฒด ์•„ํ‚คํ…์ฒ˜์— ์žˆ์–ด Redis DECR๊ณผ ๊ฐ™์€ ๋‚ด๋ถ€ ๊ธฐ๋Šฅ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋ฉ€ํ‹ฐ ์›Œ์ปค ํ™˜๊ฒฝ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”

race condition์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Œ์„ ๋ฐฐ์›Œ ํŒ€์›๋ถ„๊ป˜ ์•„ํ‚คํ…์ฒ˜ ๋ฌธ์˜๋ฅผ ๋“œ๋ ธ๋”๋‹ˆ ๋‹ต๋ณ€์„ ํ•ด์ฃผ์…จ๋‹ค.

"ํฌ๋ ˆ๋”ง์— ์ ‘๊ทผํ•˜๋Š” ์›Œ์ปค๋Š” API ํ•˜๋‚˜. 6๊ฐœ ์›Œ์ปค๋Š” AI ๊ธฐ๋Šฅ์šฉ์ด๋ผ
ํฌ๋ ˆ๋”ง ์†Œ๋ชจ ์ดํ›„์— ์‹œ์ž‘๋˜๋Š” ์• ๋“ค์ด๊ณ , Redis๋Š” ์•„์ง ์ €ํฌํ•œํ…Œ ํ•ด๋‹น ์•ˆ ๋ผ์š”."

์‚ฌ์‹ค ์ฒ˜์Œ์— ๋“ค์—ˆ์„ ๋•Œ๋„ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ๋‹ค. ๋‚˜๋Š” ๋ฉ€ํ‹ฐ์›Œ์ปค๋ฅผ ๊ทธ๋ƒฅ ์›Œ์ปค๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๋‹ˆ ๋ฉ€ํ‹ฐ์›Œ์ปค๋ผ ์ƒ๊ฐํ–ˆ๋Š”๋ฐ,

๊ทธ๊ฒŒ ์•„๋‹ˆ์—ˆ๊ณ  ํ•ด๋‹น์ด ์•ˆ๋œ๋‹ค๊ณ  ํ•˜์‹  ๊ฑฐ๊ตฌ๋‚˜ ์ •๋„๋กœ๋งŒ ์ƒ๊ฐํ–ˆ๋‹ค.

"DB์˜ ACID๋งŒ ์•Œ์•„๋„ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ์–ด๋А ์ •๋„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด์š”."

์ถ”๊ฐ€์ ์œผ๋กœ ๋ง์”€ํ•ด์ฃผ์…”์„œ ๋‹ค์‹œ ๊ณต๋ถ€ํ•ด๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค.


ACID๊ฐ€ ๋ญ”๋ฐ

ACID๋Š” Atomicity์›์ž์„ฑ, Consistency์ผ๊ด€์„ฑ, Isolation๊ฒฉ๋ฆฌ์„ฑ/๋…๋ฆฝ์„ฑ, Durability์ง€์†์„ฑ/์˜์†์„ฑ์˜ ์•ฝ์–ด.

  • A:ํŠธ๋žœ์žญ์…˜๊ฐ„ ๋ช…๋ น์€ ์ „๋ถ€ ์„ฑ๊ณตํ•˜๊ฑฐ๋‚˜ ์‹คํŒจํ•ด์•ผํ•˜๋ฉฐ, ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•˜๋ฉด ์ „์ฒด ์ž‘์—…์ทจ์†Œ
  • C:ํŠธ๋žœ์žญ์…˜ ์„ฑ๊ณตํ›„ db๋Š” ์ •ํ•ด์ง„ ๊ทœ์น™์„ ์œ ์ง€ํ•ด์•ผํ•จ.
  • I:์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜ ๋™์‹œ ์‹คํ–‰ ๊ฐ„ ์„œ๋กœ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š” ๋…๋ฆฝ์  ๋™์ž‘์ด ์ด๋ฃจ์–ด์ ธ์•ผํ•จ.
  • D:์„ฑ๊ณต๋œ ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฐ๊ณผ๋Š” ์‹œ์Šคํ…œ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ์˜๊ตฌ์ ์œผ๋กœ ๋ฐ˜์˜๋˜์–ด์•ผ ํ•œ๋‹ค.

์šฐ๋ฆฌ ์ฝ”๋“œ๋ฅผ ๋œฏ์–ด๋ณด์•˜๋‹ค

๊ณต๋ถ€ํ•˜๊ณ  ๋‚˜์„œ ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ๋ดค๋‹ค. ์ƒ๊ฐํ•ด๋ณด๋‹ˆ DB ํŠธ๋žœ์žญ์…˜๊ฐ„ ๋‹ค์–‘ํ•œ ๋ณ€์ˆ˜ ๋ฐœ์ƒ์œผ๋กœ ์ค‘๊ฐ„์— ๊ณผ์ •์ด ์ค‘๋‹จ๋ ์ˆ˜๋„,

์‹คํŒจํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ ๊ทธ๋Ÿฐ ์ค‘๋„์‹คํŒจํ–ˆ์„ ๋•Œ๋ฅผ ๋Œ€๋น„ํ•œ ์ฝ”๋“œ๊ฐ€ ์—†์—ˆ๋‹ค. ๋ฒ„๊ทธ ๋ฐœ์ƒ ์‹œ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ๋กœ๊ฑฐ ๊ตฌํ˜„์€

์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ๋“ค์œผ๋ฉฐ ์ฐจํ›„ ์ˆ˜์ •ํ•˜์˜€์—ˆ์ง€๋งŒ, ํ”„๋กœ๋•์…˜ ๋ ˆ๋ฒจ์˜ ์ฝ”๋“œ๋Š” ์ฝ”๋“œ ๋ฒ ์ด์Šค๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๋””๋ฒ„๊น…์„ ์œ„ํ•œ ์žฅ์น˜์™€

๋ณ€์ˆ˜์— ๋Œ€๋น„ํ•˜๋Š” ์ „๋žต์„ ์‹ฌ๋„์žˆ๊ฒŒ ์„ค๊ณ„ํ•ด๋†“์•„์•ผ ํ•จ์„ ์ด๋ฒˆ ๊ธฐํšŒ๋กœ ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊นจ๋‹ฌ์•˜๋‹ค.

 

์ฝ”๋“œ์—์„œ ๋ฐœ๊ฒฌํ–ˆ๋˜ ACID ์œ„๋ฐ˜ ๋ฌธ์ œ๋“ค์ด๋‹ค.

rollback ๋ฌธ์ œ ์‹๋ณ„

# ๋ฌธ์ œ ์ƒํ™ฉ (์ต๋ช…ํ™”)
def deduct_and_record(db, user):
    user.credits -= 1
    db.commit()              # 1์ฐจ ์ปค๋ฐ‹
    record_event(db, ...)    # ์—ฌ๊ธฐ์„œ ํ„ฐ์ง€๋ฉด?
    db.commit()              # 2์ฐจ ์ปค๋ฐ‹ — ํฌ๋ ˆ๋”ง๋งŒ ๋น ์ง„ ์ฑ„๋กœ ๋๋‚จ

 ํฌ๋ ˆ๋”ง ์ฐจ๊ฐ(deduct_credit)์ด ๋‹จ๋… ์ปค๋ฐ‹๋œ ๋’ค, ์ด๋ฒคํŠธ ๊ธฐ๋ก(create_event)์ด๋‚˜ Job ์ƒ์„ฑ(create_job)์—์„œ ์˜ˆ์™ธ๊ฐ€ ํ„ฐ์ง€๋ฉด 

ํฌ๋ ˆ๋”ง์€ ์ด๋ฏธ ๋น ์ง„ ์ƒํƒœ์ธ๋ฐ ๋ถ„์„ ์ž‘์—…์€ ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค. ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ๋Š” ํšŸ์ˆ˜๊ฐ€ ์ค„์—ˆ๋Š”๋ฐ ๊ฒฐ๊ณผ๊ฐ€ ์—†๋Š” ์ƒํ™ฉ์ด ๋œ ๊ฒƒ์ด์—ˆ๋‹ค. ์‹ค์ œ๋กœ credit_service.deduct_credit()์ด db.commit()์„ ์ง์ ‘ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ์—ˆ๊ณ , ์ดํ›„ job_repo.create_job()๊นŒ์ง€ ๊ฐ€๊ธฐ ์ „์—

์–ด๋””์„œ๋“  ํ„ฐ์ง€๋ฉด ๋กค๋ฐฑ ๋Œ€์ƒ ์ž์ฒด๊ฐ€ ์—†์—ˆ๋‹ค. ๋ˆ๊ณผ ์ง๊ฒฐ๋˜๋Š” ํฌ๋ ˆ๋”ง ๊ด€๋ จ ์ด์Šˆ์˜€๊ธฐ์— ์ฝ”๋“œ์—์„œ ํฐ ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•œ๊ฒŒ ๋งž์•˜๋‹ค.

 

# ๊ฐœ์„  ๋ฐฉํ–ฅ
def deduct_and_record(db, user):
    try:
        user.credits -= 1
        record_event(db, ...)
        db.commit()          # ํ•œ ๋ฒˆ๋งŒ
    except:
        db.rollback()
        raise

deduct_credit()์„ flush()๋งŒ ํ•˜๋„๋ก ๋ฐ”๊พธ๊ณ , ์ด๋ฒคํŠธ ์ƒ์„ฑ → Job ์ƒ์„ฑ๊นŒ์ง€ ์ „๋ถ€ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์ง„ํ–‰ํ•œ ๋’ค job_service

๋ ˆ๋ฒจ์—์„œ ํ•œ ๋ฒˆ๋งŒ commit(). ์ค‘๊ฐ„์— ์–ด๋””์„œ ํ„ฐ์ ธ๋„ except → db.rollback() ์œผ๋กœ ํฌ๋ ˆ๋”ง ์ฐจ๊ฐ ์ž์ฒด๊ฐ€ ์ทจ์†Œ๋˜๋Š” ํ˜•์‹์œผ๋กœ ์ˆ˜์ •ํ–ˆ๋‹ค.

BQ ์ „์†ก์ด fire-and-forget ๋ฌด์ฑ…์ž„ํ•œ ๋ฐฐ๋‹ฌ..?

์ด๋ฒคํŠธ๋ฅผ PG์— ์ €์žฅํ•œ ์งํ›„, ThreadPoolExecutor๋กœ BQ ์ „์†ก์„ ๋น„๋™๊ธฐ ์‹คํ–‰๋˜๊ณ  ์žˆ์—ˆ๋‹ค๊ณ  ํ•œ๋‹ค. BQ ์ „์†ก ํ•จ์ˆ˜๊ฐ€ executor.submit()์œผ๋กœ ๋˜์ ธ์ง€๊ณ , ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๋ฐ”๋กœ 200์„ ๋ฆฌํ„ดํ•˜๋Š” ๊ตฌ์กฐ...!

# ๋ฌธ์ œ ์ƒํ™ฉ (์ต๋ช…ํ™”)
executor.submit(send_to_bq, data)
return {"status": "ok"}   # BQ์— ์•ˆ ๋“ค์–ด๊ฐ€๋„ 200

BQ ์ „์†ก์ด ์‹คํŒจํ•ด๋„ ์–ด๋””์—๋„ ๊ธฐ๋ก์ด ๋‚จ์ง€ ์•Š๋Š” ์ƒํ™ฉ์ด์—ˆ๋‹ค..!! ๋„คํŠธ์›Œํฌ ํƒ€์ž„์•„์›ƒ, ์ธ์ฆ ๋งŒ๋ฃŒ, BQ ์ฟผํ„ฐ ์ดˆ๊ณผ ์–ด๋–ค ์ด์œ ๋กœ ์‹คํŒจํ•˜๋“  ๋ฐ์ดํ„ฐ๋Š” ์กฐ์šฉํžˆ ์‚ฌ๋ผ์ง„๋‹ค. ๋ถ„์„ ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์–ด์ œ ์ด๋ฒคํŠธ๊ฐ€ ์™œ ์ ˆ๋ฐ˜๋ฐ–์— ์•ˆ ์žกํžˆ์ง€? ๊ฐ™์€ ์ƒํ™ฉ์ด ์ƒ๊ฒจ๋„ ์›์ธ ์ถ”์ ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ

์ฝ”๋“œ๋กœ ๊ตฌํ˜„์ด ๋˜์–ด์žˆ์—ˆ๊ณ , ์ด ๋˜ํ•œ ์ถ”ํ›„ ์ค‘์š”ํ•œ ์ˆ˜์ •์‚ฌํ•ญ์œผ๋กœ ๋ถˆ๊ฑฐ์งˆ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์˜€๋‹ค.

## ๊ฐœ์„  ๋ฐฉํ–ฅ >> outbox
  def record_event(db, ...):
      event = Event(...)
      db.add(event)
      db.flush()

      outbox = BqOutbox(event_id=event.id)   # ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜
      db.add(outbox)
      db.flush()
      # commit์€ ํ˜ธ์ถœ์ž๊ฐ€ — PG ์ €์žฅ๊ณผ outbox ๊ธฐ๋ก์ด ์›์ž์ ์œผ๋กœ ๋ฌถ์ž„

outbox ํŒจํ„ด์ด๋ž€, ๋ฉ”์‹œ์ง€๋ฅผ ์™ธ๋ถ€์‹œ์Šคํ…œ(BigQuery)์— ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ํŒจํ„ด์œผ๋กœ ๋ณด๋‚ผ ๋ฉ”์‹œ์ง€๋ฅผ ํ…Œ์ด๋ธ”์— ์ ์–ด๋‘๊ณ , ๋‚˜์ค‘์— ๊บผ๋‚ด์„œ ๋ณด๋‚ด๋Š” ํŒจํ„ด์ด๋ผ๊ณ  ํ•œ๋‹ค. ๊ธฐ์กด ๋ฐฉ์‹์ด ์ฃผ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•ด๋‘๊ณ  ๋ฐ”๋กœ ํƒ๋ฐฐ๋ฅผ ๋˜์ง€๋Š” ํ˜•์‹์œผ๋กœ ํƒ๋ฐฐ๊ฐ€ ๋ถ„์‹ค๋ผ๋„

๋ชจ๋ฅด๋Š” ํ˜•ํƒœ์˜€๋‹ค๋ฉด, ์ฃผ๋ฌธ์„œ ์ž‘์„ฑ >> ๋ฐœ์†ก๋Œ€๊ธฐ ๋ชฉ๋ก์— ๊ธฐ๋ก >> ํƒ๋ฐฐ๊ธฐ์‚ฌ๊ฐ€ ๋ชฉ๋กํ™•์ธ >> ๋ฐœ์†ก / ์‹คํŒจ ์‹œ ๋ชฉ๋ก์—

๋‚จ์•„์žˆ๋Š” ๊ฒƒ ๊ธฐ๋ฐ˜์œผ๋กœ ์žฌ์‹œ๋„๋กœ ๋ณด์™„๋œ ํŒจํ„ด์ด๋‹ค.

 

PG(PostgreSQL)์— ์ด๋ฒคํŠธ๋ฅผ ์ €์žฅํ•  ๋•Œ bq_outbox ํ…Œ์ด๋ธ”์—๋„ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜์œผ๋กœ "์ „์†ก ๋Œ€๊ธฐ" ๋ ˆ์ฝ”๋“œ๋ฅผ ๋‚จ๊ธด๋‹ค.

์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ 30์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ pending ๋ ˆ์ฝ”๋“œ๋ฅผ ์ฝ์–ด BQ์— batch insertํ•˜๊ณ , ์„ฑ๊ณตํ•˜๋ฉด sent, ์‹คํŒจํ•˜๋ฉด failed๋กœ ๋งˆํ‚น.

์‹คํŒจํ•œ ๊ฑด์€ ์žฌ์‹œ๋„ ๋Œ€์ƒ์œผ๋กœ ๋‚จ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ์œ ์‹ค์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค!! fire-and-forget → at-least-once delivery ๋ณด์žฅ.

 

 


์ •๋ฆฌํ•˜๋ฉด์„œ

ํ”„๋กœ๋•์…˜ ๋ ˆ๋ฒจ์˜ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋ฉด์„œ, ๋ชจ๋ฅด๋Š”๊ฒŒ ์ •๋ง์ •๋ง ๋งŽ๋‹ค๋Š” ๊ฒƒ์„ ํฌํ•จํ•˜์—ฌ ํ•ต์‹ฌ์€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์— ๊ทธ์น˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ

๋ณ€์ˆ˜์™€ ์ด์Šˆ, ์ถ”ํ›„ ์ˆ˜์ • ๊ฐ€๋Šฅ์„ฑ์„ ๋Œ€๋น„ํ•˜๊ณ  ์ ์  ๋” ๋น„๋Œ€ํ•ด์งˆ ์ฝ”๋“œ ๋ฒ ์ด์Šค๋ฅผ ์Šค์Šค๋กœ๋„ ๊ธฐ๋Šฅ๋ณ„๋กœ ์‰ฝ๊ฒŒ ์ฐพ์•„๊ฐ€ ์ˆ˜์ •ํ•˜๊ณ  ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์ค‘์š”์„ฑ์„ ๋ฐฐ์šด๋‹ค.

 

์ด๋ก ์œผ๋กœ ๋ฐฐ์› ๋˜ DB ACID ๋˜ํ•œ ๋ฐฐ์šด์ ์ด ์žˆ์—ˆ์–ด๋„ ์ด๋ก ์œผ๋กœ๋งŒ ์ƒ๊ฐํ•ด ๊ธฐ์–ต๋„ ๋‚˜์ง€ ์•Š์•˜์ง€๋งŒ ์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ๊ธฐ์ดˆ๊ฐ€ ์ค‘์š”ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ

์ด๋ก ๊ณต๋ถ€๋„ ์ทจ์—…์„ ์œ„ํ•œ CS ์ง€์‹ ๊ณต๋ถ€๋„ ์ทจ์—…์„ ์œ„ํ•จ์ด ์•„๋‹Œ ๋ชจ๋‘ ์‹œ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๊ท€ํ•œ ์„ฑ์žฅ์žฌ๋ฃŒ๊ฐ€ ๋จ์„ ์Šค์Šค๋กœ

์ธ์ง€ํ•˜๊ณ  ๊ฐ€๋ณ๊ฒŒ ์—ฌ๊ฒจ์„  ์•ˆ๋˜๊ฒ ๋‹ค ์ƒ๊ฐํ•˜๊ฒŒ ๋˜๋Š” ์ค‘์š”ํ•œ ๊ฒฝํ—˜์„ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋ชฉ์ฐจ
    Loading...
    PREV NEXT