109 Commits

Author SHA1 Message Date
d3c6fddb58 Tune the Gemini chat provider and cache-optimize the system prompt.
기본 모델을 gemini-3.1-flash-lite로, 전 모델에 공평한 24초 호출 예산과
2048 토큰 출력 한도로 맞추고, Gemini 3 모델은 thinkingLevel을 쓴다.

개인화 값(닉네임·지식수준)을 공통 프롬프트에서 빼서 모든 유저 공통의
정적 prefix로 만들어 Vertex 교차-유저 implicit caching 히트율을 높인다.
2026-06-16 16:52:05 +09:00
1ab744e660 Add AI chat tools for team standings, attendance, and predictions.
순위(get_team_rank_snapshot)·출석(get_my_attendance_this_month)·예측 복기
(get_prediction_breakdown_date) 도구를 추가하고, 상대 날짜(dayOffset)와
리그 전체 로스터 조회를 지원한다.

모델이 호출한 도구(이름·인자)를 전송 응답과 메시지 이력에 함께 실어
클라이언트가 나중에 활용할 수 있게 한다.
2026-06-16 16:51:53 +09:00
360c0d356e Add AI chat tools to fetch KBO game standouts and refine model providers.
- 특정 경기에서 활약한 선수(WPA 상위)를 조회하는 `get_game_standouts` 도구를 추가하고, 시스템 프롬프트에 과거 기록 검증 로직을 강화했습니다.
- Gemini 3.0 이상 모델의 Thinking 설정을 지원하도록 Provider 계층을 개선하여 모델 세대별 최적의 추론 환경을 구성했습니다.
- 도구 우선 사용 원칙을 구체화하여 AI가 경기 데이터의 사실 관계를 스스로 확인하고 답변하도록 환각 방지 로직을 보완했습니다.
2026-06-16 08:36:16 +09:00
b126c05c82 Add AI chat tools to fetch KBO game lineups and player roster records.
- KBO 경기 라인업(타순) 및 선수 기록을 실시간으로 조회하는 AI 도구(Function Calling) 체계를 구현했습니다.
- Anthropic 및 Google Gemini 모델의 Tool-loop를 지원하도록 Provider 계층을 개선하여, 시점 의존적인 데이터를 모델이 필요할 때만 스스로 조회하도록 최적화했습니다.
- 시스템 프롬프트에 '도구 우선 사용' 원칙을 추가하여 환각(Hallucination) 현상을 방지하고, 미발표 정보나 경기 미진행 시 정확한 사실 확인 로직을 강화했습니다.
2026-06-15 15:44:38 +09:00
b67125f412 Add AI chat persona and infrastructure for the "Jjaek" chatbot.
- AI 챗봇 '짹'의 페르소나 정의 및 시스템 프롬프트 상수를 추가했습니다.
- 채팅 이력, 쿼터 관리, 요청 멱등성 보장을 위한 Firestore 데이터 모델을 구현했습니다.
- 위기 상황 대응(자해·위해 예고 등) 및 입력/출력 필터링 파이프라인을 구축했습니다.
- Gemini 및 Anthropic 모델을 지원하는 AI Provider 추상화 계층을 마련했습니다.
2026-06-15 13:07:58 +09:00
20e3b1cbf0 Add a script to fetch and display KBO team rankings and records.
- KBO 공식 홈페이지에서 연도별 팀 순위 및 상대 전적 데이터를 가져오는 크롤링 스크립트를 구현했습니다.
- ASP.NET의 ViewState 및 EventValidation 필드를 처리하여 동적인 연도 전환 요청을 지원합니다.
- 데이터를 가독성 높은 콘솔 테이블 형태로 출력하거나 JSON 형식으로 변환하는 기능을 제공합니다.
2026-05-29 14:59:50 +09:00
05b774a713 Expand backend-write improvement report for non-expert readability
- improvements-backend-writes.md 각 항목에 배경 단락 및 평이한 설명 추가
- 비전문가도 이해할 수 있도록 비유·용어 괄호 설명 포함
- 문서 상단에 Firestore/RTDB/cron 등 핵심 용어 풀이 추가
2026-05-28 19:51:05 +09:00
7fbc4f9501 Expand backend-read improvement report for non-expert readability
- improvements-backend-reads.md 각 항목에 배경 단락 및 평이한 설명 추가
- 비전문가도 이해할 수 있도록 비유·용어 괄호 설명 포함
- 보류 항목 R2·R3의 사유를 쉬운 말로 풀어 설명
2026-05-28 19:50:32 +09:00
dafaacc446 Add narrative summaries to backend-read improvement report
- improvements-backend-reads.md 각 항목 제목 아래에 서사 요약 blockquote 추가
- R1·R4·R5·R7 항목의 Before→Fix 흐름을 한 문장으로 정리
2026-05-28 19:33:30 +09:00
11d9fa9b5f Add narrative summaries to backend-write improvement report
- improvements-backend-writes.md 각 항목 제목 아래에 서사 요약 blockquote 추가
- W3·W4·W5+R6·W6·Doc 항목의 Before→Fix 흐름을 한 문장으로 정리
2026-05-28 19:33:29 +09:00
d23b0814d9 docs: link before/fix/after improvement reports in index
- README.md에 Before→Fix→After 개선 보고서 3개 링크 추가
- backend-writes, backend-reads, client 개선 보고서를 인덱스에서 참조
2026-05-28 19:14:16 +09:00
42826eafcc Document backend WRITE optimizations (W1-W6, doc-drift)
- 백엔드 WRITE 최적화 적용 결과를 improvements-backend-writes.md에 문서화
- W1~W6 및 문서 드리프트(Doc) 적용 내역 기록
2026-05-28 19:12:52 +09:00
e6de4d1568 Document backend READ optimizations (R1/R4/R5/R7) and deferrals
- 백엔드 READ 최적화 적용 결과를 improvements-backend-reads.md에 문서화
- R1·R4·R5·R7 적용 내역 및 R2·R3 보류 사유 기록
2026-05-28 19:12:44 +09:00
14fd893ad4 Correct CACHING.md gameListCache drift (doc)
- CACHING.md의 gameListCache 관련 잘못된 내용 정정
- 사이즈 cap(100개) 및 RTDB 메트릭 카운터 항목을 실제 코드 동작에 맞게 수정
2026-05-28 17:35:15 +09:00
571c894278 Parallelize multi-year rank fetch (R7)
fetchRankFromKbo awaited each year's getOrFetch sequentially even though
years use independent cache keys. Map to Promise.all so multi-year lookups
run concurrently; order is preserved.
2026-05-28 17:34:44 +09:00
f0b01a2dc0 Field-mask the scoreboard self-user read (R5)
getScoreboard loaded the full user doc just to use 5 fields. Add
getUserForScoreboard using getAll with a fieldMask so only displayName,
photoUrl, tierPoints, favoriteTeamCode, and rankSnapshot are fetched,
cutting per-request bandwidth. The short-lived me-rank cache (meRankCache,
30s) already covers the rank-count side of R5.
2026-05-28 17:34:16 +09:00
fa7767d172 Stop bulk-invalidating game_detail cache in kboRefresh (W6)
The 02:00 cron prefix-deleted all game_detail__ docs, but those already
carry a response-derived dynamic TTL (7d for finished games, 30s live,
etc.). Bulk-wiping them forces a cold-miss stampede of external KBO
detail re-fetches right after the cron. Drop the deletion and let each
key expire on its own TTL. rank__ and schedule_day__ still get the
safety-net invalidation.
2026-05-28 17:32:40 +09:00
92ce4f9153 Trim redundant user reads/writes in userService (W5, R6)
W5: getMe synced photoUrl on every read whenever the token URL string
differed, but providers (Google) rotate query params (=sN-c sizing) for
the same image, causing write churn on a read path. Compare only the URL
before '?' so the write fires only on a genuine photo change.

R6: createMe/updateMe re-read the user with getUser right after writing.
Synthesize the response instead — updateMe from the already-read doc plus
the applied patch; createMe from the written inputs (createdAt approximated
as now, since the stored value uses serverTimestamp and later reads reflect
it). Removes one Firestore read per onboarding/profile edit.
2026-05-28 17:32:12 +09:00
38a2f78647 Fold rank snapshot into the daily judgment transaction (W4)
dailyArchive wrote users/{uid} twice per cron run: once for rankSnapshot
and once for the judgment. Extract the read-only computeRankSnapshot, then
pass the pre-judgment snapshot into judgeDay so applyDailyJudgmentTx merges
it into the same patch as the judgment write. The snapshot is computed from
current (pre-judgment) tierPoints before the transaction, preserving the
"rank BEFORE judgment" semantics; on the idempotent guard-skip path no
write occurs (and stale re-snapshotting is avoided). snapshotRankForUser is
kept as a thin wrapper for its existing callers/tests.
2026-05-28 17:29:14 +09:00
6fc00b2952 Use exponential backoff for cache-stampede polling (R4)
waitForCache, the schedule month-poll loop, and getOrFetchDynamic all
polled Firestore every fixed 500ms (up to ~50 reads) while waiting for a
lock holder to populate the cache. Replace the fixed interval with a
shared backoffDelayMs helper (300ms base, doubling, 5s cap) bounded by the
same ~25s timeout, cutting per-waiter read amplification by roughly 5x.
Timeout/fallback semantics are unchanged.
2026-05-28 17:25:45 +09:00
ee3badb112 Skip per-game stats invalidation fan-out in archive reconcile (W3)
During dailyArchive, reconcileDayVotes may call processGameEndWithGame
per unjudged game, each invalidating every voter's stats cache. Since
dailyArchive already invalidates each archived user once at the end of
its per-user loop, the per-game fan-out is redundant. Add an opt-in
skipInvalidate flag and use it from reconcile. The live onGameCompleted
trigger path keeps invalidating as before. Stale data is impossible:
getStats also self-invalidates on the daily forDate rollover.
2026-05-28 17:24:29 +09:00
c3fe153a98 Collapse voteHistory double-write in dailyArchive to one (W2)
dailyArchive wrote the voteHistory doc twice per active user per day:
once data-only before judgeDay, then again with judgment fields inside
judgeDay. Drop the pre-write so judgeDay is the single writer in the
common path. To keep data safety: judgeDay now persists the doc before
userVotes is removed; on the idempotent guard-skip path judgeDay restores
data only if the doc is missing (crash-after-tx recovery); and if judgeDay
throws, dailyArchive still saves data-only before removal, preserving the
prior failure-path behavior. judgment/points/streak results unchanged.
2026-05-28 17:23:33 +09:00
dc87cda0c3 Share per-date game cache across dailyArchive users (R1)
judgeDay and hasMissedGameDayBetween re-ran the same listByDate query for
every user in an archive run (U× amplification for the archived date plus
its look-back window). Introduce a memoizing GameDayCache and thread one
instance through the whole runDailyArchive pass so each distinct date is
read from Firestore once. Off-day detection is naturally memoized via the
cached query results. Standalone callers (statsService) keep their prior
behavior via a per-call default cache. No change to judgment results.
2026-05-28 17:20:50 +09:00
12f21803bc Add getAll+diff to syncGamesForMonth to skip no-op writes
- KBO에서 가져온 경기 일정을 기존 DB 데이터와 비교해 변경된 항목만 Firestore에 저장
- 변경 없는 경기의 반복 write를 제거해 월간 write 비용 대폭 감소
2026-05-28 17:19:17 +09:00
e76b752a41 docs: add Firebase read/write pattern audit & optimization plan
- Firestore/RTDB read/write 패턴 전수 조사 결과를 문서화
- backend-writes.md, backend-reads.md, data-lifecycle.md, optimization-plan.md 추가
2026-05-28 17:10:19 +09:00
86b0da7c2d Remove attendance reminder system and optimize attendance logic.
- 출석 독려 푸시 알림 기능과 관련 스케줄러(attendanceReminder)를 삭제했습니다.(클라이언트로 이관)
- 유저 데이터에서 알림 슬롯 필드를 제거하고, 출석 시 수행하던 슬롯 계산 및 저장 로직을 제거했습니다.
- 주간 보너스 확인 로직을 리팩토링하고, getMonth 함수 내 비동기 데이터 조회를 병렬화하여 성능을 개선했습니다.
2026-05-18 14:19:39 +09:00
eb17177f77 Add starting lineup support and enhance pre-game data handling.
- KBO 경기 상세 정보에 선발 및 예상 라인업 정보를 포함하고 CLI에서 이를 확인할 수 있는 출력 기능을 추가했습니다.
- 경기가 시작되기 전 KBO API가 빈 데이터를 반환하는 상황(code 200)을 예외 처리하고, 경기 상태에 따라 null을 반환하도록 개선했습니다.
- 종료된 경기는 박스스코어에서 선발 명단을 추출하고, 시작 전 경기는 라인업 분석 API를 호출하는 이원화된 데이터 수집 로직을 구현했습니다.
- 예상 라인업이 확정으로 전환되는 시점을 빠르게 반영하기 위해 라인업 출처에 따라 캐시 TTL을 동적으로 조절하는 기능을 도입했습니다.
- 라인업 파싱 및 경기 시작 전후의 데이터 처리 로직에 대한 검증을 위해 서비스 레이어 테스트를 업데이트했습니다.
2026-05-18 13:07:02 +09:00
1101aedeb7 Implement attendance system with point rewards and push reminders.
- 출석 체크 시 데일리 포인트와 주간/월간 보너스를 지급하며, 멱등성 및 클럭 오차 처리를 포함한 로직을 구현했습니다.
- 포인트 이력 관리를 위한 `pointLedger` 컬렉션과 잔액 추적 리포지토리를 추가했습니다.
- 사용자별 알림 슬롯에 맞춰 FCM 출석 독려 푸시를 발송하는 스케줄러와 설정 API를 도입했습니다.
- RTDB의 희소 배열 응답을 7일 기준 배열로 정규화하여 통계 데이터의 일관성을 강화했습니다.
- 출석 시스템 전반에 대한 단위 및 통합 테스트를 추가하여 안정성을 확보했습니다.
2026-05-13 10:51:24 +09:00
e32def7e64 Capture cancellation reasons and generalize the game sync function.
- KBO 경기 취소 시 '우천취소' 등 구체적인 사유(cancelReason)를 파싱하여 저장하는 로직을 추가했습니다.
- 기존의 `syncYesterdayFromLive` 함수를 `forceSyncDay`로 이름을 변경하고, 어제 날짜뿐만 아니라 특정 날짜를 강제로 동기화할 수 있도록 기능을 일반화했습니다.
- Firestore 업데이트 전 기존 데이터와 취소 사유를 비교하는 로직을 추가하여 불필요한 쓰기 비용을 최적화했습니다.
- 다양한 취소 상황과 노트 필드 처리 방식에 대한 회귀 테스트 케이스를 추가하여 신뢰성을 확보했습니다.
2026-05-07 17:00:43 +09:00
590b3f7612 Add regression tests for KBO schedule parsing and tie game handling.
- KBO 일정 파싱 로직의 신뢰성을 확보하고 무승부 경기 처리와 관련된 회귀 오류를 방지하기 위해 통합 테스트를 추가했습니다.
- 무승부 상황에서 점수가 정상적으로 추출되어 경기 상태가 'completed'로 올바르게 표시되는지 검증하는 케이스를 포함했습니다.
- 우천 취소, 예정된 경기, 일반적인 승패 경기 등 다양한 시나리오에서 점수와 상태값이 설계된 대로 파싱되는지 확인합니다.
2026-05-07 16:53:15 +09:00
1a5f6625c0 Handle tie games in KBO schedule parsing and result processing.
- KBO 경기 결과 파싱 시 무승부(`draw`) 상황의 점수도 추출할 수 있도록 정규표현식을 개선하여 경기 상태가 'scheduled'로 오분류되는 문제를 해결했습니다.
- 승리 팀 코드(`winningTeamCode`)가 없는 무승부 경기에서도 결과 처리가 정상적으로 수행되도록 서비스 레이어의 예외 처리를 수정했습니다.
- 무승부 시 어느 팀에 투표했더라도 모두 정답(`result: true`)으로 처리되도록 투표 결과 정산 및 자가 치유(reconcile) 로직을 업데이트했습니다.
2026-05-07 16:47:48 +09:00
c94ce69e4f Remove redundant .js extensions from import paths.
- TypeScript 소스 파일 내 모든 import 구문에서 불필요한 `.js` 확장자를 제거하여 모듈 참조 방식을 표준화했습니다.
- 핸들러, 서비스, 리포지토리 및 테스트 코드를 포함한 프로젝트 전반의 import 경로를 일관성 있게 정리했습니다.
2026-05-06 16:17:51 +09:00
7496cb7de7 Standardize KBO team codes and improve API parameter validation.
- KBO API 요청 매개변수에 `TeamCode` enum을 적용하여 타입 안정성을 강화하고 서비스 레이어의 불필요한 매핑 로직을 제거했습니다.
- `TEAM_CODES` 상수를 내부 데이터 정규화용 어댑터로 한정하여 외부 API 의존성을 줄였습니다.
- 핸들러 단계에서 유효한 팀 코드인지 검증하는 로직을 추가하여 잘못된 요청에 대한 에러 처리를 강화했습니다.
- `getPlayerStats` 함수의 인자 전달 방식을 객체 형태로 리팩토링하여 가독성과 유지보수성을 높였습니다.
2026-05-06 16:15:39 +09:00
c94e2eb81b Implement UserProfile for API security and refactor KBO rank types.
- 클라이언트 응답 전용 `UserProfile` 인터페이스를 도입하여 내부 도메인 모델과의 의존성을 분리하고 데이터 보안을 강화했습니다.
- 스트릭, 티어, 티켓 등 지연 보정이 필요하거나 통계 성격이 강한 필드를 응답에서 제외하고 `/stats` API로 데이터 서빙 경로를 일원화했습니다.
- 특히 DB의 스트릭 값은 지연 보정(lazy correction) 전의 상태일 수 있어, 사용자에게 잘못된 정보가 노출되는 것을 방지했습니다.
- KBO 팀 순위 데이터를 단순 문자열 파싱에서 숫자형 및 구조화된 타입(`WinLoss`, `WinLossDraw`)으로 개선하여 데이터의 신뢰도와 활용도를 높였습니다.
- `getMe`, `createMe`, `updateMe` 등 사용자 관련 서비스 로직이 정제된 프로필 형식을 반환하도록 수정했습니다.
2026-05-06 15:51:45 +09:00
1a68d41e4f Implement lazy streak correction and enhance game result synchronization.
- 사용자의 마지막 참여일 이후 실제 경기가 있었음에도 참여하지 않은 경우를 감지하여 스트릭을 0으로 초기화하는 지연 보정(lazy correction) 로직을 도입했습니다.
- KBO 휴장일(월요일, 전체 우천 취소 등)을 자동으로 식별하여 실제 경기가 열린 날에만 결석 판정이 내려지도록 `hasMissedGameDayBetween` 검증 기능을 구현했습니다.
- 실시간 라이브 데이터를 기반으로 전일 경기 결과를 확정하는 기능을 추가하여 월간 일정 갱신 과정에서 발생할 수 있는 데이터 누락을 보완했습니다.
- 일일 아카이브 및 경기 동기화 로직을 수동으로 트리거할 수 있는 디버그 엔드포인트를 추가하고, 통계 데이터에 산출 기준일(forDate)을 포함하여 캐시 무효화 정밀도를 개선했습니다.
2026-05-06 11:14:28 +09:00
5149ecf82a Integrate scoring play analysis into game detail retrieval.
- Naver Sports의 highLight 엔드포인트를 통해 득점 발생 타석의 상세 데이터를 추출하는 기능을 구현했습니다.
- 단순 이닝별 점수 보강을 넘어 타석 결과, 주자 이동, 아웃 카운트 및 베이스 상태 변화를 포함한 정밀한 정보를 제공합니다.
- KBO와 네이버 간의 경기 ID 체계 차이를 자동으로 처리하며, 병렬 요청 구조로 전체 조회 성능을 최적화했습니다.
- 득점 주자의 출루 위치와 타점 발생 상황을 구조화된 데이터로 파싱하여 상세 조회 기능을 고도화했습니다.
2026-05-04 08:30:35 +09:00
5888d446b7 Implement KBO game detail retrieval with dynamic caching and API.
- KBO 게임센터의 스코어보드, 박스스코어, 키플레이어 데이터를 통합 조회하는 기능을 구현했습니다.
- 경기 상태(종료, 진행 중, 시작 전)에 따라 TTL을 30초에서 7일까지 유연하게 적용하는 동적 캐싱 시스템을 도입했습니다.
- KBO 공식 데이터에서 이닝별 득점이 누락되는 경우를 대비해 네이버 스포츠 API를 통한 데이터 보완 로직을 추가했습니다.
- Firestore의 중첩 배열 저장 제한을 회피하기 위해 테이블 데이터를 객체 배열 구조로 파싱하도록 설계했습니다.
- CLI 상세 조회 명령어와 REST API 엔드포인트를 추가하고, HTML 엔티티 디코딩 및 관련 단위 테스트를 보완했습니다.
2026-04-30 16:34:09 +09:00
360530ad13 Implement scoreboard system with rank snapshots and delta tracking.
- 전체 및 팀별 스코어보드 기능을 구현하고 순위 변동 시각화를 위한 스냅샷 시스템을 도입했습니다.
- `dailyArchive` 시점에 `rankSnapshot`을 기록하여 이전 대비 순위 변화량을 정확히 산출합니다.
- 상위권 데이터를 RTDB에 사전 계산하여 저장하고 `MemCache`를 적용해 조회 성능을 최적화했습니다.
- Firestore 복합 색인과 Count Aggregation으로 동점자 처리 및 상위 퍼센타일 산출 로직을 구현했습니다.
- 주간 결과 타입을 `DailyJudgment`로 전환하여 판정 세부 상태가 통계 응답에 포함되도록 수정했습니다.
2026-04-24 08:19:44 +09:00
b25e78dde8 Implement scoreboard system with rank snapshots and delta tracking.
- 전체 및 팀별 스코어보드 기능을 구현하고, 순위 변동(delta)을 시각화하기 위한 스냅샷 시스템을 도입했습니다.
- `dailyArchive` 과정에서 사용자의 현재 순위를 `rankSnapshot`으로 기록하여, 다음 판정 시점의 순위 변화량을 정확히 계산합니다.
- 상위권 데이터는 RTDB에 사전 계산(precompute)하여 저장함으로써 대규모 조회 요청에 대응하고, `MemCache`를 통해 API 응답 성능을 최적화했습니다.
- Firestore 복합 색인과 Count Aggregation을 활용하여 동점자 처리가 포함된 랭킹 및 상위 퍼센타일 산출 로직을 구현했습니다.
- 관련 서비스 레이어, 저장소 함수, API 핸들러 및 단위 테스트 코드를 추가했습니다.
2026-04-23 09:19:04 +09:00
392ecfbd0b Implement game result judgment and automated reward system.
- `dailyArchive` 과정에 `judgeDay`를 통합하여 경기 결과에 따른 일간 판정, 스트릭 업데이트, 티어 포인트 가산을 자동화했습니다.
- 주간 모든 경기를 성공적으로 예측했을 때 '주간 마스터 티켓'을 지급하는 `judgeWeekIfNeeded` 로직을 구현했습니다.
- Firestore 트랜잭션을 사용하여 판정 결과 반영의 원자성을 확보하고, 중복 처리를 방지하는 멱등성 가드를 적용했습니다.
- `statsService`가 실시간 계산 대신 사용자 문서에 저장된 스트릭과 티어 정보를 활용하도록 고도화하고 관련 유틸리티와 테스트 코드를 추가했습니다.
2026-04-22 11:08:35 +09:00
06c6de4660 Refactor stats service for KBO-aligned weeks and add documentation.
- KBO 리그의 경기 일정 특성을 반영하여 주간 통계 산출 기준을 기존 ISO 주차 방식에서 화요일~월요일 주기로 변경했습니다.
- `tuesdayOf` 유틸리티를 추가하여 특정 날짜가 속한 주의 시작일(화요일)을 기준으로 주간 데이터를 판정하고 집계하도록 개선했습니다.
- 서비스 내의 모든 주요 함수에 JSDoc 주석을 추가하여 각 로직의 역할과 매개변수에 대한 상세한 설명을 보완했습니다.
- 코드 포맷팅을 정리하고 불필요한 ISO 주차 계산 로직을 제거하여 통계 산출 파이프라인을 최적화했습니다.
2026-04-21 17:00:33 +09:00
cbf2bdd18d Optimize prediction summaries and game lists with new MemCache utility.
- `MemCache` 유틸리티 클래스를 도입하여 인메모리 캐싱 로직을 구조화하고 Promise Coalescing을 통해 동일 키에 대한 중복 fetch 요청을 방지했습니다.
- 투표 요약(`getSummary`) 정보에 5초 TTL 캐시를 적용하고, 투표 데이터 변경 시 해당 캐시를 즉시 무효화하여 데이터 정합성을 유지했습니다.
- 기존 `gameListService`에 개별적으로 구현되어 있던 수동 캐시 관리 로직을 `MemCache`로 교체하여 코드 중복을 제거했습니다.
- 투표 요약 API 응답에 `Cache-Control` 헤더를 추가하여 클라이언트 및 인프라 계층에서의 효율적인 캐싱을 지원합니다.
2026-04-21 14:02:36 +09:00
a59ccf3314 Implement user profile updates and synthesize KBO game IDs.
- 사용자 프로필 수정을 위한 `PATCH` 핸들러와 `updateMe` 서비스를 구현하여 닉네임, 응원팀, 지식 수준 변경 기능을 추가했습니다.
- HTML 정규식 파싱 오류로 인해 `null`이 반환되던 `gameId` 문제를 해결하기 위해 일정 메타데이터 기반의 ID 생성 로직을 도입했습니다.
- `updateUser`가 `FieldValue`를 수용하도록 개선하여 응원팀 설정 해제(필드 삭제)가 가능하도록 수정했습니다.
- 닉네임 중복 확인 및 변경 규칙 검증을 포함한 유저 서비스 테스트 케이스를 작성하고 `TODO.md` 이슈를 해결했습니다.
2026-04-18 15:53:29 +09:00
c3b62c5527 Automate game result judging with triggers and self-healing archive.
- Firestore 트리거(`onGameCompleted`)를 도입하여 경기 종료(`completed`) 및 취소(`cancelled`) 시 투표 결과 판정과 데이터 정리를 자동화했습니다.
- `dailyArchive` 과정에 자가치유(self-healing) 로직인 `reconcileDayVotes`를 추가하여, 트리거 누락이나 지연된 상태 변경 건을 아카이빙 시점에 보정합니다.
- `markGameEnded`가 직접 로직을 수행하는 대신 Firestore 문서만 업데이트하도록 변경하여, 결과 처리 흐름을 트리거로 일원화했습니다.
- 경기 일정 수집부터 통계 산출까지의 전체 투표 파이프라인과 운영 방법을 상세히 기술한 `VOTE_FLOW.md` 문서를 작성했습니다.
2026-04-15 10:37:06 +09:00
f1cf9610f4 Document the in-memory caching strategy for KBO game list data.
- 게임 리스트 조회 시 Firestore 공유 캐시 대신 인스턴스 단위 인메모리 캐시를 사용하는 기술적 근거를 상세히 기록했습니다.
- 실시간 경기 중 빈번한 데이터 변경으로 인한 Firestore 비용 부담과 레이턴시 문제를 해결하기 위한 결정임을 명시했습니다.
- 향후 트래픽 증가나 외부 API의 일시적인 503 오류 등 서비스 안정성에 영향을 줄 수 있는 상황에 대비하여, 공유 캐시로의 격상 검토안을 TODO에 추가했습니다.
2026-04-14 23:13:34 +09:00
c7e4ed8c22 Standardize game status values and make favorite team optional.
- `GameStatus` 타입을 `scheduled`, `live`, `completed`, `cancelled`로 변경하여 상태 관리의 일관성을 높였습니다.
- 사용자 프로필의 `favoriteTeamCode`를 선택 사항으로 변경하고, null 또는 undefined 입력에 대한 처리를 추가했습니다.
- KBO 데이터 병합 과정에 상세 로그를 도입하여 실시간 데이터 매칭 및 외부 API 호출 실패 상황에 대한 가시성을 확보했습니다.
- 상태 값 변경에 따라 영향받는 서비스 로직과 테스트 코드를 일괄 업데이트했습니다.
2026-04-14 23:08:45 +09:00
a7ce2799ef Merge live game data into KBO schedule results for today.
- KBO 일정 조회 시 오늘 날짜의 경기에 대해 실시간 점수와 경기 상태를 병합하는 기능을 추가했습니다.
- `GameStatus` 타입에 'live'를 추가하고, 게임센터 API 데이터를 기반으로 상태를 변환하는 로직을 구현했습니다.
- 외부 API 연동 시 발생할 수 있는 일시적인 오류(503 등)에 대비하여 예외 발생 시 기존 캐시 데이터를 반환하도록 예외 처리를 적용했습니다.
- 경기 ID 또는 팀 정보와 시간을 조합한 복합 키를 사용하여 스케줄 데이터와 실시간 데이터를 정확하게 매칭합니다.
2026-04-14 22:29:23 +09:00
98502916de Remove Realtime Database metrics for game list cache.
경기 일정 캐시의 적중률(Hit/Miss)을 RTDB에 기록하던 로직을 제거
2026-04-14 22:17:51 +09:00
6145af7159 Document the multi-layered caching architecture for KBO data.
- KBO 데이터 조회 성능 최적화를 위한 3계층 캐싱 전략 및 구조를 상세히 문서화했습니다.
- Firestore 기반의 일 단위 스케줄 캐시와 경기 상태에 따른 동적 TTL 적용 로직을 명시했습니다.
- 라이브 경기 정보 조회를 위한 10초 만료 메모리 캐시와 RTDB 기반의 메트릭 수집 방식을 설명했습니다.
- 분산 락을 활용한 동시성 제어 및 매일 새벽 수행되는 캐시 무효화와 프리워밍 과정을 포함했습니다.
2026-04-14 17:09:27 +09:00
742d57c56d Support daily schedule filtering and implement game list caching.
- KBO 일정 조회 기능에 일(day) 단위 필터링을 추가하여 API와 CLI에서 특정 날짜의 경기만 조회할 수 있도록 개선했습니다.
- 일 단위 일정 요청 시 캐시가 없으면 월간 데이터를 조회하여 캐시를 보충하는 효율적인 캐싱 전략을 도입했습니다.
- `gameListService`에 10초 TTL 기반의 메모리 캐시를 적용하여 외부 API 호출 빈도를 최적화했습니다.
- 캐시의 적중률(Hit/Miss)을 일자별로 RTDB에 기록하는 메트릭 수집 로직을 추가하여 모니터링 기반을 마련했습니다.
2026-04-14 17:05:43 +09:00