-
Notifications
You must be signed in to change notification settings - Fork 1
[REFACTOR] 모듈 의존성 정리 #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[REFACTOR] 모듈 의존성 정리 #272
Conversation
워크스루모듈 의존성을 정리하고 변경사항
Sequence Diagram(s)sequenceDiagram
participant Receiver as AlarmReceiver
participant Intent as Android Intent
participant Alarm as Alarm Model
participant Repo as AlarmRepository
Note over Receiver,Repo: Before: Parcelable + AlarmUseCase
Receiver->>Intent: getParcelableExtra(EXTRA_ALARM)
Intent-->>Receiver: Alarm (Parcelable 객체)
Receiver->>Receiver: alarmUseCase.updateAlarmActive()
Note over Receiver,Repo: After: JSON + Repository
Receiver->>Intent: getStringExtra(EXTRA_ALARM)
Intent-->>Receiver: JSON 문자열
Receiver->>Alarm: Alarm.fromJson(jsonString)
Alarm-->>Receiver: Alarm (복원됨)
Receiver->>Repo: alarmRepository.updateAlarmActive()
sequenceDiagram
participant ViewModel as AlarmAddEditViewModel
participant SoundMgr as AlarmSoundManager
participant Repo as AlarmRepository
participant Scheduler as AlarmScheduler
Note over ViewModel,Scheduler: Before: AlarmUseCase (통합 인터페이스)
ViewModel->>ViewModel: alarmUseCase.getAlarmSounds()
ViewModel->>ViewModel: alarmUseCase.insertAlarm()
ViewModel->>ViewModel: alarmUseCase.scheduleAlarm()
Note over ViewModel,Scheduler: After: 분리된 컴포넌트
ViewModel->>SoundMgr: alarmSoundManager.getAlarmSounds()
ViewModel->>Repo: alarmRepository.insertAlarm()
ViewModel->>Scheduler: alarmScheduler.scheduleAlarm()
추정 코드 리뷰 노력🎯 5 (Critical) | ⏱️ ~120 minutes 특별 주의 영역
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (7)
gradle/dependencyGraph.gradle (2)
46-222: 모듈 분류 및 DOT 생성 로직이 명확하나, 장황한 매개변수 목록 리팩토링 추천
categorizeModule()과applyColorTrait()메서드의 11개 매개변수 목록이 반복적이고 유지보수성을 해치고 있습니다. 모듈 분류를 데이터 클래스나 맵 기반 구조로 정리하면 코드 중복을 줄이고 확장성을 높일 수 있습니다.예를 들어:
@Immutable class ModuleCategory { Set<Project> features = [] Set<Project> domains = [] Set<Project> cores = [] Set<Project> data = [] // ... 기타 카테고리 } static ModuleCategory categorizeAll(Project rootProject) { def categories = new ModuleCategory() // categorizeModule 로직을 ModuleCategory 내부로 이동 // applyColorTrait 로직도 통합 가능 }이렇게 리팩토링하면 메서드 시그니처가 간결해지고 새로운 카테고리 추가 시 한 곳만 수정하면 됩니다.
73-103: BFS 순회 로직은 올바르나, 분류 작업 중복 최적화 기회모듈 분류가 여러 지점에서 수행되고 있습니다 (라인 77-79에서
categorizeModule()호출, 이후 라인 119-121에서applyColorTrait()호출 시 다시 분류). 분류 결과를 캐싱하거나 단일 순회 패스로 통합하면 불필요한 반복을 줄일 수 있습니다.feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt (2)
36-36: JSON 역직렬화 실패 시 로깅을 추가하는 것을 고려하세요.
Alarm.fromJson()이 실패하면 null을 반환하는데, 현재는 조용히 실패합니다. 디버깅과 모니터링을 위해 역직렬화 실패 시 로그를 남기는 것을 권장합니다.-val alarm = intent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) +val alarm = intent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let { json -> + Alarm.fromJson(json) ?: run { + Log.e("AlarmInteractionActivity", "Failed to deserialize alarm from JSON: $json") + null + } +}
62-67: JSON 역직렬화 실패 시 로깅을 추가하는 것을 고려하세요.새로운 인텐트의 알람 데이터 역직렬화가 실패할 경우에도 로그를 남기는 것을 권장합니다.
val onNewIntentConsumer = Consumer<Intent> { newIntent -> - val newAlarm = newIntent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) + val newAlarm = newIntent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let { json -> + Alarm.fromJson(json) ?: run { + Log.e("AlarmInteractionActivity", "Failed to deserialize alarm from new intent: $json") + null + } + } newAlarm?.let { alarm -> navigator.navigateToAlarmAction(alarm = alarm) } }core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt (2)
48-61: JSON 역직렬화 실패 시 로깅 추가를 고려하세요.
Alarm.fromJson()이 실패하면 null을 반환하는데, 알람이 울리지 않는 중요한 시나리오에서 실패 원인을 추적하기 어려울 수 있습니다. 역직렬화 실패 시 로그를 남기는 것을 권장합니다.-val alarm: Alarm? = alarmServiceIntent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) +val alarm: Alarm? = alarmServiceIntent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let { json -> + Alarm.fromJson(json) ?: run { + Log.e("AlarmReceiver", "Failed to deserialize alarm from JSON in ACTION_ALARM_TRIGGERED") + null + } +}
63-73: JSON 역직렬화 실패 시 로깅 추가를 고려하세요.스누즈 알람 처리 시에도 역직렬화 실패를 로깅하는 것을 권장합니다.
-val alarm: Alarm? = intent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) +val alarm: Alarm? = intent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let { json -> + Alarm.fromJson(json) ?: run { + Log.e("AlarmReceiver", "Failed to deserialize alarm from JSON in ACTION_ALARM_SNOOZED") + null + } +}core/media/src/main/java/com/yapp/media/sound/AlarmSoundManagerImpl.kt (1)
16-24: 사운드 초기화 로직이 안전하게 처리되었습니다.빈 URI에 대한 방어 코드(line 22)가 좋습니다.
Uri.parse는 시스템이나 사용자가 선택한 URI를 처리하므로 일반적으로 안전하지만, 향후 견고성을 위해 try-catch 추가를 고려할 수 있습니다.필요시 적용 가능한 방어 코드:
override fun initializeSoundPlayer(uri: String) { if (uri.isBlank()) return - soundPlayer.initialize(Uri.parse(uri)) + runCatching { + soundPlayer.initialize(Uri.parse(uri)) + }.onFailure { + // Log error or use default sound + } }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
project.dot.pngis excluded by!**/*.png
📒 Files selected for processing (45)
app/build.gradle.kts(1 hunks)build-logic/src/main/java/orbit.kotlin.library.gradle.kts(1 hunks)core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmAlertPendingIntent.kt(2 hunks)core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmSnoozePendingIntent.kt(2 hunks)core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/ScheduleAlarmPendingIntent.kt(2 hunks)core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/UnScheduleAlarmPendingIntent.kt(2 hunks)core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt(5 hunks)core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt(3 hunks)core/alarm/src/main/java/com/yapp/alarm/services/AlarmService.kt(4 hunks)core/common/build.gradle.kts(1 hunks)core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt(1 hunks)core/database/build.gradle.kts(0 hunks)core/database/src/main/java/com/yapp/database/AlarmDatabase.kt(0 hunks)core/database/src/main/java/com/yapp/database/AlarmEntity.kt(1 hunks)core/database/src/main/java/com/yapp/database/MissionTypeConverter.kt(0 hunks)core/media/build.gradle.kts(1 hunks)core/media/src/main/java/com/yapp/media/di/AlarmSoundModule.kt(1 hunks)core/media/src/main/java/com/yapp/media/sound/AlarmSoundManagerImpl.kt(1 hunks)core/ui/build.gradle.kts(1 hunks)data/build.gradle.kts(0 hunks)data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt(1 hunks)data/src/main/java/com/yapp/data/local/mapper/AlarmMapper.kt(1 hunks)data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt(1 hunks)domain/build.gradle.kts(1 hunks)domain/src/main/java/com/yapp/domain/MissionMode.kt(0 hunks)domain/src/main/java/com/yapp/domain/media/AlarmSoundManager.kt(1 hunks)domain/src/main/java/com/yapp/domain/model/Alarm.kt(1 hunks)domain/src/main/java/com/yapp/domain/model/AlarmSound.kt(1 hunks)domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt(1 hunks)domain/src/main/java/com/yapp/domain/usecase/AlarmUseCase.kt(0 hunks)domain/src/main/java/com/yapp/domain/usecase/GetMissionTypeUseCase.kt(0 hunks)feature/alarm-interaction/build.gradle.kts(1 hunks)feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt(2 hunks)feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionNavGraph.kt(3 hunks)feature/home/src/main/java/com/yapp/home/HomeViewModel.kt(9 hunks)feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditContract.kt(1 hunks)feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt(1 hunks)feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditViewModel.kt(13 hunks)feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmSoundBottomSheet.kt(1 hunks)feature/mission/src/main/java/com/yapp/mission/MissionContract.kt(1 hunks)feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt(1 hunks)feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt(1 hunks)feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt(4 hunks)gradle/dependencyGraph.gradle(1 hunks)gradle/libs.versions.toml(2 hunks)
💤 Files with no reviewable changes (7)
- core/database/src/main/java/com/yapp/database/AlarmDatabase.kt
- domain/src/main/java/com/yapp/domain/usecase/GetMissionTypeUseCase.kt
- data/build.gradle.kts
- domain/src/main/java/com/yapp/domain/usecase/AlarmUseCase.kt
- core/database/src/main/java/com/yapp/database/MissionTypeConverter.kt
- core/database/build.gradle.kts
- domain/src/main/java/com/yapp/domain/MissionMode.kt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-27T15:20:35.256Z
Learnt from: DongChyeon
Repo: YAPP-Github/Orbit-Android PR: 238
File: feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt:118-120
Timestamp: 2025-07-27T15:20:35.256Z
Learning: MissionRoute와 MissionScreen이 같은 파일(feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt)에 있을 때, MissionRoute에서 BackHandler를 사용하면 파일 레벨의 import 문은 유지되어야 함.
Applied to files:
feature/mission/src/main/java/com/yapp/mission/MissionContract.ktfeature/mission/src/main/java/com/yapp/mission/MissionViewModel.ktcore/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.ktfeature/mission/src/main/java/com/yapp/mission/MissionScreen.kt
🧬 Code graph analysis (5)
core/common/build.gradle.kts (1)
domain/src/main/java/com/yapp/domain/model/Alarm.kt (1)
id(7-29)
feature/alarm-interaction/build.gradle.kts (1)
domain/src/main/java/com/yapp/domain/model/Alarm.kt (1)
id(7-29)
core/ui/build.gradle.kts (1)
domain/src/main/java/com/yapp/domain/model/Alarm.kt (1)
id(7-29)
domain/build.gradle.kts (1)
domain/src/main/java/com/yapp/domain/model/Alarm.kt (1)
id(7-29)
build-logic/src/main/java/orbit.kotlin.library.gradle.kts (2)
build-logic/src/main/java/com/yapp/convention/KotlinAndroid.kt (1)
configureKotlin(50-64)build-logic/src/main/java/com/yapp/convention/TestKotlin.kt (1)
configureTestKotlin(6-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (50)
gradle/dependencyGraph.gradle (5)
1-44: Gradle 작업 선언 및 구조가 양호함
ProjectDependencyGraphTask의 작업 생명 주기, 속성 주입, 그리고 임시 파일 정리 로직이 올바르게 구현되었습니다.@Internal주석이 캐싱 대상에서 제외할 중간 생성 파일에 올바르게 적용되었고,graphImageFile의@OutputFile표시도 적절합니다.
37-39: @Inject ExecOperations 속성 접근 방식 확인 필요Gradle 컨텍스트에서
@Inject주석이 있는getExecOperations()메서드를execOperations속성으로 직접 접근하는 것이 의도된 동작인지 확인해주시기 바랍니다. Groovy 속성 접근 규약과 Gradle 작업 API 호환성을 검증해주시면 좋겠습니다.
50-56: 프로젝트 그래프 순회 및 루트 프로젝트 식별 로직이 명확함첫 번째 BFS(라인 50-56)에서 모든 프로젝트를 수집하고, 두 번째 BFS(라인 73-103)에서 의존성을 처리하면서
rootProjects.remove(dependency)(라인 93)를 통해 실제 루트 프로젝트를 식별하는 방식이 효과적입니다. 이렇게 하면 그래프 시각화에서 진정한 루트 프로젝트만 상단에 배치됩니다.Also applies to: 73-103
106-143: StringBuilder를 이용한 DOT 형식 생성이 효율적이고 가독성 좋음그래프 콘텐츠를 StringBuilder로 조립하는 방식이 문자열 연결보다 효율적이고, 각 섹션(프로젝트, 의존성)이 명확히 구분되어 있습니다. 동적 특성(traits) 적용도 직관적으로 구현되었습니다.
224-236: Gradle 생명 주기 훅 활용이 적절함
gradle.projectsEvaluated()훅을 통해 모든 프로젝트가 로드된 후에 DOT 그래프 콘텐츠를 설정하는 방식이 올바른 순서를 보장합니다. 이렇게 하면 부분적으로 초기화된 프로젝트 그래프 상태에서 그래프가 생성되는 문제를 방지합니다.feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt (1)
52-52: LGTM! 도메인 모듈 재구성이 올바르게 반영되었습니다.
MissionMode의 import 경로가com.yapp.domain.model.MissionMode로 정확하게 업데이트되었습니다. 이는 PR의 모듈 의존성 정리 목표와 일치하며, 도메인 레이어의 구조 개선에 기여합니다.feature/mission/src/main/java/com/yapp/mission/MissionContract.kt (1)
3-3: LGTM! Import 경로 업데이트가 정확합니다.도메인 모델 패키지 재구성에 따라
MissionModeimport가 올바르게 업데이트되었습니다. Contract 내 State에서 사용되는 타입 참조에 영향이 없습니다.feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt (1)
10-10: LGTM! ViewModel의 import가 올바르게 업데이트되었습니다.
MissionMode의 새로운 패키지 경로로 import가 정확하게 변경되었습니다. ViewModel의 비즈니스 로직에는 영향이 없으며, 도메인 레이어 재구성과 일관성을 유지합니다.core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt (1)
3-3: LGTM! 네비게이션 라우트의 import가 일관되게 업데이트되었습니다.
MissionMode의 import 경로가 올바르게 변경되었습니다.@Serializable데이터 클래스의 직렬화 동작과 기본값 설정에는 영향이 없으며, feature 모듈과 core 모듈 간 타입 참조의 일관성이 유지됩니다.core/common/build.gradle.kts (1)
3-6: 플러그인 및 의존성 이동이 명확히 정의되어 있습니다.Compose 네비게이션 의존성을 core/ui로 이동한 것은 좋은 결정입니다. core/common이 더 이상 Compose 레이아웃 관련 요소에 의존하지 않음으로써 모듈 계층을 개선했습니다.
domain/build.gradle.kts (3)
12-12: kotlinx.coroutines.core 의존성 추가가 합리적입니다.domain 모듈이 비동기 작업을 지원해야 하는 경우, coroutines-core는 필수적입니다. 다만 실제로 domain의 어느 부분에서 coroutines가 사용되는지 코드 검토 시 확인이 필요합니다.
2-3: domain 모듈의 플러그인 변경 검증 완료 - 변경사항 승인됨검증 결과, 다음을 확인했습니다:
- ✓ domain 모듈에서 Android/AndroidX 임포트 없음
- ✓ build.gradle.kts에 Android 의존성 미포함
- ✓ AlarmScheduler, AlarmSoundManager 등 모든 파일이 순수 인터페이스/모델로 정의됨
- ✓ 13개 모듈(core, feature, data)이 projects.domain을 계속 의존하며 정상 작동 가능
orbit.android.library→orbit.kotlin.library전환은 아키텍처를 개선하는 적절한 변경입니다. domain 모듈은 플랫폼 독립적인 비즈니스 로직 계약을 제공하고, 플랫폼별 구현은 core 모듈에서 담당하는 계층 구조가 명확해졌습니다.
6-8: 모든 모듈이 JVM 17로 일관되게 설정되어 있습니다.프로젝트의 convention 플러그인(
configureKotlin())에서 이미 모든 Kotlin 모듈에jvmTarget.set(JvmTarget.JVM_17)로 설정되어 있으며, Android 모듈들도JavaVersion.VERSION_17을 사용하고 있습니다. domain 모듈의 명시적jvmToolchain(17)설정은 이 convention 설정과 일치하므로, 프로젝트 전체에서 JVM 버전이 일관되게 유지됩니다.feature/alarm-interaction/build.gradle.kts (1)
5-5: 직렬화 플러그인 추가가 일관성 있게 적용되었습니다.domain의 Alarm 모델이
@Serializable어노테이션을 사용하고, feature/alarm-interaction에서 이를 사용하므로, Kotlin 직렬화 플러그인의 추가는 필수적입니다. core/common, domain, core/ui와 함께 일관성 있게 적용되었습니다.core/ui/build.gradle.kts (3)
6-6: Kotlin 직렬화 플러그인 추가는 적절합니다.core/ui 모듈이 domain의 @serializable 모델(Alarm 등)을 직렬화 형태로 처리하므로, 이 플러그인은 필수적입니다.
19-20: Compose 네비게이션 의존성이 core.common에서 core.ui로 이동되었습니다.이는 좋은 모듈화 결정입니다:
- core.common: 기본 공통 기능만 제공하는 가벼운 모듈
- core.ui: Compose UI와 네비게이션을 통합 관리
이러한 책임 분리는 모듈 간 결합도를 낮추고 재사용성을 높입니다.
14-14: 순환 의존성 위험 없음 - 검증 완료의존성 구조를 검증한 결과:
- core.ui → core.common ✓
- core.ui → domain ✓
- core.common → core.ui: 없음 ✓
- domain → core.ui: 없음 ✓
domain은 프로젝트 의존성이 없고, core.common과 core.ui 간 순환 의존성이 없어 모듈 구조가 안전합니다. core.ui가 domain 모델을 직접 사용하는 것도 아키텍처상 문제없습니다.
gradle/libs.versions.toml (1)
4-4:javaxInject버전 정의는 있으나 실제 의존성 선언이 없습니다.검증 결과:
gradle/libs.versions.toml에javaxInject = "1"버전과javax-inject라이브러리 매핑이 정의됨- 모든
build.gradle.kts파일을 검색했으나javaxInject또는javax-inject를 참조하는 모듈이 없음- 코드에서는 여러 ViewModel과 클래스에서
@Inject어노테이션을 사용 중- 현재 Hilt 의존성이
javax.inject를 전이적으로 제공하고 있을 수 있음어느 모듈이
javax-inject를 의존해야 하는지, 또는 이 변경이 실제로 필요한지 확인이 필요합니다. 버전이 정의되었다면 이를 사용하는 명시적인 의존성 선언이 있어야 합니다.app/build.gradle.kts (2)
41-68: 검증 완료: 제거된 의존성이 모두 transitive dependency로 포함됨리뷰 코멘트의 우려사항이 확인되었습니다. 제거된 의존성들은 모두 transitive dependency로 여전히 포함되어 있습니다:
- core.alarm: feature/alarm-interaction, feature/fortune, feature/mission에서 사용
- core.media: feature/alarm-interaction, feature/fortune, feature/home, feature/mission, feature/onboarding에서 사용
- core.network, core.datastore: data 모듈에서 사용
- core.domain: 모든 feature 모듈과 data 모듈에서 사용
app 모듈은 직접 이들 의존성을 사용하지 않으므로(import 없음), gradle의 transitive dependency 메커니즘이 자동으로 필요한 의존성들을 포함합니다. 따라서 build.gradle.kts에서 직접 선언을 제거한 것은 올바른 의존성 정리이며, 컴파일 및 런타임 시 문제가 없습니다.
36-38: NullSafeMutableLiveData 린트 규칙 비활성화 제거를 권장합니다.프로젝트는 lifecycle 버전 2.9.4를 사용 중이며, 이 버전은 2024년 5월 이후 릴리스된 버전으로 NullSafeMutableLiveData의 false positive 문제가 이미 해결되었고, 9월 릴리스에서 스마트 캐스트 지원도 개선되었습니다.
app 모듈에서 직접 MutableLiveData를 사용하지 않는 현황에서 이 규칙을 비활성화할 명확한 기술적 근거가 없습니다. 규칙을 재활성화하거나, 특정한 비활성화 이유가 있다면 코드 주석으로 명시하기를 권장합니다.
lint { disable.add("NullSafeMutableLiveData") }Likely an incorrect or invalid review comment.
core/media/build.gradle.kts (1)
13-15: LGTM! domain 의존성 추가가 적절합니다.AlarmSoundManager 인터페이스를 domain 모듈로 이동하고 core/media에서 구현하는 리팩토링이 올바르게 반영되었습니다.
core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/ScheduleAlarmPendingIntent.kt (1)
33-33: LGTM! JSON 직렬화 적용이 일관적입니다.다른 알람 PendingIntent 파일들과 동일하게 JSON 직렬화 방식을 적용했습니다. 코드 일관성이 유지되고 있습니다.
data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt (1)
3-3: LGTM! 매퍼 이동이 아키텍처를 개선합니다.
toDomain()매퍼를 database 모듈에서 data/local/mapper로 이동한 것은 올바른 관심사 분리입니다. database 모듈이 domain 타입에 대한 지식을 가지지 않게 되어 의존성이 정리되었습니다.core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmSnoozePendingIntent.kt (1)
31-31: LGTM! 스누즈 인텐트도 JSON 직렬화를 적용했습니다.알람 스누즈 플로우에서도 JSON 직렬화 방식이 일관되게 적용되었습니다.
core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmAlertPendingIntent.kt (1)
35-35: LGTM! 알람 알림 인텐트도 JSON 직렬화가 적용되었습니다.알람 알림 플로우에서도 JSON 직렬화 방식이 일관되게 적용되었습니다.
core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/UnScheduleAlarmPendingIntent.kt (1)
29-29: JSON 직렬화 변경 검증 완료 - 역직렬화 처리 확인됨AlarmReceiver에서 역직렬화 처리를 확인했습니다:
- Line 49:
alarmServiceIntent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson)- Line 64:
intent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson)두 위치 모두 안전한 null 처리(
?.let())와 함께Alarm::fromJson()으로 올바르게 역직렬화되고 있습니다. JSON 문자열 기반의 직렬화/역직렬화 방식이 일관되게 적용되어 있으므로 승인 가능합니다.core/database/src/main/java/com/yapp/database/AlarmEntity.kt (2)
33-34: 데이터베이스 마이그레이션이 올바르게 처리되었습니다.검증 결과, 다음과 같이 마이그레이션이 정확하게 구현되어 있습니다:
- 마이그레이션 구현:
DatabaseMigrations.kt의MIGRATION_1_2에서missionType컬럼을 정수형으로 변환하고, 기본값 1(TAP)을 설정- 기존 데이터 보존: INSERT 쿼리에서 기본값이 자동으로 적용되어 기존 데이터 손실 없음
- 타입 변환: 전체 코드베이스에서
MissionType.fromInt()와.value를 사용해 enum과 int 간 일관된 변환- 테스트 커버리지:
MigrationTest.kt에서 마이그레이션 동작을 검증
33-34: 검증 완료: DEFAULT_MISSION_TYPE_VALUE 값이 올바릅니다.MissionType enum 정의를 확인한 결과:
enum class MissionType(val value: Int) { NONE(0), TAP(1), SHAKE(2), }
DEFAULT_MISSION_TYPE_VALUE = 1은MissionType.TAP의 value와 정확히 일치하며, 도메인 모델의 기본값(MissionType.TAP)과도 일관성 있게 매핑되어 있습니다. 변환된Int타입이 기대하는 enum 값과 정확히 대응되므로 문제가 없습니다.feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditContract.kt (1)
157-157: 빈 문자열 soundUri 처리는 올바르게 구현되어 있습니다.검증 결과 다음을 확인했습니다:
- Alarm 모델의
soundUri기본값은 명시적으로 빈 문자열("")로 정의됨 (Alarm.kt:20)- AlarmSoundManagerImpl에서
if (uri.isBlank()) return으로 방어 처리 (line 22)- AlarmService의 startSound()에서 빈 문자열 시 기본 시스템 알람음으로 폴백 (lines 223-227)
- AlarmAddEditViewModel에서 soundIndex가 유효하지 않으면 첫 번째 사운드로 폴백
line 157의
.orEmpty()사용은 Alarm 모델의 정의된 기본값과 일치하며, 빈 문자열은 알람 재생 로직의 두 계층(SoundManager, AlarmService)에서 안전하게 처리됩니다.feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt (1)
544-559: LGTM! 도메인 모델 변경과 일관성 있게 업데이트되었습니다.
AlarmSound의uri타입이Uri에서String으로 변경됨에 따라 폴백 값도Uri.EMPTY에서 빈 문자열""로 적절하게 변경되었습니다.getOrElse를 사용하여 인덱스 범위를 벗어난 경우를 안전하게 처리하고 있습니다.domain/src/main/java/com/yapp/domain/model/AlarmSound.kt (1)
3-6: LGTM! Android 의존성 제거로 도메인 레이어가 개선되었습니다.
Uri타입을String으로 변경하여 도메인 모델에서 Android 의존성을 제거했습니다. 이는 PR의 핵심 목표와 일치하며, 직렬화와 이식성 측면에서도 더 나은 선택입니다.domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt (1)
9-9: LGTM! 도메인 모델 변경과 일관성 있게 업데이트되었습니다.
AlarmSound모델의 변경에 맞춰initializeSoundPlayer의 파라미터 타입을Uri에서String으로 변경했습니다.core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt (1)
16-16: LGTM! UseCase에서 Repository로의 마이그레이션이 올바르게 수행되었습니다.
AlarmUseCase를 제거하고AlarmRepository를 직접 사용하도록 변경이 잘 적용되었습니다. 이는 불필요한 래퍼를 제거하려는 PR의 목표와 일치합니다.Also applies to: 40-40, 96-96
feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmSoundBottomSheet.kt (1)
270-272: LGTM! 프리뷰 코드가 도메인 모델 변경에 맞춰 업데이트되었습니다.
AlarmSound생성자가String타입의 uri를 받도록 변경됨에 따라 프리뷰 코드도 적절하게 업데이트되었습니다.data/src/main/java/com/yapp/data/local/mapper/AlarmMapper.kt (1)
1-43: LGTM! 엔티티와 도메인 모델 간의 매퍼가 올바르게 구현되었습니다.
AlarmEntity와Alarm간의 양방향 변환 로직이 정확하게 구현되었습니다:
- 모든 필드가 1:1로 매핑됨
MissionTypeenum 변환이 적절하게 처리됨 (fromInt()/.value)- 데이터 레이어와 도메인 레이어 간의 명확한 경계 제공
feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt (2)
12-12: LGTM! UseCase에서 Repository로의 마이그레이션이 올바르게 수행되었습니다.
AlarmUseCase를 제거하고AlarmRepository를 직접 사용하도록 변경되었습니다. 이는 불필요한 래퍼 레이어를 제거하려는 PR의 목표와 일치합니다.Also applies to: 33-33
133-156: LGTM! 코드 가독성이 개선되었습니다.변수명을
defaultSoundUri에서defaultSound로 변경하여 의미가 더 명확해졌고,AlarmRepository를 통한 알람 사운드 조회 및 알람 생성이 올바르게 구현되었습니다.core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt (1)
7-7: 의존성 교체가 깔끔하게 처리되었습니다.
AlarmUseCase를AlarmRepository로 교체하여 불필요한 래퍼 레이어를 제거했습니다. 로직 변경 없이 동일한 기능을 유지하고 있습니다.Also applies to: 20-20, 38-38
domain/src/main/java/com/yapp/domain/media/AlarmSoundManager.kt (1)
5-12: 도메인 레이어에 적절한 추상화를 도입했습니다.
AlarmSoundManager인터페이스를 도메인 레이어에 배치하여 Android 의존성(Uri 타입 등)을 제거하고, 알람 사운드 관리 로직을 명확하게 분리했습니다. 문자열 기반 URI 처리는 플랫폼 독립적인 도메인 레이어에 적합합니다.core/media/src/main/java/com/yapp/media/di/AlarmSoundModule.kt (1)
11-19: DI 모듈 설정이 올바릅니다.
AlarmSoundManager인터페이스를 싱글톤으로 바인딩하는 표준적인 Dagger Hilt 구성입니다.core/alarm/src/main/java/com/yapp/alarm/services/AlarmService.kt (2)
30-30: 의존성 교체가 적절하게 수행되었습니다.
AlarmUseCase를AlarmRepository로 교체하여 불필요한 래퍼를 제거했습니다. 모든 호출 지점이 일관되게 업데이트되었습니다.Also applies to: 44-44, 196-196
83-83: 직렬화 방식 변경이 코드를 단순화합니다.Parcelable 기반(API 레벨 분기 포함)에서 JSON 기반 직렬화로 변경하여 코드가 더 간결해졌습니다.
Alarm.fromJson의 null 안전 처리와 에러 로깅(line 86)이 적절합니다.feature/home/src/main/java/com/yapp/home/HomeViewModel.kt (2)
10-10: 관심사 분리가 명확하게 개선되었습니다.
AlarmUseCase를AlarmRepository와AlarmScheduler로 분리하여 데이터 작업과 스케줄링 작업의 책임을 명확히 구분했습니다. 더 나은 아키텍처 구조를 제공합니다.Also applies to: 13-13, 34-35
183-203: 모든 호출 지점이 일관되게 업데이트되었습니다.Repository와 Scheduler의 역할이 적절하게 분리되어 사용되고 있습니다:
- 데이터 작업:
alarmRepository사용- 알람 스케줄링:
alarmScheduler사용로직 변경 없이 깔끔하게 리팩토링되었습니다.
Also applies to: 247-265, 279-304, 314-323
data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt (2)
12-14: 의존성 단순화가 효과적입니다.
RingtoneManagerHelper와SoundPlayer를 직접 의존하는 대신AlarmSoundManager로 통합하여 repository의 책임을 간소화했습니다.
16-38: 위임 패턴이 깔끔하게 구현되었습니다.모든 사운드 관련 작업을
AlarmSoundManager로 위임하여 코드 중복을 제거하고, Android 특정 타입(Uri)을 문자열로 변경하여 플랫폼 의존성을 줄였습니다.feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionNavGraph.kt (1)
3-3: 네비게이션 직렬화가 올바르게 개선되었습니다.JSON 기반 직렬화로 전환하고
serializeAsValue에서 URI 인코딩을 추가하여 네비게이션 인자에 특수 문자가 포함될 때 발생할 수 있는 문제를 방지했습니다.Also applies to: 17-17, 29-35
core/media/src/main/java/com/yapp/media/sound/AlarmSoundManagerImpl.kt (2)
10-14: 싱글톤 구현이 적절합니다.
AlarmSoundManager인터페이스의 구현체로서 올바르게 DI 설정되었습니다. 의존성 주입과 싱글톤 스코프가 적절합니다.
26-40: 사운드 플레이어 위임이 깔끔합니다.모든 사운드 재생 관련 작업이
SoundPlayer로 명확하게 위임되어 있습니다.domain/src/main/java/com/yapp/domain/model/Alarm.kt (1)
27-27: 필수 import 누락으로 컴파일 실패
Json.decodeFromString(...)은kotlinx.serialization.decodeFromString확장 함수를 필요로 합니다. 현재 파일에 해당 import가 없어 빌드 타임에Unresolved reference: decodeFromString오류가 발생합니다. 아래 import를 추가해주세요.-import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.JsonLikely an incorrect or invalid review comment.
Related issue 🛠
closed #271
어떤 변경사항이 있었나요?
CheckPoint ✅
PR이 다음 요구 사항을 충족하는지 확인하세요.
Work Description ✏️
이 모듈을 수정하면 공유 Compose UI에 의존하는 모든 feature 모듈에 변경 영향이 전파된다
(core/ui/build.gradle.kts:3-26).
이 모듈을 사용하는 하위 모듈들에 노출되는 범위(surface)를 줄였다
(core/common/build.gradle.kts:3-16).
이로 인해 domain을 수정해도 더 이상 Android 리소스·매니페스트 관련 작업이 함께 invalidation되지 않는다
(domain/build.gradle.kts:1-13).
덕분에 media 모듈을 수정해도 데이터 레이어 전체를 끌고 오는(full drag) 문제가 사라졌다
(core/media/build.gradle.kts:3-15, data/build.gradle.kts:12-24, core/database/build.gradle.kts:3-24).
(app/build.gradle.kts:41-69).
그 결과 assembleDebug 실행 시, 회귀(regression)가 발생하면 보다 넓은 범위의 invalidation으로 드러나도록 구조가 개선되었다.
이전
이후
gradle-profiler를 통해 클린 빌드 측정 시 평균 18s -> 15s (3초) 단축
Uncompleted Tasks 😅
To Reviewers 📢
Summary by CodeRabbit
릴리스 노트