작업 내용
공유 링크를 발급할 때 같은 대상 구성(시술기록 + 후보 스타일)을 가리키는 기존 활성 링크를 자동으로 REVOKED 처리한다. 결과적으로 "한 사용자, 한 대상 구성, 활성 링크 1개" 가 성립한다.
shares 에 target_hash 컬럼 추가 — 정렬된 record_id 목록과 saved_style_id 목록을 이어 붙인 문자열의 SHA-256
- 부분 유니크 인덱스로 DB 가 강제:
UNIQUE (user_id, target_hash) WHERE status = 'ACTIVE'
- 발급 시 같은
target_hash 의 활성 링크를 먼저 폐기하고 새 링크를 넣는다
리팩토링 이유
지금 ShareService.create() 에는 중복 검사도 개수 상한도 없다. 공유 버튼을 누를 때마다 새 링크가 생기고 이전 링크는 계속 살아있는다. 운영 데이터에서 시술기록 한 건에 활성 링크가 7 개까지 쌓인 사례가 있고, 이틀 만에 링크 28 개가 생성됐다.
살아있는 링크 수에 상한이 없다는 것이 문제의 핵심이다. 시술기록이 19 건이어도 활성 링크는 1000 개가 될 수 있고, 그 하나하나가 열려 있는 공개 URL 이다. 사용자는 공유를 다시 만들면서 이전 링크가 정리됐다고 여기지만 실제로는 전부 남아 있다.
공유를 다시 만드는 행위는 "이전 링크는 그만 쓰겠다" 는 의사표시로 보는 것이 자연스럽다. 그렇게 정의하면 살아있는 링크 수가 대상 구성의 가짓수로 유계가 된다.
토큰은 해시만 저장하므로(shares.token_hash) 기존 링크의 URL 을 다시 내려줄 수 없다. 따라서 "기존 링크 재사용" 이 아니라 "새로 발급 + 이전 것 폐기" 로 간다.
체크리스트
참고 자료
- 같은 기록을 서로 다른 링크로 여러 사람에게 동시에 주는 시나리오는 지원하지 않게 된다. 현재 화면에 그런 흐름이 없고, 오래된 링크가 계속 열려 있는 위험이 더 크다고 판단했다.
- 유효기간 자체에 상한이 없는 문제(
expires_in_days 에 큰 값을 넣으면 사실상 만료되지 않는다)는 이 작업의 범위가 아니다. 별도 이슈로 다룬다.
작업 내용
공유 링크를 발급할 때 같은 대상 구성(시술기록 + 후보 스타일)을 가리키는 기존 활성 링크를 자동으로
REVOKED처리한다. 결과적으로 "한 사용자, 한 대상 구성, 활성 링크 1개" 가 성립한다.shares에target_hash컬럼 추가 — 정렬된 record_id 목록과 saved_style_id 목록을 이어 붙인 문자열의 SHA-256UNIQUE (user_id, target_hash) WHERE status = 'ACTIVE'target_hash의 활성 링크를 먼저 폐기하고 새 링크를 넣는다리팩토링 이유
지금
ShareService.create()에는 중복 검사도 개수 상한도 없다. 공유 버튼을 누를 때마다 새 링크가 생기고 이전 링크는 계속 살아있는다. 운영 데이터에서 시술기록 한 건에 활성 링크가 7 개까지 쌓인 사례가 있고, 이틀 만에 링크 28 개가 생성됐다.살아있는 링크 수에 상한이 없다는 것이 문제의 핵심이다. 시술기록이 19 건이어도 활성 링크는 1000 개가 될 수 있고, 그 하나하나가 열려 있는 공개 URL 이다. 사용자는 공유를 다시 만들면서 이전 링크가 정리됐다고 여기지만 실제로는 전부 남아 있다.
공유를 다시 만드는 행위는 "이전 링크는 그만 쓰겠다" 는 의사표시로 보는 것이 자연스럽다. 그렇게 정의하면 살아있는 링크 수가 대상 구성의 가짓수로 유계가 된다.
토큰은 해시만 저장하므로(
shares.token_hash) 기존 링크의 URL 을 다시 내려줄 수 없다. 따라서 "기존 링크 재사용" 이 아니라 "새로 발급 + 이전 것 폐기" 로 간다.체크리스트
target_hash컬럼 추가 + 기존 행 백필참고 자료
expires_in_days에 큰 값을 넣으면 사실상 만료되지 않는다)는 이 작업의 범위가 아니다. 별도 이슈로 다룬다.