Skip to content

Latest commit

ย 

History

History
936 lines (704 loc) ยท 42 KB

File metadata and controls

936 lines (704 loc) ยท 42 KB

SpeedCam ์‹œ์Šคํ…œ ์„ฑ๋Šฅ ๋ถ„์„ ๋ณด๊ณ ์„œ

๋ชฉ์ฐจ

  1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š”
  2. ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜
  3. ์„œ๋ฒ„ ์ธํ”„๋ผ ์ŠคํŽ™
  4. ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ
  5. ์•„ํ‚คํ…์ฒ˜ ๋น„๊ต ๋ถ„์„
  6. ๋ชจ๋‹ˆํ„ฐ๋ง ์ง€ํ‘œ
  7. ์ตœ๋Œ€ TPS ๋ฐ ์šฉ๋Ÿ‰ ๋ถ„์„
  8. ๋ฐœ๊ฒฌ๋œ ์ด์Šˆ ๋ฐ ๊ฐœ์„ ์ 
  9. ๊ฒฐ๋ก 

1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

SpeedCam์€ ๋„๋กœ ์œ„ ๊ณผ์† ์ฐจ๋Ÿ‰์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ง€ํ•˜๊ณ  ๋ฒˆํ˜ธํŒ์„ ์ธ์‹ํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ฆผ์„ ์ „์†กํ•˜๋Š” ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์‹ค์‹œ๊ฐ„ ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ํŠน์ง•

  • Event Driven Architecture: MQTT + AMQP ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
  • ๋ถ„์‚ฐ ์‹œ์Šคํ…œ: GCE ์ธ์Šคํ„ด์Šค 6๋Œ€๋กœ ๊ตฌ์„ฑ๋œ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜
  • ์‹ค์‹œ๊ฐ„ OCR ์ฒ˜๋ฆฌ: EasyOCR์„ ํ™œ์šฉํ•œ ํ•œ๊ตญ์–ด ๋ฒˆํ˜ธํŒ ์ธ์‹
  • ์™„์ „ํ•œ ๊ด€์ธก์„ฑ: Prometheus, Grafana, Loki, Jaeger๋ฅผ ํ†ตํ•œ ํ†ตํ•ฉ ๋ชจ๋‹ˆํ„ฐ๋ง

๊ธฐ์ˆ  ์Šคํƒ

  • Backend: Django 4.2 + Gunicorn
  • Message Broker: RabbitMQ 3.13 (MQTT Plugin + AMQP)
  • Database: MySQL 8.0
  • OCR Engine: EasyOCR (Korean + English)
  • Monitoring: Prometheus, Grafana, Loki, Jaeger, OpenTelemetry
  • Infra: GCP Compute Engine (6 instances), Docker Compose
  • Load Testing: k6 (Grafana k6), Python paho-mqtt

2. ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜

2.1 ๊ธฐ์กด ์•„ํ‚คํ…์ฒ˜ (Before)

๊ธฐ์กด ์‹œ์Šคํ…œ์€ Django ๋ชจ๋†€๋ฆฌ์‹ ๊ตฌ์กฐ๋กœ, OCR ์ฒ˜๋ฆฌ๊ฐ€ ๋™๊ธฐ์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ์  ํ•œ๊ณ„๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

graph TB
    subgraph Edge["Edge Device (Raspberry Pi)"]
        Camera["๊ณผ์† ์นด๋ฉ”๋ผ"]
    end

    subgraph Backend["backend (Django)"]
        API["API Handler"]
        OCR["OCR ์ฒ˜๋ฆฌ<br/>(๋™๊ธฐ ์‹คํ–‰)"]
    end

    subgraph Workers["Celery Workers"]
        CW["celery_worker<br/>(์•Œ๋ฆผ ์ „์†ก)"]
        DLQ["celery_worker_dlq"]
    end

    Camera -->|"HTTP POST"| API
    API --> OCR
    Backend --> RMQ["RabbitMQ"]
    CW --> RMQ
    Backend --> MySQL[("MySQL")]
    CW --> MySQL

    style Backend fill:#ffcccc,stroke:#cc0000
    style OCR fill:#ff9999
Loading

์ฃผ์š” ๋ฌธ์ œ์ 

๋ฌธ์ œ ์˜์—ญ ์ƒ์„ธ ๋‚ด์šฉ
OCR ๋™๊ธฐ ์ฒ˜๋ฆฌ OCR ์ž‘์—…(์•ฝ 3์ดˆ)์ด HTTP ์Šค๋ ˆ๋“œ๋ฅผ ์ ์œ ํ•˜์—ฌ ์„œ๋ฒ„ ์ฒ˜๋ฆฌ๋Ÿ‰ ์ €ํ•˜
Edge Device ๋ธ”๋กœํ‚น ์„œ๋ฒ„ ์‘๋‹ต ๋Œ€๊ธฐ(3์ดˆ+)๋กœ ์ธํ•œ ์—ฐ์† ๊ฐ์ง€ ๋ถˆ๊ฐ€, ๋ฐ์ดํ„ฐ ์œ ์‹ค ์œ„ํ—˜
HTTP ๊ธฐ๋ฐ˜ IoT ํ†ต์‹  ์š”์ฒญ๋งˆ๋‹ค TCP ์—ฐ๊ฒฐ, ๋ฉ”์‹œ์ง€ ๋ณด์žฅ ์—†์Œ, ์˜คํ”„๋ผ์ธ ์ฒ˜๋ฆฌ ๋ถˆ๊ฐ€
์žฅ์•  ์ „ํŒŒ OCR ์žฅ์•  ์‹œ API ์„œ๋น„์Šค ์ „์ฒด ์˜ํ–ฅ, ๋…๋ฆฝ ํ™•์žฅ ๋ถˆ๊ฐ€

์„ฑ๋Šฅ ์ง€ํ‘œ (Before) โ€” ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์กฐ ๊ธฐ๋ฐ˜ ์ถ”์ •๊ฐ’

๊ธฐ์กด ์•„ํ‚คํ…์ฒ˜๋Š” ํ˜„์žฌ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋ณ„๋„๋กœ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ˆ˜์น˜๋Š” ๋™๊ธฐ OCR ์ฒ˜๋ฆฌ ์‹œ๊ฐ„(EasyOCR CPU ๊ธฐ์ค€ ~3์ดˆ)๊ณผ ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์กฐ๋กœ๋ถ€ํ„ฐ ๋„์ถœํ•œ ์„ค๊ณ„ ๊ธฐ๋ฐ˜ ์ถ”์ •๊ฐ’์ž…๋‹ˆ๋‹ค.

  • ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„: 3,000ms ์ด์ƒ (HTTP ์ˆ˜์‹  โ†’ OCR ์™„๋ฃŒ๊นŒ์ง€ ๋™๊ธฐ ์ฒ˜๋ฆฌ)
  • Edge Device ๋ธ”๋กœํ‚น: 3,000ms ์ด์ƒ (HTTP ์‘๋‹ต ๋Œ€๊ธฐ)
  • ๋ฉ”์‹œ์ง€ ๋ณด์žฅ: ์—†์Œ
  • ์žฅ์•  ๊ฒฉ๋ฆฌ: ๋ถˆ๊ฐ€๋Šฅ (๋ชจ๋†€๋ฆฌ์‹ ๊ตฌ์กฐ)

2.2 ํ˜„์žฌ ์•„ํ‚คํ…์ฒ˜ (After) - Event Driven Architecture

๊ธฐ์กด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Event Driven Architecture๋กœ ์ „ํ™˜ํ•˜์—ฌ MQTT ๊ธฐ๋ฐ˜ IoT ํ†ต์‹ ๊ณผ AMQP ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

graph TB
    subgraph Edge["Edge Device"]
        Camera["๊ณผ์† ์นด๋ฉ”๋ผ"]
    end

    subgraph Main["main (Django)"]
        API["API Handler"]
        MQTT_Sub["MQTT Subscriber"]
        Publisher["Event Publisher"]
    end

    subgraph Workers["Event Processors"]
        OCR["ocr-worker<br/>โ€ข ๊ฐ์ง€ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ<br/>โ€ข OCR ์ˆ˜ํ–‰"]
        Alert["alert-worker<br/>โ€ข ์™„๋ฃŒ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ<br/>โ€ข FCM ๋ฐœ์†ก"]
    end

    subgraph MessageBroker["RabbitMQ"]
        MQTT["MQTT Plugin"]
        Queue1[("๊ฐ์ง€ ์ด๋ฒคํŠธ ํ")]
        Queue2[("์•Œ๋ฆผ ์ด๋ฒคํŠธ ํ")]
    end

    subgraph Storage["Google Cloud Storage"]
        GCS[("GCS Bucket<br/>๋ฒˆํ˜ธํŒ ์ด๋ฏธ์ง€")]
    end

    Camera -->|"MQTT Publish"| MQTT
    Camera -->|"์ด๋ฏธ์ง€ ์—…๋กœ๋“œ"| GCS
    MQTT --> MQTT_Sub
    Publisher --> Queue1
    Queue1 --> OCR
    OCR -->|"์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ"| GCS
    OCR --> Queue2
    Queue2 --> Alert

    Main --> DB1[("default")]
    Main --> DB2[("vehicles_db")]
    OCR --> DB3[("detections_db")]
    Alert --> DB4[("notifications_db")]

    style Main fill:#90EE90
    style OCR fill:#87CEEB
    style Alert fill:#DDA0DD
    style MessageBroker fill:#FFB6C1
    style Storage fill:#FFFACD
Loading

์•„ํ‚คํ…์ฒ˜ ํŠน์ง•

์ปดํฌ๋„ŒํŠธ ์—ญํ•  ํ”„๋กœํ† ์ฝœ ํŠน์ง•
Edge Device ๊ณผ์† ์ฐจ๋Ÿ‰ ๊ฐ์ง€ MQTT QoS 1, ๊ฒฝ๋Ÿ‰, ์˜๊ตฌ ์—ฐ๊ฒฐ
main (Django) API + MQTT ๊ตฌ๋… HTTP + MQTT ์ด๋ฒคํŠธ ๋ฐœํ–‰๋งŒ ๋‹ด๋‹น
ocr-worker ๋ฒˆํ˜ธํŒ OCR ์ฒ˜๋ฆฌ AMQP ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ, concurrency=1
alert-worker FCM ํ‘ธ์‹œ ์•Œ๋ฆผ AMQP ๊ณ ์„ฑ๋Šฅ, concurrency=100
RabbitMQ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค MQTT + AMQP At-Least-Once ๋ณด์žฅ

End-to-End ์ด๋ฒคํŠธ ํ๋ฆ„

sequenceDiagram
    participant Edge as Edge Device
    participant RMQ as RabbitMQ
    participant Main as main
    participant OCR as ocr-worker
    participant Alert as alert-worker
    participant User as ์‚ฌ์šฉ์ž ์•ฑ

    Edge->>RMQ: MQTT Publish (๊ณผ์† ์ฐจ๋Ÿ‰ ๊ฐ์ง€)
    RMQ-->>Edge: PUBACK (์ฆ‰์‹œ)
    RMQ->>Main: ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ (subscribe)
    Main->>Main: DB ์ €์žฅ (pending)
    Main->>RMQ: ๊ฐ์ง€ ์ด๋ฒคํŠธ ๋ฐœํ–‰ (AMQP)

    RMQ->>OCR: ๊ฐ์ง€ ์ด๋ฒคํŠธ ์ˆ˜์‹ 
    OCR->>OCR: ๋ฒˆํ˜ธํŒ OCR ์ฒ˜๋ฆฌ
    OCR->>OCR: DB ์—…๋ฐ์ดํŠธ (completed)
    OCR->>RMQ: OCR ์™„๋ฃŒ ์ด๋ฒคํŠธ ๋ฐœํ–‰

    RMQ->>Alert: ์™„๋ฃŒ ์ด๋ฒคํŠธ ์ˆ˜์‹ 
    Alert->>User: FCM Push ์•Œ๋ฆผ
Loading

3. ์„œ๋ฒ„ ์ธํ”„๋ผ ์ŠคํŽ™

์ด 6๋Œ€์˜ GCE ์ธ์Šคํ„ด์Šค๋กœ ๊ตฌ์„ฑ๋œ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ์ธ์Šคํ„ด์Šค๋Š” asia-northeast3-a ์กด์— ์œ„์น˜ํ•˜๋ฉฐ Ubuntu 22.04 LTS, Kernel 6.8.0-1045-gcp, Docker ๊ธฐ๋ฐ˜์œผ๋กœ ์šด์˜๋ฉ๋‹ˆ๋‹ค.

3.1 ์ธ์Šคํ„ด์Šค ์ƒ์„ธ ์ŠคํŽ™

์ธ์Šคํ„ด์Šค ๋จธ์‹  ํƒ€์ž… vCPU RAM ๋””์Šคํฌ ๋””์Šคํฌ ์‚ฌ์šฉ๋ฅ  ๋‚ด๋ถ€ IP ์—ญํ• 
speedcam-app e2-small 2 2GB 20GB 34% (6.4GB) 10.178.0.4 API ์„œ๋ฒ„
speedcam-db e2-medium 2 4GB 29GB 20% (5.8GB) 10.178.0.2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
speedcam-mq e2-small 2 2GB 20GB 26% (4.9GB) 10.178.0.7 ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค
speedcam-ocr e2-small 2 2GB 20GB 87% (17GB) 10.178.0.3 OCR Worker
speedcam-alert e2-small 2 2GB 20GB 31% (5.9GB) 10.178.0.6 Alert Worker
speedcam-mon e2-small 2 2GB 20GB 37% (7.0GB) 10.178.0.5 ๋ชจ๋‹ˆํ„ฐ๋ง

3.2 ์ฃผ์š” ์ปจํ…Œ์ด๋„ˆ ๊ตฌ์„ฑ

์ธ์Šคํ„ด์Šค ์ปจํ…Œ์ด๋„ˆ ์—ญํ• 
speedcam-app Django + Gunicorn REST API (GUNICORN_WORKERS=2)
Traefik ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ
Flower Celery ๋ชจ๋‹ˆํ„ฐ๋ง
Promtail ๋กœ๊ทธ ์ˆ˜์ง‘ ์—์ด์ „ํŠธ
cAdvisor ์ปจํ…Œ์ด๋„ˆ ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
speedcam-db MySQL 8.0 ๋ฉ”์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
mysqld-exporter MySQL ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
Promtail ๋กœ๊ทธ ์ˆ˜์ง‘ ์—์ด์ „ํŠธ
cAdvisor ์ปจํ…Œ์ด๋„ˆ ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
speedcam-mq RabbitMQ 3.13 MQTT + AMQP ๋ธŒ๋กœ์ปค
Promtail ๋กœ๊ทธ ์ˆ˜์ง‘ ์—์ด์ „ํŠธ
cAdvisor ์ปจํ…Œ์ด๋„ˆ ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
speedcam-ocr Celery OCR Worker EasyOCR ์ฒ˜๋ฆฌ (concurrency=1)
Promtail ๋กœ๊ทธ ์ˆ˜์ง‘ ์—์ด์ „ํŠธ
cAdvisor ์ปจํ…Œ์ด๋„ˆ ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
speedcam-alert Celery Alert Worker FCM ์•Œ๋ฆผ ๋ฐœ์†ก (concurrency=100)
Promtail ๋กœ๊ทธ ์ˆ˜์ง‘ ์—์ด์ „ํŠธ
cAdvisor ์ปจํ…Œ์ด๋„ˆ ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
speedcam-mon Prometheus ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
Grafana ์‹œ๊ฐํ™” ๋Œ€์‹œ๋ณด๋“œ
Loki ๋กœ๊ทธ ์ˆ˜์ง‘
Jaeger ๋ถ„์‚ฐ ์ถ”์ 
OpenTelemetry Collector ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ˆ˜์ง‘
Promtail ๋กœ๊ทธ ์ˆ˜์ง‘ ์—์ด์ „ํŠธ
cAdvisor ์ปจํ…Œ์ด๋„ˆ ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘

3.3 ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ ํ˜„ํ™ฉ

์ธ์Šคํ„ด์Šค RAM ์‚ฌ์šฉ RAM ์—ฌ์œ  ๋ฉ”๋ชจ๋ฆฌ ์ง‘์•ฝ์  ํ”„๋กœ์„ธ์Šค ๋น„๊ณ 
speedcam-app 661MB/2GB 1.1GB Gunicorn 2 workers ์•ˆ์ •์ 
speedcam-db 853MB/4GB 2.6GB MySQL ๋ฒ„ํผํ’€ ์ถฉ๋ถ„ํ•œ ์—ฌ์œ 
speedcam-mq 471MB/2GB 1.2GB RabbitMQ ์•ˆ์ •์ 
speedcam-ocr 1.0GB/2GB 721MB EasyOCR ๋ชจ๋ธ (1.5GB) ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์œ„ํ—˜
speedcam-alert 433MB/2GB 1.3GB ๊ฒฝ๋Ÿ‰ ์›Œ์ปค ์ถฉ๋ถ„ํ•œ ์—ฌ์œ 
speedcam-mon 1.5GB/2GB 264MB Prometheus + Grafana ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์œ„ํ—˜

์ฃผ์˜์‚ฌํ•ญ:

  • speedcam-ocr: EasyOCR ๋ชจ๋ธ ๋กœ๋”ฉ์œผ๋กœ ์ธํ•œ ๋†’์€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋ฅ , concurrency๋ฅผ 1๋กœ ์ œํ•œ
  • speedcam-mon: ๋ชจ๋‹ˆํ„ฐ๋ง ์Šคํƒ์˜ ๋ฉ”๋ชจ๋ฆฌ ์ง‘์•ฝ์  ํŠน์„ฑ์œผ๋กœ 264MB ์—ฌ์œ ๋ถ„๋งŒ ํ™•๋ณด

4. ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ

4.1 ํ…Œ์ŠคํŠธ ๋ชฉ์ 

์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ์˜ ์‹œ์Šคํ…œ ์„ฑ๋Šฅ๊ณผ ์•ˆ์ •์„ฑ์„ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ ๋ชฉํ‘œ๋กœ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ชฉํ‘œ ์„ธ๋ถ€ ๋‚ด์šฉ
์„ฑ๋Šฅ ํ•œ๊ณ„ ํŒŒ์•… ๊ฐ ์ปดํฌ๋„ŒํŠธ๋ณ„ ์ตœ๋Œ€ ์ฒ˜๋ฆฌ๋Ÿ‰ ์ธก์ •
๋ณ‘๋ชฉ ์ง€์  ์‹๋ณ„ Event Driven ํŒŒ์ดํ”„๋ผ์ธ ๊ฐ ๋‹จ๊ณ„๋ณ„ ์†Œ์š” ์‹œ๊ฐ„ ๋ถ„์„
์•„ํ‚คํ…์ฒ˜ ๊ฒ€์ฆ ๊ธฐ์กด ๋™๊ธฐ ์ฒ˜๋ฆฌ ๋Œ€๋น„ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ์˜ ์„ฑ๋Šฅ ๊ฐœ์„  ์ •๋„ ํ™•์ธ
์•ˆ์ •์„ฑ ํ™•์ธ ์ŠคํŒŒ์ดํฌ ํŠธ๋ž˜ํ”ฝ ๋ฐœ์ƒ ์‹œ ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ ๊ฒ€์ฆ

4.2 ํ…Œ์ŠคํŠธ ๋„๊ตฌ

๋„๊ตฌ ์šฉ๋„ ํŠน์ง•
k6 (Grafana k6) HTTP API ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ Prometheus Remote Write๋กœ ๋ฉ”ํŠธ๋ฆญ ์‹ค์‹œ๊ฐ„ ์ „์†ก, ์›น ๋Œ€์‹œ๋ณด๋“œ + Grafana ์—ฐ๋™
Python + paho-mqtt MQTT ํŒŒ์ดํ”„๋ผ์ธ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ์‹ค์ œ ํ•œ๊ตญ์–ด ๋ฒˆํ˜ธํŒ ์ด๋ฏธ์ง€๋ฅผ GCS์— ์ €์žฅํ•˜์—ฌ ์‹ค ํŒŒ์ดํ”„๋ผ์ธ ํ…Œ์ŠคํŠธ, EasyOCR ์‹ค์ œ ๋™์ž‘ ๊ฒ€์ฆ

4.2.1 ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๋ฐ ์กฐ๊ฑด

ํ•ญ๋ชฉ ์ƒ์„ธ
ํ…Œ์ŠคํŠธ ์ผ์‹œ 2026-02-12 (k6 4์‹œ๋‚˜๋ฆฌ์˜ค + MQTT 3์‹œ๋‚˜๋ฆฌ์˜ค)
k6 ์‹คํ–‰ ์œ„์น˜ speedcam-app ์ธ์Šคํ„ด์Šค ๋‚ด๋ถ€ (localhost ํ˜ธ์ถœ)
MQTT ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์œ„์น˜ speedcam-app โ†’ speedcam-mq (๋‚ด๋ถ€ IP 10.178.0.7)
๋„คํŠธ์›Œํฌ ํ™˜๊ฒฝ ๋™์ผ VPC (asia-northeast3), ์ธ์Šคํ„ด์Šค ๊ฐ„ ์ง€์—ฐ <1ms
๋ถ€ํ•˜ ๋ฐœ์ƒ๊ธฐ โ†’ ์„œ๋ฒ„ ์ง€์—ฐ k6: ~0ms (localhost), MQTT: <1ms (๊ฐ™์€ VPC)
์‹œ์Šคํ…œ ์ƒํƒœ ํ…Œ์ŠคํŠธ ์™ธ ํŠธ๋ž˜ํ”ฝ ์—†์Œ (์ „์šฉ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ)

์ฐธ๊ณ : k6 HTTP ํ…Œ์ŠคํŠธ๋Š” speedcam-app ์ž์ฒด์—์„œ localhost๋กœ ํ˜ธ์ถœํ•˜์˜€์œผ๋ฏ€๋กœ, ์ธก์ •๋œ ์‘๋‹ต ์‹œ๊ฐ„์€ ์ˆœ์ˆ˜ ์„œ๋ฒ„ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์— ๊ฐ€๊น์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ํด๋ผ์ด์–ธํŠธ์—์„œ์˜ ์‘๋‹ต ์‹œ๊ฐ„์€ ๋„คํŠธ์›Œํฌ ์ง€์—ฐ์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.


4.3 HTTP API ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ (k6)

Django REST API์˜ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ๊ณผ ์‘๋‹ต ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๊ธฐ ์œ„ํ•ด 4๊ฐ€์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

4.3.1 ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค

์‹œ๋‚˜๋ฆฌ์˜ค VUs Executor ์ง€์†์‹œ๊ฐ„ ์‹œ์ž‘ ์‹œ์  ์„ค๋ช…
dashboard_polling 3 (constant) constant-vus 2๋ถ„ 0s ๋Œ€์‹œ๋ณด๋“œ ํด๋ง (๊ฐ์ง€๋ชฉ๋ก 5์ดˆ, ์•Œ๋ฆผ 10์ดˆ, ํ†ต๊ณ„ 30์ดˆ ์ฃผ๊ธฐ)
admin_ops 2 constant-arrival-rate (2/min) 2๋ถ„ 0s ๊ด€๋ฆฌ์ž ์ž‘์—… (์ฐจ๋Ÿ‰ ๋“ฑ๋ก + FCM ํ† ํฐ ์—…๋ฐ์ดํŠธ)
mixed_workload 0โ†’5โ†’9โ†’9โ†’0 ramping-vus 2๋ถ„30์ดˆ 2m ์ฝ๊ธฐ 60% + ํŒŒ์ดํ”„๋ผ์ธ ์ƒํƒœ 30% + ์“ฐ๊ธฐ 10%
spike_resilience 0โ†’3โ†’15โ†’15โ†’3โ†’0 ramping-vus 1๋ถ„10์ดˆ 4m30s ๊ธ‰๊ฒฉํ•œ ํŠธ๋ž˜ํ”ฝ ์ฆ๊ฐ€ ์‹œ ํšŒ๋ณต๋ ฅ (15 VUs = 4 ํ•ธ๋“ค๋Ÿฌ ๋Œ€๋น„ 3.75๋ฐฐ)

์ด ํ…Œ์ŠคํŠธ ์‹œ๊ฐ„: 5๋ถ„ 40์ดˆ, ์ตœ๋Œ€ ๋™์‹œ VUs: 18

4.3.2 ์ „์ฒด ๊ฒฐ๊ณผ ์š”์•ฝ

โœ… ์ด ์š”์ฒญ: 2,297๊ฑด (ํ‰๊ท  6.75 req/s)
โœ… ์ „์ฒด p95 ์‘๋‹ต์‹œ๊ฐ„: 38.85ms
โœ… ์—๋Ÿฌ์œจ: 0.21% (5/2,277๊ฑด) - FCM ํ† ํฐ ์—…๋ฐ์ดํŠธ ์—”๋“œํฌ์ธํŠธ ๋ฌธ์ œ
โœ… ๋ชจ๋“  ์ž„๊ณ„๊ฐ’(Threshold) ํ†ต๊ณผ
โœ… Prometheus Remote Write โ†’ Grafana ๋ฉ”ํŠธ๋ฆญ ๊ธฐ๋ก

4.3.3 ์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„ ์ƒ์„ธ ๊ฒฐ๊ณผ

์‘๋‹ต ์‹œ๊ฐ„ ๋ถ„ํฌ

๋ฉ”ํŠธ๋ฆญ avg min med max p(90) p(95)
dashboard_req_duration 19.27ms 9.73ms 17.65ms 118.73ms 26.89ms 30.6ms
admin_req_duration 17.78ms 4.21ms 17.82ms 53.17ms 21.05ms 23.23ms
detections_list_duration 23.98ms 14.01ms 20.53ms 162.31ms 33.3ms 43.29ms
statistics_req_duration 23.44ms 13.31ms 20.4ms 127.43ms 34.03ms 42.42ms
pending_read_duration 13.08ms 10.3ms 12.55ms 27.22ms 15.12ms 16.6ms
spike_resilience (overall) 21.42ms 8.63ms 18.79ms 162.31ms 31.6ms 40.54ms
http_req_duration (์ „์ฒด) 20.72ms 3.75ms 18.23ms 162.31ms 30.14ms 38.85ms

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: k6 Grafana ๋Œ€์‹œ๋ณด๋“œ - 4 ์‹œ๋‚˜๋ฆฌ์˜ค ์‘๋‹ต์‹œ๊ฐ„ ๊ทธ๋ž˜ํ”„]

์ž„๊ณ„์น˜(Threshold) ๊ฒ€์ฆ ๊ฒฐ๊ณผ:

์ž„๊ณ„์น˜ ๊ธฐ์ค€ ์‹ค์ธก ํŒ์ •
dashboard_req_duration p(95) < 200ms 30.6ms โœ… PASS
detections_list_duration p(95) < 300ms 43.29ms โœ… PASS
statistics_req_duration p(95) < 500ms 42.42ms โœ… PASS
pending_read_duration p(95) < 500ms 16.6ms โœ… PASS
admin_req_duration p(95) < 300ms 23.23ms โœ… PASS
spike_resilience p(95) < 1500ms 40.54ms โœ… PASS
errors (์ „์ฒด) < 5% 0.21% โœ… PASS
errors (dashboard) < 1% 0.00% โœ… PASS
errors (spike) < 10% 0.00% โœ… PASS

์ฃผ์š” ์ธ์‚ฌ์ดํŠธ:

  • ๋Œ€์‹œ๋ณด๋“œ ํด๋ง ํ‰๊ท  19ms: ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ๊ฐ€ ๋งค์šฐ ๋น ๋ฆ„
  • ์ŠคํŒŒ์ดํฌ ์ƒํ™ฉ(15 VUs)์—์„œ๋„ p95 40ms: ๊ธ‰๊ฒฉํ•œ ํŠธ๋ž˜ํ”ฝ ์ฆ๊ฐ€ ์‹œ์—๋„ ์•ˆ์ •์  ์‘๋‹ต ์œ ์ง€
  • ๊ฐ€์„ค ๋Œ€๋น„ 37๋ฐฐ ์ข‹์€ ์„ฑ๋Šฅ: ์ŠคํŒŒ์ดํฌ ๊ฐ€์„ค(p95 < 1500ms) ๋Œ€๋น„ ์‹ค์ธก 40ms
  • 4 ํ•ธ๋“ค๋Ÿฌ(Gunicorn 2wร—2t)๋กœ 15 VUs ์ถฉ๋ถ„ํžˆ ์†Œํ™”: ์‹ค์ œ ํฌํ™”์ ์€ 50+ VUs

4.3.4 Checks ๊ฒฐ๊ณผ

Check ํ•ญ๋ชฉ ์„ฑ๊ณต/์ „์ฒด ์„ฑ๊ณต๋ฅ  ๋น„๊ณ 
์„œ๋ฒ„ ํ—ฌ์Šค์ฒดํฌ 1/1 100% โœ…
์ฐจ๋Ÿ‰ ๋“ฑ๋ก (201) โœ… 100% โœ… admin_ops + mixed ์‹œ๋‚˜๋ฆฌ์˜ค
FCM ํ† ํฐ ์—…๋ฐ์ดํŠธ (200) 0/5 0% โŒ PATCH ์—”๋“œํฌ์ธํŠธ ํ˜ธํ™˜ ๋ฌธ์ œ
๊ฐ์ง€ ๋ชฉ๋ก (200) โœ… 100% โœ… dashboard + spike ์‹œ๋‚˜๋ฆฌ์˜ค
์•Œ๋ฆผ ๋ชฉ๋ก (200) โœ… 100% โœ… dashboard ์‹œ๋‚˜๋ฆฌ์˜ค
ํ†ต๊ณ„ ์กฐํšŒ (200) โœ… 100% โœ… dashboard + spike ์‹œ๋‚˜๋ฆฌ์˜ค
๋Œ€๊ธฐ ๋ชฉ๋ก (200) โœ… 100% โœ… mixed ์‹œ๋‚˜๋ฆฌ์˜ค
ํ˜ผํ•ฉ ์ฝ๊ธฐ (200) โœ… 100% โœ… mixed ์‹œ๋‚˜๋ฆฌ์˜ค
ํ˜ผํ•ฉ ์ฐจ๋Ÿ‰ ๋“ฑ๋ก (201) โœ… 100% โœ… mixed ์‹œ๋‚˜๋ฆฌ์˜ค
์ŠคํŒŒ์ดํฌ ๊ฐ์ง€ ๋ชฉ๋ก โœ… 100% โœ…
์ŠคํŒŒ์ดํฌ ์•Œ๋ฆผ ๋ชฉ๋ก โœ… 100% โœ…
์ŠคํŒŒ์ดํฌ ํ†ต๊ณ„ โœ… 100% โœ…

์ „์ฒด: 2,272/2,277 checks ์„ฑ๊ณต (99.78%). ์‹คํŒจ 5๊ฑด์€ ๋ชจ๋‘ FCM ํ† ํฐ ์—…๋ฐ์ดํŠธ PATCH ์—”๋“œํฌ์ธํŠธ.

4.3.5 HTTP API ์ตœ๋Œ€ TPS ๋ถ„์„

ํ•ญ๋ชฉ ๊ฐ’ ๊ทผ๊ฑฐ
ํ˜„์žฌ ์„ค์ • GUNICORN_WORKERS=2 (๊ฐ 2 threads = ์ด 4 HTTP handlers) ๋ฐฐํฌ ํ™˜๊ฒฝ (env.example ๊ธฐ๋ณธ๊ฐ’=4์™€ ๋‹ค๋ฆ„)
4์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ 15 VUs์—์„œ p95=40.54ms, ์—๋Ÿฌ์œจ 0% k6 4์‹œ๋‚˜๋ฆฌ์˜ค ์‹ค์ธก
์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ 50 VUs์—์„œ p95=2,230ms, ์—๋Ÿฌ์œจ 1.5% k6 stress_ramp ์‹ค์ธก
ํฌํ™”์  30~50 VUs ์‚ฌ์ด 15 VUs(์ •์ƒ) โ†’ 50 VUs(์„ฑ๋Šฅ ์ €ํ•˜)
์•ˆ์ • ์ตœ๋Œ€ TPS ~25 req/s (50 VUs, e2-small์—์„œ k6+์„œ๋ฒ„ ๊ณต์œ  ์‹œ) ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ ์‹ค์ธก
์ด๋ก  ์ตœ๋Œ€ TPS ~80-100 req/s 4 handlers ร— ํ‰๊ท  20ms ๊ธฐ์ค€
์ฃผ์š” ๋ณ‘๋ชฉ Gunicorn ํ•ธ๋“ค๋Ÿฌ ํฌํ™” + DB ์ปค๋„ฅ์…˜ (CONN_MAX_AGE ๋ฏธ์„ค์ •) ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ ๋ถ„์„

์ธก์ • ๊ทผ๊ฑฐ: 4์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ(๊ฐ€์„ค ๊ธฐ๋ฐ˜)์—์„œ 15 VUs๊นŒ์ง€ ์ •์ƒ, ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ(50 VUs)์—์„œ ํฌํ™” ํ™•์ธ. ์‹ค์ธก ์•ˆ์ • TPS ~25 req/s๋Š” k6๊ฐ€ ๋™์ผ ์ธ์Šคํ„ด์Šค์—์„œ ์‹คํ–‰๋œ ๊ฒฐ๊ณผ์ด๋ฏ€๋กœ ๋ณ„๋„ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์šฉ ์‹œ ๋” ๋†’์„ ์ˆ˜ ์žˆ์Œ.

ํ™•์žฅ ๋ฐฉ๋ฒ•:

  1. CONN_MAX_AGE ์„ค์ •์œผ๋กœ ์ปค๋„ฅ์…˜ ํ’€๋ง ํ™œ์„ฑํ™”
  2. GUNICORN_WORKERS ์ฆ๊ฐ€ (CPU ์ฝ”์–ด๋‹น 1-2๊ฐœ ๊ถŒ์žฅ)
  3. ์ธ์Šคํ„ด์Šค ์—…๊ทธ๋ ˆ์ด๋“œ (e2-medium ์ด์ƒ)

4.3.6 HTTP API ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ (ํ•œ๊ณ„์  ํƒ์ƒ‰)

๊ธฐ์กด ํ…Œ์ŠคํŠธ(์ตœ๋Œ€ 15 VUs)์—์„œ๋Š” ์‹œ์Šคํ…œ์ด ์—ฌ์œ  ์žˆ๊ฒŒ ์ฒ˜๋ฆฌํ•˜์—ฌ ์‹ค์ œ ํ•œ๊ณ„์ ์„ ํŒŒ์•…ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด VUs๋ฅผ ์ ์ง„์ ์œผ๋กœ 50๊นŒ์ง€ ์˜ฌ๋ฆฌ๋Š” ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ฃผ์˜: k6๊ฐ€ speedcam-app ๋™์ผ ์ธ์Šคํ„ด์Šค(e2-small, 2 vCPU)์—์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ, k6 ์ž์ฒด์˜ CPU/๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ์ด ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ๊ตฌ์„ฑ

Phase ์‹œ๋‚˜๋ฆฌ์˜ค VUs ์ง€์†์‹œ๊ฐ„ ์š”์ฒญ ์œ ํ˜•
Phase 1 stress_ramp (์ฝ๊ธฐ ์ „์šฉ) 0โ†’10โ†’30โ†’50โ†’0 3๋ถ„30์ดˆ GET ์ฝ๊ธฐ 100%
Phase 2 stress_mixed (ํ˜ผํ•ฉ) 0โ†’10โ†’30โ†’50โ†’0 3๋ถ„ ์ฝ๊ธฐ 80% + ์“ฐ๊ธฐ 20%

์ „์ฒด ๊ฒฐ๊ณผ (Prometheus Remote Write ํ™œ์„ฑ, Grafana ๋ฉ”ํŠธ๋ฆญ ๊ธฐ๋ก๋จ)

์ด ์š”์ฒญ:      10,525๊ฑด (ํ‰๊ท  25.1 req/s)
์—๋Ÿฌ์œจ:       1.50% (158๊ฑด ์‹คํŒจ)
p95 ์‘๋‹ต์‹œ๊ฐ„: 2,230ms
์ตœ๋Œ€ ์‘๋‹ต์‹œ๊ฐ„: 4,260ms

์‘๋‹ต ์‹œ๊ฐ„ ๋ถ„ํฌ

๋ฉ”ํŠธ๋ฆญ avg med p(90) p(95) max
์ „์ฒด (req_duration) 790ms 742ms 1,770ms 2,230ms 4,260ms
์ฝ๊ธฐ (read_latency) 800ms 751ms 1,770ms 2,230ms 4,260ms
์“ฐ๊ธฐ (write_latency) 685ms 526ms 1,730ms 2,020ms 2,790ms

Phase๋ณ„ ์—๋Ÿฌ์œจ

Phase Check ์„ฑ๊ณต๋ฅ  ์‹คํŒจ์œจ
stress_ramp (50 VUs, ์ฝ๊ธฐ) status is 200 97% 3%
stress_mixed (50 VUs, ์ฝ๊ธฐ) read 200 99% 1%
stress_mixed (50 VUs, ์“ฐ๊ธฐ) write 201 99% 1%

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ์˜ํ–ฅ ์ฐธ๊ณ : k6์™€ Prometheus Remote Write๊ฐ€ ๋™์ผ ์ธ์Šคํ„ด์Šค(e2-small, 2 vCPU)์—์„œ ์‹คํ–‰๋˜์–ด, k6์˜ ์š”์ฒญ ์ƒ์„ฑ ์†๋„๊ฐ€ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค (54 req/s โ†’ 25 req/s). ์ด๋กœ ์ธํ•ด ์„œ๋ฒ„์— ์‹ค์ œ ๋„๋‹ฌํ•˜๋Š” ๋ถ€ํ•˜๊ฐ€ ์ค„์–ด ์—๋Ÿฌ์œจ์€ ๋‚ฎ์•„์ง€๋‚˜, ์‹œ์Šคํ…œ ์ „์ฒด ๋ฆฌ์†Œ์Šค ๊ฒฝํ•ฉ์œผ๋กœ ์‘๋‹ต ์‹œ๊ฐ„(p95)์€ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: k6 Grafana ๋Œ€์‹œ๋ณด๋“œ - VUs ๋ณ€ํ™”์— ๋”ฐ๋ฅธ ์‘๋‹ต์‹œ๊ฐ„/์—๋Ÿฌ์œจ ๊ทธ๋ž˜ํ”„]

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: Container Metrics - speedcam-app์˜ CPU/Memory ๊ทธ๋ž˜ํ”„ (์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ ๊ตฌ๊ฐ„)]

๋ถ€ํ•˜ ์ˆ˜์ค€๋ณ„ ์„ฑ๋Šฅ ๋น„๊ต (์‹ค์ธก)

VUs ์‹œ๋‚˜๋ฆฌ์˜ค p95 ์—๋Ÿฌ์œจ ์ฒ˜๋ฆฌ๋Ÿ‰ ํŒ์ •
15 spike_resilience 49ms 0% 6.7 req/s โœ… ์ •์ƒ
50 stress_ramp 2,230ms 3% 25.1 req/s โš ๏ธ ์„ฑ๋Šฅ ์ €ํ•˜
50 stress_mixed 2,020ms 1% 25.1 req/s โš ๏ธ ์„ฑ๋Šฅ ์ €ํ•˜

ํ•ต์‹ฌ ๋ฐœ๊ฒฌ:

  • 15 VUs โ†’ 50 VUs: p95๊ฐ€ 49ms์—์„œ 2,230ms๋กœ 45๋ฐฐ ์•…ํ™”
  • 50 VUs์—์„œ median=742ms โ†’ ๋Œ€๋ถ€๋ถ„์˜ ์š”์ฒญ์ด 700ms ์ด์ƒ ์†Œ์š” (15 VUs์—์„œ 17ms ๋Œ€๋น„ 43๋ฐฐ)
  • ์—๋Ÿฌ์œจ์€ 1.5%๋กœ ์„œ๋น„์Šค ๊ฐ€์šฉ ๋ฒ”์œ„์ด๋‚˜, ์‘๋‹ต ์‹œ๊ฐ„ ์ €ํ•˜๊ฐ€ ์‹ฌ๊ฐ (SLA ๊ธฐ์ค€ ์œ„๋ฐ˜ ๊ฐ€๋Šฅ)
  • ์“ฐ๊ธฐ(POST)๊ฐ€ ์ฝ๊ธฐ(GET) ๋Œ€๋น„ med ๊ธฐ์ค€ ~30% ๋น ๋ฆ„ (526ms vs 751ms) โ€” DB ์ฝ๊ธฐ๊ฐ€ ์“ฐ๊ธฐ๋ณด๋‹ค ๋ฌด๊ฑฐ์šด ํŒจํ„ด
  • e2-small์—์„œ k6+์„œ๋ฒ„ ๋™์‹œ ์‹คํ–‰์˜ ํ•œ๊ณ„: ๋ณ„๋„ ๋ถ€ํ•˜ ๋ฐœ์ƒ๊ธฐ ์ธ์Šคํ„ด์Šค ์‚ฌ์šฉ ์‹œ ๋” ์ •ํ™•ํ•œ ์ธก์ • ๊ฐ€๋Šฅ

4.4 MQTT ์ด๋ฒคํŠธ ํŒŒ์ดํ”„๋ผ์ธ ํ…Œ์ŠคํŠธ

์‹ค์ œ Edge Device์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ณผ์† ๊ฐ์ง€ ์ด๋ฒคํŠธ๋ถ€ํ„ฐ OCR ์ฒ˜๋ฆฌ, ์•Œ๋ฆผ ๋ฐœ์†ก๊นŒ์ง€ End-to-End ํŒŒ์ดํ”„๋ผ์ธ ์„ฑ๋Šฅ์„ ์ธก์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

4.4.1 ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ

ํ•ญ๋ชฉ ์ƒ์„ธ
ํ…Œ์ŠคํŠธ ๋ฐฉ์‹ ๋‹จ๊ฑด ์ˆœ์ฐจ ๋ฐœํ–‰ (๋™์‹œ ๋ถ€ํ•˜ ์•„๋‹˜)
ํ…Œ์ŠคํŠธ ์ƒ˜ํ”Œ ์ˆ˜ 5๊ฑด (ํ†ต๊ณ„์  ์œ ์˜์„ฑ๋ณด๋‹ค๋Š” ํŒŒ์ดํ”„๋ผ์ธ ๊ฐ ๋‹จ๊ณ„๋ณ„ ๋™์ž‘ ๊ฒ€์ฆ ๋ชฉ์ )
MQTT ๋ฐœํ–‰ ์œ„์น˜ speedcam-app (10.178.0.4) โ†’ speedcam-mq (10.178.0.7), ๋™์ผ VPC
ํ…Œ์ŠคํŠธ ์ด๋ฏธ์ง€ ํ•œ๊ตญ์–ด ๋ฒˆํ˜ธํŒ ํ•ฉ์„ฑ ์ด๋ฏธ์ง€ 10์žฅ (PIL๋กœ ์ƒ์„ฑ)
์ด๋ฏธ์ง€ ํŠน์ง• ๊ณ ๋Œ€๋น„ ํฐ ๋ฐฐ๊ฒฝ + ๊ฒ€์ • ํ…์ŠคํŠธ (OCR ์ตœ์ ํ™”)
GCS ๋ฒ„ํ‚ท gs://speedcam-bucket-4f918446/detections/
OCR Worker EasyOCR (Korean + English), concurrency=1, Warm ์ƒํƒœ (๋ชจ๋ธ ์‚ฌ์ „ ๋กœ๋”ฉ)
์ธ์ฆ ๋ฐฉ์‹ GCE ADC (๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์„œ๋ฒ„, JSON ํ‚ค ์—†์Œ)
์ธก์ • ๋ฐฉ๋ฒ• ๊ฐ ์ปจํ…Œ์ด๋„ˆ ๋กœ๊ทธ์˜ ํƒ€์ž„์Šคํƒฌํ”„ ๋น„๊ต (Loki ์ˆ˜์ง‘)

์ฐธ๊ณ : ๋ณธ ํ…Œ์ŠคํŠธ๋Š” ๋™์‹œ ๋‹ค๋ฐœ์ ์ธ ๋ถ€ํ•˜ ์ƒํ™ฉ์ด ์•„๋‹Œ, ํŒŒ์ดํ”„๋ผ์ธ ๊ฐ ๋‹จ๊ณ„์˜ ๋‹จ์œ„ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ธก์ •์— ์ดˆ์ ์„ ๋งž์ถ”์—ˆ์Šต๋‹ˆ๋‹ค. ๋Œ€๋Ÿ‰ ๋™์‹œ ์ฒ˜๋ฆฌ ์‹œ์˜ ์„ฑ๋Šฅ์€ ํ ๊นŠ์ด ์ฆ๊ฐ€์™€ OCR Worker ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋“ฑ์˜ ์ถ”๊ฐ€ ์š”์†Œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

4.4.2 ํŒŒ์ดํ”„๋ผ์ธ ๋‹จ๊ณ„๋ณ„ ์„ฑ๋Šฅ ์ธก์ •

์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด 3๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค:

Stage 1: MQTT ์ˆ˜์‹  โ†’ Detection ์ƒ์„ฑ โ†’ OCR Task ๋””์ŠคํŒจ์น˜
Stage 2: AMQP ์ „๋‹ฌ (Subscriber โ†’ OCR Worker)
Stage 3: OCR ์ฒ˜๋ฆฌ (GCS ๋‹ค์šด๋กœ๋“œ + EasyOCR ์ถ”๋ก )

Stage 1: MQTT ์ˆ˜์‹  โ†’ Detection ์ƒ์„ฑ โ†’ OCR Task ๋””์ŠคํŒจ์น˜ (Subscriber)

Detection ID MQTT ์ˆ˜์‹  ์‹œ๊ฐ Detection ์ƒ์„ฑ OCR ๋””์ŠคํŒจ์น˜ ์ด Subscriber ์ฒ˜๋ฆฌ ์‹œ๊ฐ„
#3284 01:19:00.489 01:19:00.554 01:19:00.560 71ms
#3285 01:49:54.207 01:49:54.222 01:49:54.229 22ms
#3286 01:56:37.918 01:56:37.925 01:56:37.928 10ms
#3287 02:09:11.080 02:09:11.091 02:09:11.097 17ms
#3288 02:15:44.137 02:15:44.145 02:15:44.148 11ms

ํ‰๊ท  Subscriber ์ฒ˜๋ฆฌ ์‹œ๊ฐ„: 15ms (Cold Start #3284 ์ œ์™ธ)

  • JSON ํŒŒ์‹ฑ + DB Insert + AMQP Publish ํฌํ•จ
  • #3284์˜ 71ms๋Š” ์ฒซ ์š”์ฒญ ์‹œ DB ์ปค๋„ฅ์…˜ ์ˆ˜๋ฆฝ ์‹œ๊ฐ„์ด ํฌํ•จ๋œ ์ด์ƒ๊ฐ’ (์ดํ›„ ์•ˆ์ •ํ™”)

Stage 2: AMQP ์ „๋‹ฌ (Subscriber โ†’ OCR Worker)

Detection ID ๋””์ŠคํŒจ์น˜ ์‹œ๊ฐ Worker ์ˆ˜์‹  ์‹œ๊ฐ AMQP ์ „๋‹ฌ ์‹œ๊ฐ„
#3284 01:19:00.560 01:19:00.563 3ms
#3285 01:49:54.229 01:49:54.230 1ms
#3286 01:56:37.928 01:56:37.935 7ms

ํ‰๊ท  AMQP ์ „๋‹ฌ ์‹œ๊ฐ„: ~3ms

  • RabbitMQ ๋‚ด๋ถ€ ๋ผ์šฐํŒ… ์˜ค๋ฒ„ํ—ค๋“œ ๋งค์šฐ ๋‚ฎ์Œ

Stage 3: OCR ์ฒ˜๋ฆฌ (GCS ๋‹ค์šด๋กœ๋“œ + EasyOCR ์ถ”๋ก )

Detection ID ์ด๋ฏธ์ง€ OCR ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ธ์‹ ๊ฒฐ๊ณผ ์‹ ๋ขฐ๋„ ๋น„๊ณ 
#3284 test-plate-1.jpg (ํฐ ์ด๋ฏธ์ง€) 35.59s None 0% Cold Start (๋ชจ๋ธ ๋กœ๋”ฉ ํฌํ•จ)
#3285 plate-01.jpg (์ž๋™์ฐจ ๋ฐฐ๊ฒฝ) 8.39s None 0% Warm, ๋ฐฐ๊ฒฝ ๋…ธ์ด์ฆˆ๋กœ ์ธ์‹ ์‹คํŒจ
#3286 real-plate-01.jpg (๊ณ ๋Œ€๋น„) 5.15s 12๊ฐ€3456 72.1% โœ… ์ •์ƒ ์ธ์‹
#3287 real-plate-02.jpg (๊ณ ๋Œ€๋น„) 5.11s 34๋‚˜5678 86.8% โœ… ์ •์ƒ ์ธ์‹
#3288 real-plate-03.jpg (๊ณ ๋Œ€๋น„) 5.02s 56๋‹ค7890 98.8% โœ… ์ •์ƒ ์ธ์‹

OCR ์„ฑ๋Šฅ ์š”์•ฝ:

์ง€ํ‘œ ๊ฐ’
Cold Start (๋ชจ๋ธ ๋กœ๋”ฉ ํฌํ•จ) ~35s
Warm OCR ํ‰๊ท  ~5.1s (GCS ๋‹ค์šด๋กœ๋“œ ~0.5s + EasyOCR ์ถ”๋ก  ~4.6s)
OCR ์ตœ๋Œ€ TPS ~0.2 msg/s (1 worker, concurrency=1)
๊ณ ๋Œ€๋น„ ํ•œ๊ตญ์–ด ๋ฒˆํ˜ธํŒ ์ธ์‹๋ฅ  100% (3/3)
ํ‰๊ท  ์‹ ๋ขฐ๋„ 85.9%

์ฃผ์š” ์ธ์‚ฌ์ดํŠธ:

  • ๊ณ ๋Œ€๋น„ ํ•œ๊ตญ์–ด ๋ฒˆํ˜ธํŒ ์ด๋ฏธ์ง€์—์„œ OCR ์ธ์‹๋ฅ  100%
  • ๋ฐฐ๊ฒฝ ๋…ธ์ด์ฆˆ๊ฐ€ ์žˆ๋Š” ์ด๋ฏธ์ง€๋Š” ์ธ์‹ ์‹คํŒจ (์ „์ฒ˜๋ฆฌ ํ•„์š”)
  • Warm ์ƒํƒœ OCR ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ 5.1s๋Š” ๋‹จ์ผ ์›Œ์ปค ๊ธฐ์ค€์œผ๋กœ ์ ์ ˆ

4.4.3 End-to-End ํŒŒ์ดํ”„๋ผ์ธ ํƒ€์ด๋ฐ

์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์˜ ๊ฐ ๋‹จ๊ณ„๋ณ„ ์†Œ์š” ์‹œ๊ฐ„์„ ์ •๋ฆฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Edge Device
    โ†“ MQTT Publish (~50ms network)
RabbitMQ MQTT Plugin
    โ†“ Internal routing (~1ms)
Django Subscriber (MQTT โ†’ DB โ†’ AMQP)
    โ†“ ~15ms (JSON parse + DB insert + AMQP publish)
RabbitMQ AMQP Queue
    โ†“ ~3ms (queue routing)
OCR Worker
    โ†“ ~5,100ms (GCS download + EasyOCR inference)
DB Update (completed)
    โ†“ ~10ms
Alert Queue โ†’ FCM Notification
    โ†“ (FCM ๋ฏธ๊ตฌํ˜„ ์ƒํƒœ)

Total E2E: ~5,200ms (warm) / ~35,700ms (cold start)

๋ณ‘๋ชฉ ์ง€์ :

  • OCR Worker (5.1s): ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์˜ 98% ์ฐจ์ง€
  • GCS ๋‹ค์šด๋กœ๋“œ: ~0.5s
  • EasyOCR ์ถ”๋ก : ~4.6s

๊ฐœ์„  ๋ฐฉ์•ˆ:

  1. GPU ์ธ์Šคํ„ด์Šค ์ „ํ™˜: CPU โ†’ GPU๋กœ OCR ์ถ”๋ก  ์‹œ๊ฐ„ ๋‹จ์ถ• (5s โ†’ <1s ๋ชฉํ‘œ)
  2. ๊ฒฝ๋Ÿ‰ OCR ๋ชจ๋ธ: PaddleOCR ๋“ฑ ๋” ๋น ๋ฅธ ๋ชจ๋ธ ๊ฒ€ํ† 
  3. ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ: Edge Device์—์„œ ๊ณ ๋Œ€๋น„ ์ „์ฒ˜๋ฆฌ ์ˆ˜ํ–‰

4.4.4 MQTT ๋™์‹œ ํŒŒ์ดํ”„๋ผ์ธ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ (3 ์‹œ๋‚˜๋ฆฌ์˜ค)

๋‹จ๊ฑด ์ˆœ์ฐจ ํ…Œ์ŠคํŠธ(4.4.2)์—์„œ ์ธก์ •ํ•œ ๋‹จ์œ„ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ๋ฐ”ํƒ•์œผ๋กœ, 20๋Œ€ ์นด๋ฉ”๋ผ๊ฐ€ ๋™์‹œ ์šด์˜๋˜๋Š” ์‹ค์ œ ์‚ฌ์šฉ ํŒจํ„ด์—์„œ์˜ ํŒŒ์ดํ”„๋ผ์ธ ์„ฑ๋Šฅ์„ 3๋‹จ๊ณ„ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ์ธก์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ค‘์š”: ๋ชจ๋“  MQTT ํ…Œ์ŠคํŠธ๋Š” ์‹ค์ œ EasyOCR ํ™˜๊ฒฝ์—์„œ ์ˆ˜ํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค (OCR_MOCK=false).

ํ…Œ์ŠคํŠธ ๊ตฌ์„ฑ

์‹œ๋‚˜๋ฆฌ์˜ค ์นด๋ฉ”๋ผ ์ˆ˜ ๋ฐœํ–‰ ์†๋„ ์ง€์†์‹œ๊ฐ„ ์˜ˆ์ƒ ๋ฉ”์‹œ์ง€ ๋ชฉ์ 
Normal 20๋Œ€ 1๊ฑด/๋ถ„/์นด๋ฉ”๋ผ (0.33 msg/s) 120์ดˆ 40๊ฑด ์ •์ƒ ์šด์˜ ํŒจํ„ด
Rush Hour 20๋Œ€ 5๊ฑด/๋ถ„/์นด๋ฉ”๋ผ (1.67 msg/s) 120์ดˆ 200๊ฑด ๋Ÿฌ์‹œ์•„์›Œ ํŠธ๋ž˜ํ”ฝ
Burst 20๋Œ€ 1๊ฑด/์ดˆ/์นด๋ฉ”๋ผ (20 msg/s) 60์ดˆ 1,200๊ฑด ๊ทนํ•œ ์ŠคํŠธ๋ ˆ์Šค

๊ณตํ†ต ์„ค์ •: ์‹ค์ œ GCS ๋ฒˆํ˜ธํŒ ์ด๋ฏธ์ง€ 10์žฅ ์ˆœํ™˜ ์‚ฌ์šฉ, API ํ†ต๊ณ„ ํด๋ง + RabbitMQ ํ ๊นŠ์ด ๋ชจ๋‹ˆํ„ฐ๋ง, ํŒŒ์ดํ”„๋ผ์ธ ์™„๋ฃŒ ๋Œ€๊ธฐ ํƒ€์ž„์•„์›ƒ 300์ดˆ


์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„ ๋ฐœํ–‰ ๊ฒฐ๊ณผ

์‹œ๋‚˜๋ฆฌ์˜ค ๋ฐœํ–‰ ์„ฑ๊ณต ๋ฐœํ–‰ ์‹คํŒจ ํ‰๊ท  ๋ฐœํ–‰ ์ง€์—ฐ ์‹ค์ธก ๋ฐœํ–‰ ์†๋„
Normal 40/40 (100%) 0๊ฑด 0.91ms 0.33 msg/s
Rush Hour 200/200 (100%) 0๊ฑด 0.38ms 1.66 msg/s
Burst 1,200/1,200 (100%) 0๊ฑด 0.37ms 19.96 msg/s

์ „ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ MQTT ๋ฐœํ–‰ 100% ์„ฑ๊ณต. RabbitMQ๊ฐ€ 20 msg/s๊นŒ์ง€ ์•ˆ์ •์ ์œผ๋กœ ์ˆ˜์šฉ.


์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„ ํŒŒ์ดํ”„๋ผ์ธ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ (๊ฐ€์„ค vs ์‹ค์ธก)

์ง€ํ‘œ Normal ๊ฐ€์„ค Normal ์‹ค์ธก Rush Hour ๊ฐ€์„ค Rush Hour ์‹ค์ธก Burst ๊ฐ€์„ค Burst ์‹ค์ธก
๋ฐœํ–‰ ์„ฑ๊ณต๋ฅ  100% 100% โœ… 100% 100% โœ… 100% 100% โœ…
์™„๋ฃŒ์œจ (300s) 100% 80% (32/40) โŒ 95% 11% (22/200) โŒ 100% (drain) 1.5% (18/1200) โŒ
E2E ์™„๋ฃŒ ์‹œ๊ฐ„ 60์ดˆ 300์ดˆ TO โŒ 120์ดˆ 300์ดˆ TO โŒ 300์ดˆ 300์ดˆ TO โŒ
OCR ํ ํ”ผํฌ < 5 25 โŒ < 50 202 โŒ 200-500 1,381 โŒ
DLQ ๋ฉ”์‹œ์ง€ 0 0 โœ… 0 0 โœ… 0 0 โœ…

๊ฐ€์„ค์€ OCR_MOCK=true ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ. ์‹ค์ œ EasyOCR ํ™˜๊ฒฝ์—์„œ๋Š” OCR ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ 133~667๋ฐฐ ๋А๋ฆผ.


Normal ์‹œ๋‚˜๋ฆฌ์˜ค - OCR ํ ๋“œ๋ ˆ์ธ ์ถ”์ด

์‹œ๊ฐ„(s)  ์™„๋ฃŒ  ๋Œ€๊ธฐ  OCRํ  FCMํ  ์‹คํšจ ์ฒ˜๋ฆฌ์†๋„
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  10      16    24    24     0     -
  50      19    21    22     0     0.075 msg/s
 100      21    19    19     0     0.040 msg/s
 150      24    16    16     0     0.060 msg/s
 200      26    14    14     0     0.040 msg/s
 250      29    11    11     0     0.060 msg/s
 300      32     8     9     0     0.060 msg/s (ํƒ€์ž„์•„์›ƒ)

Rush Hour ์‹œ๋‚˜๋ฆฌ์˜ค - OCR ํ ๋“œ๋ ˆ์ธ ์ถ”์ด

์‹œ๊ฐ„(s)  ์™„๋ฃŒ  ๋Œ€๊ธฐ  OCRํ  FCMํ
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  10       7   193   201     0     โ† ๋ฐœํ–‰ ์งํ›„ ํ ํญ์ฃผ
  60      10   190   198     0
 120      13   187   195     0
 180      16   184   193     0
 240      19   181   189     0
 300      22   178   186     0     โ† ํƒ€์ž„์•„์›ƒ, 178๊ฑด ๋ฏธ์ฒ˜๋ฆฌ

Burst ์‹œ๋‚˜๋ฆฌ์˜ค - OCR ํ ๋“œ๋ ˆ์ธ ์ถ”์ด

์‹œ๊ฐ„(s)  ์™„๋ฃŒ  ๋Œ€๊ธฐ   OCRํ    FCMํ
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  10       3  1197   1,381     0     โ† 1,200๊ฑด + ๊ธฐ์กด ๋ฐฑ๋กœ๊ทธ
  60       6  1194   1,378     0
 120       9  1191   1,375     0
 180      12  1188   1,372     0
 240      15  1185   1,369     0
 300      18  1182   1,366     0     โ† ํƒ€์ž„์•„์›ƒ, 1,182๊ฑด ๋ฏธ์ฒ˜๋ฆฌ

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: RabbitMQ ๋Œ€์‹œ๋ณด๋“œ - OCR ํ ๊นŠ์ด ๋ณ€ํ™” (3 ์‹œ๋‚˜๋ฆฌ์˜ค ์ „์ฒด ๊ตฌ๊ฐ„)]

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: Celery Workers ๋Œ€์‹œ๋ณด๋“œ - OCR Task ์ฒ˜๋ฆฌ ์†๋„ (ํ…Œ์ŠคํŠธ ๊ตฌ๊ฐ„)]

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: Container Metrics - speedcam-ocr CPU/Memory (ํ…Œ์ŠคํŠธ ๊ตฌ๊ฐ„)]


ํ•ต์‹ฌ ๋ฐœ๊ฒฌ โ€” OCR ์ฒ˜๋ฆฌ ์†๋„ ๋น„๊ต

์ง€ํ‘œ ๋‹จ๊ฑด (4.4.2) Normal Rush Hour Burst
OCR ์ฒ˜๋ฆฌ ์†๋„ 0.2 msg/s (5.1s/๊ฑด) 0.053 msg/s (18.8s/๊ฑด) 0.073 msg/s (13.7s/๊ฑด) 0.060 msg/s (16.7s/๊ฑด)
OCR ํ ํ”ผํฌ 0 25 202 1,381
ํŒŒ์ดํ”„๋ผ์ธ ์™„๋ฃŒ์œจ 100% 80% 11% 1.5%
๋ถ€ํ•˜ ์‹œ ์„ฑ๋Šฅ ์ €ํ•˜ - 3.7๋ฐฐ 2.7๋ฐฐ 3.3๋ฐฐ

๋™์‹œ ๋ถ€ํ•˜ ์‹œ OCR ์ฒ˜๋ฆฌ ์†๋„ ์ €ํ•˜ ์›์ธ ๋ถ„์„:

  1. ๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ•: e2-small(2GB)์—์„œ EasyOCR ๋ชจ๋ธ(1.5GB) + ํ ๋ฒ„ํผ โ†’ 721MB ์—ฌ์œ ๋ถ„ ์†Œ์ง„
  2. GCS ๋‹ค์šด๋กœ๋“œ ๊ฒฝํ•ฉ: ์—ฐ์† ๋‹ค์šด๋กœ๋“œ ์‹œ ๋„คํŠธ์›Œํฌ/API ์ง€์—ฐ ์ฆ๊ฐ€
  3. CPU ๊ฒฝํ•ฉ: OCR ์ถ”๋ก  ์ค‘ Celery ํ ๊ด€๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ
  4. ํ ๋ฐฑ๋กœ๊ทธ ๋ˆ„์ : Rush Hour/Burst ํ›„ ํ ๋“œ๋ ˆ์ธ์— ์ˆ˜ ์‹œ๊ฐ„ ์†Œ์š” (Burst ํ›„ ์ž”์—ฌ 1,362๊ฑด โ†’ ์•ฝ 6.3์‹œ๊ฐ„)

๊ฒฐ๋ก : ๊ฐ€์žฅ ๋‚™๊ด€์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค(Normal, 0.33 msg/s)์—์„œ๋„ OCR Worker๊ฐ€ ์ฒ˜๋ฆฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. OCR Worker ํ™•์žฅ(์ˆ˜ํ‰ ๋˜๋Š” GPU ์ „ํ™˜)์€ ์„ ํƒ์ด ์•„๋‹Œ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.


5. ์•„ํ‚คํ…์ฒ˜ ๋น„๊ต ๋ถ„์„ (Before vs After)

Event Driven Architecture ์ „ํ™˜์„ ํ†ตํ•ด ๊ธฐ์กด ๋ชจ๋†€๋ฆฌ์‹ ๊ตฌ์กฐ์˜ ๋ชจ๋“  ํ•ต์‹ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

5.1 ์„ฑ๋Šฅ ๋น„๊ต

ํ•ญ๋ชฉ Before (๋™๊ธฐ HTTP) After (Event Driven) ๊ฐœ์„ ์œจ ์ธก์ • ๊ทผ๊ฑฐ
์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ (์ˆ˜์‹ ~๋””์ŠคํŒจ์น˜) 3,000ms+ 15ms 200๋ฐฐ ๋น ๋ฆ„ Before: ๊ตฌ์กฐ ์ถ”์ • / After: ์‹ค์ธก (n=4)
Edge Device ๋ธ”๋กœํ‚น 3,000ms+ 0ms (๋น„๋™๊ธฐ) ์™„์ „ ํ•ด์†Œ Before: ๊ตฌ์กฐ ์ถ”์ • / After: MQTT QoS 1 PUBACK
๋ฉ”์‹œ์ง€ ๋ณด์žฅ ์—†์Œ QoS 1 (At-Least-Once) ๋ฉ”์‹œ์ง€ ๋ฌด์†์‹ค ํ”„๋กœํ† ์ฝœ ์‚ฌ์–‘
์žฅ์•  ๊ฒฉ๋ฆฌ ์ „์ฒด ์˜ํ–ฅ ์ปดํฌ๋„ŒํŠธ๋ณ„ ๊ฒฉ๋ฆฌ ๋…๋ฆฝ ์šด์˜ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„
ํ™•์žฅ์„ฑ ์„œ๋ฒ„ ์ „์ฒด Worker๋ณ„ ๋…๋ฆฝ ์„ธ๋ฐ€ํ•œ ํ™•์žฅ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„
HTTP API p95 N/A 38.85ms - ์‹ค์ธก (k6 4์‹œ๋‚˜๋ฆฌ์˜ค, n=2,297)
์ŠคํŒŒ์ดํฌ ๋Œ€์‘ ์„œ๋ฒ„ ๋‹ค์šด ์œ„ํ—˜ 15 VUs์—์„œ ์•ˆ์ • (์—๋Ÿฌ์œจ 0%) ๊ณ ๊ฐ€์šฉ์„ฑ ์‹ค์ธก (k6 spike ์‹œ๋‚˜๋ฆฌ์˜ค)

๋น„๊ต ๊ธฐ์ค€ ์ฐธ๊ณ : Before ์ˆ˜์น˜๋Š” ๋™๊ธฐ OCR ์ฒ˜๋ฆฌ ๊ตฌ์กฐ(HTTP ์š”์ฒญ โ†’ OCR ์™„๋ฃŒ ํ›„ ์‘๋‹ต)์—์„œ์˜ ์„ค๊ณ„ ๊ธฐ๋ฐ˜ ์ถ”์ •๊ฐ’์ด๋ฉฐ, After ์ˆ˜์น˜๋Š” ํ˜„์žฌ ์šด์˜ ํ™˜๊ฒฝ์—์„œ์˜ ์‹ค์ธก๊ฐ’์ž…๋‹ˆ๋‹ค.

5.2 ์•„ํ‚คํ…์ฒ˜ ์ „ํ™˜ ํ•ต์‹ฌ ์„ฑ๊ณผ

graph LR
    subgraph Before["๊ธฐ์กด ์•„ํ‚คํ…์ฒ˜"]
        B1["Django<br/>(API + OCR)"]
        B2["3์ดˆ+ ์‘๋‹ต"]
        B3["HTTP ์˜ค๋ฒ„ํ—ค๋“œ"]
        B4["์žฅ์•  ์ „ํŒŒ"]
        style B1 fill:#ffcccc
        style B2 fill:#ffcccc
        style B3 fill:#ffcccc
        style B4 fill:#ffcccc
    end

    subgraph After["Event Driven Architecture"]
        A1["Django<br/>(API๋งŒ)"]
        A2["15ms ์ฒ˜๋ฆฌ"]
        A3["MQTT+AMQP"]
        A4["์žฅ์•  ๊ฒฉ๋ฆฌ"]
        style A1 fill:#90EE90
        style A2 fill:#90EE90
        style A3 fill:#90EE90
        style A4 fill:#90EE90
    end

    Before -->|"์•„ํ‚คํ…์ฒ˜ ์ „ํ™˜"| After
Loading

๋ฌธ์ œ๋ณ„ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

๊ธฐ์กด ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ํšจ๊ณผ
OCR ๋™๊ธฐ ์ฒ˜๋ฆฌ OCR Worker ๋ถ„๋ฆฌ + AMQP ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์‹œ๊ฐ„ 3000ms โ†’ 15ms
Edge Device ๋ธ”๋กœํ‚น MQTT QoS 1 + ์ฆ‰์‹œ ACK ์—ฐ์† ๊ฐ์ง€ ๊ฐ€๋Šฅ, ๋ฐ์ดํ„ฐ ์œ ์‹ค ๋ฐฉ์ง€
HTTP IoT ํ†ต์‹  MQTT ํ”„๋กœํ† ์ฝœ ๋„์ž… ๊ฒฝ๋Ÿ‰ ํ”„๋กœํ† ์ฝœ, ๋ฉ”์‹œ์ง€ ๋ณด์žฅ, ์˜คํ”„๋ผ์ธ ๋ฒ„ํผ๋ง
์žฅ์•  ์ „ํŒŒ ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ + ์ด๋ฒคํŠธ ํ ๋ณด์กด OCR ์žฅ์•  ์‹œ์—๋„ API ์ •์ƒ ์šด์˜

6. ๋ชจ๋‹ˆํ„ฐ๋ง ์ง€ํ‘œ

6.1 Grafana ๋Œ€์‹œ๋ณด๋“œ

์ด 7๊ฐœ์˜ ์ปค์Šคํ…€ ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์šด์˜ํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ๋ชจ๋“  ๊ณ„์ธต์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.

๋Œ€์‹œ๋ณด๋“œ ์šฉ๋„
k6 Prometheus Dashboard HTTP API ๋ฉ”ํŠธ๋ฆญ ์‹ค์‹œ๊ฐ„ ์‹œ๊ฐํ™”
System Overview ์ „์ฒด ์‹œ์Šคํ…œ ๋ฆฌ์†Œ์Šค ํ˜„ํ™ฉ
Container Metrics Docker ์ปจํ…Œ์ด๋„ˆ๋ณ„ CPU/Memory/Network
MySQL Performance ์ฟผ๋ฆฌ ์„ฑ๋Šฅ, ์ปค๋„ฅ์…˜, ์Šฌ๋กœ์šฐ ์ฟผ๋ฆฌ
RabbitMQ Monitoring ๋ฉ”์‹œ์ง€ ํ ๊นŠ์ด, ์ฒ˜๋ฆฌ๋Ÿ‰, ์ปจ์Šˆ๋จธ
Celery Workers Task ์ฒ˜๋ฆฌ๋Ÿ‰, ์ง€์—ฐ ์‹œ๊ฐ„, ์‹คํŒจ์œจ
Application Logs Loki ๊ธฐ๋ฐ˜ ํ†ตํ•ฉ ๋กœ๊ทธ ๊ฒ€์ƒ‰

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: System Overview ๋Œ€์‹œ๋ณด๋“œ - 6๊ฐœ ์ธ์Šคํ„ด์Šค CPU/Memory ์ „์ฒด ํ˜„ํ™ฉ]

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: MySQL Performance ๋Œ€์‹œ๋ณด๋“œ - ์ปค๋„ฅ์…˜ ์ˆ˜ ๋ณ€ํ™” (๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ๊ตฌ๊ฐ„)]

6.2 Prometheus ํƒ€๊ฒŸ ์ƒํƒœ

์ด 11๊ฐœ ํƒ€๊ฒŸ (All UP)

ํƒ€๊ฒŸ ์ธ์Šคํ„ด์Šค ์ƒํƒœ
cAdvisor speedcam-app โœ… UP
cAdvisor speedcam-db โœ… UP
cAdvisor speedcam-mq โœ… UP
cAdvisor speedcam-ocr โœ… UP
cAdvisor speedcam-alert โœ… UP
cAdvisor speedcam-mon โœ… UP
django speedcam-app โœ… UP
mysql speedcam-db โœ… UP
rabbitmq speedcam-mq โœ… UP
celery speedcam-ocr โœ… UP
otel speedcam-mon โœ… UP

๐Ÿ“ธ [์Šคํฌ๋ฆฐ์ƒท ์‚ฝ์ž…: Prometheus โ†’ Status โ†’ Targets ํŽ˜์ด์ง€ (11๊ฐœ ํƒ€๊ฒŸ All UP)]

6.3 ๋กœ๊ทธ ์ˆ˜์ง‘ ํ˜„ํ™ฉ

์ด 16๊ฐœ ์ปจํ…Œ์ด๋„ˆ ๋กœ๊ทธ ์ˆ˜์ง‘ (Promtail โ†’ Loki)

  • Django, Gunicorn, Celery Workers
  • MySQL, RabbitMQ
  • Traefik, Flower
  • Prometheus, Grafana, Loki, Jaeger, OpenTelemetry Collector

7. ์ตœ๋Œ€ TPS ๋ฐ ์šฉ๋Ÿ‰ ๋ถ„์„

๊ฐ ์ปดํฌ๋„ŒํŠธ๋ณ„ ์ตœ๋Œ€ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ๊ณผ ๋ณ‘๋ชฉ ์ง€์ ์„ ๋ถ„์„ํ–ˆ์Šต๋‹ˆ๋‹ค.

7.1 ์ปดํฌ๋„ŒํŠธ๋ณ„ ์ตœ๋Œ€ TPS

์ปดํฌ๋„ŒํŠธ ์ด๋ก ๊ฐ’ ์‹ค์ธก๊ฐ’ ๊ทผ๊ฑฐ ๋ณ‘๋ชฉ ์š”์ธ
HTTP API (Django) ~80-100 req/s 25 req/s (50VUs) k6 ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ ์‹ค์ธก Gunicorn 4 handlers + k6 ๋ฆฌ์†Œ์Šค ๊ฒฝํ•ฉ
HTTP API (15VUs) - 6.75 req/s (p95=39ms) k6 4์‹œ๋‚˜๋ฆฌ์˜ค ์‹ค์ธก (์‹ค์ œ ์‚ฌ์šฉ ํŒจํ„ด) sleep ๊ฐ„๊ฒฉ์œผ๋กœ ๋‚ฎ์€ req/s, ์‘๋‹ต์€ ๋น ๋ฆ„
MQTT Subscriber ~40 msg/s 20 msg/s ๋ฌด์†์‹ค Burst ์‹œ๋‚˜๋ฆฌ์˜ค (1200๊ฑด/60์ดˆ) ๋‹จ์ผ ์Šค๋ ˆ๋“œ loop_forever()
MQTT Publish - 0.37~0.91ms/๊ฑด 3๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹ค์ธก ์ง€์—ฐ ๋ฌด์‹œ ๊ฐ€๋Šฅ
AMQP Broker ~10,000 msg/s - RabbitMQ ๊ณต์‹ ๋ฒค์น˜๋งˆํฌ ์ฐธ๊ณ  ์ถฉ๋ถ„ํ•œ ์—ฌ์œ  (๋ณ‘๋ชฉ ์—†์Œ)
OCR Worker (๋‹จ๊ฑด) ~0.2 msg/s 0.2 msg/s ๋‹จ๊ฑด ์‹ค์ธก (5.1s/๊ฑด, n=3) EasyOCR CPU ์ถ”๋ก 
OCR Worker (๋ถ€ํ•˜ ์‹œ) - 0.053~0.073 msg/s 3๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹ค์ธก (13.7~18.8s/๊ฑด) ๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ• + GCS ๊ฒฝํ•ฉ
Alert Worker ~100 msg/s - ์ถ”์ • (concurrency=100 ์„ค์ •) FCM API ํ˜ธ์ถœ
MySQL ~500 qps - ์ถ”์ • (e2-medium ๋ฒค์น˜๋งˆํฌ) e2-medium 4GB RAM

์ฐธ๊ณ : HTTP ์‹ค์ธก๊ฐ’์€ k6๊ฐ€ ๋™์ผ ์ธ์Šคํ„ด์Šค(e2-small)์—์„œ ์‹คํ–‰๋œ ๊ฒฐ๊ณผ. MQTT Subscriber๋Š” Burst(20 msg/s)์—์„œ๋„ 1,200๊ฑด ์ „๋Ÿ‰ ์ˆ˜์‹ ํ•˜์—ฌ ๋‹จ์ผ ์Šค๋ ˆ๋“œ์ž„์—๋„ ์ถฉ๋ถ„ํ•œ ์ฒ˜๋ฆฌ๋Ÿ‰ ํ™•์ธ. OCR Worker๊ฐ€ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ง€๋ฐฐ์  ๋ณ‘๋ชฉ.

7.2 ํŒŒ์ดํ”„๋ผ์ธ ์ „์ฒด ๋ณ‘๋ชฉ

ํ˜„์žฌ ๋ณ‘๋ชฉ: OCR Worker

graph LR
    A["HTTP API<br/>25 req/s (์‹ค์ธก)"] ~~~ B
    B["MQTT Subscriber<br/>20 msg/s ์ฒ˜๋ฆฌ ํ™•์ธ"] -->|"๋ณ‘๋ชฉ"| C["OCR Worker<br/>0.06 msg/s (๋ถ€ํ•˜์‹œ ์‹ค์ธก)"]
    C --> D["Alert Worker<br/>~100 msg/s (์ถ”์ •)"]

    style C fill:#ff6666
Loading

์‹ค์ธก ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ๋ณ‘๋ชฉ ๋ถ„์„ (3 ์‹œ๋‚˜๋ฆฌ์˜ค ์ข…ํ•ฉ):

  • OCR Worker๊ฐ€ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ง€๋ฐฐ์  ๋ณ‘๋ชฉ์ž„์ด 3๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์ผ๊ด€๋˜๊ฒŒ ํ™•์ธ๋จ
  • ๋‹จ๊ฑด ์ฒ˜๋ฆฌ: 5.1s/๊ฑด (0.2 msg/s) โ†’ ๋™์‹œ ๋ถ€ํ•˜ ์‹œ: 13.718.8s/๊ฑด (0.0530.073 msg/s)๋กœ 2.7~3.7๋ฐฐ ์„ฑ๋Šฅ ์ €ํ•˜
  • Normal(0.33 msg/s)์—์„œ๋„ ํ ํ”ผํฌ 25, 300์ดˆ ๋‚ด 80%๋งŒ ์™„๋ฃŒ
  • Rush Hour(1.67 msg/s)์—์„œ ํ ํ”ผํฌ 202, 300์ดˆ ๋‚ด 11%๋งŒ ์™„๋ฃŒ
  • Burst(20 msg/s)์—์„œ ํ ํ”ผํฌ 1,381, 300์ดˆ ๋‚ด 1.5%๋งŒ ์™„๋ฃŒ โ†’ ๋“œ๋ ˆ์ธ ์•ฝ 6.3์‹œ๊ฐ„ ์†Œ์š”
  • e2-small(2GB)์—์„œ EasyOCR concurrency=1๋งŒ ๊ฐ€๋Šฅ (๋ฉ”๋ชจ๋ฆฌ ์ œ์•ฝ)

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ:

๋ฐฉ๋ฒ• ์˜ˆ์ƒ ๊ฐœ์„  ๋น„์šฉ ๋‚œ์ด๋„
OCR ์ธ์Šคํ„ด์Šค ์ถ”๊ฐ€ (horizontal) 0.053 msg/s ร— N ์ € ๋‚ฎ์Œ
GPU ์ธ์Šคํ„ด์Šค ์ „ํ™˜ 5.1s โ†’ <1s (5x+) ์ค‘ ์ค‘
e2-medium ์—…๊ทธ๋ ˆ์ด๋“œ ๋ฉ”๋ชจ๋ฆฌ ์—ฌ์œ ๋กœ ๋ถ€ํ•˜ ์‹œ ์„ฑ๋Šฅ ์ €ํ•˜ ์™„ํ™” ์ € ๋‚ฎ์Œ
๊ฒฝ๋Ÿ‰ OCR ๋ชจ๋ธ (PaddleOCR) ~2-3x ๋น ๋ฆ„ ์ € ์ค‘
Edge ์ „์ฒ˜๋ฆฌ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ฐ์†Œ ์ € ๋‚ฎ์Œ

8. ๋ฐœ๊ฒฌ๋œ ์ด์Šˆ ๋ฐ ๊ฐœ์„ ์ 

8.1 ํ•ด๊ฒฐ๋œ ์ด์Šˆ

์ด์Šˆ ์›์ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
MQTT Subscriber Stale DB Connection ์žฅ๊ธฐ ์‹คํ–‰ ์Šค๋ ˆ๋“œ์—์„œ MySQL ์—ฐ๊ฒฐ ๋งŒ๋ฃŒ close_old_connections() ์ถ”๊ฐ€๋กœ ํ•ด๊ฒฐ
OCR Worker OOM EasyOCR ๋ชจ๋ธ ร— 4 workers = 6GB (e2-small 2GB ์ดˆ๊ณผ) concurrency=1๋กœ ์กฐ์ •
GCS ์ธ์ฆ JSON ํ‚ค ํŒŒ์ผ ์—†์Œ ADC(๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์„œ๋ฒ„) ํ™œ์šฉ์œผ๋กœ ํ•ด๊ฒฐ

8.2 ๊ฐœ์„ ์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„

8.2.1 ๊ธด๊ธ‰ (High Priority)

์ด์Šˆ ํ˜„์žฌ ์ƒํƒœ ์˜ํ–ฅ๋„ ๊ฐœ์„  ๋ฐฉ์•ˆ
FCM ํ† ํฐ ์—…๋ฐ์ดํŠธ API PATCH ์—”๋“œํฌ์ธํŠธ 0% ์„ฑ๊ณต๋ฅ  ๐Ÿ”ด High API endpoint ๋กœ์ง ์ˆ˜์ •
OCR Worker ํ™•์žฅ์„ฑ ๋‹จ์ผ ์›Œ์ปค 0.2 msg/s ๐Ÿ”ด High GPU ์ธ์Šคํ„ด์Šค ๋˜๋Š” ๊ฒฝ๋Ÿ‰ OCR ๋ชจ๋ธ ๊ฒ€ํ† 
๋ชจ๋‹ˆํ„ฐ๋ง ์ธ์Šคํ„ด์Šค ๋ฉ”๋ชจ๋ฆฌ 264MB ์—ฌ์œ  (๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์œ„ํ—˜) ๐ŸŸก Medium e2-medium ์—…๊ทธ๋ ˆ์ด๋“œ ๊ถŒ์žฅ

8.2.2 ์ตœ์ ํ™” (Medium Priority)

์ด์Šˆ ํ˜„์žฌ ์ƒํƒœ ์˜ํ–ฅ๋„ ๊ฐœ์„  ๋ฐฉ์•ˆ
CONN_MAX_AGE ๋ฏธ์„ค์ • ๋งค ์š”์ฒญ ์ƒˆ DB ์ปค๋„ฅ์…˜ ๐ŸŸก Medium ์ปค๋„ฅ์…˜ ํ’€๋ง ์„ค์ • (์„ฑ๋Šฅ 10-20% ๊ฐœ์„  ์˜ˆ์ƒ)
MQTT Subscriber ๋‹จ์ผ ์Šค๋ ˆ๋“œ ๋ณ‘๋ชฉ ์‹œ ๋ฉ”์‹œ์ง€ ํ์ž‰ ๐ŸŸก Medium ์Šค๋ ˆ๋“œํ’€ or ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค ๊ฒ€ํ† 
speedcam-ocr ๋””์Šคํฌ ์‚ฌ์šฉ๋ฅ  87% (17GB/20GB) ๐ŸŸก Medium ๋””์Šคํฌ ์ •๋ฆฌ ๋˜๋Š” ํ™•์žฅ

8.2.3 ์žฅ๊ธฐ ๊ฐœ์„  (Low Priority)

ํ•ญ๋ชฉ ๋ชฉํ‘œ ์˜ˆ์ƒ ํšจ๊ณผ
์ฝ๊ธฐ ๋ณต์ œ๋ณธ ์ถ”๊ฐ€ MySQL ์ฝ๊ธฐ ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ ํ–ฅ์ƒ
Redis ์บ์‹ฑ ํ†ต๊ณ„ ์กฐํšŒ ์บ์‹ฑ API ์‘๋‹ต ์†๋„ ํ–ฅ์ƒ
Celery Beat ์ถ”๊ฐ€ ์ฃผ๊ธฐ์  ์ž‘์—… ์ž๋™ํ™” ์šด์˜ ํšจ์œจ์„ฑ ํ–ฅ์ƒ

9. ๊ฒฐ๋ก 

9.1 ํ•ต์‹ฌ ์„ฑ๊ณผ

SpeedCam ์‹œ์Šคํ…œ์€ ๊ธฐ์กด ๋™๊ธฐ์‹ HTTP ๊ธฐ๋ฐ˜ ๋ชจ๋†€๋ฆฌ์‹ ์•„ํ‚คํ…์ฒ˜์—์„œ Event Driven Architecture๋กœ ์„ฑ๊ณต์ ์œผ๋กœ ์ „ํ™˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ •๋Ÿ‰์  ์„ฑ๊ณผ:

์ง€ํ‘œ Before After ๊ฐœ์„  ๊ทผ๊ฑฐ
์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ 3,000ms+ ยน 15ms 200๋ฐฐ After ์‹ค์ธก (n=4)
Edge Device ๋ธ”๋กœํ‚น 3,000ms+ ยน 0ms ์™„์ „ ํ•ด์†Œ MQTT PUBACK
HTTP API p95 N/A 38.85ms - k6 4์‹œ๋‚˜๋ฆฌ์˜ค ์‹ค์ธก (n=2,297)
MQTT ๋ฐœํ–‰ ์„ฑ๊ณต๋ฅ  N/A 100% (20 msg/s๊นŒ์ง€) - MQTT 3์‹œ๋‚˜๋ฆฌ์˜ค ์‹ค์ธก (n=1,440)
๋ฉ”์‹œ์ง€ ๋ณด์žฅ ์—†์Œ QoS 1 ๋ฌด์†์‹ค ํ”„๋กœํ† ์ฝœ ์‚ฌ์–‘ + DLQ 0๊ฑด ์‹ค์ธก
์ŠคํŒŒ์ดํฌ ์—๋Ÿฌ์œจ ์„œ๋ฒ„ ๋‹ค์šด ์œ„ํ—˜ ยน 0% ๊ณ ๊ฐ€์šฉ์„ฑ k6 ์‹ค์ธก (15 VUs)

ยน Before ์ˆ˜์น˜๋Š” ๋™๊ธฐ OCR ์ฒ˜๋ฆฌ ๊ตฌ์กฐ ๊ธฐ๋ฐ˜ ์„ค๊ณ„ ์ถ”์ •๊ฐ’ (๋ณ„๋„ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ๋ฏธ์ˆ˜ํ–‰)

์ •์„ฑ์  ์„ฑ๊ณผ:

  1. ์žฅ์•  ๊ฒฉ๋ฆฌ: OCR ์žฅ์•  ์‹œ์—๋„ API ์ •์ƒ ์šด์˜ ๊ฐ€๋Šฅ
  2. ๋…๋ฆฝ ํ™•์žฅ: Worker๋ณ„ ๋…๋ฆฝ์  ์Šค์ผ€์ผ ์•„์›ƒ
  3. ์™„์ „ํ•œ ๊ด€์ธก์„ฑ: Prometheus + Grafana + Loki + Jaeger ํ†ตํ•ฉ ๋ชจ๋‹ˆํ„ฐ๋ง
  4. IoT ์ตœ์ ํ™”: MQTT QoS 1๋กœ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ ๋ณด์žฅ

9.2 ๊ฐœ์„  ๋กœ๋“œ๋งต

Phase 1: ์ฆ‰์‹œ ๊ฐœ์„  (1-2์ฃผ)

  • FCM ํ† ํฐ ์—…๋ฐ์ดํŠธ API ๋ฒ„๊ทธ ์ˆ˜์ •
  • CONN_MAX_AGE ์„ค์ •์œผ๋กœ DB ์ปค๋„ฅ์…˜ ํ’€๋ง ํ™œ์„ฑํ™”
  • speedcam-ocr ๋””์Šคํฌ ์ •๋ฆฌ

Phase 2: ์„ฑ๋Šฅ ๊ฐœ์„  (1๊ฐœ์›”)

  • OCR Worker GPU ์ธ์Šคํ„ด์Šค ์ „ํ™˜ (5s โ†’ <1s ๋ชฉํ‘œ)
  • ๋ชจ๋‹ˆํ„ฐ๋ง ์ธ์Šคํ„ด์Šค e2-medium ์—…๊ทธ๋ ˆ์ด๋“œ
  • MQTT Subscriber ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ๊ตฌํ˜„

Phase 3: ์žฅ๊ธฐ ์ตœ์ ํ™” (2-3๊ฐœ์›”)

  • Redis ์บ์‹ฑ ๋ ˆ์ด์–ด ์ถ”๊ฐ€
  • MySQL ์ฝ๊ธฐ ๋ณต์ œ๋ณธ ๊ตฌ์„ฑ
  • Celery Beat ์Šค์ผ€์ค„๋Ÿฌ ์ถ”๊ฐ€
  • ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•

9.3 ์ตœ์ข… ํ‰๊ฐ€

SpeedCam ํ”„๋กœ์ ํŠธ๋Š” Event Driven Architecture๋ฅผ ํ†ตํ•ด ๊ธฐ์กด ๋ชจ๋†€๋ฆฌ์‹ ๊ตฌ์กฐ์˜ ๊ทผ๋ณธ์  ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•˜๊ณ , ์‹ค์‹œ๊ฐ„ IoT ์‹œ์Šคํ…œ์œผ๋กœ์„œ ์š”๊ตฌ๋˜๋Š” ๋†’์€ ์‘๋‹ต์„ฑ, ๋ฉ”์‹œ์ง€ ๋ณด์žฅ, ์žฅ์•  ๊ฒฉ๋ฆฌ๋ฅผ ๋ชจ๋‘ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ 200๋ฐฐ ๊ฐœ์„  (3,000ms+ โ†’ 15ms), ์™„์ „ํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ, ์ปดํฌ๋„ŒํŠธ๋ณ„ ๋…๋ฆฝ ํ™•์žฅ์ด๋ผ๋Š” ํ•ต์‹ฌ ๋ชฉํ‘œ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜์—ฌ, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ •์ ์œผ๋กœ ์šด์˜ ๊ฐ€๋Šฅํ•œ ์‹œ์Šคํ…œ์œผ๋กœ ๋ฐœ์ „ํ–ˆ์Šต๋‹ˆ๋‹ค.

์•ž์œผ๋กœ OCR Worker GPU ์ „ํ™˜๊ณผ DB ์ปค๋„ฅ์…˜ ํ’€๋ง ์ตœ์ ํ™”๋ฅผ ํ†ตํ•ด ๋”์šฑ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ธ ์‹œ์Šคํ…œ์œผ๋กœ ๋ฐœ์ „ํ•  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€๋ฉ๋‹ˆ๋‹ค.


๋ฌธ์„œ ๋ฒ„์ „: 2.0 ์ตœ์ข… ์ˆ˜์ •์ผ: 2026-02-12 ํ…Œ์ŠคํŠธ ์ผ์‹œ: 2026-02-12 (k6 HTTP 4์‹œ๋‚˜๋ฆฌ์˜ค + MQTT 3์‹œ๋‚˜๋ฆฌ์˜ค) ์ž‘์„ฑ์ž: SpeedCam Backend Team ๊ด€๋ จ ๋ฌธ์„œ: ARCHITECTURE_COMPARISON.md