Skip to content

사용자 행동에 관여하는 스냅샷 읽기를 전부 표시값으로 정합 - #1007

Merged
m-a-king merged 2 commits into
devfrom
refactor/snapshot-read-path
Sep 4, 2026
Merged

사용자 행동에 관여하는 스냅샷 읽기를 전부 표시값으로 정합#1007
m-a-king merged 2 commits into
devfrom
refactor/snapshot-read-path

Conversation

@m-a-king

@m-a-king m-a-king commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Situation

  • 담기 게이트가 포인터가 아니라 표시값을 보게 한다 #1006(담기 게이트)이 잡은 어긋남은 한 지점이 아니라 축이었다. 같은 item 에 값이 둘(포인터 / 표시값)인데 어느 쪽을 봐야 하는지가 코드에 안 드러나고, 타입이 같아 컴파일러도, 단일 사용자 테스트도 못 잡는다
  • 원본 저장소(ItemSnapshotRepository) 소비 23군데를 전수로 열어 갈래를 갈랐다
갈래 표시값으로 바꾸면 판정
쓰기(PENDING·MANUAL 적재) 개념이 없음 원본 유지
정체성(itemId 만) 답이 같음 원본 유지
버전 자체(알림·가격 이력) 틀린 답 원본 유지
표시값 파생의 입력 통로 안쪽 유지
수기 수정 base 맞는 답 이 PR 이 고침
새로고침 판정 맞는 답 이 PR 이 고침

Task

기준 한 줄: 사용자가 화면에서 보고 행동한 결과에 관여하는 읽기는 표시값이어야 한다. 화면은 표시값(#858, 최신 기계 READY 우선)을 그리는데 판정·병합만 포인터를 보면, 사용자가 따를 수 없는 안내가 나간다.

Action

수기 수정 base 를 표시값으로 - base 선택은 persistence 가 소유

수정 요청은 전 필드가 선택이라 안 보낸 필드는 base 에서 병합된다. base 가 포인터면: 내 포인터는 가격 없는 미완성, 남이 같은 링크를 담아 성공, 카드엔 89,000원이 떠 있는 상태에서 이름만 고치면 "가격이 필요하다"(400) 로 튕겼다. 채울 칸은 화면에 없다.

  • 토너먼트의 "pin 을 base 로" 주석은 카드 표시값을 파생 조회로: 최신 기계 READY 우선 + start 시점 박제 #858 이전 유물이었다. 수정은 PENDING 전용이고 PENDING 카드는 표시값을 그리므로, "이 카드가 보던 버전 위에 수정"이라는 그 주석의 의도 자체가 지금은 표시값을 가리킨다. 시작 후 격리는 start 의 repin 이 진다
  • 내가 손으로 고친 값이 최신이면 표시값 = 내 수정본이라 연쇄 없음
  • base 선택은 각 persistence 서비스의 editBasisOf 한 곳이 소유한다. dry-run(validateManualEdit)과 실제 저장(manualEdit)이 같은 코드를 타므로 "둘이 같은 base" 가 주석이 아니라 구조로 지켜진다
  • dry-run 은 이미지가 있을 때만 부른다. 유일한 존재 이유가 S3 orphan 방지라, 업로드 없는 수정(지배적 케이스)은 락 안 최종 판정 하나로 충분하다 - 예외·응답 동일, 표시값 파생 쿼리 요청당 2회에서 0회
  • 새로고침 판정은 단락 평가(포인터 FAILED 그리고 표시값 FAILED) - 포인터가 FAILED 가 아니면 표시값도 FAILED 일 수 없어, READY 재추출(지배 경로)의 쿼리 1회가 결과 무관이었다

새로고침 판정을 표시값으로

포인터가 FAILED 여도 남의 성공(기계 READY)이 있으면 item 이 추출 가능하다는 증거다. 막을 이유가 없어 허용하고, 표시값까지 FAILED(아무도 성공 못함)일 때만 보정 유도 409 가 남는다.

  • 기존 주석의 recover-refresh 상호배제 근거를 실물로 재검증했다: 보정(recover)은 manualEdit 위임이고 두 경로 모두 wish 행 락을 잡아 직렬화된다. 상태 게이트가 지던 몫이 아니었다

소비자 집합 동결 (기계 강제)

SnapshotAccessConventionTest - item 패키지 밖에서 원본 저장소를 직접 쓰는 클래스(현재 8개)를 갈래 사유와 함께 경로 접미사 키로 동결한다(동명 파일 무단 통과 방지, 스캔은 companion 공유로 1회). 새 소비 클래스는 목록에 자기 갈래를 적어야 하고, 그 순간 규칙을 읽게 된다. 역방향 검사(소비를 멈춘 항목은 목록에서 제거)로 목록이 낡는 것도 막는다. 갈래 판정 자체는 사람 리뷰 몫이다(오탐 없는 기계 판정 불가).

검토 후 채택하지 않은 것:

판단
원본 접근 전면 차단 22/23 군데가 정당한 원본 사용이라 틀린 규칙
포인터/표시값 타입 분리 수단이 무겁고, 동결 + 표시값 경유로 같은 목적 달성

Result

  • 네거티브 컨트롤 실측: 세 수정을 일시 되돌리면 정확히 새 테스트 3개만 실패한다 - 테스트 3쌍(각각 대조군 포함)이 각 수정을 하나씩 물고 있다
  • 새로고침 409(WISH-008)의 계약은 그대로다 - code 추가·제거 없음, 발생 조건만 "포인터 FAILED"에서 "표시값까지 FAILED"로 좁아진다
  • 위시 수정 시딩은 실제 공유 경로(남이 같은 링크 persist -> 같은 item 에 새 버전)를 그대로 태워, 픽스처가 아니라 실물 흐름으로 재현했다
  • dry-run 위임으로 TournamentItemService 의 주입 3개가 사라졌고, 동결 목록의 stale 검사가 그 제거를 실제로 강제했다 - 장치가 첫 변경에서 바로 일했다
  • 포인터와 표시값 중 무엇을 봐야 하는지를 코드에 드러낸다 #1005 를 닫는다. "통로를 안 거치는 길을 없앤다"의 착지: 판정·병합은 표시값 경유(담기 게이트가 포인터가 아니라 표시값을 보게 한다 #1006 + 이 PR), 정당한 원본 소비는 동결 목록이 갈래째 문서화

연관 이슈

- #1006(담기 게이트)과 같은 축의 어긋남을 전수 조사했다. 원본 저장소 소비
  23군데를 갈래로 가르니 쓰기·정체성·버전 자체(알림·이력)·표시값 입력은 원본이
  맞고, 사용자가 화면에서 보고 행동한 결과에 관여하는 읽기만 표시값이어야 했다
- 어긋난 곳 셋을 고쳤다
  - 수기 수정 base(위시·토너먼트): 카드는 표시값을 그리는데 병합 base 만
    포인터라, 남이 채운 가격이 화면에 떠 있는데 이름만 고치면 "가격이
    필요하다"(400)로 튕겼다. base 를 표시값으로 바꿔 카드에 보인 값 위에
    수정이 얹힌다. dry-run(업로드 전 검증)도 같은 base 로 맞췄다
  - 새로고침 판정: 포인터가 FAILED 여도 남의 성공(기계 READY)이 있으면 item 이
    추출 가능하다는 증거라 막을 이유가 없다. 표시값까지 FAILED 일 때만 보정
    유도(409)가 남는다. recover 와의 직렬화는 원래 wish 행 락이 지고 있었다 -
    기존 주석의 "상태로 갈려 침범하지 않는다"는 근거를 실물로 재검증한 결과다
- 토너먼트 수정의 "pin 을 base 로" 주석은 #858 이전 유물이었다. 수정은 PENDING
  전용이고 PENDING 카드는 표시값을 그리므로, "이 카드가 보던 버전 위에 수정"
  이라는 그 주석의 의도 자체가 지금은 표시값을 가리킨다
- SnapshotAccessConventionTest 로 원본 소비자 집합을 동결했다. 포인터와 표시값이
  같은 타입이라 컴파일러가 못 잡고 단일 사용자 테스트에선 둘이 같아 테스트도 못
  잡는 함정이라, 새 소비 클래스가 목록에 자기 갈래를 적게 강제해 규칙을 읽게
  한다. 갈래 판정 자체는 사람 리뷰 몫이다
- 회귀 테스트 3쌍(위시 수정·토너먼트 수정·새로고침, 각각 대조군 포함). 세 수정을
  일시 되돌리면 정확히 새 테스트 3개만 실패하는 것을 실측했다
@m-a-king m-a-king added the fix 외부 가시적 결함 수정 label Aug 29, 2026
@m-a-king m-a-king self-assigned this Aug 29, 2026
@github-actions

Copy link
Copy Markdown

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

@coderabbitai

coderabbitai Bot commented Aug 29, 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: Pro Plus

Run ID: f346da85-3625-4e5f-a321-b93a33f54f5c


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.

/simplify 4각(재사용·단순화·효율·깊이) 리뷰 반영.

- dry-run(업로드 전 검증)을 각 persistence 서비스의 validateManualEdit 으로
  이동해 병합 base 선택(editBasisOf)이 실제 저장과 같은 코드를 탄다. 이 PR 이
  "dry-run 과 실저장은 같은 base" 를 우연에서 규칙으로 승격시켰는데, 그 규칙을
  주석 두 벌이 지키는 구조였다 - 한 벌이면 갈라지는 것 자체가 불가능하다
- dry-run 은 이미지가 있을 때만 부른다. 유일한 존재 이유가 S3 orphan 방지라,
  업로드 없는 수정(지배적 케이스)은 manualEdit(락 안) 최종 판정 하나로 충분하다.
  예외·응답 동일, 수기 수정 요청당 표시값 파생 쿼리 2회 -> 이미지 없으면 0회
- 새로고침 판정을 단락 평가로: 포인터가 FAILED 가 아니면 표시값도 FAILED 일 수
  없어(파생 후보가 READY 뿐) 지배적 READY 경로의 쿼리 1회가 결과 무관 낭비였다
- TournamentItemService 의 사전 권한 검사·스냅샷 의존이 위임으로 사라져 주입
  3개(스냅샷 저장소·표시값 서비스·아이템 저장소)를 걷어냈다. 원본 소비자가 9곳
  에서 8곳으로 줄었고 동결 목록의 stale 검사가 이 제거를 실제로 강제했다
- SnapshotAccessConventionTest: JUnit 이 테스트마다 인스턴스를 새로 만들어
  인스턴스 lazy 로는 1회 스캔이 안 됐다(전체 트리 2회 walk). companion 으로
  올려 1회로 만들고, 두 테스트가 각자 돌리던 같은 필터를 공유 집합으로 합쳤다.
  파일명 키는 동명 파일이 생기면 무단 통과라 경로 접미사 키로 바꿨다
- 테스트 시딩 중복 제거: 위시는 기존 seedReadyWish 에 extractionMethod 파라미터
  (기본 null 이라 기존 호출 무변화)를 더해 인라인 3블록을 대체, 새로고침은
  seedFailedWish 추출, 토너먼트는 saveTournamentItemFor 재사용. 그 과정에서
  saveTournamentItemFor 의 extractedAt 규칙이 saveSnapshot 과 어긋나 있던 것
  (READY 만 vs READY·INCOMPLETE)도 정정했다
@m-a-king
m-a-king merged commit d5fed54 into dev Sep 4, 2026
13 checks passed
@m-a-king
m-a-king deleted the refactor/snapshot-read-path branch September 4, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix 외부 가시적 결함 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

포인터와 표시값 중 무엇을 봐야 하는지를 코드에 드러낸다

1 participant