Conversation
|
Caution Review failedThe pull request is closed. Walkthrough피드 삭제·좋아요·저장 API 연동을 위해 요청/응답 모델, 서비스·레포지토리 엔드포인트 및 유스케이스·뷰모델을 추가하고 UI/네비게이션을 Long ID로 일관되게 변경했다. 낙관적 UI 업데이트와 삭제 후 뒤로가기 상태 전파가 포함된다. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI (FeedScreen/Others)
participant VM as FeedViewModel
participant UC as ChangeFeedLikeUseCase
participant Repo as FeedRepository
participant Svc as FeedService
participant API as Backend API
UI->>VM: onLikeClick(feedId)
VM->>VM: 낙관적 토글(isLiked/likeCount)
VM->>UC: invoke(feedId, newLikeStatus)
UC->>Repo: changeFeedLike(feedId, newLikeStatus)
Repo->>Svc: POST /feeds/{feedId}/likes {type: Boolean}
Svc->>API: Request
API-->>Svc: BaseResponse<FeedLikeResponse>
Svc-->>Repo: FeedLikeResponse
Repo-->>UC: Result
UC-->>VM: Result
alt 실패
VM->>VM: UI 상태 롤백
end
sequenceDiagram
participant UI as UI (FeedScreen)
participant VM as FeedViewModel
participant UC as ChangeFeedSaveUseCase
participant Repo as FeedRepository
participant Svc as FeedService
participant API as Backend API
UI->>VM: onBookmarkClick(feedId)
VM->>VM: 낙관적 토글(isSaved)
VM->>UC: invoke(feedId, newSaveStatus)
UC->>Repo: changeFeedSave(feedId, newSaveStatus)
Repo->>Svc: POST /feeds/{feedId}/saved {type: Boolean}
Svc->>API: Request
API-->>Svc: BaseResponse<FeedSaveResponse>
Svc-->>Repo: FeedSaveResponse
Repo-->>UC: Result
UC-->>VM: Result
alt 실패
VM->>VM: UI 상태 롤백
end
sequenceDiagram
participant UI as UI (FeedCommentScreen)
participant VM as FeedDetailViewModel
participant UC as DeleteFeedUseCase
participant Repo as FeedRepository
participant Svc as FeedService
participant API as Backend API
UI->>VM: confirmDelete(feedId)
VM->>VM: isDeleting=true, error=null
VM->>UC: invoke(feedId)
UC->>Repo: deleteFeed(feedId)
Repo->>Svc: DELETE /feeds/{feedId}
Svc->>API: Request
API-->>Svc: BaseResponse<String>
Svc-->>Repo: Result
Repo-->>UC: Result
UC-->>VM: Result
alt 성공
VM->>VM: isDeleting=false, deleteSuccess=true
else 실패
VM->>VM: isDeleting=false, error=message
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (5)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 4
🔭 Outside diff range comments (4)
app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt (1)
14-14: Write.feedId 타입 통일 및 관련 호출부 수정 필요
FeedRoutes.Write만Int?로 남아 있어, 다른 라우트(Comment,Others) 및 API/내비게이션 호출부에서는 모두Long으로 사용 중입니다. 불필요한toLong()/toInt()변환을 제거하고, 라우트 직·역직렬화에서도Long?타입을 일관되게 써야 합니다.수정이 필요한 위치:
- app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt: Write.feedId 타입 변경
- app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt:
route.feedId.toLong()호출 제거 또는route.feedId직접 사용 (69, 80번 줄)- app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt:
navigateToFeedWrite에서feedId?.toInt()변환 제거 및Long?처리 확인- (테스트) 라우트 직·역직렬화 포맷과 SavedStateHandle 키(
"feedId")도Long기반으로 올바르게 동작하는지 검증 필요제안 diff:
--- a/app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt @@ -12,7 +12,7 @@ sealed class FeedRoutes : Routes() { @Serializable data class Write( - val feedId: Int? = null, // 수정 모드용 피드 ID + val feedId: Long? = null, // 수정 모드용 피드 ID val isbn: String? = null, val bookTitle: String? = null, //...app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt (1)
155-158: 컴파일 오류 가능성: nullable uiState를 안전 호출 없이 참조하고 있습니다조건식에서
uiState?.isLoadingMore == false다음에!uiState.isLast를 사용하고 있어 NPE/컴파일 오류가 납니다. 아래처럼 null-safe하게 변경해 주세요.- if (shouldLoadMore && uiState?.isLoadingMore == false && !uiState.isLast) { + if (shouldLoadMore && uiState?.isLoadingMore == false && (uiState?.isLast == false)) { onLoadMore() }대안:
- 가독성을 위해
val isLast = uiState?.isLast ?: true로컬 변수를 두고!isLast를 사용하는 것도 좋습니다.app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
66-71: onNavigateToFeedEdit 파라미터가 Int로 남아 Long 전환과 불일치 (컴파일 실패 가능)화면 시그니처가 feedId: Long으로 변경되었는데, onNavigateToFeedEdit는 (Int) -> Unit을 그대로 유지하고 있습니다. 아래 호출부에서 feedDetail.feedId(아마 Long)를 그대로 넘기므로 현재 상태로는 타입 불일치로 컴파일 에러가 날 확률이 높습니다. 전체 PR 목표(피드 ID Long 전환)와도 불일치합니다. Long으로 맞춰주세요.
아래 패치로 정합성 맞출 수 있습니다:
fun FeedCommentScreen( modifier: Modifier = Modifier, - feedId: Long, + feedId: Long, onNavigateBack: () -> Unit = {}, - onNavigateToFeedEdit: (Int) -> Unit = {}, + onNavigateToFeedEdit: (Long) -> Unit = {}, feedDetailViewModel: FeedDetailViewModel = hiltViewModel(), commentsViewModel: CommentsViewModel = hiltViewModel() )app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (1)
69-81: FeedRoutes.Write의 feedId 타입을 Long?으로 통일하고 toLong() 호출 제거현재
FeedRoutes.Comment에서는feedId: Long으로 정의되어 있지만,FeedRoutes.Write의feedId는Int?로 되어 있어FeedNavigation.kt에서.toLong()변환이 중복 사용됩니다. 일관성을 위해 다음을 반영해주세요.수정할 위치:
- app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt
- data class Write의 선언을
에서data class Write( val feedId: Int? = null, // 수정 모드용 피드 ID … )로 변경data class Write( val feedId: Long? = null, // 수정 모드용 피드 ID … )- app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt
route.feedId.toLong()호출을route.feedId로 대체 (null검사는 기존 분기에서 이미 처리됨)
🧹 Nitpick comments (23)
app/src/main/java/com/texthip/thip/data/model/feed/response/FeedLikeResponse.kt (1)
1-10: 응답 모델 정의 적절함. 낙관적 업데이트 동기화 포인트만 확인해 주세요
- 계약 상 필드명이 "feedId", "isLiked"라면 현재 정의로 충분합니다.
- 낙관적 좋아요 토글을 쓰는 경우, 서버가 likeCount(혹은 기타 동기화용 메타)도 내려준다면 모델에 포함해 최종 상태를 재동기화하는 걸 고려해 주세요. 미반환이면 현재 구조로도 무방합니다.
app/src/main/java/com/texthip/thip/data/model/feed/request/FeedSaveRequest.kt (1)
1-9: 요청 필드 의미를 명시하는 KDoc 추가 제안Boolean 타입의 "type"은 의미가 모호할 수 있습니다. 계약 의미를 KDoc으로 남겨 오해를 줄이는 것을 권장합니다.
다음 주석을 추가해 주세요:
package com.texthip.thip.data.model.feed.request import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +/** + * 서버 계약: + * - type = true -> 저장 ON + * - type = false -> 저장 OFF + */ @Serializable data class FeedSaveRequest( @SerialName("type") val type: Boolean )app/src/main/java/com/texthip/thip/data/model/feed/request/FeedLikeRequest.kt (1)
1-9: 요청 필드 의미를 명시하는 KDoc 추가 제안FeedSaveRequest와 동일하게 "type" 의미를 문서화하면 가독성이 좋아집니다.
적용 예시는 다음과 같습니다:
package com.texthip.thip.data.model.feed.request import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +/** + * 서버 계약: + * - type = true -> 좋아요 ON + * - type = false -> 좋아요 OFF + */ @Serializable data class FeedLikeRequest( @SerialName("type") val type: Boolean )app/src/main/java/com/texthip/thip/data/service/FeedService.kt (2)
85-90: DELETE 응답 타입을 의미적으로 단순화하는 것을 고려해주세요서버가 본문 없이 성공 상태만 반환(예: 204 No Content)이면,
BaseResponse<String>대신BaseResponse<Unit>사용이 더 명확합니다. 서버 계약이 문자열 메시지를 항상 내려준다면 현행 유지하세요.다음과 같이 변경 가능:
- ): BaseResponse<String> + ): BaseResponse<Unit>서버 스펙 상 응답 바디가 없는지(204/200+빈바디) 또는 메시지 문자열을 반환하는지 확인 부탁드립니다.
91-97: 좋아요 상태 변경: 멱등성(HTTP 메서드) 검토 제안요청 바디에 최종 상태(Boolean)를 전달하는 형태라면
POST보다PUT/PATCH가 의미상 더 적합합니다. 백엔드 계약이 고정이라면 유지하시되, API 설계 합의 여지가 있다면 다음처럼 고려해볼 수 있습니다.- @POST("feeds/{feedId}/likes") + @PATCH("feeds/{feedId}/likes") suspend fun changeFeedLike( @Path("feedId") feedId: Long, @Body request: FeedLikeRequest ): BaseResponse<FeedLikeResponse>app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt (1)
93-101: 접근성(a11y): 이미지 contentDescription 고려피드 이미지가 정보 전달에 의미가 있다면
contentDescription제공을 검토해 주세요. 순수 장식 용도라면null유지가 맞습니다.app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt (1)
75-83: 접근성(a11y) 개선 제안피드 이미지가 의미 있는 콘텐츠라면
contentDescription제공을 고려해 주세요. 장식 이미지라면 현재처럼null유지가 적절합니다.app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt (1)
78-79: 에러 핸들링 누락 — 최소 로깅 또는 사용자 피드백 추가 권장onError 블록이 비어 있어 실패 시 원인 파악/사용자 피드백이 어렵습니다. 최소한 로깅을 넣고, 가능하면 Snackbar/Toast 등 사용자 피드백을 추가해 주세요.
아래처럼 간단 로깅을 추가해 두면 초기에 도움이 됩니다:
- onError = { errorMessage -> - } + onError = { errorMessage -> + android.util.Log.e("FeedWriteScreen", "Failed to create/update feed: $errorMessage") + // TODO: Snackbar/Toast 등 사용자 피드백 추가 고려 + }app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt (1)
6-11: UseCase 구성 적절 — 단일 책임으로 리포지토리 호출 위임 OK의존성 주입과 operator invoke 패턴으로 사용성이 좋습니다. 가독성 향상을 위해 간단한 KDoc을 권장합니다.
class ChangeFeedSaveUseCase @Inject constructor( private val feedRepository: FeedRepository ) { - suspend operator fun invoke(feedId: Long, newSaveStatus: Boolean) = + /** + * 피드 저장 상태를 변경합니다. + * @param feedId 대상 피드 ID + * @param newSaveStatus true면 저장, false면 해제 + */ + suspend operator fun invoke(feedId: Long, newSaveStatus: Boolean) = feedRepository.changeFeedSave(feedId, newSaveStatus) }app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (2)
75-78: 불필요한 toLong() 호출 제거 제안feedId 자체가 Long이므로 toLong()은 중복 변환입니다. 가독성을 위해 제거하는 것이 좋습니다.
LaunchedEffect(feedId) { feedDetailViewModel.loadFeedDetail(feedId) - commentsViewModel.initialize(postId = feedId.toLong(), postType = "FEED") + commentsViewModel.initialize(postId = feedId, postType = "FEED") }
387-400: 삭제 확인 후 후속 처리(내비게이션/피드백) 흐름 확인 필요onConfirm에서 바로 deleteFeed(feedId)를 호출하지만, 삭제 성공 시 화면을 닫거나 상위로 결과를 전달하는 흐름(토스트, popBackStack, savedStateHandle 등)이 이 화면에서 관찰되고 있지 않습니다. ViewModel에서 성공 이벤트를 내보낸다면, 여기서 구독해 후속 동작을 수행해 주세요.
원한다면 다음과 같이 성공 이벤트(예: feedDetailUiState.deleteSuccess)를 관찰해 뒤로 가기/결과 전달을 수행할 수 있습니다:
LaunchedEffect(feedDetailUiState.deleteSuccess) { if (feedDetailUiState.deleteSuccess == true) { // 필요 시 결과 전달 로직 추가 (savedStateHandle 등) onNavigateBack() } }app/src/main/java/com/texthip/thip/ui/feed/usecase/DeleteFeedUseCase.kt (1)
10-12: 반환 타입 설계 검토 제안(Result<String?> 대신 도메인 친화 타입)서버 메시지(String?)를 그대로 Result로 노출하면 상위 계층 결합도가 올라갑니다. 결과 자체가 중요하지 않다면 Result 또는 도메인 이벤트(예: Deleted)로 감추는 것을 고려해 보세요.
app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedLikeUseCase.kt (1)
9-11: 반환 타입 명시로 인터페이스 안정성 강화 제안public 유스케이스는 반환 타입을 명시하면 변경 파급을 줄일 수 있습니다. Repository 시그니처가 Result<FeedLikeResponse?>라면 다음처럼 타입을 명시하는 것을 권장합니다.
package com.texthip.thip.ui.feed.usecase import com.texthip.thip.data.repository.FeedRepository +import com.texthip.thip.data.model.feed.response.FeedLikeResponse import javax.inject.Inject class ChangeFeedLikeUseCase @Inject constructor( private val feedRepository: FeedRepository ) { - suspend operator fun invoke(feedId: Long, newLikeStatus: Boolean) = - feedRepository.changeFeedLike(feedId, newLikeStatus) + suspend operator fun invoke(feedId: Long, newLikeStatus: Boolean): Result<FeedLikeResponse?> { + return feedRepository.changeFeedLike(feedId, newLikeStatus) + } }app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (1)
127-129: FeedCommentScreen 쪽 onNavigateToFeedEdit 시그니처(Long)와의 정합성 점검 필요FeedCommentScreen의 onNavigateToFeedEdit를 Long으로 변경할 예정이므로(앞선 코멘트), 이 람다의 파라미터도 Long을 기대하게 됩니다. 또한 FeedRoutes.Write의 feedId 역시 Long으로 정렬되어야 합니다. 라우트가 아직 Int라면 임시로 toLong() 변환을 추가하세요.
Long으로 정렬된 경우 그대로 두시면 됩니다. 만약 Int라면 임시 패치는 아래와 같습니다:
- onNavigateToFeedEdit = { feedId -> - navController.navigate(FeedRoutes.Write(feedId = feedId)) - } + onNavigateToFeedEdit = { feedId -> + navController.navigate(FeedRoutes.Write(feedId = feedId /* .toInt()가 필요하다면 라우트 타입을 Long으로 수정하는 것이 우선입니다 */)) + }권장사항: FeedRoutes.Write(feedId: Long)로 통일하고 중간 변환을 제거하세요.
app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt (2)
93-97: 북마크 토글은 현재 로컬 상태만 변경 — 향후 실제 저장 API 연동 시 일관된 패턴 권장현재는 로컬 StateFlow만 갱신합니다. 이후 Saved 피드 화면도 실제 저장 상태 변경 API(ChangeFeedSaveUseCase)로 연동한다면, 본 뷰모델에서도 동일한 “낙관적 업데이트 → 실패 시 롤백” 패턴을 적용하는 편이 일관적입니다.
다음과 같이 유스케이스를 주입하고, 실패 시 이전 상태로 복구하는 형태를 고려해 보세요:
-class SavedFeedViewModel: ViewModel() { +class SavedFeedViewModel( + // 예: @Inject 주입 + private val changeFeedSaveUseCase: ChangeFeedSaveUseCase? = null +): ViewModel() { @@ - fun toggleBookmark(id: Long) { - _feeds.value = _feeds.value.map { - if (it.id == id) it.copy(isSaved = !it.isSaved) else it - } - } + fun toggleBookmark(id: Long) { + val prev = _feeds.value + val next = prev.map { if (it.id == id) it.copy(isSaved = !it.isSaved) else it } + _feeds.value = next + // 실제 API가 연결된 경우에만 호출 + changeFeedSaveUseCase?.invoke(id, next.first { it.id == id }.isSaved) + ?.onFailure { _ -> + // 실패 시 롤백 + _feeds.value = prev + } + }
99-103: 좋아요 토글도 동일하게 낙관적 업데이트/롤백 패턴 고려Saved 영역에서 좋아요를 다루지 않는다면 현상 유지로 충분하나, 다룰 계획이라면 북마크와 동일 패턴으로 처리 권장합니다.
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedOthersViewModel.kt (1)
69-94: 낙관적 업데이트는 적절합니다. 실패 시 사용자에게 피드백 노출을 추가하세요현재 실패 시 UI 롤백만 수행합니다. FeedOthersUiState에 errorMessage가 이미 있으므로, 실패 원인을 errorMessage에 담아 스낵바 등으로 사용자에게 피드백 제공을 권장합니다.
또한 동일 피드에 대해 연속 탭으로 다중 요청이 중첩되는 경우를 완화(예: in-flight set으로 중복 요청 차단)하면 더 안정적입니다.다음과 같이 실패 시 에러 메시지 업데이트를 추가해 보세요:
- changeFeedLikeUseCase(feedId, newLikeStatus) - .onFailure { - _uiState.update { it.copy(feeds = currentFeeds) } - } + changeFeedLikeUseCase(feedId, newLikeStatus) + .onFailure { e -> + _uiState.update { + it.copy( + feeds = currentFeeds, + errorMessage = e.message ?: "좋아요 상태 변경에 실패했어요." + ) + } + }app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (1)
3-3: 사용하지 않는 import 정리현재 파일에서 Log는 사용되지 않습니다. 제거를 권장합니다.
다음 변경으로 정리할 수 있습니다:
-import android.util.Logapp/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt (1)
61-78: 삭제 성공 이벤트 소모(consume) 메서드 추가 고려deleteSuccess는 상태로 남아 재구성 시 재처리될 수 있습니다. 화면에서 한 번 처리 후 false로 되돌리는 consume 메서드를 두면 안정적입니다.
아래와 같이 보조 메서드를 추가해 사용하세요:
fun consumeDeleteSuccess() { updateState { it.copy(deleteSuccess = false) } }app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (2)
312-337: 낙관적 업데이트는 잘 적용 — 실패 시 에러 전달 및 updateState 일관 사용 제안
- 실패 시 단순 롤백 외에 FeedUiState.error에 메시지를 채워 사용자 피드백을 제공하면 UX가 개선됩니다.
- 동일 파일 내 다른 곳처럼 updateState를 일관되게 사용하면 코드 가독성이 좋아집니다.
다음과 같이 수정해 보세요:
- _uiState.update { it.copy(allFeeds = newFeeds) } + updateState { it.copy(allFeeds = newFeeds) } @@ - changeFeedLikeUseCase(feedId, newLikeStatus) - .onFailure { - _uiState.update { it.copy(allFeeds = currentFeeds) } - } + changeFeedLikeUseCase(feedId, newLikeStatus) + .onFailure { e -> + updateState { + it.copy( + allFeeds = currentFeeds, + error = e.message ?: "좋아요 상태 변경에 실패했어요." + ) + } + }추가로, 동일 피드에 대한 연속 탭으로 요청이 중첩되는 문제를 방지하려면 in-flight 관리(예: Set로 진행 중인 feedId 추적)도 고려해 보세요.
339-361: 저장 상태 변경도 동일하게 에러 전달/일관 API 사용위와 동일한 맥락으로 updateState 사용 일관성 및 실패 시 에러 메시지 설정을 권장합니다.
- updateState { it.copy(allFeeds = newFeeds) } + updateState { it.copy(allFeeds = newFeeds) } @@ - changeFeedSaveUseCase(feedId, newSaveStatus) - .onFailure { - updateState { it.copy(allFeeds = currentFeeds) } - } + changeFeedSaveUseCase(feedId, newSaveStatus) + .onFailure { e -> + updateState { + it.copy( + allFeeds = currentFeeds, + error = e.message ?: "저장 상태 변경에 실패했어요." + ) + } + }app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (1)
440-442: toLong() 변환이 불필요할 수 있음
updatedFeedId가 이미 Long이면 변환이 불필요합니다. 타입 확인 후 제거를 권장합니다.제안 diff:
- onSuccess(updatedFeedId.toLong()) + onSuccess(updatedFeedId)확인은 위에서 제공한 스크립트(CreateFeedResponse)와 더불어
FeedRepository.updateFeed의 반환 모델 타입을 참고하세요.app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt (1)
226-231: deleteFeed 반환 타입 단순화 제안
현재FeedService.deleteFeed는BaseResponse<String>을 반환하고,FeedRepository.deleteFeed는 이를Result<String?>으로 래핑하고 있습니다. 만약 호출부나 도메인 로직에서 서버가 반환하는 문자열 메시지에 전혀 의존하지 않는다면, 반환 타입을Result<Unit>으로 변경하여 불필요한 메시지 처리를 제거할 수 있습니다.
FeedRepository.deleteFeed서명:→suspend fun deleteFeed(feedId: Long): Result<String?> = …suspend fun deleteFeed(feedId: Long): Result<Unit> = runCatching { feedService.deleteFeed(feedId) .handleBaseResponse() // 내부 메시지 무시 .let { } // 반환값 대신 Unit 사용 }- 호출부(예: ViewModel)의
deleteFeed처리 로직에서 반환된 문자열 대신 성공 여부만을 판단하도록 수정 필요- 서비스 시그니처가 항상 메시지를 포함하는지, 앞으로 메시지가 없어질 가능성은 없는지 팀과 한 번 더 확인 권장
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (27)
app/src/main/java/com/texthip/thip/data/model/feed/request/FeedLikeRequest.kt(1 hunks)app/src/main/java/com/texthip/thip/data/model/feed/request/FeedSaveRequest.kt(1 hunks)app/src/main/java/com/texthip/thip/data/model/feed/response/FeedLikeResponse.kt(1 hunks)app/src/main/java/com/texthip/thip/data/model/feed/response/FeedSaveResponse.kt(1 hunks)app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt(3 hunks)app/src/main/java/com/texthip/thip/data/service/FeedService.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedLikeUseCase.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/feed/usecase/DeleteFeedUseCase.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedOthersViewModel.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt(8 hunks)app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/mypage/mock/FeedItem.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/search/viewmodel/BookDetailViewModel.kt(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt (2)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (1)
updateState(64-66)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (1)
updateState(35-37)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (2)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (1)
updateState(35-37)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt (1)
updateState(32-34)
app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (1)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (1)
FeedScreen(65-389)
🔇 Additional comments (37)
app/src/main/java/com/texthip/thip/ui/mypage/mock/FeedItem.kt (1)
4-4: Long ID 전환 잘 적용되었습니다UI/네비게이션/리포지토리 전반의 Long 이관과 일관성이 맞습니다. 특별한 이슈는 보이지 않습니다.
app/src/main/java/com/texthip/thip/data/model/feed/response/FeedSaveResponse.kt (1)
6-10: 직렬화 응답 모델 정의가 명확하며 Long 마이그레이션과 일치합니다필드 네이밍(SerialName)과 타입이 일관되고, Boolean 기반의 저장 상태 표현도 적절합니다.
app/src/main/java/com/texthip/thip/data/service/FeedService.kt (3)
101-103: updateFeed의 반환 타입이 CreateFeedResponse인 점 확인 필요업데이트도 생성과 동일한 응답 스키마를 의도적으로 사용한다면 OK입니다. 아니라면
UpdateFeedResponse(또는 typealias)로 의미를 분리하는 것이 가독성에 좋습니다.API 문서 기준으로 업데이트 응답 스키마가 생성과 동일한지 확인 부탁드립니다.
105-111: 저장 상태 변경 엔드포인트 경로 확인 요청
POST("feeds/{feedId}/saved")경로가 서버 계약의 정확한 리소스명(예: saved vs saves vs bookmarks)과 일치하는지 재확인 부탁드립니다. 또한 좋아요와 동일하게 멱등성 측면에서PATCH고려 여지도 있습니다.
71-73: feedId 타입(Long) 마이그레이션 영향 범위 재확인 요청서비스 함수 시그니처를
Long으로 변경하셨습니다. 호출부, Nav-args, ViewModel, UseCase 등에서feedId파라미터가 모두Long으로 일치하는지 다시 한번 확인해 주세요. 아래 명령어로.kt파일 전체를 스캔하여Int잔여 여부를 점검하실 수 있습니다:
rg -n '\bfeedId\s*:\s*Int\b' -g '*.kt'rg -n 'NavType\.\s*IntType' -g '*.kt'rg -n ':\s*Int\s*->' -g '*.kt'rg -n '\.toInt\(\)' -g '*.kt'위 확인 후에도 누락된 부분이 없으면 이 건은 종료하셔도 좋습니다.
app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt (1)
123-123: 프리뷰 데이터의 ID Long 전환이 일관성 있게 반영되었습니다프로덕션 전반의 Long 마이그레이션과 맞물려 Preview도 안전하게 반영되었습니다.
Also applies to: 139-139
app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt (1)
131-131: 프리뷰 데이터의 ID Long 전환이 적절합니다전역적인 ID 타입 변경과 일치하며, Preview 빌드에도 문제 없습니다.
Also applies to: 147-147
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt (1)
27-27: editingFeedId의 Long? 변경이 PR 목표와 일치합니다작성/수정 플로우에서 ID 범위 확장에 대비한 타입 변경으로 합리적입니다.
app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt (1)
10-10: 피드 댓글 route 파라미터를 Long으로 전환한 점 일관성 좋습니다전반적인 ID Long 마이그레이션과 맞물려 내비게이션 타입 일치가 잘 반영되었습니다.
다른 경로에서 Comment(feedId)를 생성/역직렬화하는 부분(네비게이션 그래프, 인코더/디코더)이 모두 Long 기준으로 동기화되었는지 한 번만 확인 부탁드립니다.
app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt (1)
23-25: navigateToFeedComment(feedId: Long) 전환 LGTM경로 객체 FeedRoutes.Comment도 Long을 받도록 맞춰져 있어 시그니처 정합성이 확보되었습니다.
- 기존 Int 기반 호출부가 남아 있지 않은지 검색해 주세요.
- NavType/직렬화 스키마가 Long을 정상 지원하는지(딥링크 포함)도 점검 부탁드립니다.
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt (1)
65-65: onFeedCreated 콜백을 (Long) -> Unit으로 전환 — OK생성/수정 완료 후 전달되는 feedId 타입과 일치합니다. 상위 네비게이션/호출부도 Long 시그니처로 동기화되었는지 확인만 부탁드립니다.
- Feed 생성 후 이동(예: 상세/댓글 화면) 경로가 Long 기반으로 모두 마이그레이션되었는지 확인해 주세요.
app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt (2)
76-77: SearchBookDetailScreen의 onFeedClick(Long) 전환 — 일관성 확보상위/하위 컴포저블 및 feedItem.id(Long)과 타입이 맞아 떨어집니다.
123-124: 내부 Content 컴포저블의 onFeedClick(Long)도 동기화 — OK호출 경로 일관성 유지가 잘 되어 있습니다.
app/src/main/java/com/texthip/thip/ui/feed/usecase/DeleteFeedUseCase.kt (1)
6-13: 단일 책임의 얇은 유스케이스 위임, 적절합니다리포지토리로의 위임이 명확하고 테스트/주입 관점에서도 다루기 좋습니다. 현재 요구사항 범위에서는 충분합니다.
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt (1)
46-50: 좋아요 콜백 전파 및 ViewModel 위임 연동이 깔끔합니다
- Screen -> Content -> Card로 일관되게 (Long) feedId 콜백을 전파하고 ViewModel에 위임하는 구조가 명확합니다.
- Preview에서도 새 파라미터 반영 완료되어 컴파일 안전성에 도움 됩니다.
Also applies to: 55-58, 137-141, 189-191
app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (2)
25-31: SavedStateHandle에서 Long으로 결과 수신/소모 처리, 방향성 적절feedId를 Long으로 통일하는 맥락에 부합하며, get/remove 사용도 문제 없습니다.
118-121: Comment 라우트에서 getLong 사용은 일관성 측면에서 적절Comment 경로도 Long으로 수신하여 ID 타입 일관성을 유지합니다.
app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt (1)
13-25: ID를 Long으로 일괄 전환한 점 좋습니다목데이터의 FeedItem.id를 Long 리터럴(…L)로 전환해 본 PR의 전반적 ID 타입 변경과 일관성이 맞습니다.
Also applies to: 27-40, 42-55, 57-70, 72-84
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedOthersViewModel.kt (1)
10-10: UseCase 주입으로 책임 분리 OKChangeFeedLikeUseCase를 주입해 ViewModel-Repository 사이에 유스케이스 레이어를 둔 점 좋습니다. 관심사 명확하고 테스트 용이성이 올라갑니다.
Also applies to: 29-31
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (3)
70-73: 피드 댓글 내비게이션/결과 ID를 Long으로 정규화한 점 좋습니다콜백 및 resultFeedId의 Long 일관화가 본 PR의 타입 변경과 맞습니다.
341-345: 북마크/좋아요 이벤트를 ViewModel 유스케이스로 연결한 부분 좋습니다낙관적 업데이트 + 실패 롤백이 ViewModel에 구현되어 있어 UI는 단순 위임으로 깔끔합니다.
269-269: toLong() 호출 유지 필요 — AllFeedItem/MyFeedItem의 feedId는 Int
AllFeedItem(data class AllFeedItem)와 MyFeedItem(data class MyFeedItem)의 feedId 프로퍼티가 Int로 선언되어 있어, Long 타입 필드에 할당하기 위해.toLong()호출이 필요합니다.Likely an incorrect or invalid review comment.
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt (3)
18-21: 삭제 진행/완료 상태 플래그 추가 적절isDeleting, deleteSuccess를 UI 상태에 포함시켜 화면 단에서 로딩/완료/네비게이션 처리를 분리할 수 있어 좋습니다.
25-27: DeleteFeedUseCase 주입 OKRepository 호출을 UseCase를 통해 감싸 관심사 분리가 잘 되어 있습니다.
36-60: loadFeedDetail feedId 타입 검증 완료 — 모든 호출부에서 Long 전달 중
- app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (라인 66):
feedId: Long을 그대로loadFeedDetail(feedId)에 전달- 그 외
loadFeedDetail호출부 없음app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (2)
52-55: 좋아요/저장 UseCase 주입으로 액션 분리 OKUI 액션이 ViewModel → UseCase → Repository로 분리되어 테스트 용이성과 유지보수성이 좋아졌습니다.
312-337: toLong() 변환 유지 필요
- 검증 결과
AllFeedItem.feedId가Int타입이므로toLong()호출이 필요합니다.- 단순 비교(
it.feedId == feedId)로 변경 시 타입 불일치 오류가 발생합니다.위 제안은 무시해 주세요.
Likely an incorrect or invalid review comment.
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (5)
65-99: Long 마이그레이션 및 편집 상태 반영 로직 적정 — LGTM
feedId: Long으로의 전환이 리포지토리 API와 일관되며, 로딩/에러 상태 업데이트와 편집 UI 상태 세팅도 적절합니다.
120-156: 편집 데이터 세팅 시그니처 Long으로 통일 — LGTM
setEditData의feedId: Long적용이 상위 레이어와 일관되며, 카테고리 인덱스 계산 및 상태 업데이트 흐름도 타당합니다.
356-364: 생성/수정 분기와 콜백 시그니처(Long) 통일 — LGTM편집 모드 여부와
editingFeedId존재 여부로 분기하는 구조가 명확합니다. 콜백 타입(Long) 통일도 일관됩니다.
419-460: 업데이트 시그니처 Long으로 전환 및 에러 처리 — LGTM
updateFeed(feedId: Long, ...)전환과 결과 처리/에러 처리 흐름 모두 기존 패턴과 일관됩니다.
392-396: feedId는 Int 타입이므로.toLong()변환을 유지해야 합니다
CreateFeedResponse.feedId가Int로 정의되어 있어,Long이 필요한onSuccess호출 시 변환이 필수입니다.
- 확인 위치:
• app/src/main/java/com/texthip/thip/data/model/feed/response/CreateFeedResponse.kt:val feedId: Int(라인 9)Likely an incorrect or invalid review comment.
app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt (5)
7-19: 신규 요청/응답 모델 import 추가 — LGTM좋아요/저장 요청·응답과 전체 피드/연관 피드 응답 import가 적절히 추가되었습니다. 마이그레이션 방향과 일치합니다.
176-180: 피드 상세 조회 feedId를 Long으로 변경 — LGTM서비스 시그니처와의 정합성이 확보되었습니다.
184-200: 피드 수정 feedId Long 전환 — LGTM요청 모델 구성 및 서비스 호출 흐름이 기존 패턴과 동일해 신뢰할 수 있습니다.
233-239: 좋아요 요청 필드명(type) 검증 필요현재
FeedLikeRequest.kt에@SerialName("type") val type: Boolean로 설정되어 요청 페이로드에"type": true/false가 전송됩니다.
서버 API 스펙에서 해당 엔드포인트가type필드를 사용하는지(liked또는isLiked등 다른 이름이 아닌지) 반드시 확인해주세요. 불일치 시 400/422 에러가 발생할 수 있습니다.
- app/src/main/java/com/texthip/thip/data/model/feed/request/FeedLikeRequest.kt:8 (
@SerialName("type") val type: Boolean)- app/src/main/java/com/texthip/thip/data/service/FeedService.kt 내
changeFeedLike메서드 확인
241-247: 저장 요청 필드명(type) 일치 확인 완료
- FeedSaveRequest 데이터 클래스에
@SerialName("type") val type: Boolean으로 정의되어 있어 서버 스펙의type필드와 일치함을 확인했습니다.
추가 수정은 필요하지 않습니다.
| val isLoadingCategories: Boolean = false, | ||
| val isEditMode: Boolean = false, | ||
| val editingFeedId: Int? = null | ||
| val editingFeedId: Long? = null |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
연쇄 영향도 점검 스크립트 제공: Int 기반 잔존 호출부/네비게이션 파악
해당 필드 타입 변경에 따라 저장/복원, NavArgs, 콜백 시그니처까지 Long으로 일관 정리되었는지 확인을 권장합니다.
🏁 Script executed:
#!/bin/bash
# editingFeedId가 Int로 남아있는 선언/사용 탐색
rg -nP --type=kt -C2 '\beditingFeedId\s*:\s*Int\??\b'
# feedId 파라미터에 Int 사용 흔적 탐색
rg -nP --type=kt -C2 '\bfeedId\s*:\s*Int\??\b'
# 네비게이션 인자 타입 Long 반영 여부 (IntType 사용 잔존 여부)
rg -nP --type=kt -C2 'NavType\.\s*IntType'
rg -nP --type=kt -C2 'navArgument\(\s*"feedId"\s*\)\s*\{\s*type\s*=\s*NavType\.\s*IntType'
# 콜백 시그니처에서 Int -> Long 전환 확인
rg -nP --type=kt -C2 'onFeedCreated\s*:\s*\(\s*Int\s*\)\s*->'
rg -nP --type=kt -C2 '\(\s*Int\s*\)\s*->\s*Unit'
# Int 변환 사용 잔존 여부
rg -nP --type=kt -C2 '\.toInt\(\)'Length of output: 583
Int 타입 잔존 여부 점검을 위해 --glob '*.kt' 옵션을 사용해 .kt 파일을 대상으로 재검색합니다. 아래 스크립트를 실행해주세요.
🏁 Script executed:
#!/bin/bash
# editingFeedId가 Int로 남아있는 선언/사용 탐색
rg -nP -C2 '\beditingFeedId\s*:\s*Int\??\b' --glob '*.kt'
# feedId 파라미터에 Int 사용 흔적 탐색
rg -nP -C2 '\bfeedId\s*:\s*Int\??\b' --glob '*.kt'
# 네비게이션 인자 타입 Long 반영 여부 (IntType 사용 잔존 여부)
rg -nP -C2 'NavType\.\s*IntType' --glob '*.kt'
rg -nP -C2 'navArgument\(\s*"feedId"\s*\)\s*\{\s*type\s*=\s*NavType\.\s*IntType' --glob '*.kt'
# 콜백 시그니처에서 Int -> Long 전환 확인
rg -nP -C2 'onFeedCreated\s*:\s*\(\s*Int\s*\)\s*->' --glob '*.kt'
rg -nP -C2 '\(\s*Int\s*\)\s*->\s*Unit' --glob '*.kt'
# Int 변환 사용 잔존 여부
rg -nP -C2 '\.toInt\(\)' --glob '*.kt'Length of output: 20964
feedId/편집 모드 ID 타입 일관성 검증·수정 필요
editingFeedId를 Long?으로 변경하셨다면, 저장·복원(StateSaver), NavArgs, 콜백 시그니처 등 모든 feedId 관련 선언 및 사용부가 Long으로 일관되도록 반드시 정리해야 합니다. 아래 위치에서 여전히 Int 타입이 남아있거나 NavType.IntType을 사용 중이니, Long으로 교체하거나 필요 시 toLong() 변환 로직을 추가해주세요.
- data/model/feed/response/*.kt
- RelatedBooksResponse, MyFeedResponse, CreateFeedResponse, AllFeedResponse, FeedDetailResponse 등
val feedId: Int
- RelatedBooksResponse, MyFeedResponse, CreateFeedResponse, AllFeedResponse, FeedDetailResponse 등
- ui/navigator/routes/FeedRoutes.kt
data class Write(val feedId: Int? = null)
- ui/feed/screen/FeedCommentScreen.kt
onNavigateToFeedEdit: (Int) -> Unit→(Long) -> Unit
- NavArgs 설정
navArgument("feedId"){ type = NavType.IntType }→NavType.LongType
- 기타 feedId 전달·콜백부
- 네비게이션 호출부, ViewModel 콜백 시그니처 등
위 항목들을 점검·수정해 주시고, 변경 후에도 컴파일 및 런타임 네비게이션이 정상 동작하는지 확인 부탁드립니다.
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt
around line 27, editingFeedId is now Long? but many related declarations still
use Int; update all feedId-related types and usages to Long for consistency:
change model response properties (RelatedBooksResponse, MyFeedResponse,
CreateFeedResponse, AllFeedResponse, FeedDetailResponse) from Int to Long,
update ui/navigator/routes/FeedRoutes.kt Write data class feedId to Long?,
change FeedCommentScreen callback signatures from (Int)->Unit to (Long)->Unit,
change NavArgs navArgument("feedId"){ type = NavType.IntType } to
NavType.LongType, and update any navigation call sites or ViewModel callbacks to
pass Long (or call toLong() where converting from Int). Also ensure
StateSaver/State restoration and serialization use Long, run a full compile and
validate navigation at runtime.
| import kotlinx.coroutines.flow.asStateFlow | ||
| import kotlinx.coroutines.launch | ||
| import javax.inject.Inject | ||
| import kotlin.onFailure |
There was a problem hiding this comment.
컴파일 오류: 잘못된 import 제거 필요
onFailure는 kotlin.Result의 멤버 확장으로 별도 import가 필요하지 않습니다. import kotlin.onFailure는 존재하지 않는 심볼을 참조하므로 컴파일 에러가 발생합니다. 해당 import를 제거하세요.
-import kotlin.onFailure📝 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.
| import kotlin.onFailure |
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt
around line 21, remove the invalid import statement `import kotlin.onFailure`
because it references a non-existent symbol; delete that import line and
recompile to confirm the compile error is resolved.
|
|
||
| data class FeedItem( | ||
| val id: Int, | ||
| val id: Long, |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
남아있는 Int 기반 feedId/id 사용 여부 점검 요청
이번 변경과 맞물려 잔존 Int 타입 식별자가 있으면 추후 서버가 32비트 범위를 넘는 ID를 내려줄 때 파싱 실패/범위 초과 이슈가 생길 수 있습니다. 레포 전체에서 Int 타입 선언/캐스팅 흔적을 확인해 주세요.
다음 스크립트로 흔적을 점검할 수 있습니다:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) feedId: Int 선언 검색"
rg -nP --type=kt -C2 '\bfeedId\s*:\s*Int\b'
echo
echo "2) Feed 관련 data class에서 id: Int 선언 검색"
rg -nPU --type=kt -n -C2 '(?s)^\s*data\s+class\s+\w*Feed\w*\s*\([^)]*\bid\s*:\s*Int\b'
echo
echo "3) toLong() 캐스팅 흔적(원천이 Int일 가능성) 검색"
rg -nP --type=kt -C2 '\bfeedId\.toLong\s*\('Length of output: 143
Int 타입 식별자 잔여 여부를 재확인하기 위해 아래 스크립트를 실행해 주세요.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) 'feedId: Int' 선언 검색"
rg -n --glob '*.kt' -C2 '\bfeedId\s*:\s*Int\b'
echo
echo "2) Feed 관련 data class에서 'id: Int' 선언 검색"
rg -n --glob '*.kt' -C2 'data class \w*Feed\w*.*\bid\s*:\s*Int\b'
echo
echo "3) .toLong() 캐스팅 흔적 검색"
rg -n --glob '*.kt' -C2 '\.toLong\s*\('Length of output: 3677
Int 기반 feedId → Long 변경 요청
서버에서 32비트 범위(±2,147,483,647)를 넘는 ID를 내려줄 경우 파싱 실패/범위 초과 문제가 발생할 수 있습니다. 아직 Int 타입으로 선언된 feedId를 모두 Long으로 변경해 주세요.
발견된 위치:
- app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt:14
val feedId: Int? = null
- app/src/main/java/com/texthip/thip/data/model/feed/response/RelatedBooksResponse.kt:15
@SerialName("feedId") val feedId: Int
- app/src/main/java/com/texthip/thip/data/model/feed/response/CreateFeedResponse.kt:9
val feedId: Int
- app/src/main/java/com/texthip/thip/data/model/feed/response/AllFeedResponse.kt:16
@SerialName("feedId") val feedId: Int
- app/src/main/java/com/texthip/thip/data/model/feed/response/FeedDetailResponse.kt:8
@SerialName("feedId") val feedId: Int
- app/src/main/java/com/texthip/thip/data/model/feed/response/MyFeedResponse.kt:16
@SerialName("feedId") val feedId: Int
id: Int 선언은 확인되지 않았습니다.
위 위치의 Int → Long 변경을 부탁드립니다.
| private fun RelatedFeedItem.toFeedItem(): FeedItem { | ||
| return FeedItem( | ||
| id = this.feedId, | ||
| id = this.feedId.toLong(), |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
toLong() 캐스팅은 임시 대응입니다 — RelatedFeedItem.feedId를 Long으로 이관하세요
feedId의 근원 타입이 Int인 채로 UI에서 Long으로 캐스팅하면, 서버가 64비트 범위의 ID를 내려주는 순간 파싱/범위 문제로 이어질 수 있습니다. 모델 층(RelatedFeedItem)의 feedId를 Long으로 이관한 뒤 이 캐스팅을 제거하는 방향이 안전합니다.
모델 이관 이후 이 파일 변경은 다음과 같습니다:
- id = this.feedId.toLong(),
+ id = this.feedId,RelatedFeedItem의 현재 타입을 확인하려면:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "RelatedFeedItem 선언 위치 확인"
rg -nP --type=kt -C3 '\bdata\s+class\s+RelatedFeedItem\b'
echo
echo "RelatedFeedItem.feedId 타입 확인 (Int 잔존 여부)"
rg -nP --type=kt -C2 '\bfeedId\s*:\s*Int\b'Length of output: 165
모델 이관 상태를 정확히 재확인하기 위해, --type=kt 대신 --glob '*.kt' 옵션을 사용하여 다음 스크립트를 실행해 주세요.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) RelatedFeedItem 데이터 클래스 선언 위치 확인"
rg -nP '\bdata\s+class\s+RelatedFeedItem\b' -n --glob '*.kt'
echo
echo "2) RelatedFeedItem.feedId 타입 확인 (Int 또는 Long)"
rg -nP '\bfeedId\s*:\s*(Int|Long)\b' -n --glob '*.kt'Length of output: 4622
RelatedFeedItem.feedId를 Long으로 변경하고 toLong() 캐스팅 제거
feedId의 원시 타입이 Int인 상태에서 UI에서 .toLong()으로 변환 사용하는 것은, 서버가 64비트 범위의 ID를 내려줄 때 파싱/범위 손실 문제를 일으킬 수 있습니다. 모델 계층에서 feedId를 Long으로 이관한 뒤 UI 코드에서 캐스팅을 제거하세요.
- app/src/main/java/com/texthip/thip/data/model/feed/response/RelatedBooksResponse.kt
- @SerialName("feedId") val feedId: Int, + @SerialName("feedId") val feedId: Long,
- app/src/main/java/com/texthip/thip/ui/search/viewmodel/BookDetailViewModel.kt (181행)
- id = this.feedId.toLong(), + id = this.feedId,
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/data/model/feed/response/RelatedBooksResponse.kt
(update the model) and
app/src/main/java/com/texthip/thip/ui/search/viewmodel/BookDetailViewModel.kt
around line 181, change RelatedFeedItem.feedId's type from Int to Long in the
data model and remove the .toLong() cast in the ViewModel; update any
constructors, serializers/deserializers (e.g., Gson/Moshi), parcelable/room
annotations or mapping functions that reference feedId to use Long so JSON
parsing and downstream usages accept 64-bit IDs without overflow, and run/adjust
any call sites or tests that assumed Int accordingly.
There was a problem hiding this comment.
Actionable comments posted: 1
🔭 Outside diff range comments (2)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (2)
65-74: 컴파일 오류 위험: onNavigateToFeedEdit 파라미터 타입을 Long으로 변경하세요이 컴포저블은 feedId를 Long으로 사용합니다. 그러나 onNavigateToFeedEdit의 시그니처가 (Int) -> Unit로 남아 있어, 내부에서 onNavigateToFeedEdit(feedDetail.feedId)를 호출할 때 타입 불일치가 발생합니다. 또한 FeedNavigation.kt에서 이 람다를 FeedRoutes.Write(feedId = feedId)로 넘기고 있어, 라우트가 Long을 기대하면 컴파일 에러가 됩니다. 전체적으로 Long으로 일관화하는 것이 안전합니다.
아래처럼 시그니처를 Long으로 변경해 주세요.
@Composable fun FeedCommentScreen( navController: NavHostController, modifier: Modifier = Modifier, - feedId: Long, + feedId: Long, onNavigateBack: () -> Unit = {}, - onNavigateToFeedEdit: (Int) -> Unit = {}, + onNavigateToFeedEdit: (Long) -> Unit = {}, feedDetailViewModel: FeedDetailViewModel = hiltViewModel(), commentsViewModel: CommentsViewModel = hiltViewModel() )
438-446: Preview 컴파일 오류: feedId 리터럴을 Long으로 변경 필요FeedCommentScreen의 feedId가 Long으로 변경되었으므로, Preview에서 1 대신 1L을 전달해야 컴파일됩니다.
FeedCommentScreen( - feedId = 1, + feedId = 1L, navController = rememberNavController() )
🧹 Nitpick comments (2)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
87-90: 사소하지만 정리: toLong() 호출 불필요feedId가 이미 Long이므로 중복 변환을 제거해 가독성을 높일 수 있습니다.
LaunchedEffect(feedId) { feedDetailViewModel.loadFeedDetail(feedId) - commentsViewModel.initialize(postId = feedId.toLong(), postType = "FEED") + commentsViewModel.initialize(postId = feedId, postType = "FEED") }app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (1)
119-123: 타입 세이프 라우팅 도입 좋습니다. 주석된 이전 코드 제거 권장toRoute<FeedRoutes.Comment>() 사용으로 간결하고 안전해졌습니다. 과거 방식 주석은 유지 필요성이 낮으므로 정리하면 가독성이 좋아집니다.
- /*val route = backStackEntry.arguments?.let { - FeedRoutes.Comment(it.getLong("feedId")) - } ?: return@composable*/ val route = backStackEntry.toRoute<FeedRoutes.Comment>()
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt(5 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt(10 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt
🧰 Additional context used
🧬 Code Graph Analysis (1)
app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (2)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (1)
FeedScreen(67-402)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
FeedCommentScreen(65-436)
🔇 Additional comments (16)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (3)
41-43: NavController 도입과 관련 import 추가 적절삭제 성공 후 이전 스크린으로 결과 전달 흐름(BackStack SavedStateHandle)과 일관되게 작동할 기반이 마련되었습니다.
78-86: 삭제 성공 후 결과 전달 로직은 적절합니다. 단, 플래그 소모 처리(consume/reset) 확인 필요deleteSuccess가 true인 상태로 재구성되면, LaunchedEffect가 다시 트리거될 수 있습니다. 뷰모델에서 deleteSuccess를 한 번 사용 후 false로 되돌리는 consume 메서드(또는 Event 래핑)로 재발동을 방지하는지 확인해 주세요.
411-411: 삭제 API 호출 연결 LGTM확정 삭제 시점에 feedDetailViewModel.deleteFeed(feedId) 호출이 정확히 연결되었습니다.
app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (7)
25-25: SavedState에서 Long으로 결과 수신 일관화 좋습니다피드 생성 결과 feedId를 Long으로 읽어오는 변경이 전체 Long 마이그레이션과 일치합니다.
28-28: FeedScreen에 NavController 전달 적절FeedScreen이 SavedStateHandle을 구독해 삭제 결과를 반영할 수 있는 기반이 마련되었습니다.
31-31: 결과 소모 시 remove 사용 적절결과 소비 후 상태 정리(remove)로 중복 처리를 방지합니다.
69-71: 작성(수정) 경로로의 feedId 변환(toLong) 처리 OKEdit 인라인 데이터 세팅 경로에서 feedId를 Long으로 맞춰 사용하고 있습니다. 라우트 정의(FeedRoutes.Write)에서 feedId가 Long?이면 toLong()은 불필요할 수 있으니 최종 타입을 한 번 점검해 주세요.
81-82: 수정 모드 로드에서도 Long 변환 일관성 유지loadFeedForEdit 호출부도 Long으로 일관화되어 있습니다.
125-127: FeedCommentScreen에 NavController 전달 OK코멘트 화면에서도 삭제 결과를 이전 스크린으로 전달할 수 있게 되어 흐름이 완성됩니다.
130-132: onNavigateToFeedEdit 인자 타입 정합성 확인 필요FeedCommentScreen의 onNavigateToFeedEdit가 (Int) -> Unit으로 남아 있으면, 여기서 feedId를 FeedRoutes.Write(feedId = feedId)로 넘길 때 라우트가 Long을 기대하는 경우 컴파일 에러가 납니다. FeedCommentScreen 쪽 시그니처를 (Long) -> Unit으로 변경하면 이 람다의 feedId 타입도 Long으로 추론되어 문제가 해소됩니다. 앞 파일의 수정과 함께 적용해 주세요.
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (6)
45-47: NavController 관련 import 추가 OK프리뷰에서 rememberNavController()를 사용하도록 기반이 갖춰졌습니다.
72-77: Public API 시그니처(Long 전환, NavController 주입) 정합성 좋습니다
- onNavigateToFeedComment: (Long) -> Unit
- resultFeedId: Long?
- navController: NavHostController
네비게이션 및 결과 전달 체인과 일관됩니다.
282-282: 내 피드 id Long 전환 일관성 좋습니다MyFeed -> FeedItem 변환 시 id를 Long으로 맞춰 downstream과 타입 일치를 확보했습니다.
331-331: 전체 피드 id Long 전환 일관성 좋습니다AllFeed -> FeedItem 변환에서도 Long으로 일치합니다.
354-358: 좋아요/저장 상태 변경 핸들러 연결 완료뷰모델 메서드(changeFeedSave, changeFeedLike)로 연결되어 실제 API 연동 경로가 확보되었습니다.
410-424: 프리뷰에서 NavController 주입 OK프리뷰 빌드가 원활합니다. (FeedCommentScreen 프리뷰의 feedId는 1L로 변경 필요 — 해당 파일 코멘트 참조)
| LaunchedEffect(Unit) { | ||
| navController.currentBackStackEntry?.savedStateHandle?.let { handle -> | ||
| handle.getLiveData<Long>("deleted_feed_id").observeForever { deletedId -> | ||
| if (deletedId != null) { | ||
| feedViewModel.removeDeletedFeed(deletedId) | ||
| handle.remove<Long>("deleted_feed_id") | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
observeForever로 인한 메모리 누수/중복 구독 위험 — SavedStateHandle의 StateFlow로 전환 권장
observeForever는 수동 해제가 없으면 누수를 유발합니다. SavedStateHandle.getStateFlow를 사용해 안전하게 수집하세요.
- LaunchedEffect(Unit) {
- navController.currentBackStackEntry?.savedStateHandle?.let { handle ->
- handle.getLiveData<Long>("deleted_feed_id").observeForever { deletedId ->
- if (deletedId != null) {
- feedViewModel.removeDeletedFeed(deletedId)
- handle.remove<Long>("deleted_feed_id")
- }
- }
- }
- }
+ LaunchedEffect(Unit) {
+ val handle = navController.currentBackStackEntry?.savedStateHandle ?: return@LaunchedEffect
+ handle.getStateFlow<Long?>("deleted_feed_id", null).collect { deletedId ->
+ if (deletedId != null) {
+ feedViewModel.removeDeletedFeed(deletedId)
+ handle.remove<Long>("deleted_feed_id")
+ }
+ }
+ }📝 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.
| LaunchedEffect(Unit) { | |
| navController.currentBackStackEntry?.savedStateHandle?.let { handle -> | |
| handle.getLiveData<Long>("deleted_feed_id").observeForever { deletedId -> | |
| if (deletedId != null) { | |
| feedViewModel.removeDeletedFeed(deletedId) | |
| handle.remove<Long>("deleted_feed_id") | |
| } | |
| } | |
| } | |
| } | |
| LaunchedEffect(Unit) { | |
| val handle = navController.currentBackStackEntry?.savedStateHandle | |
| ?: return@LaunchedEffect | |
| handle | |
| .getStateFlow<Long?>("deleted_feed_id", null) | |
| .collect { deletedId -> | |
| if (deletedId != null) { | |
| feedViewModel.removeDeletedFeed(deletedId) | |
| handle.remove<Long>("deleted_feed_id") | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt around lines
103–112, replace the observeForever usage (which leaks and causes duplicate
subscriptions) with SavedStateHandle.getStateFlow("deleted_feed_id", /* default
*/ 0L) and collect it inside LaunchedEffect; when you receive a non-default id
call feedViewModel.removeDeletedFeed(deletedId) and then clear the key via
handle.remove("deleted_feed_id") or set it back to the default so the value is
consumed—this uses the StateFlow collection tied to the coroutine lifecycle and
avoids manual observer removal.
There was a problem hiding this comment.
Actionable comments posted: 3
🔭 Outside diff range comments (2)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (2)
89-91: feedId 타입 변환 중복 제거 필요Line 91에서
feedId.toLong()으로 변환하고 있지만,feedId는 이미Long타입입니다. 불필요한 변환을 제거해야 합니다.- commentsViewModel.initialize(postId = feedId.toLong(), postType = "FEED") + commentsViewModel.initialize(postId = feedId, postType = "FEED")
392-393: feedId 타입 불일치 문제
onNavigateToFeedEdit함수는Int타입을 받지만feedDetail.feedId는Int타입입니다. 그러나 FeedCommentScreen의feedId파라미터가Long으로 변경되었으므로,onNavigateToFeedEdit함수의 시그니처도 일관성있게Long으로 변경해야 합니다.- onNavigateToFeedEdit: (Int) -> Unit = {}, + onNavigateToFeedEdit: (Long) -> Unit = {},그리고 Line 392에서:
- onNavigateToFeedEdit(feedDetail.feedId) + onNavigateToFeedEdit(feedDetail.feedId.toLong())
🧹 Nitpick comments (5)
app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt (3)
33-33: 이름 의도를 더 명확히: isLockIcon → isSaveLocked 제안현재 플래그는 “아이콘 모양”이 아니라 “저장 기능 잠금 상태”를 의미합니다. API/도메인 용어와 맞춘
isSaveLocked(또는isLocked)가 의도를 더 잘 전달합니다. 기본값은 동일하게 false 유지가 가능합니다.적용 예(서명만 발췌):
- isLockIcon: Boolean = false, + isSaveLocked: Boolean = false,채택 시, 호출부(SavedFeedCard, Preview 등)와 내부 분기(if …)의 식별자도 함께 변경해야 합니다. 필요한 경우 전체 참조를 안전하게 치환하는 스크립트 제공 가능.
91-108: 잠금 상태 a11y 개선: contentDescription 및 비활성(semantics.disabled) 전달잠금일 때 클릭을 막은 점은 좋습니다. 접근성(스크린리더) 측면에서 상태 전달을 위해 잠금 아이콘에 적절한 contentDescription과
semantics { disabled() }를 함께 제공하는 것을 권장합니다. 또한 북마크 아이콘에도 on/off에 따른 설명을 추가하면 좋습니다.잠금/북마크 아이콘 블록만 발췌:
- if (isLockIcon) { - Icon( - painter = painterResource(R.drawable.ic_lock), - contentDescription = null, - tint = Color.Unspecified - ) - } else { - Icon( - modifier = Modifier.clickable { onBookmarkClick() }, - painter = painterResource( - if (isSaved) R.drawable.ic_save_filled else R.drawable.ic_save - ), - contentDescription = null, - tint = Color.Unspecified - ) - } + if (isLockIcon) { + Icon( + modifier = Modifier.semantics { disabled() }, + painter = painterResource(R.drawable.ic_lock), + contentDescription = stringResource(R.string.content_desc_locked), + tint = Color.Unspecified + ) + } else { + Icon( + modifier = Modifier.clickable { onBookmarkClick() }, + painter = painterResource( + if (isSaved) R.drawable.ic_save_filled else R.drawable.ic_save + ), + contentDescription = stringResource( + if (isSaved) R.string.content_desc_bookmark_on else R.string.content_desc_bookmark_off + ), + tint = Color.Unspecified + ) + }추가로 필요한 import:
import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.disabled import androidx.compose.ui.semantics.semantics추가 string 리소스 예시(res/values/strings.xml):
<string name="content_desc_locked">잠금됨</string> <string name="content_desc_bookmark_on">저장됨</string> <string name="content_desc_bookmark_off">저장</string>
129-129: Preview 가시성 강화: 잠금/비잠금 두 경우 모두 확인 가능한 추가 프리뷰 권장현재 프리뷰는 잠금 케이스만 보여줍니다. 북마크 토글 경로(잠금 해제)를 함께 미리보기로 추가해두면 리그레션을 빠르게 감지하는 데 도움이 됩니다.
예시(파일 하단에 추가):
@Preview @Composable private fun ActionBarButtonPreview_Unlocked() { var isLiked by remember { mutableStateOf(false) } var isSaved by remember { mutableStateOf(false) } ActionBarButton( isLiked = isLiked, likeCount = 123, commentCount = 45, isSaveVisible = true, isSaved = isSaved, isPinVisible = false, onLikeClick = { isLiked = !isLiked }, onCommentClick = {}, onBookmarkClick = { isSaved = !isSaved }, onPinClick = {}, isLockIcon = false ) }app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt (1)
112-112: 잠금 상태 전달 방식 적절함(아이콘 비활성 경로와 정합)
isLockIcon = feedItem.isLocked매핑으로 저장 아이콘의 비활성 표시 흐름과 일치합니다. 위에서 제안한 이름 개선을 채택한다면 호출부도 함께 변경해 주세요.호출부 예시:
- isLockIcon = feedItem.isLocked, + isSaveLocked = feedItem.isLocked,app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
150-163: 조건문 단순화 가능blur 효과 적용 조건을 더 간결하게 표현할 수 있습니다.
- modifier = if (isBottomSheetVisible || showDialog) { - Modifier - .fillMaxSize() - .blur(5.dp) - } else { - Modifier.fillMaxSize() - } + modifier = Modifier + .fillMaxSize() + .then( + if (isBottomSheetVisible || showDialog) Modifier.blur(5.dp) else Modifier + )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt (2)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (1)
updateState(64-66)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (1)
updateState(35-37)
🔇 Additional comments (3)
app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt (1)
124-124: Preview에서 핀 숨김(false) 설정 OK잠금 케이스를 강조하려는 의도가 명확합니다. 문제 없습니다.
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
79-87: 피드 삭제 후 이전 화면에서deleted_feed_id처리 로직 확인됨
FeedCommentScreen의 LaunchedEffect에서 저장한deleted_feed_id를
FeedScreen에서 아래와 같이 받아 처리하고 있습니다.
- FeedCommentScreen.kt (79–87줄):
savedStateHandle.set("deleted_feed_id", feedId) 호출- FeedScreen.kt (105–108줄):
handle.getLiveData("deleted_feed_id").observeForever { deletedId →
• feedViewModel.removeDeletedFeed(deletedId)
• handle.remove("deleted_feed_id")
}위 코드로 삭제된 피드 ID를 정상적으로 수신·처리하고 있으므로 추가 확인은 필요 없습니다.
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt (1)
94-94: feedId 타입 변환 검증 완료FeedDetailResponse.kt의 feedId가 Int로 선언되어 있어
originalFeed.feedId.toLong()호출이 필요하며, 해당 변환이 올바르게 적용되어 있습니다. 추가 조치 불필요합니다.
| fun changeFeedLike() { | ||
| viewModelScope.launch { | ||
| val originalFeed = _uiState.value.feedDetail ?: return@launch | ||
|
|
||
| val updatedFeed = originalFeed.copy( | ||
| isLiked = !originalFeed.isLiked, | ||
| likeCount = if (originalFeed.isLiked) originalFeed.likeCount - 1 else originalFeed.likeCount + 1 | ||
| ) | ||
| updateState { it.copy(feedDetail = updatedFeed) } | ||
|
|
||
| val newLikeStatus = !originalFeed.isLiked | ||
| changeFeedLikeUseCase(originalFeed.feedId.toLong(), newLikeStatus) | ||
| .onFailure { | ||
| updateState { it.copy(feedDetail = originalFeed) } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
좋아요 상태 변경 시 에러 처리 개선 필요
좋아요 API 호출이 실패했을 때 원래 상태로 롤백은 하지만, 사용자에게 에러 메시지를 표시하지 않습니다. 네트워크 오류 등으로 실패했을 때 사용자가 인지할 수 있도록 에러 상태를 업데이트해야 합니다.
changeFeedLikeUseCase(originalFeed.feedId.toLong(), newLikeStatus)
- .onFailure {
- updateState { it.copy(feedDetail = originalFeed) }
+ .onFailure { exception ->
+ updateState {
+ it.copy(
+ feedDetail = originalFeed,
+ error = "좋아요 상태 변경에 실패했습니다."
+ )
+ }
}📝 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.
| fun changeFeedLike() { | |
| viewModelScope.launch { | |
| val originalFeed = _uiState.value.feedDetail ?: return@launch | |
| val updatedFeed = originalFeed.copy( | |
| isLiked = !originalFeed.isLiked, | |
| likeCount = if (originalFeed.isLiked) originalFeed.likeCount - 1 else originalFeed.likeCount + 1 | |
| ) | |
| updateState { it.copy(feedDetail = updatedFeed) } | |
| val newLikeStatus = !originalFeed.isLiked | |
| changeFeedLikeUseCase(originalFeed.feedId.toLong(), newLikeStatus) | |
| .onFailure { | |
| updateState { it.copy(feedDetail = originalFeed) } | |
| } | |
| } | |
| } | |
| fun changeFeedLike() { | |
| viewModelScope.launch { | |
| val originalFeed = _uiState.value.feedDetail ?: return@launch | |
| val updatedFeed = originalFeed.copy( | |
| isLiked = !originalFeed.isLiked, | |
| likeCount = if (originalFeed.isLiked) originalFeed.likeCount - 1 else originalFeed.likeCount + 1 | |
| ) | |
| updateState { it.copy(feedDetail = updatedFeed) } | |
| val newLikeStatus = !originalFeed.isLiked | |
| changeFeedLikeUseCase(originalFeed.feedId.toLong(), newLikeStatus) | |
| .onFailure { exception -> | |
| updateState { | |
| it.copy( | |
| feedDetail = originalFeed, | |
| error = "좋아요 상태 변경에 실패했습니다." | |
| ) | |
| } | |
| } | |
| } | |
| } |
| fun changeFeedSave() { | ||
| viewModelScope.launch { | ||
| val originalFeed = _uiState.value.feedDetail ?: return@launch | ||
|
|
||
| val updatedFeed = originalFeed.copy( | ||
| isSaved = !originalFeed.isSaved | ||
| ) | ||
| updateState { it.copy(feedDetail = updatedFeed) } | ||
|
|
||
| val newSaveStatus = !originalFeed.isSaved | ||
| changeFeedSaveUseCase(originalFeed.feedId.toLong(), newSaveStatus) | ||
| .onFailure { | ||
| updateState { it.copy(feedDetail = originalFeed) } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
저장(북마크) 상태 변경 시 에러 처리 개선 필요
좋아요와 마찬가지로 저장 API 호출 실패 시에도 사용자에게 에러를 알려야 합니다.
changeFeedSaveUseCase(originalFeed.feedId.toLong(), newSaveStatus)
- .onFailure {
- updateState { it.copy(feedDetail = originalFeed) }
+ .onFailure { exception ->
+ updateState {
+ it.copy(
+ feedDetail = originalFeed,
+ error = "저장 상태 변경에 실패했습니다."
+ )
+ }
}📝 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.
| fun changeFeedSave() { | |
| viewModelScope.launch { | |
| val originalFeed = _uiState.value.feedDetail ?: return@launch | |
| val updatedFeed = originalFeed.copy( | |
| isSaved = !originalFeed.isSaved | |
| ) | |
| updateState { it.copy(feedDetail = updatedFeed) } | |
| val newSaveStatus = !originalFeed.isSaved | |
| changeFeedSaveUseCase(originalFeed.feedId.toLong(), newSaveStatus) | |
| .onFailure { | |
| updateState { it.copy(feedDetail = originalFeed) } | |
| } | |
| } | |
| } | |
| fun changeFeedSave() { | |
| viewModelScope.launch { | |
| val originalFeed = _uiState.value.feedDetail ?: return@launch | |
| val updatedFeed = originalFeed.copy( | |
| isSaved = !originalFeed.isSaved | |
| ) | |
| updateState { it.copy(feedDetail = updatedFeed) } | |
| val newSaveStatus = !originalFeed.isSaved | |
| changeFeedSaveUseCase(originalFeed.feedId.toLong(), newSaveStatus) | |
| .onFailure { exception -> | |
| updateState { | |
| it.copy( | |
| feedDetail = originalFeed, | |
| error = "저장 상태 변경에 실패했습니다." | |
| ) | |
| } | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt
around lines 101 to 116, the failure branch for changeFeedSave only reverts UI
state but does not notify the user; update the onFailure handler to both revert
to originalFeed and emit a user-facing error (e.g., send a UI event or update an
error field in the state) with the failure message so the user is informed when
the save/bookmark API call fails; ensure you pass the exception or its message
into the event/state and keep reverting the optimistic change as currently
implemented.
| private fun SavedFeedCardPrev() { | ||
| val feed1 = FeedItem( | ||
| id = 1, | ||
| id = 1L, |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
ID Long 리터럴로의 전환 반영 👍 / 전역 일관성 점검 권장
Preview 데이터의 id = 1L, 2L 변경은 Long 전환과 일치합니다. 실제 코드 전체에서도 FeedItem.id, 관련 DTO/엔드포인트의 feedId가 Long으로 일관되는지 한 번에 점검해보는 것을 권장합니다.
다음 스크립트로 주요 지점을 점검할 수 있습니다.
Also applies to: 140-140
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== FeedItem 정의의 id 타입 확인 =="
fd -a 'FeedItem.kt' | while read -r f; do
echo ">>> $f"
rg -n -C2 -e 'data\s+class\s+FeedItem' -e '\bid\s*:\s*(Int|Long)\b' "$f" || true
done
echo
echo "== feedId 파라미터 타입 사용 현황 (Int vs Long) =="
rg -nP --type=kt -C2 '\bfeedId\s*:\s*Int\b' || true
rg -nP --type=kt -C2 '\bfeedId\s*:\s*Long\b' || true
echo
echo "== 리터럴 Long 누락 의심 (id = <숫자> 형태에서 L 접미사 없는 경우) =="
# 오탐 가능성 있습니다. 리뷰 보조용으로만 참고하세요.
rg -nP --type=kt -C1 '\bid\s*=\s*\d+(?!L)\b' || trueLength of output: 1076
아래 스크립트로 FeedItem 정의와 feedId 사용, id 리터럴 전환 누락 여부를 전수 검사해 보시기 바랍니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== FeedItem 정의에서 id 타입 확인 =="
rg -nP 'data\s+class\s+FeedItem\b' -C2 -g '*.kt'
echo
echo "== feedId 파라미터 타입 사용 현황 =="
rg -nP '\bfeedId\s*:\s*(Int|Long)\b' -C2 -g '*.kt'
echo
echo "== id 리터럴에서 L 접미사 누락 의심 지점 =="
rg -nP '\bid\s*=\s*\d+(?!L)\b' -C1 -g '*.kt'Length of output: 25773
FeedItem ID Long 반영 확인 및 응답 DTO·리터럴 일관성 수정 필요
SavedFeedCard 에서 id = 1L 로 변경하신 건 적절하나, 전체적으로 feedId/id 타입이 혼용되어 있습니다. 아래 지점을 점검·수정해 주세요.
-
데이터 모델(응답 DTO)에서
feedId: Int로 선언된 항목들
•RelatedBooksResponse.RelatedFeedItem (feedId: Int)
•MyFeedResponse.MyFeedItem (feedId: Int)
•AllFeedResponse.AllFeedItem (feedId: Int)
•FeedDetailResponse (feedId: Int)
•CreateFeedResponse (feedId: Int)
→Long으로 변경하여 API, 서비스, 레포지토리 전반과 타입 일치시킬 것 -
코드 상의 리터럴에
L접미사 없는id = <숫자>(실제 타입이 Long이라면 누락)
•SearchBookGroupScreen.kt
•SavedBookViewModel.kt
•GroupLiveSearchResult.kt
•GroupFilteredSearchResult.kt
•GroupSearchScreen.kt
→id = 1L,2L… 처럼 명시적 Long 리터럴로 변경 -
그 외
FeedItem.id,FeedService/@path,FeedRepository, 뷰모델·유스케이스 등에서 모두Long사용 일관 확인
위 항목을 반영하면 전체 코드베이스의 ID 타입이 일관되어 예기치 못한 변환 오류를 방지할 수 있습니다.
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt
around line 124, you changed the literal to id = 1L but the codebase mixes Int
and Long for feed IDs; update the DTOs RelatedBooksResponse.RelatedFeedItem,
MyFeedResponse.MyFeedItem, AllFeedResponse.AllFeedItem, FeedDetailResponse, and
CreateFeedResponse to declare feedId as Long (not Int), change any numeric
literals in SearchBookGroupScreen.kt, SavedBookViewModel.kt,
GroupLiveSearchResult.kt, GroupFilteredSearchResult.kt, GroupSearchScreen.kt,
etc. to use Long literals (1L, 2L ...), and audit FeedItem.id, FeedService
@Path/@RequestParam signatures, FeedRepository methods, viewmodels and use-cases
to accept/return Longs so the API, service, repository and UI layers are
consistent.
➕ 이슈 링크
🔎 작업 내용
📸 스크린샷
bandicam.2025-08-19.00-51-46-874.mp4
😢 해결하지 못한 과제
없습니다
📢 리뷰어들에게
피드 스크린에서 사용자 찾기 클릭 로직도 수정해서 추가하겠습니다!
swagger를 보니 feedId가 0으로 되어있어 변경했습니다.
Summary by CodeRabbit
New Features
Improvements