Skip to content

[Fix] 점수 행 없는 회원 활동점수 부여 시 silent no-op → 명시적 실패로 수정 - #350

Open
GOOHAESEUNG wants to merge 1 commit into
devfrom
fix/activity-record-missing-score
Open

[Fix] 점수 행 없는 회원 활동점수 부여 시 silent no-op → 명시적 실패로 수정#350
GOOHAESEUNG wants to merge 1 commit into
devfrom
fix/activity-record-missing-score

Conversation

@GOOHAESEUNG

Copy link
Copy Markdown
Member

배경

POST /v1/manager/activity-records(또는 /v1/admin/...)로 활동점수를 부여할 때, 대상 회원의 PersonalActivityScore 행이 존재하지 않으면 아무 기록도 생성하지 않고 201 성공을 반환하는 silent no-op 버그가 있었습니다.

  • 원인: ActivityRecordUsecasegetPersonalScoreListByIdsForUpdate(memberIdList)기존 점수 행만 조회해 루프를 돌기 때문에, 행이 없는 회원은 조회 결과에서 빠져 조용히 건너뛰어짐
  • 점수 행은 회원 승인 플로우에서 생성(YB 100/OB 50)되므로 정상 플로우에서는 드물지만, 마이그레이션·수동 삽입·과거 데이터 정합성 문제가 있으면 관리자는 점수를 줬다고 믿는데 실제로는 미반영되는 무결성 문제
  • 같은 버그가 V2 개인 경로뿐 아니라 V2 팀 경로, V1 createActivityRecordList에도 동일하게 존재

변경 사항

수정 — PersonalScoreGetService

갱신용 행 잠금 조회 2개(getPersonalScoreListByIdsForUpdate, getTeamScoreListByIdsForUpdate)에서 조회 결과 수가 요청 대상 수(중복 제거 기준)와 다르면 PersonalScoreNotFoundException(400) 을 던집니다.

  • 이 위치 한 곳으로 V1·V2, 개인·팀 네 경로가 모두 커버됨
  • 부수 효과: findScoreByRecord의 팀 점수 get(0)이 행 부재 시 IndexOutOfBoundsException(500)을 내던 것도 같은 예외(400)로 정리됨

수정 방향 선택 근거 (옵션 1: 명시적 실패)

대안이었던 자가 치유(없는 행을 PersonalActivityScore.from(Member)로 생성 후 진행)는 배제했습니다 — 기본 점수 부여(YB 100/OB 50)는 회원 승인 플로우의 책임이고, 점수 행 부재라는 정합성 이상 신호를 조용히 덮게 되기 때문입니다. 자가 치유가 낫다고 판단되면 리뷰에서 논의 후 전환 가능합니다.

재현 회귀 테스트 — ActivityRecordMissingScoreRegressionTest (4건)

수정 전 코드에서 4건 모두 실패(예외 없이 통과)함을 확인 후 수정했습니다.

  • V2 개인 — 점수 행 없는 회원 부여 시 예외
  • V2 개인 — 일부 회원만 행이 없어도 부분 반영 없이 예외
  • V2 팀 — 점수 행 없는 팀 부여 시 예외
  • V1 다수 부여 — 행 없는 회원이 섞여 있으면 예외

mock 리포지토리 + 실제 PersonalScoreGetService 조합으로 검증 (H2의 ActivityRecord DDL 한계로 기존 회귀 테스트와 동일한 방식).

검증

  • 신규 회귀 테스트 4건 통과
  • 전체 테스트 통과 (ArchUnit·E2E 포함, 유일한 실패 RefreshTokenService는 로컬 Docker 미기동으로 인한 Testcontainers 환경 문제 — 본 변경과 무관)

🤖 Generated with Claude Code

활동점수 부여(V1·V2, 개인·팀)가 기존 PersonalActivityScore 행만 조회해
루프를 돌기 때문에, 행이 없는 대상은 조회 결과에서 빠져 기록 생성 없이
201 성공을 반환했다. 관리자는 점수를 부여했다고 믿지만 실제로는 아무것도
반영되지 않는 무결성 문제다.

갱신용 행 잠금 조회(getPersonalScoreListByIdsForUpdate,
getTeamScoreListByIdsForUpdate)에서 조회 결과 수가 요청 대상 수와 다르면
PersonalScoreNotFoundException으로 명확히 실패시킨다. 재현 회귀 테스트 4건 추가.
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