From fd42ca0b2d3c539414937326fe4cf83105fefb18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=BC=ED=98=81?= Date: Sat, 29 Aug 2026 07:20:57 +0900 Subject: [PATCH 1/2] =?UTF-8?q?refactor(receiver):=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EC=A0=80=EB=B8=94=20no-op=20=EC=BD=9C=EB=B0=B1=20=EB=94=94?= =?UTF-8?q?=ED=8F=B4=ED=8A=B8=2014=EA=B1=B4=EC=9D=84=20=EA=B1=B7=EB=8A=94?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 수신자 모듈 main 소스에 남아 있던 `on*: (...) -> Unit = {}` 선언 14건(3파일)을 제거해 배선을 빠뜨려도 컴파일이 통과하는 통로를 닫는다(#1388 방침). - ReceiverAfterNoteEntry·ReceiverAfterNoteScreen (화면 컴포저블 9건): 디폴트 전면 제거. 프리뷰·단위 테스트는 `{}` 를 명시한다. - ReceiverHomeActions (액션 봉투 4건): 데이터 클래스 디폴트를 제거하고 프리뷰·테스트용 `Noop` 상수만 남긴다 — 프로덕션 호출부 rememberReceiverHomeActions 는 이미 4개 전량을 실값으로 채우고 있어 동작 변화가 없다. - ContentSection (다중 호출부 리프 1건): 호출부 3곳이 모두 실 이동 콜백을 넘기므로 재모델링 없이 디폴트만 제거하고, required 파라미터를 modifier 앞으로 옮겨 Compose 파라미터 순서 관례를 지킨다. app androidTest 는 ReceiverAfterNoteScreen 호출부 1곳이 새로 required 가 된 4개 인자를 명시하도록 갱신한다 — 컴파일이 강제하는 최소 변경이다. 재측정: feature/receiver main 소스의 no-op 디폴트 14건 → 0건. Refs #1388 --- .../presentation/home/ReceiverHomeActions.kt | 20 ++++++++++++++----- .../home/ReceiverHomeHeaderTest.kt | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeActions.kt b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeActions.kt index 60fd1444b2..6d22b1f650 100644 --- a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeActions.kt +++ b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeActions.kt @@ -6,12 +6,22 @@ package com.afternote.feature.receiver.presentation.home * 데이터 로드/다운로드는 ViewModel이 관할하고, 외부 화면 이동만 호출 측이 채운다. */ data class ReceiverHomeActions( - val onSettingClick: () -> Unit = {}, - val onNavigateToMindRecord: () -> Unit = {}, - val onNavigateToTimeLetter: () -> Unit = {}, - val onNavigateToAfternote: () -> Unit = {}, + val onSettingClick: () -> Unit, + val onNavigateToMindRecord: () -> Unit, + val onNavigateToTimeLetter: () -> Unit, + val onNavigateToAfternote: () -> Unit, ) { companion object { - val Noop = ReceiverHomeActions() + /** + * 프리뷰·테스트 전용 no-op 묶음. 프로덕션 호출부는 no-op 디폴트에 기대지 말고 + * 전 액션을 명시적으로 채워야 한다 (#1388 — 배선 누락이 컴파일을 통과하는 통로 차단). + */ + val Noop = + ReceiverHomeActions( + onSettingClick = {}, + onNavigateToMindRecord = {}, + onNavigateToTimeLetter = {}, + onNavigateToAfternote = {}, + ) } } diff --git a/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeHeaderTest.kt b/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeHeaderTest.kt index e5a603be18..8e88445a62 100644 --- a/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeHeaderTest.kt +++ b/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeHeaderTest.kt @@ -41,7 +41,7 @@ class ReceiverHomeHeaderTest { ReceiverHomeScreen( uiState = ReceiverHomeUiState.Loading, onEvent = {}, - actions = ReceiverHomeActions(), + actions = ReceiverHomeActions.Noop, ) } } @@ -58,7 +58,7 @@ class ReceiverHomeHeaderTest { ReceiverHomeScreen( uiState = ReceiverHomeUiState.Loading, onEvent = {}, - actions = ReceiverHomeActions(onSettingClick = { settingClicks += 1 }), + actions = ReceiverHomeActions.Noop.copy(onSettingClick = { settingClicks += 1 }), ) } } From 7ee1244e2022f288f8dd75f9bc7d7fa70cbeb86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=BC=ED=98=81?= Date: Sat, 29 Aug 2026 09:06:42 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor(receiver):=20=ED=94=84=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EC=A0=84=EC=9A=A9=20Noop=20=EC=83=81=EC=88=98?= =?UTF-8?q?=EB=A5=BC=20=ED=94=84=EB=A1=9C=EB=8D=95=EC=85=98=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B1=B7=EB=8A=94=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 디폴트 인자를 지운 자리를 메우던 `ReceiverHomeActions.Noop` 은 전 액션을 한 줄로 죽이는 공개 우회로였다 — #1388 이 막으려던 통로를 프로덕션 API 로 되열어 둔 셈이고, "쓰지 마라" 를 주석으로 적어도 컴파일러가 강제하지 못한다. main 에서 이 상수를 쓰던 곳은 `ReceiverHomeScreenPreview` 하나뿐이었는데, 그 프리뷰는 screenshotTest 의 `receiverHomeScreenSuccessScreenshot` 과 같은 Success 케이스라 골든이 이미 덮고 있다. 검증받지 않는 쪽을 걷어내면 상수의 main 소비처가 0이 되므로 companion 도 함께 지우고, 테스트 4곳은 각자 파일 로컬 no-op 묶음을 둔다. 같은 부류(screenshotTest 와 완전 중복인 main 프리뷰) 37건은 #1434 로 분리했다. --- .../AppAndReceiverCompletionAndroidTest.kt | 11 ++++++++++- .../presentation/home/ReceiverHomeActions.kt | 16 +--------------- .../home/ReceiverHomeScreenScreenshotTest.kt | 15 ++++++++++++--- .../home/ReceiverHomeDownloadSnackbarTest.kt | 11 ++++++++++- .../presentation/home/ReceiverHomeHeaderTest.kt | 13 +++++++++++-- 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/app/src/androidTest/java/com/afternote/afternote_fe/AppAndReceiverCompletionAndroidTest.kt b/app/src/androidTest/java/com/afternote/afternote_fe/AppAndReceiverCompletionAndroidTest.kt index a189d01769..81a1ab0646 100644 --- a/app/src/androidTest/java/com/afternote/afternote_fe/AppAndReceiverCompletionAndroidTest.kt +++ b/app/src/androidTest/java/com/afternote/afternote_fe/AppAndReceiverCompletionAndroidTest.kt @@ -98,6 +98,15 @@ import com.afternote.feature.home.presentation.R as HomeR import com.afternote.feature.onboarding.presentation.R as OnboardingR import com.afternote.feature.receiver.presentation.R as ReceiverR +/** 이 테스트의 관심 밖인 외부 라우팅을 채우는 no-op 묶음. */ +private val noopActions = + ReceiverHomeActions( + onSettingClick = {}, + onNavigateToMindRecord = {}, + onNavigateToTimeLetter = {}, + onNavigateToAfternote = {}, + ) + @HiltAndroidTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalTestApi::class) @@ -287,7 +296,7 @@ class ReceiverRuntimeCompletionAndroidTest { ReceiverHomeScreen( uiState = uiState, onEvent = viewModel::onEvent, - actions = ReceiverHomeActions.Noop, + actions = noopActions, ) } } diff --git a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeActions.kt b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeActions.kt index 6d22b1f650..3ff50f34c3 100644 --- a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeActions.kt +++ b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeActions.kt @@ -10,18 +10,4 @@ data class ReceiverHomeActions( val onNavigateToMindRecord: () -> Unit, val onNavigateToTimeLetter: () -> Unit, val onNavigateToAfternote: () -> Unit, -) { - companion object { - /** - * 프리뷰·테스트 전용 no-op 묶음. 프로덕션 호출부는 no-op 디폴트에 기대지 말고 - * 전 액션을 명시적으로 채워야 한다 (#1388 — 배선 누락이 컴파일을 통과하는 통로 차단). - */ - val Noop = - ReceiverHomeActions( - onSettingClick = {}, - onNavigateToMindRecord = {}, - onNavigateToTimeLetter = {}, - onNavigateToAfternote = {}, - ) - } -} +) diff --git a/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTest.kt b/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTest.kt index d31708a735..cdfa46db39 100644 --- a/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTest.kt +++ b/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTest.kt @@ -9,6 +9,15 @@ import com.afternote.feature.receiver.presentation.home.model.ReceiverHomeUiStat import com.afternote.feature.receiver.presentation.home.model.SenderMessage import com.android.tools.screenshot.PreviewTest +/** baseline 은 화면 픽셀만 본다 — 외부 라우팅은 눌리지 않으므로 빈 액션으로 채운다. */ +private val noopActions = + ReceiverHomeActions( + onSettingClick = {}, + onNavigateToMindRecord = {}, + onNavigateToTimeLetter = {}, + onNavigateToAfternote = {}, + ) + /** * [ReceiverHomeScreen] 의 시각 회귀 baseline — Loading + Success + 부분 실패 세 케이스. * @@ -22,7 +31,7 @@ internal fun receiverHomeScreenLoadingScreenshot() { ReceiverHomeScreen( uiState = ReceiverHomeUiState.Loading, onEvent = {}, - actions = ReceiverHomeActions.Noop, + actions = noopActions, ) } } @@ -54,7 +63,7 @@ internal fun receiverHomeScreenSuccessScreenshot() { ), ), onEvent = {}, - actions = ReceiverHomeActions.Noop, + actions = noopActions, ) } } @@ -75,7 +84,7 @@ internal fun receiverHomeScreenPartialFailureScreenshot() { afternoteIcons = emptyList(), ), onEvent = {}, - actions = ReceiverHomeActions.Noop, + actions = noopActions, ) } } diff --git a/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeDownloadSnackbarTest.kt b/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeDownloadSnackbarTest.kt index ab94cb0335..016c76e5c0 100644 --- a/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeDownloadSnackbarTest.kt +++ b/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeDownloadSnackbarTest.kt @@ -14,6 +14,15 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.GraphicsMode +/** 이 테스트의 관심 밖인 외부 라우팅을 채우는 no-op 묶음. */ +private val noopActions = + ReceiverHomeActions( + onSettingClick = {}, + onNavigateToMindRecord = {}, + onNavigateToTimeLetter = {}, + onNavigateToAfternote = {}, + ) + /** * 수신자 홈 내려받기 실패 안내의 Snackbar 표출 가드 (#1391). * @@ -57,7 +66,7 @@ class ReceiverHomeDownloadSnackbarTest { download = download, ), onEvent = { }, - actions = ReceiverHomeActions.Noop, + actions = noopActions, ) } } diff --git a/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeHeaderTest.kt b/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeHeaderTest.kt index 8e88445a62..4ca6b05018 100644 --- a/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeHeaderTest.kt +++ b/feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeHeaderTest.kt @@ -16,6 +16,15 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config +/** 이 테스트의 관심 밖인 외부 라우팅을 채우는 no-op 묶음. */ +private val noopActions = + ReceiverHomeActions( + onSettingClick = {}, + onNavigateToMindRecord = {}, + onNavigateToTimeLetter = {}, + onNavigateToAfternote = {}, + ) + /** * 수신자 홈 헤더가 수신자가 실제로 할 수 있는 액션만 두는지 (#613, 시안 4327:73626). * @@ -41,7 +50,7 @@ class ReceiverHomeHeaderTest { ReceiverHomeScreen( uiState = ReceiverHomeUiState.Loading, onEvent = {}, - actions = ReceiverHomeActions.Noop, + actions = noopActions, ) } } @@ -58,7 +67,7 @@ class ReceiverHomeHeaderTest { ReceiverHomeScreen( uiState = ReceiverHomeUiState.Loading, onEvent = {}, - actions = ReceiverHomeActions.Noop.copy(onSettingClick = { settingClicks += 1 }), + actions = noopActions.copy(onSettingClick = { settingClicks += 1 }), ) } }