Feature/110 #130
Conversation
- `StepIndicator`에서 불필요한 `totalSteps` 및 `label` 파라미터를 제거하고 내부 `stepLabel` 함수로 대체 - `StepIndicator`의 단계별(1~3단계) 원 크기, 배경색, 아이콘 표시 로직을 `when` 식을 사용하여 명시적으로 재구현 - `SignUpStepLayout` 및 `SignUpSelectionLayout`에서 사용되지 않는 `totalSteps`, `label`, `stepLabel` 프로퍼티 삭제 - 이메일 및 소셜 회원가입 관련 모든 화면(`EmailInputScreen`, `SignUpPasswordScreen`, `SocialNicknameScreen` 등)의 레이아웃 호출부 업데이트 - `EmailVerificationScreen`에서 사용되지 않는 `timer` 상태 변수 제거 - `FolderApi` 및 `_ShareBottomSheet`에서 미사용 코드 주석 처리 및 임포트 정리 - `StepIndicator` 프리뷰 코드를 단일 컴포ザ블로 통합 및 간소화
- `TermsAgreementState` 및 `TermsAgreementEvent` 데이터 모델을 추가하여 약관 동의 관련 상태와 이벤트를 캡슐화 - `TermsAgreementSheet` 및 `TermsAgreementContent`가 `ViewModel` 대신 위 모델을 직접 참조하도록 수정하여 UI 결합도 해제 - `NoAnimBottomSheet`에서 불필요한 파라미터(`visible`, `scrimColor`, `shape`)를 제거하고 내부 구현 단순화 - `ServiceTermsScreen`, `PrivacyTermsScreen`, `MarketingTermsScreen`에 `alreadyAgreed` 파라미터를 추가하여 이미 동의한 경우 하단 버튼이 바로 활성화되도록 로직 개선 - `LoginTextField` 및 `PasswordLoginTextField`에서 불필요한 `textStyle` 파라미터를 제거하고 `modifier` 위치 등 파라미터 순서 정리 - `LoginApp` 내비게이션 그래프에서 `TermsAgreementSheet` 호출 시 상태 및 이벤트 매핑 로직 업데이트 - 불필요한 import 문 및 사용되지 않는 변수/리소스 제거 (HorizontalDivider 사용 등)
- 이메일 인증 코드 전송(`sendVerificationEmail`) 및 검증(`checkVerificationEmail`) API의 파라미터 전달 방식을 `@Query`에서 `@Body`로 변경 - 이메일 인증 관련 DTO 클래스 추가 (`EmailCodeRequestDTO`, `EmailVerifyRequestDTO`) - `AuthRepositoryImpl`에서 변경된 API 명세에 맞춰 요청 로직 수정 - `EmailVerificationScreen` 진입 시 타이머 상태를 유지하기 위해 `viewModel.resetAll()`을 `viewModel.reset()`으로 변경
==== ♻️ refactor(login): 이메일 인증 화면 상태 관리 구조 개선 및 리팩터링 - `EmailVerificationUiState` 및 `EmailVerificationEvents` 데이터 클래스를 추가하여 UI 상태와 이벤트 핸들러를 캡슐화 - `EmailVerificationScreenContent`가 개별 파라미터 대신 위 모델들을 참조하도록 수정하여 가독성 및 유지보수성 향상 - `EmailAuthViewModel`에서 관리하는 `isCodeSent`, `timer` 상태를 `collectAsStateWithLifecycle`로 구독하도록 변경 - 이메일 입력(1단계)과 인증 코드 입력(2단계) UI 로직을 `when` 조건을 통해 명확히 분리 - `TimerText` 컴포넌트에서 `ViewModel` 의존성을 제거하고 `timer` 값을 직접 주입받도록 수정 - 불필요한 import 및 주석을 제거하고 프리뷰 데이터를 신규 UI 모델 구조에 맞게 업데이트 ====
- 이메일 인증 코드 발송 요청을 위한 `SendEmailCodeRequestDTO` 추가 - 이메일 인증 코드 확인 요청을 위한 `CheckEmailCodeRequestDTO` 추가 (email, code 필드 포함) - Moshi 기반의 JSON 직렬화를 위한 `@JsonClass` 및 `@Json` 어노테이션 적용
- `AndroidManifest.xml`에 네트워크 상태 접근 권한(`ACCESS_NETWORK_STATE`) 추가 - `MainViewModel`을 `AndroidViewModel`로 변경하고 `ConnectivityManager`를 이용한 실시간 네트워크 감지 로직 구현 - `callbackFlow`를 사용하여 네트워크 가용 상태를 `StateFlow<Boolean>` 형태로 제공 (`isConnected`) - `MainApp` 컴포넌트에서 `isConnected` 상태를 구독하여 네트워크 미연결 시 토스트 메시지 알림 표시 - `MainViewModel` 생성자 변경에 따른 의존성 주입(Hilt) 및 관련 코드 정리
- `core` 모듈에 서버 및 클라이언트 에러를 계층화한 `ApiError` sealed class 정의
- `Common` (400, 401, 403, 429, 500 등 HTTP 상태 코드 기반)
- `Network` (연결 없음, 타임아웃)
- 도메인별 에러: `Auth`, `User`, `S3`, `Linku`, `Resource`, `Folder`, `Alarm` 등 세부 에러 코드 반영
- `data` 모듈에 Retrofit 호출을 위한 공통 처리 로직 및 유틸리티 추가
- `safeApiCall`, `safeApiCallUnit`: API 응답의 성공 여부를 판단하고 예외 발생 시 `ApiError`로 변환하여 throw하는 래퍼 함수 구현
- `AuthClient`, `PublicClient`: Dagger/Hilt용 API 클라이언트 구분 어노테이션(Qualifier) 추가
- `HttpException`, `IOException` 등 네트워크 예외 상황에 대한 에러 매핑 로직 구현 (단, `mapToApiError` 및 `mapHttpError` 구현체는 외부 참조로 포함됨)
- `AuthApi` 및 `AuthRepository`의 API 호출 방식을 `safeApiCall` 및 `safeApiCallUnit` 공통 모듈 기반으로 리팩터링 - 토큰 재발급(`reissue`) 시 `deviceId`를 포함하도록 `ReissueRequestDTO`를 적용하고 로직 수정 - `AuthPreference`에 소셜 로그인을 위한 `socialToken` 관리 필드 추가 및 `refreshToken` 널 허용 처리 - 소셜 프로필 완성 API 엔드포인트를 `/auth/signup/social/profile`에서 `/auth/signup/social/complete`로 변경 - 구글 로그인 인터페이스의 반환 타입을 `Result<LoginResult>`로 변경하여 에러 핸들링 강화 - `AuthRepositoryImpl` 내 로깅 메시지 정리 및 불필요한 주석 제거 - 이메일 인증 및 닉네임 중복 확인 등 결과값이 없는 API의 반환 타입을 `Any` 또는 `Unit`으로 통일하여 처리 로직 간소화
- 서버 에러 코드(String)를 도메인 에러 객체(`ApiError`)로 변환하는 `mapToApiError` 함수 추가 - COMMON, OAUTH, USERS, S3, LINKU, FOLDER, ALARM 등 도메인별 상세 에러 코드 대응 로직 구현 - `HttpException`을 기반으로 일반적인 HTTP 상태 코드를 매핑하는 `mapHttpError` 함수 추가 - 기존 `ServerApiExt.kt` 내 `ApiError` 클래스에 `@Deprecated` 어노테이션 추가 및 미사용 토큰 갱신 로직 주석 처리 (신규 `safeApiCall` 구조로 전환 준비)
…구조 개선 - `TokenAuthenticator` 클래스를 추가하여 401 Unauthorized 발생 시 `refreshToken`을 이용한 토큰 재발급 및 요청 재시도 로직 구현 - `ServerApiModule` 리팩터링을 통해 토큰 필요 여부에 따른 Retrofit 인스턴스 분리 (`@AuthClient`, `@PublicClient` 한정자 도입) - `AuthApi`를 `ServerApi` 상속 구조에서 분리하여 토큰이 불필요한 공용 API로 독립 (로그인, 회원가입, 재발급 등) - 토큰 재발급 요청을 위한 `ReissueRequestDTO` 데이터 클래스 추가 - 네트워크 인터셉터에서 불필요한 경로 체크 로직을 제거하고, `Authenticator`와 `addNetworkInterceptor`를 통한 표준 인증 방식으로 전환 - `UserIdNullException`의 사소한 코드 포맷 수정 (기본 파라미터 공백 정리)
- `LinkuRepositoryModule`, `CategoryRepositoryModule`, `FolderRepositoryModule`, `AIArticleRepositoryModule`을 `object`에서 `abstract class`로 변경하고 `@Provides` 대신 `@Binds`를 사용하도록 리팩터링
- `AuthRepositoryModule`을 신규 생성하여 `RepositoryModule`에 있던 `AuthRepository` 바인딩 로직을 분리 및 이동
- `CurationRepositoryImpl` 생성 시 더 이상 사용되지 않는 `authPreference` 파라미터 제거
- DI 바인딩 메서드에 `@Suppress("unused")` 어노테이션을 추가하여 정적 분석 경고 대응
- 불필요한 import 문(`ServerApi`, `AuthPreference` 등) 정리 및 코드 가독성 개선
- `Response<Unit>`을 반환하는 API 호출(예: 데이터 삭제)을 안전하게 처리하기 위한 래퍼 함수 `safeApiCall204` 구현 - 성공적인 응답(2xx) 시 결과값 없이 반환하며, 실패 시 `HttpException`을 `ApiError`로 매핑하여 throw 하도록 처리 - 네트워크 연결 오류, 타임아웃, 일반 예외 상황에 대한 예외 처리 로직 포함 (기존 `safeApiCall`의 예외 처리 구조와 동일)
…ApiCall`로 교체 - 모든 Repository 클래스(`Folder`, `AIArticle`, `Linku`, `Category`, `Auth`)에서 사용되지 않는 `AuthPreference` 주입 제거 - 확장 함수 기반의 인증 처리 방식(`withAuth`, `withAuthResp204Raw`)을 표준화된 `safeApiCall` 및 `safeApiCall204` 유틸리티 함수 사용으로 전환 - API 호출 시 `serverApi` 인스턴스를 `safeApiCall` 블록 내부에서 직접 참조하도록 구조 변경 - `CurationRepositoryImpl`에 향후 연동을 위한 주석 추가 - `AuthRepositoryImpl` 내 불필요하게 선언된 `signUpEmailRequest` 지역 변수 제거 및 로직 최적화
…조 개선 - `DataStore<Preferences>`의 명확한 주입을 위해 `@RecentSearchDataStore` 한정자 어노테이션 정의 - `RecentSearchRepositoryModule`에서 `provideRecentSearchDataStore`와 `provideRecentSearchRepository`에 해당 한정자를 적용하여 의존성 충돌 방지 및 명확성 확보
- `LoginSessionStore` 내 기기 식별 정보(`DEVICE_ID`, `DEVICE_TYPE`) 저장을 위한 Key 정의 - 기기 정보를 최초 1회만 저장하는 `saveDeviceInfoIfAbsent()` 함수 및 읽기용 Flow(`deviceId`, `deviceType`) 추가 - `clear()` 수행 시 세션 정보만 삭제하고 기기 정보는 유지되도록 로직 수정 - `LoginApp` 컴포넌트에서 사용되지 않는 `showNavBar` 파라미터 및 관련 `LaunchedEffect` 제거 - `LoginSessionStore` 내 주석 정리 및 코드 가독성 개선 (Long 타입 필드 등)
- **기능 개선 및 리팩터링** - `DoubleBackToExitIfTop` 컴포넌트를 적용하여 중복 구현된 뒤로가기 종료 로직 제거 및 관리 최적화 - `MainApp`에서 사용하지 않는 `BackHandler`, `activity` 참조 및 관련 변수(`lastBackPressed`) 주석 처리 - `Splash` 및 로그인 성공 로직에 자동 로그인 및 세션 갱신 리팩터링을 위한 TODO 추가 - `MainApplication`에서 디버그용 `keyHash` 확인 로직 및 불필요한 import(`Utility`) 제거 - **내비게이션 및 UI** - 로그인 화면(`login_root`) 진입 시 바텀 네비게이션 바를 숨기도록 명시적으로 설정 - 딥링크 관련 미사용 로그인 라우트(`NavigationRoute.Login.route`) 및 관련 모달 로직 제거 - `MainActivity`에 세션 갱신 로직 추가를 위한 `onResume` 오버라이드 추가 - **기타** - 앱 실행 시 `clearRecentQuery()` 호출 위치 유지 및 관련 개선 사항 주석 추가 - 불필요한 import 정리 및 `hiltViewModel` 패키지 경로 최적화 (`androidx.hilt.lifecycle.viewmodel.compose`)
- 더 이상 사용되지 않는 `GsonModule` 삭제 - `LocalDate` 및 `LocalDateTime`에 대한 커스텀 직렬화 설정(`LocalDateSerializer`, `LocalDateTimeSerializer`) 제거 - `Hilt` 의존성 주입 범위(`SingletonComponent`)에서 `Gson` 객체 제공 중단
- **소셜 로그인 (카카오/구글) 처리 개선**
- `refreshToken` 유무에 따라 신규(TEMP) 유저와 기존 유저를 구분하는 로직 추가
- 신규 유저의 경우 토큰을 영구 저장하지 않고 `authPreference.socialToken`에 임시 보관하도록 수정
- 구글 로그인의 상태 관리를 위한 `GoogleLoginStatus` 인터페이스 및 `work()` 메서드 구조 도입
- `loginWithGoogle` 함수를 `suspend`로 변경하고 `Result` 타입을 통한 에러 핸들링 반영
- **기기 정보(DeviceId, DeviceType) 관리 로직 이관**
- `LoginViewModel` 내부에 직접 구현되어 있던 `deviceId` 및 `getDeviceType()` 로직 제거
- `loginSessionStore`를 통해 저장된 기기 정보를 비동기(`first()`)로 가져오도록 수정
- 저장된 기기 정보가 없을 경우 `UUID`를 생성하고 `loginSessionStore`에 저장하는 초기화 로직 추가
- **기타 수정 사항**
- `LoginViewModel` 내 불필요한 주석 및 `Settings.Secure` 관련 import 제거
- `SocialAuthViewModel` 내 로깅 및 미사용 코드 정리 (주석 처리 등)
…ticator` 갱신 로직 보완 - `UserRepositoryImpl.getUserInfo` 호출 시 기존 `withAuth` 래퍼 대신 `safeApiCall`을 사용하도록 변경 및 불필요한 파라미터 주석 처리 - `TokenAuthenticator`에서 토큰 재발급 시, 이미 다른 요청에 의해 토큰이 갱신되었는지 확인하는 로직 추가 (중복 갱신 방지) - `TokenAuthenticator` 내 `runBlocking` 블록의 반환 제어 흐름을 `withLock` 컨텍스트에 맞게 수정 - `UserRepositoryImpl` 내 사용되지 않는 import 정리 및 향후 리팩터링을 위한 주석 추가
# Conflicts: # app/src/main/AndroidManifest.xml # app/src/main/java/com/linku/MainApp.kt # app/src/main/java/com/linku/MainViewModel.kt
- `MainViewModel`의 상속 클래스를 `ViewModel`에서 `AndroidViewModel`로 변경하여 `Application` 컨텍스트 활용 가능하도록 수정 - 생성자 파라미터에 `NotificationController`를 추가하여 의존성 주입 로직 반영 - 파일 하단에 중복으로 포함되어 있던 잘못된 코드 블록(중복 생성자 정의 및 필드 선언) 제거
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough에러 모델(AppError/ApiError)과 안전한 API 호출(safeApiCall 계열)을 도입하고, 토큰 재발급(TokenAuthenticator) 및 인증용/공용 Retrofit 분리, LoginSessionStore의 기기 정보 보존, AuthRepository의 Result 기반 재구성, 여러 리포지토리의 호출 패턴 통일, DI 모듈 바인딩 전환, 로그인·약관·이메일 UI 리팩터링을 포함합니다. Changes인증 및 네트워크 감지 기반 개편
Estimated code review effort: Possibly related PRs
Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
core/src/main/java/com/linku/core/repository/AuthRepository.kt (1)
50-58: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
loginWithKakao와loginWithGoogle의 반환 타입이 일관되지 않습니다.
loginWithKakao는LoginResult를 직접 반환하고,loginWithGoogle은Result<LoginResult>를 반환합니다. 호출자 입장에서 동일한 소셜 로그인 기능인데 에러 처리 방식이 다르면 혼란스럽습니다.두 메서드 모두 동일한 반환 타입을 사용하도록 통일하는 것이 좋습니다:
- 둘 다
Result<LoginResult>로 통일하거나- 둘 다
LoginResult로 반환하고 예외로 에러 처리🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/src/main/java/com/linku/core/repository/AuthRepository.kt` around lines 50 - 58, The two methods loginWithKakao and loginWithGoogle return inconsistent types (LoginResult vs Result<LoginResult>); make them consistent by choosing one approach (preferably both return Result<LoginResult> for unified error handling) and update the function signatures for loginWithKakao and loginWithGoogle to match, then adjust their implementations, callers, and any tests to handle Result<LoginResult> (or, if you choose exceptions, change both to return LoginResult and propagate/handle exceptions consistently). Ensure the service/repository interface and any implementing classes reference the same return type and that imports for Result and LoginResult are correct.data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt (1)
485-510:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win
updateLinkFolder가 API 응답 대신 원래 입력값을 반환합니다.
result변수에 API 응답을 매핑했지만 사용하지 않고, 원래 입력 파라미터인linku를 그대로 반환합니다. 서버 측 변경 사항(예: 업데이트된 타임스탬프, 서버에서 수정된 값 등)이 호출자에게 전달되지 않습니다.🐛 수정 제안
- Log.d("FolderRepositoryImpl", "updateLinkFolder response: $result") - } catch (e: Exception) { - Log.d("FolderRepositoryImpl", "updateLinkFolder error: $e") - throw e - } - - Log.d("FolderRepositoryImpl", "updateLinkFolder return: $linku") - return linku + Log.d("FolderRepositoryImpl", "updateLinkFolder response: $result") + Log.d("FolderRepositoryImpl", "updateLinkFolder return: $result") + return result + } catch (e: Exception) { + Log.d("FolderRepositoryImpl", "updateLinkFolder error: $e") + throw e + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt` around lines 485 - 510, The method updateLinkFolder currently maps the API response into result (using safeApiCall -> serverApi.updateLinkFolder and constructing a LinkItemInfo) but then returns the original linku parameter instead of the mapped result; fix this in updateLinkFolder by returning the constructed LinkItemInfo (or assigning result to linku before return) so callers receive the server-updated values (use the result from the safeApiCall mapping), and ensure the mapping code that builds LinkItemInfo (fields linkuId, parentFolderId, title, url/domain, linkuImageUrl, createdAt) is used as the method return value.data/src/main/java/com/linku/data/api/ServerApiExt.kt (1)
135-160:⚠️ Potential issue | 🟠 Major | ⚡ Quick win401 분기에서 실제 토큰 재발급 호출이 제거되어 복구가 불가능합니다.
Line 158에서 재발급 호출이 빠져 현재는 만료 토큰으로 동일 요청만 재시도합니다. 이 확장 함수를 아직 사용하는 경로에서는 401 복구가 깨집니다.
수정 예시
-//private suspend fun ServerApi.refreshTokenIfNeeded( -// authPreference: AuthPreference -//) = refreshMutex.withLock { //락 걸음. -// val refresh = authPreference.refreshToken -// ?: throw ApiError.TokenExpired("다시 로그인해주세요") -// //헤더 방식으로 변환. -// val pair = withCheck { reissue(refresh) } -// pair.refreshToken?.let { authPreference.refreshToken = it } -// pair.accessToken?.let { authPreference.accessToken = it } //null이 아니면 실행 -//} +private suspend fun ServerApi.refreshTokenIfNeeded( + authPreference: AuthPreference +) = refreshMutex.withLock { + val refresh = authPreference.refreshToken + ?: throw ApiError.TokenExpired("다시 로그인해주세요") + val pair = withCheck { reissue(refresh) } + pair.refreshToken?.let { authPreference.refreshToken = it } + pair.accessToken?.let { authPreference.accessToken = it } +} @@ -// refreshTokenIfNeeded(authPreference) + refreshTokenIfNeeded(authPreference) block() // api 재시도🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/api/ServerApiExt.kt` around lines 135 - 160, The 401 recovery currently skips the actual token reissue, so inside ServerApi.withTokenRefresh (the catch where shouldRefreshToken(exception) is true) call the token refresh routine (refreshTokenIfNeeded(authPreference)) before retrying block(); use the existing refreshTokenIfNeeded(authPreference) helper (which handles refreshMutex and updates AuthPreference.refreshToken/accessToken) and keep the retry block() and the outer error handling/propagation intact so that failed refreshes still throw the refreshError.app/src/main/java/com/linku/MainApp.kt (2)
258-307:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
autoLoginTried가드가 현재는 절대 켜지지 않습니다.Line 276에서 재시도를 막으려 하지만, 이 파일에서는
autoLoginTried를true로 바꾸는 코드가 없습니다. 지금 상태로는 가드 분기가 영원히 닿지 않아서,Splash가 다시onResult를 호출하는 경우tryAutoLogin()이 그대로 재실행됩니다.🔧 제안 수정
if (!hasRefresh) { // refresh 없음 → 로그인 화면으로 이동 navigator.navigate("login_root") { popUpTo(NavigationRoute.Splash.route) { inclusive = true } } return@Splash } - - // refresh 있음 → 자동로그인 시도 + // refresh 있음 → 자동로그인 시도 + autoLoginTried = true loginViewModel.tryAutoLogin( onSuccess = { navigator.navigate(NavigationRoute.Home.route) { popUpTo(NavigationRoute.Splash.route) { inclusive = true } launchSingleTop = trueAs per coding guidelines: Focus on state management (state hoisting, remember vs rememberSaveable) in Android/Kotlin/Compose code.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/main/java/com/linku/MainApp.kt` around lines 258 - 307, autoLoginTried is never set true so the retry-guard never triggers; update state handling so autoLoginTried is toggled when an auto-login attempt starts (or immediately in onResult before calling loginViewModel.tryAutoLogin) and/or when the attempt completes (in onSuccess and onFail) to prevent repeated retries; reference the variables/functions Splash, onResult, autoLoginTried, and loginViewModel.tryAutoLogin and either set autoLoginTried = true right before invoking tryAutoLogin or hoist this flag into LoginViewModel (preferred) so the boolean is updated reliably across recompositions and restored appropriately (if persisted use rememberSaveable, otherwise remember/VM state).
115-127:⚠️ Potential issue | 🟠 Major | ⚡ Quick win로그인 성공 네비게이션을 여기서 한 번 더 수행하면 충돌합니다.
이
LaunchedEffect(loginState)는 Line 295의 스플래시 자동 로그인 성공 처리와 Line 318의onLoginSuccess가 이미 담당하는 이동을 다시 실행합니다. 수동 로그인에서는 pending share를 파일 화면으로 보내기 전에 홈으로popUpTo해버릴 수 있고, 성공 네비게이션도 중복 호출됩니다. 로그인 후 이동은 한 경로에서만 처리하고, 공통 후처리만 별도 helper로 분리하는 편이 안전합니다.As per coding guidelines: Focus on SideEffect / LaunchedEffect misuse in Android/Kotlin/Compose code.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/main/java/com/linku/MainApp.kt` around lines 115 - 127, This LaunchedEffect(loginState) is duplicating navigation already handled elsewhere (splash auto-login and onLoginSuccess); remove the navigator.navigate(...) block from this LaunchedEffect and factor only the shared post-login work into a helper (e.g., handlePostLogin or loginViewModel.handlePostLogin) that performs homeViewModel.refreshAfterLogin() and sets showNavBar = true, then call that helper from the single canonical navigation sites (onLoginSuccess and the splash auto-login handler) so navigation (navigator.navigate(NavigationRoute.Home.route) / popUpTo / launchSingleTop) is executed only once.
🧹 Nitpick comments (9)
data/src/main/java/com/linku/data/di/repository/LinkuRepositoryModule.kt (1)
19-21: ⚡ Quick win바인딩 함수명과 대상 리포지토리명을 맞춰 주세요.
LinkuRepository를 바인딩하는 메서드가bindFolderRepository로 되어 있어 의도가 혼동됩니다. 메서드명을 맞추는 게 좋습니다.제안 diff
- abstract fun bindFolderRepository( + abstract fun bindLinkuRepository( impl: LinkuRepositoryImpl ): LinkuRepository🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/di/repository/LinkuRepositoryModule.kt` around lines 19 - 21, 메서드명과 바인딩 대상이 불일치합니다: 현재 bindFolderRepository가 LinkuRepositoryImpl을 LinkuRepository에 바인딩하고 있어 혼동을 유발하므로 bindFolderRepository의 이름을 LinkuRepository로 바인딩하는 의도에 맞게 변경하세요; 구체적으로 LinkuRepositoryModule에서 함수명 bindFolderRepository를 LinkuRepository(또는 bindLinkuRepository)로 바꾸어 LinkuRepositoryImpl -> LinkuRepository 바인딩을 명확히 하세요.data/src/main/java/com/linku/data/di/repository/CurationRepositoryModule.kt (1)
15-37: ⚡ Quick win주석 처리된 이전 구현과 임시 메모는 정리해 주세요.
현재 파일에 과거 구현 블록과 리뷰성 코멘트가 함께 남아 있어 DI 설정 의도를 읽기 어렵습니다. 머지 전에 삭제하는 편이 유지보수에 유리합니다.
제안 diff
-/* 미래의 제가 수정할겁니다. */ `@Module` `@InstallIn`(SingletonComponent::class) object CurationRepositoryModule { - - -// `@Provides` -// `@Singleton` -// fun provideLinkuRepository( -// serverApi: ServerApi, -// authPreference: AuthPreference -// ): CurationRepository { -// return CurationRepositoryImpl( -// serverApi = serverApi, -// authPreference = authPreference -// ) -// } `@Provides` `@Singleton` -fun provideCurationRepository( // 함수명도 의미 맞게 변경 권장 +fun provideCurationRepository( serverApi: ServerApi, - curationApi: CurationApi, // ★ 추가 + curationApi: CurationApi, authPreference: AuthPreference, moshi: Moshi ): CurationRepository {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/di/repository/CurationRepositoryModule.kt` around lines 15 - 37, Remove the leftover commented-out implementation and inline memo from CurationRepositoryModule: delete the commented provideLinkuRepository block and the Korean temporary note ("/* 미래의 제가 수정할겁니다. */") so the module only contains the active DI provider (provideCurationRepository) and related imports; ensure no stray review comments remain and keep function names (CurationRepositoryModule, provideCurationRepository) and added parameter curationApi intact.data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt (2)
245-273: ⚡ Quick win주석 처리된 코드를 제거해주세요.
더 이상 사용되지 않는 구글 로그인 코드가 주석으로 남아있습니다. 버전 관리를 통해 이전 코드를 확인할 수 있으므로, 코드베이스 정리를 위해 삭제하는 것이 좋습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt` around lines 245 - 273, Remove the dead commented Google login block: delete the entire commented-out loginWithGoogle function and its related commented imports/usages (the block referencing loginWithGoogle, SocialLoginResponseDTO, SocialLoginRequestDTO, and serverApi.withErrorHandling) so the file no longer contains unused commented code; rely on VCS to retain history if needed.
210-216: 💤 Low value
inactiveDate처리가 이메일 로그인과 일관되지 않습니다.이메일 로그인(line 78)에서는
emailSignUpResponse.inactiveDate를 사용하지만, 카카오 로그인에서는 항상 빈 문자열""을 할당합니다. 소셜 로그인 응답에도inactiveDate필드가 있다면 해당 값을 사용하거나, 없다면null을 사용하는 것이 일관성 있습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt` around lines 210 - 216, The Kakao login branch constructs a LoginResult with inactiveDate set to an empty string, which is inconsistent with the email sign-up branch that uses emailSignUpResponse.inactiveDate; update the Kakao flow to read the actual inactiveDate from the social response (e.g., response.inactiveDate) if present, or set it to null when the API doesn't provide it, instead of "" so LoginResult.inactiveDate usage is consistent across flows; adjust any nullable types or LoginResult constructor if needed to accept null.data/src/main/java/com/linku/data/implementation/preference/AuthPreferenceImpl.kt (1)
89-89: 💤 Low valueTODO 주석을 명확히 해주세요.
//TODO : 수정하기가 무엇을 수정해야 하는지 불분명합니다. 이 PR에서 처리할 내용인지, 아니면 후속 작업인지 명확히 해주세요. TODO가 남아있어야 한다면 구체적인 내용을 기술해주세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/preference/AuthPreferenceImpl.kt` at line 89, The comment on the refreshToken parameter in AuthPreferenceImpl.kt is ambiguous; replace "//TODO : 수정하기." with a clear, actionable note: either resolve the TODO now (describe the intended change, e.g., "make refreshToken non-nullable once migration completes" or "store refresh token encrypted using X method") or mark it as a tracked follow-up with an issue ID and expected owner/ETA (e.g., "TODO: revisit nullability & encryption — see ISSUE-123 assigned to `@owner` by yyyy-mm-dd"). Update the comment adjacent to the refreshToken parameter so reviewers know whether this PR addresses it or it is deferred and what exactly must be changed.data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt (1)
461-465: 💤 Low value주석 처리된 코드 블록을 제거하세요.
deleteSharedFolder호출이 주석 처리되어 있습니다. 테스트/디버깅 목적으로 남겨진 것으로 보이며, 병합 전에 정리가 필요합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt` around lines 461 - 465, 파일의 FolderRepositoryImpl 클래스에 남아 있는 주석 처리된 코드 블록 (safeApiCall204 { serverApi.deleteSharedFolder(folderId) })을 제거하세요; 해당 주석은 테스트용으로 보이므로 병합 전 삭제해야 하며, 관련된 메서드 이름 deleteSharedFolder 및 유틸 호출 safeApiCall204를 참고해 주석만 삭제하고 기능적 변경은 하지 마세요.data/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.kt (1)
137-144: 💤 Low value
safeApiCall이 항상 unwrap된 결과를 반환한다면 타입 체크가 불필요합니다.
safeApiCall이 이미BaseResponse<T>에서T를 추출해 반환한다면,when (raw)블록의BaseResponse<*>케이스는 도달하지 않을 수 있습니다.safeApiCall의 반환 타입을 확인하고, 불필요하다면 단순화를 고려하세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.kt` around lines 137 - 144, The when-check on raw is likely redundant because safeApiCall already unwraps BaseResponse<T>; inspect safeApiCall's return type and, if it returns the inner T for serverApi.recentLinks, simplify by removing the BaseResponse<*> branch and directly map/convert raw (the result of safeApiCall { serverApi.recentLinks(limit = limit) }) to List<LinkuSimpleDTO> (e.g., filterIsInstance or a safe cast) so the code uses a single, correct path for obtaining list: List<LinkuSimpleDTO>.data/src/main/java/com/linku/data/implementation/repository/UserRepositoryImpl.kt (1)
108-140: 🏗️ Heavy lift
safeApiCall마이그레이션이 일관성 없이 적용되었습니다.
getUserInfo()는safeApiCall로 마이그레이션되었지만,updateUserInfo(),deleteUser(),getNickname()은 여전히withAuth/withAuthRaw를 사용합니다. PR 목표에 따르면authPreference는logout()에만 유지되어야 하는데, 현재 여러 메서드에서 사용 중입니다.의도적인 단계적 마이그레이션이라면 TODO 주석을 추가하거나, 이번 PR에서 함께 마이그레이션하는 것을 권장합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/UserRepositoryImpl.kt` around lines 108 - 140, Several methods are inconsistent in migrating to safeApiCall: updateUserInfo (uses withAuthRaw), deleteUser (uses withAuth), and getNickname (uses withAuth) while getUserInfo already uses safeApiCall and authPreference should be limited to logout; either migrate these methods to use safeApiCall consistently (e.g., wrap serverApi.updateUserInfo/deleteUser/getUserInfo calls inside safeApiCall and stop passing authPreference into withAuth/withAuthRaw) or mark them explicitly with TODO comments if you intend staged migration—update the code paths for updateUserInfo, deleteUser, and getNickname to call safeApiCall with the same error handling approach as getUserInfo and remove direct authPreference usage (or add TODOs per method indicating planned migration).feature/login/src/main/java/com/linku/login/viewmodel/SocialAuthViewModel.kt (1)
233-271: ⚡ Quick win이전 구글 로그인 구현 주석 블록은 제거하는 편이 안전합니다.
대형 주석 코드가 남아 있으면 실제 동작 경로를 오해하기 쉽습니다. 현재 구현으로 확정됐다면 정리해 주세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/login/src/main/java/com/linku/login/viewmodel/SocialAuthViewModel.kt` around lines 233 - 271, Remove the large commented-out legacy Google login implementation inside SocialAuthViewModel: delete the entire commented block that contains the old loginWithGoogle(...) function and related logs (_googleLoginState, googleLoginStatus, authRepository folding and authPreference.saveTokens) so only the current active implementation remains; ensure no leftover commented references to loginWithGoogle, googleLoginStatus, _googleLoginState, authRepository, or authPreference are left behind.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/main/java/com/linku/MainViewModel.kt`:
- Around line 41-42: onLost currently unconditionally calls trySend(false),
causing false positives during network transitions; update the onLost(Network)
callback to re-check the system's active network state before emitting: obtain
the current active Network via ConnectivityManager.activeNetwork (or
connectivityManager.getActiveNetwork()) and inspect its NetworkCapabilities
(e.g., hasCapability(NET_CAPABILITY_INTERNET) and NET_CAPABILITY_VALIDATED) or
nullity, then call trySend(true/false) based on that result. Modify the onLost
handler in the registerNetworkCallback block (the onLost method that invokes
trySend) to perform this connectivityManager check and only emit false when no
validated/Internet-capable active network exists.
In `@app/src/main/java/com/linku/navigation/DoubleBackToExitIfTop.kt`:
- Around line 35-37: The BackHandler is enabled solely by checking currentRoute
in topLevelRoutes, causing it to intercept back presses even when the screen is
not the true top of the back stack; update the activation logic around
BackHandler(enabled = enabled) to also verify the nav back stack state (e.g.,
ensure there is no previous back stack entry) before enabling the handler so it
only runs when the destination is the actual top-level entry; reference
currentRoute, topLevelRoutes, enabled and BackHandler and use the NavController
backStack (previousBackStackEntry/currentBackStackEntry) or equivalent
backStackEntry check combined with the existing currentRoute check to gate the
handler.
In `@data/src/main/java/com/linku/data/api/TokenAuthenticator.kt`:
- Around line 53-63: The missing cleanup paths leave stale auth info; update the
TokenAuthenticator authenticate flow (the withLock block that reads refreshToken
and loginSessionStore.deviceId and the branches checking isSuccess/exception) to
call authPreference.clear() before every early return on failure (including when
refreshToken or deviceId is null, when the refresh request fails/isSuccess ==
false, and in exception handlers) so credentials are removed consistently;
ensure you reference and invoke authPreference.clear() in the same scopes where
you currently return@withLock null (and mirror this for the other similar blocks
around the ranges noted).
In `@data/src/main/java/com/linku/data/di/api/ServerApiModule.kt`:
- Around line 65-73: The network interceptor currently uses
addHeader("Authorization", ...) which appends headers and causes duplicate
Authorization on retries; change the interceptor in addNetworkInterceptor to
set/replace the header instead (use
chain.request().newBuilder().header("Authorization", "Bearer $token").build()
when authPreference.accessToken is not blank) so that when TokenAuthenticator
returns a new request the interceptor won't accumulate multiple Authorization
headers; ensure you still call chain.proceed(request).
In
`@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt`:
- Around line 143-147: The code in AuthRepositoryImpl that reads
loginSessionStore.deviceId.first() currently throws ApiError.Auth.TokenExpired
when the deviceId is null; change this to throw a semantically appropriate error
(e.g., ApiError.Auth.DeviceNotFound or another ApiError.Auth subtype) instead of
TokenExpired. Locate the deviceId retrieval in the method that uses
loginSessionStore.deviceId.first(), add or use an existing DeviceNotFound error
class under ApiError.Auth (or create it), and replace the thrown
ApiError.Auth.TokenExpired(...) with ApiError.Auth.DeviceNotFound(...) including
the same code and message.
In `@feature/login/src/main/java/com/linku/login/navigation/LoginApp.kt`:
- Line 16: The import for hiltViewModel is wrong: replace the current import of
androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel with the correct package
androidx.hilt.navigation.compose.hiltViewModel used across the project and
declared in dependencies so the symbol hiltViewModel resolves; update the import
line in LoginApp.kt to reference androidx.hilt.navigation.compose.hiltViewModel.
In `@feature/login/src/main/java/com/linku/login/ui/item/StepIndicator.kt`:
- Around line 121-123: The checkmark Image used in StepIndicator is decorative
and being announced by screen readers; update the Image calls (in StepIndicator)
that currently pass a non-null content description (the instances around the
check icon) to instead use contentDescription = null so the icon is treated as a
decoration and not read aloud by accessibility services—apply this change to
both Image occurrences referenced in the diff.
In
`@feature/login/src/main/java/com/linku/login/viewmodel/SocialAuthViewModel.kt`:
- Around line 152-164: When handling the branch where result.refreshToken ==
null in SocialAuthViewModel, explicitly clear existing stored auth tokens before
setting authPreference.socialToken to avoid leftover
accessToken/refreshToken/userId being used; call the appropriate token-clear
method on authPreference (or saveTokens with nulls) prior to assigning
socialToken and emitting _kakaoLoginState.value =
SocialLoginState.Success(result). Apply the same change to the analogous
"refreshToken == null" branch for the other social-login block in this class so
both branches clear previous tokens before storing the temporary social token.
- Around line 220-231: loginWithGoogle currently sets _googleLoginState to
SocialLoginState.Loading but does not handle exceptions thrown before or during
authRepository.loginWithGoogle(...) so Loading can get stuck; wrap the body of
loginWithGoogle in a try/catch around the call to
authRepository.loginWithGoogle(...) (including the fold) and in the catch set
_googleLoginState to a GoogleLoginStatus.Error with a sensible message (use the
caught exception's message or a default like "구글 로그인 실패") and perform the same
.work(authPreference){ _googleLoginState.value = it } behavior so state is
always updated on failure; keep the existing fold logic inside the try block.
---
Outside diff comments:
In `@app/src/main/java/com/linku/MainApp.kt`:
- Around line 258-307: autoLoginTried is never set true so the retry-guard never
triggers; update state handling so autoLoginTried is toggled when an auto-login
attempt starts (or immediately in onResult before calling
loginViewModel.tryAutoLogin) and/or when the attempt completes (in onSuccess and
onFail) to prevent repeated retries; reference the variables/functions Splash,
onResult, autoLoginTried, and loginViewModel.tryAutoLogin and either set
autoLoginTried = true right before invoking tryAutoLogin or hoist this flag into
LoginViewModel (preferred) so the boolean is updated reliably across
recompositions and restored appropriately (if persisted use rememberSaveable,
otherwise remember/VM state).
- Around line 115-127: This LaunchedEffect(loginState) is duplicating navigation
already handled elsewhere (splash auto-login and onLoginSuccess); remove the
navigator.navigate(...) block from this LaunchedEffect and factor only the
shared post-login work into a helper (e.g., handlePostLogin or
loginViewModel.handlePostLogin) that performs homeViewModel.refreshAfterLogin()
and sets showNavBar = true, then call that helper from the single canonical
navigation sites (onLoginSuccess and the splash auto-login handler) so
navigation (navigator.navigate(NavigationRoute.Home.route) / popUpTo /
launchSingleTop) is executed only once.
In `@core/src/main/java/com/linku/core/repository/AuthRepository.kt`:
- Around line 50-58: The two methods loginWithKakao and loginWithGoogle return
inconsistent types (LoginResult vs Result<LoginResult>); make them consistent by
choosing one approach (preferably both return Result<LoginResult> for unified
error handling) and update the function signatures for loginWithKakao and
loginWithGoogle to match, then adjust their implementations, callers, and any
tests to handle Result<LoginResult> (or, if you choose exceptions, change both
to return LoginResult and propagate/handle exceptions consistently). Ensure the
service/repository interface and any implementing classes reference the same
return type and that imports for Result and LoginResult are correct.
In `@data/src/main/java/com/linku/data/api/ServerApiExt.kt`:
- Around line 135-160: The 401 recovery currently skips the actual token
reissue, so inside ServerApi.withTokenRefresh (the catch where
shouldRefreshToken(exception) is true) call the token refresh routine
(refreshTokenIfNeeded(authPreference)) before retrying block(); use the existing
refreshTokenIfNeeded(authPreference) helper (which handles refreshMutex and
updates AuthPreference.refreshToken/accessToken) and keep the retry block() and
the outer error handling/propagation intact so that failed refreshes still throw
the refreshError.
In
`@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt`:
- Around line 485-510: The method updateLinkFolder currently maps the API
response into result (using safeApiCall -> serverApi.updateLinkFolder and
constructing a LinkItemInfo) but then returns the original linku parameter
instead of the mapped result; fix this in updateLinkFolder by returning the
constructed LinkItemInfo (or assigning result to linku before return) so callers
receive the server-updated values (use the result from the safeApiCall mapping),
and ensure the mapping code that builds LinkItemInfo (fields linkuId,
parentFolderId, title, url/domain, linkuImageUrl, createdAt) is used as the
method return value.
---
Nitpick comments:
In `@data/src/main/java/com/linku/data/di/repository/CurationRepositoryModule.kt`:
- Around line 15-37: Remove the leftover commented-out implementation and inline
memo from CurationRepositoryModule: delete the commented provideLinkuRepository
block and the Korean temporary note ("/* 미래의 제가 수정할겁니다. */") so the module only
contains the active DI provider (provideCurationRepository) and related imports;
ensure no stray review comments remain and keep function names
(CurationRepositoryModule, provideCurationRepository) and added parameter
curationApi intact.
In `@data/src/main/java/com/linku/data/di/repository/LinkuRepositoryModule.kt`:
- Around line 19-21: 메서드명과 바인딩 대상이 불일치합니다: 현재 bindFolderRepository가
LinkuRepositoryImpl을 LinkuRepository에 바인딩하고 있어 혼동을 유발하므로 bindFolderRepository의
이름을 LinkuRepository로 바인딩하는 의도에 맞게 변경하세요; 구체적으로 LinkuRepositoryModule에서 함수명
bindFolderRepository를 LinkuRepository(또는 bindLinkuRepository)로 바꾸어
LinkuRepositoryImpl -> LinkuRepository 바인딩을 명확히 하세요.
In
`@data/src/main/java/com/linku/data/implementation/preference/AuthPreferenceImpl.kt`:
- Line 89: The comment on the refreshToken parameter in AuthPreferenceImpl.kt is
ambiguous; replace "//TODO : 수정하기." with a clear, actionable note: either
resolve the TODO now (describe the intended change, e.g., "make refreshToken
non-nullable once migration completes" or "store refresh token encrypted using X
method") or mark it as a tracked follow-up with an issue ID and expected
owner/ETA (e.g., "TODO: revisit nullability & encryption — see ISSUE-123
assigned to `@owner` by yyyy-mm-dd"). Update the comment adjacent to the
refreshToken parameter so reviewers know whether this PR addresses it or it is
deferred and what exactly must be changed.
In
`@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt`:
- Around line 245-273: Remove the dead commented Google login block: delete the
entire commented-out loginWithGoogle function and its related commented
imports/usages (the block referencing loginWithGoogle, SocialLoginResponseDTO,
SocialLoginRequestDTO, and serverApi.withErrorHandling) so the file no longer
contains unused commented code; rely on VCS to retain history if needed.
- Around line 210-216: The Kakao login branch constructs a LoginResult with
inactiveDate set to an empty string, which is inconsistent with the email
sign-up branch that uses emailSignUpResponse.inactiveDate; update the Kakao flow
to read the actual inactiveDate from the social response (e.g.,
response.inactiveDate) if present, or set it to null when the API doesn't
provide it, instead of "" so LoginResult.inactiveDate usage is consistent across
flows; adjust any nullable types or LoginResult constructor if needed to accept
null.
In
`@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt`:
- Around line 461-465: 파일의 FolderRepositoryImpl 클래스에 남아 있는 주석 처리된 코드 블록
(safeApiCall204 { serverApi.deleteSharedFolder(folderId) })을 제거하세요; 해당 주석은
테스트용으로 보이므로 병합 전 삭제해야 하며, 관련된 메서드 이름 deleteSharedFolder 및 유틸 호출 safeApiCall204를
참고해 주석만 삭제하고 기능적 변경은 하지 마세요.
In
`@data/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.kt`:
- Around line 137-144: The when-check on raw is likely redundant because
safeApiCall already unwraps BaseResponse<T>; inspect safeApiCall's return type
and, if it returns the inner T for serverApi.recentLinks, simplify by removing
the BaseResponse<*> branch and directly map/convert raw (the result of
safeApiCall { serverApi.recentLinks(limit = limit) }) to List<LinkuSimpleDTO>
(e.g., filterIsInstance or a safe cast) so the code uses a single, correct path
for obtaining list: List<LinkuSimpleDTO>.
In
`@data/src/main/java/com/linku/data/implementation/repository/UserRepositoryImpl.kt`:
- Around line 108-140: Several methods are inconsistent in migrating to
safeApiCall: updateUserInfo (uses withAuthRaw), deleteUser (uses withAuth), and
getNickname (uses withAuth) while getUserInfo already uses safeApiCall and
authPreference should be limited to logout; either migrate these methods to use
safeApiCall consistently (e.g., wrap
serverApi.updateUserInfo/deleteUser/getUserInfo calls inside safeApiCall and
stop passing authPreference into withAuth/withAuthRaw) or mark them explicitly
with TODO comments if you intend staged migration—update the code paths for
updateUserInfo, deleteUser, and getNickname to call safeApiCall with the same
error handling approach as getUserInfo and remove direct authPreference usage
(or add TODOs per method indicating planned migration).
In
`@feature/login/src/main/java/com/linku/login/viewmodel/SocialAuthViewModel.kt`:
- Around line 233-271: Remove the large commented-out legacy Google login
implementation inside SocialAuthViewModel: delete the entire commented block
that contains the old loginWithGoogle(...) function and related logs
(_googleLoginState, googleLoginStatus, authRepository folding and
authPreference.saveTokens) so only the current active implementation remains;
ensure no leftover commented references to loginWithGoogle, googleLoginStatus,
_googleLoginState, authRepository, or authPreference are left behind.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: aec7641a-f116-4ec3-8f1c-b2ae4315f677
📒 Files selected for processing (76)
app/src/main/AndroidManifest.xmlapp/src/main/java/com/linku/MainActivity.ktapp/src/main/java/com/linku/MainApp.ktapp/src/main/java/com/linku/MainApplication.ktapp/src/main/java/com/linku/MainViewModel.ktapp/src/main/java/com/linku/navigation/DoubleBackToExitIfTop.ktcore/src/main/java/com/linku/core/datastore/session/LoginSessionStore.ktcore/src/main/java/com/linku/core/error/ApiError.ktcore/src/main/java/com/linku/core/repository/AuthRepository.ktdata/src/main/java/com/linku/data/api/ApiExt.ktdata/src/main/java/com/linku/data/api/AuthApi.ktdata/src/main/java/com/linku/data/api/AuthQualifiers.ktdata/src/main/java/com/linku/data/api/ServerApi.ktdata/src/main/java/com/linku/data/api/ServerApiExt.ktdata/src/main/java/com/linku/data/api/TokenAuthenticator.ktdata/src/main/java/com/linku/data/api/dto/auth/refreshToken/ReissueRequestDTO.ktdata/src/main/java/com/linku/data/api/dto/auth/signup/email/CheckEmailCodeRequestDTO.ktdata/src/main/java/com/linku/data/api/dto/auth/signup/email/EmailCodeRequestDTO.ktdata/src/main/java/com/linku/data/api/dto/auth/signup/email/EmailVerifyRequestDTO.ktdata/src/main/java/com/linku/data/api/dto/auth/signup/email/SendEmailCodeRequestDTO.ktdata/src/main/java/com/linku/data/api/mapToApiError.ktdata/src/main/java/com/linku/data/di/api/ServerApiModule.ktdata/src/main/java/com/linku/data/di/preference/AuthPreferenceModule.ktdata/src/main/java/com/linku/data/di/qualifier/DataStoreQualifiers.ktdata/src/main/java/com/linku/data/di/repository/AIArticleRepositoryModule.ktdata/src/main/java/com/linku/data/di/repository/AlarmRepositoryModule.ktdata/src/main/java/com/linku/data/di/repository/AuthRepositoryModule.ktdata/src/main/java/com/linku/data/di/repository/CategoryRepositoryModule.ktdata/src/main/java/com/linku/data/di/repository/CurationRepositoryModule.ktdata/src/main/java/com/linku/data/di/repository/FolderRepositoryModule.ktdata/src/main/java/com/linku/data/di/repository/GsonModule.ktdata/src/main/java/com/linku/data/di/repository/LinkuRepositoryModule.ktdata/src/main/java/com/linku/data/di/repository/RecentSearchRepositoryModule.ktdata/src/main/java/com/linku/data/di/repository/RepositoryModule.ktdata/src/main/java/com/linku/data/implementation/preference/AuthPreferenceImpl.ktdata/src/main/java/com/linku/data/implementation/repository/AIArticleRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/CategoryRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/CurationRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/UserRepositoryImpl.ktdata/src/main/java/com/linku/data/preference/AuthPreference.ktfeature/login/src/main/java/com/linku/login/navigation/LoginApp.ktfeature/login/src/main/java/com/linku/login/ui/bottom_sheet/NoAnimBottomSheet.ktfeature/login/src/main/java/com/linku/login/ui/bottom_sheet/TermsAgreementSheet.ktfeature/login/src/main/java/com/linku/login/ui/content/TermsAgreementContent.ktfeature/login/src/main/java/com/linku/login/ui/item/LoginTextField.ktfeature/login/src/main/java/com/linku/login/ui/item/PasswordLoginTextField.ktfeature/login/src/main/java/com/linku/login/ui/item/StepIndicator.ktfeature/login/src/main/java/com/linku/login/ui/item/WrongRuleItem.ktfeature/login/src/main/java/com/linku/login/ui/layout/SignUpSelectionLayout.ktfeature/login/src/main/java/com/linku/login/ui/layout/SignUpStepLayout.ktfeature/login/src/main/java/com/linku/login/ui/model/EmailVerificationEvents.ktfeature/login/src/main/java/com/linku/login/ui/model/EmailVerificationUiState.ktfeature/login/src/main/java/com/linku/login/ui/model/TermsAgreementEvent.ktfeature/login/src/main/java/com/linku/login/ui/model/TermsAgreementState.ktfeature/login/src/main/java/com/linku/login/ui/screen/email/EmailLoginScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/email/EmailVerificationScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/email/InterestContentScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/email/InterestPurposeScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/email/SignUpGenderScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/email/SignUpJobScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/email/SignUpNicknameScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/email/SignUpPasswordScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/social/EmailInputScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/social/SocialGenderScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/social/SocialInterestScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/social/SocialJobScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/social/SocialNicknameScreen.ktfeature/login/src/main/java/com/linku/login/ui/screen/social/SocialPurposeScreen.ktfeature/login/src/main/java/com/linku/login/ui/terms/MarketingTermsScreen.ktfeature/login/src/main/java/com/linku/login/ui/terms/PrivacyTermsScreen.ktfeature/login/src/main/java/com/linku/login/ui/terms/ServiceTermsScreen.ktfeature/login/src/main/java/com/linku/login/viewmodel/LoginViewModel.ktfeature/login/src/main/java/com/linku/login/viewmodel/SocialAuthViewModel.kt
💤 Files with no reviewable changes (16)
- app/src/main/java/com/linku/MainApplication.kt
- data/src/main/java/com/linku/data/api/ServerApi.kt
- data/src/main/java/com/linku/data/di/repository/GsonModule.kt
- feature/login/src/main/java/com/linku/login/ui/item/WrongRuleItem.kt
- feature/login/src/main/java/com/linku/login/ui/screen/social/SocialNicknameScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/social/SocialGenderScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/social/SocialInterestScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/social/SocialPurposeScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/email/InterestPurposeScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/social/EmailInputScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/email/SignUpNicknameScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/email/SignUpGenderScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/email/SignUpPasswordScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/email/InterestContentScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/email/SignUpJobScreen.kt
- feature/login/src/main/java/com/linku/login/ui/screen/social/SocialJobScreen.kt
- `DoubleBackToExitIfTop`에서 `enabled` 판별 조건을 수정하여, 현재 경로가 최상위 경로(topLevelRoutes)에 있더라도 이전 백스택 엔트리가 최상위 경로인 경우에는 뒤로 가기 핸들러가 활성화되지 않도록 변경 - 이를 통해 최상위 경로 간 이동 시 앱이 즉시 종료 대기 상태(Double Back)로 진입하지 않고 정상적으로 이전 화면으로 돌아갈 수 있도록 개선
- `onLost` 콜백 발생 시 단순히 `false`를 반환하는 대신, `isNetworkAvailable()`을 호출하여 실제 네트워크 연결 상태를 재확인한 후 결과를 전달하도록 수정 (다중 네트워크 환경에서의 정확한 상태 반영)
- `addNetworkInterceptor`를 `addInterceptor`로 변경하여 네트워크 레벨 이전의 요청 단계에서 헤더를 수정하도록 조정 - 요청 헤더에 이미 `Authorization` 필드가 존재하는 경우, 중복 삽입을 방지하기 위해 기존 요청을 그대로 진행하는 방어 로직 추가 - `addHeader` 대신 `header` 메소드를 사용하여 기존에 존재할 수 있는 `Authorization` 값을 덮어쓰도록 변경 - 가독성 향상을 위해 토큰 삽입 조건부 로직의 코드 블록 구조 정리
- `ApiError` 클래스 내 `Auth.DeviceNotFound` 에러 타입 추가 - `AuthRepositoryImpl`에서 토큰 재발급 시 `deviceId`가 없을 경우 발생하는 에러를 `TokenExpired`에서 `Unauthorized`로 변경하여 의미 명확화 - 기기 정보 누락에 대한 에러 메시지 및 코드(`AUTH_DEVICE_NOT_FOUND`) 수정 반영
- `StepIndicator`에서 완료 상태를 표시하는 `ic_login_check` 이미지의 `contentDescription`을 `"completed"`에서 `null`로 변경하여 불필요한 접근성 레이블 제거 (단순 장식 요소로 처리)
- 카카오 및 구글 로그인 성공 시, `refreshToken`이 없는 임시 유저(회원가입 필요 상태)일 경우 `authPreference.clear()`를 호출하여 기존 토큰 정보를 초기화하도록 수정 - `loginWithGoogle` 함수에 `try-catch` 블록을 추가하여 네트워크 오류 등 예기치 않은 예외 발생 시 에러 상태(`SocialLoginState.Error`)를 UI에 전달하도록 개선 - 코루틴 취소 예외(`CancellationException`)는 상위로 재전파(rethrow)하여 비정상적인 상태 업데이트 방지 - 구글 로그인 실패 시 로그 기록(`Log.e`) 로직 추가
- `verifyEmailCode` 함수 내 기존 `try-catch` 구조를 `authRepository`의 반환 타입 변경에 맞춰 `onSuccess`/`onFailure` 확장 함수를 사용하는 흐름으로 수정 - 인증 성공 시 타이머 정지 및 `VerifySuccess` 상태 반영, 실패 시 `VerifyError` 처리 로직을 가독성 있게 개선 - 예외 발생 시 로그 출력 및 네트워크 에러 상태(`NETWORK_ERROR`) 할당 로직 유지
- `MainApp`에서 자동 로그인 성공 시 `homeViewModel.refreshAfterLogin()`을 호출하여 홈 화면 데이터를 최신화하도록 수정 - `HomeApp`에서 `recentLinks` 상태 구독 방식을 `collectAsStateWithLifecycle`로 변경하여 생명주기 안정성 확보 - 코드 내 주석 위치 조정 및 불필요한 import 정리 (`getValue` 추가 등)
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt (1)
526-528:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win예외가 재발생되지 않아 오류가 무시됩니다.
다른 모든 메서드들은 예외를 잡은 후
throw e로 다시 발생시키지만,deleteLink는 예외를 로깅만 하고 무시합니다. 이로 인해 삭제가 실패해도 호출자에게 성공한 것처럼 보이게 됩니다.🐛 예외 재발생 추가
} catch (e: Exception) { Log.d("FolderRepositoryImpl", "deleteLink error: $e") + throw e }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt` around lines 526 - 528, In deleteLink inside FolderRepositoryImpl, the catch block currently logs the exception but swallows it; change the catch to rethrow the caught exception after logging (i.e., keep Log.d("FolderRepositoryImpl", "deleteLink error: $e") then add throw e) so callers receive the failure like other methods; ensure you reference the same caught variable name (e) and preserve logging before rethrowing.
🧹 Nitpick comments (4)
feature/file/src/main/java/com/linku/file/FileViewModel.kt (1)
346-346: ⚡ Quick win개인 메모성 주석은 제거해 주세요.
Line 346 주석은 코드 의도 설명이 아니라 개인 커뮤니케이션이라 코드베이스에 노이즈를 만듭니다.
✂️ 제안 변경
- //지민아 너가 판단해서 fold 패턴이 맞다고 생각하면 그거로 변경하는 것도 좋을듯?🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/file/src/main/java/com/linku/file/FileViewModel.kt` at line 346, Remove the personal note comment on FileViewModel (the inline comment "//지민아 너가 판단해서 fold 패턴이 맞다고 생각하면 그거로 변경하는 것도 좋을듯?") — delete it from the FileViewModel.kt source or replace it with a concise, objective comment explaining the intended code behavior or a proper TODO with owner and reason (e.g., "// TODO: consider using fold pattern for X — if adopted, update Y"), so the codebase contains only actionable or explanatory comments.data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt (2)
226-233: 💤 Low value소셜 로그인에서
inactiveDate처리가 이메일 로그인과 다릅니다.이메일 로그인은
response.inactiveDate를 사용하지만, 카카오/구글 로그인은 빈 문자열("")로 하드코딩되어 있습니다. 소셜 로그인 응답에도inactiveDate필드가 있다면 해당 값을 사용하고, 없다면null이나 일관된 기본값 처리를 권장합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt` around lines 226 - 233, The social-login mapping in AuthRepositoryImpl currently hardcodes inactiveDate = "" when constructing LoginResult; change it to use response.inactiveDate when present and fall back to a consistent default (e.g., null or the same default used by email login) so inactiveDate handling matches email login; update the LoginResult construction in the social login branch to reference response.inactiveDate (or wrap it with a null-coalescing/default) instead of the empty string to keep behavior consistent.
265-293: 💤 Low value주석 처리된 이전 구현을 제거해주세요.
새로운
loginWithGoogle구현이 완료되었으므로, 이전 구현 코드는 제거하는 것이 좋습니다. 버전 관리 시스템에서 히스토리를 확인할 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt` around lines 265 - 293, Remove the entire commented-out legacy implementation of loginWithGoogle (the block that references SocialLoginResponseDTO, SocialLoginRequestDTO, serverApi.withErrorHandling and the Log.d/Log.e statements); keep the new loginWithGoogle implementation only so dead/commented code is not left in AuthRepositoryImpl and rely on VCS history for the old implementation.data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt (1)
459-461: 💤 Low value주석 처리된 코드를 제거하거나 의도를 명확히 해주세요.
updateViewerPermission내에서deleteSharedFolder호출이 주석 처리되어 있습니다. 불필요한 코드라면 제거하고, 향후 구현 예정이라면 TODO 주석으로 의도를 명시해주세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt` around lines 459 - 461, In updateViewerPermission, remove the commented-out call to deleteSharedFolder (and its safeApiCall204 wrapper) if it's no longer needed; if it's planned for future work, replace the commented block with a clear TODO comment indicating intended behavior and a ticket/PR reference (e.g., "TODO: call serverApi.deleteSharedFolder(folderId) after X - ISSUE-123"); ensure the change references the updateViewerPermission function and the deleteSharedFolder / safeApiCall204 symbols so readers know the intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt`:
- Around line 506-507: In FolderRepositoryImpl's updateLinkFolder method you map
the API response to result but return the original input linku, which discards
server-updated fields; change the method to return result (the mapped API
response) instead of linku, or if returning linku was intentional, add a clear
comment explaining why; alternatively, declare and assign result outside the try
block so it can be returned after the API call—ensure you reference the result
variable and the updateLinkFolder method when making the change.
---
Outside diff comments:
In
`@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt`:
- Around line 526-528: In deleteLink inside FolderRepositoryImpl, the catch
block currently logs the exception but swallows it; change the catch to rethrow
the caught exception after logging (i.e., keep Log.d("FolderRepositoryImpl",
"deleteLink error: $e") then add throw e) so callers receive the failure like
other methods; ensure you reference the same caught variable name (e) and
preserve logging before rethrowing.
---
Nitpick comments:
In
`@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt`:
- Around line 226-233: The social-login mapping in AuthRepositoryImpl currently
hardcodes inactiveDate = "" when constructing LoginResult; change it to use
response.inactiveDate when present and fall back to a consistent default (e.g.,
null or the same default used by email login) so inactiveDate handling matches
email login; update the LoginResult construction in the social login branch to
reference response.inactiveDate (or wrap it with a null-coalescing/default)
instead of the empty string to keep behavior consistent.
- Around line 265-293: Remove the entire commented-out legacy implementation of
loginWithGoogle (the block that references SocialLoginResponseDTO,
SocialLoginRequestDTO, serverApi.withErrorHandling and the Log.d/Log.e
statements); keep the new loginWithGoogle implementation only so dead/commented
code is not left in AuthRepositoryImpl and rely on VCS history for the old
implementation.
In
`@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt`:
- Around line 459-461: In updateViewerPermission, remove the commented-out call
to deleteSharedFolder (and its safeApiCall204 wrapper) if it's no longer needed;
if it's planned for future work, replace the commented block with a clear TODO
comment indicating intended behavior and a ticket/PR reference (e.g., "TODO:
call serverApi.deleteSharedFolder(folderId) after X - ISSUE-123"); ensure the
change references the updateViewerPermission function and the deleteSharedFolder
/ safeApiCall204 symbols so readers know the intent.
In `@feature/file/src/main/java/com/linku/file/FileViewModel.kt`:
- Line 346: Remove the personal note comment on FileViewModel (the inline
comment "//지민아 너가 판단해서 fold 패턴이 맞다고 생각하면 그거로 변경하는 것도 좋을듯?") — delete it from the
FileViewModel.kt source or replace it with a concise, objective comment
explaining the intended code behavior or a proper TODO with owner and reason
(e.g., "// TODO: consider using fold pattern for X — if adopted, update Y"), so
the codebase contains only actionable or explanatory comments.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 34379c34-8982-484c-8c52-477d446ef198
📒 Files selected for processing (19)
app/src/main/java/com/linku/MainApp.ktcore/src/main/java/com/linku/core/error/ApiError.ktcore/src/main/java/com/linku/core/repository/AuthRepository.ktcore/src/main/java/com/linku/core/repository/UserRepository.ktdata/src/main/java/com/linku/data/api/ApiExt.ktdata/src/main/java/com/linku/data/implementation/preference/AuthPreferenceImpl.ktdata/src/main/java/com/linku/data/implementation/repository/AIArticleRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/CategoryRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/UserRepositoryImpl.ktfeature/file/src/main/java/com/linku/file/FileViewModel.ktfeature/home/src/main/java/com/linku/home/HomeApp.ktfeature/home/src/main/java/com/linku/home/HomeViewModel.ktfeature/login/src/main/java/com/linku/login/viewmodel/EmailAuthViewModel.ktfeature/login/src/main/java/com/linku/login/viewmodel/LoginViewModel.ktfeature/login/src/main/java/com/linku/login/viewmodel/SignUpViewModel.ktfeature/login/src/main/java/com/linku/login/viewmodel/SocialAuthViewModel.kt
🚧 Files skipped from review as they are similar to previous changes (7)
- data/src/main/java/com/linku/data/implementation/repository/AIArticleRepositoryImpl.kt
- data/src/main/java/com/linku/data/implementation/repository/CategoryRepositoryImpl.kt
- data/src/main/java/com/linku/data/implementation/preference/AuthPreferenceImpl.kt
- data/src/main/java/com/linku/data/implementation/repository/UserRepositoryImpl.kt
- data/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.kt
- feature/login/src/main/java/com/linku/login/viewmodel/SocialAuthViewModel.kt
- app/src/main/java/com/linku/MainApp.kt
ugmin1030
left a comment
There was a problem hiding this comment.
ApiError.kt 수정 요청 코멘트
codebidoof
left a comment
There was a problem hiding this comment.
정말 고생 많으셨습니다! 최정 Approve는 팀장님이 하시는 거루..ㅎㅎ
| /** | ||
| * 네트워크 에러. | ||
| * 서버 응답 없이 클라이언트에서 직접 생성되므로 기본 코드와 메시지를 사용한다. | ||
| */ | ||
| sealed class Network(code: String, message: String) : ApiError(code, message) { | ||
| /** 인터넷 연결 없음 (UnknownHostException, IOException) */ | ||
| class NoConnection : Network( | ||
| code = "NETWORK_NO_CONNECTION", | ||
| message = "네트워크 연결을 확인해주세요." | ||
| ) | ||
|
|
||
| /** 연결 시간 초과 (SocketTimeoutException) */ | ||
| class Timeout : Network( | ||
| code = "NETWORK_TIMEOUT", | ||
| message = "연결 시간이 초과되었습니다." | ||
| ) | ||
| } | ||
|
|
There was a problem hiding this comment.
이 부분은 ApiError 밖으로 분리하는 게 더 적절해 보입니다.
코드베이스를 파악해본 결과 ApiError는 백엔드에서 내려주는 상태 코드 기반 에러를 표현하는 역할에 가까운데, 주석에도 적혀 있듯이 Network는 서버 응답 이전에 발생하는 클라이언트 레벨의 예외라 성격이 조금 다른 것 같습니다. 예를 들면 다음과 같은 식으로 구조화 해 보는 거 어떨까요?
sealed class ApiError(
val code: String,
message: String
) : Exception(message)
sealed class NetworkError(
message: String
) : Exception(message) {
data object NoConnection : NetworkError(
"네트워크 연결을 확인해주세요."
)
data object Timeout : NetworkError(
"연결 시간이 초과되었습니다."
)
}There was a problem hiding this comment.
맞는 조언입니다!
네트워크는 api와 부관하기에 따로 분리하는게 맞다고 생각합니다!
맞춰서 수정하겠습니다:) 감사합니다!
There was a problem hiding this comment.
exception은 오브젝트로 만들면 안되기에 class로 구현하는 방향으로 수정해보겠습니다..!
- 모든 API 에러의 최상위 계층인 `BaseError` sealed 클래스 정의 - 에러 식별을 위한 `code`와 사용자 메시지 전달을 위한 `serverMessage` 필드 구성 - `Exception`을 상속받아 표준 예외 처리 메커니즘과 통합 가능하도록 구현
- 앱 내 모든 비즈니스 에러를 아우르는 최상위 규격인 `AppError` 인터페이스 추가 (UI 레이어 노출용 `displayMessage` 포함)
- 기존 `ApiError.Network`를 `AppError`를 상속받는 독립된 `NetworkError` 클래스(NoConnection, Timeout)로 분리
- `ApiError`가 `BaseError` 및 `AppError`를 상속받도록 수정하고, 서버 메시지를 기본 `displayMessage`로 사용하도록 설정
- `Common.Unauthorized` 에러 발생 시 사용자에게 노출할 커스텀 에러 메시지("로그인 세션이 만료되었습니다...") 정의
- `Gemini` 관련 에러 중 불필요하거나 중복된 하위 에러 클래스 정리 및 `GeminiApiError`로 통합
- 에러 클래스 및 도메인별(Auth, User, Linku, Crawler 등) 분류에 대한 KDoc 주석 추가로 가독성 및 문서화 강화
- `handleApiExceptions`를 `Result<T>.apiExceptions()` 확장 함수로 개편하고 `runCatching` 기반의 선언적 에러 핸들링 구조로 변경 - `safeApiCall`에 DTO를 도메인 모델로 직접 변환할 수 있는 `transform` 매퍼 파라미터 추가 - `CancellationException` 발생 시 코루틴 취소 메커니즘 보존을 위해 즉시 재전파(re-throw) 로직 유지 - 네트워크 에러 타입을 `ApiError.Network`에서 `NetworkError` 계열로 세분화하여 매핑하도록 수정 - `safeApiCallUnit` 및 `safeApiCall204` 함수에 신규 에러 핸들링 로직 적용 및 가독성 개선 - 각 API 호출 도우미 함수에 역할 설명 및 파라미터 정보를 담은 KDoc 주석 추가
- `mapToApiError` 함수 내 Gemini 에러 매핑 로직 중 `GEMINI4291`, `GEMINI5001`, `GEMINI5002` 케이스 제거 - 주석에 명시된 중복 코드 관리 및 불필요한 에러 핸들링 로직 최적화
- `AuthRepository.verifyEmailCode`의 반환 타입을 `Result<Boolean>`에서 `Result<Unit>`으로 변경하여 일관성 확보
- `AuthApi`의 공통 응답 처리 시 `Any` 대신 `Unit`을 사용하도록 수정 (`checkNickname`, `sendVerificationEmail`, `checkVerificationEmail`)
- `AuthRepositoryImpl`에서 `safeApiCall` 호출 시 `apiCall`과 `transform` 파라미터를 명시적으로 구분하여 가독성 및 코드 구조 개선
- 이메일 코드 검증 로직에서 불필요한 `map { true }` 처리를 제거하고 `onSuccess` 로그 출력으로 변경
- API 호출 및 토큰 저장 과정에서의 로깅 강화 및 데이터 무결성 검증 로직 가독성 정돈
- `safeApiCall`에 `transform` 파라미터를 적용하여 API 응답(DTO)을 도메인 모델(`AiArticle`)로 변환하는 로직을 일관된 구조로 리팩터링 - 불필요한 `requireNotNull` 체크 및 수동 매핑 로직을 제거하여 가독성 향상 및 코드 간결화
- `safeApiCall` 구조 변경에 따라 API 응답 데이터 변환(transform) 로직을 파라미터 내로 이동 - 반환값이 없는 카테고리 색상 업데이트 로직에 `safeApiCallUnit`을 적용하여 안정성 강화 - 불필요한 로그 메시지 정리 및 코드 가독성 향상을 위한 DTO 매핑 구조 최적화
- `EmailAuthViewModel.verifyEmailCode`의 `onSuccess` 콜백 내에서 불필요한 `isSuccess` 성공 여부 판별 조건문 제거 - 인증 성공 시 결과값과 무관하게 타이머를 중지하고 `EmailAuthState.VerifySuccess` 상태로 즉시 업데이트하도록 수정하여 로직 간소화
- `safeApiCall` 함수가 `apiCall`과 `transform` 파라미터를 명시적으로 받는 구조로 변경됨에 따라 관련 호출 로직 수정
- 람다식 내에서 `it` 중첩 사용으로 인한 가독성 저하를 방지하기 위해 `dtoList`, `res` 등 명시적인 매개변수 이름 사용
- 기존 `run { ... }` 블록을 통한 데이터 변환 로직을 `safeApiCall`의 `transform` 인자 내로 이동하여 일관성 확보
- `FolderRepositoryImpl` 내 모든 메서드(폴더 생성, 수정, 조회, 권한 설정 등)에 변경된 인터페이스 규격 반영
- `LinkuRepositoryImpl` 내 모든 API 호출 로직을 `safeApiCall`의 새로운 `apiCall` 및 `transform` 파라미터 구조로 리팩터링 - API 응답 DTO를 도메인 모델(`LinkSimpleInfo`, `LinkResultInfo` 등)로 변환하는 로직을 `transform` 블록 내부에 캡슐화 - 기존의 수동 `null` 체크(`requireNotNull`) 로직을 `safeApiCall` 내부 처리에 맞춰 제거하고 변환 로직으로 통합 - `recentLinks`에서 `BaseResponse`와 `List` 타입을 모두 처리하던 복잡한 캐스팅 로직을 `transform` 블록으로 이동하여 가독성 향상 - 불필요한 중간 변수 선언을 제거하고 메서드 반환값을 `safeApiCall(...).getOrThrow()`로 직접 연결하여 코드 간결화
- `safeApiCall` 호출 시 기존의 `map` 블록을 사용하는 대신 `apiCall`과 `transform` 파라미터를 명시적으로 전달하도록 수정 - 유저 정보 변환(DTO to Domain) 로직을 `transform` 람다 내부로 이동하여 데이터 흐름의 일관성 확보 - 코드 인덴트 및 구조 정리를 통해 `getUserInfo` 메서드의 가독성 개선
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
data/src/main/java/com/linku/data/implementation/repository/CategoryRepositoryImpl.kt (1)
68-73:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
safeApiCallUnit실패가 정상 처리처럼 보입니다.Line 68의 반환값을 검사하지 않아 실패 시에도 성공 로그가 남고
catch가 동작하지 않습니다.getOrThrow()로 예외 흐름을 명확히 복원하는 편이 안전합니다.제안 수정안
- val result = safeApiCallUnit { + safeApiCallUnit { serverApi.updateCategoryColor(categoryId, UpdateCategoryColorRequestDTO(body)) - } - - Log.d("CategoryRepositoryImpl", "try well done: $result") + }.getOrThrow() + Log.d("CategoryRepositoryImpl", "try well done")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/CategoryRepositoryImpl.kt` around lines 68 - 73, The call to safeApiCallUnit in CategoryRepositoryImpl (the result variable from serverApi.updateCategoryColor with UpdateCategoryColorRequestDTO) is treated as success regardless of failure; change handling so you unwrap or rethrow failures (e.g., call result.getOrThrow() or explicitly check result.isSuccess and throw on failure) before logging — ensure the Log.d("CategoryRepositoryImpl", ...) only runs after the successful unwrap so exceptions propagate to the catch block.feature/login/src/main/java/com/linku/login/viewmodel/EmailAuthViewModel.kt (1)
117-124:⚠️ Potential issue | 🟠 Major | ⚡ Quick win검증 실패를 모두 네트워크 오류로 고정하면 UX가 왜곡됩니다.
Line 123에서 실패를 전부
AuthErrorMessages.NETWORK_ERROR로 처리해 인증코드 불일치/만료도 네트워크 오류로 표시됩니다. 예외 타입별로VERIFY_FAILED와 네트워크 오류를 분리해 주세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/login/src/main/java/com/linku/login/viewmodel/EmailAuthViewModel.kt` around lines 117 - 124, The current onFailure handler in EmailAuthViewModel (inside the verifyEmailCode flow) maps every failure to AuthErrorMessages.NETWORK_ERROR which hides verification-specific failures; change the onFailure { exception -> ... } logic to inspect the exception type/source (e.g., treat verification errors such as code mismatch/expired as VERIFY_FAILED and network IO/HTTP errors as NETWORK_ERROR), then call stopTimer() and set _authState.value = EmailAuthState.VerifyError(...) with the appropriate AuthErrorMessages constant; reference the EmailAuthViewModel, the onFailure block, _authState, EmailAuthState.VerifyError, and AuthErrorMessages to locate where to implement the conditional mapping.data/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.kt (1)
237-255:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
fastSearch()에서 실패를emptyList()로 삼키지 마세요.여기서 예외를 빈 결과로 바꾸면 인증 만료/네트워크 장애/서버 오류가 모두 “검색 결과 없음”으로 보입니다. 게다가 아래의
catch (Exception)은CancellationException도 함께 삼켜서 코루틴 취소 전파까지 깨집니다.🔧 수정 예시
- }catch (e: Exception){ - Log.d("fastSearch", "error: $e") - - return emptyList() + } catch (e: kotlinx.coroutines.CancellationException) { + throw e + } catch (e: Exception) { + Log.e("fastSearch", "error", e) + throw e }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.kt` around lines 237 - 255, The fastSearch() catch block is swallowing all exceptions (including CancellationException) and returning emptyList(), which hides auth/network/server errors and breaks coroutine cancellation; change it to stop converting every exception into empty results: remove the broad catch or rework it to rethrow non-recoverable errors, explicitly check for and rethrow CancellationException, and only handle/transform specific expected errors (e.g., map a 404/no-content to emptyList() if appropriate) while logging others via Log.d or processLogger and propagating them; locate the error handling around safeApiCall(apiCall = { serverApi.quickSearch(...) }) / fastSearch() and update the catch to preserve cancellation and propagate unexpected exceptions instead of returning emptyList().data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt (1)
555-567:⚠️ Potential issue | 🟠 Major | ⚡ Quick win삭제 실패가 호출자에게 전파되지 않습니다.
여기서는 실패해도 로그만 남기고 정상 반환해서, 호출자가 삭제 성공으로 오인할 수 있습니다. 그 결과 UI/로컬 상태만 먼저 지워지고 실제 서버 데이터는 남을 수 있습니다.
🔧 수정 예시
} catch (e: Exception) { Log.d("FolderRepositoryImpl", "deleteLink error: $e") + throw e }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt` around lines 555 - 567, The deleteLink implementation in FolderRepositoryImpl currently catches all exceptions and only logs them, causing failures to be hidden; change the catch block (or remove it) so the error is propagated to the caller instead of being swallowed—either rethrow the caught exception or return a failed Result/throwable from deleteLink so callers can react. Specifically update the deleteLink method that uses safeApiCall { serverApi.getDetailLink(linkuId) } and safeApiCall204 { serverApi.deleteLink(userLinkuId) } to propagate errors (e.g., rethrow the exception or convert it to a failed Result) rather than just logging them.
♻️ Duplicate comments (1)
data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt (1)
518-545:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAPI 응답을 버리고 입력값을 반환하고 있습니다.
result에 서버 응답을 정상적으로 매핑해놓고 마지막에linku를 반환해서, 서버가 돌려준 최신 필드가 전부 무시됩니다.🔧 수정 예시
try { Log.d("FolderRepositoryImpl", "updateLinkFolder try") val result = safeApiCall( apiCall = { serverApi.updateLinkFolder( linku.linkuId, UpdateLinkFolderDTO(folderId) ) }, transform = { LinkItemInfo( linkuId = it.linkuId, parentFolderId = folderId, title = it.title, tags = emptyList(), url = it.domain ?: "", linkuImageUrl = it.linkuImageUrl, createdAt = it.createdAt, ) } ).getOrThrow() Log.d("FolderRepositoryImpl", "updateLinkFolder response: $result") + return result } catch (e: Exception) { Log.d("FolderRepositoryImpl", "updateLinkFolder error: $e") throw e } - - Log.d("FolderRepositoryImpl", "updateLinkFolder return: $linku") - return linku //TODO: 지민님께 왜 result를 안 던지고 이걸 던진 이유 물어보기!🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt` around lines 518 - 545, The code calls safeApiCall and maps the server response into result (via LinkItemInfo) but then ignores it and returns the original linku; update the method (e.g., the updateLinkFolder implementation in FolderRepositoryImpl) to return the mapped result instead of linku, adjust the final Log.d to log the result (and remove or update the TODO), and ensure any exception handling still rethrows after logging so the returned value comes from result produced by safeApiCall/serverApi.updateLinkFolder.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/src/main/java/com/linku/core/repository/AuthRepository.kt`:
- Around line 35-36: The callers of AuthRepository.sendEmailCode and
verifyEmailCode must handle the Result<Unit> return rather than ignoring it;
update EmailAuthViewModel to call sendEmailCode/verifyEmailCode and consume the
Result with onSuccess { /* set success state */ } and onFailure { /* set
error/failure state */ } (or use fold/getOrElse as preferred) so failures don't
get treated as successes—locate EmailAuthViewModel and replace the current
direct-success assignment (around the send/verify invocation) with proper Result
handling referencing sendEmailCode and verifyEmailCode.
In `@data/src/main/java/com/linku/data/api/ApiExt.kt`:
- Around line 43-50: The KDoc currently describes handling a Retrofit
Response<Unit>/204-style empty body but is attached to a function that accepts
BaseResponse<*> (safeApiCallUnit vs the actual function below). Update the
documentation to match the function signature: either rename the KDoc/title to
describe BaseResponse handling (e.g., "Safe API call for BaseResponse<*>
responses") and adjust the text to explain BaseResponse error-to-HttpException
behavior, or move the existing Response<Unit>/204-focused KDoc to the correct
function (safeApiCall204) if that function exists; ensure the doc references the
actual parameter type (BaseResponse<*>) and the actual function name used in the
file.
In
`@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt`:
- Around line 143-149: The log in verifyEmailCode currently writes the raw email
(PII) to Log.d; remove or redact the raw email there—update the Log.d call in
function verifyEmailCode (and any similar use of TAG) to either omit the email
entirely or log a non-identifying representation (e.g., masked local-part or a
deterministic hash) before calling safeApiCallUnit so no plaintext email appears
in logs or crash reports.
- Around line 54-63: The login() implementation in AuthRepositoryImpl ignores
the incoming deviceType by hardcoding "PHONE" when constructing LoginRequestDTO;
change the LoginRequestDTO construction in the authApi.signIn call to use the
login() parameter (deviceType) instead of the literal "PHONE" so the
caller-provided value is sent to the server, and ensure the login() method
signature indeed accepts a deviceType parameter if not already present; update
any tests or call sites if they relied on the hardcoded value.
---
Outside diff comments:
In
`@data/src/main/java/com/linku/data/implementation/repository/CategoryRepositoryImpl.kt`:
- Around line 68-73: The call to safeApiCallUnit in CategoryRepositoryImpl (the
result variable from serverApi.updateCategoryColor with
UpdateCategoryColorRequestDTO) is treated as success regardless of failure;
change handling so you unwrap or rethrow failures (e.g., call
result.getOrThrow() or explicitly check result.isSuccess and throw on failure)
before logging — ensure the Log.d("CategoryRepositoryImpl", ...) only runs after
the successful unwrap so exceptions propagate to the catch block.
In
`@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt`:
- Around line 555-567: The deleteLink implementation in FolderRepositoryImpl
currently catches all exceptions and only logs them, causing failures to be
hidden; change the catch block (or remove it) so the error is propagated to the
caller instead of being swallowed—either rethrow the caught exception or return
a failed Result/throwable from deleteLink so callers can react. Specifically
update the deleteLink method that uses safeApiCall {
serverApi.getDetailLink(linkuId) } and safeApiCall204 {
serverApi.deleteLink(userLinkuId) } to propagate errors (e.g., rethrow the
exception or convert it to a failed Result) rather than just logging them.
In
`@data/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.kt`:
- Around line 237-255: The fastSearch() catch block is swallowing all exceptions
(including CancellationException) and returning emptyList(), which hides
auth/network/server errors and breaks coroutine cancellation; change it to stop
converting every exception into empty results: remove the broad catch or rework
it to rethrow non-recoverable errors, explicitly check for and rethrow
CancellationException, and only handle/transform specific expected errors (e.g.,
map a 404/no-content to emptyList() if appropriate) while logging others via
Log.d or processLogger and propagating them; locate the error handling around
safeApiCall(apiCall = { serverApi.quickSearch(...) }) / fastSearch() and update
the catch to preserve cancellation and propagate unexpected exceptions instead
of returning emptyList().
In `@feature/login/src/main/java/com/linku/login/viewmodel/EmailAuthViewModel.kt`:
- Around line 117-124: The current onFailure handler in EmailAuthViewModel
(inside the verifyEmailCode flow) maps every failure to
AuthErrorMessages.NETWORK_ERROR which hides verification-specific failures;
change the onFailure { exception -> ... } logic to inspect the exception
type/source (e.g., treat verification errors such as code mismatch/expired as
VERIFY_FAILED and network IO/HTTP errors as NETWORK_ERROR), then call
stopTimer() and set _authState.value = EmailAuthState.VerifyError(...) with the
appropriate AuthErrorMessages constant; reference the EmailAuthViewModel, the
onFailure block, _authState, EmailAuthState.VerifyError, and AuthErrorMessages
to locate where to implement the conditional mapping.
---
Duplicate comments:
In
`@data/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.kt`:
- Around line 518-545: The code calls safeApiCall and maps the server response
into result (via LinkItemInfo) but then ignores it and returns the original
linku; update the method (e.g., the updateLinkFolder implementation in
FolderRepositoryImpl) to return the mapped result instead of linku, adjust the
final Log.d to log the result (and remove or update the TODO), and ensure any
exception handling still rethrows after logging so the returned value comes from
result produced by safeApiCall/serverApi.updateLinkFolder.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 61606bc7-8c8b-4dac-a200-69204906a1c8
📒 Files selected for processing (13)
core/src/main/java/com/linku/core/error/ApiError.ktcore/src/main/java/com/linku/core/error/BaseError.ktcore/src/main/java/com/linku/core/repository/AuthRepository.ktdata/src/main/java/com/linku/data/api/ApiExt.ktdata/src/main/java/com/linku/data/api/AuthApi.ktdata/src/main/java/com/linku/data/api/mapToApiError.ktdata/src/main/java/com/linku/data/implementation/repository/AIArticleRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/CategoryRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/FolderRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/LinkuRepositoryImpl.ktdata/src/main/java/com/linku/data/implementation/repository/UserRepositoryImpl.ktfeature/login/src/main/java/com/linku/login/viewmodel/EmailAuthViewModel.kt
💤 Files with no reviewable changes (1)
- data/src/main/java/com/linku/data/api/mapToApiError.kt
- `safeApiCallUnit` 함수가 빈 객체(`{}`) 응답을 처리하는 용도임을 명확히 하도록 설명 및 Kdoc 수정
- HTTP 204(No Content) 응답 처리를 위한 `safeApiCall204` 함수를 별도로 분리하여 명시적인 응답 처리 구조 마련
- 코드 가독성 향상을 위해 각 API 호출 래퍼 함수의 역할 재정의 및 주석 정리
- `AuthRepositoryImpl`의 로그인 로직에서 `deviceType`을 "PHONE"으로 하드코딩하던 부분을 외부에서 전달받은 `deviceType` 변수를 사용하도록 수정
- `AppError`를 `sealed interface`에서 `sealed class`로 변경하고 `BaseError`를 상속하도록 수정 - `NetworkError`가 `Exception` 대신 `AppError`를 상속하도록 변경하여 에러 계층 구조 통일 - `NetworkError`의 기본 에러 코드를 `"NETWORK_ERROR"`로 정의 - `ApiError`가 `BaseError`와 `AppError`를 중복 상속하던 구조를 `AppError` 상속으로 단일화하여 가독성 개선
ugmin1030
left a comment
There was a problem hiding this comment.
예외 처리 이외에도 안정성을 확보하고 책임을 분리해서 추적이 쉬운 코드들 너무 멋졌습니다~ 에러 모델 설계에 대한 댓글도 잘봤어요~
| * @see Exception | ||
| */ | ||
| sealed class ApiError( | ||
| override val code: String, |
There was a problem hiding this comment.
에러 코드는 매핑 이외에 필요할지 의문이기도 하고 코드 추가나 수정, 삭제가 일어나면 이 코드와 매핑 함수 코드까지 두 번 변경해야해서 꼭 멤버로 정의해야할지 한 번 생각해보면 좋을 것 같습니당
There was a problem hiding this comment.
짚어주신 점이 정확합니다. 혹시나 나중에 에러 코드를 쓸 일이 생기지 않을까 하는 노파심에 작성된 코드여서 오버 엔지니어링으로 코드를 작성을 한 게 맞습니다!!
음 팀장님께서 결정해주시는 대로 진행하는 것이(?) 좋을 것 같습니다:) 고민해보시고 알려주세요~!
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt (1)
134-135:⚠️ Potential issue | 🟠 Major | ⚡ Quick win이메일 주소를 로그에 그대로 남기지 마세요 (PII 노출).
sendEmailCode와verifyEmailCode모두 로그에 이메일 주소를 평문으로 기록하고 있습니다. 이는 크래시 리포트나 logcat 수집 시 개인정보가 노출될 수 있습니다.🔒 수정 제안
/* 이메일 인증 코드 전송 */ override suspend fun sendEmailCode(email: String): Result<Unit> { - Log.d(TAG, "[이메일 코드 전송 시도] email=$email") + Log.d(TAG, "[이메일 코드 전송 시도]") return safeApiCallUnit { authApi.sendVerificationEmail(EmailCodeRequestDTO(email = email)) }.onSuccess { Log.d(TAG, "[이메일 코드 전송 성공]") } } /* 이메일 인증 코드 검증 */ override suspend fun verifyEmailCode(email: String, code: String): Result<Unit> { - Log.d(TAG, "[이메일 코드 검증 시도] email=$email") + Log.d(TAG, "[이메일 코드 검증 시도]") return safeApiCallUnit {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt` around lines 134 - 135, The log statements in sendEmailCode and verifyEmailCode currently print plaintext email addresses (PII); remove the raw email from logs and instead log a non-identifying token (e.g., mask the local part, log only the domain, or log a hash) and/or a simple action message. Update the Log.d calls in AuthRepositoryImpl.sendEmailCode and AuthRepositoryImpl.verifyEmailCode to omit the full email and use a maskedEmail or hashedEmail placeholder (or just "email redacted") while keeping the rest of the debug message intact.
🧹 Nitpick comments (2)
data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt (2)
281-309: 💤 Low value주석 처리된 코드를 제거하세요.
이전 구현이 주석으로 남아 있습니다. 버전 관리 시스템(Git)에서 히스토리를 추적할 수 있으므로, 더 이상 필요하지 않은 코드는 삭제하는 것이 좋습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt` around lines 281 - 309, Remove the commented-out legacy Google login implementation: delete the entire commented block that defines loginWithGoogle (including references to SocialLoginResponseDTO, SocialLoginRequestDTO, serverApi.withErrorHandling and the Log statements) from AuthRepositoryImpl to keep the file clean—rely on Git history for past code instead of keeping commented code in the source.
187-189: 💤 Low valueTAG 상수가 클래스 이름과 불일치합니다.
TAG가"UserRepository"로 정의되어 있지만 실제 클래스는AuthRepositoryImpl입니다. 로그 필터링 및 디버깅 시 혼란을 줄 수 있습니다.♻️ 수정 제안
companion object { - private const val TAG = "UserRepository" + private const val TAG = "AuthRepositoryImpl" }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt` around lines 187 - 189, The TAG constant in the companion object is set to "UserRepository" but the containing class is AuthRepositoryImpl; update the companion object's TAG value to match the class (e.g., "AuthRepositoryImpl") or derive it programmatically (e.g., AuthRepositoryImpl::class.java.simpleName) so log tags reflect the correct class name; locate the companion object and adjust the private const val TAG declaration in AuthRepositoryImpl accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt`:
- Around line 134-135: The log statements in sendEmailCode and verifyEmailCode
currently print plaintext email addresses (PII); remove the raw email from logs
and instead log a non-identifying token (e.g., mask the local part, log only the
domain, or log a hash) and/or a simple action message. Update the Log.d calls in
AuthRepositoryImpl.sendEmailCode and AuthRepositoryImpl.verifyEmailCode to omit
the full email and use a maskedEmail or hashedEmail placeholder (or just "email
redacted") while keeping the rest of the debug message intact.
---
Nitpick comments:
In
`@data/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt`:
- Around line 281-309: Remove the commented-out legacy Google login
implementation: delete the entire commented block that defines loginWithGoogle
(including references to SocialLoginResponseDTO, SocialLoginRequestDTO,
serverApi.withErrorHandling and the Log statements) from AuthRepositoryImpl to
keep the file clean—rely on Git history for past code instead of keeping
commented code in the source.
- Around line 187-189: The TAG constant in the companion object is set to
"UserRepository" but the containing class is AuthRepositoryImpl; update the
companion object's TAG value to match the class (e.g., "AuthRepositoryImpl") or
derive it programmatically (e.g., AuthRepositoryImpl::class.java.simpleName) so
log tags reflect the correct class name; locate the companion object and adjust
the private const val TAG declaration in AuthRepositoryImpl accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 63ab2d45-3ebd-446c-a1b2-7e23a1c737de
📒 Files selected for processing (3)
core/src/main/java/com/linku/core/error/ApiError.ktdata/src/main/java/com/linku/data/api/ApiExt.ktdata/src/main/java/com/linku/data/implementation/repository/AuthRepositoryImpl.kt
- `TermsAgreementEvent` 데이터 클래스의 모든 프로퍼티에 기본값(`{}`)을 추가하여 객체 생성 시 유연성 확보
- `LoginApp` 내 중복 정의되었던 약관 동의 변경 이벤트(`onAgreeTermsChange` 등) 제거 및 모델 기본값 활용
- `TermsAgreementSheet` 프리뷰 및 실제 호출부에서 불필요하게 구현된 빈 람다식 제거
- `TermsAgreementContent` 프리뷰 코드를 기본 생성자를 사용하는 방식으로 간소화 및 가동성 향상
- 중복된 `when` 조건문과 하드코딩된 UI 선언을 제거하고 `currentStep` 상태에 따른 동적 바인딩 로직으로 개선 - `isCompleted`, `isCurrent` 상태 변수를 도입하여 완료된 단계(체크 아이콘 표시)와 현재 단계의 시각적 구분 로직 간소화 - 단계별 배경색 처리를 위한 `backgrounds` 리스트 적용 및 점선(Spacer)의 활성화 색상 반영 로직 수정 - 3단계 레이블의 가독성 향상을 위해 `startPadding` 값을 조정 (122.dp -> 132.dp) - 불필요한 import 제거 및 코드 구조 정리를 통해 유지보수성 향상
- `Modifier.run`을 사용하던 조건부 스타일링 로직을 `Modifier.then` 방식으로 변경하여 가독성 및 가이드라인 준수 - 현재 단계 또는 완료 상태에 따른 배경색(background) 및 테두리(border) 적용 로직 최적화
📝 설명
주요 변경 사항
🏗 아키텍처
ApiErrorsealed class 도입 (core) — 서버/네트워크/비즈니스 에러 통합ApiErrorMapper— 서버 에러코드 →ApiError변환safeApiCall/safeApiCallUnit/safeApiCall204래퍼 함수로 통일@AuthClient/@PublicClientQualifier 분리TokenAuthenticator— 401 시 자동 토큰 재발급 + Mutex (동시 401 방지)ServerApiModule— PublicClient / AuthClient OkHttpClient 분리🔧 RepositoryImpl authPreference 제거
FolderRepositoryImplwithAuth→safeApiCall교체,authPreference제거AIArticleRepositoryImplwithAuth→safeApiCall교체,authPreference제거CurationRepositoryImplauthPreference제거LinkuRepositoryImplwithAuth→safeApiCall교체,authPreference제거CategoryRepositoryImplwithAuth→safeApiCall교체,authPreference제거AuthRepositoryImplsafeApiCall적용,authPreference제거UserRepositoryImplwithAuth→safeApiCall교체,authPreference는logout()에서만 유지📦 Module 정리
FolderRepositoryModule→@Provides→@Binds전환AuthRepositoryModule신규 생성RecentSearchRepositoryModule—@RecentSearchDataStoreQualifier 추가 (DataStore 충돌 방지)변경 전 / 후
미완료 (다음 PR)
tryAutoLogin토큰 재발급 로직)ServerApiExt.kt@Deprecated함수 완전 제거 (팀원 마이그레이션 완료 후)리뷰 포인트
UserRepositoryImpl.logout()의authPreference.clear()위치 유지 여부safeApiCall에러 처리 방식 통일성 확인BaseViewModel구현 (공통 에러 처리) 방향성 의견 부탁드립니다✔️ PR 유형
어떤 변경 사항이 있나요?
📎 관련 이슈 번호
Summary by CodeRabbit
새로운 기능
개선사항
권한