Skip to content

JAVACAFE-CLUB/realtime-ju

Repository files navigation

Realtime Search Platform

μ‹€μ‹œκ°„ λ‰΄μŠ€ 및 μ†Œμ…œλ―Έλ””μ–΄ 데이터λ₯Ό μˆ˜μ§‘, μ •μ œ, μƒ‰μΈν™”ν•˜μ—¬ νŠΈλ Œλ”© ν‚€μ›Œλ“œλ₯Ό μ œκ³΅ν•˜λŠ” ν”Œλž«νΌ


κ°œμš”

이 ν”Œλž«νΌμ€ λ‹€μ–‘ν•œ μ†ŒμŠ€(Wikipedia, YNA λ‰΄μŠ€, YouTube)μ—μ„œ μ‹€μ‹œκ°„μœΌλ‘œ 데이터λ₯Ό μˆ˜μ§‘ν•˜κ³ , 이λ₯Ό μ •μ œ 및 μƒ‰μΈν™”ν•˜μ—¬ νŠΈλ Œλ”© ν‚€μ›Œλ“œλ₯Ό μΆ”μΆœν•˜κ³  μ œκ³΅ν•©λ‹ˆλ‹€.

μ£Όμš” κΈ°λŠ₯

  • μ‹€μ‹œκ°„ 데이터 μˆ˜μ§‘: Wikipedia, μ—°ν•©λ‰΄μŠ€(YNA), YouTubeμ—μ„œ 데이터 μˆ˜μ§‘
  • μžλ™ μ •μ œ: HTML/JSON/Wikitext νŒŒμ‹± 및 ν…μŠ€νŠΈ μΆ”μΆœ
  • ν•œκ΅­μ–΄ ν‚€μ›Œλ“œ μΆ”μΆœ: Elasticsearch Nori ν”ŒλŸ¬κ·ΈμΈ 기반 ν˜•νƒœμ†Œ 뢄석
  • κ°€μ€‘μΉ˜ 기반 λž­ν‚Ή: μ†ŒμŠ€λ³„, μ‹œκ°„λ³„ κ°€μ€‘μΉ˜ μ μš©ν•œ ν‚€μ›Œλ“œ 점수 계산
  • μ‹€μ‹œκ°„ API: νŠΈλ Œλ”© ν‚€μ›Œλ“œ 쑰회 REST API 제곡

μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                           Realtime Search Platform Architecture                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                         β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚   β”‚   Collector   β”‚      β”‚    Refine     β”‚      β”‚     Index     β”‚      β”‚    Serving    β”‚β”‚
β”‚   β”‚    System     │─────>β”‚    System     │─────>β”‚    System     │─────>β”‚    System     β”‚β”‚
β”‚   β”‚   (9080)      β”‚      β”‚   (9082)      β”‚      β”‚   (9083)      β”‚      β”‚   (9086)      β”‚β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚           β”‚                      β”‚                      β”‚                      β”‚        β”‚
β”‚           β”‚                      β”‚                      β”‚                      β”‚        β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚   β”‚     MinIO     β”‚      β”‚    MongoDB    β”‚      β”‚ Elasticsearch β”‚      β”‚     Redis     β”‚β”‚
β”‚   β”‚  (원본 μ €μž₯)    β”‚      β”‚  (μ •μ œ μ €μž₯)    β”‚      β”‚   (색인 μ €μž₯)    β”‚      β”‚    (캐싱)      β”‚β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                                                         β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚   β”‚                              Apache Kafka (λ©”μ‹œμ§• λ°±λ³Έ)                               β”‚β”‚
β”‚   β”‚  raw.* topics ──────────────────> refined.* topics ─────────────────> API Response β”‚β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                                                         β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚   β”‚                               MySQL (메타데이터 μ €μž₯)                                  β”‚β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

계측별 μ—­ν• 

계측 μ‹œμŠ€ν…œ μ—­ν•  μ €μž₯μ†Œ
μˆ˜μ§‘ Collector μ™ΈλΆ€ μ†ŒμŠ€μ—μ„œ 원본 데이터 μˆ˜μ§‘ MinIO
μ •μ œ Refine HTML/ν…μŠ€νŠΈ νŒŒμ‹± 및 μ •μ œ MongoDB
색인 Index ν‚€μ›Œλ“œ μΆ”μΆœ 및 검색 색인 Elasticsearch
μ„œλΉ™ Serving API 제곡 및 λž­ν‚Ή 계산 Redis + MySQL

Kafka ν† ν”½ ꡬ쑰

ν† ν”½ μ†ŒμŠ€ μ„€λͺ…
raw.docs.wikipedia Collector Wikipedia 원본 데이터
raw.news.yna Collector YNA λ‰΄μŠ€ 원본 데이터
raw.sns.youtube Collector YouTube 원본 데이터
refined.docs.wikipedia Refine Wikipedia μ •μ œ 데이터
refined.news.yna Refine YNA λ‰΄μŠ€ μ •μ œ 데이터
refined.sns.youtube Refine YouTube μ •μ œ 데이터
*.dlq 각 μ‹œμŠ€ν…œ Dead Letter Queue (였λ₯˜ 처리)

λ©”μ‹œμ§€ ꡬ쑰

CollectMessage (μˆ˜μ§‘ β†’ μ •μ œ)

{
  "schemaVersion": "1",
  "collectionId": "YNA_20250902_001",
  "source": "NEWS_YNA",
  "occurredAt": "2025-09-02T10:30:00Z",
  "rawDataUrl": "minio://raw-news-yna/2025-09-02/article_123.html",
  "recordCount": 1
}

RefineMessage (μ •μ œ β†’ 색인)

{
  "schemaVersion": "1",
  "collectionId": "YNA_123",
  "source": "NEWS_YNA",
  "occurredAt": "2025-09-02T10:35:00Z",
  "refinedId": "refined_abc123def456"
}

1. μˆ˜μ§‘ μ‹œμŠ€ν…œ (Collector System)

포트: 9080 | μ—­ν• : μ™ΈλΆ€ μ†ŒμŠ€μ—μ„œ 데이터 μˆ˜μ§‘ β†’ Kafka λ©”μ‹œμ§€ λ°œμ†‘

μš”κ΅¬μ‚¬ν•­ μΆ©μ‘± 평가

μ‹œμŠ€ν…œ μš”κ΅¬μ‚¬ν•­ μƒνƒœ κ΅¬ν˜„ 방식
μˆ˜μ§‘ λŒ€μš©λŸ‰ 파일 처리 (1GB+) βœ… μΆ©μ‘± StAX 슀트리밍 + BZip2 μ••μΆ• ν•΄μ œ
μˆ˜μ§‘ λ©”μ‹œμ§€ λΆ„ν•  (1MB μ΄ν•˜) βœ… μΆ©μ‘± NDJSON + GZIP μƒ€λ“œ (500νŽ˜μ΄μ§€/μƒ€λ“œ)
μˆ˜μ§‘ HTML 크둀링 (ν•˜λ£¨μΉ˜ μˆ˜μ§‘) βœ… μΆ©μ‘± RSS ν”Όλ“œ 기반 병렬 크둀링
μˆ˜μ§‘ RSS ν™œμš© βœ… μΆ©μ‘± Rome RSS Parser + 폴백 νŒŒμ‹±
μˆ˜μ§‘ YouTube API 연동 βœ… μΆ©μ‘± YouTube Data API v3
μˆ˜μ§‘ API 호좜 μž₯μ•  λŒ€μ‘ βœ… μΆ©μ‘± Exponential Backoff + μž¬μ‹œλ„

1.1 λŒ€μš©λŸ‰ 파일 처리 (Wikipedia)

μš”κ΅¬μ‚¬ν•­: 1GB 이상 파일 핸듀링, λ©”μ‹œμ§€ 1MB μ΄ν•˜λ‘œ λΆ„ν• 

κ΅¬ν˜„ 방식:

Wikipedia XML 덀프 (1GB+)
    ↓ BZip2 μ••μΆ• ν•΄μ œ (슀트리밍)
    ↓ StAX νŒŒμ„œ (XMLStreamReader)
    ↓ 500νŽ˜μ΄μ§€ λ‹¨μœ„ μƒ€λ“œ λΆ„ν• 
    ↓ NDJSON + GZIP μ••μΆ• (μ•½ 500KB~1MB)
    ↓ MinIO μ—…λ‘œλ“œ + Kafka 이벀트 λ°œν–‰

μ„€μ •κ°’:

μ„€μ • κ°’ μ„€λͺ…
pages-per-shard 500 μƒ€λ“œλ‹Ή νŽ˜μ΄μ§€ 수
μ••μΆ• 방식 GZIP μƒ€λ“œ μ••μΆ•
ν˜•μ‹ NDJSON 슀트리밍 νŒŒμ‹± 용이

πŸ’‘ μ™œ 500 νŽ˜μ΄μ§€λ‘œ μƒ€λ“œ ν–ˆλŠ”κ°€?

  • Kafka λ©”μ‹œμ§€ 크기 μ œν•œ: Kafka κΈ°λ³Έ μ΅œλŒ€ λ©”μ‹œμ§€ ν¬κΈ°λŠ” 1MB, 500νŽ˜μ΄μ§€ Γ— GZIP μ••μΆ• β‰ˆ 500KB~1MB둜 적정
  • λ©”λͺ¨λ¦¬ 효율: ν•œ λ²ˆμ— λ„ˆλ¬΄ λ§Žμ€ νŽ˜μ΄μ§€λ₯Ό λ©”λͺ¨λ¦¬μ— 올리면 OOM μœ„ν—˜
  • μ‹€νŒ¨ 볡ꡬ λ‹¨μœ„: μƒ€λ“œ 처리 μ‹€νŒ¨ μ‹œ 500νŽ˜μ΄μ§€λ§Œ μž¬μ²˜λ¦¬ν•˜λ©΄ 됨 (전체 재처리 λ°©μ§€)
  • 병렬 처리 효율: μƒ€λ“œκ°€ λ„ˆλ¬΄ 크면 Consumer κ°„ λΆ€ν•˜ λΆˆκ· ν˜•, λ„ˆλ¬΄ μž‘μœΌλ©΄ μ˜€λ²„ν—€λ“œ 증가

πŸ’‘ NDJSON ν˜•μ‹μ΄λž€?

  • Newline Delimited JSON: ν•œ 쀄에 ν•˜λ‚˜μ˜ JSON 객체, μ€„λ°”κΏˆ(\n)으둜 ꡬ뢄
  • 슀트리밍 νŒŒμ‹± κ°€λŠ₯: 전체 νŒŒμΌμ„ λ©”λͺ¨λ¦¬μ— μ˜¬λ¦¬μ§€ μ•Šκ³  ν•œ 쀄씩 μ½μ–΄μ„œ 처리
  • 일반 JSON λ°°μ—΄κ³Όμ˜ 차이:
    # 일반 JSON λ°°μ—΄ (전체λ₯Ό νŒŒμ‹±ν•΄μ•Ό 함)
    [{"id": 1}, {"id": 2}, {"id": 3}]
    
    # NDJSON (ν•œ 쀄씩 λ…λ¦½μ μœΌλ‘œ νŒŒμ‹± κ°€λŠ₯)
    {"id": 1}
    {"id": 2}
    {"id": 3}
    
  • λŒ€μš©λŸ‰ μ²˜λ¦¬μ— 적합: Wikipedia λ€ν”„μ²˜λŸΌ μˆ˜μ‹­λ§Œ 개의 λ¬Έμ„œλ₯Ό μ²˜λ¦¬ν•  λ•Œ λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰ μ΅œμ†Œν™”

1.2 HTML 크둀링 (YNA λ‰΄μŠ€)

μš”κ΅¬μ‚¬ν•­: ν•˜λ£¨μΉ˜ μˆ˜μ§‘, RSS ν™œμš©, νŽ˜μ΄μ§€ 링크 μžλ™ 탐색 Bot

κ΅¬ν˜„ 방식:

RSS ν”Όλ“œ λ‹€μš΄λ‘œλ“œ
    ↓ Rome RSS Parser둜 기사 λͺ©λ‘ μΆ”μΆœ
    ↓ 쀑볡 제거 (articleId κΈ°μ€€)
    ↓ Semaphore 기반 병렬 크둀링 (λ™μ‹œ 4개)
    ↓ μš”μ²­ κ°„ μ§€μ—° (250ms + jitter 150ms)
    ↓ MinIO μ €μž₯ + Kafka 이벀트 λ°œν–‰

μ„€μ •κ°’:

μ„€μ • κ°’ μ„€λͺ…
concurrency 4 λ™μ‹œ μš”μ²­ 수
inter-request-delay-ms 250 μš”μ²­ κ°„ μ§€μ—°
inter-request-jitter-ms 150 μ§€μ—° λžœλ€ν™”
connect-timeout-ms 2000 μ—°κ²° νƒ€μž„μ•„μ›ƒ
response-timeout-ms 5000 응닡 νƒ€μž„μ•„μ›ƒ

1.3 API 연동 (YouTube)

μš”κ΅¬μ‚¬ν•­: YouTube Developer API ν™œμš©, API 호좜 μž₯μ•  λŒ€μ‘

κ΅¬ν˜„ 방식:

YouTube Data API v3 호좜
    ↓ mostPopular λΉ„λ””μ˜€ λͺ©λ‘ 쑰회
    ↓ μž₯μ•  μ‹œ Exponential Backoff μž¬μ‹œλ„
    ↓ 429/5xx μ—λŸ¬ μžλ™ μž¬μ‹œλ„
    ↓ μ‹€νŒ¨ μ‹œ DLQ 전솑

μ„€μ •κ°’:

μ„€μ • κ°’ μ„€λͺ…
max-attempts 2 μ΅œλŒ€ μž¬μ‹œλ„ 횟수
base-backoff-ms 200 κΈ°λ³Έ λ°±μ˜€ν”„ μ‹œκ°„
batch-size 1000 배치 크기

2. μ •μ œ μ‹œμŠ€ν…œ (Refine System)

포트: 9082 | μ—­ν• : Kafka λ©”μ‹œμ§€ μˆ˜μ‹  β†’ 데이터 μ •μ œ β†’ Kafka λ©”μ‹œμ§€ λ°œμ†‘

μš”κ΅¬μ‚¬ν•­ μΆ©μ‘± 평가

μ‹œμŠ€ν…œ μš”κ΅¬μ‚¬ν•­ μƒνƒœ κ΅¬ν˜„ 방식
μ •μ œ λŒ€λŸ‰ 데이터 μˆ˜μ‹  (μ΄ˆλ‹Ή 1000건) ⚠️ λΆ€λΆ„ μΆ©μ‘± 배치(50) Γ— λ™μ‹œμ„±(10) Γ— 2 = ~1000건/초
μ •μ œ μΉ΄ν”„μΉ΄ 컨슈머 νŠœλ‹ βœ… μΆ©μ‘± 11개 νŠœλ‹ 포인트 적용
μ •μ œ 데이터 λ„˜λ²„λ§ (PK λΆ€μ—¬) βœ… μΆ©μ‘± refined_<SHA256(source+id)> 방식
μ •μ œ 메타데이터 생성 βœ… μΆ©μ‘± μ†ŒμŠ€λ³„ 상세 메타데이터 생성
μ •μ œ FullText μΆ”μΆœ βœ… μΆ©μ‘± Wikitext/HTML/JSON νŒŒμ‹±

2.1 λŒ€λŸ‰ 데이터 μˆ˜μ‹ 

μš”κ΅¬μ‚¬ν•­: μ΄ˆλ‹Ή 1000건 처리

κ΅¬ν˜„ 방식:

Kafka Consumer (concurrency=10, νŒŒν‹°μ…˜ μˆ˜μ™€ 동일)
    ↓ 배치 μˆ˜μ‹  (max-poll-records=50)
    ↓ 병렬 처리 (ThreadPool: core=10, max=20)
    ↓ μˆ˜λ™ 컀밋 (ack-mode=manual)

μ²˜λ¦¬λŸ‰ 계산:

이둠적 μ²˜λ¦¬λŸ‰ = 배치크기(50) Γ— λ™μ‹œμ„±(10) Γ— μ΄ˆλ‹Ήλ°°μΉ˜μˆ˜
             = 50 Γ— 10 Γ— ~2 = ~1000건/초

πŸ’‘ μ™œ μŠ€λ ˆλ“œ ν’€(core=10, max=20)을 μ΄λ ‡κ²Œ μ„€μ •ν–ˆλŠ”κ°€?

  • Kafka concurrency와 λ§€μΉ­: Consumer μŠ€λ ˆλ“œ 수(10)와 λ™μΌν•˜κ²Œ μ„€μ •ν•˜μ—¬ 병λͺ© λ°©μ§€
  • CPU λ°”μš΄λ“œ μž‘μ—… νŠΉμ„±: μ •μ œ μž‘μ—…μ€ HTML/Wikitext νŒŒμ‹±μœΌλ‘œ CPUλ₯Ό 많이 μ‚¬μš©
  • max = core Γ— 2 원칙: λ²„μŠ€νŠΈ νŠΈλž˜ν”½ μ‹œ 2λ°°κΉŒμ§€ ν™•μž₯, I/O λŒ€κΈ° 쀑 μΆ”κ°€ 처리 κ°€λŠ₯
  • queue-capacity=500: μŠ€λ ˆλ“œκ°€ 바쁠 λ•Œ λŒ€κΈ° 큐, λ„ˆλ¬΄ 크면 λ©”λͺ¨λ¦¬ λ‚­λΉ„

2.2 μΉ΄ν”„μΉ΄ 컨슈머 νŠœλ‹

적용된 νŠœλ‹ 포인트:

μ„€μ • κ°’ λͺ©μ 
max-poll-records 50 LAG λͺ¨λ‹ˆν„°λ§ μ΅œμ ν™”
fetch.min.bytes 1MB λ„€νŠΈμ›Œν¬ νš¨μœ¨ν™”
fetch.max.wait.ms 50ms μ§€μ—°/μ²˜λ¦¬λŸ‰ κ· ν˜•
concurrency 10 νŒŒν‹°μ…˜ μˆ˜μ™€ λ§€μΉ­
ack-mode MANUAL μ •ν™•ν•œ 처리 보μž₯
enable-auto-commit false μˆ˜λ™ 컀밋
session.timeout.ms 45000 μ„Έμ…˜ μœ μ§€
heartbeat.interval.ms 15000 ν•˜νŠΈλΉ„νŠΈ 간격
isolation.level read_committed μ»€λ°‹λœ λ°μ΄ν„°λ§Œ
max.poll.interval.ms 600000 배치 처리 μ΅œλŒ€ μ‹œκ°„

πŸ’‘ μ™œ max-poll-recordsλ₯Ό 50으둜 μ„€μ •ν–ˆλŠ”κ°€?

  • LAG λͺ¨λ‹ˆν„°λ§ 정확도: 값이 λ„ˆλ¬΄ 크면(300+) Consumer LAG μ§€ν‘œκ°€ μ‹€μ œλ³΄λ‹€ λΆ€ν’€λ €μ Έ λ³΄μž„
  • 처리 μ‹€νŒ¨ μ‹œ 재처리 λ²”μœ„ μ΅œμ†Œν™”: 배치 쀑간에 μ‹€νŒ¨ν•˜λ©΄ 전체 배치λ₯Ό μž¬μ²˜λ¦¬ν•΄μ•Ό ν•˜λ―€λ‘œ, μž‘μ€ λ°°μΉ˜κ°€ 볡ꡬ에 유리
  • λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰ μ œμ–΄: 50개 Γ— λ©”μ‹œμ§€ 크기둜 νž™ λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰ 예츑 κ°€λŠ₯

πŸ’‘ μ™œ fetch.min.bytesλ₯Ό 1MB둜 μ„€μ •ν–ˆλŠ”κ°€?

  • λ„€νŠΈμ›Œν¬ 왕볡 μ΅œμ†Œν™”: μž‘μ€ λ©”μ‹œμ§€κ°€ λ§Žμ„ λ•Œ 1MBκ°€ 될 λ•ŒκΉŒμ§€ λΈŒλ‘œμ»€κ°€ λŒ€κΈ° ν›„ 일괄 전솑
  • 브둜컀 λΆ€ν•˜ κ°μ†Œ: μž¦μ€ fetch μš”μ²­μœΌλ‘œ μΈν•œ 브둜컀 CPU μ‚¬μš©λŸ‰ 절감
  • μ²˜λ¦¬λŸ‰ ν–₯상: ν•œ λ²ˆμ— 더 λ§Žμ€ 데이터λ₯Ό 가져와 배치 처리 효율 증가

πŸ’‘ μ™œ concurrencyλ₯Ό νŒŒν‹°μ…˜ μˆ˜μ™€ λ™μΌν•˜κ²Œ λ§žμΆ”λŠ”κ°€?

  • 1:1 λ§€μΉ­ 원칙: Kafkaμ—μ„œ ν•˜λ‚˜μ˜ νŒŒν‹°μ…˜μ€ Consumer Group λ‚΄ 단 ν•˜λ‚˜μ˜ Consumer만 μ†ŒλΉ„ κ°€λŠ₯
  • λ¦¬μ†ŒμŠ€ λ‚­λΉ„ λ°©μ§€: concurrency > νŒŒν‹°μ…˜ 수이면 초과 μŠ€λ ˆλ“œλŠ” 유휴 μƒνƒœλ‘œ λ©”λͺ¨λ¦¬λ§Œ λ‚­λΉ„
  • 병렬성 μ΅œλŒ€ν™”: concurrency < νŒŒν‹°μ…˜ 수이면 일뢀 νŒŒν‹°μ…˜μ΄ ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œμ— λͺ°λ € LAG 증가
  • ν˜„μž¬ ꢌμž₯: νŒŒν‹°μ…˜ 10개 ν™˜κ²½μ—μ„œλŠ” concurrency: 10이 졜적

πŸ’‘ μ™œ ack-modeλ₯Ό MANUAL둜 μ„€μ •ν–ˆλŠ”κ°€?

  • μ •ν™•νžˆ ν•œ 번(Exactly-Once) 처리 보μž₯: λ©”μ‹œμ§€ μ²˜λ¦¬κ°€ μ™„μ „νžˆ λλ‚œ ν›„μ—λ§Œ 컀밋
  • 데이터 μœ μ‹€ λ°©μ§€: auto-commit은 처리 전에 컀밋될 수 μžˆμ–΄, μž₯μ•  μ‹œ λ©”μ‹œμ§€ μœ μ‹€ κ°€λŠ₯
  • 재처리 κ°€λŠ₯: 처리 쀑 μ˜ˆμ™Έ λ°œμƒ μ‹œ μ»€λ°‹ν•˜μ§€ μ•Šμ•„ μž¬μ‹œμž‘ ν›„ 동일 λ©”μ‹œμ§€ 재처리

πŸ’‘ μ™œ session.timeout.msλ₯Ό 45초둜 μ„€μ •ν–ˆλŠ”κ°€?

  • λ¦¬λ°ΈλŸ°μ‹± μ§€μ—° λ°©μ§€: 값이 λ„ˆλ¬΄ μž‘μœΌλ©΄ GCλ‚˜ μΌμ‹œμ  λ„€νŠΈμ›Œν¬ 지연에도 Consumerκ°€ 죽은 κ²ƒμœΌλ‘œ νŒλ‹¨
  • μž₯μ•  감지 속도 κ· ν˜•: 값이 λ„ˆλ¬΄ 크면 μ‹€μ œ μž₯μ•  μ‹œ λ¦¬λ°ΈλŸ°μ‹±μ΄ λŠ¦μ–΄μ Έ 처리 μ§€μ—°
  • heartbeat.interval.ms의 3λ°° 원칙: session.timeout(45s) = heartbeat(15s) Γ— 3, 3번의 ν•˜νŠΈλΉ„νŠΈ μ‹€νŒ¨ ν›„ μ œμ™Έ

πŸ’‘ μ™œ max.poll.interval.msλ₯Ό 600초(10λΆ„)둜 μ„€μ •ν–ˆλŠ”κ°€?

  • λŒ€μš©λŸ‰ 배치 처리 ν—ˆμš©: Wikipedia μƒ€λ“œ(500νŽ˜μ΄μ§€)λ‚˜ λŒ€λŸ‰ λ‰΄μŠ€ 처리 μ‹œ μΆ©λΆ„ν•œ μ‹œκ°„ 확보
  • μ™ΈλΆ€ μ„œλΉ„μŠ€ μ§€μ—° λŒ€λΉ„: MongoDB, MinIO λ“± μ™ΈλΆ€ μ €μž₯μ†Œ 응닡 μ§€μ—° μ‹œμ—λ„ νƒ€μž„μ•„μ›ƒ λ°©μ§€
  • λ¦¬λ°ΈλŸ°μ‹± 트리거 λ°©μ§€: 이 μ‹œκ°„ 내에 poll()을 ν˜ΈμΆœν•˜μ§€ μ•ŠμœΌλ©΄ Consumerκ°€ κ·Έλ£Ήμ—μ„œ μ œμ™Έλ¨

2.3 데이터 μ •μ œ

λ„˜λ²„λ§ (PK λΆ€μ—¬):

// 결정둠적 ID 생성 - λ©±λ“±μ„± 보μž₯
String refinedId = "refined_" + HashUtils.sha256Hex(source + pageId);
// 예: refined_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

FullText μΆ”μΆœ:

μ†ŒμŠ€ νŒŒμ„œ 처리
Wikipedia Bliki (Wikitext) Wikitext β†’ HTML β†’ PlainText
YNA News JSoup (HTML) HTML β†’ λ³Έλ¬Έ 선택 β†’ κ΄‘κ³ /UI 제거 β†’ PlainText
YouTube Jackson (JSON) JSON β†’ κ΅¬μ‘°ν™”λœ ν•„λ“œ μΆ”μΆœ

3. 색인 μ‹œμŠ€ν…œ (Index System)

포트: 9083 | μ—­ν• : Kafka λ©”μ‹œμ§€ μˆ˜μ‹  β†’ Elasticsearch둜 데이터 색인

μš”κ΅¬μ‚¬ν•­ μΆ©μ‘± 평가

μ‹œμŠ€ν…œ μš”κ΅¬μ‚¬ν•­ μƒνƒœ κ΅¬ν˜„ 방식
색인 λŒ€λŸ‰ 데이터 μˆ˜μ‹  (μ΄ˆλ‹Ή 1000건) ⚠️ λΆ€λΆ„ μΆ©μ‘± 배치(100) Γ— λ™μ‹œμ„±(10) = 1000건/초. κ·ΈλŸ¬λ‚˜ ν˜„μž¬ 600건/초
색인 μΉ΄ν”„μΉ΄ 컨슈머 νŠœλ‹ βœ… μΆ©μ‘± 11개 νŠœλ‹ 포인트 적용
색인 ν˜•νƒœμ†Œ 뢄석 색인 βœ… μΆ©μ‘± Elasticsearch Nori 뢄석기
색인 μžμ‹ λ§Œμ˜ ν˜•νƒœμ†Œ 뢄석기 ⚠️ λΆ€λΆ„ μΆ©μ‘± Nori + 자체 λΆˆμš©μ–΄ 필터링

3.1 λŒ€λŸ‰ 데이터 μˆ˜μ‹ 

μ²˜λ¦¬λŸ‰:

μ²˜λ¦¬λŸ‰ = 배치크기(100) Γ— λ™μ‹œμ„±(10) Γ— μ΄ˆλ‹Ήλ°°μΉ˜μˆ˜ β‰ˆ 1000~2000건/초

μ„€μ •:

μ„€μ • κ°’ μ„€λͺ…
max-poll-records 100 배치 크기
concurrency 10 νŒŒν‹°μ…˜ μˆ˜μ™€ λ§€μΉ­
BULK_SIZE 500 ES Bulk 크기

πŸ’‘ μ™œ max-poll-recordsλ₯Ό 100으둜 μ„€μ •ν–ˆλŠ”κ°€?

  • Elasticsearch Bulk API μ΅œμ ν™”: ESλŠ” κ°œλ³„ λ¬Έμ„œ 색인보닀 Bulk 색인이 10λ°° 이상 빠름
  • μ •μ œ μ‹œμŠ€ν…œλ³΄λ‹€ 큰 배치: 색인 μž‘μ—…μ€ μ •μ œ(νŒŒμ‹±/λ³€ν™˜)보닀 I/O λ°”μš΄λ“œμ΄λ―€λ‘œ 큰 λ°°μΉ˜κ°€ 유리
  • BULK_SIZE(500)μ™€μ˜ 관계: 100κ°œμ”© μ—¬λŸ¬ 번 pollν•˜μ—¬ 500κ°œκ°€ λͺ¨μ΄λ©΄ Bulk μš”μ²­ 전솑

πŸ’‘ μ™œ BULK_SIZEλ₯Ό 500으둜 μ„€μ •ν–ˆλŠ”κ°€?

  • ES 졜적 배치 크기: Elasticsearch 곡식 ꢌμž₯은 5-15MB λ˜λŠ” 1000-5000 λ¬Έμ„œ
  • λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰ κ· ν˜•: 500 Γ— 평균 λ¬Έμ„œ 크기(~10KB) = ~5MB둜 적정 μˆ˜μ€€
  • μ‹€νŒ¨ μ‹œ μž¬μ‹œλ„ λ²”μœ„: Bulk μ‹€νŒ¨ μ‹œ 500개 λ‹¨μœ„λ‘œ μž¬μ‹œλ„, λ„ˆλ¬΄ 크면 μž¬μ‹œλ„ λΆ€λ‹΄ 증가
  • λ„€νŠΈμ›Œν¬ 효율: λ„ˆλ¬΄ μž‘μœΌλ©΄ HTTP μ˜€λ²„ν—€λ“œ 증가, λ„ˆλ¬΄ 크면 νƒ€μž„μ•„μ›ƒ μœ„ν—˜

πŸ’‘ μ™œ μŠ€λ ˆλ“œ ν’€(core=10, max=20)을 μ΄λ ‡κ²Œ μ„€μ •ν–ˆλŠ”κ°€?

  • I/O λ°”μš΄λ“œ μž‘μ—… νŠΉμ„±: ES 색인은 λ„€νŠΈμ›Œν¬ I/O λŒ€κΈ°κ°€ λ§Žμ•„ CPU μ½”μ–΄ μˆ˜λ³΄λ‹€ λ§Žμ€ μŠ€λ ˆλ“œ ν•„μš”
  • core-pool-size=10: ν‰μƒμ‹œ μœ μ§€ν•  μŠ€λ ˆλ“œ 수, ES μ—°κ²° ν’€ 크기와 μœ μ‚¬ν•˜κ²Œ μ„€μ •
  • max-pool-size=20: λ²„μŠ€νŠΈ νŠΈλž˜ν”½ μ‹œ μž„μ‹œ ν™•μž₯, λ„ˆλ¬΄ 크면 ES μ—°κ²° 고갈 μœ„ν—˜
  • queue-capacity=500: μŠ€λ ˆλ“œκ°€ λͺ¨λ‘ 바쁠 λ•Œ λŒ€κΈ° 큐, BULK_SIZE와 λ™μΌν•˜κ²Œ μ„€μ •

3.2 ν˜•νƒœμ†Œ 뢄석 색인

Elasticsearch 뢄석기 μ„€μ •(elasticsearch-index-settings.json):

{
  "analyzer": {
    "korean_analyzer": {
      "type": "custom",
      "tokenizer": "nori_tokenizer",
      "filter": [
        "lowercase",
        "nori_part_of_speech",
        "korean_stopwords",
        "nori_readingform"
      ]
    }
  }
}

ν’ˆμ‚¬ 필터링 (λͺ…사 μœ„μ£Ό μΆ”μΆœ)

"nori_part_of_speech": {
"stoptags": [
"E", "IC", "J", "MAG", "MAJ", "MM", "SP", "SSC", "SSO",
"SC", "SE", "XPN", "XSA", "XSN", "XSV", "UNA", "NA", "VSV"
]
}
  • 제거: μ’…λ£ŒλΆ€ν˜Έ(E), 감탄사(IC), 쑰사(J), μˆ˜μ •μž(MM) λ“±
  • μœ μ§€: λͺ…사(N*), μ£Όμš” 동사/ν˜•μš©μ‚¬

3.3 자체 λΆˆμš©μ–΄ 필터링

Java 레벨 μΆ”κ°€ 필터링(ElasticsearchKeywordExtractor.java):

private static final Set<String> STOPWORDS = Set.of(
        // ν•œκ΅­μ–΄ 쑰사/μ–΄λ―Έ
        "μžˆλ‹€", "μžˆλŠ”", "ν•˜λ‹€", "λ˜λ‹€", "이닀", "것", "수", "λ“±", "κ·Έ", "및",
        "이", "κ°€", "을", "λ₯Ό", "의", "에", "와", "κ³Ό", "도", "만",
        // Wikipedia 메타데이터
        "탄생", "사망", "κΈ°λ…„", "사건", "λΆ„λ₯˜", "μ—°ν˜Έ", "μ™•μ‘°", "μΆœμƒ"
);

// ν‚€μ›Œλ“œ μΆ”μΆœ 둜직
Map<String, Long> tokenFrequency = response.tokens().stream()
        .filter(token -> token.length() >= 2)           // μ΅œμ†Œ 길이
        .filter(token -> !isStopword(token))            // λΆˆμš©μ–΄ 제거
        .collect(groupingBy(identity(), counting()));   // λΉˆλ„ 계산

3.4 Elasticsearch μ„€μ •

μ„€μ • κ°’ μ„€λͺ…
μƒ€λ“œ 수 3 λΆ„μ‚° 처리
λ ˆν”Œλ¦¬μΉ΄ 수 1 κ³ κ°€μš©μ„±
λ¦¬ν”„λ ˆμ‹œ 간격 5초 μ‹€μ‹œκ°„ 검색
Bulk 크기 500 λ¬Έμ„œ 배치 색인
JVM λ©”λͺ¨λ¦¬ 1GB νž™ λ©”λͺ¨λ¦¬

4. μ„œλΉ™ μ‹œμŠ€ν…œ (Serving System)

포트: 9086 | μ—­ν• : Elasticsearch 쿼리 및 Redis 캐싱

μš”κ΅¬μ‚¬ν•­ μΆ©μ‘± 평가

μ‹œμŠ€ν…œ μš”κ΅¬μ‚¬ν•­ μƒνƒœ κ΅¬ν˜„ 방식
μ„œλΉ™ λͺ…사 μΆ”μΆœ ⚠️ λΆ€λΆ„ μΆ©μ‘± Nori POS νƒœκ·Έ 필터링
μ„œλΉ™ λΉˆλ„μˆ˜ 계산 + κ°€μ€‘μΉ˜ βœ… μΆ©μ‘± μ†ŒμŠ€/μ‹œκ°„ κ°€μ€‘μΉ˜ 적용
μ„œλΉ™ 데이터 μ €μž₯ (ES) βœ… μΆ©μ‘± Elasticsearch에 ν‚€μ›Œλ“œ μ €μž₯
μ„œλΉ™ 데이터 μΊμ‹œ (μƒμœ„ 10건) βœ… μΆ©μ‘± Redis 캐싱 (ν•˜λ£¨ 1회 κ°±μ‹ , 24μ‹œκ°„ TTL)

4.1 였늘의 ν‚€μ›Œλ“œ 생성

처리 흐름

KeywordRankingScheduler (맀일 μžμ • + μ‹œμž‘ μ‹œ 10초 ν›„)
    ↓ Elasticsearch ν‚€μ›Œλ“œ 집계 (졜근 24μ‹œκ°„)
    ↓ μ†ŒμŠ€λ³„ κ°€μ€‘μΉ˜ 적용
    ↓ μ‹œκ°„λ³„ κ°€μ€‘μΉ˜ 적용 (ν•˜λ£¨ κΈ°μ€€)
    ↓ 점수 계산 및 λž­ν‚Ή
    ↓ Redis μΊμ‹œ μ €μž₯ (TTL: 24μ‹œκ°„)

ν‚€μ›Œλ“œ 점수 계산 κ°€μ€‘μΉ˜ μ„€μ •

  • μ†ŒμŠ€λ³„ κ°€μ€‘μΉ˜
μ†ŒμŠ€ κ°€μ€‘μΉ˜ μ„€λͺ…
NEWS_YNA 100.0 μ‹€μ‹œκ°„ λ‰΄μŠ€ (졜고 κ°€μ€‘μΉ˜)
SNS_YOUTUBE 5.0 SNS νŠΈλ Œλ“œ
DOCS_WIKIPEDIA 0.1 λ°°κ²½ 지식
  • μ‹œκ°„λ³„ κ°€μ€‘μΉ˜ (ν•˜λ£¨ κΈ°μ€€)
μ‹œκ°„ λ²”μœ„ κ°€μ€‘μΉ˜ μ„€λͺ…
당일 10.0 였늘 νŠΈλ Œλ“œ
전일 5.0 μ–΄μ œ νŠΈλ Œλ“œ
κ·Έ 이전 1.0 κΈ°λ³Έκ°’

점수 계산 곡식

점수 = docCount Γ— sourceWeight Γ— timeWeight

μ˜ˆμ‹œ: "ν•œλ™ν›ˆ" ν‚€μ›Œλ“œ

YNAμ—μ„œ 15건 (2μ‹œκ°„ μ „): 15 Γ— 100.0 Γ— 5.0 = 7,500.0
YouTubeμ—μ„œ 3건 (30λΆ„ μ „): 3 Γ— 5.0 Γ— 10.0 = 150.0
────────────────────────────────────────────────────
μ΅œμ’… 점수: 7,650.0

4.2 데이터 μΊμ‹œ

Redis 캐싱(KeywordRankingCache.java)

private static final String RANKING_KEY = "trending:keywords:full";
private static final long CACHE_TTL_HOURS = 24;

// μΊμ‹œ μ €μž₯
public void saveRanking(List<RankedKeyword> rankedKeywords) {
    String json = objectMapper.writeValueAsString(rankedKeywords);
    redisTemplate.opsForValue().set(RANKING_KEY, json,
            CACHE_TTL_HOURS, TimeUnit.HOURS);
}

// μƒμœ„ N개 쑰회
public List<RankedKeyword> getTopKeywords(int limit) {
    return allKeywords.stream().limit(limit).toList();
}

κ°±μ‹  μŠ€μΌ€μ€„λŸ¬(KeywordRankingScheduler.java)

@Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul")  // 맀일 μžμ •
public void updateKeywordRanking() {
    // 1. 졜근 24μ‹œκ°„ ν‚€μ›Œλ“œ 집계
    Map<ContentSource, List<RawKeyword>> keywordsBySource =
            aggregationService.aggregateAllKeywords(
                    LocalDateTime.now().minusHours(24));

    // 2. μŠ€μ½”μ–΄λ§ 및 λž­ν‚Ή
    List<RankedKeyword> ranked =
            scoringService.calculateRanking(keywordsBySource);

    // 3. Redis μ €μž₯
    rankingCache.saveRanking(ranked);
}

기술 μŠ€νƒ

Core

기술 버전 μš©λ„
Java 21 λŸ°νƒ€μž„
Spring Boot 3.3.1 ν”„λ ˆμž„μ›Œν¬
Gradle 8.x λΉŒλ“œ 도ꡬ

데이터 μ €μž₯

기술 버전 μš©λ„
MySQL 8.0 메타데이터 μ €μž₯
Elasticsearch 8.11.0 검색 인덱슀
MongoDB 7.0 μ •μ œ 데이터 μ €μž₯
Redis 7.2 캐싱
MinIO latest 원본 객체 μ €μž₯

λ©”μ‹œμ§•

기술 버전 μš©λ„
Apache Kafka 7.4.0 이벀트 슀트리밍

크둀링 & νŒŒμ‹±

기술 버전 μš©λ„
Selenium 4.16.1 동적 크둀링
JSoup 1.18.1 HTML νŒŒμ‹±
Apache Tika 2.9.2 λ¬Έμ„œ νŒŒμ‹±
Bliki 3.1.0 Wikitext νŒŒμ‹±
Rome 1.18.0 RSS νŒŒμ‹±

μ••μΆ•

기술 μš©λ„
Zstandard (zstd) Kafka λ©”μ‹œμ§€ μ••μΆ•
Snappy λΉ λ₯Έ μ••μΆ•
Commons Compress bz2 μ••μΆ• ν•΄μ œ

λͺ¨λ‹ˆν„°λ§

기술 μš©λ„
Prometheus λ©”νŠΈλ¦­ μˆ˜μ§‘
Grafana λŒ€μ‹œλ³΄λ“œ
Kibana Elasticsearch UI

인프라 ꡬ성

μ„œλΉ„μŠ€ 포트 λ§΅ν•‘

μ„œλΉ„μŠ€ 포트 μš©λ„
μ• ν”Œλ¦¬μΌ€μ΄μ…˜
Collector System 9080 데이터 μˆ˜μ§‘ API
Refine System 9082 데이터 μ •μ œ API
Index System 9083 검색 색인 API
Serving System 9086 μ‹€μ‹œκ°„ ν‚€μ›Œλ“œ API
λ°μ΄ν„°λ² μ΄μŠ€
MySQL 3306 메타데이터
Elasticsearch 9200 검색 μ—”μ§„
MongoDB 27017 λ¬Έμ„œ μ €μž₯μ†Œ
Redis 6379 캐싱
MinIO API 9000 객체 μŠ€ν† λ¦¬μ§€
MinIO Console 19000 MinIO 관리 UI
λ©”μ‹œμ§•
Kafka 9092 λ©”μ‹œμ§€ 브둜컀
λͺ¨λ‹ˆν„°λ§
Grafana 18083 λŒ€μ‹œλ³΄λ“œ
Prometheus 19090 λ©”νŠΈλ¦­
Kibana 15601 ES λͺ¨λ‹ˆν„°λ§
개발 도ꡬ
Kafka UI 18080 Kafka 관리
Mongo Express 18081 MongoDB UI
Redis Commander 18082 Redis UI

Docker Compose ν”„λ‘œνŒŒμΌ

ν”„λ‘œνŒŒμΌ μ„œλΉ„μŠ€ μš©λ„
collector MySQL, Kafka, MinIO μˆ˜μ§‘ μ‹œμŠ€ν…œ
refine MySQL, Kafka, MinIO, MongoDB, Redis μ •μ œ μ‹œμŠ€ν…œ
index MySQL, Kafka, Elasticsearch, MongoDB 색인 μ‹œμŠ€ν…œ
serving MySQL, Elasticsearch, Redis μ„œλΉ™ μ‹œμŠ€ν…œ
dev Kafka UI, Mongo Express, Redis Commander 개발 도ꡬ

Elasticsearch μ»€μŠ€ν…€ 이미지

FROM docker.elastic.co/elasticsearch/elasticsearch:8.11.0
RUN bin/elasticsearch-plugin install --batch analysis-nori

ν•œκ΅­μ–΄ ν˜•νƒœμ†Œ 뢄석을 μœ„ν•΄ Nori ν”ŒλŸ¬κ·ΈμΈμ΄ ν¬ν•¨λœ μ»€μŠ€ν…€ 이미지 μ‚¬μš©


μ„€μΉ˜ 및 μ‹€ν–‰

사전 μš”κ΅¬μ‚¬ν•­

  • Java 21+
  • Docker & Docker Compose
  • Gradle 8.x

1. ν”„λ‘œμ νŠΈ 클둠 및 ν™˜κ²½ μ„€μ •

git clone <repository-url>
cd realtime-ju

# ν™˜κ²½ λ³€μˆ˜ μ„€μ •
cp .env.example .env
# .env νŒŒμΌμ„ μ—΄μ–΄ ν•„μš”ν•œ κ°’ μˆ˜μ •

2. 인프라 μ‹€ν–‰

# 전체 인프라 + λͺ¨λ‹ˆν„°λ§ μ‹œμž‘
./scripts/compose-up-all.sh

# λ˜λŠ” κ°œλ³„ μ‹œμŠ€ν…œλ³„ μ‹€ν–‰
./scripts/compose-up-collector.sh   # μˆ˜μ§‘ μ‹œμŠ€ν…œμš©
./scripts/compose-up-refine.sh      # μ •μ œ μ‹œμŠ€ν…œμš©
./scripts/compose-up-index.sh       # 색인 μ‹œμŠ€ν…œμš©
./scripts/compose-up-serving.sh     # μ„œλΉ™ μ‹œμŠ€ν…œμš©

# 전체 쀑지 및 λ³Όλ₯¨ 정리
./scripts/compose-down.sh

3. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λΉŒλ“œ

# 전체 ν”„λ‘œμ νŠΈ λΉŒλ“œ
./gradlew clean build

# 곡톡 라이브러리 λ°œν–‰
./gradlew :common-lib:publishToMavenLocal

4. μ‹œμŠ€ν…œ μ‹€ν–‰

# 각 μ‹œμŠ€ν…œ κ°œλ³„ μ‹€ν–‰
./gradlew :collector-system:bootRun
./gradlew :refine-system:bootRun
./gradlew :index-system:bootRun
./gradlew :serving-system:bootRun

5. ν…ŒμŠ€νŠΈ

# 전체 ν…ŒμŠ€νŠΈ
./gradlew test

# νŠΉμ • λͺ¨λ“ˆ ν…ŒμŠ€νŠΈ
./gradlew :collector-system:test
./gradlew :refine-system:test
./gradlew :index-system:test
./gradlew :serving-system:test

About

πŸ” μ‹€μ‹œκ°„ 검색어 μ„œλΉ„μŠ€

Resources

Stars

Watchers

Forks

Contributors

Languages