Skip to content

๐Ÿ› ๏ธ ์™ธ๋ถ€ API ํ˜ธ์ถœ ์•ˆ์ •์„ฑ ๋ฐ ๊ด€๋ฆฌ์ž ๋ชจ๋‹ˆํ„ฐ๋ง ๊ฐœ์„  - #30

Merged
son0307 merged 3 commits into
mainfrom
codex/external-api-resilience
Jul 19, 2026
Merged

๐Ÿ› ๏ธ ์™ธ๋ถ€ API ํ˜ธ์ถœ ์•ˆ์ •์„ฑ ๋ฐ ๊ด€๋ฆฌ์ž ๋ชจ๋‹ˆํ„ฐ๋ง ๊ฐœ์„ #30
son0307 merged 3 commits into
mainfrom
codex/external-api-resilience

Conversation

@son0307

@son0307 son0307 commented Jul 19, 2026

Copy link
Copy Markdown
Owner

๋ณ€๊ฒฝ ์‚ฌํ•ญ

์™ธ๋ถ€ API ๊ณตํ†ต ํ˜ธ์ถœ ์•ˆ์ •์„ฑ ๊ฐœ์„ 

  • API-Football, SerpAPI, OpenAI ํ˜ธ์ถœ์— ๊ณตํ†ต ์‹คํ–‰๊ธฐ๋ฅผ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • provider๋ณ„ connect/read timeout์„ ์„ค์ •ํ•˜๊ณ  HTTP ์ƒํƒœ ๋ฐ ์˜ˆ์™ธ ์œ ํ˜•์— ๋”ฐ๋ผ ์˜ค๋ฅ˜๋ฅผ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค.
  • 408, API-Football 499, rate limit 429, 500/502/503/504, timeout ๋ฐ ์ผ์‹œ์ ์ธ ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋งŒ ์ตœ๋Œ€ 3ํšŒ ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.
  • ์ง€์ˆ˜ ๋ฐฑ์˜คํ”„, jitter, Retry-After๋ฅผ ์ ์šฉํ•˜๊ณ  ์ธ์ฆยท๊ถŒํ•œยท์ž˜๋ชป๋œ ์š”์ฒญยทquota ์†Œ์ง„ยท์‘๋‹ต ๋ณ€ํ™˜ ์˜ค๋ฅ˜๋Š” ์žฌ์‹œ๋„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • API-Football์˜ ๊ธฐ์กด ์„œํ‚ท ๋ธŒ๋ ˆ์ด์ปค๋ฅผ ๊ณตํ†ต ์‹คํ–‰๊ธฐ์™€ ์—ฐ๋™ํ•˜๊ณ , scheduler/startup ์žฌ์‹คํ–‰์€ ์ตœ์ข… ์˜ค๋ฅ˜๊ฐ€ ์žฌ์‹œ๋„ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ์—๋งŒ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค.

provider ์ƒํƒœ ๋ฐ ๊ด€๋ฆฌ์ž ๊ฐ์‚ฌ ๋กœ๊ทธ ํ™•์žฅ

  • ๋ณ„๋„ ์ƒํƒœ ํ…Œ์ด๋ธ”์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ๊ธฐ์กด api_football_sync_status๋ฅผ API-Football, SerpAPI, OpenAI์˜ ์ตœ์‹  ์ƒํƒœ ์ €์žฅ์†Œ๋กœ ํ™•์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.
  • provider๋ณ„ ๋งˆ์ง€๋ง‰ ์‹œ๋„ยท์„ฑ๊ณตยท์‹คํŒจ ์‹œ๊ฐ, ์—ฐ์† ์‹คํŒจ ํšŸ์ˆ˜, operation, HTTP ์ƒํƒœ, ์˜ค๋ฅ˜ category, attempt ์ˆ˜๋ฅผ ์ตœ์ข… ํ˜ธ์ถœ ๊ฒฐ๊ณผ ๊ธฐ์ค€์œผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ์ƒํƒœ ์ €์žฅ์„ ๋ณ„๋„ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ƒ์œ„ ์ž‘์—…์ด rollback๋˜๋”๋ผ๋„ ์ตœ์ข… ์žฅ์•  ๊ธฐ๋ก์ด ๋‚จ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌ์ž๊ฐ€ ์‹œ์ž‘ํ•œ SerpAPI/OpenAI ํ˜ธ์ถœ๋งŒ EXTERNAL_API_CALL ๊ฐ์‚ฌ ๋กœ๊ทธ๋กœ ๊ธฐ๋กํ•˜๊ณ , scheduler ํ˜ธ์ถœ๊ณผ retry ์ค‘๊ฐ„ ์‹คํŒจ๋Š” ๊ธฐ๋กํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • API ํ‚ค, ์š”์ฒญ query, Authorization, prompt, ์›๋ฌธยท๋ฒˆ์—ญ ๊ฒฐ๊ณผ์™€ ์‘๋‹ต ๋ณธ๋ฌธ์€ ๊ฐ์‚ฌ ๋กœ๊ทธ์—์„œ ์ œ์™ธํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ด€๋ฆฌ์ž ๋ฐ ๋‰ด์Šค ํ™”๋ฉด ๊ฐœ์„ 

  • ๊ด€๋ฆฌ์ž ํ™”๋ฉด์— API-Football, SerpAPI, OpenAI์˜ ์ตœ์‹  ํ†ต์‹  ์ƒํƒœ ์นด๋“œ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
  • Recent Admin Logs์— ์™ธ๋ถ€ API ํ˜ธ์ถœ ์œ ํ˜•, provider, ์„ฑ๊ณตยท์‹คํŒจ badge์™€ ์•ˆ์ „ํ•œ ํ˜ธ์ถœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
  • ๋‰ด์Šค ์ˆ˜์ง‘ ์ƒํƒœ๊ฐ€ Team ๋‚ด๋ถ€ DB ID์™€ API-Football teamId๋ฅผ ํ˜ผ์šฉํ•ด ๋งˆ์ง€๋ง‰ ์ˆ˜์ง‘ ์‹œ๊ฐ์„ ์ฐพ์ง€ ๋ชปํ•˜๋˜ ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌ์ž ์ˆ˜๋™ ์ˆ˜์ง‘๊ณผ scheduler ์ˆ˜์ง‘ ๋ชจ๋‘ ๋‰ด์Šค ํƒญ์˜ ๋งˆ์ง€๋ง‰ ์ˆ˜์ง‘ ์‹œ๊ฐ์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

๋ณ€๊ฒฝ ์ด์œ 

์™ธ๋ถ€ API๋ณ„๋กœ timeout๊ณผ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด ๋‹ฌ๋ผ ์ผ์‹œ ์žฅ์• ์™€ ์˜๊ตฌ ์˜ค๋ฅ˜๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์–ด๋ ค์› ๊ณ , retry ์ดํ›„์˜ ์ตœ์ข… ์ƒํƒœ์™€ ๊ด€๋ฆฌ์ž ํ˜ธ์ถœ ์ด๋ ฅ์„ ํ™•์ธํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ณตํ†ต ์‹คํ–‰๊ธฐ์™€ ๊ธฐ์กด ์ƒํƒœ ํ…Œ์ด๋ธ”์„ ์ด์šฉํ•ด ํ˜ธ์ถœ ์•ˆ์ •์„ฑ๊ณผ ์šด์˜ ๊ฐ€์‹œ์„ฑ์„ ํ•จ๊ป˜ ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋‰ด์Šค ์ˆ˜์ง‘ ์ƒํƒœ ํ–‰์€ Team ๋‚ด๋ถ€ ID๋ฅผ ๊ธฐ๋ณธํ‚ค๋กœ ์‚ฌ์šฉํ•˜์ง€๋งŒ API teamId๋กœ ์กฐํšŒํ•˜๊ณ  ์žˆ์–ด, ๋‘ ๊ฐ’์ด ๋‹ค๋ฅธ ํŒ€์—์„œ๋Š” ์ˆ˜์ง‘์ด ์™„๋ฃŒ๋˜์–ด๋„ ์ˆ˜์ง‘ ๊ธฐ๋ก ์—†์Œ์œผ๋กœ ํ‘œ์‹œ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

API ๋ฐ ์„ค์ • ๋ณ€๊ฒฝ

  • api_football_sync_status์— provider ๊ณตํ†ต ์ƒํƒœ ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  • admin_audit_log์— nullable provider ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  EXTERNAL_API_CALL ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  • API-Football, SerpAPI, OpenAI timeout๊ณผ ๊ณตํ†ต retry ์„ค์ •์„ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ธฐ์กด ๊ด€๋ฆฌ์ž ์‘๋‹ต ์ฝ”๋“œ์™€ ๋‰ด์Šค ์กฐํšŒยท์ƒˆ๋กœ๊ณ ์นจ API ํ˜•์‹์€ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๊ฒ€์ฆ

  • ์™ธ๋ถ€ API ์˜ค๋ฅ˜ ๋ถ„๋ฅ˜, ์žฌ์‹œ๋„, quota/rate limit ๊ตฌ๋ถ„, timeout ๋ฐ interrupt ํ…Œ์ŠคํŠธ ํ†ต๊ณผ
  • provider ์ƒํƒœ ์ƒ์„ฑ, ์‹คํŒจ ๋ˆ„์ , ์„ฑ๊ณต ๋ณต๊ตฌ ๋ฐ ๊ธฐ์กด API-Football ํ–‰ ํ˜ธํ™˜ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ
  • ๊ด€๋ฆฌ์ž ํ˜ธ์ถœ๊ณผ scheduler ํ˜ธ์ถœ์˜ ๊ฐ์‚ฌ ๋กœ๊ทธ ๋ถ„๋ฆฌ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ
  • ๋‰ด์Šค ์ˆ˜์ง‘ ์‹œ๊ฐ ์ €์žฅยท์กฐํšŒ ๋ฐ ๊ด€๋ฆฌ์ž ์ƒˆ๋กœ๊ณ ์นจ ๊ด€๋ จ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ
  • ./gradlew.bat compileJava ํ†ต๊ณผ
  • npm run build ํ†ต๊ณผ
  • ์ „์ฒด ๋ฐฑ์—”๋“œ ํ…Œ์ŠคํŠธ 190๊ฐœ ์ค‘ 189๊ฐœ ํ†ต๊ณผ
    • contextLoads() 1๊ฐœ๋Š” ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ Docker/Testcontainers๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.

๋™์ผํ•œ Docker ์ œ์•ฝ์œผ๋กœ ๋กœ๊ทธ์ธ๋œ ๊ด€๋ฆฌ์ž ํ™”๋ฉด์˜ ๋ธŒ๋ผ์šฐ์ € ์ง์ ‘ ๊ฒ€์ฆ์€ ์ˆ˜ํ–‰ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

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

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codex PR Review

PR ์š”์•ฝ

์š”์•ฝ

์™ธ๋ถ€ API(API-Football, SerpAPI, OpenAI) ํ˜ธ์ถœ์— ๊ณตํ†ต ์žฌ์‹œ๋„ยทํƒ€์ž„์•„์›ƒยท์˜ค๋ฅ˜ ๋ถ„๋ฅ˜๋ฅผ ์ ์šฉํ•ด ์•ˆ์ •์„ฑ์„ ๋†’์ด๊ณ , ๊ด€๋ฆฌ์ž ํ™”๋ฉด๊ณผ ๊ฐ์‚ฌ ๋กœ๊ทธ์—์„œ ์ตœ์‹  ํ†ต์‹  ์ƒํƒœ๋ฅผ ๋” ์ž˜ ๋ณด์ด๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋‰ด์Šค ์ˆ˜์ง‘ ์ƒํƒœ ์กฐํšŒ ๊ธฐ์ค€์„ ์ •๋ฆฌํ•ด ๋งˆ์ง€๋ง‰ ์ˆ˜์ง‘ ์‹œ๊ฐ์ด ๋ˆ„๋ฝ๋˜๋˜ ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ฃผ์š” ๋ณ€๊ฒฝ์‚ฌํ•ญ

  • API-Football, SerpAPI, OpenAI์— ๊ณตํ†ต ์‹คํ–‰๊ธฐ์™€ provider๋ณ„ timeout/retry ์ •์ฑ…์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์žฌ์‹œ๋„ ๋Œ€์ƒ ์˜ค๋ฅ˜๋งŒ ์ตœ๋Œ€ 3ํšŒ ์žฌ์‹œ๋„ํ•˜๊ณ , Retry-After, ์ง€์ˆ˜ ๋ฐฑ์˜คํ”„, jitter๋ฅผ ๋ฐ˜์˜ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์™ธ๋ถ€ API ํ˜ธ์ถœ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์กด ์ƒํƒœ ํ…Œ์ด๋ธ”์— provider๋ณ„ ์ตœ์‹  ์ƒํƒœ๋กœ ์ €์žฅํ•˜๋„๋ก ํ™•์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌ์ž ์‹œ์ž‘ ์™ธ๋ถ€ API ํ˜ธ์ถœ๋งŒ EXTERNAL_API_CALL ๊ฐ์‚ฌ ๋กœ๊ทธ๋กœ ๋‚จ๊ธฐ๊ณ , ๋ฏผ๊ฐํ•œ ์š”์ฒญ/์‘๋‹ต ๋‚ด์šฉ์€ ์ œ์™ธํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌ์ž ํ™”๋ฉด์— SerpAPI/OpenAI ์ƒํƒœ ์นด๋“œ์™€ ์™ธ๋ถ€ API ํ˜ธ์ถœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋‰ด์Šค ์ˆ˜์ง‘ ์ƒํƒœ ์กฐํšŒ๋ฅผ team ๋‚ด๋ถ€ ID/์™ธ๋ถ€ teamId ํ˜ผ์šฉ ๋ฌธ์ œ ์—†์ด ๋™์ž‘ํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌ์ž ์ˆ˜๋™ ์ˆ˜์ง‘/๋ฒˆ์—ญ ์š”์ฒญ ์‹œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์™ธ๋ถ€ API ํ˜ธ์ถœ ์ปจํ…์ŠคํŠธ์— ๋ฐ˜์˜ํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

์˜ํ–ฅ ๋ฒ”์œ„

  • ์™ธ๋ถ€ API ํ˜ธ์ถœ ์•ˆ์ •์„ฑ
  • ๊ด€๋ฆฌ์ž ๋ชจ๋‹ˆํ„ฐ๋ง/๊ฐ์‚ฌ ๋กœ๊ทธ
  • ๋‰ด์Šค ์ˆ˜์ง‘ ๋ฐ ๋ฒˆ์—ญ
  • API-Football ์Šค์ผ€์ค„๋Ÿฌ/์Šคํƒ€ํŠธ์—… ์žฌ์‹œ๋„
  • ํ”„๋ก ํŠธ์—”๋“œ ๊ด€๋ฆฌ์ž UI
  • ์„ค์ • ๋ฐ DB ์Šคํ‚ค๋งˆ

๊ฒ€์ฆ

  • ./gradlew.bat compileJava ํ†ต๊ณผ
  • npm run build ํ†ต๊ณผ
  • ๋ฐฑ์—”๋“œ ํ…Œ์ŠคํŠธ 190๊ฐœ ์ค‘ 189๊ฐœ ํ†ต๊ณผ
  • ์ž๋™ ์ƒ์„ฑ ์š”์•ฝ ๊ธฐ์ค€์œผ๋กœ ํ™•์ธ๋œ ํ…Œ์ŠคํŠธ ์ •๋ณด ์—†์Œ

์‹œ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž ๋ฆฌ๋ทฐ

  • [P1] src/main/java/com/son/soccerStreaming/global/externalapi/ExternalApiExecutor.java:91-132 - 429/4xx ์˜ค๋ฅ˜ ๋ถ„๋ฅ˜๊ฐ€ ์ž˜๋ชป๋˜์–ด ์žฌ์‹œ๋„ ์ •์ฑ…๊ณผ ์ƒํƒœ ๊ธฐ๋ก์ด ํ‹€์–ด์งˆ ์ˆ˜ ์žˆ์Œ

    • ๋ฌธ์ œ: classify()์—์„œ 400์€ BAD_REQUEST๋กœ ๋น„์žฌ์‹œ๋„ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ, 401/403/404 ์™ธ์˜ ๋‹ค๋ฅธ 4xx๋Š” ์ „๋ถ€ BAD_REQUEST๋กœ ๋–จ์–ด์ง‘๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ PR ์„ค๋ช…์ƒ โ€œ์ธ์ฆยท๊ถŒํ•œยท์ž˜๋ชป๋œ ์š”์ฒญยทquota ์†Œ์ง„ยท์‘๋‹ต ๋ณ€ํ™˜ ์˜ค๋ฅ˜๋Š” ์žฌ์‹œ๋„ํ•˜์ง€ ์•Š๋Š”๋‹คโ€๋Š” ์˜๋„์ธ๋ฐ, ์‹ค์ œ๋กœ๋Š” ์˜ˆ๋ฅผ ๋“ค์–ด 422 ๊ฐ™์€ ์™ธ๋ถ€ API์˜ ์˜๋ฏธ ์žˆ๋Š” ํด๋ผ์ด์–ธํŠธ ์˜ค๋ฅ˜๋„ ๋ชจ๋‘ ๋™์ผ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ๋” ํฐ ๋ฌธ์ œ๋Š” 429์˜ quota ํŒ๋ณ„์ด OpenAI/SerpAPI ๋ณธ๋ฌธ ๋ฌธ์ž์—ด์— ๊ฐ•ํ•˜๊ฒŒ ์˜์กดํ•ด์„œ, ์‘๋‹ต ํ˜•ํƒœ๊ฐ€ ์กฐ๊ธˆ๋งŒ ๋ฐ”๋€Œ์–ด๋„ ์ž˜๋ชป RATE_LIMITED๋กœ ๋ถ„๋ฅ˜๋˜์–ด ์žฌ์‹œ๋„๋ฉ๋‹ˆ๋‹ค.
    • ์˜ํ–ฅ: ์™ธ๋ถ€ API๊ฐ€ ๋น„์ •ํ˜•/๋ณ€ํ˜•๋œ ์˜ค๋ฅ˜ ๋ฐ”๋””๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ์žฌ์‹œ๋„๋กœ ํ˜ธ์ถœ๋Ÿ‰๊ณผ ์ง€์—ฐ์ด ๋Š˜๊ณ , ๊ด€๋ฆฌ์ž ์ƒํƒœ์—๋„ ์ž˜๋ชป๋œ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ๋‰ด์Šค ์ˆ˜์ง‘/๋ฒˆ์—ญ ๊ฒฝ๋กœ์—์„œ rate limit๊ณผ quota exhausted๋ฅผ ์ž˜๋ชป ๊ตฌ๋ถ„ํ•˜๋ฉด ๋ฐ˜๋ณต ์‹คํŒจ๊ฐ€ ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ˆ˜์ • ๋ฐฉํ–ฅ: 4xx๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„์žฌ์‹œ๋„๋กœ ๋‘๋˜, 429๋Š” provider๋ณ„๋กœ Retry-After/๋ณธ๋ฌธ ํŒŒ์‹ฑ์— ์‹คํŒจํ•ด๋„ ์•ˆ์ „ํ•˜๊ฒŒ RATE_LIMITED ๋˜๋Š” QUOTA_EXHAUSTED๋กœ ๋ณด์ˆ˜์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์„ธ์š”. BAD_REQUEST๋กœ ๋ฌถ๋Š” ๋ฒ”์œ„๋ฅผ ๋ช…ํ™•ํžˆ ํ•˜๊ณ , provider ์‘๋‹ต ํ˜•ํƒœ๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ์žฌ์‹œ๋„ ์—ฌ๋ถ€๊ฐ€ ํ”๋“ค๋ฆฌ์ง€ ์•Š๊ฒŒ ๋ถ„๊ธฐ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  • [P1] src/main/java/com/son/soccerStreaming/global/externalapi/ExternalApiExecutor.java:202-214 - Retry-After๋ฅผ ๊ทธ๋Œ€๋กœ sleepํ•ด์„œ ์žฅ์‹œ๊ฐ„ ์Šค๋ ˆ๋“œ ์ ์œ  ๊ฐ€๋Šฅ

    • ๋ฌธ์ œ: retryDelay()๊ฐ€ Retry-After ํ—ค๋”๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ , sleep()์ด ์ด๋ฅผ ๊ทธ๋Œ€๋กœ Thread.sleep() ํ•ฉ๋‹ˆ๋‹ค. ์ƒ๋‹จ์—๋Š” max-retry-after ์„ค์ •์ด ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” retryAfterExceedsLimit()์ผ ๋•Œ๋งŒ โ€œ์ตœ์ข… ์‹คํŒจโ€๋กœ ๋๋‚ด๊ณ , ๊ทธ ์ดํ•˜๋ผ๋„ ๋งค์šฐ ๊ธด ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋˜ํ•œ retryAfter๊ฐ€ ์ดˆ ๋‹จ์œ„๋“  ๋‚ ์งœ๋“  ๊ฒฐ๊ตญ ๊ธธ๊ฒŒ ์žกํžˆ๋ฉด ์š”์ฒญ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ธ”๋กœํ‚น๋ฉ๋‹ˆ๋‹ค.
    • ์˜ํ–ฅ: ์™ธ๋ถ€ API๊ฐ€ Retry-After: 300 ๊ฐ™์€ ๊ฐ’์„ ์ฃผ๋ฉด ์š”์ฒญ ์ฒ˜๋ฆฌ ์Šค๋ ˆ๋“œ๊ฐ€ 5๋ถ„์”ฉ ๋ฌถ์ผ ์ˆ˜ ์žˆ์–ด, ๊ด€๋ฆฌ์ž ์ˆ˜๋™ ํ˜ธ์ถœ์ด๋‚˜ ๋ฐฐ์น˜ ์‹คํ–‰ ์ค‘ ์Šค๋ ˆ๋“œ ๊ณ ๊ฐˆ/์‘๋‹ต ์ง€์—ฐ์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ˆ˜์ • ๋ฐฉํ–ฅ: Retry-After๋Š” maxRetryAfter๋กœ ์ƒํ•œ์„ ๋‘๊ฑฐ๋‚˜, ์ƒํ•œ ์ดˆ๊ณผ ์‹œ ์ฆ‰์‹œ ์žฌ์‹œ๋„ ์ค‘๋‹จ ๋Œ€์‹  โ€œ์ง€์—ฐ ์žฌ์‹œ๋„ ์Šค์ผ€์ค„๋งโ€์œผ๋กœ ๋„˜๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ ํ˜„์žฌ ๋™๊ธฐ ์‹คํ–‰ ํ๋ฆ„์—์„œ๋Š” min(retryAfter, maxRetryAfter)๋กœ ์ž˜๋ผ์„œ sleep ํ•˜์„ธ์š”.
  • [P1] src/main/java/com/son/soccerStreaming/global/externalapi/ExternalApiExecutor.java:43-81 - ์„ฑ๊ณต ํ›„ ๊ฐ์‚ฌ ์ด๋ฒคํŠธ/์ƒํƒœ ๊ธฐ๋ก์ด ์š”์ฒญ ์Šค๋ ˆ๋“œ ์˜ˆ์™ธ๋กœ ์œ ์‹ค๋  ์ˆ˜ ์žˆ์Œ

    • ๋ฌธ์ œ: recordSuccess()์™€ publish()๊ฐ€ ์š”์ฒญ ์„ฑ๊ณต ์งํ›„ ๋™๊ธฐ์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š”๋ฐ, recordSuccess() ์‹คํŒจ ์‹œ ์˜ˆ์™ธ๋ฅผ ์‚ผํ‚ค๊ณ , publish()๋„ ์‹คํŒจ๋ฅผ ์‚ผํ‚ต๋‹ˆ๋‹ค. ์ด ์ž์ฒด๋Š” ํ˜ธ์ถœ ์„ฑ๊ณต์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ์„ฑ๊ณต ์ด๋ฒคํŠธ/์ƒํƒœ๊ฐ€ ์ €์žฅ๋˜์ง€ ์•Š์•„ ๊ด€๋ฆฌ์ž ํ™”๋ฉด๊ณผ ๊ฐ์‚ฌ ๋กœ๊ทธ๊ฐ€ ๋ˆ„๋ฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ์‹คํŒจ ๊ธฐ๋ก๋„ ๋™์ผํ•œ ๋ฐฉ์‹์ด๋ผ ์ƒํƒœ ์ €์žฅ ์‹คํŒจ ์‹œ ๊ด€์ธก์„ฑ์ด ํฌ๊ฒŒ ๋–จ์–ด์ง‘๋‹ˆ๋‹ค.
    • ์˜ํ–ฅ: ์™ธ๋ถ€ API๋Š” ์„ฑ๊ณตํ–ˆ๋Š”๋ฐ ๊ด€๋ฆฌ์ž ์ƒํƒœ๊ฐ€ ๊ณ„์† RUNNING/์ด์ „ ์‹คํŒจ๋กœ ๋‚จ๋Š” ์‹์˜ ์šด์˜ ํ˜ผ๋ž€์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ˆ˜์ • ๋ฐฉํ–ฅ: ์ƒํƒœ ์ €์žฅ ์‹คํŒจ๋ฅผ ์ตœ์†Œํ•œ ๋ณ„๋„ ๊ฒฝ๊ณ  ์ง€ํ‘œ๋กœ ๋‚จ๊ธฐ๊ณ , ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ์ด๋ฒคํŠธ ๋ฐœํ–‰/์ƒํƒœ ์ €์žฅ์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์ฒ˜๋Ÿผ ๋ถ„๋ฆฌํ•˜๋˜ ์‹คํŒจ ์‹œ์—๋„ ์–ด๋–ค provider/operation์—์„œ ์‹คํŒจํ–ˆ๋Š”์ง€ ๋‚จ๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•œ๋‹ค๋ฉด recordSuccess/recordFailure/publish์˜ ์‹คํŒจ๊ฐ€ ์–ด๋””์„œ ์–ผ๋งˆ๋‚˜ ๋ฐœ์ƒํ•˜๋Š”์ง€ ๊ณ„์ธก์„ ๊ฐ•ํ™”ํ•˜์„ธ์š”.
  • [P2] frontend/src/pages/AdminPage.tsx:1172,1450-1480 - ์™ธ๋ถ€ API ์ƒํƒœ ์นด๋“œ๋ฅผ ๋ชจ๋ฐ”์ผ์—์„œ ํ•œ ์„น์…˜์— ๊ณผ๋„ํ•˜๊ฒŒ ๋ชฐ์•„๋„ฃ์–ด ์Šค์บ”์„ฑ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ์Œ

    • ๋ฌธ์ œ: External APIs ์„น์…˜์ด API-Football + SerpAPI + OpenAI ์ƒํƒœ๋ฅผ ๋ชจ๋‘ ํ•œ ํŒจ๋„ ์•ˆ์— ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€๊ฐ€ ์ •๋ณด ๋ฐ€๋„๊ฐ€ ๋†’์€๋ฐ, ์นด๋“œ ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด ๋ชจ๋ฐ”์ผ์—์„œ ๊ธด ์ƒํƒœ/์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์™€ ๋ฒ„ํŠผ์ด ํ•œ ํ™”๋ฉด์— ์„ž์—ฌ ์ฝ๊ธฐ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.
    • ์˜ํ–ฅ: ์‚ฌ์šฉ์ž๊ฐ€ โ€œ์–ด๋–ค provider๊ฐ€ ๋ฌธ์ œ์ธ์ง€โ€ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง€๊ณ , ํŠนํžˆ ๊ธด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ์žˆ์œผ๋ฉด ์ˆ˜๋™ ์ƒˆ๋กœ๊ณ ์นจ ๋ฒ„ํŠผ์ด ์•„๋ž˜๋กœ ๋ฐ€๋ ค UX๊ฐ€ ๋‚˜๋น ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ˆ˜์ • ๋ฐฉํ–ฅ: ์ตœ์†Œํ•œ provider๋ณ„ ์นด๋“œ๋ฅผ ์ ‘์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ฑฐ๋‚˜, ์ƒํƒœ ์š”์•ฝ๊ณผ ์ƒ์„ธ๋ฅผ ๋ถ„๋ฆฌํ•˜์„ธ์š”. ์ง€๊ธˆ ๋ฒ”์œ„ ๋‚ด์—์„œ๋Š” ์นด๋“œ ๊ฐ„ ๊ฐ„๊ฒฉ/์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๊ธธ์ด ์ œํ•œ์„ ๋‘๋Š” ์ •๋„๊ฐ€ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.
  • [P2] src/main/java/com/son/soccerStreaming/news/controller/AdminTeamNewsController.java:19-30 - ๊ด€๋ฆฌ์ž ์ธ์ฆ ์ฃผ์ž…์ด ์—†์œผ๋ฉด NPE ๊ฐ€๋Šฅ์„ฑ

    • ๋ฌธ์ œ: @AuthenticationPrincipal AuthUserDetails userDetails๋ฅผ ์ƒˆ๋กœ ๋ฐ›๋„๋ก ๋ฐ”๋€Œ์—ˆ์ง€๋งŒ, ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ userDetails.getId()๋ฅผ ๋ฐ”๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ์—”๋“œํฌ์ธํŠธ๋Š” ๊ด€๋ฆฌ์ž ์ „์šฉ์œผ๋กœ ๋ณด์ด์ง€๋งŒ, ์ธ์ฆ ํ•„ํ„ฐ ๋ˆ„๋ฝ/๋งŒ๋ฃŒ/๋น„์ •์ƒ ์ ‘๊ทผ ์‹œ userDetails๊ฐ€ null์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์˜ํ–ฅ: ์ธ์ฆ ์ปจํ…์ŠคํŠธ๊ฐ€ ์—†๋Š” ์š”์ฒญ์—์„œ 500์ด ๋ฐœ์ƒํ•ด, ์›๋ž˜๋ผ๋ฉด 401/403์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์–ด์•ผ ํ•  ์ผ€์ด์Šค๊ฐ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜๋กœ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ˆ˜์ • ๋ฐฉํ–ฅ: userDetails == null์ด๋ฉด ๋ช…์‹œ์ ์œผ๋กœ ์ธ์ฆ ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ฐฉ์–ด ์ฝ”๋“œ๋ฅผ ๋„ฃ๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ ์„œ๋น„์Šค ํ˜ธ์ถœ ์ „์— null ์ฒดํฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ ๋ฆฌ๋ทฐ

์ค‘๋Œ€ํ•œ ๋ณด์•ˆ ๋ฌธ์ œ ์—†์Œ
Model: gpt-5.4-mini
Diff was truncated to 90000 characters; review may be incomplete.

@son0307
son0307 merged commit 536d03c into main Jul 19, 2026
1 check passed
@son0307
son0307 deleted the codex/external-api-resilience branch July 19, 2026 09:05
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