Skip to content

Conversation

@MoonsuKang
Copy link
Contributor

@MoonsuKang MoonsuKang commented Aug 3, 2025

📌 ISSUE

closed #297

📄 Work Description

에러 핸들링 중앙화 해버리기
네트워크 감지 추가

✨ PR Point

  • 실험적으로 일기 부분에 SafeApiCall도입

📸 ScreenShot/Video

Summary by CodeRabbit

  • New Features

    • Added centralized error message management with localized support for various error scenarios.
    • Introduced a real-time network connectivity observer for improved network status handling.
    • Implemented a new use case for writing diaries.
  • Improvements

    • Enhanced error handling and messaging across all screens, replacing hardcoded messages with dynamic, localized ones.
    • Updated all network-dependent features to use the new network connectivity observer for more reliable status checks.
    • Simplified and unified repository and ViewModel logic for error and network handling.
  • Bug Fixes

    • Improved user feedback for network and API errors with more descriptive, localized messages.
  • Refactor

    • Removed legacy network utility and error message constants.
    • Streamlined API call error handling and response mapping.
  • Documentation

    • Added new and updated string resources for error messages in both Korean and English.
  • Tests

    • Updated test data sources to align with new error and network handling logic.

- 에러 메시지를 제공하는 ErrorMessageProvider 인터페이스와 구현체 ErrorMessageProviderImpl을 추가했습니다.
- ErrorMessageModule을 추가하여 ErrorMessageProvider를 주입할 수 있도록 설정했습니다.
- ViewModel에서 NetworkUtil 대신 NetworkConnectivityObserver 사용
- 에러 메시지를 ErrorMessages 객체 대신 ErrorMessageProvider를 통해 제공하도록 수정
- 각 ViewModel의 네트워크 요청 및 에러 처리 로직에 변경된 방식 적용
@MoonsuKang MoonsuKang requested a review from SYAAINN August 3, 2025 15:38
@MoonsuKang MoonsuKang self-assigned this Aug 3, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 3, 2025

Walkthrough

This change centralizes error handling and network status logic across the application. It introduces new abstractions for error messages and network connectivity observation, removes hardcoded error strings, and updates ViewModels and repositories to use these new mechanisms. Supporting resources and modules are added, and obsolete utilities and constants are removed.

Changes

Cohort / File(s) Change Summary
Network Connectivity Abstraction
app/src/main/java/com/sopt/clody/core/network/NetworkConnectivityModule.kt, .../NetworkConnectivityObserver.kt, .../NetworkStatus.kt
Added a reactive network connectivity observer, Dagger module for DI, and a sealed class for network status representation.
Error Message Provider System
app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessageModule.kt, .../ErrorMessageProvider.kt, .../ErrorMessageProviderImpl.kt
Introduced an interface and implementation for centralized, localized error message provision, and a Dagger module for dependency injection.
Error Message Resource Strings
app/src/main/res/values/strings.xml, app/src/main/res/values-ko/strings.xml
Added new error-related string resources in both English and Korean for all error scenarios.
Safe API Call & Error Wrapping
app/src/main/java/com/sopt/clody/data/remote/util/SafeApiCall.kt, .../ApiError.kt
Added a utility function for safe API calls returning Result<T> and a custom error class for API errors.
Diary Remote Data Source Refactor
app/src/main/java/com/sopt/clody/data/remote/datasource/DiaryRemoteDataSource.kt, .../datasourceimpl/DiaryRemoteDataSourceImpl.kt, app/src/test/java/com/sopt/clody/datasource/FakeDiaryRemoteDataSource.kt
Changed all API response types from ApiResponse<T> to Result<T>, updated implementation and tests to use new error/result handling.
Repository & Use Case Refactor
app/src/main/java/com/sopt/clody/data/repositoryimpl/DiaryRepositoryImpl.kt, .../domain/usecase/FetchDraftDiaryUseCase.kt, .../domain/usecase/SaveDraftDiaryUseCase.kt, .../domain/usecase/WriteDiaryUseCase.kt
Simplified error handling, removed manual error mapping, and updated to use new result types and error message provider.
ViewModel Network/Error Refactor
app/src/main/java/com/sopt/clody/presentation/ui/auth/signup/SignUpViewModel.kt, .../ui/auth/timereminder/TimeReminderViewModel.kt, .../ui/diarylist/screen/DiaryListViewModel.kt, .../ui/home/screen/HomeViewModel.kt, .../ui/login/LoginViewModel.kt, .../ui/replydiary/ReplyDiaryViewModel.kt, .../ui/replyloading/screen/ReplyLoadingViewModel.kt, .../ui/setting/notificationsetting/screen/NotificationSettingViewModel.kt, .../ui/setting/screen/AccountManagementViewModel.kt, .../ui/writediary/screen/WriteDiaryViewModel.kt
Replaced NetworkUtil with NetworkConnectivityObserver, replaced hardcoded error messages with ErrorMessageProvider, updated constructor dependencies and error handling logic.
Obsolete Utility/Constant Removal
app/src/main/java/com/sopt/clody/data/remote/util/NetworkUtil.kt, .../presentation/utils/network/ErrorMessages.kt, app/src/main/java/com/sopt/clody/di/NetworkModule.kt
Removed obsolete network utility, error message constants, and related DI provider.
UI Call Signature Update
app/src/main/java/com/sopt/clody/presentation/ui/home/screen/HomeScreen.kt
Updated method call to enableDraftAlarm() to match new signature (removed Context argument).

Sequence Diagram(s)

sequenceDiagram
    participant VM as ViewModel
    participant NCO as NetworkConnectivityObserver
    participant EMP as ErrorMessageProvider
    participant Repo as Repository
    participant DS as DataSource
    participant API as Remote API

    VM->>NCO: Check networkStatus (Flow)
    alt Network Unavailable
        VM->>EMP: getNetworkError()
        VM-->>VM: Show error message
    else Network Available
        VM->>Repo: Perform operation
        Repo->>DS: safeApiCall { API call }
        DS->>API: Make network request
        API-->>DS: Response / Error
        DS-->>Repo: Result<T> (success/failure)
        Repo-->>VM: Result<T>
        alt Failure
            VM->>EMP: getApiError(error) or getTemporaryError()
            VM-->>VM: Show error message
        else Success
            VM-->>VM: Update UI with data
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Assessment against linked issues

Objective Addressed Explanation
Centralize error handling logic (Issue #297)
Remove hardcoded error messages and use provider (Issue #297)
Replace direct network checks with observer-based approach (Issue #297)
Remove obsolete error/network utility code (Issue #297)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes detected.

Suggested labels

♻️ REFACTOR, 🔥 민재

Poem

A bunny hops through fields of code,
Centralizing errors, lightening the load.
No more strings that scatter and hide—
Now all messages are unified!
With network flows and results so neat,
This refactor’s truly a treat.
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#297-error-handle

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (2)
app/src/main/java/com/sopt/clody/presentation/ui/login/LoginViewModel.kt (1)

102-107: Complete the error handling centralization.

The error handling in validateKakaoUser and validateGoogleUser methods still uses raw error.message directly instead of the centralized ErrorMessageProvider. Consider adding appropriate error message methods to the provider for these scenarios.

For consistency with the centralized error handling pattern, consider adding methods like:

  • getSignInFailedError()
  • getSignUpFailedError()
  • getServerError()

Then replace the raw error message usage:

-_sideEffects.send(LoginContract.LoginSideEffect.ShowError(msg))
+_sideEffects.send(LoginContract.LoginSideEffect.ShowError(errorMessageProvider.getServerError()))

Also applies to: 128-135

app/src/main/java/com/sopt/clody/presentation/ui/diarylist/screen/DiaryListViewModel.kt (1)

89-94: Verify state assignment in error handling

Line 91 assigns a DiaryDeleteState.Failure value but doesn't assign it to _diaryDeleteState.value. This looks like a potential bug where the state isn't properly updated.

Apply this fix:

-                DiaryDeleteState.Failure(_failureDialogMessage.value)
+                _diaryDeleteState.value = DiaryDeleteState.Failure(_failureDialogMessage.value)
🧹 Nitpick comments (6)
app/src/main/java/com/sopt/clody/domain/repository/DiaryRepository.kt (1)

3-3: Remove unused ApiResponse import

ApiResponse is no longer referenced in this interface; keeping the import introduces an unnecessary compiler warning and noise.

-import com.sopt.clody.data.remote.dto.base.ApiResponse
app/src/main/java/com/sopt/clody/data/remote/util/SafeApiCall.kt (1)

8-25: Well-implemented error handling utility with proper coroutine support.

The function correctly handles cancellation by rethrowing CancellationException and provides good error mapping. The approach of returning Result<T> enables functional error handling throughout the app.

One consideration: Line 15 treats null response.data as a temporary error. Verify that this aligns with your API contract, as some endpoints might legitimately return null data for successful operations.

app/src/main/java/com/sopt/clody/presentation/ui/auth/timereminder/TimeReminderViewModel.kt (1)

45-46: Consider centralizing FCM token error message

The FCM token error message "FCM 못가져옴" is still hardcoded. Consider adding this to the ErrorMessageProvider for consistency and localization support.

Add a method to ErrorMessageProvider:

interface ErrorMessageProvider {
    // ... existing methods
+   fun getFcmTokenRetrievalError(): String
}
app/src/main/java/com/sopt/clody/presentation/ui/replyloading/screen/ReplyLoadingViewModel.kt (1)

85-89: Improve network status check for clarity

The network connectivity check uses a double negation pattern that could be simplified for better readability.

Consider this clearer approach:

-            val isConnected = networkConnectivityObserver.networkStatus.first() == NetworkStatus.Available
-            if (!isConnected) {
+            if (networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable) {
app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessageProviderImpl.kt (1)

49-51: Consider adding fallback for ApiError messages.

The current implementation directly returns apiError.message. Consider adding a fallback in case the message is null or not user-friendly.

 override fun getApiError(apiError: ApiError): String {
-    return apiError.message
+    return apiError.message.takeIf { it.isNotBlank() } ?: getUnknownError()
 }
app/src/main/java/com/sopt/clody/presentation/ui/setting/notificationsetting/screen/NotificationSettingViewModel.kt (1)

66-66: Network connectivity checks properly migrated to reactive pattern.

The migration from synchronous NetworkUtil checks to reactive NetworkConnectivityObserver.networkStatus.first() is architecturally sound and consistent across all methods.

Consider caching the network status check result at the beginning of each method to avoid multiple first() calls:

 viewModelScope.launch {
+    val isNetworkUnavailable = networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable
-    if (networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable) {
+    if (isNetworkUnavailable) {

Also applies to: 99-99, 149-149

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e74c5e6 and 89fe3c8.

📒 Files selected for processing (32)
  • app/src/main/java/com/sopt/clody/core/network/NetworkConnectivityModule.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/core/network/NetworkConnectivityObserver.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/core/network/NetworkStatus.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/data/remote/datasource/DiaryRemoteDataSource.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/data/remote/datasourceimpl/DiaryRemoteDataSourceImpl.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/data/remote/util/ApiError.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/data/remote/util/NetworkUtil.kt (0 hunks)
  • app/src/main/java/com/sopt/clody/data/remote/util/SafeApiCall.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/data/repositoryimpl/DiaryRepositoryImpl.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/di/NetworkModule.kt (0 hunks)
  • app/src/main/java/com/sopt/clody/domain/repository/DiaryRepository.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/domain/usecase/FetchDraftDiaryUseCase.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/domain/usecase/SaveDraftDiaryUseCase.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/domain/usecase/WriteDiaryUseCase.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/auth/signup/SignUpViewModel.kt (6 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/auth/timereminder/TimeReminderViewModel.kt (3 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/diarylist/screen/DiaryListViewModel.kt (6 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/home/screen/HomeScreen.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/home/screen/HomeViewModel.kt (9 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/login/LoginViewModel.kt (3 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/replydiary/ReplyDiaryViewModel.kt (5 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/replyloading/screen/ReplyLoadingViewModel.kt (8 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/setting/notificationsetting/screen/NotificationSettingViewModel.kt (7 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/setting/screen/AccountManagementViewModel.kt (7 hunks)
  • app/src/main/java/com/sopt/clody/presentation/ui/writediary/screen/WriteDiaryViewModel.kt (6 hunks)
  • app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessageModule.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessageProvider.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessageProviderImpl.kt (1 hunks)
  • app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessages.kt (0 hunks)
  • app/src/main/res/values-ko/strings.xml (1 hunks)
  • app/src/main/res/values/strings.xml (1 hunks)
  • app/src/test/java/com/sopt/clody/datasource/FakeDiaryRemoteDataSource.kt (1 hunks)
💤 Files with no reviewable changes (3)
  • app/src/main/java/com/sopt/clody/di/NetworkModule.kt
  • app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessages.kt
  • app/src/main/java/com/sopt/clody/data/remote/util/NetworkUtil.kt
🧰 Additional context used
🧬 Code Graph Analysis (2)
app/src/main/java/com/sopt/clody/presentation/ui/replydiary/ReplyDiaryViewModel.kt (1)
app/src/main/java/com/sopt/clody/presentation/ui/replyloading/screen/ReplyLoadingViewModel.kt (1)
  • setupRetryFlow (67-72)
app/src/main/java/com/sopt/clody/data/remote/datasourceimpl/DiaryRemoteDataSourceImpl.kt (1)
app/src/main/java/com/sopt/clody/data/remote/util/SafeApiCall.kt (1)
  • safeApiCall (8-25)
⏰ 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 (56)
app/src/main/java/com/sopt/clody/presentation/ui/home/screen/HomeScreen.kt (1)

198-202: Manual verification required: Double-check all enableDraftAlarm call sites

I ran an automated search for any enableDraftAlarm( invocations passing arguments (e.g.
rg --type kotlin --line-number 'enableDraftAlarm\('), but found no matches. Absence of hits isn’t proof—please manually review all call sites to ensure none still pass a Context parameter.

app/src/main/java/com/sopt/clody/core/network/NetworkStatus.kt (1)

3-5: Kotlin toolchain is set to 2.1.0 (≥1.9)

The version catalog confirms you’re on Kotlin 2.1.0, which satisfies the minimum requirement for data object:

• gradle/libs.versions.toml
– [versions] kotlin = "2.1.0"
– alias(libs.plugins.kotlin.android), kotlin-compose, kotlin-serialization all reference version “kotlin”

No changes needed—data object is fully supported.

app/src/main/java/com/sopt/clody/domain/usecase/SaveDraftDiaryUseCase.kt (1)

9-10: Cleaner expression body looks good

The switch to an expression body is concise and keeps behaviour identical.

app/src/main/java/com/sopt/clody/domain/usecase/WriteDiaryUseCase.kt (1)

6-11: LGTM! Clean use case implementation.

The use case follows clean architecture principles with proper dependency injection and direct delegation to the repository. The suspend operator function pattern is consistent with other use cases in the codebase.

app/src/main/java/com/sopt/clody/presentation/ui/login/LoginViewModel.kt (2)

17-17: Good addition of ErrorMessageProvider dependency.

The constructor injection of ErrorMessageProvider follows the dependency injection pattern and supports the centralized error handling refactoring.

Also applies to: 36-36


73-73: Consistent use of centralized error messages.

The replacement of hardcoded error messages with errorMessageProvider.getLoginFailedError() properly implements the centralized error handling pattern.

Also applies to: 81-81

app/src/main/res/values-ko/strings.xml (1)

199-208: Well-organized error message strings.

The new Korean error message strings are comprehensive and well-organized under a dedicated error section. The translations appear user-friendly and cover the main error scenarios needed for centralized error handling.

app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessageModule.kt (1)

11-22: Proper Dagger Hilt module implementation.

The module correctly provides a singleton ErrorMessageProvider using @ApplicationContext for string resource access. The setup follows Dagger Hilt best practices and supports the centralized error handling architecture.

app/src/main/java/com/sopt/clody/domain/usecase/FetchDraftDiaryUseCase.kt (2)

7-7: Improved parameter naming.

The renamed parameter diaryRepository is more descriptive than the previous repository, improving code readability.


9-10: Clean single-expression function.

The simplified single-expression function is more concise and readable while maintaining the same functionality. This aligns well with the direct delegation pattern used in other use cases.

app/src/main/res/values/strings.xml (1)

199-209: LGTM! Well-structured error messages for centralized error handling.

The new error strings provide clear, user-friendly messages that align well with the centralized error handling approach. The messages are consistent in tone and provide appropriate guidance to users (e.g., "Please check your network connection").

app/src/main/java/com/sopt/clody/core/network/NetworkConnectivityModule.kt (1)

11-22: LGTM! Clean Dagger Hilt module following best practices.

The module properly provides a singleton NetworkConnectivityObserver with the correct scope and context injection. The structure follows Dagger Hilt conventions well.

app/src/main/java/com/sopt/clody/presentation/ui/writediary/screen/WriteDiaryViewModel.kt (3)

31-34: LGTM! Constructor properly updated for centralized error handling.

The constructor correctly replaces NetworkUtil with NetworkConnectivityObserver and adds ErrorMessageProvider injection, aligning with the centralized error handling approach.


77-81: Good implementation of reactive network status checking.

The use of networkStatus.first() correctly obtains the current network status synchronously within the coroutine context. The approach maintains the reactive benefits while providing immediate status access when needed.


78-78: Excellent use of centralized error message provider.

The consistent usage of ErrorMessageProvider methods throughout the ViewModel properly centralizes error message handling. The fallback pattern using localizedMessage when available (lines 101, 234) provides good error handling coverage.

Also applies to: 99-101, 220-220, 234-234

app/src/main/java/com/sopt/clody/core/network/NetworkConnectivityObserver.kt (3)

34-66: Excellent reactive network monitoring implementation.

The callbackFlow implementation is well-structured with proper resource management. Key strengths:

  • Immediate emission of current status provides responsive UX
  • Proper callback registration/unregistration in awaitClose
  • distinctUntilChanged() prevents duplicate emissions
  • Clean conversion from callback-based to reactive API

35-53: Robust network status detection in callbacks.

The NetworkCallback implementation correctly checks both NET_CAPABILITY_INTERNET and NET_CAPABILITY_VALIDATED capabilities, ensuring actual internet connectivity rather than just network connection. The use of trySend is appropriate for non-blocking operations in callbacks.


73-78: Consistent and defensive current status detection.

The isCurrentlyAvailable() method uses the same capability checks as the callback implementation, ensuring consistency. Proper null handling for both network and capabilities provides defensive programming against edge cases.

app/src/main/java/com/sopt/clody/presentation/ui/diarylist/screen/DiaryListViewModel.kt (4)

5-8: LGTM: Clean dependency imports

The new imports for NetworkConnectivityObserver, NetworkStatus, and ErrorMessageProvider are correctly added to support the centralized error handling refactor.


20-24: LGTM: Constructor properly updated for dependency injection

The constructor correctly replaces NetworkUtil with NetworkConnectivityObserver and adds ErrorMessageProvider, aligning with the centralized error handling approach.


49-49: LGTM: Centralized error message usage

Error messages are now properly retrieved from the ErrorMessageProvider instead of hardcoded strings, successfully centralizing error message management and enabling localization.

Also applies to: 61-61, 64-64, 66-66, 90-90, 102-102, 104-104


48-51: Network connectivity check pattern verified across ViewModels

Verification confirms that every ViewModel consistently uses
networkConnectivityObserver.networkStatus.first() == NetworkStatus.{Available,Unavailable}
(e.g. WriteDiaryViewModel, TimeReminderViewModel, SignUpViewModel, HomeViewModel, DiaryListViewModel, AccountManagementViewModel, NotificationSettingViewModel, ReplyLoadingViewModel, ReplyDiaryViewModel).
No inconsistencies found—approving these changes.

app/src/main/java/com/sopt/clody/presentation/ui/auth/timereminder/TimeReminderViewModel.kt (4)

9-11: LGTM: Consistent dependency imports

The imports align with the centralized error handling pattern established across the codebase.

Also applies to: 15-15


24-28: LGTM: Constructor follows the established pattern

The constructor properly replaces NetworkUtil with the new dependencies, maintaining consistency with other ViewModels in the refactor.


38-41: LGTM: Network connectivity check implementation

The network status checking follows the same pattern as other ViewModels, ensuring consistency across the application.


64-64: LGTM: Centralized error message usage

Error messages are properly retrieved from the provider, supporting the centralization goal.

Also applies to: 66-66

app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessageProvider.kt (1)

1-16: Well-designed interface for centralized error handling

The ErrorMessageProvider interface provides a comprehensive set of methods for various error scenarios. The inclusion of an ApiError-specific method shows good integration with the existing error handling infrastructure.

The interface successfully abstracts error message retrieval, enabling:

  • Centralized error message management
  • Localization support
  • Consistent error handling across the app
app/src/main/java/com/sopt/clody/presentation/ui/replyloading/screen/ReplyLoadingViewModel.kt (5)

7-8: LGTM: Consistent dependency imports

The imports follow the established pattern for the centralized error handling refactor.

Also applies to: 14-14


28-34: LGTM: Constructor properly updated

The constructor correctly integrates the new dependencies while maintaining the existing functionality.


99-101: LGTM: Clean destructuring assignment

The destructuring assignment with map { it.toInt() } is a clean improvement over the previous implementation.


112-118: LGTM: Enhanced error handling with centralized messages

The error handling now properly uses the ErrorMessageProvider for both ApiError instances and generic errors, with improved logging that includes the throwable.


140-140: LGTM: Consistent error message usage

Ad-related error messages now use the centralized provider, maintaining consistency across the application.

Also applies to: 154-154

app/src/main/java/com/sopt/clody/presentation/ui/auth/signup/SignUpViewModel.kt (4)

11-12: LGTM: Consistent dependency imports

The imports align with the centralized error handling pattern used across other ViewModels.

Also applies to: 21-21


40-40: LGTM: Proper dependency injection in MvRx ViewModel

The constructor correctly integrates the new dependencies while maintaining compatibility with the MvRx pattern and assisted injection.

Also applies to: 42-42


152-155: LGTM: Network connectivity check implementation

The network status checking follows the established pattern and properly updates the state with centralized error messages.


165-165: LGTM: Centralized error message usage

All error scenarios now use the ErrorMessageProvider, successfully centralizing error message management across the signup flow.

Also applies to: 188-188, 205-205

app/src/main/java/com/sopt/clody/presentation/ui/setting/screen/AccountManagementViewModel.kt (2)

6-30: LGTM! Clean dependency injection setup.

The constructor properly injects the new NetworkConnectivityObserver and ErrorMessageProvider dependencies, aligning with the centralized error handling architecture.


65-67: Network check implementation looks good.

The asynchronous network status check using first() correctly retrieves the current network state before proceeding with the API call.

app/src/main/java/com/sopt/clody/presentation/ui/replydiary/ReplyDiaryViewModel.kt (4)

37-50: Well-implemented retry mechanism with proper throttling.

The retry flow setup correctly prevents rapid successive retries with a 2-second throttle and is properly scoped to the ViewModel lifecycle.


61-65: Clean network connectivity check with proper early return.

Good implementation - the network check correctly returns early when unavailable, preventing unnecessary API calls.


79-79: Good null safety handling for content field.

Using orEmpty() ensures the content field is never null, preventing potential NPEs downstream.


87-93: Excellent error handling with proper type discrimination.

The error handling correctly distinguishes between ApiError and general exceptions, providing appropriate error messages for each case. The Timber logging aids debugging.

app/src/main/java/com/sopt/clody/presentation/ui/home/screen/HomeViewModel.kt (4)

116-118: Clean error state management with sensible defaults.

Good use of default parameters to ensure consistent error messaging while maintaining flexibility.


122-125: Consistent network checking pattern across methods.

Good extraction of network checking logic and consistent error handling pattern with early returns.


273-276: Well-implemented network availability helper.

The suspend function properly encapsulates the network status check, promoting code reuse across the ViewModel.


259-271: enableDraftAlarm signature update verified

All usages of enableDraftAlarm() have been updated to the new no-argument signature:

  • app/src/main/java/com/sopt/clody/presentation/ui/home/screen/HomeScreen.kt: homeViewModel.enableDraftAlarm()

No further changes are required.

app/src/main/java/com/sopt/clody/presentation/utils/network/ErrorMessageProviderImpl.kt (1)

1-52: Clean implementation of centralized error message provider.

The implementation properly uses Android string resources for localization and follows a consistent pattern across all methods.

app/src/main/java/com/sopt/clody/data/remote/datasourceimpl/DiaryRemoteDataSourceImpl.kt (1)

11-42: Excellent centralization of API error handling.

The consistent use of safeApiCall across all methods effectively centralizes error handling and response wrapping. The implementation is clean and follows the single responsibility principle.

app/src/main/java/com/sopt/clody/presentation/ui/setting/notificationsetting/screen/NotificationSettingViewModel.kt (2)

6-7: LGTM! Clean migration to centralized abstractions.

The imports correctly introduce the new NetworkConnectivityObserver, NetworkStatus, and ErrorMessageProvider abstractions, along with the required first() extension for Flow operations. The constructor changes properly replace the deprecated NetworkUtil with the new reactive network observer and error message provider.

Also applies to: 13-13, 17-17, 24-25


67-67: Excellent centralization of error messages.

All hardcoded error messages have been properly replaced with calls to ErrorMessageProvider. The error categorization (network, temporary, unknown) is consistent and the implementation correctly handles different error scenarios based on HTTP status codes.

Also applies to: 83-83, 86-86, 88-88, 100-100, 135-135, 137-137, 150-150, 173-173, 175-175

app/src/test/java/com/sopt/clody/datasource/FakeDiaryRemoteDataSource.kt (2)

20-20: Return types correctly migrated to Result.

All method signatures have been properly updated from ApiResponse<T> to Result<T>, maintaining consistency with the interface changes in the centralized error handling refactor.

Also applies to: 24-24, 28-28, 32-32, 36-36, 40-40, 44-44


59-61: LGTM! Safe Result implementation.

The saveDraftDiary method correctly returns Result<Unit> and safely handles the null check without potential null pointer exceptions.

app/src/main/java/com/sopt/clody/data/repositoryimpl/DiaryRepositoryImpl.kt (3)

12-13: Excellent architectural simplification.

The direct delegation to diaryRemoteDataSource effectively removes boilerplate error handling from the repository layer, aligning with the centralized error handling approach. This moves error management to the more appropriate remote data source layer.

Also applies to: 15-16, 18-19, 21-22, 24-25, 27-28, 30-31


33-34: LGTM! Proper Result transformation.

The use of .map { it.toDomain() } correctly transforms the success value while preserving error handling in the Result<T> type. This maintains the repository's domain mapping responsibility.


36-39: Clean separation of concerns.

The method correctly handles DTO creation (repository responsibility) before delegating the API call to the remote data source. The direct return of the Result<Unit> maintains consistency with the new error handling approach.

app/src/main/java/com/sopt/clody/data/remote/datasource/DiaryRemoteDataSource.kt (1)

13-22: DiaryRemoteDataSource implementations updated to Result<T>

Verified that both the production and test implementations now match the new interface signatures:

• DiaryRemoteDataSourceImpl (app/src/main/java/.../DiaryRemoteDataSourceImpl.kt) uses safeApiCall(...) to return Result<T>.
• FakeDiaryRemoteDataSource (app/src/test/java/.../FakeDiaryRemoteDataSource.kt) overrides all methods as Result<T>.

No remaining ApiResponse<T> return types in any DiaryRemoteDataSource implementation.

Comment on lines +3 to +5
data class ApiError(
override val message: String,
) : Exception()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

ApiError won’t compile – illegal override of nullable message

Exception.message is declared as String?. Overriding it with a non-nullable String violates the Liskov rule and the Kotlin compiler rejects it.

-data class ApiError(
-    override val message: String,
-) : Exception()
+data class ApiError(
+    val errorMessage: String,
+) : Exception(errorMessage)

This preserves the non-null guarantee for callers via errorMessage while delegating to the base Exception correctly.

🤖 Prompt for AI Agents
In app/src/main/java/com/sopt/clody/data/remote/util/ApiError.kt around lines 3
to 5, the override of the Exception.message property is declared as non-nullable
String, but Exception.message is nullable String?, causing a compilation error.
Change the override to be nullable String? to match the base class signature,
and if a non-nullable message is needed, add a separate property like
errorMessage that guarantees non-nullability while delegating the nullable
message to the base Exception.

Comment on lines 81 to 85
val errorMessage = if (it.message?.contains("200") == false) {
FAILURE_TEMPORARY_MESSAGE
errorMessageProvider.getTemporaryError()
} else {
UNKNOWN_ERROR
errorMessageProvider.getUnknownError()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider improving error type detection logic.

The current approach of checking if the error message contains "200" is fragile and could break if error message formats change. Consider using typed errors or error codes instead.

This would be more robust if the repository returned typed errors that can be pattern matched:

onFailure = { throwable ->
    retryCount++
    if (retryCount >= maxRetryCount) {
        UserInfoState.Failure(errorMessageProvider.getTemporaryError())
    } else {
        val errorMessage = when (throwable) {
            is ApiError -> errorMessageProvider.getApiError(throwable)
            is NetworkException -> errorMessageProvider.getNetworkError()
            else -> errorMessageProvider.getUnknownError()
        }
        UserInfoState.Failure(errorMessage)
    }
}
🤖 Prompt for AI Agents
In
app/src/main/java/com/sopt/clody/presentation/ui/setting/screen/AccountManagementViewModel.kt
around lines 81 to 85, the error handling currently checks if the error message
contains "200", which is fragile. Refactor this to use typed exceptions or error
codes by updating the repository to return specific error types. Then, replace
the string check with a when expression that matches on the throwable type
(e.g., ApiError, NetworkException) and calls the appropriate error message
provider method for each type, falling back to an unknown error for others.

Comment on lines +95 to 98
if (networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable) {
_failureDialogMessage.value = errorMessageProvider.getNetworkError()
_showFailureDialog.value = true
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add return statement after network unavailability detection.

When network is unavailable, the method should return early to prevent unnecessary state updates and API call attempts.

 if (networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable) {
     _failureDialogMessage.value = errorMessageProvider.getNetworkError()
     _showFailureDialog.value = true
+    return@launch
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable) {
_failureDialogMessage.value = errorMessageProvider.getNetworkError()
_showFailureDialog.value = true
}
if (networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable) {
_failureDialogMessage.value = errorMessageProvider.getNetworkError()
_showFailureDialog.value = true
return@launch
}
🤖 Prompt for AI Agents
In
app/src/main/java/com/sopt/clody/presentation/ui/setting/screen/AccountManagementViewModel.kt
around lines 95 to 98, after detecting that the network status is unavailable,
add a return statement immediately to exit the method early. This prevents
further state updates and avoids making unnecessary API calls when there is no
network connectivity.

Comment on lines +146 to 149
if (networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable) {
_failureDialogMessage.value = errorMessageProvider.getNetworkError()
_showFailureDialog.value = true
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add return statement after network unavailability detection.

Similar to changeNickname, this method should return early when network is unavailable.

 if (networkConnectivityObserver.networkStatus.first() == NetworkStatus.Unavailable) {
     _failureDialogMessage.value = errorMessageProvider.getNetworkError()
     _showFailureDialog.value = true
+    return@launch
 }
🤖 Prompt for AI Agents
In
app/src/main/java/com/sopt/clody/presentation/ui/setting/screen/AccountManagementViewModel.kt
around lines 146 to 149, after detecting that the network status is unavailable
and setting the failure dialog message and visibility, add a return statement to
exit the method early. This prevents further execution when the network is down,
consistent with the behavior in the changeNickname method.

Comment on lines +52 to +54
): Result<DraftDiariesResponseDto> {
return draftDiariesResponse?.let { Result.success(it.data!!) }
?: Result.failure(ApiError("draftDiariesResponse not set"))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential null pointer exception.

The non-null assertion it.data!! could cause a crash if the data field is null, even when the ApiResponse object exists.

Apply this diff for safer null handling:

-        return draftDiariesResponse?.let { Result.success(it.data!!) }
+        return draftDiariesResponse?.data?.let { Result.success(it) }
             ?: Result.failure(ApiError("draftDiariesResponse not set"))
🤖 Prompt for AI Agents
In app/src/test/java/com/sopt/clody/datasource/FakeDiaryRemoteDataSource.kt
around lines 52 to 54, the code uses a non-null assertion on it.data which can
cause a crash if data is null. To fix this, replace the non-null assertion with
a safe call and handle the null case by returning a failure Result with an
appropriate error message when data is null, ensuring no potential null pointer
exceptions occur.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/src/main/java/com/sopt/clody/domain/usecase/WriteDiaryUseCase.kt (1)

9-11: Consider adding business logic or removing unnecessary abstraction.

This use case currently acts as a thin wrapper around the repository call without adding business value. Consider either:

  1. Add domain logic: Include validation, business rules, or data transformation
  2. Remove if unnecessary: If no business logic is needed, consider calling the repository directly from ViewModels

Additionally, consider more domain-specific parameter types or validation:

-suspend operator fun invoke(lang: String, date: String, content: List<String>) =
+suspend operator fun invoke(language: String, date: String, content: List<String>): Result<Unit> {
+    require(content.isNotEmpty()) { "Diary content cannot be empty" }
+    require(date.isNotBlank()) { "Date cannot be blank" }
+    return diaryRepository.writeDiary(language, date, content)
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b48ca20 and d71ab48.

📒 Files selected for processing (1)
  • app/src/main/java/com/sopt/clody/domain/usecase/WriteDiaryUseCase.kt (1 hunks)
⏰ 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 (2)
app/src/main/java/com/sopt/clody/domain/usecase/WriteDiaryUseCase.kt (2)

1-4: LGTM!

Package structure and imports are appropriate for a domain layer use case following Clean Architecture patterns.


6-8: LGTM!

Constructor injection is properly implemented with appropriate dependency on the repository layer.

@SYAAINN SYAAINN merged commit 471be98 into develop Aug 3, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] 에러 처리 로직 중앙화를 진행합니다.

3 participants