Skip to content

MSG-499 feat: 행사 운영자 계정을 공개 접수와 관리자 검토로 발급한다 - #242

Merged
s13121312 merged 9 commits into
developfrom
feature/MSG-499-org-account-issue
Aug 29, 2026
Merged

MSG-499 feat: 행사 운영자 계정을 공개 접수와 관리자 검토로 발급한다#242
s13121312 merged 9 commits into
developfrom
feature/MSG-499-org-account-issue

Conversation

@s13121312

@s13121312 s13121312 commented Aug 29, 2026

Copy link
Copy Markdown
Member

🎫 관련 티켓

작업 내용

  • 계정이 없는 행사 운영자가 비로그인 공개 폼으로 발급을 신청하는 API를 열었습니다 (POST /api/org-account-requests, permitAll — 필터 skip 목록은 건드리지 않음)
  • 관리자 발급 흐름 7종을 만들었습니다: 요청 큐(탭 건수)·상세 조회, 승인(계정 생성 + 초기 비밀번호 생성·메일 발송), 반려(사유 필수), 공문 선행 건 직접 발급, 초기 비밀번호 재발송, 발급 계정 목록(이메일 검색 포함)
  • org_account_requests 테이블을 신설했습니다 (Flyway V48 — V47은 병렬 진행 중인 MSG-498이 공유 로컬 DB에 먼저 적용해 재번호, 아래 리뷰 포인트)
  • 초기 비밀번호 평문은 응답·서버 로그·DB 어디에도 남지 않습니다 (로그 캡처 테스트로 고정, 예외 스택 경유 유출도 실측 차단)
  • 비밀번호를 쓰는 세 경로(재발송·본인 변경·재설정)를 같은 사용자 행 잠금으로 직렬화했습니다 (MSG-497 PasswordService 조회 2곳을 잠금 조회로 교체)
  • 문서: 스펙 docs/spec/MSG-499.md(작업 로그 포함), PRD 서버 재료 목록 갱신, SRS FR-AUTH-13 구현됨·rtm 재생성(검증 공백 0건), status.md 갱신
  • 테스트 신규 48건, 전체 스위트 green (Codex 교차 리뷰: 스펙 4라운드 + 구현 2라운드 수렴)

🤔 고민한 내용

  • "발송 성공"의 의미를 SES 접수까지로 확정: SES는 배달 확인을 동기로 주지 않아(MessageId = 접수), 오타 주소 복구는 재발송 API가 맡습니다. 배달 확인 대기 설계는 불가능해 기각했습니다
  • 접수 UPSERT(마지막 접수 유효) 유지 vs DO NOTHING: 익명 덮어쓰기 변조가 가능하지만, DO NOTHING은 반대로 공격자 선점 행이 진짜 신청을 조용히 삼킵니다. UPSERT를 유지하고 신뢰 경계를 관리자 심사(공문·공식 채널 확인)로 명시했습니다. 검토-승인 사이 변조는 updatedAt 에코 대조(불일치 시 1426)로 막습니다
  • 비관적 락을 세 경로 전부에 건 이유: 재발송만 잠그면 changePassword·resetPassword의 잠금 없는 조회가 lost update로 새 해시를 덮습니다(한쪽 락으로는 못 막음). 실제 생성 SQL은 FOR UPDATE가 아니라 FOR NO KEY UPDATE로 실측됐고(Hibernate 6 PG 방언) 기존 FOR KEY SHARE 경로와 충돌하지 않습니다
  • 커밋 후 발송: 발송을 트랜잭션 안에 두면 SES 왕복이 커넥션을 점유하고, 발송 후 커밋 실패 시 "비밀번호는 나갔는데 계정이 없는" 역방향 구멍이 생깁니다. 커밋 후 발송 + 실패 시 emailSent: false + 재발송 복구로 정리했습니다

👀 리뷰 포인트

  • Flyway 머지 순서: 이 브랜치는 V48이고 V47은 MSG-498 몫으로 비워뒀습니다. 이 PR이 먼저 머지되면 498은 V49로 재번호해야 합니다 (Flyway가 out-of-order 기본 거부)
  • AdminOrgAccountItemResponseDto.orgName에 nullable을 표기했습니다 — MSG-496·497이 만든 기존 ORG 계정은 org_name이 없어서인데, 목록 필터가 그 계정들을 포함하는 게 맞는지 봐주세요
  • 발송 실패 ERROR 로그에서 예외 객체를 빼고 타입만 남겼습니다(스택 경유 평문 유출 차단). SES 거부 상세는 SesMailSender 쪽 로그가 갖는데, 진단 정보가 부족하다고 보이면 의견 주세요

@claude

claude Bot commented Aug 29, 2026

Copy link
Copy Markdown

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


리뷰 완료

  • 병렬 브랜치 경합 확인 (마이그레이션 번호, developCode 대역, Owner A/B 계약)
  • 스펙 대비 구현 대조 (docs/spec/MSG-499.md)
  • 실행 시 드러나는 결함 (트랜잭션, 동시성, 시간대, 인가)
  • PR 본문 주장 검증
  • 결과 정리

확인 범위: origin/develop 대비 마이그레이션 번호(V46까지, V47 공백 확인)·UserErrorCode 1421~1426 대역·SecurityConfig matcher 순서, docs/spec/MSG-499.md 전체 대 구현 8종 API, PasswordService/OrgAccountIssueService/OrgAccountRequestService의 트랜잭션·잠금 경계, 동시성 테스트 4종(InitialPasswordResendConcurrencyTest 등, 실제 스레드+CyclicBarrier로 실 DB 경합 재현 확인), UTC 코덱과 updatedAt 에코 대조 라운드트립.

발견 없음. 인라인 코멘트도 없음.

  • 마이그레이션 번호(V48)와 developCode 대역(1421~1426)은 origin/develop 최신(V46, 1420까지)과 실제로 충돌하지 않는다 — PR 본문이 밝힌 V47 공백(MSG-498 몫)은 지금 시점엔 실재 경합이 아니라 머지 순서에 따른 재번호 필요성일 뿐이다.
  • 스펙 문서와 구현이 API 8종·에러 코드·잠금 전략·발송 실패 격리까지 세부적으로 일치한다. 스펙에 없는 동작이나 스펙과 어긋나는 처리를 찾지 못했다.
  • 비밀번호 세 경로(재발송·변경·재설정)의 행 잠금 통일은 실 스레드 경합 테스트로 뒷받침되고, updatedAt 낙관적 검증 값의 UTC 코덱 왕복도 @SpringBootTest+@AutoConfigureMockMvc로 실 매퍼를 거쳐 검증된다.

리뷰 포인트로 남긴 AdminOrgAccountItemResponseDto.orgName nullable 건: 목록 필터가 role=ORG AND provider=LOCAL이라 MSG-496/497이 만든 기존 계정도 스펙상 포함 대상이 맞다(스펙 "목록 대상은 role=ORG AND provider=LOCAL 필터다" 문구에 예외 없음). API 8의 목적(재발송 대상 식별, 이메일 검색을 통한 크래시 복구 판정)에는 orgName 유무가 영향을 주지 않으므로 지금 그대로도 기능상 문제는 없어 보인다 — 다만 화면에서 그 계정들의 "기관명" 칸을 빈 값으로 보여줄지는 FE 판단으로 넘기면 될 것 같다.

@github-actions

Copy link
Copy Markdown

@s13121312
s13121312 merged commit 4d31767 into develop Aug 29, 2026
2 checks passed
@s13121312
s13121312 deleted the feature/MSG-499-org-account-issue branch August 29, 2026 08:17
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