Repository navigation
refactor: 담당 area 의 main 소스셋 @Preview 148건 전량 삭제 (#1388 파생) #1434
Description
작업 유형
refactor — 동작 변경 없는 구조·책임·경계 개선
주 담당 모듈
receiver — 수신자 관리
개요
⚠️ 2026-08-29 범위 확대 — 아래 본문은 확대 이전의 실측 기록이다.
범위가 «screenshotTest 와 완전 중복인 A 등급 37건» 에서 «담당 area 의 main 소스셋@Preview전량(148건)» 으로 넓어졌다. 동기는 중복 여부가 아니라 컴포저블을 고칠 때마다 프리뷰까지 따라 고쳐야 하는 부담이다. 그래서 아래의 A/B/C 등급 구분과 «B 는 2단계» 계획, 그리고 착수 전제(«AS 가 screenshotTest 프리뷰를 렌더하는가»)는 모두 판정 기준이 아니다.
경위와 결과는 범위 확대 코멘트에, 구현은 PR #1435 에 있다. 아래 본문은 A 등급 실측을 남겨 두기 위해 그대로 둔다.
main 소스셋의@Preview중 screenshotTest 가 같은 컴포저블을 같은 인자로 이미 렌더하는 것이 남아 있다. 프로덕션 소스셋에 있을 이유가 없는 중복이라 걷어낸다.
발견 경위: PR #1414(#1388 의 receiver 몫)에서 ReceiverHomeActions 의 디폴트 인자를 제거하자, 이를 메우려고 companion object { val Noop } 을 프로덕션 소스에 공개 상수로 두게 됐다. 그 상수를 main 에서 쓰는 곳은 ReceiverHomeScreenPreview 하나뿐이었고, 그 프리뷰는 receiverHomeScreenSuccessScreenshot 과 같은 Success 케이스였다. 프리뷰를 지우니 Noop 도 함께 사라졌다 — #1388 이 막으려던 "전 액션을 한 줄로 죽이는 우회로"가 프리뷰 때문에 프로덕션 API 로 새어 나온 것이다. 같은 구조가 얼마나 더 있는지 전수 실측했다.
실측(origin/develop 스냅샷, main @Preview 258건 ↔ screenshotTest 프리뷰 198건 / 커버 심볼 99개):
| 등급 | 판정 | 전체 | 내 담당 area |
|---|---|---|---|
| A | 같은 심볼 + 인자 문자열까지 동일 | 38 | 37 |
| B | 같은 심볼, 인자 다름(상태가 다른지 더미 값만 다른지 대조 필요) | 69 | 62 |
| C | screenshotTest 커버 없음 | 151 | 52 |
이 이슈는 A 등급 37건을 범위로 한다. B 62건은 케이스 단위 대조가 필요해 2단계로 둔다 — ReceiverHomeScreenPreview 는 더미 값이 달라 B 로 분류됐지만 실제로는 중복이었으므로, B 를 통째로 남겨 둘 근거는 없다.
왜 지우는가
- main 프리뷰는 CI 가 검증하지 않는다. 골든이 없어 렌더가 깨져도 아무도 모른다. 같은 그림을 검증받는 쪽(screenshotTest)이 정본이다.
- 같은 그림을 두 소스셋에서 관리하면 시안이 바뀔 때 한쪽만 고쳐진다.
- 프리뷰용 더미 데이터와 no-op 배선이 프로덕션 소스셋에 남는다. 위
Noop이 그 실사례다.
전제 — 착수 전 확인할 것: Android Studio 프리뷰 창이 screenshotTest 소스셋의 @Preview 를 렌더한다는 점(AGP Compose Screenshot Testing 소스셋). 이게 성립해야 "지워도 개발 중 프리뷰를 잃지 않는다"가 참이 된다. 이 레포의 screenshotTest 프리뷰는 internal fun + @PreviewTest + @Preview 형태라 렌더 대상이어야 하지만, 1건을 Studio 에서 실제로 열어 확인하고 시작한다. 렌더되지 않는다면 이 이슈는 그대로 닫고 방향을 바꾼다(중복을 남기거나, screenshotTest 프리뷰를 main 에서 참조하는 형태로).
A 등급 37건 (내 담당 area)
core/ui (8건)
AfternoteFloatingActionButtonPreview—com/afternote/core/ui/button/FAB/AfternoteFloatingActionButton.ktAfternoteOutlinedCardPreview—com/afternote/core/ui/AfternoteOutlinedCard.ktAfternoteSectionHeaderPreview—com/afternote/core/ui/AfternoteSectionHeader.ktHomeTopBarPreview—com/afternote/core/ui/topbar/HomeTopBar.ktKoreanConsonantIndexPreview—com/afternote/core/ui/KoreanConsonantIndex.ktPenFloatingActionButtonPreview—com/afternote/core/ui/button/FAB/PenFloatingActionButton.ktProfileImagePreview—com/afternote/core/ui/ProfileImage.ktRecipientDesignationBadgeCompletePreview—com/afternote/core/ui/badge/RecipientDesignationBadge.kt
feature/afternote/presentation (5건)
ErrorListBodyPreview—com/afternote/feature/afternote/presentation/shared/body/ErrorListBody.ktMemorialMediaSourceBottomSheetPhotoPreview—com/afternote/feature/afternote/presentation/author/editor/memorial/MemorialMediaSourceBottomSheet.ktMemorialMediaSourceBottomSheetRemovablePreview—com/afternote/feature/afternote/presentation/author/editor/memorial/MemorialMediaSourceBottomSheet.ktMemorialMediaSourceBottomSheetVideoPreview—com/afternote/feature/afternote/presentation/author/editor/memorial/MemorialMediaSourceBottomSheet.ktUnimplementedTypeContentPreview—com/afternote/feature/afternote/presentation/author/editor/UnimplementedTypeContent.kt
feature/onboarding/presentation (7건)
FindIdScreenCodeSentPreview—feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/findaccount/FindIdScreen.ktFindIdScreenErrorPreview—feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/findaccount/FindIdScreen.ktFindIdScreenPreview—feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/findaccount/FindIdScreen.ktSignUpPasswordScreenPreview—feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/signup/SignUpPasswordScreen.ktSignUpResidentNumberScreenPreview—feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/signup/SignUpResidentNumberScreen.ktSignUpScreenMismatchPreview—feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/signup/SignUpScreen.ktWelcomeScreenPreview—feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/WelcomeScreen.kt
feature/receiver/presentation (17건)
DeliveryVerificationCompletePreview—com/afternote/feature/receiver/presentation/deliveryverification/DeliveryVerificationCompleteScreen.ktDocumentSourceBottomSheetPreview—com/afternote/feature/receiver/presentation/deliveryverification/component/DocumentSourceBottomSheet.ktDocumentUploadEmptyPreview—com/afternote/feature/receiver/presentation/deliveryverification/DocumentUploadScreen.ktDocumentUploadFilledPreview—com/afternote/feature/receiver/presentation/deliveryverification/DocumentUploadScreen.ktDocumentUploadWithSheetOpenPreview—com/afternote/feature/receiver/presentation/deliveryverification/DocumentUploadScreen.ktIdentityVerificationEmailEmptyPreview—com/afternote/feature/receiver/presentation/deliveryverification/IdentityVerificationEmailScreen.ktIdentityVerificationEmailSentPreview—com/afternote/feature/receiver/presentation/deliveryverification/IdentityVerificationEmailScreen.ktIdentityVerificationIntroScreenPreview—com/afternote/feature/receiver/presentation/deliveryverification/IdentityVerificationIntroScreen.ktMasterKeyScreenEmptyPreview—com/afternote/feature/receiver/presentation/deliveryverification/MasterKeyScreen.ktMasterKeyScreenFilledPreview—com/afternote/feature/receiver/presentation/deliveryverification/MasterKeyScreen.ktReceivedRecordsScreenEmptyPreview—com/afternote/feature/receiver/presentation/recordsbox/ReceivedRecordsScreen.ktReceivedRecordsScreenFilledPreview—com/afternote/feature/receiver/presentation/recordsbox/ReceivedRecordsScreen.ktSenderDetailApprovedPreview—com/afternote/feature/receiver/presentation/senderdetail/SenderDetailScreen.ktSenderDetailNotRequestedPreview—com/afternote/feature/receiver/presentation/senderdetail/SenderDetailScreen.ktSenderDetailPendingPreview—com/afternote/feature/receiver/presentation/senderdetail/SenderDetailScreen.ktSenderRegistrationScreenEmptyPreview—com/afternote/feature/receiver/presentation/recordsbox/SenderRegistrationScreen.ktSenderRegistrationScreenFilledPreview—com/afternote/feature/receiver/presentation/recordsbox/SenderRegistrationScreen.kt
범위 밖: A 등급 중feature/timeletter1건은 koongmai 담당이라 이 이슈에 넣지 않는다. B 등급의 mindrecord 3 · home 2 · setting 2 도 같다.
완료 조건
- 위 37건의
@Preview함수가 main 소스셋에서 제거되고, 그로 인해 미사용이 된 import·더미 상수도 함께 걷힌다. screenshotTest골든은 변하지 않는다(프리뷰 삭제는 baseline 에 영향이 없어야 한다 —validateDebugScreenshotTest로 확인).- 각 모듈의
compileDebugKotlin·compileDebugUnitTestKotlin·compileDebugScreenshotTestKotlin과ktlintCheck통과. - B 등급 62건은 케이스 대조 결과를 이 이슈 코멘트에 남기고, 삭제분이 있으면 같은 PR 또는 후속 PR 로 처리한다.
참고
- 근원 이슈: refactor(platform): 컴포저블 no-op 콜백 디폴트 — 재발 가드·컨벤션 도입 + afternote 잔여 2건 #1388 (컴포저블 no-op 콜백 디폴트 전수 청소) — 이 발견은 그 receiver 몫 PR refactor(receiver): ReceiverHomeActions no-op 콜백 디폴트 4건 제거 (#1388) #1414 에서 나왔다.
- 실측 재현:
origin/develop스냅샷에서 main·screenshotTest 의@Preview함수 본문을 파싱해 첫 비-래퍼 컴포저블 호출을 정규화(공백 제거)한 뒤 대조했다. 래퍼 제외 목록은AfternoteTheme·Box·Column·Row·Surface·Scaffold등.
Activity
refactor(receiver): 프리뷰 전용 Noop 상수를 프로덕션에서 걷는다
OnlyReadOneBook commentedon Aug 29, 2026
B 등급 대조 결과 (완료 조건 이행) — PR #1435
A 등급 처리와 함께 B 등급을 인자 키 단위로 대조했다. 이슈의 원 판정이 «인자 문자열 전체 일치» 였던 것과 달리, 여기서는 첫 비-래퍼 호출의 인자를 최상위 콤마로 쪼개
이름=값맵으로 만든 뒤 어떤 인자가 다른지를 뽑았다. 같은 심볼의 screenshotTest 후보가 여럿이면 차이 나는 키가 가장 적은 것을 짝으로 잡았다.내 담당 area 의 B 는 60건이었고, 아래처럼 갈렸다.
① A 판정에서 새어 나간 진짜 중복 — 4건 (PR #1435 에서 삭제)
인자가 실제로 같은데 후행 쉼표·줄바꿈 때문에 문자열 비교에서 갈렸던 것들이다. 원 실측이 공백만 제거하고 후행 쉼표를 정규화하지 않아 A 에서 빠졌다.
ProfileImagePickerPreviewprofileImagePickerPlaceholderScreenshotonPickClick = {},↔onPickClick = {}RecipientDesignationBadgeIncompletePreviewrecipientDesignationBadgeIncompleteScreenshotFingerprintLoginScreenPreviewfingerprintLoginScreenScreenshotSectionGoButtonPreviewsectionGoButtonScreenshot② 렌더 상태는 같고 더미 값만 다름 — 21건 (2단계 삭제 대상)
분기를 타는 인자는 같고,
modifier의 padding dp·표시 문자열·카운트 숫자처럼 어느 쪽을 남겨도 검증 가치가 같은 값만 다르다. screenshotTest 쪽이 골든을 갖고 있으므로 정본으로 두면 된다.AfternoteButtonDefaultPreviewtext("시작하기" ↔ "확인")CaptionLabeledTextFieldPreviewlabel("Label" ↔ "이메일")NetworkErrorPopupPreviewmodifier(st 만 padding 16dp)AddItemTextFieldPreviewmodifierMessageSectionEmptyPreviewmodifierLastMomentQuestionPreviewmodifier,textProcessingMethodCheckboxPreviewitem(문구),modifierMemorialPhotoUploadPreviewMemorialVideoUploadPreviewHomeHeaderSectionPreviewnextStep(문구)DetailLoadErrorContentPreviewmessageRes(다른 문자열 리소스)SignUpInputLabelPreviewmodifierOnboardingProfileScreenPreviewinitialNameTermsDetailScreenPreviewtitleDocumentSlotCardEmptyPreviewmodifierDocumentSlotCardFilledPreviewmodifierPreviewHeroCardleaveMessage(main 이 기본값을 명시)SenderMessageHeroCardPreviewmessage,modifierMindRecordSectionPreviewmodifier,summary(수치)TimeLetterSectionPreviewmodifier,totalCountAfternoteSectionPreviewicons,modifier,totalCount주의: 이 21건은 인자 키 diff 로 «중복일 것» 까지만 말한다. ①의 4건은 한 건씩 본문을 눈으로 대조한 뒤 지웠고, 이 21건도 삭제 전 같은 눈 대조가 필요하다 — 특히
MemorialPhotoUpload·MemorialVideoUpload는 main 이 생략한 인자의 기본값이 st 가 명시한 값과 같은지 확인해야 한다.③ 상태가 실제로 다름 — 35건 (존치)
screenshotTest 가 커버하지 않는 분기·변형을 main 프리뷰가 들고 있다.
EditorMessageSectionPreview(messages 채움 ↔ st 는emptyList()),MemorialEditorContentPreview·GalleryAndFileEditorContentPreview(editorMessages),EditorSelectionDropdownPreview·…PlaceholderPreview(options 채움 ↔emptyList())ProcessingMethodCheckboxEditingPreview(isEditing = true),LoginScreenPreview(isLoading),SignUpScreenPreview(isVerificationSent),AfternoteFieldContainerPreview(enabled)BottomBarPreview(selectedNavTab),FlowStepProgressBarStep1Preview·Step2Preview(currentStep/totalSteps),AfternoteTypeFilterRowPreview(selectedTab)DailyRecordTopBarPreview2건(DetailTopBar·TitleTopBar의actions에ViewModeSwitcher를 넣는데 st 엔 없다)AfternoteCircularCheckboxPreview(20dp ↔ 24dp),CloseIconPreview·RightArrowIconMaterialPreview(size·tint 지정 ↔ 기본),PlusBadgeButtonPreview(paddingValues),MindRecordStatCardPreview(iconResId가 다른 아이콘),AfternoteRadioGroupPreview(옵션 타입이String↔Unit)GalleryDetailScreenPreview·AccountDetailScreenPreview·GalleryReceivedDetailScreenPreview·SocialNetworkReceivedDetailScreenPreview(main 이 처리방법·메시지 블록까지 채워 섹션 수가 다르다),PreviewMemorialReceivedDetail·…WithVideo,AddSongScreenPreview,MemorialPlaylistEntry3건,PreviewMemorialPlaylist,MessageSectionPreviewReceiverHomeScreenPreview— PR refactor(receiver): ReceiverHomeActions no-op 콜백 디폴트 4건 제거 (#1388) #1414 에서 삭제된다A 등급 목록의 오탐 1건
DocumentUploadWithSheetOpenPreview는 A 에서 빼고 남겼다. 첫 비-래퍼 호출이DocumentUploadScreenContent(uiState = DocumentUploadUiState(), …)라documentUploadScreenEmptyScreenshot과 같아 보였지만, 실제 본문은 그 위에DocumentSourceBottomSheet를 그림자·클립까지 얹어 시트가 열린 상태를 덧그린다.DocumentUploadScreenScreenshotTest에는 empty / deathOnly / filled 3건뿐이라 대응 케이스가 없다.판정 알고리즘의 한계로 봐야 한다 — «첫 비-래퍼 컴포저블 호출» 은 프리뷰가 여러 컴포저블을 겹쳐 그리는 경우를 보지 못한다. ②를 지울 때도 인자 diff 만 보지 말고 본문에 다른 호출이 더 있는지 함께 봐야 한다.
착수 전제 — 해제됨 (2026-08-29 작성자 확인)
이슈가 건 전제 «Android Studio 프리뷰 창이
screenshotTest소스셋의@Preview를 렌더한다» 는 작성자 확인으로 무효가 됐다. 프리뷰를 거의 보지 않고, 삭제 동기가 «컴포저블을 고칠 때마다 프리뷰까지 따라 고쳐야 하는 것» 이라 Studio 렌더 여부가 판단을 바꾸지 않는다. PR #1435 의 블로커도 아니다.조사했던 내용은 기록만 남긴다: 공식 문서엔 서술이 없고, Android Studio 2026.1(
AI-261) 의 Compose 디자이너 플러그인 jar 에도screenshotTest문자열이 한 건도 없다(특별 취급 부재라 «렌더된다»·«지원 안 한다» 어느 쪽과도 어긋나지 않는다). 이 레포는 8개 모듈 전부screenshotTestImplementation(libs.androidx.compose.ui.tooling)를 건다.이 해제는 ②·③의 판정 기준도 바꾼다. 존치 근거로 적은 «main 프리뷰가 screenshotTest 미커버 상태를 들고 있다» 는 그 프리뷰를 실제로 보는 사람이 있을 때만 값이 있다. 삭제 범위를 어디까지 넓힐지는 별도로 정한다.
refactor: 담당 area 의 main 소스셋 @Preview 를 전량 제거한다 (#1434)
fix(test): 빈 목록 문구 단언을 리소스 기준으로 바꿔 #567 이후 낡은 계측 테스트를 되살린다 (#1434)
OnlyReadOneBook commentedon Aug 29, 2026
범위 확대로 위 B 등급 분류는 무효 — 전량 삭제로 마무리 (PR #1435)
작성자 결정(2026-08-29)으로 범위가 «A 등급 중복분» 에서 «담당 area 의 main 소스셋
@Preview전량» 으로 넓어졌다. 동기는 중복 여부가 아니라 컴포저블을 고칠 때마다 프리뷰까지 따라 고쳐야 하는 부담이다.그래서 앞 코멘트의 ②(2단계 삭제 후보 21건)·③(존치 35건) 구분은 더 이상 판정 기준이 아니다. ③의 존치 근거였던 «screenshotTest 가 커버하지 않는 상태를 main 프리뷰가 들고 있다» 는 그 프리뷰를 보는 사람이 있을 때만 값이 있는데, 그 전제가 깨졌다. B·C 등급을 포함해 전부 삭제했다.
결과
@Preview삭제importregion마커core/ui·feature/{afternote,onboarding,receiver}/presentation의 main@Preview는 0건이 됐다. 골든이 있는screenshotTest프리뷰는 그대로다.범위 밖(timeletter·setting·mindrecord·home)에는 110건이 남아 있다 — 남의 담당이라 건드리지 않았다.
삭제한 148건의 성격
착수 전제
앞 코멘트에서 정리한 대로 해제됐다. Studio 가
screenshotTest프리뷰를 렌더하는지는 끝내 확정하지 못했지만, 프리뷰를 보지 않는 것이 삭제 동기이므로 판단을 바꾸지 않는다.곁다리로 드러난 것 2가지
① 판정 알고리즘의 사각.
DocumentUploadWithSheetOpenPreview는 A 등급 오탐이었다 — 첫 비-래퍼 호출만 같고 실제로는DocumentSourceBottomSheet를 덧그려 시트가 열린 상태를 보여줬다. «첫 비-래퍼 컴포저블 호출» 로 중복을 판정하면 여러 컴포저블을 겹쳐 그리는 프리뷰를 보지 못한다. (전량 삭제라 결과적으로 함께 지워졌다.)② #567 이 남긴 잠재 파손.
AfternoteAuthorExtendedAndroidTest가 빈 목록 문구를 하드코딩하고 있어 develop 커밋5b6db1de3(#567, 문구 교체) 이후 실패 상태였다. 계측 레인은 PR 의 CI Test Plan 이 그 테스트를selected로 고르거나full일 때만 도는데, #567 머지 이후 그런 PR 이 없어 아무도 밟지 않았다. 이 PR 이navigation/경로 때문에 selected 를 강제받아 처음 밟았고, 문구를 리소스에서 읽도록 고쳐 같이 되살렸다.이 PR 이 손대지 않은 것
프리뷰 삭제로 사용처가 0이 된 심볼 3건 —
SelectableRadioCard,AccountDetailRoute,ReceiverAfterNoteEntry. 셋 다 origin/develop 에서도 이미 프로덕션 사용처가 없었고(SelectableRadioCard는 자기 프리뷰만이 호출자였다), 죽은 코드 판정에는 시안·NavGraph 대조가 따로 필요해 이 PR 범위 밖으로 뒀다.[-]refactor: screenshotTest 와 완전 중복인 main 소스셋 @Preview 37건 삭제 (#1388 파생)[/-][+]refactor: 담당 area 의 main 소스셋 @Preview 148건 전량 삭제 (#1388 파생)[/+]Merge pull request #1435 from Afternote/refactor/1434-duplicate-main-…
refactor(receiver): 프리뷰 전용 Noop 상수를 프로덕션에서 걷는다
refactor(receiver): 프리뷰 전용 Noop 상수를 프로덕션에서 걷는다