Repository navigation
refactor(afternote): EditableMemorialVideo 의 네 상태를 sealed 로 봉인한다 — nullable 두 칸의 if/?: 를 exhaustive when 으로 (#1597 후속) #1901
Copy link
Copy link
Closed
Labels
area:afternote애프터노트 기능 영역애프터노트 기능 영역refactor동작 변경 없이 코드 구조·책임·경계를 개선하는 작업동작 변경 없이 코드 구조·책임·경계를 개선하는 작업
Description
작업 유형
refactor — 동작 변경 없는 구조·책임·경계 개선
주 담당 모듈
afternote — 애프터노트
개요
EditableMemorialVideo(#1561, PR #1565)는 nullable 칸 두 개(서버 기준값 persisted 와 미저장 교체분 selection)로 네 조합(빈 상태 · 서버만 · 선택만 · 교체 중)을 표현하고, 규칙은 파생값(displayed = selection ?: persisted, canRemove)과 연산 여섯 개의 if/?:/when 이 닫는다. 네 조합이 전부 합법이라 지금 "표현 불가능해야 할 상태" 는 없다. 그래서 sealed 로 바꿔도 상태 집합은 그대로 넷이고, 얻는 것은 표현의 명시성이다.
기준 리비전: 9d9b69e9c9244a274158b4ae958e98771f46a6e7 (PR #1620 head)
EditableMemorialVideo.kt: 값 객체 전체. 생성자·copy·componentN이 private 이라 두 칸은 밖으로 새지 않는다.
왜 sealed 인가
- 다섯 번째 상태가 들어올 때의 exhaustive 보장. 업로드 대기·실패 같은 상태를 필드 하나 더 얹어 표현하면 지금 구조는 조용히 컴파일되고, 연산 넷(
withSelection·withSelectionThumbnail·withoutThumbnail·toMediaInput)과 파생값 둘(displayed·canRemove) 중 어느 것이 새 상태를 빠뜨렸는지 아무도 알려 주지 않는다. sealed 위의else없는when이면 여섯 곳이 전부 컴파일 에러로 드러난다(refactor(core): core:ui 의 when else 가 닫힌 타입의 새 분기를 삼킨다 — 3곳 #1771 과 같은 부류다. 그쪽은 이미 닫힌 타입에서else를 떼는 일이고, 여기는 타입을 먼저 닫아야 한다). - 가독성. 지금은 어느 조합인지를
selection != null/persisted != null로 읽어야 한다. 0905 에 feat(afternote): 수정 모드에서 서버 영정사진·추모 영상을 지울 수 있게 한다 (#1597) #1620 을 읽으면서 두 칸의 조합 표가 필요했다.
치르는 비용 (#1620 안에서 하지 않고 떼어 내는 이유)
- 연산 넷과 파생값 둘이 각각 한 줄에서 네 가지 분기로 늘어난다.
@Serializable sealed는 polymorphic 직렬화라 JSON 에 discriminator 가 붙는다. 이 값 객체는EditorFormSnapshot.memorialVideo로SavedStateHandle에 JSON 으로 들어가므로(formSnapshotJson,ignoreUnknownKeys·encodeDefaults), 프로세스 재생성 복원 경로를 다시 실측해야 한다. 옛 스냅샷({"persisted":…,"selection":…})은runCatching폴백으로 기본 폼이 되니 크래시는 아니다. wire 형태를 바꾸지 않으려면 nullable 두 칸을 그대로 쓰는 surrogate serializer 를 붙이는 길이 있다. 판단은 착수 시.- refactor(afternote): 추모 영상 편집 상태를 값 객체로 캡슐화한다 (#1561) #1565 가 막 착륙했고 feat(afternote): 수정 모드에서 서버 영정사진·추모 영상을 지울 수 있게 한다 (#1597) #1620 이 그 위에 열려 있다. feat(afternote): 수정 모드에서 서버 영정사진·추모 영상을 지울 수 있게 한다 (#1597) #1620 안에서 표현을 바꾸면 그 PR 범위가 넓어진다.
완료 조건
-
EditableMemorialVideo가 sealed 로 네 상태를 이름으로 갖는다:Empty,ServerOnly,SelectionOnly,Replaced(이름은 착수 시 확정). 두 칸은 지금처럼 클래스 밖으로 새지 않는다. - 연산 넷과 파생값 둘이
else없는 exhaustivewhen이다. - 외부 계약은 그대로다.
displayed,canRemove,toMediaInput(),withoutThumbnail()의 결과가 같고,EditableMemorialVideoTest의 단언이 케이스 구성 이외의 수정 없이 통과한다. -
SavedStateHandle왕복: 스냅샷을 JSON 으로 저장→복원했을 때 네 상태가 모두 같은 상태로 돌아온다는 테스트가 있다. wire 형태를 바꿨다면 그 사실을EditorFormSnapshotKDoc 에 적는다. -
:feature:afternote:presentation:testDebugUnitTest·ktlintCheck통과.
자식 이슈
No response
참고
- feat(afternote): 수정 모드에서 서버 영정사진·추모 영상을 지울 수 있게 한다 — BE 삭제 계약 확장 반영 #1597(PR feat(afternote): 수정 모드에서 서버 영정사진·추모 영상을 지울 수 있게 한다 (#1597) #1620) 이 먼저 머지된다. 같은 파일의
canRemove·toMediaInput()을 이 이슈가 다시 만지므로blocked_by로 건다. - refactor(afternote): 에디터 미디어 삭제 진입점을 set*(null) 에서 떼고 미디어 콜백을 non-null 로 조인다 (#1561 후속) #1717 은 값 객체 위의 호출부 이름(
removeMemorialVideo())을 바꾸는 이슈다. 이 이슈는 값 객체 안의 표현을 바꾼다. 파일이 갈려 순서 제약은 두지 않는다. - 착수 시점은 feat(afternote): 수정 모드에서 서버 영정사진·추모 영상을 지울 수 있게 한다 (#1597) #1620 머지 뒤다. 다섯 번째 상태가 실제로 필요해지는 시점이 오면 그 작업보다 먼저 한다.
- feat(afternote): 수정 모드에서 서버 영정사진·추모 영상을 지울 수 있게 한다 (#1597) #1620 리뷰 중(0905) 삭제가 두 층을 함께 비우는 것으로 바뀌어
removeDisplayed와discardSelection이 사라졌다. 되돌리기가 없으니 두 층을 따로 들 이유는 저장 출처 구분뿐이다. 교체 중 상태에서 서버 기준값은 표시에도 저장에도 쓰이지 않으므로, sealed 로 갈 때 네 상태가 아니라Remote / Local / None세 상태 한 칸으로 줄일 수 있는지를 먼저 본다.
현재 선행 작업과 범위
사진 #1902는 PR #1971에서 처음부터 sealed 값 객체로 구현한다. 사진의 서버 기준값은 이탈 가드·기존 스냅샷 동일성 보존에 쓰이므로 유지한다. 이 이슈는 영상 값 객체 내부 표현과 호환성 검증에 집중하고, 사진이나 편집기 MVI의 외부 호출 계약을 다시 설계하지 않는다. 영상에서 세 상태로 축소할지는 영상의 표시·저장뿐 아니라 이탈 가드·SavedState 왕복까지 같은지 확인한 뒤 판단한다.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:afternote애프터노트 기능 영역애프터노트 기능 영역refactor동작 변경 없이 코드 구조·책임·경계를 개선하는 작업동작 변경 없이 코드 구조·책임·경계를 개선하는 작업
Activity