Skip to content

위시 읽기를 item_id 로 옮기고 참조를 기다리는 행으로 확정하며 해소 통지를 표시값 규칙으로 판정한다 (2단계) - #1055

Merged
m-a-king merged 2 commits into
devfrom
refactor/1051-wish-item-ref-stage2
Sep 8, 2026
Merged

위시 읽기를 item_id 로 옮기고 참조를 기다리는 행으로 확정하며 해소 통지를 표시값 규칙으로 판정한다 (2단계)#1055
m-a-king merged 2 commits into
devfrom
refactor/1051-wish-item-ref-stage2

Conversation

@m-a-king

@m-a-king m-a-king commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Situation

Task

  • 위시 읽기를 item_id 로 전환하고 NOT NULL 로 올린다.
  • 위시 참조를 "기다리는 행" 으로 확정한다.
  • 알림 수신자 판정이 카드 표시값과 같은 함수를 쓰게 한다.

Action

스키마

마이그레이션 내용
wishes.item_id NOT NULL 1단계 배포 창에 생긴 NULL 을 snapshot_id 조인으로 재백필한 뒤 승격. 현재 컨테이너(1단계 코드)가 이미 dual write 라 배포 창에서 옛 컨테이너의 INSERT 가 깨지지 않는다

위시 읽기 전환

경로
목록·단건 포인터 행을 읽어 itemId 를 얻음 wish.itemId 로 상품과 표시값을 바로 질의
수기 base·메모·새로고침 포인터 행 경유 wish.itemId 직접
출전 소유 체크·메모 조회 스냅샷 theta join w.itemId 직접
  • Wish.snapshotIdwaitingSnapshotId, swapSnapshotwaitFor 가 됐다. 본인의 등록·새로고침·합류·수기로만 움직이는 "기다리는 행" 이며 화면값이 아니다. 컬럼명은 rename 이 3단계 배포를 요구해 snapshot_id 그대로 둔다.
  • 표시값 질의는 DisplayCard(itemId, owner, waitingOn) 다. 위시는 displayCard() 확장으로 묻고, 출전은 pin 에서 상품을 읽는 DisplayCard.waitingOn(pin, owner) 로 같은 모양을 쓴다. 결과는 카드를 키로 한 맵이라 pin 이 겹치는 카드도 안전하다.

해소 통지를 표시값 규칙으로

  • 판정은 도메인 ItemVersions.recovers(viewer, waitingOn, snapshotId) 다. 그 버전이 생기던 순간(그보다 앞선 버전들)의 표시값이 실패·미완이었고, 그 버전을 넣으면 표시값이 그 버전이 되는 카드의 주인이 수신자다. 카드와 같은 displayFor 를 쓰므로 알림과 화면이 어긋날 수 없다.
  • "그 버전을 뺀 집합" 이 아니라 접두 상태로 판정하는 이유: 성공 버전이 둘 쌓인 채 앞 버전 이벤트를 처리하면 집합 비교는 뒤 버전이 앞 버전을 가려 아무 알림도 안 나간다(리뷰 발견).
  • 적재는 표시값과 같은 경로(ItemDisplayService.versionsContaining)를 탄다. 상품은 이벤트의 itemId 가 아니라 버전 행에서 되짚으므로, 정체성 병합이 파싱 완료와 겹쳐도 예외·유실이 없다.
  • 후보를 상태로 SQL 선필터하지 않는다. 내 INCOMPLETE 가 기다리는 행보다 새로우면 카드가 미완성이 되는데 기다리는 행의 상태만 보는 필터는 그 카드를 놓친다. 출전은 대기실(PENDING)만 후보다. 시작된 토너먼트는 pin 을 박제해 읽어 채워질 카드가 없다.
  • 이 버전을 기다리는 카드는 완료·새로고침 완료 알림을 받으므로 제외한다(두 알림은 배타적).

그 외

  • 표시값 배치 결과는 카드를 키로 한 맵이다. 호출부가 순서에 기대지 않고 자기 카드로 찾는다. 출전 판정 맵은 위시의 상품을 키로 만들고 키 커버리지로 검사한다.
  • editedByMe 는 도메인 술어(isManual·isOwnedBy)로 판정한다. 수기 팩토리 인자는 createdBy 하나로 모았고 edited_by 는 같은 값을 함께 적는다(제거는 3단계).
  • 메트릭 SQL 의 위시-스냅샷 조인을 wishes.item_id 직접 참조로 바꿨다. CLAUDE.md 도메인 용어의 "wish 는 활성 버전을 가리킨다" 를 현재 모델로 고쳤다.
  • raw SQL 로 위시를 넣던 테스트 두 곳에 item_id 를 채웠다.

배포 조건

Result

  • 해소 통지 판정은 ItemVersionsTest 단위 9건(접두 상태·기다리는 카드 배제·내 수기 우선·연속 성공·뒤늦은 대기)이 망라하고, 통합 테스트에 옛 규칙과 갈리는 케이스 두 건(내 수기값이 떠 있던 카드 제외, 시작된 토너먼트 제외와 대기실의 내 INCOMPLETE 포함)을 더했다.
  • 리뷰에서 지적된 병합 경합(등록이 진 상품의 진행 중 버전에 합류하는 동안 병합)은 등록이 item 행 락을 먼저 잡아 병합의 soft delete 가 그 뒤로 밀리고, 위시 참조 이관은 그 뒤 같은 트랜잭션에서 돌아 병합 후 상태를 본다. 별도 처리 없음.
  • 후속 3단계: edited_by 컬럼 제거. snapshot_id 는 rename 이익이 배포 비용에 못 미쳐 유지한다.
  • 보류: 상품별 버전 전체 조회의 상한(이슈 후속 최적화).

연관 이슈

…정한다 (2단계)

- 1단계(#1054)가 깐 wishes.item_id 를 배포 창의 NULL 재백필 뒤 NOT NULL 로 올리고, 목록·단건·수기 base·메모·새로고침·출전 소유 체크·메모 조회가 위시의 item_id 로 상품에 간다. 스냅샷 theta join 세 개가 사라진다
- Wish.snapshotId 를 waitingSnapshotId / waitFor 로 바꿔 "보는 값" 이 아니라 "기다리는 행" 임을 코드가 말하게 했다. 컬럼명(snapshot_id)은 rename 이 3단계 배포를 요구해 그대로 둔다
- 표시값 질의를 DisplayCard(itemId, owner, waitingOn) 로 바꿨다. 위시는 포인터 행을 따로 읽지 않고 카드 셋으로 묻고, 출전은 pin 에서 상품을 읽는 waitingOn 팩토리로 같은 모양을 쓴다. 결과는 입력 순서의 리스트라 pin 이 겹치는 카드도 안전하다
- 해소 통지(#1028) 수신자를 "미완성 버전을 가리키던 사람" 에서 "화면값이 실패·미완이었다가 이 버전으로 채워지는 카드의 주인" 으로 바꿨다. 카드 표시값 규칙(ItemVersions) 그대로 이 버전을 뺀 표시값과 넣은 표시값을 비교하므로 알림과 화면이 어긋날 수 없다. 상태 필터 쿼리 두 개는 상품의 카드 전부를 읽는 조회로 대체
- editedByMe 를 created_by 로 읽는다. edited_by 제거는 3단계
- raw SQL 로 위시를 넣던 테스트 두 곳에 item_id 를 채웠다(NOT NULL 회귀)

Claude-Session: https://claude.ai/code/session_01HnKAcdnoziL8hdVr9xinFb
@m-a-king m-a-king added the refactor 구조 개선, 외부 동작 불변 label Sep 7, 2026
@m-a-king m-a-king self-assigned this Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Discord 스레드 연동용 메타데이터입니다. discord-pr-bot 워크플로가 자동 생성하며, 수정·삭제하면 PR 과 Discord 알림 연동이 끊깁니다.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Advanced

Run ID: 0dd42ad4-347b-4c54-bef7-6304302776ac


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

- 코드 리뷰 반영. 해소 통지 판정이 서비스 안 지역 함수로 남아 있던 것을 ItemVersions.recovers 로 옮기고, 판정 기준을 "그 버전을 뺀 집합" 이 아니라 "그 버전이 생기던 순간의 접두 상태" 로 바꿨다. 성공 버전이 둘 쌓인 채 앞 버전 이벤트를 처리하면 집합 비교는 앞 버전을 뒤 버전이 가려 아무 알림도 안 나갔다. 그 버전 뒤에 기다리기 시작한 카드도 제외한다. 단위 9건 추가
- 적재도 표시값과 같은 경로를 탄다(ItemDisplayService.versionsContaining). 상품은 이벤트의 itemId 가 아니라 버전 행에서 되짚어, 정체성 병합이 파싱 완료와 겹쳐 버전이 이긴 상품으로 옮겨져도 예외·유실 없이 판정한다. 알림 해석기가 스냅샷 저장소를 직접 쓰지 않게 돼 동결 목록에서 뺐다
- 후보를 상태로 SQL 선필터하지 않는다. 내 INCOMPLETE 가 기다리는 행(READY)보다 새로우면 규칙 2 로 카드가 미완성이 되므로 기다리는 행의 상태만 보는 필터는 그 카드를 놓친다. 출전은 파생을 타는 대기실(PENDING)만 후보다 - 시작된 토너먼트는 pin 을 박제해 읽어 채워질 카드가 없다. 두 케이스를 통합 테스트로 고정
- 표시값 배치 결과를 카드 키 맵으로 바꿔 호출부가 순서에 기대지 않게 했다. 출전 판정 맵은 위시의 상품을 키로 만들고 키 커버리지로 검사한다
- 메모만 수정하는 경로의 표시값 이중 파생, displayedSnapshotsOf 의 도달 불가 fallback, 한 줄 위임 displayOf, 죽은 projection data class 2개를 제거했다. TournamentItemCardView 는 라우팅 뷰를 상속한다
- 수기 팩토리 인자를 createdBy 로 바꿔 쓰는 곳을 하나로 모았다(edited_by 는 같은 값을 함께 적고 3단계에서 제거). editedByMe 는 도메인 술어(isManual·isOwnedBy)로 판정한다
- 메트릭 SQL 의 위시-스냅샷 조인을 wishes.item_id 직접 참조로 바꾸고 옛 주석을 고쳤다. CLAUDE.md 도메인 용어의 "wish 는 활성 버전을 가리킨다" 를 현재 모델로 고쳤다

Claude-Session: https://claude.ai/code/session_01HnKAcdnoziL8hdVr9xinFb
@m-a-king
m-a-king merged commit 833d7b6 into dev Sep 8, 2026
6 checks passed
@m-a-king
m-a-king deleted the refactor/1051-wish-item-ref-stage2 branch September 8, 2026 05:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor 구조 개선, 외부 동작 불변

Projects

None yet

Development

Successfully merging this pull request may close these issues.

위시 참조를 아이템으로 바꾸고 화면값을 행에서 계산한다 (스냅샷 created_by 통합)

1 participant