Skip to content

MSG-224 feat: 축제 미션 적재 — 시드·격주 수동 갱신 러너, source 소유 식별(V13) - #86

Merged
Ss0Mae merged 6 commits into
developfrom
feature/MSG-224-festival-seed
Jul 31, 2026
Merged

MSG-224 feat: 축제 미션 적재 — 시드·격주 수동 갱신 러너, source 소유 식별(V13)#86
Ss0Mae merged 6 commits into
developfrom
feature/MSG-224-festival-seed

Conversation

@Ss0Mae

@Ss0Mae Ss0Mae commented Jul 31, 2026

Copy link
Copy Markdown
Member

🎫 관련 티켓

  • Closes MSG-224
  • PRD: docs/prd/MSG-219-mission-prd.md (검토됨, FR-1~5) / 스펙: docs/MSG-224.md

작업 내용

  • 전국문화축제 정제본(festivals.jsonl 1,059건)에서 진행 중이거나 예정인 축제를 골라 EVENT 미션으로 적재한다. 축제마다 중심 좌표 주변 9×9 격자 81개를 미션 대상으로 잡고, 한 곳만 찍어도 완료되도록 target_count는 1로 둔다.
  • 시드와 격주 수동 갱신을 러너 하나로 처리한다. 빈 DB에서 돌리면 시드가 되고, 이후 격주로 돌리면 끝난 축제를 정리하고 새 축제를 넣는 갱신이 된다. 플래그 기본 off라 평소 기동에는 영향이 없고, 전체가 한 트랜잭션이라 중간에 실패해도 기존 데이터는 그대로 남는다. 상시 스케줄러는 두지 않는다.
  • 같은 축제가 이름만 바뀌어 중복 등록되는 문제는 이름 대신 중심 격자와 기간으로 걸러낸다. 재실행해도 결과가 같다.
  • 종료 정리 SQL은 MSG-223에서 확립한 UTC 정규화 규칙을 그대로 따르고, 스탬프가 걸린 미션은 지우지 않는다.
  • V13으로 missions.source 컬럼을 추가했다. 러너가 어떤 행이 자기 것인지 알아보기 위한 적재 출처 표시다(아래 참고).
  • 테스트는 22건을 새로 넣었다(파서 6, 순수 로직 5, DB 통합 11). 전체 718건 green.

🤔 고민한 내용

  • Codex 리뷰가 P2 두 건을 잡았다. 당초 정리 조건이 type='EVENT'였는데, 팝업(MSG-235)도 같은 EVENT 타입을 쓴다. 그대로 두면 축제 갱신이 끝난 팝업까지 지워버리고, 1격자 팝업이 dedupe 대조에 섞여 가짜 축제 키를 만들면 진짜 축제를 건너뛸 수도 있었다. 그래서 source 컬럼을 도입해 source='FESTIVAL'인 행만 건드리게 했다. source가 비어 있는 행(수동 데이터)은 어떤 러너도 건드리지 않는다.
  • source 필드는 enum이 아니라 String으로 뒀다. 나중에 팝업이 자기 값을 넣을 때 enum 상수 추가를 잊으면 기존 조회가 통째로 깨지기 때문이다(스펙 §D7).
  • 격자 81개는 좌표를 다시 인코딩하지 않고 인덱스 문자열로 조립한다. 셀 경계에서 부동소수점 오차로 한 칸 밀리는 사고를 피하기 위해서다.
  • Mission.created_at은 DB DEFAULT에 맡겼다(insertable=false). 기존 조회 전용 매핑 그대로 INSERT하면 NOT NULL 위반이 나는 함정을 리뷰 사전 점검에서 찾았다.

👀 리뷰 포인트

  • 정리 SQL의 NOT EXISTS(user_missions) 조건. 빠지면 스탬프 걸린 미션의 FK에 걸려 격주 갱신 전체가 롤백된다.
  • 회귀 테스트 일격자_EVENT는_dedupe_대조에_포함되지_않는다. 팝업 모사 데이터를 축제 중심에서 (-4,-4) 자리에 같은 기간으로 놓아서, 예전 코드로 되돌리면 반드시 깨지게 배치했다.
  • 검증 경로: 컨벤션 리뷰(사전 점검, 본리뷰, 델타) 위반 0건. Codex 1라운드 지적을 반영한 뒤 2라운드에서 신규 지적 0건으로 수렴.

@Ss0Mae Ss0Mae added the feat 기능 추가 (MSG-XXX feat:) label Jul 31, 2026
@Ss0Mae Ss0Mae self-assigned this Jul 31, 2026
@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

Claude finished @Ss0Mae's task in 3m 25s —— View job


🔍 PR 리뷰 완료

작업 목록

  • 컨텍스트 수집 (컨벤션 규칙, 스펙 docs/MSG-224.md, 변경 파일 12개)
  • 요청 파악 (코드 리뷰 — 구현 없음)
  • 컨벤션 위반 검토 (네이버 Java, DTO 네이밍, 응답 패턴)
  • Owner A/B 도메인 경계 검토
  • 버그/로직 검토
  • 테스트 커버리지 검토
  • 리뷰 결과 정리 및 게시

총평

스펙(docs/MSG-224.md)·구현·테스트가 서로 정확히 맞물려 있고, git diff HEAD~5 HEAD로 커밋 단위 변경을 대조한 결과 문서 서술(D1~D7)과 실제 코드가 어긋나는 곳을 찾지 못했습니다. 이미 reviewer 2회 + Codex 2라운드를 통과한 PR답게 완성도가 높습니다. 블로킹 이슈 없음.

컨벤션

  • Import 순서(javaorglombokcom), 하드탭, 4필드 이상 @Builder(Mission.java:73 — type/title/startAt/endAt/targetCount/source 6필드) 등 project-conventions.md 규칙 위반 없음.
  • response-pattern.md는 이 PR과 무관 (신규 API 없음 — 스펙에도 명시).

Owner A/B 도메인 경계

  • 전 파일이 mission.*(Owner B) 내부이며, GridEncoder.encode/decode(Owner A 유틸) 사용은 읽기 전용 public 메서드 호출뿐이라 계약 변경 없음 — CLAUDE.md 협업 원칙과 스펙 "계약 변경: 없음" 서술이 일치합니다.
  • FestivalMissionSeeder.java:190-204(expandGrids)는 GridEncoder.encode(double,double) 재호출 대신 인덱스 문자열 조립을 택해 셀 경계 부동소수점 오프바이원을 피했고, GridEncoderencode(long,long) 오버로드를 새로 얹지 않아 Owner A 파일도 건드리지 않았습니다 — 스펙 D2에서 이미 트레이드오프를 명시.

버그·로직

  • V13__mission_source.sql + Mission.source(String, enum 아님) 설계는 "공유 EVENT 타입에서 팝업 오삭제·가짜 중심 키" 문제를 정확히 해소합니다. MissionRepository.java:78-101findBySource/deleteEndedFestivalsWithoutStampssource='FESTIVAL' 한정으로 일관되게 적용돼 있습니다.
  • deleteEndedFestivalsWithoutStamps()@Modifying(flushAutomatically = true, clearAutomatically = true)가 붙어 같은 트랜잭션에서 방금 save()한 미션도 정리 판정 대상에 들어가지만, FestivalJsonlReaderendDate < todayKst 행을 이미 걸러내므로 신규 삽입 미션이 자기 자신을 삭제하는 경합은 없습니다 — 안전.
  • MissionGridId/GridEncoder.decodeindexOf('_')로 분리하므로 음수 격자 인덱스(-41642_110458류)에서도 파싱이 깨지지 않음을 확인했습니다.
  • @Transactional 단일 트랜잭션 + 파일 부재/유효 0건 즉시 예외로 FR-5(부분 실패 시 무손상)를 충족합니다.

테스트

  • 스펙의 "테스트 시나리오" 목록(모듈 1~3, 22건)과 실제 테스트 파일 3종의 테스트 메서드가 1:1로 대응하는 것을 확인했습니다 — 리뷰 포인트로 짚어주신 일격자_EVENT는_dedupe_대조에_포함되지_않는다(FestivalMissionSeederIntegrationTest.java:191-207)는 구코드(findByType)로 되돌리면 실제로 깨지는 회귀 배치임을 로직상 확인했습니다.
  • 누락된 테스트 시나리오는 발견하지 못했습니다.

참고(블로킹 아님)

  • missions.source에 인덱스가 없어 findBySource/정리 SQL이 풀스캔이지만, 스펙에서 "수백 건 규모·수동 실행"으로 명시적으로 판단한 사항이라 지금 단계에서 문제 삼지 않습니다.

@github-actions

Copy link
Copy Markdown

테스트 커버리지

Overall Project 92.09% -0.66% 🍏
Files changed 90.12% 🍏

File Coverage
MissionGrid.java 100% 🍏
Mission.java 100% 🍏
FestivalRecord.java 100% 🍏
FestivalJsonlReader.java 99.27% -0.73% 🍏
FestivalMissionSeeder.java 86.51% -13.49% 🍏

@Ss0Mae
Ss0Mae merged commit addafa8 into develop Jul 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 추가 (MSG-XXX feat:)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant