Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/docs/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
- **없는 것**: 댓글·도움돼요 API(441)와 그 경로의 상호작용 잠금 배선(13422 — 가드는 442에 있음), 익명 heartbeat rate limit(공개 근사값이라 부풀리기 수용 — 스펙 D4)

- MSG-501: 행사 운영자 콘솔 승인 이벤트 목록 — `GET /api/org/events`(`controller/OrgEventController` 신규, city·name 선택 파라미터), `EventQueryService.getApprovedEvents(String, String)` 추가(+Impl — 후보는 `findByEndsAtAfter(now)` 파생 쿼리 하나로 DB에서 거르고(D-1 `ends_at > now`, `statusAt` 미경유), `visible_from` 미적용(D-2 — 콘솔은 노출 전 예정 회차도 노출), 필터·검색(대소문자 무시 부분 일치)·시도별 집계(전체 기준 — totalCount·cityCounts는 필터 무관)·정렬(startsAt→id, 건수 내림차순→이름)은 자바에서. placeLabel은 기존 `findWithOccurrenceByOccurrenceIdIn` 재사용(fetch join, displayOrder→id 정렬의 첫 행 — 승인 흐름산 name 채움 규칙은 MSG-500 접점, 스펙 명시). 쿼리 회차 1 + 위치 일괄 1 고정(빈 후보면 1회). DTO 3종 `Org*ResponseDto`(placeLabel은 required+`@Schema(nullable=true)` 병기 — ResponseSchemaNullabilityTest 규약). SecurityConfig·Flyway·developCode·계약 인터페이스 전부 무변경(/api/org/** matcher가 MSG-496에서 기확정). 테스트 25건(서비스 17·컨트롤러 5 — ORG 200 실경로 포함·리포지토리 3), 전부 `// 검증: FR-EVENT-16`. reviewer 1R(P2 용어 1건 수정)·Codex 1R 수렴(지적 0)
- MSG-498: 행사 운영자 행사 등재 신청 — `event.submission` 하위 패키지 신설(V49 `event_submissions`·`_locations`·`_location_rects`·`_status_history` 4테이블 + 신청 번호 전역 시퀀스, 이력 테이블 CHECK가 REJECTED 행에만 사유 강제 — 반려 사유 저장 원천은 이력 하나 D-3). API 5개 전부 `/api/org/event-submissions`(SecurityConfig 무변경 — 496 matcher·497 mustChange 인터셉터 자동 커버, 인가 테스트는 전제 확인 4건): presign(프로필 미러링, jpg·png 10MB, webp 불허) · 제출 POST(유형 FESTIVAL·POPUP 2종, 유형 밖 필드 13439 거부, 81칸은 사각형 **합집합** 판정(전개 전 단일 사각형 long 선판정으로 OOM 차단), 격자 인덱스 상한 상수는 `RepresentativeGridResolver.GRID_INDEX_UPPER_EXCLUSIVE` 승격 공유(시더 사본 제거), 대표 격자 서버 계산, 신청 번호 `FM-{KST연도}-{4자리}` native `nextval` 채번) · 내 목록(상태별 건수는 별도 쿼리 없이 로드 목록에서 groupingBy 파생 — 스냅숏 불일치 차단, 페이지네이션 도입 시 되돌릴 지점) · 상세(존재 은닉 13430 — 모든 조회가 `findByIdAndUserId` 단일 경로, rejection은 이력 최신 행) · 재제출 PATCH(REJECTED 한정, 유형 불변 전체 교체, `@Modifying(clearAutomatically=true)` 조건부 UPDATE 술어에 userId 포함 → 0행이면 소유 조회 분기(부재 13430/비REJECTED 13434), 재로드는 UPDATE 뒤, imageS3Key 생략=유지). 이미지 확정: HEAD 실측(0바이트 13436)→original 복사(uuid 시도마다 신규 — 객체 공유 차단)→롤백 보상 삭제는 **복사 호출 앞 등록**(응답 유실 고아 차단)→커밋 후 pending 삭제(재확정 차단)·STATUS_UNKNOWN엔 미삭제. 엔티티는 다대일 양방향(자식 `@ManyToOne` 주인 + mappedBy 컬렉션 + 편의 메서드, D-12 — 단방향 왕복 실측 후 주류 관행으로 확정). 리스트 요소 null은 type-use `@NotNull`로 공통 400. errorCode 13430~13439(EventErrorCode 확장). 테스트 58건(단위 25·통합 33, 동시 재제출·복사 응답 유실·게이트 커버 포함). reviewer 3R·Codex 3R 수렴(채택 3건 반영, 멱등 키는 스펙 3회+구현 3회 기각 — 콘솔 수동 제출 수용). 심사·승인(APPROVED 쓰기)은 MSG-500 몫, pending S3 라이프사이클 규칙은 배포 전 런북
- MSG-457: `EventQueryService.getLocationsBulk(Collection<Long>)` + `LocationPoint(name, representativeGridId)` 신설 — 여러 회차 위치 일괄 조회(JPQL fetch join 1쿼리, 미노출 회차 키 생략 = 단건 13404 은닉과 동결, 정렬 회차 id→display_order→id로 단건 계약 승계). route 후보 수집이 소비(회차별 getLocations 반복의 N+1 차단), `getLocations` 시그니처 불변

### `region` (Owner A · 구현 강정민) — 🟡 부분
Expand Down
174 changes: 174 additions & 0 deletions docs/explainers/MSG-498-submission-flow.html

Large diffs are not rendered by default.

33 changes: 22 additions & 11 deletions docs/prd/event-submission.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

> 티켓: 미발행 · 작성일: 2026-08-27 · 작성: prd-writer
> 상태: 검토됨 (2026-08-28 승인. ver 14 v2 시안 `15525:8613`과 피그마 댓글, 게이트 문답 4건 반영) (2026-08-28 MSG-501 최소 개정: 모달 조회 FR·API 표면 추가)
> 개정: 2026-08-28 v2.1. 세 번째 등록 유형을 이벤트 참여형으로 재편했다(승인 이벤트 선택 후
> 참여 신청, MSG-501·MSG-502 분리. MSG-498은 지역축제·팝업스토어 한정). 근거는 지라 MSG-498
> 코멘트, 사용자 승인. 개정 범위는 유형 재편 반영에 한정한다

## 1. 문제 상황

Expand All @@ -20,7 +23,7 @@
확인(운영자 6-1), 비로그인 진입점 섹션(`/ops` 랜딩·지도 홈 레일 슬롯)이 새로 생겼다. 행사 운영자가 직접 신청하고 관리자가 심사해
승인하는 구조다. 이 방향은 구 행사방 PRD(`docs/prd/event-room.md`)의 "운영자용 행사 등록
화면은 만들지 않는다" 결정을 뒤집는다. 그 결정의 전제는 등재 대상이 초대형 행사 몇 개뿐이라는
것이었는데, 시안은 등록 유형을 지역축제와 팝업스토어와 행사방까지 넓혔다.
것이었는데, 시안은 등록 유형을 지역축제와 팝업스토어와 이벤트 참여형까지 넓혔다.

용어는 glossary.md "행사 등재" 항목을 따른다. 행사를 등록하는 외부 주체는 언제나 두 단어를
붙여 "행사 운영자"라 부르고, 필맵 쪽은 사용자 대면 문구에서 운영팀, 심사와 계정 발급 주체로는
Expand Down Expand Up @@ -73,7 +76,7 @@ JWT role 클레임 또는 로그인 응답의 role 필드로 한다. 서버 몫

| ID | 요구사항 | 우선순위 |
|----|----------|----------|
| FR-7 | 행사 운영자는 등록 유형(지역축제, 팝업스토어, 행사방) 중 하나를 골라 행사를 신청할 수 있다. 유형마다 기본 정보 항목이 다르다(아래 표) | Must |
| FR-7 | 행사 운영자는 등록 유형(지역축제, 팝업스토어, 이벤트 참여형) 중 하나를 골라 행사를 신청할 수 있다. 유형마다 기본 정보 항목이 다르다(아래 표). 이벤트 참여형은 독립 행사를 새로 만드는 신청이 아니라 승인된 이벤트를 골라 참여를 신청하는 구조다(2026-08-28 v2.1 재편, 표 아래 설명) | Must |
| FR-8 | 행사 위치는 주소가 아니라 지도 위 격자 사각형 영역으로 지정한다. 신청 하나에 위치 여러 개, 위치 하나에 사각형 여러 개를 담을 수 있다. 형식은 기존 행사 시드의 areaRects[^2]와 같다. 위치에 이름을 붙이지 않고, 순번과 지역 라벨(격자 표시명 재료)로만 식별한다(피그마 댓글 #102) | Must |
| FR-24 | 위치 하나가 쓸 수 있는 격자는 사각형들을 합쳐 최대 81칸(9×9 유래)이다. 초과는 제출에서 막는다(피그마 댓글 #106·#109 "1km로 합의", v2 시안 반영 완료. 미해결 질문 2 해소) | Must |
| FR-9 | 위치의 대표 격자[^3]는 신청자가 정하지 않고 서버가 계산한다 | Must |
Expand All @@ -90,12 +93,19 @@ JWT role 클레임 또는 로그인 응답의 role 필드로 한다. 서버 몫
|------|------|
| 지역축제 | 축제명, 주최 기관, 축제 기간, 주요 프로그램, 축제 소개, 대표 이미지 |
| 팝업스토어 | 팝업명, 브랜드/운영사, 운영 기간, 운영 시간, 팝업 소개, 대표 이미지 |
| 행사방 | 행사방 이름, 운영 주체, 공개 기간, 참여 방식, 행사방 소개, 커버 이미지 |
| 이벤트 참여형 | 이름(참여로 생기는 하위 실체의 명칭은 용어 정리 중), 운영 주체, 공개 기간, 참여 방식, 소개, 커버 이미지. 입력 전에 시·도 칩과 승인 이벤트 목록 모달([3-1] 신규)에서 부모 이벤트를 고른다 |

이미지는 권장 16:9, JPG 또는 PNG, 최대 10MB(시안 문구). 주요 프로그램 같은 서술 항목은
구조화하지 않고 자유 문자열로 받되 최소 10자를 요구한다(피그마 댓글 #100 "String으로 받고
최소 10자 제한", [4A] 주요 프로그램 필드 앵커).

등록 유형 재편(2026-08-28 v2.1, 사용자 승인): 세 번째 유형이 구 "행사방"에서 **이벤트
참여형**으로 바뀌었다. 이벤트는 지역축제와 팝업스토어를 제외한 큰 행사 카테고리를 가리키는
확정 용어다(MSG-503). 유형 카드를 누르면 페이지 이동 대신 모달이 뜨고, 시·도 칩과 승인 이벤트
목록(건수, 유형 배지, 기간, 장소 라벨, 이름 검색)에서 부모를 고른 뒤 입력을 계속한다. 티켓
배분은 승인 이벤트 목록 조회가 MSG-501, 참여 신청이 MSG-502이고, MSG-498은 지역축제와
팝업스토어 2개 독립 유형만 다룬다.

### 심사 (관리자)

| ID | 요구사항 | 우선순위 |
Expand Down Expand Up @@ -156,7 +166,7 @@ classDiagram
}
class EventSubmission {
<<신규 엔티티>>
+유형 (지역축제/팝업스토어/행사방)
+유형 (지역축제/팝업스토어/이벤트 참여형)
+유형별 기본 정보
+위치 목록 (areaRects)
+상태 (심사 중/승인/반려)
Expand Down Expand Up @@ -225,13 +235,14 @@ admin/events 2개(목록·unpublish), auth/password 4개(status·change·reset-r
작성자(강정민) 확인으로 2건이 더 닫혔다. 남은 미확정은 2건이며 각각 게이트가 걸린 티켓을
명시했다.

- [ ] **승인 행사 노출 방식과 행사방 유형의 등록 구조** (시안 열린 판단 1, 미확정 유지):
2026-08-28 작성자 설명으로 방향은 잡혔다. 행사방 유형(4C)은 독립 행사를 새로 만드는 게
아니라 지역을 고르고 기존 큰 행사(행사방)를 고른 뒤 그 하위 위치(부산물)를 추가하는
구조인데, v2 [4C] 디자인에는 아직 반영되지 않았다. 지역축제와 팝업스토어 승인분의 노출
형태(행사방 편입 / 칩 / 규모별 분기)는 여전히 열려 있다. **MSG-498(4C 입력 구조)과
MSG-500(approve) 스펙 착수 전 확정이 게이트다.** 확정되면 SRS FR-EVENT-01의 등재 기준
서술을 함께 고친다
- [ ] **승인 행사 노출 방식과 이벤트 참여형의 세부** (시안 열린 판단 1, 미확정 유지):
2026-08-28 v2.1 디자인 개정으로 입력 구조는 잡혔다. 세 번째 유형은 독립 행사를 새로 만드는
게 아니라 시·도와 승인 이벤트를 모달([3-1] 신규)에서 고른 뒤 그 아래 참여를 신청하는
구조다. 지역축제와 팝업스토어 승인분의 노출 형태(이벤트 편입 / 칩 / 규모별 분기)와 참여로
이벤트 아래 생기는 하위 실체의 명칭은 여전히 열려 있다. **MSG-502(참여 신청)와
MSG-500(approve) 스펙 착수 전 확정이 게이트다.** v2.1 재편으로 이벤트 참여형이 빠진
MSG-498에는 이 게이트가 걸리지 않는다. 확정되면 SRS FR-EVENT-01의 등재 기준 서술을 함께
고친다
- [ ] **승인 후 일정 수정 정책** (시안 열린 판단 3): 2026-08-28 유보 확정. MVP에서는 구현하지
않고 시안대로 "운영팀에 문의"로 안내한다. A 재신청 재심사 / B 관리자 직접 수정 / C 항목별
분리 중 후속 결정이 나면 그때 티켓을 새로 판다
Expand Down
12 changes: 5 additions & 7 deletions docs/rtm.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
**병합 충돌이 나면 손으로 합치지 말고 재생성한다.** 어느 쪽이든 골라 충돌만 없앤 뒤(`git checkout --ours docs/rtm.md` 등)
스크립트를 다시 돌려 그 결과를 커밋한다. 이 표는 두 원천에서 계산되는 값이라 양쪽 diff를 섞으면 어느 쪽과도 다른 상태가 된다.

요약: FR 288건 중 테스트 연결 254건, 검증 공백 0건 (계획·폐기라 테스트 부재가 정상인 27건, 성격상 테스트 비대상 7건 별도)
요약: FR 288건 중 테스트 연결 256건, 검증 공백 0건 (계획·폐기라 테스트 부재가 정상인 25건, 성격상 테스트 비대상 7건 별도)

| 요구사항 ID | SRS 상태 | 검증 테스트 |
|---|---|---|
Expand Down Expand Up @@ -199,8 +199,8 @@
| FR-EVENT-10 | 구현됨 | EventInteractionLockTest, EventLifecycleGuardTest, EventNotificationSubscriptionRepositoryTest, EventSeederScheduleChangeTest, EventVideoQueryServiceTest, EventVideoUploadConcurrencyTest, EventVideoUploadServiceTest |
| FR-EVENT-11 | 구현됨 | EventViewerCacheFailureControllerTest, EventViewerControllerTest, EventViewerServiceImplTest |
| FR-EVENT-12 | 구현됨 | MissionAwardQueryTest, MissionProgressQueryTest, MissionVideoCountQueryTest, MissionVideoListQueryTest, MissionVisitedGridQueryTest |
| FR-EVENT-13 | 계획 | (없음) |
| FR-EVENT-14 | 계획 | (없음) |
| FR-EVENT-13 | 구현됨 | EventSubmissionCommitBoundaryTest, EventSubmissionControllerTest, EventSubmissionImageStoreTest, EventSubmissionValidationTest |
| FR-EVENT-14 | 구현됨 | EventSubmissionCommitBoundaryTest, EventSubmissionControllerTest |
| FR-EVENT-15 | 계획 | (없음) |
| FR-EVENT-16 | 구현됨 | EventOccurrenceRepositoryTest, OrgEventControllerTest, OrgEventQueryServiceTest |
| FR-ROUTE-01 | 진행 중 | AnonymousReadAccessHttpTest, RouteCandidateCollectorTest, RouteControllerTest, RouteIntentClientTest, RouteRecommendServiceTest |
Expand Down Expand Up @@ -278,8 +278,8 @@
| FR-AUTH-11 | 구현됨 | AuthControllerTest, AuthServiceTest |
| FR-AUTH-12 | 계획 | (없음) |
| FR-AUTH-13 | 구현됨 | AdminOrgAccountControllerTest, AdminOrgAccountRequestControllerTest, AuthControllerTest, AuthServiceTest, InitialPasswordResendConcurrencyTest, OrgAccountApprovalConcurrencyTest, OrgAccountRequestControllerTest, OrgAccountRequestPersistenceTest |
| FR-AUTH-14 | 구현됨 | JwtTokenProviderTest, OidcLoginServiceTest, OrgAccountControllerTest, OrgAuthorizationTest, PasswordControllerTest, UserProfileIntegrationTest, UserRoleOrgPersistenceTest |
| FR-AUTH-15 | 구현됨 | AdminOrgAccountControllerTest, OrgAccountControllerTest, OrgAccountSecurityPersistenceTest, PasswordControllerTest |
| FR-AUTH-14 | 구현됨 | EventSubmissionAuthorizationTest, JwtTokenProviderTest, OidcLoginServiceTest, OrgAccountControllerTest, OrgAuthorizationTest, PasswordControllerTest, UserProfileIntegrationTest, UserRoleOrgPersistenceTest |
| FR-AUTH-15 | 구현됨 | AdminOrgAccountControllerTest, EventSubmissionAuthorizationTest, OrgAccountControllerTest, OrgAccountSecurityPersistenceTest, PasswordControllerTest |
| FR-AUTH-16 | 구현됨 | InitialPasswordResendConcurrencyTest, JwtTokenProviderTest, MailSenderConfigurationTest, PasswordControllerTest, PasswordResetInvalidationFailureTest, RedisPasswordResetTokenStoreTest, RefreshTokenServiceTest |
| FR-USER-01 | 구현됨 | UserProfileControllerTest, UserProfileIntegrationTest |
| FR-USER-02 | 구현됨 | UserProfileControllerTest, UserProfileIntegrationTest |
Expand Down Expand Up @@ -322,8 +322,6 @@
- FR-EVENT-03 (폐기됨)
- FR-EVENT-04 (폐기됨)
- FR-EVENT-05 (폐기됨)
- FR-EVENT-13 (계획)
- FR-EVENT-14 (계획)
- FR-EVENT-15 (계획)
- FR-ROUTE-15 (계획)
- FR-NOTI-12 (계획)
Expand Down
Loading
Loading