Skip to content

7주차 미션/안드로이드 1조 안현지#25

Open
hyeonjiana wants to merge 8 commits into
Konkuk-KUIT:developfrom
hyeonjiana:week07
Open

7주차 미션/안드로이드 1조 안현지#25
hyeonjiana wants to merge 8 commits into
Konkuk-KUIT:developfrom
hyeonjiana:week07

Conversation

@hyeonjiana

@hyeonjiana hyeonjiana commented Nov 14, 2025

Copy link
Copy Markdown

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 백엔드 API 연동으로 게시물 데이터를 네트워크에서 실시간으로 조회
    • 게시물 생성, 조회, 수정, 삭제(CRUD) 기능 완벽 지원
    • 로딩 상태, 성공, 오류 상태에 따른 UI 피드백 추가
    • 작업 완료 시 스낵바 알림으로 사용자 경험 개선

@coderabbitai

coderabbitai Bot commented Nov 14, 2025

Copy link
Copy Markdown

Walkthrough

이 PR은 로컬 더미 데이터에서 API 기반 아키텍처로의 전환을 구현합니다. Retrofit/OkHttp를 이용한 네트워킹 계층, Repository 패턴 기반 데이터 접근, MVVM 상태 관리, 의존성 주입 컨테이너, 그리고 UI 상태 표현을 추가합니다.

Changes

코호트 / 파일(들) 변경 요약
네트워킹 계층
app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt, RetrofitClient.kt
Retrofit 인터페이스와 OkHttp 로깅, Gson 변환기를 포함한 Retrofit 클라이언트 추가. GET/POST/DELETE/PUT 엔드포인트로 5개 API 메서드 정의
요청/응답 모델
app/src/main/java/com/example/kuit6_android_api/data/model/request/PostCreateRequest.kt, response/BaseResponse.kt, response/PostResponse.kt, response/AuthorResponse.kt
PostCreateRequest, BaseResponse, PostResponse, AuthorResponse 데이터 클래스 추가. @SerialName으로 JSON 필드 매핑
Repository 패턴
app/src/main/java/com/example/kuit6_android_api/data/model/repository/PostRepository.kt, PostRepositoryImpl.kt
PostRepository 인터페이스 및 ApiService 위임을 통한 PostRepositoryImpl 구현. Result 래퍼로 CRUD 작업 반환
의존성 주입
app/src/main/java/com/example/kuit6_android_api/data/di/AppContaitner.kt, app/src/main/java/com/example/kuit6_android_api/App.kt, AndroidManifest.xml
AppContainer로 lazy 초기화 DI 컨테이너 구성. 커스텀 Application 클래스 추가 및 AndroidManifest에 등록
UI 상태 관리
app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostListUiState.kt, PostDetailUiState.kt, PostEditUiState.kt, CreateUiState.kt
Loading/Success/Error 상태를 나타내는 sealed class 기반 UI 상태 클래스들 추가
ViewModel 레이어
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt, PostDetailViewModel.kt, PostCreateViewModel.kt, PostEditViewModel.kt, PostViewModelFactory.kt
각 화면별 전문 ViewModel 및 제네릭 Factory 추가. StateFlow로 UI 상태 노출
UI 화면 업데이트
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt, PostDetailScreen.kt, PostCreateScreen.kt, PostEditScreen.kt, component/PostItem.kt
UI 상태 기반 렌더링으로 전환. SnackbarHostState 매개변수 추가. 전문 ViewModel 사용
네비게이션 및 기존 모델 제거
app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt, app/src/main/java/com/example/kuit6_android_api/data/model/Post.kt
NavGraph에 snackBarState 매개변수 추가. 기존 Post/Author 데이터 모델 제거
빌드 설정
.idea/gradle.xml, app/build.gradle.kts, gradle/libs.versions.toml
Gradle 모듈 참조 경로 업데이트. BuildConfig.BASE_URL 추가. Retrofit, OkHttp, adapter-guava, androidx.startup 의존성 추가

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI Screen
    participant VM as ViewModel
    participant Repo as Repository
    participant API as ApiService
    participant Net as Network

    UI->>VM: loadPosts() / loadPost(id)
    VM->>VM: _uiState = Loading
    VM->>Repo: getPost() / getPostDetail(id)
    Repo->>API: 해당 메서드 호출
    API->>Net: HTTP 요청
    Net-->>API: HTTP 응답
    alt 성공
        API-->>Repo: BaseResponse<T>
        Repo->>Repo: data 검증 및 매핑
        Repo-->>VM: Result.success(PostResponse)
        VM->>VM: _uiState = Success(data)
    else 실패
        API-->>Repo: BaseResponse (오류)
        Repo-->>VM: Result.failure(Exception)
        VM->>VM: _uiState = Error(message)
    end
    VM-->>UI: uiState 업데이트 감지
    UI->>UI: when(uiState) 렌더링 업데이트
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

추가 검토 필요 영역:

  • PostViewModel.kt — 기존 로컬 데이터 모델에서 API 기반 PostResponse로의 전환. 모든 메서드(getPosts, getPostDetail, createPost, updatePost, deletePost)의 로직 검증 필요
  • PostViewModelFactory.kt — 제네릭 ViewModel 생성 로직. create 람다가 무시되는 부분과 실제 ViewModel 타입 매핑 확인 필요
  • PostDetailScreen.kt, PostCreateScreen.kt, PostEditScreen.kt — 각 화면의 UI 상태 처리 로직과 SnackbarHostState 통합. 비동기 작업의 상태 전이 흐름 검증
  • PostRepositoryImpl.kt — runCatching 기반 예외 처리와 기본값 메시지("규반" vs 매개변수 author 불일치) 확인
  • AppContainer.kt — lazy 초기화 순서와 의존성 주입 구조 검증
  • app/build.gradle.kts — local.properties에서 BASE_URL 로딩 메커니즘 검증

Possibly related PRs

Poem

🐰 API를 집어삼켜 네트워크의 소리 들으니,
Repository는 데이터 정원의 수호자요,
ViewModel과 State는 춤을 춘다네 🎭
DI 컨테이너에서 의존성 열매가 열리고,
UI는 비동기의 파도를 타며 새로 태어났네! 🌊

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목은 저자 이름과 주차 정보를 포함하고 있으나, 변경사항의 핵심 내용을 구체적으로 설명하지 않습니다. 제목을 구체적인 변경사항으로 수정하십시오. 예: 'Retrofit 기반 API 통합 및 DI 컨테이너 구현' 또는 'Post CRUD API 연동 및 ViewModel 리팩토링'
Docstring Coverage ⚠️ Warning Docstring coverage is 5.13% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hyeonjiana

Copy link
Copy Markdown
Author

postEdit를 구현하지 못했어요...

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 22

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt (1)

63-92: val post = (postId,)는 잘못된 문법이며, 아래 사용과도 타입이 맞지 않습니다.

현재 코드:

val uiState by viewModel.uiState.collectAsState()

val post = (postId,)
...
LaunchedEffect(post) {
    if (post != null && !isLoaded) {
        title = post.title
        content = post.content
        isLoaded = true
    }
}
...
post?.let {
    // it.title, it.imageUrl 등 사용
}

문제점:

  • (postId,)는 Kotlin에서 유효한 튜플 문법이 아니고, post.title, post.imageUrl 등을 사용할 수 있는 타입이 아닙니다.
  • 실제로는 uiState에서 서버에서 받아온 Post(예: PostResponse)를 꺼내 post에 넣어야 합니다.

예를 들어 PostEditUiState.Success(post: PostResponse) 같은 구조라면, 다음과 같이 정리할 수 있습니다.

val uiState by viewModel.uiState.collectAsState()

val post = when (uiState) {
    is PostEditUiState.Success -> (uiState as PostEditUiState.Success).post
    else -> null
}

LaunchedEffect(post) {
    if (post != null && !isLoaded) {
        title = post.title
        content = post.content
        isLoaded = true
    }
}
...
post?.let { post ->
    // 기존 UI 그대로
}

또한 uiStateLoading/Error 상태를 가질 수 있다면, post?.let { ... } 바깥에서 when (uiState)로 로딩 인디케이터나 에러 메시지를 표시하는 분기도 추가하는 것을 고려해 주세요. 지금 상태로는 로딩 중/에러 시 화면이 비어 보일 수 있습니다.

♻️ Duplicate comments (2)
app/build.gradle.kts (1)

81-82: 사용되지 않는 의존성일 수 있습니다.

이 의존성들은 gradle/libs.versions.toml 파일에서도 지적한 것과 동일한 문제입니다. 실제로 코드에서 사용되지 않는다면 제거하는 것이 좋습니다.

app/src/main/java/com/example/kuit6_android_api/App.kt (1)

9-9: 클래스 이름과 속성 이름의 오타를 수정하세요.

AppContaitner 타입과 contaitner 속성 이름 모두 오타가 있습니다. AppContainercontainer로 수정해야 합니다. 이는 AppContaitner.kt 파일에서 지적한 것과 동일한 문제입니다.

🧹 Nitpick comments (15)
app/src/main/java/com/example/kuit6_android_api/data/model/response/BaseResponse.kt (1)

8-8: 주석 품질을 개선하는 것을 고려하세요.

"데이터가 안 올 수도 ?"는 비공식적입니다. 더 명확한 설명으로 개선할 수 있습니다.

-    @SerialName(value = "data")  val data: T?,//데이터가 안 올 수도 ?
+    @SerialName(value = "data")  val data: T?, // 응답에 데이터가 없을 수 있음
app/src/main/java/com/example/kuit6_android_api/data/model/request/PostCreateRequest.kt (1)

6-8: 어노테이션 공백이 일관되지 않습니다.

@SerialName 어노테이션의 공백 스타일을 통일하는 것을 고려하세요.

-    @SerialName( value = "title" ) val title: String,
-    @SerialName( value = "content" ) val content: String,
-    @SerialName(value = "imageUrl" ) val imageUrl: String?
+    @SerialName(value = "title") val title: String,
+    @SerialName(value = "content") val content: String,
+    @SerialName(value = "imageUrl") val imageUrl: String?
app/src/main/java/com/example/kuit6_android_api/data/model/response/PostResponse.kt (1)

6-13: 어노테이션 공백 스타일을 일관되게 유지하세요.

@SerialName 어노테이션의 공백이 일관되지 않습니다.

-    @SerialName(value = "id")  val id: Long,
+    @SerialName(value = "id") val id: Long,
app/src/main/java/com/example/kuit6_android_api/data/api/RetrofitClient.kt (1)

13-15: 프로덕션 빌드에서 로깅 레벨 조정을 고려하세요.

개발 중에는 HttpLoggingInterceptor.Level.BODY가 유용하지만, 릴리즈 빌드에서는 성능과 보안을 위해 Level.NONE 또는 Level.BASIC으로 변경하는 것이 좋습니다.

빌드 타입에 따라 조건부로 설정하려면 다음과 같이 수정할 수 있습니다:

 private val loggingInterceptor = HttpLoggingInterceptor().apply {
-    level = HttpLoggingInterceptor.Level.BODY
+    level = if (BuildConfig.DEBUG) {
+        HttpLoggingInterceptor.Level.BODY
+    } else {
+        HttpLoggingInterceptor.Level.NONE
+    }
 }
app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt (1)

29-32: 주석 처리된 코드를 제거하세요.

사용하지 않는 주석 처리된 코드는 코드베이스를 어지럽히고 혼란을 줄 수 있습니다. 더 이상 필요하지 않다면 제거하는 것이 좋습니다.

-
-            /*val viewModel = viewModel<PostListViewModel>(
-                factory = PostViewModelFactory.Factory
-            )*/
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt (1)

7-9: 사용하지 않는 import를 제거하세요.

CreateUiStatePostDetailUiState는 이 파일에서 사용되지 않습니다.

 import com.example.kuit6_android_api.data.model.repository.PostRepository
 import com.example.kuit6_android_api.data.model.request.PostCreateRequest
-import com.example.kuit6_android_api.ui.post.state.CreateUiState
-import com.example.kuit6_android_api.ui.post.state.PostDetailUiState
 import com.example.kuit6_android_api.ui.post.state.PostEditUiState
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt (1)

8-8: 사용하지 않는 import를 제거하세요.

androidx.startup.Initializer는 이 파일에서 사용되지 않습니다.

 import androidx.lifecycle.viewmodel.initializer
 import androidx.lifecycle.viewmodel.viewModelFactory
-import androidx.startup.Initializer
 import com.example.kuit6_android_api.App
app/src/main/java/com/example/kuit6_android_api/ui/post/component/PostItem.kt (1)

32-33: 주석 처리된 import를 제거하세요.

더 이상 사용하지 않는 주석 처리된 import 문은 제거하는 것이 좋습니다.

 import coil.compose.AsyncImage
-//import com.example.kuit6_android_api.data.model.Author
-//import com.example.kuit6_android_api.data.model.Post
 import com.example.kuit6_android_api.data.model.response.AuthorResponse
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt (1)

6-6: 사용하지 않는 import를 제거하세요.

CreateUiState는 이 파일에서 사용되지 않습니다.

 import com.example.kuit6_android_api.data.model.repository.PostRepository
-import com.example.kuit6_android_api.ui.post.state.CreateUiState
 import com.example.kuit6_android_api.ui.post.state.PostDetailUiState
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt (1)

37-77: 로딩/에러 상태에서도 Scaffold 패딩과 레이아웃을 일관되게 적용하는 게 좋습니다.

지금은 Success 분기에서만 padding(paddingValues)fillMaxSize()를 써서 리스트를 배치하고, Loading/Error 분기에서는 단순히 CircularProgressIndicator()/Text("로딩 실패")만 그려서 상단 앱바 아래 여백, FAB와의 겹침 등이 어색할 수 있습니다.

예를 들어 아래처럼 공통 컨테이너를 두고 상태별로 내용을 바꾸면 레이아웃이 더 안정적입니다.

Scaffold(/*...*/) { paddingValues ->
    Box(
        modifier = Modifier
            .fillMaxSize()
            .padding(paddingValues)
            .background(MaterialTheme.colorScheme.background),
        contentAlignment = Alignment.Center
    ) {
        when (val state = uiState) {
            is PostListUiState.Loading -> CircularProgressIndicator()
            is PostListUiState.Success -> {
                LazyColumn(
                    modifier = Modifier.fillMaxSize(),
                    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
                    verticalArrangement = Arrangement.spacedBy(16.dp)
                ) {
                    items(
                        items = state.posts,
                        key = { it.id } // 선택 사항: 안정적인 리스트 key
                    ) { post ->
                        PostItem(post = post) { onPostClick(post.id) }
                    }
                }
            }
            is PostListUiState.Error -> Text("로딩 실패")
        }
    }
}

레이아웃 통일 + items(key = ...) 사용은 선택 사항이지만, 추후 리스트 업데이트 시 스크롤 위치 유지 등 UX에 도움이 됩니다.

app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt (1)

14-39: author 기본값과 주석 처리 정리가 필요해 보입니다.

  • createPost에서 author 기본값을 "규빈"으로 두고 있는데, Repository 구현에서는 "규반"을 하드코딩해 사용하고 있어 서로 불일치합니다. 실제 의도에 맞게 한 곳에서만 기본값을 관리하거나, 파라미터로 받은 값을 그대로 전달하도록 통일해 주세요.
  • Line 14의 //file 들어가서 project structure 에서 retrofit2 추가 해야된 주석은 현재 Retrofit 의존성이 이미 설정되어 있다면 제거하거나, 필요한 설정이라면 README/문서로 옮기는 편이 코드 가독성에 더 좋습니다.
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt (2)

102-105: 로딩/에러 상태에서도 paddingValues를 사용해 상단 AppBar와 겹치지 않도록 하는 편이 좋습니다.

현재 Loading/Error 분기에서는 ScaffoldpaddingValues를 적용하지 않아, 텍스트가 TopAppBar 뒤에 살짝 가려질 수 있습니다. 간단히 Modifier.padding(paddingValues)를 함께 적용하면 일관된 레이아웃을 유지할 수 있습니다.

예시:

-            is PostDetailUiState.Loading -> {
-                Text("로딩 중...", modifier = Modifier.padding(16.dp))
-            }
+            is PostDetailUiState.Loading -> {
+                Text(
+                    "로딩 중...",
+                    modifier = Modifier
+                        .padding(paddingValues)
+                        .padding(16.dp)
+                )
+            }
...
-            is PostDetailUiState.Error -> {
-                val message = (uiState as PostDetailUiState.Error).message
-                Text("에러: $message", modifier = Modifier.padding(16.dp))
-            }
+            is PostDetailUiState.Error -> {
+                val message = (uiState as PostDetailUiState.Error).message
+                Text(
+                    "에러: $message",
+                    modifier = Modifier
+                        .padding(paddingValues)
+                        .padding(16.dp)
+                )
+            }

필수 사항은 아니지만, 전체 화면에서 패딩 일관성이 좋아집니다.

Also applies to: 217-220


249-258: Preview에서 ViewModel/DI 의존성 때문에 미리보기가 깨질 수 있습니다.

PostDetailScreenPreview에서 기본 파라미터를 사용하면 viewModel(factory = PostViewModelFactory { ... })가 실제 Application/DI 컨테이너를 요구해서 Preview 환경에서는 실패할 수 있습니다.

필요하다면 Preview 전용 오버로드를 하나 더 두고, 미리보기에서는 고정된 더미 postSnackbarHostState만 넘겨주는 방식도 고려해 보세요. (예: PostDetailScreen(uiState = PostDetailUiState.Success(dummyPost), ...) 형태의 프리뷰 전용 컴포즈블)

app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt (2)

17-25: ViewModel에서 직접 RetrofitClient를 쓰기보다는 Repository 주입으로 통일하는 것이 좋습니다.

이 PR 전체 요약을 보면 다른 계층은 Repository 패턴과 DI 컨테이너를 사용하도록 정리된 것으로 보이는데, PostViewModel은 아직 RetrofitClient.apiService를 직접 들고 있습니다. 장기적으로는:

  • PostRepository를 생성자 주입받고,
  • 네트워크 세부 구현은 Repository에만 두고,
  • ViewModel은 UI 상태/비즈니스 로직만 담당하도록

정리하면 테스트성과 아키텍처 일관성이 좋아집니다.


27-42: 사용하지 않는 getPosts 구현이 주석으로 남아 있어 혼동을 줄 수 있습니다.

주석에 “얜 이제 사용을 안함”이라고 되어 있는데, 그렇다면:

  • 정말 불필요한 코드라면 과감히 삭제하거나,
  • 새 API/Repository 기반 구현으로 교체한 뒤 주석을 제거하는 편이

향후 유지보수 시 혼선을 줄여 줍니다. Git 히스토리가 있으니, 필요하면 언제든 복구도 가능할 것입니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e1e035 and ec8cf1e.

📒 Files selected for processing (32)
  • .idea/gradle.xml (1 hunks)
  • app/build.gradle.kts (4 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 (3 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/api/RetrofitClient.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/di/AppContaitner.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/model/Post.kt (0 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/model/repository/PostRepository.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/model/repository/PostRepositoryImpl.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/model/request/PostCreateRequest.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/model/response/AuthorResponse.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/model/response/BaseResponse.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/data/model/response/PostResponse.kt (1 hunks)
  • app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt (5 hunks)
  • app/src/main/java/com/example/kuit6_android_api/ui/post/component/PostItem.kt (2 hunks)
  • app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt (5 hunks)
  • app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt (5 hunks)
  • app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt (6 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/CreateUiState.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/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 (2 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/data/model/Post.kt
🧰 Additional context used
🧬 Code graph analysis (6)
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt (1)
app/src/main/java/com/example/kuit6_android_api/ui/post/component/PostItem.kt (1)
  • PostItem (38-151)
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt (1)
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt (1)
  • PostViewModelFactory (11-23)
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt (1)
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt (1)
  • PostViewModelFactory (11-23)
app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt (2)
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt (1)
  • PostListScreen (32-81)
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt (1)
  • PostViewModelFactory (11-23)
app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt (1)
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt (1)
  • PostViewModelFactory (11-23)
app/src/main/java/com/example/kuit6_android_api/MainActivity.kt (1)
app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt (1)
  • NavGraph (18-86)
🔇 Additional comments (20)
.idea/gradle.xml (1)

12-13: LGTM!

표준 Android 프로젝트 구조에 맞게 Gradle 모듈 경로가 올바르게 업데이트되었습니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/state/CreateUiState.kt (1)

5-12: LGTM!

게시글 작성 UI 상태를 위한 sealed class 구조가 잘 설계되었습니다. Idle, Loading, Success, Error 상태가 모두 적절하게 모델링되어 있습니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostDetailUiState.kt (1)

5-10: LGTM!

게시글 상세 화면을 위한 UI 상태가 잘 설계되었습니다. Loading, Success, Deleted, Error 상태가 모두 적절하게 모델링되어 있으며, Deleted 상태에 기본 메시지를 제공하는 것도 합리적입니다.

app/src/main/java/com/example/kuit6_android_api/data/model/repository/PostRepository.kt (1)

7-26: Repository 인터페이스가 잘 설계되었습니다.

Repository 패턴이 올바르게 적용되었으며, Result<T>를 사용한 에러 핸들링 방식이 적절합니다. 모든 함수가 suspend로 선언되어 코루틴 기반의 비동기 처리가 가능합니다.

app/src/main/AndroidManifest.xml (1)

17-17: LGTM!

커스텀 Application 클래스를 올바르게 등록했습니다. DI 컨테이너 초기화를 위해 필요한 변경사항입니다.

app/build.gradle.kts (2)

50-50: LGTM!

BuildConfig.BASE_URL 사용을 위해 필요한 설정입니다.


90-93: LGTM!

Retrofit 및 OkHttp 의존성이 올바르게 추가되었습니다. API 통신을 위한 표준 설정입니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostListUiState.kt (1)

6-16: LGTM!

UI 상태를 나타내는 sealed class 구조가 올바르게 구현되었습니다. Loading, Success, Error 상태를 명확하게 표현하고 있습니다.

app/src/main/java/com/example/kuit6_android_api/data/di/AppContaitner.kt (1)

10-16: LGTM!

의존성 주입 패턴이 올바르게 구현되었습니다. lazy 초기화를 사용하여 필요한 시점에 인스턴스를 생성하는 방식이 적절합니다.

app/src/main/java/com/example/kuit6_android_api/data/api/RetrofitClient.kt (1)

17-31: LGTM!

Retrofit 클라이언트가 올바르게 구성되었습니다. OkHttpClient의 타임아웃 설정(30초)이 적절하고, Gson 컨버터와 함께 표준적인 설정을 사용하고 있습니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostCreateViewModel.kt (2)

15-19: LGTM!

ViewModel의 구조가 올바릅니다. StateFlow를 사용한 상태 관리와 Repository를 통한 의존성 주입이 적절하게 구현되었습니다.


21-38: LGTM!

코루틴을 사용한 비동기 처리가 올바르게 구현되었습니다. Loading 상태 설정, Repository 호출, 그리고 성공/실패 처리가 적절합니다.

app/src/main/java/com/example/kuit6_android_api/App.kt (1)

11-13: LGTM!

Application 클래스에서 DI 컨테이너를 초기화하는 패턴이 올바르게 구현되었습니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostEditUiState.kt (1)

5-11: LGTM!

UI 상태 모델링이 잘 되어 있습니다. sealed class를 사용한 상태 표현이 적절하며, 각 상태가 명확하게 정의되어 있습니다.

app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt (1)

40-40: ViewModel 생성 로직이 잘 구현되어 있습니다.

Factory 패턴을 사용하여 ViewModel을 생성하는 방식이 적절합니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt (1)

17-47: LGTM!

ViewModel 구현이 잘 되어 있습니다. StateFlow를 사용한 상태 관리, init 블록에서의 자동 로딩, 그리고 적절한 에러 처리가 구현되어 있습니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/component/PostItem.kt (1)

40-40: Response 모델로의 전환이 잘 되어 있습니다.

PostResponse를 사용하도록 컴포넌트를 업데이트한 것이 API 기반 아키텍처 전환에 적합합니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt (1)

32-42: LGTM!

삭제 기능이 적절하게 구현되어 있습니다.

app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt (2)

28-57: 신규 import 추가는 의도와 일치해 보입니다.

Snackbar, 코루틴, UI 상태 관련 import 추가가 현재 컴포저블 구현(스낵바 표시, collectAsState, rememberCoroutineScope)과 잘 맞습니다. 별다른 문제는 없어 보입니다.


232-235: 삭제 확인 후 uiState 전이와 에러 처리 방식 확인이 필요합니다.

viewModel.deletePost(postId) 호출 후 바로 showDeleteDialog = false로만 처리하고 있어, 삭제 실패 시 사용자에게 별도 피드백이 없을 수 있습니다. PostDetailUiStateError 상태가 정의되어 있으므로:

  • 삭제 실패 시 PostDetailUiState.Error로 전환되도록 PostDetailViewModel에서 처리하고,
  • 이 화면의 Error 분기에서 메시지를 보여주는지

를 한 번 더 확인해 보시면 좋겠습니다. (코드 상에서는 그 전이가 보이지 않아 확인 요청드립니다.)

Comment thread app/build.gradle.kts
Comment on lines +23 to +29
val properties = Properties()
val localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
properties.load(localPropertiesFile.inputStream())
}
val baseUrl = properties.getProperty("BASE_URL")
buildConfigField("String", "BASE_URL", "\"$baseUrl\"")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

local.properties 설정이 문서화되어 있는지 확인하세요.

BuildConfig.BASE_URL을 위해 local.properties 파일에서 BASE_URL을 읽어오는 구현은 올바릅니다. 다만, 새로운 개발자가 프로젝트를 설정할 때 필요한 정보를 제공하기 위해 README.md나 예제 파일(예: local.properties.example)에 필수 설정이 문서화되어 있는지 확인하세요.

다음 스크립트로 문서화 여부를 확인해주세요:


🏁 Script executed:

#!/bin/bash
# Description: local.properties 설정 관련 문서 확인

echo "=== Checking for README or setup documentation ==="
fd -t f 'README|SETUP|local.properties.example' -x cat {}

echo -e "\n=== Checking for BASE_URL documentation ==="
rg -nP 'BASE_URL' README.md || echo "No BASE_URL documentation found in README.md"

Length of output: 3603


local.propertiesBASE_URL 설정에 대한 문서를 추가하세요.

검증 결과, README.md에 local.properties 파일 설정이나 BASE_URL 구성에 대한 문서가 없습니다. 코드 구현은 올바르지만, 새로운 개발자가 프로젝트를 설정할 때 필요한 다음 정보를 README.md나 local.properties.example 파일에 추가해야 합니다:

  • local.properties 파일 생성 방법
  • 필수 속성 BASE_URL 설정 방법 및 예제 값
🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 23 to 29, the build reads BASE_URL from
local.properties but there is no developer documentation explaining how to
create that file or what BASE_URL should contain; add documentation by creating
or updating README.md (or add a local.properties.example) to show how to create
local.properties in the project root, include the required BASE_URL key with an
example value (e.g. BASE_URL=https://api.example.com), and brief instructions on
where to place the file and that it is not committed to VCS.

import com.example.kuit6_android_api.data.model.repository.PostRepository
import com.example.kuit6_android_api.data.model.repository.PostRepositoryImpl

class AppContaitner {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

클래스 이름의 오타를 수정하세요.

클래스 이름 AppContaitner에 오타가 있습니다. AppContainer로 수정해야 합니다. 이 오타는 다른 파일들(예: App.kt)에도 전파되어 있으므로 모든 참조를 함께 수정해야 합니다.

다음 스크립트로 모든 사용처를 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: AppContaitner 오타 사용처 확인

echo "=== Finding all usages of AppContaitner (typo) ==="
rg -nP '\bAppContaitner\b' --type=kt

echo -e "\n=== Files that need to be updated ==="
rg -l '\bAppContaitner\b' --type=kt

Length of output: 143


🏁 Script executed:

#!/bin/bash
# Description: Search for AppContaitner typo across all Kotlin files

echo "=== Finding all usages of AppContaitner (typo) in .kt files ==="
rg -nP '\bAppContaitner\b' -g '*.kt'

echo -e "\n=== Files that need to be updated ==="
rg -l '\bAppContaitner\b' -g '*.kt'

echo -e "\n=== Checking specific files mentioned in review ==="
fd '\.kt$' -x grep -l 'AppContaitner' {}

Length of output: 855


클래스 이름 및 변수명의 오타를 수정하세요.

AppContaitner 클래스명 오타가 확인되었으며, 다음 위치에서 수정이 필요합니다:

  • app/src/main/java/com/example/kuit6_android_api/data/di/AppContaitner.kt:8 - 클래스명 AppContaitnerAppContainer
  • app/src/main/java/com/example/kuit6_android_api/App.kt:4 - import 문 수정
  • app/src/main/java/com/example/kuit6_android_api/App.kt:9 - 변수 타입 및 변수명 contaitnercontainer 수정
  • app/src/main/java/com/example/kuit6_android_api/App.kt:13 - 클래스 인스턴스화 수정

모든 참조 위치에서 함께 수정해야 합니다.

🤖 Prompt for AI Agents
In app/src/main/java/com/example/kuit6_android_api/data/di/AppContaitner.kt
around line 8 the class name is misspelled as AppContaitner; rename the class to
AppContainer, update the filename to AppContainer.kt, and update all references
accordingly; then update app/src/main/java/com/example/kuit6_android_api/App.kt
(imports at line 4, variable declaration at line 9 changing type and name from
contienter/contaitner to container of type AppContainer, and the instantiation
at line 13) so the import points to the new AppContainer, the variable is named
container, and the new AppContainer() is used; ensure you refactor any other
references across the project to match the corrected class name and variable
name.

Comment on lines +8 to +13
suspend fun createPost(
author: String,
title: String,
content: String,
imageUrl: String? = null
): Result<PostResponse>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

createPost의 author 파라미터를 검증하세요.

클라이언트가 author를 직접 지정할 수 있도록 허용하는 것은 보안 문제가 될 수 있습니다. 일반적으로 작성자 정보는 인증 토큰에서 추출해야 합니다.

이것이 학습/데모 목적의 의도된 설계인지 확인하세요. 실제 프로덕션 환경에서는 서버에서 인증된 사용자를 author로 설정해야 합니다.

백엔드 API 명세를 확인하여 author 파라미터가 필요한지 검증하세요:


🌐 Web query:

Does the KUIT6 Android API require the author parameter in the createPost endpoint, or is it derived from authentication?

💡 Result:

I can't find public documentation for a "KUIT6" Android API. Could you share a link to the createPost endpoint docs (or paste the request/JSON schema) so I can check whether the author must be supplied or is derived from authentication?



🏁 Script executed:

#!/bin/bash
# Search for PostCreateRequest structure
echo "=== Searching for PostCreateRequest ==="
rg -n "PostCreateRequest" --type kotlin -C5

echo -e "\n=== Searching for createPost implementation ==="
rg -n "fun createPost" --type kotlin -C10

echo -e "\n=== Searching for authentication/User context ==="
rg -n "authentication|auth|User|Token" --type kotlin -C3 | head -100

echo -e "\n=== Looking for API request/response specs ==="
find . -type f \( -name "*.kt" -o -name "*.md" -o -name "*.txt" \) -exec grep -l "author" {} \; 2>/dev/null | head -20

echo -e "\n=== Checking PostResponse structure ==="
rg -n "data class PostResponse\|class PostResponse" --type kotlin -C10

Length of output: 40320


🏁 Script executed:

#!/bin/bash
# Search for KUIT6 or any API documentation files
find . -type f \( -name "*spec*" -o -name "*doc*" -o -name "README*" -o -name "*API*" \) 2>/dev/null | head -20

Length of output: 11



🏁 Script executed:

#!/bin/bash
# Read README to check project context and API documentation
cat ./README.md

Length of output: 3463


PostRepositoryImpl의 createPost에서 author 파라미터가 무시되고 있습니다.

코드 검증 결과, PostRepositoryImpl.kt 32번 줄에서 author 파라미터를 무시하고 "규반"을 하드코딩하고 있습니다:

val response = apiService.createPost("규반", PostCreateRequest(title,content,imageUrl))

인터페이스는 author 파라미터를 요구하지만 구현체에서는 이를 완전히 무시합니다. UI에서 사용자가 입력한 작성자 정보는 ViewModel을 거쳐 Repository에 전달되지만 폐기되고, 항상 고정된 값 "규반"이 전송됩니다.

PostRepositoryImpl의 createPost 메서드에서 전달받은 author 파라미터를 실제로 사용하도록 수정하세요:

val response = apiService.createPost(author, PostCreateRequest(title,content,imageUrl))
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/data/model/repository/PostRepository.kt
around lines 8 to 13, the createPost implementation ignores the passed-in author
and hardcodes "규반"; update the implementation to pass the method's author
parameter into the API call instead of the hardcoded value (i.e., replace the
hardcoded author with the author parameter when calling apiService.createPost)
so the repository forwards the provided author to the backend.

Comment on lines +25 to +42
override suspend fun createPost(
author: String,
title: String,
content: String,
imageUrl: String?
): Result<PostResponse> {
return runCatching {
val response = apiService.createPost("규반", PostCreateRequest(title,content,imageUrl))

if(response.success && response.data != null){
response.data
}else{
throw Exception(response.message ?:"게시글 생성 실패")
}
}.onFailure { error->
Log.e("PostRepository",error.message.toString())
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

createPost에서 author 파라미터를 사용하지 않는 버그가 있습니다.

  • createPost 시그니처는 author: String을 받고 있지만, 실제 호출은 apiService.createPost("규반", ...)로 고정값을 전달하고 있습니다. 호출하는 쪽에서 어떤 author를 넣어도 무시되므로 기능적으로 문제가 됩니다.
  • 동시에 ApiService.createPost의 기본값 "규빈"과도 불일치하여, 어디가 단일 진실인지 파악이 어렵습니다.

의도에 따라 다음 중 하나로 정리하는 것을 권장합니다.

// 1) Repository에서 파라미터를 그대로 전달 (권장)
val response = apiService.createPost(author, PostCreateRequest(title, content, imageUrl))

// 또는 2) author를 Repository 레벨에서만 기본값 처리하고 ApiService에서는 필수 인자로 사용
suspend fun createPost(
    author: String,
    title: String,
    content: String,
    imageUrl: String?
): Result<PostResponse> {
    val finalAuthor = author.ifBlank { "anonymous" } // 예시
    val response = apiService.createPost(finalAuthor, PostCreateRequest(title, content, imageUrl))
    ...
}

추가로, onFailure에서

Log.e("PostRepository", error.message.toString())

보다는

Log.e("PostRepository", "요청 실패", error)

처럼 throwable을 함께 넘겨 스택트레이스를 남기는 것이 디버깅에 더 유용합니다.

🤖 Prompt for AI Agents
app/src/main/java/com/example/kuit6_android_api/data/model/repository/PostRepositoryImpl.kt
lines 25-42: the createPost implementation ignores the author parameter and
hardcodes "규반" (and conflicts with ApiService's "규빈" default), causing incorrect
behavior; change the call to pass the provided author (or derive a
repository-level default like author.ifBlank { "anonymous" } and pass that) into
apiService.createPost, and update the onFailure logging to
Log.e("PostRepository", "요청 실패", error) so the throwable and stacktrace are
preserved.

Comment on lines +5 to +9
data class PostCreateRequest(
@SerialName( value = "title" ) val title: String,
@SerialName( value = "content" ) val content: String,
@SerialName(value = "imageUrl" ) val imageUrl: String?
) No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

@serializable 어노테이션이 누락되었습니다.

kotlinx.serialization을 사용하려면 @Serializable 어노테이션이 필요합니다.

다음 diff를 적용하세요:

 import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
 
+@Serializable
 data class PostCreateRequest(
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/data/model/request/PostCreateRequest.kt
around lines 5 to 9, the data class is missing the @Serializable annotation
required for kotlinx.serialization; add @Serializable directly above the data
class declaration and ensure you import kotlinx.serialization.Serializable (keep
the existing @SerialName imports intact) so the class is serializable.

Comment on lines +19 to +30
fun loadPost(id: Long) {
viewModelScope.launch {
_uiState.value = PostDetailUiState.Loading
repository.getPostDetail(id)
.onSuccess { posts ->
_uiState.value = PostDetailUiState.Success(posts)
}
.onFailure { error ->
_uiState.value = PostDetailUiState.Error(error.message ?: "조회 실패")
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

변수명을 수정하세요.

Line 23의 변수명 posts는 단수 게시글을 나타내므로 post로 변경해야 합니다.

     fun loadPost(id: Long) {
         viewModelScope.launch {
             _uiState.value = PostDetailUiState.Loading
             repository.getPostDetail(id)
-                .onSuccess { posts ->
-                    _uiState.value = PostDetailUiState.Success(posts)
+                .onSuccess { post ->
+                    _uiState.value = PostDetailUiState.Success(post)
                 }
                 .onFailure { error ->
                     _uiState.value = PostDetailUiState.Error(error.message ?: "조회 실패")
                 }
         }
     }
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt
around lines 19 to 30, the onSuccess lambda uses the variable name `posts` but
the result is a single post; rename the parameter to `post` and update its usage
(e.g., PostDetailUiState.Success(post)) so the variable name correctly reflects
a single post.

Comment on lines +21 to +35
fun loadEditPost(
id: Long,
request: PostCreateRequest
){
viewModelScope.launch {
_uiState.value = PostEditUiState.Loading
postRepository.editPost(id,request)
.onSuccess { posts ->
_uiState.value = PostEditUiState.Success(posts)
}
.onFailure { error ->
_uiState.value = PostEditUiState.Error(error.message ?: "조회 실패")
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

함수명과 변수명을 수정하세요.

  1. 함수명 loadEditPost는 오해의 소지가 있습니다. 실제로는 게시글을 수정하는 작업을 수행하므로 editPost가 더 적절합니다.
  2. Line 28의 변수명 posts는 단수 게시글을 나타내므로 post로 변경해야 합니다.
-    fun loadEditPost(
+    fun editPost(
         id: Long,
         request: PostCreateRequest
     ){
         viewModelScope.launch {
             _uiState.value = PostEditUiState.Loading
             postRepository.editPost(id,request)
-                .onSuccess { posts ->
-                    _uiState.value = PostEditUiState.Success(posts)
+                .onSuccess { post ->
+                    _uiState.value = PostEditUiState.Success(post)
                 }
                 .onFailure { error ->
                     _uiState.value = PostEditUiState.Error(error.message ?: "조회 실패")
                 }
         }
     }
📝 Committable suggestion

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

Suggested change
fun loadEditPost(
id: Long,
request: PostCreateRequest
){
viewModelScope.launch {
_uiState.value = PostEditUiState.Loading
postRepository.editPost(id,request)
.onSuccess { posts ->
_uiState.value = PostEditUiState.Success(posts)
}
.onFailure { error ->
_uiState.value = PostEditUiState.Error(error.message ?: "조회 실패")
}
}
}
fun editPost(
id: Long,
request: PostCreateRequest
){
viewModelScope.launch {
_uiState.value = PostEditUiState.Loading
postRepository.editPost(id,request)
.onSuccess { post ->
_uiState.value = PostEditUiState.Success(post)
}
.onFailure { error ->
_uiState.value = PostEditUiState.Error(error.message ?: "조회 실패")
}
}
}
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt
around lines 21 to 35, rename the function from loadEditPost to editPost and
change the success callback variable from posts to post; update all
references/usages and tests if any, and ensure the function signature remains
(id: Long, request: PostCreateRequest) and behavior is unchanged, only renaming
the function and the local lambda parameter to reflect that a single post is
returned.

Comment on lines 45 to 57
fun getPostDetail(postId: Long) {
viewModelScope.launch {
delay(300)
postDetail = dummyPosts.find { it.id == postId }
runCatching {
apiService.getPostDetail(postId)
}.onSuccess { response ->
response.data?.let{
if(response.success){
postDetail = response.data
}
}
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

모든 네트워크 메서드에서 실패 케이스가 완전히 무시되고 있어 디버깅/UX가 어렵습니다.

getPostDetail, createPost, updatePost, deletePost 모두:

runCatching { ... }
    .onSuccess { response -> /* success 처리 */ }
    // onFailure 없음

형태라 예외가 발생하거나 response.success == false인 경우에도:

  • 별도 로그/에러 상태 업데이트가 없고,
  • UI에서 “무응답”처럼 보일 수 있습니다.

권장 사항:

  1. 최소한 onFailure에서 로그를 남기거나 에러 콜백/상태를 업데이트
  2. response.successfalse인 경우도 명시적으로 처리 (else 분기에서 에러 메시지 노출 등)
  3. 필요하다면 에러용 mutableStateOf<Throwable?> 또는 UiState(Loading/Success/Error)로 승격

예시(패턴만):

viewModelScope.launch {
    runCatching {
        apiService.getPostDetail(postId)
    }.onSuccess { response ->
        if (response.success && response.data != null) {
            postDetail = response.data
        } else {
            // TODO: 에러 상태/메시지 업데이트
        }
    }.onFailure { e ->
        // TODO: 로그 또는 에러 상태 업데이트
        // Log.e("PostViewModel", "getPostDetail 실패", e)
    }
}

동일한 패턴을 createPost, updatePost, deletePost에도 적용하면, 네트워크 오류 시 사용자에게도 적절한 피드백을 줄 수 있습니다.

Also applies to: 59-80, 82-115

🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt
around lines 45-57 (and similarly for 59-80, 82-115), the network calls use
runCatching().onSuccess(...) but omit onFailure handling and do not explicitly
handle response.success == false; update the ViewModel to add an onFailure { ...
} that logs the exception and sets an error state, and in onSuccess add an else
branch for response.success == false to set an error message/state; expose a
simple error state (e.g., mutableStateOf<String?> or a UiState sealed class with
Loading/Success/Error) and update it on failure or unsuccessful responses so the
UI can show errors and developers can see logs; apply this pattern to
getPostDetail, createPost, updatePost, and deletePost.

Comment on lines +11 to +22
inline fun <reified VM : ViewModel> PostViewModelFactory(
crossinline create: (PostRepository) -> VM
) : ViewModelProvider.Factory = viewModelFactory {
initializer {
val application = this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]
as App

val postRepository = application.contaitner.postRepository

PostListViewModel(postRepository)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

치명적 버그: create 람다가 무시되고 있습니다.

현재 구현은 전달받은 create 람다를 무시하고 항상 PostListViewModel만 생성합니다(Line 20). 이는 PostDetailViewModel, PostEditViewModel 등 다른 ViewModel을 생성할 수 없게 만드는 심각한 버그입니다.

 inline fun <reified VM : ViewModel> PostViewModelFactory(
     crossinline create: (PostRepository) -> VM
 ) : ViewModelProvider.Factory = viewModelFactory {
         initializer {
             val application = this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]
                     as App
 
             val postRepository = application.contaitner.postRepository
 
-            PostListViewModel(postRepository)
+            create(postRepository)
         }
 
 }
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt
around lines 11 to 22, the factory currently ignores the incoming create lambda
and always constructs PostListViewModel; update the initializer to retrieve the
App (fixing the 'contaitner' typo to 'container'), get
application.container.postRepository, and invoke the provided
create(postRepository) lambda to produce and return the requested VM instead of
hardcoding PostListViewModel.

Comment thread gradle/libs.versions.toml
Comment on lines +32 to +33
adapterGuava = "2.10.0"
startupRuntime = "1.2.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

사용되지 않는 의존성일 수 있습니다.

adapter-guavaandroidx-startup-runtime 의존성이 선언되었지만, 실제로 코드베이스에서 사용되는지 확인이 필요합니다. Kotlin Coroutines를 사용하는 경우 adapter-guava는 일반적으로 불필요하며, 간단한 Application.onCreate() 초기화만으로 충분하다면 startup-runtime도 필요하지 않을 수 있습니다.

다음 스크립트로 사용 여부를 확인해주세요:

Also applies to: 72-73


🏁 Script executed:

#!/bin/bash
# Description: 선언된 의존성들의 실제 사용 여부 확인

echo "=== Checking for adapter-guava usage ==="
rg -nP '\b(ListenableFuture|GuavaCallAdapterFactory)\b' --type=kt

echo -e "\n=== Checking for startup-runtime usage ==="
rg -nP '\b(Initializer|AppInitializer)\b' --type=kt

echo -e "\n=== Checking imports ==="
rg -nP 'import.*\.(guava|startup)' --type=kt

Length of output: 191


🏁 Script executed:

#!/bin/bash
# Description: 선언된 의존성들의 실제 사용 여부 확인 (수정된 버전)

echo "=== Checking for adapter-guava usage (Retrofit/Guava imports and classes) ==="
rg -nP '(ListenableFuture|GuavaCallAdapterFactory|retrofit.*guava)' -g '*.kt' -g '*.java' -g '*.gradle.kts'

echo -e "\n=== Checking for startup-runtime usage (Initializer imports) ==="
rg -nP '(androidx\.startup\.|Initializer|InitializationProvider)' -g '*.kt' -g '*.java' -g '*.gradle.kts'

echo -e "\n=== Checking gradle files for dependency references ==="
rg -n '(adapterGuava|startupRuntime|adapter-guava|androidx-startup)' -g '*.gradle.kts' -g '*.gradle'

echo -e "\n=== Checking imports from androidx.startup or guava ==="
rg -nP 'import.*androidx\.startup|import.*guava|import.*retrofit2\.adapter' -g '*.kt' -g '*.java'

Length of output: 581


🏁 Script executed:

#!/bin/bash
# Description: 심층 분석 - Initializer 실제 사용 여부 및 adapter-guava 재확인

echo "=== Checking Initializer actual usage in PostListViewModel ==="
cat app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt | grep -n "Initializer" || echo "No Initializer references found in usage"

echo -e "\n=== Checking for any indirect adapter-guava usage (gradle dependencies) ==="
rg -n 'retrofit.*adapter|adapter.*guava' -g '*.gradle.kts' -g '*.gradle' -i

echo -e "\n=== Checking if adapter-guava is referenced in AndroidManifest or other configs ==="
rg -n 'adapter.*guava|guava' -g '*.xml' -g '*.pro' -i

echo -e "\n=== Searching for any Retrofit configuration ==="
rg -n 'Retrofit|addCallAdapterFactory' -g '*.kt' -g '*.java'

Length of output: 1247


🏁 Script executed:

#!/bin/bash
# Description: RetrofitClient 구성 확인 - 실제 사용되는 CallAdapterFactory

echo "=== Checking RetrofitClient configuration ==="
cat app/src/main/java/com/example/kuit6_android_api/data/api/RetrofitClient.kt

echo -e "\n=== Checking PostListViewModel for any actual Initializer usage beyond import ==="
cat app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt | head -50

Length of output: 2633


불필요한 의존성을 제거하세요.

검증 결과 두 의존성 모두 실제로 사용되지 않고 있습니다:

  • adapter-guava: app/build.gradle.kts:81에 선언되었으나, RetrofitClient.kt에서 Retrofit 설정 시 GsonConverterFactory만 사용하고 있으며 addCallAdapterFactory()가 호출되지 않습니다.

  • startup-runtime: app/build.gradle.kts:82에 선언되고 PostListViewModel.kt:8에 import되었으나, 실제 코드에서 Initializer 인터페이스를 구현하지 않으며 사용하지 않습니다. (단순 미사용 import)

이 두 의존성을 제거하여 프로젝트를 정리하세요.

🤖 Prompt for AI Agents
In gradle/libs.versions.toml around lines 32-33, remove the unused dependency
entries adapterGuava = "2.10.0" and startupRuntime = "1.2.0"; then update
app/build.gradle.kts to drop the corresponding library declarations (previously
at ~lines 81-82) and remove the unused import in PostListViewModel.kt (line 8)
so there are no leftover references to adapter-guava or startup-runtime in the
build files or source.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant