Skip to content

MSG-600 feat: 행사 위치 영역 상한을 81칸에서 2,500칸으로 올린다 - #286

Merged
s13121312 merged 5 commits into
developfrom
feature/MSG-600-event-area-cap
Sep 23, 2026
Merged

s13121312 merged 5 commits into
developfrom
feature/MSG-600-event-area-cap

Conversation

@s13121312

@s13121312 s13121312 commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

🎫 관련 티켓

작업 내용

행사 운영자가 등재 신청에서 위치 하나에 그릴 수 있는 격자 칸1 수 상한을 81칸에서 2,500칸으로 올렸다. 판정 방식(사각형 합집합2)과 에러 응답(developCode 13432, HTTP 400)은 그대로이고 값과 안내 문구만 바뀐다. 스키마, API 표면, 에러코드 변경은 없다.

  • 상한 상수를 RepresentativeGridResolver.MAX_CELLS_PER_LOCATION(2,500) 한 곳으로 모았다. 행사 위치 시더와 신청 접수 검증이 각자 들고 있던 상수(2,500과 81)를 지우고 이 값을 참조한다. 위치당 사각형 개수 상한도 같은 상수에 묶여 함께 오른다.
  • 대표 격자3 계산의 거리 산술을 Math.multiplyExact와 Math.addExact로 바꿨다. 넘치면 조용한 오답이 아니라 ArithmeticException으로 드러난다(아래 고민한 내용).
  • 13432 메시지를 "위치 하나의 영역은 최대 2,500칸입니다"로, Swagger 설명과 자바독의 81 표기 4곳을 2,500으로 고쳤다.
  • 문서: PRD docs/prd/event-submission.md v2.4(FR-24 개정, 결정 기록), 스펙 docs/spec/MSG-600.md 신규, docs/spec/MSG-498.md의 현행 계약 서술 갱신, SRS FR-EVENT-13 구현됨, RTM 재생성, .claude/docs/status.md.

변경 파일 22개(문서 7, main 11, test 4). 테스트는 검증 경계값과 재제출, 겹침, 사각형 2,500개, 이벤트 참여형 위치, 위치 20개 접수, 컨트롤러 2,501칸, 리졸버 2건(5만 칸 결정성, 오버플로 예외), 승인 통합 1건(mission_grids 50,000행과 대표 격자)을 추가하거나 고쳤다. 마커는 // 검증: FR-EVENT-13, AC-600-NN이다.

검증 결과
관련 스위트(검증·참여형·리졸버·컨트롤러·승인 통합) 전부 green, 승인 통합 단건 38.8초
전체 빌드 ./gradlew build (CI와 같은 이미지의 임시 PostGIS) 3,188건 중 2건 실패, 둘 다 이 티켓 밖이고 공유 DB 단독 재실행에서 통과
flowchart LR
    A[행사 운영자 신청<br/>사각형 목록] --> B{사각형 하나가<br/>2,500칸 초과?}
    B -- 예 --> X1[13432 거부<br/>전개 없이]
    B -- 아니오 --> C[Set으로 전개<br/>겹침은 한 번만]
    C --> D{합집합 > 2,500?}
    D -- 예 --> X1
    D -- 아니오 --> E[접수, 대표 격자 계산]
    E --> F[관리자 승인]
    F --> G[전 위치 합집합<br/>최대 20 × 2,500 = 50,000칸]
    G --> H[대표 격자 재계산<br/>exact 산술, 넘치면 예외]
    H --> I[mission_grids 저장]
Loading

PRD 결정 기록 발췌(v2.4 §8):

81칸 합의는 위치 하나가 대략 1km 사방의 덩어리라는 상정 위에 있었는데, 하천이나 거리 축제처럼 가늘고 긴 행사장은 그 상정을 벗어나 면적이 작아도 신청이 막힌다. 새 값을 2,500으로 고른 이유는 행사 위치 시드가 이미 위치당 2,500칸(5km×5km 상당)을 허용하고 있고 대표 격자 계산기도 그 값을 전제로 안전 논증을 세워 두어서, 신청과 시드의 상한이 한 벌로 합쳐지고 새로 검증할 규모가 생기지 않기 때문이다.

🤔 고민한 내용

왜 81이었고 왜 2,500인가. 81은 피그마 댓글의 "1km로 합의"에서 온 디자인 값이지 기술 제약이 아니다. 청계천 경계 기술검증(docs/reports/2026-09-13-event-tour-additive-poc.md)에서 중심선에 편측 20m 경계만 줘도 면적은 33칸 상당인데 걸치는 격자가 135칸이라 신청 자체가 안 됐다. 값을 조금만 올리는 안(예: 300)은 상한이 두 벌로 남고 다음 선형 행사장에서 반복되므로 기각했고, 시더가 이미 쓰던 2,500과 통일했다. 사각형 대신 경계 도형을 입력받는 안은 상한이 아니라 입력 계약 문제라 별도 PRD로 넘겼다. 이 PR만으로 청계천을 신청하려면 사각형을 여러 개 그려야 하는 불편은 남는다.

상수를 왜 global.geo에 뒀나. 대표 격자 계산기의 long 오버플로4 안전 논증이 "셀 수 상한" 위에 서 있어서, 셀 집합을 만드는 쪽(시딩, 접수)이 계산기 옆의 값을 보게 하는 편이 사본 두 벌보다 낫다. MSG-498이 격자 인덱스 상한을 같은 자리로 승격한 선례를 따랐다.

승인 경로에서 논증이 깨진다. 스펙 교차 리뷰(Codex 1라운드)가 잡은 것으로, 승인은 위치 20개의 전 셀 합집합으로 대표 격자를 다시 계산한다. 위치당 2,500이면 합집합은 최대 50,000칸이고, 기존 자바독의 "인덱스 100,000 × 셀 수 2,500 → 2.5e17" 논증은 이 n에서 long 범위를 넘을 수 있다(5e9의 제곱). 실제 한국 좌표는 인덱스 폭이 1만 셀 안팎이라 5e17에 그쳐 열 배 넘게 여유가 있지만, 문서화된 전제가 무너지는 것은 그대로 둘 수 없었다. 부동소수점 전환은 동률 결정성(남서 우선)을 깨서 기각했고, exact 연산으로 바꿔 넘치면 예외가 나게 했다. 이 예외는 ApiException이 아니라 핸들러의 공통 500으로 떨어진다. 도달 불가 조건이라 수용했다.

심사가 실질 방어선이 된다. 5km 사방을 한 위치로 신청하는 것은 형식 검증을 통과한다. FE 티켓 MSG-547이 "한 변 10칸 경고, 확정은 허용, 관리자 심사가 최종 판단"으로 이미 같은 구도를 택한 상태라 서버도 그 위에 선다.

전체 빌드를 임시 DB로 돌린 이유. 로컬 공유 DB에 MSG-596 부하 데이터(videos 135만 건)가 남아 있어 GridEpsg5179MigrationTest의 V28 재생이 videos 잠금을 쥔 채 9분 넘게 돌고 다른 세션 96개가 대기했다. CI와 같은 이미지(postgis 16-3.4-alpine)를 5433에 띄워 빌드했고 컨테이너는 지웠다. 실패 2건(VideoEncodingJobRepositoryTest 시각 창, MissionVideoUploadRollbackTest 예외 기대)은 1시간 46분짜리 빌드의 순서·시각 플레이크로 판정했고 공유 DB 단독 재실행에서 통과했다.

👀 리뷰 포인트

  • RepresentativeGridResolver.nearestToCentroid의 exact 연산 전환과 자바독 논증. 폭 기준 논증(폭 × n ≤ 약 3e9)이 맞는지 봐 주시면 좋겠다.
  • 사각형 개수 상한을 칸 수 상한과 같은 상수에 계속 묶어 둔 판단(스펙 D-2). MSG-498 D-7의 "개수 초과는 전부 중복 입력" 근거가 2,500에서도 유효한지.
  • RTM에서 AC-600-08(2,500 리터럴 정의 단일성)만 테스트 마커 없이 미커버로 남는다. grep 불변식이라 그대로 뒀다.
  • FE 몫: MSG-547 위저드의 "n/81" 진행 바와 차단 값, 피그마 영역 상한 주석(15525:8840) 갱신. 한 변 10칸 경고는 유지를 권한다.
  • Codex 코드 교차 리뷰는 결제 문제로 이번 PR에서 돌리지 못했다(스펙 리뷰 1라운드만 반영). reviewer 에이전트 전 모듈 통과.

Footnotes

  1. 칸: 국토 평면 좌표계 위 100m×100m 격자 하나. 2,500칸은 50×50이라 한 변 5km 정사각형 상당이다. ↩

  2. 합집합 판정: 사각형들이 덮는 칸을 Set으로 전개해 겹치는 칸을 한 번만 세는 방식. 같은 영역을 어떤 사각형 조합으로 그렸든 결과가 같아 그리는 방식에 따라 거부되는 비결정성이 없다. ↩

  3. 대표 격자: 위치의 영상이 실제로 연결되는 격자 하나. 홀수 행렬 직사각형이면 정중앙, 아니면 영역 중심에 가장 가까운 포함 격자를 정수 산술로 고른다. ↩

  4. long 오버플로: 64비트 정수 범위(약 9.2×10¹⁸)를 넘는 곱셈이 조용히 엉뚱한 값으로 감기는 현상. 거리 제곱 비교에서 일어나면 예외 없이 잘못된 격자가 대표로 저장된다. multiplyExact는 그 순간 예외를 던진다. ↩

@claude

claude Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Claude finished @s13121312's task in 2m 13s —— View job


리뷰 완료

  • 컨텍스트 수집 (diff, 스펙 문서, 관련 파일)
  • 병렬 브랜치 경합 확인 (마이그레이션 번호, developCode 대역, Owner A/B 계약)
  • 스펙 대비 구현 확인 (docs/spec/MSG-600.md)
  • 실행해야 보이는 결함 확인 (트랜잭션·동시성·시간대·인가)
  • PR 본문 주장 검증
  • 결과 정리

확인한 범위

  • origin/develop 대조: 마이그레이션 파일 변경 없음(DB 칸 수 제약 없다는 주장과 일치), developCode 13432는 메시지 문자열만 변경(기존 코드 재사용, 신규 코드 없음 — 대역표 갱신 불필요), RepresentativeGridResolver는 event·mission 양쪽이 쓰지만 둘 다 Owner B 내부라 A/B 계약 경계 이슈 없음.
  • RepresentativeGridResolver.nearestToCentroid의 Math.multiplyExact/addExact 전환: n * cell.gridY() 자체는 exact 아닌 일반 곱셈이지만 n ≤ 50,000, gridY < 100,000 상한에서 곱이 5×10⁹대라 long 오버플로 여지가 없고, 실제 오버플로 위험은 제곱합(dy*dy+dx*dx) 쪽이라 그 부분만 exact로 감싼 설계가 스펙 D-3 논증과 일치함을 확인.
  • EventSubmissionServiceImpl.expand·EventSeeder의 상수 통일과 경계값(2,500 통과/2,501 거부, 사각형 2,500개/2,501개) 로직·테스트가 docs/spec/MSG-600.md AC-600-01~09와 1:1로 대응.
  • docs/spec/MSG-498.md·.claude/docs/status.md·docs/srs.md의 값 갱신 내용이 실제 코드와 어긋나지 않음.

발견된 P1/P2/P3 이슈 없음. RepresentativeGridResolver.MAX_CELLS_PER_LOCATION import가 이미 있어 컴파일 이슈도 없음(수동 확인).

@github-actions

Copy link
Copy Markdown

테스트 커버리지

Overall Project 95.86% 🍏
Files changed 100% 🍏

File Coverage
EventSubmissionLocationRequestDto.java 100% 🍏
EventSubmissionLocationResponseDto.java 100% 🍏
EventSubmissionAreaRectDto.java 100% 🍏
EventErrorCode.java 100% 🍏
RepresentativeGridResolver.java 100% 🍏
EventSubmissionCells.java 100% 🍏
EventSubmissionAreaRect.java 100% 🍏
EventSubmissionLocation.java 100% 🍏
EventSubmissionServiceImpl.java 97.85% 🍏
EventSeeder.java 95.4% 🍏
EventSubmissionController.java 80% 🍏

@s13121312
s13121312 merged commit 47d64c0 into develop Sep 23, 2026
2 checks passed
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