-
Notifications
You must be signed in to change notification settings - Fork 10
8주차 미션 / 안드로이드 1조 박경민 #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: koongmai
Are you sure you want to change the base?
Conversation
Walkthrough애플리케이션에 의존성 주입 컨테이너, 인증 플로우, 토큰 관리 기능을 추가하고 단일 ViewModel을 여러 개의 특화된 ViewModel로 분리하는 대규모 아키텍처 개선 작업입니다. DataStore 기반 토큰 저장소, 로그인/회원가입 API 엔드포인트, UI 상태 관리 체계를 도입합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application<br/>(onCreate)
participant AppContainer as AppContainer<br/>(DI)
participant Repo as Repository
participant API as ApiService
App->>AppContainer: initialize container
AppContainer->>AppContainer: lazy init PostRepository
AppContainer->>AppContainer: lazy init LoginRepository
AppContainer->>AppContainer: lazy init TokenRepository
Note over AppContainer: Each Repository<br/>holds ApiService reference
AppContainer-->>App: container ready
sequenceDiagram
participant UI as UI Screen
participant VM as ViewModel
participant Repo as Repository
participant DS as DataStore
participant API as ApiService
UI->>VM: refresh() / loadDetail()
activate VM
VM->>VM: emit Loading state
VM->>Repo: fetch data (runCatching)
activate Repo
Repo->>API: API call
API-->>Repo: Response
Repo->>Repo: validate response
Note over Repo: Check success &<br/>non-null data
Repo-->>VM: Result<Data>
deactivate Repo
alt Success
VM->>VM: emit Success(data)
UI->>UI: render with data
else Error
VM->>VM: emit Error(message)
UI->>UI: render error state
end
deactivate VM
sequenceDiagram
participant LoginUI as LoginScreen
participant LoginVM as LoginViewModel
participant AuthRepo as LoginRepository
participant TokenRepo as TokenRepository
participant API as ApiService
participant DS as DataStore
LoginUI->>LoginVM: login(context)
activate LoginVM
LoginVM->>AuthRepo: login(id, password)
activate AuthRepo
AuthRepo->>API: POST /api/auth/login
API-->>AuthRepo: LoginResponse(token, userId, username)
AuthRepo-->>LoginVM: Result<LoginResponse>
deactivate AuthRepo
alt Success
LoginVM->>TokenRepo: saveToken(context, token)
activate TokenRepo
TokenRepo->>DS: save token
DS-->>TokenRepo: saved
deactivate TokenRepo
LoginVM->>LoginVM: emit Success with token
end
deactivate LoginVM
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45분 주의가 필요한 영역:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@coderabbitai review |
|
@alswlekk I'll review the changes in this pull request now. ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt (1)
139-171: 게시글 생성 결과를 확인하지 않고 네비게이션하는 문제Lines 147-148에서
viewModel.createPost()를 호출한 직후onPostCreated()를 즉시 호출하고 있습니다. 이는 게시글 생성이 완료되기 전에 화면을 벗어나는 문제를 일으킵니다.현재
uiState를 수집하고 있지만(Line 50) 네비게이션 트리거에 활용하지 않고 있습니다. 게시글 생성 성공 시에만 화면을 벗어나도록 수정해야 합니다.// PostCreateScreen 내부에 LaunchedEffect 추가 LaunchedEffect(uiState) { if (uiState is PostCreateUiState.Success) { onPostCreated() } } // Button onClick에서 onPostCreated() 제거 Button( onClick = { val finalAuthor = author val request = PostCreateRequest( title = title, content = content, imageUrl = null ) viewModel.createPost(finalAuthor, request) // onPostCreated() 제거 }, // ... 나머지 코드에러 처리도 고려하여 Snackbar 등으로 사용자에게 피드백을 제공하는 것이 좋습니다.
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt (1)
200-220: deleteUiState를 관찰하고 성공 시에만 네비게이션을 수행해야 합니다검증 결과, 리뷰 지적이 정확합니다. ViewModel에는
deleteUiState: StateFlow<DeletePostUiState>가 이미 구현되어 있으며 삭제 결과(Success/Error)를 추적하고 있습니다. 그러나 PostDetailScreen에서는 이 상태를 관찰하지 않고 있어서, 네트워크 요청의 실제 결과와 무관하게 즉시 네비게이션되는 문제가 발생합니다.필요한 수정:
deleteUiState를collectAsState()로 수집LaunchedEffect에서 Success 상태만 감지하여onNavigateBack()호출- Error 상태 시 사용자에게 에러 메시지 표시 (토스트/스낵바)
현재 확인 버튼의 로직:
viewModel.deletePost(postId) showDeleteDialog = false onNavigateBack()수정해야 할 로직:
deletePost()호출만 실행LaunchedEffect에서deleteUiState를 감시하여 결과에 따라 처리
🧹 Nitpick comments (21)
app/src/main/java/com/example/kuit6_android_api/MainActivity.kt (1)
52-60: 사용되지 않는 SnackbarHostState 관련 코드를 제거하세요.
snackBarState를 생성하고Scaffold에snackbarHost를 구성하고 있지만, 이제NavGraph에 전달되지 않아 실제로 사용되지 않습니다. 불필요한 코드는 제거하는 것이 좋습니다.다음 diff를 적용하여 사용되지 않는 코드를 제거하세요:
- val snackBarState = remember{ SnackbarHostState() } - Scaffold( modifier = Modifier .fillMaxSize() - .background( MaterialTheme.colorScheme.background), - snackbarHost= { - SnackbarHost(hostState = snackBarState) //자동으로 UI를 띄울 공간을 만듦 - } + .background( MaterialTheme.colorScheme.background) ) {app/src/main/java/com/example/kuit6_android_api/ui/post/state/LoginUiState.kt (1)
6-7: 코드 스타일 일관성을 위해 공백을 추가하세요.등호(
=) 앞에 공백이 누락되어 일관성이 떨어집니다.다음 diff를 적용하여 스타일을 통일하세요:
- val password: String= "", - val isAutoLogin: Boolean= false, + val password: String = "", + val isAutoLogin: Boolean = false,app/src/main/java/com/example/kuit6_android_api/data/repository/TokenRepositoryImpl.kt (2)
14-14: 불필요한 생성자 괄호를 제거하세요.빈 생성자 괄호
()는 불필요합니다.-class TokenRepositoryImpl(): TokenRepository { +class TokenRepositoryImpl : TokenRepository {
15-24: 에러 처리를 추가하세요.DataStore 연산은 실패할 수 있지만 현재 구현에는 에러 처리가 없습니다. 디스크 I/O 오류, 권한 문제 등으로 인해 예외가 발생할 수 있습니다.
Result 타입을 반환하거나 try-catch로 예외를 처리하는 것을 고려하세요:
override suspend fun saveToken(context: Context, token: String) { try { context.datastore.edit { it[KEY] = token } } catch (e: Exception) { // 로깅 또는 에러 전파 } } override suspend fun getToken(context: Context): String? { return try { val prefs = context.datastore.data.first() prefs[KEY] } catch (e: Exception) { null } }app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt (1)
3-3: 불필요한 import를 제거하세요.
android.R.attr.valueimport는 사용되지 않으며 API 서비스 인터페이스에 필요하지 않습니다.-import android.R.attr.valueapp/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostCreateViewModel.kt (2)
16-16: 초기 상태를 재검토하세요.
_uiState의 초기값이Loading으로 설정되어 있습니다. 게시물 생성이 시작되지 않은 초기 상태에서 Loading을 표시하는 것은 사용자 경험을 해칠 수 있습니다.Idle이나 초기 상태를 나타내는 별도의 상태를 고려하세요.-private val _uiState = MutableStateFlow<PostCreateUiState>(PostCreateUiState.Loading) +private val _uiState = MutableStateFlow<PostCreateUiState>(PostCreateUiState.Idle) // Idle 상태가 있다면
29-32: 에러 로깅을 추가하는 것을 고려하세요.에러 발생 시 UI 상태는 업데이트되지만 로깅이나 추가적인 에러 추적이 없습니다. 디버깅과 모니터링을 위해 로깅을 추가하는 것을 권장합니다.
.onFailure { error -> + Log.e("PostCreateViewModel", "Failed to create post", error) _uiState.value = PostCreateUiState.Error( error.message ?: "error" ) }app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt (1)
19-20: 삭제 상태의 초기값을 수정하세요.
_deleteUiState가Loading으로 초기화되어 있습니다. 삭제 작업이 시작되지 않은 상태에서 Loading을 표시하는 것은 부적절합니다.Idle또는 초기 상태를 나타내는 상태값을 사용하세요.-private val _deleteUiState = MutableStateFlow<DeletePostUiState>(DeletePostUiState.Loading) +private val _deleteUiState = MutableStateFlow<DeletePostUiState>(DeletePostUiState.Idle) // Idle 상태가 있다면app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt (1)
99-101: 사용되지 않는 변수를 제거하세요.
route변수가 선언되었지만 사용되지 않습니다. LoginScreen은 매개변수를 필요로 하지 않으므로 이 코드는 불필요합니다.composable<LoginRoute> { backStackEntry -> - val route = backStackEntry.toRoute<LoginRoute>() - LoginScreen(app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/LoginViewModel.kt (2)
3-3: 불필요한 import를 제거하세요.
android.R.attr.passwordimport는 사용되지 않습니다.-import android.R.attr.password
20-21: 보안 고려사항: 비밀번호 관리현재 비밀번호가
LoginUiState에 평문으로 저장되어 메모리 덤프 시 노출될 수 있습니다. 프로덕션 환경에서는 다음을 고려하세요:
- 비밀번호를 상태로 저장하지 않고 직접 매개변수로 전달
- 민감한 데이터 처리를 위한 별도의 보안 메커니즘 사용
학습 목적의 코드라면 현재 구조도 괜찮지만, 실제 서비스에서는 개선이 필요합니다.
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt (1)
19-36: 에러 메시지 일관성 개선 권장Line 32의 기본 에러 메시지가 "error"로 되어 있어
getPostDetail의 "게시글 불러오기 실패"에 비해 구체성이 떨어집니다. 사용자에게 더 명확한 피드백을 제공하기 위해 "게시글 수정 실패"와 같은 설명적인 메시지로 변경하는 것을 고려해보세요..onFailure { error -> _uiState.value = PostEditUiState.Error( - error.message ?: "error" + error.message ?: "게시글 수정 실패" ) }app/src/main/java/com/example/kuit6_android_api/data/repository/LoginRepositoryImpl.kt (2)
11-23: 입력 유효성 검사 추가 권장ID와 password에 대한 유효성 검사가 없습니다. 빈 문자열이나 부적절한 형식의 입력이 API로 전달될 수 있습니다. 네트워크 비용을 줄이고 사용자 경험을 개선하기 위해 기본적인 검증을 추가하는 것을 권장합니다.
예시:
override suspend fun signup(id: String, password: String): Result<LoginResponse> { if (id.isBlank() || password.isBlank()) { return Result.failure(Exception("아이디와 비밀번호를 입력해주세요")) } return runCatching { val response = apiService.signup(LoginRequest(username = id, password = password)) // ... 기존 로직
25-39: 코드 중복 제거 고려
signup과login함수가 거의 동일한 구조를 가지고 있습니다. 공통 로직을 추출하여 중복을 줄일 수 있습니다.private suspend fun authenticateUser( id: String, password: String, apiCall: suspend (LoginRequest) -> BaseResponse<LoginResponse>, errorMessage: String ): Result<LoginResponse> { return runCatching { val response = apiCall(LoginRequest(username = id, password = password)) if (response.success && response.data != null) { response.data } else { throw Exception(response.message ?: errorMessage) } }.onFailure { error -> Log.e("LoginRepository", error.message.toString()) } } override suspend fun signup(id: String, password: String): Result<LoginResponse> { return authenticateUser(id, password, apiService::signup, "회원가입 실패") } override suspend fun login(id: String, password: String): Result<LoginResponse> { return authenticateUser(id, password, apiService::login, "로그인 실패") }app/src/main/java/com/example/kuit6_android_api/ui/post/screen/LoginScreen.kt (1)
85-90: 토큰 노출 주의Line 85에서 토큰을 UI에 직접 표시하고 있습니다. 실습 목적으로는 적절하지만, 실제 프로덕션 환경에서는 보안상 토큰을 UI에 노출하지 않아야 합니다. 추후 개발 시에는 이 부분을 제거하거나 디버그 모드에서만 표시되도록 수정하는 것을 권장합니다.
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt (1)
21-35: 에러 메시지 개선 권장Line 31의 기본 에러 메시지가 "error"로 되어 있어 구체성이 떨어집니다. "게시글 목록을 불러올 수 없습니다"와 같은 설명적인 메시지로 변경하면 사용자 경험이 개선됩니다.
.onFailure { error-> _postListUiState.value = PostListUiState.Error( - error.message ?: "error" + error.message ?: "게시글 목록을 불러올 수 없습니다" ) }app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepository.kt (1)
7-27: 인터페이스 설계가 일관성 있게 잘 구성되었습니다모든 함수가
Result타입을 반환하여 에러 처리가 일관적이며, suspend 함수로 비동기 처리를 명확히 했습니다.다만 Line 25의
uploadImage함수가Map<String, String>을 반환하는 것은 타입 안정성이 떨어집니다. 이미지 업로드 응답을 위한 전용 데이터 클래스(예:ImageUploadResponse)를 정의하는 것을 고려해보세요.예시:
data class ImageUploadResponse(val imageUrl: String) interface PostRepository { suspend fun uploadImage(file: MultipartBody.Part): Result<ImageUploadResponse> // ... 나머지 함수들 }app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt (1)
45-50: Lifecycle-aware 상태 수집 권장Line 50에서
collectAsState()를 사용하고 있습니다.collectAsStateWithLifecycle()을 사용하면 생명주기를 인식하여 백그라운드에서 불필요한 수집을 방지할 수 있습니다.- val uiState by viewModel.uiState.collectAsState() + val uiState by viewModel.uiState.collectAsStateWithLifecycle()필요한 import 추가:
import androidx.lifecycle.compose.collectAsStateWithLifecycleapp/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt (1)
12-88: 코드 중복 제거를 통한 유지보수성 향상 권장모든 함수가 유사한 패턴을 따르고 있습니다:
runCatching, 응답 검증, 데이터 반환/예외 발생, 에러 로깅. 공통 로직을 추출하여 중복을 줄이면 유지보수가 용이해집니다.private suspend fun <T> executeApiCall( errorMessage: String, apiCall: suspend () -> BaseResponse<T> ): Result<T> { return runCatching { val response = apiCall() if (response.success && response.data != null) { response.data } else { throw Exception(response.message ?: errorMessage) } }.onFailure { error -> Log.e("PostRepository", error.message.toString()) } } override suspend fun getPosts(): Result<List<PostResponse>> { return executeApiCall("게시글 불러오기 실패") { apiService.getPosts() } } override suspend fun createPost(author: String, request: PostCreateRequest): Result<PostResponse> { return executeApiCall("게시글 작성 실패") { apiService.createPost(author, request) } } override suspend fun getPostDetail(id: Long): Result<PostResponse> { return executeApiCall("게시글 불러오기 실패") { apiService.getPostDetail(id) } } // 나머지 함수들도 동일한 패턴으로 리팩토링
deletePost는 약간 다른 로직이므로 별도 헬퍼 함수나 조건 처리가 필요할 수 있습니다.app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt (1)
87-196: 로딩/에러 상태에 최소한의 UI 피드백을 추가하는 것이 좋겠습니다.
PostDetailUiState.Loading과PostDetailUiState.Error분기에서 실제로 아무 것도 렌더링하지 않고 주석만 있어, 사용자가 “지금 로딩 중인지/에러인지”를 전혀 인지할 수 없습니다.간단히라도
Box(modifier = Modifier.fillMaxSize().padding(paddingValues), contentAlignment = Alignment.Center)안에CircularProgressIndicator()나"로딩 중","불러오기 실패"텍스트 정도를 추가해 두면 UX가 많이 개선될 것 같습니다.app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt (1)
56-98: 로딩/에러 UI의 레이아웃을 약간만 손보면 가독성이 좋아질 것 같습니다.
PostListUiState.Loading에서CircularProgressIndicator()만 바로 배치하면, 상단 AppBar 아래 좌측 상단에 작게 떠서 사용자가 잘 인지하지 못할 수 있습니다.PostListUiState.Error의"로딩 실패"텍스트도paddingValues가 적용되지 않아 AppBar에 붙어 보일 가능성이 있습니다.아래처럼 두 분기 모두
Box(Modifier.fillMaxSize().padding(paddingValues), contentAlignment = Alignment.Center)안에서 표시하도록 정리하면, 로딩/에러 상태가 더 눈에 잘 들어오고 다른 상태와 레이아웃 일관성도 맞출 수 있을 것 같습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (37)
app/build.gradle.kts(1 hunks)app/src/main/AndroidManifest.xml(1 hunks)app/src/main/java/com/example/kuit6_android_api/App.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/MainActivity.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt(2 hunks)app/src/main/java/com/example/kuit6_android_api/data/di/AppContainer.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/model/request/LoginRequest.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/model/response/LoginResponse.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/repository/LoginRepository.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/repository/LoginRepositoryImpl.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepository.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/repository/TokenRepository.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/data/repository/TokenRepositoryImpl.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/navigation/Routes.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/screen/LoginScreen.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt(2 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt(10 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt(2 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt(3 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/state/DeletePostUiState.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/state/LoginUiState.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostCreateUiState.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostDetailUiState.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostEditUiState.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostListUiState.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/state/UploadImageUiState.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/LoginViewModel.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/LoginViewModelFactory.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostCreateViewModel.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt(1 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt(0 hunks)app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt(1 hunks)gradle/libs.versions.toml(2 hunks)
💤 Files with no reviewable changes (1)
- app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt
🔇 Additional comments (22)
app/src/main/java/com/example/kuit6_android_api/ui/post/state/UploadImageUiState.kt (1)
5-15: UploadImageUiState 설계가 명확하고 일관적입니다.이미지 업로드 흐름을
Idle / Loading / Success / Error로 나눈 sealed class 구조가 직관적이고, 다른 UI state 패턴과도 잘 맞아 보여서 사용성과 유지보수성이 좋습니다. 현재로서는 추가 수정이 필요 없어 보입니다.app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt (1)
10-20: LGTM!ViewModel 팩토리 구현이 올바르며, DI 패턴을 적절히 따르고 있습니다.
reified타입 파라미터와crossinline사용이 정확합니다.app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostCreateUiState.kt (1)
5-15: LGTM!UI 상태 모델링이 올바르게 구현되었습니다. Success 상태에 의미 있는 페이로드(
PostResponse)를 포함하고 있어 적절합니다.app/src/main/AndroidManifest.xml (1)
17-17: LGTM!커스텀 Application 클래스 등록이 올바르게 구현되었습니다. DI 컨테이너 초기화를 위한 표준 Android 패턴입니다.
app/src/main/java/com/example/kuit6_android_api/data/model/request/LoginRequest.kt (1)
5-9: LGTM!로그인 요청 모델이 올바르게 구현되었습니다.
@Serializable어노테이션과 불변 프로퍼티 사용이 적절합니다.gradle/libs.versions.toml (1)
33-33: DataStore 라이브러리 추가가 올바르게 구성되었습니다.버전 카탈로그 항목이 올바르게 구조화되어 있습니다. 다만, alpha 버전(1.3.0-alpha01)을 사용하고 있으므로, 프로덕션 배포 전에 안정 버전으로 업그레이드하는 것을 고려하세요.
Also applies to: 73-73
app/src/main/java/com/example/kuit6_android_api/data/model/response/LoginResponse.kt (1)
5-10: LGTM! 깔끔한 응답 모델 구조입니다.@serializable 어노테이션을 사용한 데이터 클래스 구조가 적절합니다. API 응답 모델로서 필요한 필드들이 명확하게 정의되어 있습니다.
app/src/main/java/com/example/kuit6_android_api/App.kt (1)
6-12: LGTM! 표준적인 DI 컨테이너 초기화 패턴입니다.Application 클래스에서 DI 컨테이너를 초기화하는 방식이 적절합니다.
lateinit을 사용했지만,onCreate()에서 즉시 초기화되므로 안전합니다.app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostEditUiState.kt (1)
5-15: LGTM! 타입 안전한 UI 상태 관리 구조입니다.sealed class를 사용하여 Loading, Success, Error 상태를 명확하게 구분한 것이 좋습니다. 이 패턴은 컴파일 타임에 모든 케이스를 처리하도록 강제하여 안전한 UI 상태 관리를 가능하게 합니다.
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/LoginViewModelFactory.kt (1)
11-22: LGTM! 깔끔한 ViewModel 팩토리 구현입니다.
reified타입 파라미터와viewModelFactoryDSL을 활용하여 타입 안전한 팩토리를 구현했습니다. LoginRepository와 TokenRepository 의존성을 주입하는 패턴이 적절합니다.app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostDetailUiState.kt (1)
5-15: LGTM! 일관성 있는 UI 상태 패턴입니다.PostEditUiState 및 PostListUiState와 동일한 sealed class 패턴을 사용하여 코드베이스 전체에 일관성을 유지하고 있습니다.
app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostListUiState.kt (1)
5-15: LGTM! 리스트 화면에 적합한 UI 상태 구조입니다.다른 UI 상태 클래스들과 일관된 패턴을 유지하면서, Success 상태에서
List<PostResponse>를 포함하여 리스트 화면의 요구사항을 적절히 반영했습니다.app/src/main/java/com/example/kuit6_android_api/data/repository/LoginRepository.kt (1)
5-8: LGTM! 명확한 레포지토리 계약입니다.
suspend함수와Result<LoginResponse>반환 타입을 사용하여 비동기 작업과 에러 처리를 타입 안전하게 정의했습니다. 인터페이스가 간결하고 명확합니다.app/src/main/java/com/example/kuit6_android_api/ui/navigation/Routes.kt (1)
17-18: LGTM!
LoginRoute추가가 올바르게 구현되었습니다. 모든 route 객체가@Serializable로 표시되어 있어 타입 안전 네비게이션을 지원합니다.app/src/main/java/com/example/kuit6_android_api/data/repository/TokenRepository.kt (1)
5-8: LGTM!
TokenRepository인터페이스가 간결하고 명확하게 정의되었습니다. suspend 함수를 사용하여 비동기 작업을 올바르게 지원합니다.app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt (1)
68-73: LGTM!
savedStateHandle을 사용하여 게시물 생성/수정 후 목록 새로고침을 트리거하는 패턴이 잘 구현되었습니다. 이는 화면 간 상태 동기화를 위한 좋은 접근 방식입니다.app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt (1)
38-50: LGTM!게시글 상세 정보를 불러오는 로직이 올바르게 구현되었습니다. 에러 처리와 상태 관리가 적절합니다.
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt (1)
37-39: LGTM!refresh 함수가 간결하고 명확하게 구현되었습니다. Pull-to-refresh 패턴에 적합합니다.
app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt (1)
67-75: LGTM!삭제 작업에 대한 처리가 적절합니다.
Unit반환 타입과 단순한 성공 여부 확인이 삭제 API의 특성에 맞게 구현되었습니다.app/src/main/java/com/example/kuit6_android_api/data/di/AppContainer.kt (1)
12-28: 검증 완료: 의존성 주입 구조가 정상적으로 작동 중입니다TokenRepositoryImpl의 구현을 확인한 결과, Context 의존성이 다음과 같이 올바르게 처리되고 있습니다:
- 생성자 레벨: TokenRepositoryImpl()는 인자 없이 안전하게 인스턴스화되며, AppContainer의 구현이 정확합니다.
- 메서드 레벨: saveToken(context: Context)과 getToken(context: Context) 메서드에서 Context를 매개변수로 받아 runtime에 주입됩니다.
- DataStore 접근: Context 확장 프로퍼티
context.datastore를 통해 DataStore에 접근하는 방식은 표준 Android 패턴입니다.원래 제시된 우려사항은 확인 결과 문제가 아닙니다. 코드는 메서드 레벨 의존성 주입 패턴을 사용하고 있으며, 이는 Android에서 흔히 사용되는 유효한 방식입니다.
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt (1)
88-170: 상태 기반 UI 전환 구조는 전체적으로 잘 잡혀 있습니다.
PostEditUiState의 Loading/Success/Error 분기와, 성공 시에만title/content를 한 번 초기화하는 패턴이 깔끔해서 이후 기능 추가(예: 저장 중 상태, 검증 에러 등)에도 확장성이 좋아 보입니다. UI 레이아웃도paddingValues와verticalScroll조합이 자연스럽습니다.app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt (1)
38-48: 전용 ViewModel + 초기refresh()트리거 흐름은 잘 정리되었습니다.목록 화면에서
PostListViewModel을 외부에서 주입하고,LaunchedEffect(Unit)에서 한 번만refresh()를 호출하는 구조가 명확하고 사이드 이펙트 위치도 잘 드러납니다. 다른 화면들과의 아키텍처 일관성도 좋아 보입니다.
|
|
||
| // Coroutines | ||
| implementation(libs.kotlinx.coroutines.android) | ||
| implementation(libs.androidx.datastore.preferences) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
DataStore alpha 버전 사용을 확인하세요.
androidx.datastore.preferences 라이브러리의 alpha 버전(1.3.0-alpha01)을 사용하고 있습니다. 프로덕션 환경에서는 안정적인(stable) 버전 사용을 권장합니다.
다음 스크립트를 실행하여 최신 안정 버전을 확인하세요:
🏁 Script executed:
#!/bin/bash
# DataStore Preferences 최신 버전 확인
curl -s https://maven.google.com/web/index.html?q=androidx.datastore#androidx.datastore:datastore-preferences | grep -oP 'datastore-preferences/[\d.]+' | head -5Length of output: 0
검증을 위해 추가 정보를 수집하겠습니다.
이제 실제 build.gradle.kts 파일과 버전 카탈로그를 확인하겠습니다.
Now awaiting script results to verify the actual version being used.
🏁 Script executed:
#!/bin/bash
# build.gradle.kts의 datastore 관련 라인 확인
echo "=== build.gradle.kts 라인 75-90 ==="
sed -n '75,90p' app/build.gradle.kts
echo -e "\n=== libs.versions.toml 또는 gradle/libs.versions.toml 찾기 ==="
find . -name "libs.versions.toml" -o -name "gradle.properties" | head -5Length of output: 213
---
🏁 Script executed:
```shell
#!/bin/bash
# build.gradle.kts 파일 위치 찾기
echo "=== build.gradle.kts 파일 위치 ==="
find . -name "build.gradle.kts" -type f | head -10
echo -e "\n=== libs.versions.toml 내용 ==="
cat ./git/gradle/libs.versions.toml
Length of output: 167
---
🏁 Script executed:
```shell
#!/bin/bash
# app/build.gradle.kts의 라인 81 주변 확인
echo "=== app/build.gradle.kts 라인 75-90 ==="
sed -n '75,90p' ./app/build.gradle.kts
echo -e "\n=== gradle/libs.versions.toml 내용 ==="
cat ./gradle/libs.versions.toml 2>/dev/null || echo "파일을 찾을 수 없습니다."
Length of output: 4394
이제 최신 안정 버전을 확인하겠습니다.
---
🌐 Web query:
```web
androidx.datastore preferences latest stable version 2024 2025
💡 Result:
- Latest stable androidx.datastore:datastore-preferences (as of Nov 20, 2025): 1.1.7 (released May 20, 2025). [1]
- Key 2024 stable releases: 1.1.0 (Apr 17, 2024) and 1.1.1 (May 1, 2024). [1][2]
Sources:
[1] Android Developers — DataStore release notes.
[2] Maven / mvnrepository listing for datastore-preferences.
androidx.datastore.preferences alpha 버전을 안정 버전으로 업그레이드하세요.
프로젝트에서 최신 안정 버전인 1.1.7(2025년 5월 20일 릴리스)로 업그레이드할 것을 권장합니다. gradle/libs.versions.toml의 datastorePreferences 버전을 다음과 같이 변경하세요:
datastorePreferences = "1.1.7"
변경 후 프로덕션 환경에 배포하기 전에 충분한 테스트를 수행해주세요.
🤖 Prompt for AI Agents
In app/build.gradle.kts around line 81, the project is using the alpha version
of androidx.datastore.preferences; update the version in
gradle/libs.versions.toml by setting datastorePreferences = "1.1.7" (the stable
2025-05-20 release) and then run a full build and regression tests to verify no
regressions before deploying to production.
| @POST("/api/upload") | ||
| suspend fun uploadImage( | ||
| @Part file: MultipartBody.Part | ||
| ): BaseResponse<Map<String, String>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@multipart 어노테이션이 누락되었습니다.
uploadImage 함수는 @Part를 사용하여 MultipartBody.Part를 받고 있지만, 함수에 @Multipart 어노테이션이 없습니다. Retrofit에서 multipart 요청을 처리하려면 이 어노테이션이 필요합니다.
+@Multipart
@POST("/api/upload")
suspend fun uploadImage(
@Part file: MultipartBody.Part
): BaseResponse<Map<String, String>>🤖 Prompt for AI Agents
In app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt around
lines 47 to 50, the suspend function uploadImage is missing the @Multipart
annotation even though it accepts a @Part MultipartBody.Part; add the @Multipart
annotation to the function declaration so Retrofit treats the request as
multipart/form-data and keep the existing @POST and @Part parameters unchanged.
| val Context.datastore: DataStore<Preferences> by preferencesDataStore("Token") | ||
| val KEY = stringPreferencesKey("token") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
top-level 선언을 private으로 제한하세요.
Context.datastore와 KEY가 파일의 top-level에서 public으로 선언되어 있습니다. 이는 네임스페이스 오염을 유발할 수 있으며, 다른 모듈이나 파일에서 실수로 접근할 수 있습니다.
다음 diff를 적용하여 접근 범위를 제한하세요:
-val Context.datastore: DataStore<Preferences> by preferencesDataStore("Token")
-val KEY = stringPreferencesKey("token")
+private val Context.datastore: DataStore<Preferences> by preferencesDataStore("token")
+private val KEY = stringPreferencesKey("token")추가로 DataStore 이름도 "Token"에서 "token"으로 소문자로 통일하는 것을 권장합니다.
📝 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.
| val Context.datastore: DataStore<Preferences> by preferencesDataStore("Token") | |
| val KEY = stringPreferencesKey("token") | |
| private val Context.datastore: DataStore<Preferences> by preferencesDataStore("token") | |
| private val KEY = stringPreferencesKey("token") |
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/data/repository/TokenRepositoryImpl.kt
around lines 11 to 12, the top-level declarations are public; make them
file-private by adding the private modifier to both the Context.datastore
extension and the KEY declaration, and change the DataStore name from "Token" to
lowercase "token" to standardize naming (i.e., private val Context.datastore:
DataStore<Preferences> by preferencesDataStore("token") and private val KEY =
stringPreferencesKey("token") so they are not exposed outside this file).
| @Composable | ||
| fun LoginScreen( | ||
| modifier: Modifier = Modifier, | ||
| onNavigateBack: ()-> Unit, | ||
| viewModel: LoginViewModel= viewModel() | ||
| ) { | ||
| val uiState by viewModel.uiState.collectAsStateWithLifecycle() | ||
| val context = LocalContext.current |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
ViewModel 기본값 제거 권장
Line 29에서 viewModel: LoginViewModel = viewModel()로 기본값을 제공하고 있습니다. DI 컨테이너(AppContainer)를 사용하는 아키텍처에서는 ViewModel에 생성자 인자(LoginRepository, TokenRepository)가 필요하므로, 기본 viewModel() 호출은 실패할 수 있습니다. Navigation에서 ViewModelFactory를 통해 ViewModel을 주입받도록 기본값을 제거하는 것을 권장합니다.
fun LoginScreen(
modifier: Modifier = Modifier,
onNavigateBack: ()-> Unit,
- viewModel: LoginViewModel= viewModel()
+ viewModel: LoginViewModel
) {📝 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.
| @Composable | |
| fun LoginScreen( | |
| modifier: Modifier = Modifier, | |
| onNavigateBack: ()-> Unit, | |
| viewModel: LoginViewModel= viewModel() | |
| ) { | |
| val uiState by viewModel.uiState.collectAsStateWithLifecycle() | |
| val context = LocalContext.current | |
| @Composable | |
| fun LoginScreen( | |
| modifier: Modifier = Modifier, | |
| onNavigateBack: ()-> Unit, | |
| viewModel: LoginViewModel | |
| ) { | |
| val uiState by viewModel.uiState.collectAsStateWithLifecycle() | |
| val context = LocalContext.current |
| Row() { | ||
| val context = LocalContext.current | ||
| Button(onClick = { | ||
| viewModel.login(context) | ||
| }) { | ||
| Text("로그인") | ||
| } | ||
| Button(onClick = { | ||
| viewModel.signup(context) | ||
| }) { | ||
| Text("회원가입") | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
중복된 LocalContext 선언 제거
Line 73에서 LocalContext.current를 다시 선언하고 있지만, Line 32에서 이미 선언되어 사용 가능합니다. 중복 선언은 불필요하며 코드 가독성을 해칩니다.
Row() {
- val context = LocalContext.current
Button(onClick = {Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In app/src/main/java/com/example/kuit6_android_api/ui/post/screen/LoginScreen.kt
around lines 72 to 84, remove the redundant "val context = LocalContext.current"
declared inside the Row (line 73) and instead use the existing context variable
declared earlier (line 32); simply delete the inner declaration and pass the
outer context to viewModel.login(...) and viewModel.signup(...) so there is a
single context variable used throughout the composable.
| data class Success( | ||
| val deletePost : Unit | ||
| ) : DeletePostUiState() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Success 상태에서 불필요한 Unit 프로퍼티를 제거하세요.
Success 상태에 Unit 타입의 프로퍼티를 포함하는 것은 의미가 없습니다. Unit은 "의미 있는 값이 없음"을 나타내므로, data class의 프로퍼티로 포함할 필요가 없습니다.
다음 diff를 적용하여 불필요한 프로퍼티를 제거하세요:
- data class Success(
- val deletePost : Unit
- ) : DeletePostUiState()
+ data object Success : DeletePostUiState()📝 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.
| data class Success( | |
| val deletePost : Unit | |
| ) : DeletePostUiState() | |
| data object Success : DeletePostUiState() |
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/state/DeletePostUiState.kt
around lines 6 to 8, the Success data class currently declares an unnecessary
property of type Unit; remove the Unit property and convert Success to either an
object or a parameterless data class (preferably an object) so it carries no
useless Unit field, updating any call sites that construct Success accordingly.
| val id: String = "", | ||
| val password: String= "", | ||
| val isAutoLogin: Boolean= false, | ||
| val token: String = "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
UI state에 token 필드가 필요한지 검토하세요.
token은 민감한 인증 정보로, TokenRepository를 통해 별도 관리되는 것으로 보입니다. LoginUiState에서 token 필드가 실제로 사용되는지 확인하고, 사용되지 않는다면 제거하는 것을 권장합니다.
다음 스크립트로 token 필드의 실제 사용처를 확인하세요:
🏁 Script executed:
#!/bin/bash
# LoginUiState.token 필드가 실제로 읽히거나 쓰이는지 확인
# token 필드에 접근하는 코드 검색
rg -nP --type=kt -C3 'LoginUiState.*\.token|\.copy\s*\([^)]*token\s*='Length of output: 26
🏁 Script executed:
#!/bin/bash
# Search for LoginUiState.token usage (retry without unsupported type)
echo "=== Searching for LoginUiState.token usage ==="
rg -nP --type=kotlin -C3 'LoginUiState.*\.token|\.copy\s*\([^)]*token\s*=' 2>/dev/null || rg -nP -C3 'LoginUiState.*\.token|\.copy\s*\([^)]*token\s*='
echo -e "\n=== Searching for token property assignment in LoginUiState ==="
rg -nP -C3 'token\s*[:=]' app/src/main/java/com/example/kuit6_android_api/ui/post/state/LoginUiState.kt
echo -e "\n=== Checking for LoginUiState instantiation and token usage ==="
rg -nP -C5 'LoginUiState\s*\(' --type=kotlin 2>/dev/null || rg -nP -C5 'LoginUiState\s*\('Length of output: 3484
🏁 Script executed:
#!/bin/bash
# Search for where token is READ from LoginUiState
echo "=== Searching for token field being READ ==="
rg -nP -C3 'uiState.*\.token|state\.token|\.token\s*(,|\)|:|\s*\?|&&)'
echo -e "\n=== Searching for LoginUiState usage in UI/Composable ==="
rg -nP -C3 'LoginUiState' --type=kotlin 2>/dev/null || rg -nP -C3 'LoginUiState'
echo -e "\n=== Check if token is used in any API calls or repositories ==="
rg -nP -C3 'token.*network|api.*token|send.*token|header.*token'Length of output: 5345
token 필드를 LoginUiState에서 제거하세요.
검증 결과, token 필드가 실제로 사용되고 있음을 확인했습니다:
- 쓰기:
LoginViewModel.kt라인 61에서_uiState.update{it.copy(token = token ?: "")}로 업데이트 - 읽기:
LoginScreen.kt라인 85에서Text("토큰:${uiState.token} ")로 디버깅 목적 표시
그러나 원래 우려사항이 타당합니다. 민감한 인증 정보인 token을 UI state에 노출하는 것은 보안 위험입니다. TokenRepository를 통해 안전하게 관리되어야 하며, UI state에는 포함되면 안 됩니다.
필요한 수정사항:
LoginUiState.kt라인 8:val token: String = "",제거LoginViewModel.kt라인 61:_uiState.update{it.copy(token = token ?: "")}제거LoginScreen.kt라인 85:Text("토큰:${uiState.token} ")제거
🤖 Prompt for AI Agents
In app/src/main/java/com/example/kuit6_android_api/ui/post/state/LoginUiState.kt
around line 8, remove the token property declaration (val token: String = "") so
the UI state no longer carries the sensitive token; in
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/LoginViewModel.kt
around line 61 remove the _uiState.update { it.copy(token = token ?: "") } call
and any other code that writes the token into UI state; in
app/src/main/java/com/example/kuit6_android_api/ui/post/view/LoginScreen.kt
around line 85 remove the debug Text("토큰:${uiState.token} ") that reads/displays
the token; ensure token handling stays inside TokenRepository and no other UI
state or view code exposes the token.
| fun signup(context: Context) { | ||
| viewModelScope.launch { | ||
| loginRepository.signup( | ||
| id = uiState.value.id, | ||
| password = uiState.value.password | ||
| ).onSuccess { | ||
| tokenRepository.saveToken(context, it.token) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
로딩 상태와 에러 처리를 추가하세요.
signup 함수에 로딩 상태나 에러 처리가 없어서 사용자가 작업 진행 상황이나 실패 여부를 알 수 없습니다.
LoginUiState에 isLoading과 error 필드를 추가하고 다음과 같이 구현하세요:
fun signup(context: Context) {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true, error = null) }
loginRepository.signup(
id = uiState.value.id,
password = uiState.value.password
).onSuccess {
tokenRepository.saveToken(context, it.token)
_uiState.update { it.copy(isLoading = false) }
}.onFailure { error ->
_uiState.update {
it.copy(isLoading = false, error = error.message ?: "회원가입 실패")
}
}
}
}🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/LoginViewModel.kt
around lines 37 to 46, the signup function lacks loading and error handling; add
isLoading and error fields to LoginUiState, and in signup update the state
before the call to set isLoading=true and error=null, then on success save the
token and set isLoading=false, and on failure set isLoading=false and populate
error with the throwable message (or a default message) using _uiState.update {
... } in both success and failure branches so the UI can reflect progress and
errors.
| fun login(context: Context) { | ||
| viewModelScope.launch { | ||
| loginRepository.login( | ||
| id = uiState.value.id, | ||
| password = uiState.value.password | ||
| ).onSuccess { | ||
| tokenRepository.saveToken(context, it.token) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
login 함수에도 동일한 개선이 필요합니다.
login 함수도 signup과 동일하게 로딩 상태와 에러 처리가 누락되었습니다. signup 함수에 적용한 개선 사항을 login 함수에도 동일하게 적용하세요.
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/LoginViewModel.kt
around lines 48 to 57, the login function is missing the same loading state and
error handling added to signup; update login to set uiState loading=true before
calling loginRepository, handle the Result by onSuccess saving the token via
tokenRepository.saveToken(context, it.token) and clearing any error, handle
onFailure (or catch exception) to set uiState error with a user-friendly message
(or the exception message), and ensure loading is set back to false after
completion (use try/catch/finally or Result.onFailure + finally-equivalent) so
the UI reflects loading and error states consistently.
| class PostListViewModel ( | ||
| private val postRepository : PostRepository | ||
| ) : ViewModel() { | ||
| private val _PostList_uiState = MutableStateFlow<PostListUiState>(PostListUiState.Loading) // 변경 가능 상태 | ||
| val postListUiState: StateFlow<PostListUiState> = _PostList_uiState.asStateFlow() // 읽기 전용 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
네이밍 컨벤션 수정 필요
Line 15의 _PostList_uiState가 PascalCase를 사용하고 있어 Kotlin 네이밍 컨벤션을 위반합니다. 프로퍼티명은 camelCase를 따라야 합니다.
- private val _PostList_uiState = MutableStateFlow<PostListUiState>(PostListUiState.Loading)
- val postListUiState: StateFlow<PostListUiState> = _PostList_uiState.asStateFlow()
+ private val _postListUiState = MutableStateFlow<PostListUiState>(PostListUiState.Loading)
+ val postListUiState: StateFlow<PostListUiState> = _postListUiState.asStateFlow()그리고 Line 23, 27, 30에서도 _postListUiState로 변경해야 합니다.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt
around lines 12 to 16, the private MutableStateFlow is named `_PostList_uiState`
which violates Kotlin camelCase conventions; rename it to `_postListUiState` and
update all usages accordingly (including the references on lines 23, 27 and 30)
so the private variable and its asStateFlow exposure use the new camelCase
identifier consistently.
Summary by CodeRabbit
새로운 기능
✏️ Tip: You can customize this high-level summary in your review settings.