Skip to content

refactor: 담당 area 의 main 소스셋 @Preview 148건 전량 삭제 (#1388 파생) #1434

Closed

Description

@OnlyReadOneBook

작업 유형

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 를 통째로 남겨 둘 근거는 없다.

왜 지우는가

  1. main 프리뷰는 CI 가 검증하지 않는다. 골든이 없어 렌더가 깨져도 아무도 모른다. 같은 그림을 검증받는 쪽(screenshotTest)이 정본이다.
  2. 같은 그림을 두 소스셋에서 관리하면 시안이 바뀔 때 한쪽만 고쳐진다.
  3. 프리뷰용 더미 데이터와 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.kt
  • AfternoteOutlinedCardPreview — com/afternote/core/ui/AfternoteOutlinedCard.kt
  • AfternoteSectionHeaderPreview — com/afternote/core/ui/AfternoteSectionHeader.kt
  • HomeTopBarPreview — com/afternote/core/ui/topbar/HomeTopBar.kt
  • KoreanConsonantIndexPreview — com/afternote/core/ui/KoreanConsonantIndex.kt
  • PenFloatingActionButtonPreview — com/afternote/core/ui/button/FAB/PenFloatingActionButton.kt
  • ProfileImagePreview — com/afternote/core/ui/ProfileImage.kt
  • RecipientDesignationBadgeCompletePreview — com/afternote/core/ui/badge/RecipientDesignationBadge.kt

feature/afternote/presentation (5건)

  • ErrorListBodyPreview — com/afternote/feature/afternote/presentation/shared/body/ErrorListBody.kt
  • MemorialMediaSourceBottomSheetPhotoPreview — com/afternote/feature/afternote/presentation/author/editor/memorial/MemorialMediaSourceBottomSheet.kt
  • MemorialMediaSourceBottomSheetRemovablePreview — com/afternote/feature/afternote/presentation/author/editor/memorial/MemorialMediaSourceBottomSheet.kt
  • MemorialMediaSourceBottomSheetVideoPreview — com/afternote/feature/afternote/presentation/author/editor/memorial/MemorialMediaSourceBottomSheet.kt
  • UnimplementedTypeContentPreview — 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.kt
  • FindIdScreenErrorPreview — feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/findaccount/FindIdScreen.kt
  • FindIdScreenPreview — feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/findaccount/FindIdScreen.kt
  • SignUpPasswordScreenPreview — feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/signup/SignUpPasswordScreen.kt
  • SignUpResidentNumberScreenPreview — feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/signup/SignUpResidentNumberScreen.kt
  • SignUpScreenMismatchPreview — feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/signup/SignUpScreen.kt
  • WelcomeScreenPreview — 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.kt
  • DocumentSourceBottomSheetPreview — com/afternote/feature/receiver/presentation/deliveryverification/component/DocumentSourceBottomSheet.kt
  • DocumentUploadEmptyPreview — com/afternote/feature/receiver/presentation/deliveryverification/DocumentUploadScreen.kt
  • DocumentUploadFilledPreview — com/afternote/feature/receiver/presentation/deliveryverification/DocumentUploadScreen.kt
  • DocumentUploadWithSheetOpenPreview — com/afternote/feature/receiver/presentation/deliveryverification/DocumentUploadScreen.kt
  • IdentityVerificationEmailEmptyPreview — com/afternote/feature/receiver/presentation/deliveryverification/IdentityVerificationEmailScreen.kt
  • IdentityVerificationEmailSentPreview — com/afternote/feature/receiver/presentation/deliveryverification/IdentityVerificationEmailScreen.kt
  • IdentityVerificationIntroScreenPreview — com/afternote/feature/receiver/presentation/deliveryverification/IdentityVerificationIntroScreen.kt
  • MasterKeyScreenEmptyPreview — com/afternote/feature/receiver/presentation/deliveryverification/MasterKeyScreen.kt
  • MasterKeyScreenFilledPreview — com/afternote/feature/receiver/presentation/deliveryverification/MasterKeyScreen.kt
  • ReceivedRecordsScreenEmptyPreview — com/afternote/feature/receiver/presentation/recordsbox/ReceivedRecordsScreen.kt
  • ReceivedRecordsScreenFilledPreview — com/afternote/feature/receiver/presentation/recordsbox/ReceivedRecordsScreen.kt
  • SenderDetailApprovedPreview — com/afternote/feature/receiver/presentation/senderdetail/SenderDetailScreen.kt
  • SenderDetailNotRequestedPreview — com/afternote/feature/receiver/presentation/senderdetail/SenderDetailScreen.kt
  • SenderDetailPendingPreview — com/afternote/feature/receiver/presentation/senderdetail/SenderDetailScreen.kt
  • SenderRegistrationScreenEmptyPreview — com/afternote/feature/receiver/presentation/recordsbox/SenderRegistrationScreen.kt
  • SenderRegistrationScreenFilledPreview — com/afternote/feature/receiver/presentation/recordsbox/SenderRegistrationScreen.kt
    범위 밖: A 등급 중 feature/timeletter 1건은 koongmai 담당이라 이 이슈에 넣지 않는다. B 등급의 mindrecord 3 · home 2 · setting 2 도 같다.

완료 조건

  • 위 37건의 @Preview 함수가 main 소스셋에서 제거되고, 그로 인해 미사용이 된 import·더미 상수도 함께 걷힌다.
  • screenshotTest 골든은 변하지 않는다(프리뷰 삭제는 baseline 에 영향이 없어야 한다 — validateDebugScreenshotTest 로 확인).
  • 각 모듈의 compileDebugKotlin · compileDebugUnitTestKotlin · compileDebugScreenshotTestKotlin 과 ktlintCheck 통과.
  • B 등급 62건은 케이스 대조 결과를 이 이슈 코멘트에 남기고, 삭제분이 있으면 같은 PR 또는 후속 PR 로 처리한다.

참고

Activity

  1. added
    refactor동작 변경 없이 코드 구조·책임·경계를 개선하는 작업
    area:afternote애프터노트 기능 영역
    area:core공용 코어·네트워크 기능 영역
    area:receiver수신자 관리 기능 영역
    on Aug 28, 2026
  2. OnlyReadOneBook commented on Aug 29, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    B 등급 대조 결과 (완료 조건 이행) — PR #1435

    A 등급 처리와 함께 B 등급을 인자 키 단위로 대조했다. 이슈의 원 판정이 «인자 문자열 전체 일치» 였던 것과 달리, 여기서는 첫 비-래퍼 호출의 인자를 최상위 콤마로 쪼개 이름=값 맵으로 만든 뒤 어떤 인자가 다른지를 뽑았다. 같은 심볼의 screenshotTest 후보가 여럿이면 차이 나는 키가 가장 적은 것을 짝으로 잡았다.

    내 담당 area 의 B 는 60건이었고, 아래처럼 갈렸다.

    ① A 판정에서 새어 나간 진짜 중복 — 4건 (PR #1435 에서 삭제)

    인자가 실제로 같은데 후행 쉼표·줄바꿈 때문에 문자열 비교에서 갈렸던 것들이다. 원 실측이 공백만 제거하고 후행 쉼표를 정규화하지 않아 A 에서 빠졌다.

    프리뷰 screenshotTest 대응 차이
    ProfileImagePickerPreview profileImagePickerPlaceholderScreenshot onPickClick = {}, ↔ onPickClick = {}
    RecipientDesignationBadgeIncompletePreview recipientDesignationBadgeIncompleteScreenshot 후행 쉼표 + 줄바꿈
    FingerprintLoginScreenPreview fingerprintLoginScreenScreenshot 후행 쉼표
    SectionGoButtonPreview sectionGoButtonScreenshot 줄바꿈

    ② 렌더 상태는 같고 더미 값만 다름 — 21건 (2단계 삭제 대상)

    분기를 타는 인자는 같고, modifier 의 padding dp·표시 문자열·카운트 숫자처럼 어느 쪽을 남겨도 검증 가치가 같은 값만 다르다. screenshotTest 쪽이 골든을 갖고 있으므로 정본으로 두면 된다.

    모듈 프리뷰 다른 인자
    core/ui AfternoteButtonDefaultPreview text ("시작하기" ↔ "확인")
    core/ui CaptionLabeledTextFieldPreview label ("Label" ↔ "이메일")
    core/ui NetworkErrorPopupPreview modifier (st 만 padding 16dp)
    feature/afternote AddItemTextFieldPreview modifier
    feature/afternote MessageSectionEmptyPreview modifier
    feature/afternote LastMomentQuestionPreview modifier, text
    feature/afternote ProcessingMethodCheckboxPreview item (문구), modifier
    feature/afternote MemorialPhotoUploadPreview main 이 전부 기본 인자, st 가 같은 값을 명시
    feature/afternote MemorialVideoUploadPreview 위와 같음
    feature/afternote HomeHeaderSectionPreview nextStep (문구)
    feature/afternote DetailLoadErrorContentPreview messageRes (다른 문자열 리소스)
    feature/onboarding SignUpInputLabelPreview modifier
    feature/onboarding OnboardingProfileScreenPreview initialName
    feature/onboarding TermsDetailScreenPreview title
    feature/receiver DocumentSlotCardEmptyPreview modifier
    feature/receiver DocumentSlotCardFilledPreview modifier
    feature/receiver PreviewHeroCard leaveMessage (main 이 기본값을 명시)
    feature/receiver SenderMessageHeroCardPreview message, modifier
    feature/receiver MindRecordSectionPreview modifier, summary (수치)
    feature/receiver TimeLetterSectionPreview modifier, totalCount
    feature/receiver AfternoteSectionPreview icons, modifier, totalCount

    주의: 이 21건은 인자 키 diff 로 «중복일 것» 까지만 말한다. ①의 4건은 한 건씩 본문을 눈으로 대조한 뒤 지웠고, 이 21건도 삭제 전 같은 눈 대조가 필요하다 — 특히 MemorialPhotoUpload·MemorialVideoUpload 는 main 이 생략한 인자의 기본값이 st 가 명시한 값과 같은지 확인해야 한다.

    ③ 상태가 실제로 다름 — 35건 (존치)

    screenshotTest 가 커버하지 않는 분기·변형을 main 프리뷰가 들고 있다.

    • st 가 빈 상태만 커버: EditorMessageSectionPreview(messages 채움 ↔ st 는 emptyList()), MemorialEditorContentPreview·GalleryAndFileEditorContentPreview(editorMessages), EditorSelectionDropdownPreview·…PlaceholderPreview(options 채움 ↔ emptyList())
    • st 에 없는 분기 플래그: ProcessingMethodCheckboxEditingPreview(isEditing = true), LoginScreenPreview(isLoading), SignUpScreenPreview(isVerificationSent), AfternoteFieldContainerPreview(enabled)
    • 다른 선택·단계 상태: BottomBarPreview(selectedNavTab), FlowStepProgressBarStep1Preview·Step2Preview(currentStep/totalSteps), AfternoteTypeFilterRowPreview(selectedTab)
    • 슬롯 컴포저블 차이: DailyRecordTopBarPreview 2건(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, MemorialPlaylistEntry 3건, PreviewMemorialPlaylist, MessageSectionPreview
    • 다른 PR 이 처리 중: ReceiverHomeScreenPreview — 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 미커버 상태를 들고 있다» 는 그 프리뷰를 실제로 보는 사람이 있을 때만 값이 있다. 삭제 범위를 어디까지 넓힐지는 별도로 정한다.

  3. added 2 commits that reference this issue on Aug 29, 2026
  4. OnlyReadOneBook commented on Aug 29, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    범위 확대로 위 B 등급 분류는 무효 — 전량 삭제로 마무리 (PR #1435)

    작성자 결정(2026-08-29)으로 범위가 «A 등급 중복분» 에서 «담당 area 의 main 소스셋 @Preview 전량» 으로 넓어졌다. 동기는 중복 여부가 아니라 컴포저블을 고칠 때마다 프리뷰까지 따라 고쳐야 하는 부담이다.

    그래서 앞 코멘트의 ②(2단계 삭제 후보 21건)·③(존치 35건) 구분은 더 이상 판정 기준이 아니다. ③의 존치 근거였던 «screenshotTest 가 커버하지 않는 상태를 main 프리뷰가 들고 있다» 는 그 프리뷰를 보는 사람이 있을 때만 값이 있는데, 그 전제가 깨졌다. B·C 등급을 포함해 전부 삭제했다.

    결과

    항목 수
    main @Preview 삭제 148건
    프리뷰가 유일 사용처였던 더미 상수·함수 4건
    미사용이 된 import 282개
    잔여 주석·region 마커 3건

    core/ui · feature/{afternote,onboarding,receiver}/presentation 의 main @Preview 는 0건이 됐다. 골든이 있는 screenshotTest 프리뷰는 그대로다.

    범위 밖(timeletter·setting·mindrecord·home)에는 110건이 남아 있다 — 남의 담당이라 건드리지 않았다.

    삭제한 148건의 성격

    • screenshotTest 가 같은 그림을 렌더 — 44건
    • screenshotTest 가 같은 컴포저블의 다른 케이스를 렌더 — 53건
    • screenshotTest 대응이 아예 없음 — 51건 (골든이 없어 원래도 CI 가 검증하지 않던 프리뷰다)

    착수 전제

    앞 코멘트에서 정리한 대로 해제됐다. 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 범위 밖으로 뒀다.

  5. changed the title [-]refactor: screenshotTest 와 완전 중복인 main 소스셋 @Preview 37건 삭제 (#1388 파생)[/-] [+]refactor: 담당 area 의 main 소스셋 @Preview 148건 전량 삭제 (#1388 파생)[/+] on Aug 29, 2026
  6. added
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Aug 29, 2026
  7. removed
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:receiver수신자 관리 기능 영역refactor동작 변경 없이 코드 구조·책임·경계를 개선하는 작업

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions