You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
changed the title [-]feat(afternote): 서버 임시저장 상세 지원 — 발행 상세와 nullable 도메인 분리[/-][+]feat(afternote): 서버 임시저장 상세·이어쓰기 지원 — draft 상태·부분 credentials 모델링[/+]on Aug 21, 2026
changed the title [-]feat(afternote): 서버 임시저장 상세·이어쓰기 지원 — draft 상태·부분 credentials 모델링[/-][+]서버 임시저장 상세·이어쓰기 지원 — draft 상태·부분 credentials 모델링[/+]on Aug 28, 2026
서버 계약 실측 (Afternote-BE origin/main, 2026-08-30) · 범위 좁힘
BE 실코드로 대조한 결과 서버 계약은 이 이슈가 요구하는 만큼 다 갖춰져 있다. 다만 이슈 본문과 어긋난 지점이 셋 있고, 남은 작업 다섯 항목 중 넷만 지금 닫을 수 있어 범위를 좁혔다.
1. 확인한 서버 계약
확인
근거 (BE origin/main)
draft 상태 실재·상세 스키마 분기
AfternotedetailResponse 가 sealed interface … permits Draft, Published, PublishedPlaylist, of(...) 가 if (draft) return new Draft(...). 엔티티는 Afternote.isDraft (@Column(name = "is_draft", nullable = false), 기본 false)
부분 입력 저장 허용
AfternoteValidator KDoc — “임시저장(isDraft=true)은 느슨, 정식 등록은 credentials/playlist 등 필수값을 서버에서 강제한다”. CredentialsCategoryValidation.validateCreate 가 if (!request.isDraftValue()) requireCredentials(request), PlaylistValidationStrategy 도 같은 모양으로 requirePlaylistSongs 를 건너뛴다
부분 credentials 의 와이어 모양
AfternoteService.buildCredentialDetailResponse 가 아이디·비밀번호를 각각 복호화한 뒤 accountId != null || accountPassword != null ? new CredentialsRequest(accountId, accountPassword) : null. CredentialsRelationSupport 가 두 값을 별개 레코드로 저장하므로 ①둘 다 없음 → credentials 자체 부재 ②하나만 있음 → 나머지 칸이 null ③둘 다 있음 세 가지가 나온다
임시저장 PLAYLIST 의 playlist 부재
Draft 레코드 스키마 설명 — “임시저장 PLAYLIST는 미작성 시 null”. 발행 완료는 requirePublishedPlaylist 로 최소 1곡 강제
발행 상세 credentials non-blank 보장
requirePublishedCredentials 가 id/password blank 면 400
임시저장 목록 조회 수단
GET /api/v1/afternotes?draftOnly=true (AfternoteController.getAfternotes → findByUserIdAndIsDraftOrderByCreatedAtDesc). 기본값은 isDraft=false 만 반환한다
2. 이슈 본문과 어긋난 지점
“현재 FE는 … isDraft를 수신하지 않는다” 는 낡았다.AfternoteDetailDto.isDraft: Boolean 은 이미 필수 필드로 받고 있다. 실제 결손은 «수신» 이 아니라 «도메인 매핑» 이었다.
부분 credentials 는 «id/password 일부» 말고 «객체 자체 부재» 경우가 하나 더 있다. 위 표 ①.
이슈에 없는 결손이 하나 더 있었다 — 임시저장 PLAYLIST 상세는 아예 열리지 않는다.AfternoteDetailMapper 의 requireNotNull(memorial) { "playlist is required for MEMORIAL detail" } 이 던져 Result.failure 가 되므로, 이어쓰기 이전에 조회부터 실패한다.
AfternoteDetail 지붕 아래 발행 완료 Detail / 임시저장 DraftDetail 을 타입으로 가르고, DraftCredentials 로 부분 입력을 받고, playlist 부재도 상세가 열리게 했다. 회귀 테스트 13건 포함. 남은 작업 5개 중 1·2·3·5 를 닫는다.
진입 경로가 통째로 없다. FE 는 지금 서버 임시저장을 만들지도 (AfternoteCreate*RequestDto·AfternoteUpdateRequestDto 어디에도 isDraft 가 없다) 조회하지도 (AfternotePagingSource 가 draftOnly 를 안 보내 BE 기본값인 발행분만 받는다) 않는다. 이어쓰기를 성립시키려면 임시저장 저장 액션 + 임시저장 목록 화면 + 진입점이 함께 와야 한다.
#1589 는 그 배선이 올라탈 도메인·데이터 계약만 세운다 (AfternoteEditorViewModel 은 한 줄도 안 바뀐다). 이 이슈는 열어 둔 채 남은 이어쓰기 배선을 계속 추적한다 — 위 PR 들이 정리되면 같은 담당(1hyok)이 이어서 한다. 그때 필요한 서버 수단(draftOnly=true 목록, 생성·수정 요청의 isDraft)은 BE 에 이미 다 있으므로 BE 이슈는 필요 없다.
PR #1589 의 몫(playlist 부재 임시저장 상세 매핑 관용)을 #1745 로 분리했고 #1589 가 그 이슈를 Closes 합니다.
이 이슈엔 이어쓰기 흐름이 통째로 남습니다 — 생성·수정 요청 isDraft 탑재, 목록 draftOnly=true, 진입점·저장 액션, 에디터 프리필 연결, 이어쓰기 회귀 테스트. develop 기준 isDraft 는 응답 DTO 두 곳뿐이고 draftOnly 는 0건입니다.
본문의 «isDraft 수신» 은 이미 develop 에 있고, «부분 credentials 허용» 도 매퍼 orEmpty() 강등으로 열려 있습니다. «isDraft 도메인 매핑·상태 분리» 는 #1589 첫 판이 넣었다가 소비처가 없어 접었습니다 — 이어쓰기 진입점을 설계할 때 그 소비처와 함께 올립니다. 블로커 없음.
Activity
[-]feat(afternote): 서버 임시저장 상세 지원 — 발행 상세와 nullable 도메인 분리[/-][+]feat(afternote): 서버 임시저장 상세·이어쓰기 지원 — draft 상태·부분 credentials 모델링[/+][-]feat(afternote): 서버 임시저장 상세·이어쓰기 지원 — draft 상태·부분 credentials 모델링[/-][+]서버 임시저장 상세·이어쓰기 지원 — draft 상태·부분 credentials 모델링[/+]1hyok commentedon Aug 30, 2026
서버 계약 실측 (Afternote-BE
origin/main, 2026-08-30) · 범위 좁힘BE 실코드로 대조한 결과 서버 계약은 이 이슈가 요구하는 만큼 다 갖춰져 있다. 다만 이슈 본문과 어긋난 지점이 셋 있고, 남은 작업 다섯 항목 중 넷만 지금 닫을 수 있어 범위를 좁혔다.
1. 확인한 서버 계약
origin/main)AfternotedetailResponse가sealed interface … permits Draft, Published, PublishedPlaylist,of(...)가if (draft) return new Draft(...). 엔티티는Afternote.isDraft(@Column(name = "is_draft", nullable = false), 기본false)AfternoteValidatorKDoc — “임시저장(isDraft=true)은 느슨, 정식 등록은 credentials/playlist 등 필수값을 서버에서 강제한다”.CredentialsCategoryValidation.validateCreate가if (!request.isDraftValue()) requireCredentials(request),PlaylistValidationStrategy도 같은 모양으로requirePlaylistSongs를 건너뛴다AfternoteService.buildCredentialDetailResponse가 아이디·비밀번호를 각각 복호화한 뒤accountId != null || accountPassword != null ? new CredentialsRequest(accountId, accountPassword) : null.CredentialsRelationSupport가 두 값을 별개 레코드로 저장하므로 ①둘 다 없음 →credentials자체 부재 ②하나만 있음 → 나머지 칸이null③둘 다 있음 세 가지가 나온다playlist부재Draft레코드 스키마 설명 — “임시저장 PLAYLIST는 미작성 시 null”. 발행 완료는requirePublishedPlaylist로 최소 1곡 강제requirePublishedCredentials가 id/password blank 면 400GET /api/v1/afternotes?draftOnly=true(AfternoteController.getAfternotes→findByUserIdAndIsDraftOrderByCreatedAtDesc). 기본값은isDraft=false만 반환한다2. 이슈 본문과 어긋난 지점
isDraft를 수신하지 않는다” 는 낡았다.AfternoteDetailDto.isDraft: Boolean은 이미 필수 필드로 받고 있다. 실제 결손은 «수신» 이 아니라 «도메인 매핑» 이었다.AfternoteDetailMapper의requireNotNull(memorial) { "playlist is required for MEMORIAL detail" }이 던져Result.failure가 되므로, 이어쓰기 이전에 조회부터 실패한다.3. 좁힌 범위 — PR #1589 (
Refs #808)AfternoteDetail지붕 아래 발행 완료Detail/ 임시저장DraftDetail을 타입으로 가르고,DraftCredentials로 부분 입력을 받고,playlist부재도 상세가 열리게 했다. 회귀 테스트 13건 포함. 남은 작업 5개 중 1·2·3·5 를 닫는다.4. 이 이슈에 남는 몫 — «임시저장 항목의 에디터 이어쓰기 및 prefill 연결»
#1589 에 넣지 않았다. 이유 둘:
AfternoteCreate*RequestDto·AfternoteUpdateRequestDto어디에도isDraft가 없다) 조회하지도 (AfternotePagingSource가draftOnly를 안 보내 BE 기본값인 발행분만 받는다) 않는다. 이어쓰기를 성립시키려면 임시저장 저장 액션 + 임시저장 목록 화면 + 진입점이 함께 와야 한다.author/navigation/**·AppNavigationActions.kt(feat(afternote): 수신자 선택 반환 채널을 복수로 열어 한 번에 N명 지정 (#1426) #1582),AfternoteEditorViewModel.kt·AfternoteTypeForm.kt·EditorFormMutations.kt(refactor(afternote): 추모 영상 편집 상태를 값 객체로 캡슐화한다 (#1561) #1565·fix(afternote): 추모 영상 썸네일 실패에 재시도 수단을 준다 (#1550) #1567·feat(afternote): 수신자 선택 반환 채널을 복수로 열어 한 번에 N명 지정 (#1426) #1582). 지금 손대면 충돌 정리 비용이 구현 비용을 넘는다.#1589 는 그 배선이 올라탈 도메인·데이터 계약만 세운다 (
AfternoteEditorViewModel은 한 줄도 안 바뀐다). 이 이슈는 열어 둔 채 남은 이어쓰기 배선을 계속 추적한다 — 위 PR 들이 정리되면 같은 담당(1hyok)이 이어서 한다. 그때 필요한 서버 수단(draftOnly=true목록, 생성·수정 요청의isDraft)은 BE 에 이미 다 있으므로 BE 이슈는 필요 없다.곡을 안 담은 임시저장 추억 노트 상세가 열리게 playlist 부재를 관용한다 (#808)
fix(afternote): 곡을 안 담은 임시저장 추억 노트 상세가 열리게 playlist 부재를 관용한다 (#808)
1hyok commentedon Sep 2, 2026
PR #1589 의 몫(playlist 부재 임시저장 상세 매핑 관용)을 #1745 로 분리했고 #1589 가 그 이슈를 Closes 합니다.
이 이슈엔 이어쓰기 흐름이 통째로 남습니다 — 생성·수정 요청
isDraft탑재, 목록draftOnly=true, 진입점·저장 액션, 에디터 프리필 연결, 이어쓰기 회귀 테스트. develop 기준isDraft는 응답 DTO 두 곳뿐이고draftOnly는 0건입니다.본문의 «isDraft 수신» 은 이미 develop 에 있고, «부분 credentials 허용» 도 매퍼
orEmpty()강등으로 열려 있습니다. «isDraft 도메인 매핑·상태 분리» 는 #1589 첫 판이 넣었다가 소비처가 없어 접었습니다 — 이어쓰기 진입점을 설계할 때 그 소비처와 함께 올립니다. 블로커 없음.docs(afternote): 임시저장 상세 폴백 주석을 서버 계약 실측대로 고친다 (#1745)
20 remaining items
refactor(afternote): 임시저장 코어를 최종 MVI 계약에 연결 (#808)
feat(afternote): 에디터에 임시저장 저장과 이어쓰기 프리필을 붙인다 (#808)
test(afternote): base 의 접근성 스윕 복구분(f6e9028)을 되살린다 (#808)
test(afternote): 발행분 편집의 임시저장이 isDraft 를 싣지 않음을 잠근다 (#808)
refactor(afternote): 임시저장 목록 축을 걷어내고 조회 분리만 남긴다
refactor(afternote): 수정 입력 이름 변경 반영 (#808)
refactor(afternote): 마지막 말씀 생성·수정 매퍼 이름 명시 (#808)
merge: 에디터 후속 부모와 임시저장 계약 통합 (#808)
merge: 카테고리 스냅샷으로 임시저장 비교 기준 통합 (#808)
refactor(afternote): 수정 입력의 수신자 id 계약 통합 (#808)
refactor(afternote): 미디어 URL 인자 이름 통합 (#808)