Skip to content

πŸ› οΈ API-Football 동기화 μ•ˆμ •μ„± 보완 - #31

Merged
son0307 merged 2 commits into
mainfrom
codex/api-football-sync-reliability
Jul 20, 2026
Merged

πŸ› οΈ API-Football 동기화 μ•ˆμ •μ„± 보완#31
son0307 merged 2 commits into
mainfrom
codex/api-football-sync-reliability

Conversation

@son0307

@son0307 son0307 commented Jul 20, 2026

Copy link
Copy Markdown
Owner

λ³€κ²½ 사항

API-Football 호좜 μ‹€νŒ¨ 및 μ„œν‚· 브레이컀 보완

  • 곡톡 μ‹€ν–‰κΈ°μ˜ λΉ λ₯Έ μž¬μ‹œλ„κ°€ λͺ¨λ‘ λλ‚œ λ’€ μ΅œμ’… 결과만 API-Football μ„œν‚· λΈŒλ ˆμ΄μ»€μ— λ°˜μ˜ν•˜λ„λ‘ λ³€κ²½ν–ˆμŠ΅λ‹ˆλ‹€.
  • timeout, network, upstream server, invalid response만 provider μž₯μ• λ‘œ μ§‘κ³„ν•˜κ³  μΈμ¦Β·μš”μ²­ 였λ₯˜Β·quotaΒ·rate limit은 μ„œν‚· μ‹€νŒ¨ νšŸμˆ˜μ—μ„œ μ œμ™Έν–ˆμŠ΅λ‹ˆλ‹€.
  • μ΅œμ’… μ‹€νŒ¨ μ˜ˆμ™Έμ˜ retry κ°€λŠ₯ 여뢀와 Retry-After 정보λ₯Ό μžλ™ λ™κΈ°ν™”μ˜ 느린 μž¬μ‹œλ„ 정책에 μ „λ‹¬ν•©λ‹ˆλ‹€.

μžλ™ 동기화 느린 μž¬μ‹œλ„ κ°œμ„ 

  • μ •κΈ° scheduler와 startup μžλ™ λ™κΈ°ν™”μ—λ§Œ 느린 μž¬μ‹œλ„λ₯Ό μœ μ§€ν•˜κ³  κ΄€λ¦¬μž μš”μ²­ μ‹€νŒ¨μ—λŠ” μ˜ˆμ•½ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • κΈ°λ³Έ 지연을 1λΆ„, 5λΆ„μœΌλ‘œ μ¦κ°€μ‹œν‚€λ©° 일반 지연은 μ΅œλŒ€ 30λΆ„μœΌλ‘œ μ œν•œν•©λ‹ˆλ‹€.
  • μœ νš¨ν•œ Retry-Afterκ°€ 있으면 일반 지연보닀 μš°μ„ ν•˜κ³ , μž¬μ‹œλ„ μ‹€νŒ¨ μ‹œ κ°€μž₯ 졜근 응닡 값을 λ‹€μŒ μ˜ˆμ•½μ— μ‚¬μš©ν•©λ‹ˆλ‹€.
  • retryKey와 executionKeyλ₯Ό ν˜ΈμΆœλΆ€μ—μ„œ λͺ…μ‹œμ μœΌλ‘œ 전달해 κ°œλ³„ νŒ€Β·μ²­ν¬ μž¬μ‹œλ„μ™€ 전체 동기화 λ²”μœ„λ₯Ό κ΅¬λΆ„ν•©λ‹ˆλ‹€.
  • μ˜ˆμ•½ μƒνƒœμ— ScheduledFuture와 μ·¨μ†Œ μƒνƒœλ₯Ό 보관해 동일 λ²”μœ„μ˜ μžλ™ λ˜λŠ” κ΄€λ¦¬μž 동기화가 λ¨Όμ € μ„±κ³΅ν•˜λ©΄ λŒ€κΈ° 쀑인 μž¬μ‹œλ„λ₯Ό μ·¨μ†Œν•©λ‹ˆλ‹€.
  • μ·¨μ†Œμ™€ timer 싀행이 κ²½ν•©ν•˜λ”λΌλ„ 이전 μž‘μ—…μ΄ APIλ₯Ό ν˜ΈμΆœν•˜κ±°λ‚˜ 슀슀둜 λ‹€μ‹œ μ˜ˆμ•½ν•˜μ§€ λͺ»ν•˜λ„둝 μ†Œμœ κΆŒμ„ ν™•μΈν•©λ‹ˆλ‹€.
  • 동일 μž‘μ—…μ΄ 이미 μ‹€ν–‰ 쀑이면 μž¬μ‹œλ„ 횟수λ₯Ό μ†Œλͺ¨ν•˜μ§€ μ•Šκ³  1λΆ„ ν›„ λ‹€μ‹œ ν™•μΈν•©λ‹ˆλ‹€.
  • λΆ€λΆ„ μž¬μ‹œλ„ 성곡은 μžμ‹ μ˜ μž‘μ—…λ§Œ μ œκ±°ν•˜κ³ , 전체 동기화 성곡은 같은 μ‹€ν–‰ λ²”μœ„μ˜ νŒ€Β·μ²­ν¬ μž¬μ‹œλ„λ₯Ό λͺ¨λ‘ μ œκ±°ν•©λ‹ˆλ‹€.

쀑볡 동기화 λ°©μ§€

  • 동기화 λ²”μœ„λ³„ executionKey와 UUID μ†Œμœ κΆŒ 토큰을 μ‚¬μš©ν•˜λŠ” μ‹€ν–‰ μž κΈˆμ„ μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.
  • scheduler, startup μž¬μ‹œλ„, 동기·비동기 κ΄€λ¦¬μž μš”μ²­μ΄ 같은 λ²”μœ„μ˜ μž‘μ—…μ„ 쀑볡 μˆ˜ν–‰ν•˜μ§€ μ•Šλ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€.
  • 이전 μž‘μ—…μ΄ 늦게 μ’…λ£Œλ˜λ”λΌλ„ μƒˆ μž‘μ—…μ˜ μž κΈˆμ„ ν•΄μ œν•  수 없도둝 μ†Œμœ κΆŒμ„ κ²€μ¦ν•©λ‹ˆλ‹€.
  • 비동기 κ΄€λ¦¬μž μž‘μ—…μ€ 큐 등둝 μ‹œμ μ΄ μ•„λ‹ˆλΌ worker의 μ‹€μ œ 처리 μ™„λ£Œ μ‹œμ κΉŒμ§€ μž κΈˆμ„ μœ μ§€ν•©λ‹ˆλ‹€.
  • DB의 QUEUED/RUNNING/CANCEL_REQUESTED μƒνƒœλ„ ν•¨κ»˜ 확인해 쀑볡 큐 등둝을 μ°¨λ‹¨ν•˜κ³  409 였λ₯˜λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

μ½”λ“œ 리뷰 μ›Œν¬ν”Œλ‘œ λͺ¨λΈ λ³€κ²½

  • Codex PR 리뷰의 κΈ°λ³Έ λͺ¨λΈμ„ gpt-5.4-miniμ—μ„œ gpt-5.6-luna둜 λ³€κ²½ν–ˆμŠ΅λ‹ˆλ‹€.
  • μ €μž₯μ†Œ λ³€μˆ˜ CODEX_REVIEW_MODEL이 μ„€μ •λœ κ²½μš°μ—λŠ” 기쑴처럼 ν•΄λ‹Ή 값을 μš°μ„ ν•©λ‹ˆλ‹€.

λ³€κ²½ 이유

곡톡 μ‹€ν–‰κΈ°μ˜ λΉ λ₯Έ μž¬μ‹œλ„μ™€ μžλ™ λ™κΈ°ν™”μ˜ 느린 μž¬μ‹œλ„λŠ” λͺ©μ μ΄ λ‹€λ₯΄λ―€λ‘œ λͺ¨λ‘ μœ μ§€ν•˜λ˜, 이미 동일 λ²”μœ„μ˜ 동기화가 μ„±κ³΅ν•œ 뒀에도 μ˜ˆμ•½λœ μž‘μ—…μ΄ λ‹€μ‹œ μ‹€ν–‰λ˜λŠ” 문제λ₯Ό 막을 ν•„μš”κ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ scheduler와 κ΄€λ¦¬μž μš”μ²­μ΄ λ™μ‹œμ— 같은 데이터λ₯Ό κ°±μ‹ ν•˜λ©΄ 쀑볡 API 호좜과 μƒνƒœ 경합이 λ°œμƒν•  수 μžˆμ–΄ μ‹€ν–‰ λ²”μœ„μ™€ μ†Œμœ κΆŒμ„ λͺ…ν™•νžˆ κ΄€λ¦¬ν•˜λ„λ‘ λ³΄μ™„ν–ˆμŠ΅λ‹ˆλ‹€.

μ„€μ • λ³€κ²½

  • API_FOOTBALL_SYNC_FAILURE_RETRY_INITIAL_DELAY_MINUTES: 느린 μž¬μ‹œλ„ 졜초 μ§€μ—°
  • API_FOOTBALL_SYNC_FAILURE_RETRY_DELAY_MULTIPLIER: μ§€μ—° 배수
  • API_FOOTBALL_SYNC_FAILURE_RETRY_MAX_DELAY_MINUTES: 일반 μ΅œλŒ€ μ§€μ—°
  • κΈ°μ‘΄ API_FOOTBALL_SYNC_FAILURE_RETRY_DELAY_MINUTESλŠ” 졜초 μ§€μ—°μ˜ ν˜Έν™˜ μ„€μ •μœΌλ‘œ μœ μ§€ν•©λ‹ˆλ‹€.

μ™ΈλΆ€ REST API, DB μŠ€ν‚€λ§ˆ, κ΄€λ¦¬μž ν™”λ©΄ 응닡 ν˜•μ‹μ€ λ³€κ²½ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

검증

  • API-Football μ„œν‚· 브레이컀 였λ₯˜ category 집계 ν…ŒμŠ€νŠΈ 톡과
  • μ‹€ν–‰ 잠금의 쀑볡 차단 및 UUID μ†Œμœ κΆŒ 검증 ν…ŒμŠ€νŠΈ 톡과
  • κ΄€λ¦¬μž 동기화 쀑볡 μš”μ²­κ³Ό 비동기 worker 성곡 μ‹œμ  ν…ŒμŠ€νŠΈ 톡과
  • 느린 μž¬μ‹œλ„μ˜ 1λΆ„β†’5λΆ„ μ§€μ—°, Retry-After μš°μ„  적용, 쀑볡 μ˜ˆμ•½ λ°©μ§€ ν…ŒμŠ€νŠΈ 톡과
  • 전체 성곡·뢀뢄 성곡에 λ”°λ₯Έ μž¬μ‹œλ„ μ·¨μ†Œ λ²”μœ„μ™€ timer κ²½ν•© ν…ŒμŠ€νŠΈ 톡과
  • κ΄€λ ¨ λ‹¨μœ„ ν…ŒμŠ€νŠΈ 톡과
  • git diff --cached --check 톡과
  • 전체 λ°±μ—”λ“œ ν…ŒμŠ€νŠΈ 219개 쀑 218개 톡과
    • contextLoads() 1κ°œλŠ” 둜컬 ν™˜κ²½μ—μ„œ Docker/Testcontainersλ₯Ό μ‚¬μš©ν•  수 μ—†μ–΄ μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.

ν”„λŸ°νŠΈμ—”λ“œ 변경은 μ—†μ–΄ 별도 λΉŒλ“œλ₯Ό μˆ˜ν–‰ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

후속 κ²€ν† 

  • κ΄€λ¦¬μž 전체 동기화가 일뢀 μ‹€νŒ¨ν–ˆμ„ λ•Œ κΈ°μ‘΄ μžλ™ λΆ€λΆ„ μž¬μ‹œλ„μ˜ 후속 성곡과 전체 μƒνƒœ μš°μ„ μˆœμœ„λ₯Ό μ–΄λ–»κ²Œ νŒλ‹¨ν• μ§€ λ³„λ„λ‘œ κ²€ν† ν•©λ‹ˆλ‹€.
  • ν”„λ‘œμ„ΈμŠ€ μž¬μ‹œμž‘κ³Ό 닀쀑 μΈμŠ€ν„΄μŠ€ ν™˜κ²½μ—μ„œλ„ μž κΈˆΒ·μž¬μ‹œλ„ μƒνƒœλ₯Ό μœ μ§€ν•΄μ•Ό ν•œλ‹€λ©΄ μ˜μ† μ €μž₯μ†Œ 기반으둜 ν™•μž₯ν•©λ‹ˆλ‹€.
  • 뢀상 λ™κΈ°ν™”μ˜ νŽ˜μ΄μ§€Β·λΆ€λΆ„ μž¬μ‹œλ„ λΆ„λ¦¬λŠ” μ‹€μ œ 호좜 규λͺ¨μ™€ μž₯μ•  λΉˆλ„κ°€ 컀질 λ•Œ κ²€ν† ν•©λ‹ˆλ‹€.

@son0307
son0307 marked this pull request as ready for review July 20, 2026 08:06
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codex PR Review

PR μš”μ•½

μš”μ•½

API-Football λ™κΈ°ν™”μ˜ μž¬μ‹œλ„, 쀑볡 μ‹€ν–‰, μ‹€νŒ¨ νŒμ • λ‘œμ§μ„ 보완해 μžλ™ λ™κΈ°ν™”μ˜ μ•ˆμ •μ„±μ„ λ†’μ˜€μŠ΅λ‹ˆλ‹€. ν•¨κ»˜ Codex PR 리뷰 μ›Œν¬ν”Œλ‘œμš°μ˜ κΈ°λ³Έ λͺ¨λΈλ„ μ—…λ°μ΄νŠΈν–ˆμŠ΅λ‹ˆλ‹€.

μ£Όμš” 변경사항

  • API-Football 응닡 검증을 κ°•ν™”ν•΄ 빈 응닡, 잘λͺ»λœ envelope, λΆˆμΌμΉ˜ν•œ κ²°κ³Ό 수λ₯Ό INVALID_RESPONSE둜 μ²˜λ¦¬ν•˜λ„λ‘ λ³€κ²½ν–ˆμŠ΅λ‹ˆλ‹€.
  • μ„œν‚· λΈŒλ ˆμ΄μ»€κ°€ timeout, network, upstream server, invalid response만 μž₯μ• λ‘œ μ§‘κ³„ν•˜λ„λ‘ μ‘°μ •ν–ˆμŠ΅λ‹ˆλ‹€.
  • μžλ™ λ™κΈ°ν™”μ˜ 느린 μž¬μ‹œλ„λ₯Ό κ°œμ„ ν•΄ Retry-After μš°μ„  적용, 초기/μ΅œλŒ€ μ§€μ—° μ‘°μ •, 쀑볡 μ˜ˆμ•½ μ·¨μ†Œλ₯Ό μ§€μ›ν–ˆμŠ΅λ‹ˆλ‹€.
  • scheduler, startup, κ΄€λ¦¬μž 동기화에 executionKey 기반 μ‹€ν–‰ μž κΈˆμ„ μΆ”κ°€ν•΄ 같은 λ²”μœ„μ˜ 쀑볡 싀행을 λ§‰μ•˜μŠ΅λ‹ˆλ‹€.
  • κ΄€λ¦¬μž λ™κΈ°ν™”λŠ” 이미 μ‹€ν–‰/λŒ€κΈ° 쀑인 μž‘μ—…μ΄ 있으면 409둜 μ°¨λ‹¨ν•˜κ³ , 성곡 μ‹œ μ˜ˆμ•½λœ μž¬μ‹œλ„λ₯Ό μ·¨μ†Œν•˜λ„λ‘ λ³€κ²½ν–ˆμŠ΅λ‹ˆλ‹€.
  • Codex 리뷰 κΈ°λ³Έ λͺ¨λΈμ„ gpt-5.4-miniμ—μ„œ gpt-5.6-luna둜 λ³€κ²½ν–ˆμŠ΅λ‹ˆλ‹€.
  • κ΄€λ ¨ 섀정값을 application.yml에 μΆ”κ°€/λ³€κ²½ν–ˆμŠ΅λ‹ˆλ‹€.

영ν–₯ λ²”μœ„

  • API-Football 동기화
  • μžλ™ μž¬μ‹œλ„ 및 μŠ€μΌ€μ€„λŸ¬
  • κ΄€λ¦¬μž 동기화
  • 였λ₯˜ 처리/μ„œν‚· 브레이컀
  • μ„€μ •(application.yml)
  • CI μ›Œν¬ν”Œλ‘œμš°(codex-review.yml)

검증

  • κ΄€λ ¨ λ‹¨μœ„ ν…ŒμŠ€νŠΈ 톡과
  • git diff --cached --check 톡과
  • 전체 λ°±μ—”λ“œ ν…ŒμŠ€νŠΈ 일뢀 톡과: 219개 쀑 218개 톡과
  • ν”„λŸ°νŠΈμ—”λ“œ λ³€κ²½ μ—†μŒμœΌλ‘œ 별도 λΉŒλ“œ μ—†μŒ

μ‹œλ‹ˆμ–΄ 개발자 리뷰

  • [P1] src/main/java/com/son/soccerStreaming/apifootball/scheduler/ApiFootballSyncFailureRetryScheduler.java:~70-120 - Retry-Afterλ₯Ό λ¬΄μ‹œν•œ 1λΆ„ κ°•μ œ μž¬μŠ€μΌ€μ€„ κ°€λŠ₯μ„±

    • 문제: runRetry()μ—μ„œ 동일 싀행이 아직 active이면 scheduleNext(..., activeJobDeferred = true)둜 λ‹€μ‹œ μ˜ˆμ•½ν•˜λŠ”λ°, 이 λΆ„κΈ°μ—μ„œλŠ” retryAfter(failure)λ₯Ό μ „ν˜€ 보지 μ•Šκ³  initialDelayMinutes(κΈ°λ³Έ 1λΆ„)만 μ‚¬μš©ν•©λ‹ˆλ‹€. PR μ„€λͺ…상 β€œμœ νš¨ν•œ Retry-Afterκ°€ 있으면 일반 지연보닀 μš°μ„ β€ν•΄μ•Ό ν•˜λŠ”λ°, 였히렀 아직 μ‹€ν–‰ 쀑일 λ•ŒλŠ” Retry-Afterκ°€ λ¬΄μ‹œλ©λ‹ˆλ‹€.
    • 영ν–₯: API-Football이 Retry-After: 10뢄을 λ°˜ν™˜ν•œ 직후에도 1λΆ„λ§ˆλ‹€ μž¬μ‹œλ„κ°€ λ°œμƒν•  수 μžˆμ–΄ rate limit을 더 μ•…ν™”μ‹œν‚€κ³ , λΆˆν•„μš”ν•œ μ˜ˆμ•½/λ‘œκ·Έκ°€ λ°˜λ³΅λ©λ‹ˆλ‹€.
    • μˆ˜μ • λ°©ν–₯: active job defer κ²½λ‘œμ—μ„œλ„ λ§ˆμ§€λ§‰ μ‹€νŒ¨ μ˜ˆμ™Έλ₯Ό μ €μž₯ν•΄ 두고 retryDelay()λ₯Ό κ·ΈλŒ€λ‘œ μ μš©ν•˜κ±°λ‚˜, μ΅œμ†Œν•œ Retry-Afterκ°€ 있으면 max(initialDelay, retryAfter)κ°€ μ•„λ‹ˆλΌ retryAfterλ₯Ό μš°μ„  μ‚¬μš©ν•˜λ„λ‘ μˆ˜μ •ν•˜μ„Έμš”.
  • [P1] src/main/java/com/son/soccerStreaming/apifootball/scheduler/ApiFootballSyncFailureRetryScheduler.java:~220-290 - recordSuccessByKeyκ°€ λ„ˆλ¬΄ μ‰½κ²Œ λˆŒλ €μ„œ μ‹€νŒ¨ μƒνƒœκ°€ 영ꡬ적으둜 남을 수 있음

    • 문제: recordSuccessWhenNoRelatedRetryRemains()λŠ” completedRetryKeyκ°€ :team: λ˜λŠ” :chunk:λ₯Ό 포함할 λ•Œλ§Œ successλ₯Ό κΈ°λ‘ν•©λ‹ˆλ‹€. 그런데 terminalFailedRetriesμ—λŠ” 같은 executionKey의 μ‹€νŒ¨ ν‚€κ°€ 남아 μžˆμ„ 수 있고, cancelPendingByExecutionKey()κ°€ ν˜ΈμΆœλ˜μ§€ μ•Šμ€ 경둜(예: 성곡 ν›„ μƒνƒœ κ°±μ‹  타이밍 κ²½ν•©, λ‹€λ₯Έ 성곡 경둜)μ—μ„œλŠ” μ„±κ³΅μœΌλ‘œ μ „ν™˜λ˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. 특히 전체 동기화 성곡 후에도 이미 μ‹€νŒ¨λ‘œ 기둝된 retryKey/μƒνƒœκ°€ 남아 있으면 relatedRetryFailedκ°€ true둜 남아 성곡 볡ꡬ가 λ§‰νž™λ‹ˆλ‹€.
    • 영ν–₯: μš΄μ˜μƒ β€œμ΄λ―Έ μ„±κ³΅ν•œ 동기화”가 μ‹€νŒ¨ μƒνƒœλ‘œ 지속 ν‘œμ‹œλ  수 있고, κ΄€λ¦¬μž ν™”λ©΄/μƒνƒœ APIκ°€ μ‹€μ œμ™€ λ‹€λ₯΄κ²Œ 보여 ν˜Όλž€μ„ μ€λ‹ˆλ‹€.
    • μˆ˜μ • λ°©ν–₯: 성곡 μ‹œμ—λŠ” 같은 executionKey에 λ§€ν•‘λœ terminal μ‹€νŒ¨λ„ ν•¨κ»˜ μ •λ¦¬ν•˜κ±°λ‚˜, terminalFailedRetriesλ₯Ό 성곡 μ΄λ²€νŠΈμ—μ„œ clearν•˜λŠ” 경둜λ₯Ό λͺ…ν™•νžˆ λ‘μ„Έμš”. μ΅œμ†Œν•œ cancelPendingByExecutionKey()처럼 성곡 처리 κ²½λ‘œμ—μ„œλ„ 같은 executionKey의 terminal failureλ₯Ό μ œκ±°ν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • [P2] src/main/java/com/son/soccerStreaming/admin/service/AdminService.java:704-720 - 큐 등둝 전에 락을 μž‘μ•„ 비정상 μ’…λ£Œ μ‹œ μž‘μ—…μ΄ 영ꡬ 차단될 수 있음

    • 문제: queueSync()μ—μ„œ acquireManualSync(syncKey)λ₯Ό λ¨Όμ € ν˜ΈμΆœν•œ λ’€ adminSyncJobService.create(...)λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€. create()κ°€ DB μ˜ˆμ™Έ λ“±μœΌλ‘œ μ‹€νŒ¨ν•˜λ©΄ releaseManualSync()λŠ” ν˜ΈμΆœλ˜μ§€λ§Œ, apiFootballSyncExecutionGuard.acquire()κ°€ μ„±κ³΅ν•œ λ’€ manualSyncStates.compute(...) λ‚΄λΆ€μ—μ„œ μ˜ˆμ™Έκ°€ λ‚œ 경우처럼 κ²½λ‘œκ°€ λ³΅μž‘ν•΄μ Έ, ν–₯ν›„ λ³€κ²½ μ‹œ 락/μƒνƒœ λΆˆμΌμΉ˜κ°€ 생기기 μ‰½μŠ΅λ‹ˆλ‹€. λ˜ν•œ hasActiveJob(task, details)κ°€ pre-check만 ν•˜κ³  DB μœ λ‹ˆν¬ μ œμ•½μ΄ μ—†μ–΄ 경쟁 μš”μ²­μ„ μ™„μ „νžˆ 막지 λͺ»ν•©λ‹ˆλ‹€.
    • 영ν–₯: λ™μ‹œ μš”μ²­μ΄ λͺ°λ¦΄ λ•Œ 409 λŒ€μ‹  쀑볡 job 생성 μ‹œλ„λ‚˜ λΆˆν•„μš”ν•œ 락 μ μœ κ°€ λ°œμƒν•  수 있고, κ΅¬ν˜„μ΄ 쑰금만 λ°”λ€Œμ–΄λ„ 남은 reservation λ•Œλ¬Έμ— 같은 syncKeyκ°€ λ§‰νž 수 μžˆμŠ΅λ‹ˆλ‹€.
    • μˆ˜μ • λ°©ν–₯: μ§€κΈˆ ꡬ쑰λ₯Ό μœ μ§€ν•˜λ”λΌλ„ job 생성 μ‹€νŒ¨/μ˜ˆμ™Έ λͺ¨λ“  κ²½λ‘œμ—μ„œ guard와 manual stateκ°€ λ°˜λ“œμ‹œ ν•΄μ œλ˜λŠ”μ§€ 더 λͺ…μ‹œμ μœΌλ‘œ 보μž₯ν•˜μ„Έμš”. κ°€λŠ₯ν•˜λ©΄ DB λ ˆλ²¨μ—μ„œ task+details+active status μΆ©λŒμ„ λ§‰λŠ” μ΅œμ†Œν•œμ˜ μ œμ•½λ„ κ²€ν† ν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • [P2] src/main/java/com/son/soccerStreaming/apifootball/client/ApiFootballResponseValidator.java:17-35 - API-Football의 정상 응닡을 μ˜€νƒμ§€λ‘œ μ‹€νŒ¨ μ²˜λ¦¬ν•  수 있음

    • 문제: λͺ¨λ“  응닡에 λŒ€ν•΄ errors ν•„λ“œκ°€ λΉ„μ–΄ μžˆμ–΄μ•Ό ν•˜κ³ , results == response.size() μ—¬μ•Όλ§Œ μ •μƒμœΌλ‘œ μΈμ •ν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ API-Football은 μ—”λ“œν¬μΈνŠΈ/버전에 따라 results와 response κ°œμˆ˜κ°€ μ •ν™•νžˆ μΌμΉ˜ν•˜μ§€ μ•Šκ±°λ‚˜, errors ν•„λ“œκ°€ μ—†κ±°λ‚˜ ν˜•νƒœκ°€ λ‹€λ₯Έ 응닡을 쀄 수 μžˆμŠ΅λ‹ˆλ‹€. ν˜„μž¬ 검증은 μ‹€μ œ 정상 μ‘λ‹΅κΉŒμ§€ INVALID_RESPONSE둜 λ§Œλ“€ μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€.
    • 영ν–₯: 동기화가 λΆˆν•„μš”ν•˜κ²Œ μ‹€νŒ¨λ‘œ μ „ν™˜λ˜κ³ , μ„œν‚· λΈŒλ ˆμ΄μ»€μ™€ 느린 μž¬μ‹œλ„κ°€ μ—°μ‡„μ μœΌλ‘œ λ™μž‘ν•΄ 운영 μ•ˆμ •μ„±μ΄ λ‚˜λΉ μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
    • μˆ˜μ • λ°©ν–₯: μ΅œμ†Œ κ²€μ¦λ§Œ ν•˜μ„Έμš”. μ‹€μ œλ‘œ ν•„μš”ν•œ ν•„λ“œκ°€ λΉ„μ—ˆλŠ”μ§€, responseκ°€ null인지 μ •λ„λ‘œ μ œν•œν•˜κ³ , results 일치 검증은 ν•΄λ‹Ή μ—”λ“œν¬μΈνŠΈμ—μ„œ ν™•μ‹€νžˆ 보μž₯λ˜λŠ” κ²½μš°μ—λ§Œ μ μš©ν•˜λŠ” 게 μ•ˆμ „ν•©λ‹ˆλ‹€.
  • [P3] src/main/resources/application.yml:100-104 - μƒˆ μ„€μ • ν‚€λŠ” μΆ”κ°€λμ§€λ§Œ κΈ°μ‘΄ delay-minutesλ₯Ό μ½λŠ” μ½”λ“œ κ²½λ‘œκ°€ μ—†μŒ

    • 문제: application.yml은 initial-delay-minutes둜 λ°”λ€Œμ—ˆμ§€λ§Œ, μ½”λ“œκ°€ λ‹€λ₯Έ κ³³μ—μ„œ μ—¬μ „νžˆ API_FOOTBALL_SYNC_FAILURE_RETRY_DELAY_MINUTESλ₯Ό κΈ°λŒ€ν•˜κ±°λ‚˜ λ¬Έμ„œ/운영 섀정이 혼재돼 있으면 μ‹€μ œ 배포 ν™˜κ²½μ—μ„œ μ˜λ„μΉ˜ μ•Šκ²Œ κΈ°λ³Έκ°’ 1λΆ„μœΌλ‘œ λ™μž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 영ν–₯: μš΄μ˜μžκ°€ ν™˜κ²½λ³€μˆ˜λ₯Ό λ°”κΏ¨λ‹€κ³  μƒκ°ν–ˆλŠ”λ° λ°˜μ˜λ˜μ§€ μ•Šμ•„ μž¬μ‹œλ„ 정책이 μ˜ˆμƒκ³Ό λ‹¬λΌμ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
    • μˆ˜μ • λ°©ν–₯: ν˜Έν™˜ 섀정을 μœ μ§€ν•˜λŠ” μ˜λ„λŠ” μ’‹μ•„ λ³΄μ΄μ§€λ§Œ, 배포 λ¬Έμ„œ/ν™˜κ²½ λ³€μˆ˜ μ•ˆλ‚΄μ— μƒˆ ν‚€ μš°μ„ μˆœμœ„λ₯Ό λͺ…ν™•νžˆ 남기고, κ°€λŠ₯ν•˜λ©΄ λ‘œκ·Έμ— μ‹€μ œ μ μš©κ°’μ„ μ΄ˆκΈ°μ— ν•œ 번 λ…ΈμΆœν•˜μ„Έμš”.

λ³΄μ•ˆ 리뷰

  • [Medium] src/main/java/com/son/soccerStreaming/apifootball/service/ApiFootballSyncExecutionGuard.java:1-49 - 단일 μΈμŠ€ν„΄μŠ€ λ©”λͺ¨λ¦¬ 잠금으둜 쀑볡 λ™κΈ°ν™”λ§Œ 막고, 닀쀑 μΈμŠ€ν„΄μŠ€/μž¬μ‹œμž‘ ν™˜κ²½μ—μ„œλŠ” 우회 κ°€λŠ₯

    • 문제:
      • ConcurrentHashMap 기반의 μ‹€ν–‰ κ°€λ“œλŠ” JVM λ‚΄λΆ€μ—μ„œλ§Œ μœ νš¨ν•©λ‹ˆλ‹€. PR 본문에도 닀쀑 μΈμŠ€ν„΄μŠ€μ™€ ν”„λ‘œμ„ΈμŠ€ μž¬μ‹œμž‘ μ‹œ μ˜μ† μ €μž₯μ†Œλ‘œ ν™•μž₯ν•΄μ•Ό ν•œλ‹€κ³  μ–ΈκΈ‰λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
      • λ”°λΌμ„œ μ—¬λŸ¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μΈμŠ€ν„΄μŠ€κ°€ 같은 API-Football 동기화λ₯Ό λ™μ‹œμ— μˆ˜ν–‰ν•  수 μžˆμ–΄, 쀑볡 ν˜ΈμΆœΒ·μƒνƒœ κ²½ν•©Β·μΏΌν„° μ†Œμ§„μ΄ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 곡격/λ…ΈμΆœ μ‹œλ‚˜λ¦¬μ˜€:
      • 운영이 μˆ˜ν‰ ν™•μž₯된 μƒνƒœμ—μ„œ μŠ€μΌ€μ€„λŸ¬κ°€ 각 μΈμŠ€ν„΄μŠ€λ§ˆλ‹€ μ‹€ν–‰λ˜λ©΄ 같은 동기화 μž‘μ—…μ΄ 쀑볡 싀행될 수 μžˆμŠ΅λ‹ˆλ‹€.
      • μž¬μ‹œμž‘ 직후 μ˜ˆμ•½λœ μž¬μ‹œλ„μ™€ μƒˆ μΈμŠ€ν„΄μŠ€μ˜ μ •κΈ° μž‘μ—…μ΄ λ™μ‹œμ— APIλ₯Ό ν˜ΈμΆœν•΄ μž₯μ•  μž¬ν˜„μ΄λ‚˜ λ ˆμ΄νŠΈλ¦¬λ°‹ 유발이 κ°€λŠ₯ν•΄μ§‘λ‹ˆλ‹€.
    • μˆ˜μ • λ°©ν–₯:
      • DB/뢄산락(Redis, DB row lock λ“±) 기반의 μ „μ—­ μ‹€ν–‰ 잠금으둜 λ³€κ²½ν•˜μ„Έμš”.
      • μ΅œμ†Œν•œ executionKeyλ₯Ό μ˜μ† μ €μž₯μ†Œμ— κΈ°λ‘ν•˜κ³  TTL/heartbeatλ₯Ό λ‘¬μ„œ μž₯μ•  볡ꡬ와 쀑볡 λ°©μ§€λ₯Ό ν•¨κ»˜ 보μž₯ν•˜μ„Έμš”.
  • [Medium] src/main/java/com/son/soccerStreaming/apifootball/scheduler/ApiFootballSyncFailureRetryScheduler.java:전체 hunk(라인 번호 제곡 λΆˆκ°€, diffκ°€ 잘렀 μ •ν™•ν•œ 라인 확인 λΆˆκ°€) - μž¬μ‹œλ„ μŠ€μΌ€μ€„ μƒνƒœκ°€ λ©”λͺ¨λ¦¬μ—λ§Œ μ €μž₯λ˜μ–΄ μž¬μ‹œμž‘ μ‹œ μ†Œμ‹€

    • 문제:
      • retryStates, terminalFailedRetriesκ°€ λͺ¨λ‘ 인메λͺ¨λ¦¬ μžλ£Œκ΅¬μ‘°λΌμ„œ ν”„λ‘œμ„ΈμŠ€ μž¬μ‹œμž‘ μ‹œ μ˜ˆμ•½/μ·¨μ†Œ/μ‹€νŒ¨ μƒνƒœκ°€ μ‚¬λΌμ§‘λ‹ˆλ‹€.
      • κ·Έ κ²°κ³Ό 이미 μ‹€νŒ¨ν•œ μž‘μ—…μ΄ λ‹€μ‹œ μ˜ˆμ•½λ˜κ±°λ‚˜, λ°˜λŒ€λ‘œ λ°˜λ“œμ‹œ μˆ˜ν–‰λ˜μ–΄μ•Ό ν•  μž¬μ‹œλ„κ°€ 영ꡬ적으둜 λˆ„λ½λ  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 곡격/λ…ΈμΆœ μ‹œλ‚˜λ¦¬μ˜€:
      • 운영 쀑 μž¬λ°°ν¬λ‚˜ ν¬λž˜μ‹œκ°€ λ°œμƒν•˜λ©΄ μ˜ˆμ•½λœ 후속 μž¬μ‹œλ„κ°€ λ‚ μ•„κ°€ 데이터 동기화가 λˆ„λ½λ  수 μžˆμŠ΅λ‹ˆλ‹€.
      • λ°˜λŒ€λ‘œ μž¬μ‹œμž‘ ν›„ 이전 μ‹€νŒ¨ μƒνƒœλ₯Ό μžƒμ–΄λ²„λ € 같은 μž‘μ—…μ΄ 반볡 싀행될 수 μžˆμŠ΅λ‹ˆλ‹€.
    • μˆ˜μ • λ°©ν–₯:
      • μž¬μ‹œλ„ 큐와 μƒνƒœλ₯Ό DB/Redis 등에 μ €μž₯ν•˜κ³ , μž¬μ‹œμž‘ μ‹œ 볡ꡬ κ°€λŠ₯ν•œ ꡬ쑰둜 λ°”κΎΈμ„Έμš”.
      • μ΅œμ†Œν•œ μž¬μ‹œμž‘ μ‹œ 쀑볡 μ˜ˆμ•½μ„ λ§‰λŠ” idempotency key와 TTL 기반 μƒνƒœ 정리λ₯Ό μΆ”κ°€ν•˜μ„Έμš”.
  • [Low] src/main/java/com/son/soccerStreaming/apifootball/client/ApiFootballClient.java:268-287, src/main/java/com/son/soccerStreaming/apifootball/client/ApiFootballResponseValidator.java:1-59 - μ™ΈλΆ€ 응닡 검증 κ°•ν™”λ‘œ μΈν•œ μ„œλΉ„μŠ€ κ±°λΆ€ κ°€λŠ₯μ„±

    • 문제:
      • results와 response.size()λ₯Ό 엄격 λΉ„κ΅ν•˜κ³ , errors 메타데이터 ν˜•μ‹μ΄ κΈ°λŒ€μ™€ λ‹€λ₯΄λ©΄ λͺ¨λ‘ INVALID_RESPONSE둜 μ²˜λ¦¬ν•©λ‹ˆλ‹€.
      • μ™ΈλΆ€ κ³΅κΈ‰μžκ°€ μŠ€ν‚€λ§ˆλ₯Ό μ•½κ°„ λ³€κ²½ν•˜κ±°λ‚˜, μ‘λ‹΅μ˜ 일뢀 ν•„λ“œλ₯Ό λΉ„μ›Œ λ°˜ν™˜ν•˜λ©΄ 정상 응닡도 μž₯μ• λ‘œ λΆ„λ₯˜λ  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 곡격/λ…ΈμΆœ μ‹œλ‚˜λ¦¬μ˜€:
      • API-Football이 μΌμ‹œμ μœΌλ‘œ 포맷을 λ³€κ²½ν•˜κ±°λ‚˜ λΆ€λΆ„ 응닡을 λ°˜ν™˜ν•˜λ©΄ 동기화가 반볡 μ‹€νŒ¨ν•˜κ³  μž¬μ‹œλ„ μŠ€μΌ€μ€„μ΄ μŒ“μ—¬ μž₯μ• κ°€ κΈΈμ–΄μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
    • μˆ˜μ • λ°©ν–₯:
      • μ‹€μ œλ‘œ ν•„μˆ˜μΈ ν•„λ“œλ§Œ μ—„κ²©ν•˜κ²Œ κ²€μ¦ν•˜κ³ , κ³΅κΈ‰μž 변동에 λŒ€λΉ„ν•΄ ν—ˆμš© κ°€λŠ₯ν•œ λ³€ν˜• λ²”μœ„λ₯Ό μ •μ˜ν•˜μ„Έμš”.
      • errors/results 검증 μ‹€νŒ¨ μ‹œ μ¦‰μ‹œ μž₯μ• λ‘œ λΆ„λ₯˜ν•˜κΈ°λ³΄λ‹€, 일뢀 μ—”λ“œν¬μΈνŠΈλ³„λ‘œ μ™„ν™”λœ 검증 κ·œμΉ™μ„ λ‘λŠ” 것을 κ²€ν† ν•˜μ„Έμš”.

μ€‘λŒ€ν•œ λ³΄μ•ˆ 문제 μ—†μŒ
Model: gpt-5.4-mini
Diff was truncated to 90000 characters; review may be incomplete.

@son0307
son0307 merged commit 5ab4667 into main Jul 20, 2026
1 check passed
@son0307
son0307 deleted the codex/api-football-sync-reliability branch July 20, 2026 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant