Skip to content

[REFACTOR] QA 2번째 반영#110

Merged
Nico1eKim merged 11 commits into
THIP-TextHip:developfrom
Nico1eKim:refactor/#101-qa2
Aug 20, 2025
Merged

[REFACTOR] QA 2번째 반영#110
Nico1eKim merged 11 commits into
THIP-TextHip:developfrom
Nico1eKim:refactor/#101-qa2

Conversation

@Nico1eKim
Copy link
Copy Markdown
Member

@Nico1eKim Nico1eKim commented Aug 19, 2025

➕ 이슈 링크


🔎 작업 내용

  • 내 피드 -> 띱 목록 이동
  • 띱 목록 -> 유저 피드 이동
  • 유저 피드 -> 띱 목록 이동
  • 내 띱 목록 -> 유저 피드 이동
  • 유저 피드 -> 피드 상세 이동
  • 답글 생성 시 기존 답글 사라지는 문제 수정
  • 댓글 키보드 클릭 시 키보드 올라오고, 키보드 내리면 focus 잃도록 수정

📸 스크린샷


😢 해결하지 못한 과제


📢 리뷰어들에게

  • 참고해야 할 사항들을 적어주세요

Summary by CodeRabbit

  • 신기능

    • 피드에서 타인 “구독 목록” 화면으로 이동 기능 및 해당 화면 추가
  • 개선/리팩터링

    • 댓글/답글 UX 개선: 키보드 연동 포커스, 입력 후 위치로 스크롤 등 반응성 향상
    • 프로필/작성자 헤더 터치 영역 통합(헤더 전체 클릭으로 이동)
    • 스플래시 화면 네비게이션을 상태 기반으로 전환
    • 댓글 처리 흐름 및 네비게이션을 콜백에서 더 명확한 흐름으로 정리
  • 스타일

    • 앱 아이콘 리소스 정리(모노크롬 제거 등)
    • 테마 구조 정리 및 창 반투명 적용

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 19, 2025

Walkthrough

Feed 관련 모델의 creatorId 타입을 Int에서 Long으로 변경. 여러 화면에서 AuthorHeader의 클릭 API를 onThipNumClick에서 onClick으로 통합. OthersSubscription 라우트/스크린 및 네비게이션 확장 추가. 댓글 UI를 ViewModel 기반(사이드이펙트 포함)으로 리팩토링하고 키보드/포커스 처리를 강화. SplashScreen을 SplashViewModel 상태 기반으로 변경. 런처 아이콘·테마 포맷 정리 및 기타 UI 클릭/상태 조정.

Changes

Cohort / File(s) Summary
Model ID 타입 변경
app/src/main/java/.../feed/response/FeedMineInfoResponse.kt, app/src/main/java/.../feed/response/FeedUsersInfoResponse.kt
creatorId 타입 Int → Long으로 변경.
AuthorHeader 클릭 API 통합 및 호출처 정리
app/src/main/java/.../ui/common/header/AuthorHeader.kt, app/src/main/java/.../ui/feed/component/LiveSearchPeopleResult.kt, app/src/main/java/.../ui/feed/screen/MySubscriptionListScreen.kt
AuthorHeaderonClick 추가, onThipNumClick 제거. 호출부를 onClick으로 전환하고 클릭 핸들링을 헤더로 위임.
OthersSubscription 네비게이션 추가/연결
app/src/main/java/.../ui/feed/screen/FeedOthersScreen.kt, app/src/main/java/.../ui/feed/screen/FeedScreen.kt, app/src/main/java/.../ui/feed/screen/OthersSubscriptionListScreen.kt, app/src/main/java/.../ui/navigator/extensions/FeedNavigationExtensions.kt, app/src/main/java/.../ui/navigator/navigations/FeedNavigation.kt, app/src/main/java/.../ui/navigator/routes/FeedRoutes.kt
OthersSubscription 라우트·스크린 추가, Feed 관련 스크린에 네비게이션 콜백 추가 및 NavController 확장 navigateToOthersSubscription 도입. 스크린명(…Subsciption→…Subscription) 및 콜백 기반 네비게이션으로 전환.
댓글 UX 리팩토링(이벤트→ViewModel / 사이드이펙트 / 포커스)
app/src/main/java/.../ui/group/note/component/CommentBottomSheet.kt, app/src/main/java/.../ui/group/note/screen/GroupNoteScreen.kt, app/src/main/java/.../ui/group/note/viewmodel/CommentsViewModel.kt, app/src/main/java/.../ui/feed/screen/FeedCommentScreen.kt
CommentBottomSheet가 콜백 대신 ViewModel에 의존하도록 변경. CommentsEvent에 Create/Delete/LikeReply 추가, SharedFlow 기반 sideEffect(ScrollToItem) 도입. 키보드 가시성·FocusRequester 및 외부 LazyListState 전달로 포커스/스크롤 제어 강화.
Feed 댓글 화면의 포커스/키보드 UX 개선
app/src/main/java/.../ui/feed/screen/FeedCommentScreen.kt
FocusRequester, LazyListState, IME(키보드) 감지 추가. 키보드 숨김 시 리플라이 상태 초기화 및 포커스 해제. 답글 시 프로그래밍식 포커스 요청 적용.
스플래시 화면 상태 기반 전환
app/src/main/java/.../ui/signin/screen/SplashScreen.kt, app/src/main/java/.../ui/signin/viewmodel/SplashViewModel.kt
SplashViewModel 추가(초기화 후 3초 지연으로 navigateToLogin=true). SplashScreen이 ViewModel의 StateFlow를 구독해 네비게이션 트리거.
리소스/테마 정리
app/src/main/res/drawable/ic_launcher_background.xml, app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml, app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml, app/src/main/res/values/themes.xml
런처 아이콘 포맷 정리 및 adaptive-icon에서 monochrome 제거, foreground 참조를 mipmap으로 변경. Base.Theme.Thip 추가하고 Theme.Thip은 base를 상속해 windowIsTranslucent 설정 추가.
기타 UI 조정
app/src/main/java/.../ui/feed/component/OthersFeedCard.kt, app/src/main/java/.../ui/common/forms/WarningTextField.kt
OthersFeedCard: 내부 isLiked 상태 제거, 카드 전체 클릭으로 onContentClick 호출; 액션 버튼의 내부 onLike/onComment 처리 제거. WarningTextField: unfocusedTextColor = colors.White 추가.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as 사용자
  participant CBS as CommentBottomSheet
  participant VM as CommentsViewModel
  note over CBS,VM: 댓글(또는 답글) 생성 흐름
  U->>CBS: 입력 후 Send 클릭
  CBS->>VM: CommentsEvent.CreateComment(content, parentId)
  VM-->>VM: 상태 업데이트(댓글/답글 추가)
  VM-->>CBS: sideEffect.ScrollToItem(parentId)
  CBS->>CBS: 입력 초기화 및 포커스 해제
  CBS->>CBS: 요청된 항목으로 스크롤
Loading
sequenceDiagram
  autonumber
  actor U as 사용자
  participant FS as FeedScreen
  participant Nav as NavHostController
  participant OSL as OthersSubscriptionListScreen
  note over FS,Nav: OthersSubscription 네비게이션
  U->>FS: 구독바 클릭
  FS->>Nav: navigateToOthersSubscription(userId)
  Nav->>OSL: OthersSubscriptionListScreen 표시
Loading
sequenceDiagram
  autonumber
  participant SS as SplashScreen
  participant SVM as SplashViewModel
  participant App as 앱 내비게이터
  note over SS,SVM: 상태 기반 전환
  SS->>SVM: navigateToLogin 상태 구독
  SVM-->>SVM: 초기화 후 3초 지연 -> true 설정
  SVM-->>SS: navigateToLogin = true
  SS->>App: onNavigateToLogin() 호출
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • JJUYAAA
  • rbqks529

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.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Status, Documentation and Community

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (2)
app/src/main/java/com/texthip/thip/ui/signin/screen/SplashScreen.kt (1)

73-75: Preview 크래시 방지: 명시적 ViewModel 전달

Preview에서 hiltViewModel() 기본값을 사용하면 실패할 수 있습니다. 미리보기에서는 직접 인스턴스를 전달해 주세요.

-    SplashScreen()
+    SplashScreen(viewModel = com.texthip.thip.ui.signin.viewmodel.SplashViewModel())

참고: 이미 상단에 SplashViewModel import가 있으므로, 패키지 접두사 없이 SplashViewModel()를 사용해도 됩니다. 패키지 접두사를 제거하려면 import를 유지하세요:

-    SplashScreen()
+    SplashScreen(viewModel = SplashViewModel())
app/src/main/java/com/texthip/thip/ui/common/header/AuthorHeader.kt (1)

1-146: 레거시 콜백 onThipNumClick 사용 흔적 발견 – 수정 필요

컴파일 에러 방지를 위해 AuthorHeader 시그니처 변경에 맞춰 아래 콜백 및 호출부를 업데이트해 주세요:

  • app/src/main/java/com/texthip/thip/ui/feed/component/LiveSearchPeopleResult.kt (25행): 함수 파라미터 onThipNumClick
  • app/src/main/java/com/texthip/thip/ui/feed/component/LiveSearchPeopleResult.kt (37행): onClick = { onThipNumClick(user) }

필수 리팩토링이 필요합니다.

🧹 Nitpick comments (15)
app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt (2)

17-22: 네비게이션 신호를 "상태" 대신 "이벤트"로 모델링하는 개선 제안

현재 불린 상태가 true가 된 뒤 별도 reset이 없으므로, 동일 화면에 잔류한 채 재구성이 발생하더라도 재트리거되진 않지만, 장기적으로는 One-shot 이벤트 모델(예: SharedFlow<Unit>/Channel)이 의도가 더 명확합니다. 혹은 네비게이션 직후 false로 되돌리는 방법도 있습니다.

예시(간단 reset 방식):

         viewModelScope.launch {
-            delay(3_000L)
-            _navigateToLogin.value = true
+            delay(3_000L)
+            _navigateToLogin.value = true
+            // UI에서 처리 후 동일 화면에 남는 경우의 재트리거 방지
+            _navigateToLogin.value = false
         }

또는 MutableSharedFlow<Unit>(replay = 0)으로 바꾸고 emit(Unit)을 사용하는 방식을 고려해주세요.


1-23: 지연 시간 상수화로 가독성/테스트 용이성 향상

매직 넘버를 상수로 분리하면 의도가 명확해지고 테스트 시 주입이 쉬워집니다.

예시:

 package com.texthip.thip.ui.signin.viewmodel

+private const val SPLASH_DELAY_MS = 3_000L
 ...
         viewModelScope.launch {
-            delay(3_000L)
+            delay(SPLASH_DELAY_MS)
             _navigateToLogin.value = true
         }
app/src/main/java/com/texthip/thip/ui/signin/screen/SplashScreen.kt (1)

45-49: Modifier 체인 순서 미세 개선 제안: size → background 순서

현재 background().fillMaxSize() 순서도 동작하지만, 일반적으로 사이즈 결정 후 배경을 그리는 fillMaxSize().background(...) 순서가 더 직관적입니다.

-        Modifier
-            .background(colors.Black)
-            .fillMaxSize(),
+        Modifier
+            .fillMaxSize()
+            .background(colors.Black),
app/src/main/java/com/texthip/thip/data/model/feed/response/FeedUsersInfoResponse.kt (1)

5-16: 직렬화 네이밍 일관성 제안.

동일 계층의 FeedMineInfoResponse는 @SerialName을 사용하고, 본 타입은 기본 이름 매핑을 사용합니다. 백엔드 필드가 고정되어 있다면, 두 모델 모두 동일한 스타일로 맞추는 편이 유지보수에 유리합니다.

예: 이 파일에 @SerialName을 추가하는 경우

 package com.texthip.thip.data.model.feed.response
 
-import kotlinx.serialization.Serializable
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
 
 @Serializable
 data class FeedUsersInfoResponse(
-    val creatorId: Long,
+    @SerialName("creatorId") val creatorId: Long,
     val profileImageUrl: String,
     ...
 )
app/src/main/java/com/texthip/thip/data/model/feed/response/FeedMineInfoResponse.kt (1)

6-17: ID 전용 값타입 도입 고려(미래 변경 내성 강화).

creatorId 같은 식별자는 @JvmInline value class로 감싸 두면 Int/Long 마이그레이션, 잘못된 형 변환, 파라미터 순서 실수 등을 컴파일 타임에 차단할 수 있습니다.

예:

@JvmInline
value class CreatorId(val value: Long)
  • 네비/DB/매퍼/도메인에 동일 타입 적용 → 변환 지점이 명확해지고 오용이 줄어듭니다.
app/src/main/res/values/themes.xml (1)

6-8: 기본 테마에서 전역 투명 창 속성 분리 및 전용 Translucent 테마 추가

기본 애플리케이션 테마인 Theme.ThipwindowIsTranslucent를 제거하고, 실제 투명 창이 필요한 액티비티에만 적용할 전용 테마를 분리하세요.

· 수정 필요 위치
• app/src/main/res/values/themes.xml
• app/src/main/AndroidManifest.xml

· 제안된 변경사항

--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@
-    <style name="Theme.Thip" parent="Base.Theme.Thip">
-        <item name="android:windowIsTranslucent">true</item>
-    </style>
+    <style name="Theme.Thip" parent="Base.Theme.Thip" />
+    <style name="Theme.Thip.Translucent" parent="Base.Theme.Thip">
+        <item name="android:windowIsTranslucent">true</item>
+    </style>
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ application
-        android:theme="@style/Theme.Thip"
+        android:theme="@style/Theme.Thip"
@@ <activity android:name=".MainActivity"
-        android:theme="@style/Theme.Thip">
+        android:theme="@style/Theme.Thip.Translucent">

· 적용 방법

  1. 기본 애플리케이션/대부분 액티비티는 Theme.Thip 유지
  2. 투명 창이 필요한 액티비티(예: SplashActivity, DialogActivity 등)만 android:theme="@style/Theme.Thip.Translucent"로 설정

위와 같이 분리하면 전역 성능 저하·제스처/배경 누락 문제를 방지할 수 있습니다.

app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (1)

2-5: Android 13 테마 아이콘 지원 여부 확인(모노크롬 제거).

monochrome 요소가 제거되어 A13 테마 아이콘이 비활성화될 수 있습니다. 의도라면 OK, 아니라면 monochrome 추가를 고려해 주세요.

예:

 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@drawable/ic_launcher_background"/>
     <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+    <monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
 </adaptive-icon>

검증 스크립트:

#!/bin/bash
set -euo pipefail

echo "[1] monochrome 요소 존재 여부"
rg -n -C1 '<monochrome' app/src/main/res/mipmap-anydpi-v26 || true
app/src/main/java/com/texthip/thip/ui/feed/component/LiveSearchPeopleResult.kt (1)

25-38: AuthorHeader API 변경 대응은 적절합니다. 다만 콜백 이름(onThipNumClick)은 의미에 맞게 정리해 주세요.

이제 헤더 전체가 클릭 대상이므로 onThipNumClick 네이밍은 오해의 소지가 있습니다. onUserClick 또는 onHeaderClick 등으로 바꾸면 가독성과 의도가 더 명확해집니다.

아래처럼 파라미터명만 정리하면 호출부(프리뷰 포함) 영향이 없습니다.

-fun SearchPeopleResult(
-    modifier: Modifier = Modifier,
-    peopleList: List<MySubscriptionData>,
-    onThipNumClick: (MySubscriptionData) -> Unit = {}
-)
+fun SearchPeopleResult(
+    modifier: Modifier = Modifier,
+    peopleList: List<MySubscriptionData>,
+    onUserClick: (MySubscriptionData) -> Unit = {}
+)
 ...
-    AuthorHeader(
+    AuthorHeader(
         profileImage = user.profileImageUrl,
         nickname = user.nickname,
         badgeText = user.role,
         profileImageSize = 36.dp,
         showButton = false,
         showThipNum = true,
         thipNum = user.subscriberCount,
-        onClick = { onThipNumClick(user) }
+        onClick = { onUserClick(user) }
     )
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (1)

3-4: Android 13 테마 아이콘 지원 검토 권장

  • ic_launcher.xmlic_launcher_round.xml 모두 <foreground android:drawable="@mipmap/ic_launcher_foreground"/>로 일관되게 설정되어 있으며,
    mipmap-hdpi ~ mipmap-xxxhdpiic_launcher_foreground.webp 리소스가 정상 존재함을 확인했습니다.
  • 다만 monochrome 요소가 제거되어 Android 13+ 테마 아이콘 기능이 비활성화됩니다.
    제품 정책상 테마 아이콘 지원이 계속 필요하다면, 별도 단색 전용 리소스를 유지하는 리팩터를 권장드립니다.

예시:

<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background"/>
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
    <monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
</adaptive-icon>
app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt (1)

48-48: 주석 표현을 더 명확하게

"띱 목록으로 이동"은 모호할 수 있습니다. "다른 사용자 띱 목록으로 이동"으로 구체화하면 가독성이 좋아집니다.

-// 띱 목록으로 이동
+// 다른 사용자 띱 목록으로 이동
app/src/main/java/com/texthip/thip/ui/feed/screen/MySubscriptionListScreen.kt (1)

154-165: 클릭 처리를 AuthorHeader로 이관한 방향은 좋습니다. 다만 클릭 영역 변화(헤더만 클릭 가능) 영향을 확인해 주세요.

이전에는 아이템 전체(Column)가 클릭 가능했다면, 지금은 헤더 영역만 클릭됩니다. 기획/UX 요구사항이 "아이템 전체 클릭"이라면 기존 범위를 유지하는 편이 나을 수 있습니다.

옵션 B(아이템 전체 클릭 유지)를 원하시면 아래처럼 되돌리는 것을 고려해 보세요.

-Column(modifier = Modifier.padding(horizontal = 20.dp)) {
+Column(modifier = Modifier
+    .padding(horizontal = 20.dp)
+    .clickable { onUserClick(user.userId) }
+) {
     AuthorHeader(
         profileImage = user.profileImageUrl,
         nickname = user.nickname,
         badgeText = user.aliasName,
         badgeTextColor = hexToColor(user.aliasColor),
         buttonText = stringResource(if (user.isFollowing) R.string.thip_cancel else R.string.thip),
         buttonWidth = 64.dp,
         profileImageSize = 36.dp,
         onButtonClick = { onToggleFollow(user.userId, user.nickname) },
-        onClick = { onUserClick(user.userId) }
+        // 아이템 전체 클릭으로 일원화 시 헤더 onClick은 제거
     )

추가로 필요한 import:

import androidx.compose.foundation.clickable

옵션 A(현재처럼 헤더만 클릭)로 유지 시에는 별도 변경 불필요합니다. 이 경우 헤더가 Row 전역 클릭을 처리하므로 ripple/접근성(semantics)도 AuthorHeader 내부에서 일관되게 관리되는 장점이 있습니다.

app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (1)

244-246: 내 피드 → 띱 목록 네비게이션 연결 정상 동작 예상

myFeedInfo?.creatorId 존재 시 OthersSubscription으로 라우팅하는 흐름 일관적입니다.

  • 네이밍 측면에서는 “OthersSubscription”이 내 피드에서도 쓰이는 점이 살짝 혼동을 줄 수 있습니다. 장기적으로 route/확장 함수명을 “UserSubscription” 등 중립적으로 리네임 고려해도 좋겠습니다.
app/src/main/java/com/texthip/thip/ui/common/header/AuthorHeader.kt (1)

53-55: 접근성(세만틱) 보완: Row.clickable에 role 지정 권장

스크린리더/테스트 자동화를 위해 Role 지정이 있으면 좋습니다.

다음 변경을 제안합니다:

-            .clickable(onClick = onClick),
+            .clickable(
+                role = androidx.compose.ui.semantics.Role.Button,
+                onClick = onClick
+            ),

추가로 필요한 import(선택):

import androidx.compose.ui.semantics.Role
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)

292-295: 댓글 버튼 클릭 시 입력창 포커싱을 바로 연결하세요

현재 주석으로 남아있는 포커스 처리 TODO를 바로 구현하면 UX가 매끄러워집니다.

다음과 같이 최소 구현으로 포커스만 요청해도 체감 개선 효과가 큽니다.

-                                onCommentClick = { /* 스크롤 이동 or 포커스 처리 */ },
+                                onCommentClick = {
+                                    focusRequester.requestFocus()
+                                },

추가로 스크롤 이동까지 원하시면 rememberCoroutineScope()를 도입해 listState.animateScrollToItem(...)를 호출하는 확장을 도와드릴 수 있습니다.

app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt (1)

148-155: 답글 병합 시 중복 방지 또는 응답 스키마 확인 필요

originalParentComment.replyList + res.replyList는 응답이 “신규 답글 1건”이 아닌 “부모의 최신 전체 답글 리스트” 형태일 경우 중복을 야기할 수 있습니다. 응답 스키마를 확인하거나 안전장치를 추가하세요.

안전한 병합 예시:

-                                val updatedParentComment = originalParentComment.copy(
-                                    replyList = originalParentComment.replyList + res.replyList
-                                )
+                                val updatedParentComment = originalParentComment.copy(
+                                    replyList = (originalParentComment.replyList + res.replyList)
+                                        .distinctBy { it.commentId }
+                                )

응답이 “신규 답글 1건”이라면, 명시적으로 res.replyList.singleOrNull()만 추가하는 것도 방법입니다.

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 498fc94 and 38bf5fd.

⛔ Files ignored due to path filters (1)
  • app/src/main/ic_launcher-playstore.png is excluded by !**/*.png
📒 Files selected for processing (21)
  • app/src/main/java/com/texthip/thip/data/model/feed/response/FeedMineInfoResponse.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/data/model/feed/response/FeedUsersInfoResponse.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/ui/common/header/AuthorHeader.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/feed/component/LiveSearchPeopleResult.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (6 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 (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/feed/screen/MySubscriptionListScreen.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/feed/screen/OthersSubscriptionListScreen.kt (5 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt (11 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt (5 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/signin/screen/SplashScreen.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt (1 hunks)
  • app/src/main/res/drawable/ic_launcher_background.xml (1 hunks)
  • app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (1 hunks)
  • app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (1 hunks)
  • app/src/main/res/values/themes.xml (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (1)
app/src/main/java/com/texthip/thip/ui/feed/screen/OthersSubscriptionListScreen.kt (1)
  • OthersSubscriptionListScreen (43-73)
🔇 Additional comments (37)
app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt (2)

14-16: StateFlow 캡슐화 방식 좋아요

쓰기 가능한 MutableStateFlow를 private으로 두고, 외부에는 읽기 전용 StateFlow로 노출한 점 적절합니다.


12-13: 의존성 없는 ViewModel에 @Inject 유지 vs 제거

현재 의존성이 없는 ViewModel이지만 Hilt로 관리하려면 생성자에 @Inject가 필요합니다. 위 import만 javax.inject.Inject로 바꾸면 현 구조 유지가 적절합니다.

app/src/main/java/com/texthip/thip/ui/signin/screen/SplashScreen.kt (2)

39-43: Lifecycle-aware 상태 기반 네비게이션 전환, 적절합니다

LaunchedEffect(navigateToLogin)로 키를 걸고 true일 때만 네비게이션을 트리거하는 패턴이 깔끔합니다. 중복 네비게이션도 방지됩니다.


32-36: SplashScreen viewModel 기본값 사용 확인 완료

현재 코드베이스에 viewModel 파라미터를 명시적으로 전달하는 SplashScreen 호출이 없으므로, 모두 기본값(hiltViewModel())이 안전하게 사용되고 있습니다. 추가 조치는 필요하지 않습니다.

app/src/main/java/com/texthip/thip/data/model/feed/response/FeedUsersInfoResponse.kt (1)

7-7: creatorId 타입 Long 전환 자체는 합리적입니다.

대규모 ID 스페이스를 고려한 확장으로 보이며, 모델 수준에서는 문제 없어 보입니다.

app/src/main/java/com/texthip/thip/data/model/feed/response/FeedMineInfoResponse.kt (1)

8-8: RelatedBooksResponse.kt/RoomsDailyGreetingResponse.kt도 creatorId:Int → Long으로 통일 필요

다음 지점에서 추가 수정이 필요해 보입니다:

  • app/src/main/java/com/texthip/thip/data/model/feed/response/RelatedBooksResponse.kt (라인 16)
    @SerialName("creatorId") val creatorId: Int → Long으로 변경
  • app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsDailyGreetingResponse.kt (라인 15)
    val creatorId: Int → Long으로 변경

현재 코드베이스에서

  • Compose Navigation(NavType.IntTypeNavType.LongType, getIntgetLong)
  • Bundle/Intent (putInt/getIntputLong/getLong)
  • Room 엔티티·DAO·쿼리 파라미터
  • 매핑 계층(toDomain/toUi/toEntity)에서의 toInt() 캐스팅
    사용 흔적은 발견되지 않았습니다.
    하지만 변경 후 실제 네비게이션, 번들, DB 매퍼 흐름에 영향이 없는지 반드시 교차 확인해 주세요.
app/src/main/res/drawable/ic_launcher_background.xml (1)

2-74: 포맷 정리만 이뤄졌고 기능적 변화는 없습니다. LGTM.

리소스 속성/값/도형 데이터는 동일합니다.

app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (1)

3-4: 리소스 확인 완료: ic_launcher_foreground가 mipmap에 정상 존재하며 adaptive-icon XML에서 올바르게 참조되고 있습니다.
추가 조치 없이 머지해 주세요.

app/src/main/java/com/texthip/thip/ui/feed/component/LiveSearchPeopleResult.kt (1)

37-37: 확인 결과 레거시 onThipNumClick= 명명 인자 사용 흔적 없음

전체 Kotlin 파일을 대상으로 검색한 결과, 다음과 같습니다:

  • onThipNumClick= 형태의 명명 인자 사용 사례: 없음
  • 일반 호출(onThipNumClick(user))은 LiveSearchPeopleResult.kt:37에서만 존재

추가로 남아 있는 레거시 호출은 없으므로, 더 이상 조치할 필요가 없습니다.

app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt (1)

29-30: OthersSubscription 라우트 추가 적절합니다

  • @serializable 적용과 userId: Long 타입 일관성 모두 적절합니다.
app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt (1)

49-51: 확장 함수 추가 LGTM

FeedRoutes.OthersSubscription(userId)로의 네비게이션 헬퍼가 일관된 패턴으로 잘 추가되었습니다.

app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (1)

75-75: 새 네비게이션 콜백 추가로 확장성 확보 (역호환 유지 OK)

기본값을 빈 람다로 두어 호출부 수정 없이도 컴파일되는 점 좋습니다. 공개 API 확장 방향 적절합니다.

app/src/main/java/com/texthip/thip/ui/common/header/AuthorHeader.kt (2)

48-49: 헤더 단일 onClick 진입점으로 인터랙션 통합

이전 onThipNumClick 분산을 정리하고 헤더 전체 클릭으로 통일한 점 좋습니다. 내부 OutlinedButton 등 중첩 클릭과의 충돌은 Compose에서 하위 요소가 우선 소비하므로 문제 없을 것으로 보입니다. 의도가 “버튼 영역은 버튼만, 그 외는 헤더 클릭”이면 현재 구현으로 충분합니다.

버튼 영역까지 헤더 onClick이 동작하지 않아야 한다는 명확한 UI 요구사항이 있는지 한번 확인 부탁드립니다. 현재 구현은 버튼 탭 시 버튼만 동작합니다.


142-142: 프리뷰 데이터 최신화 LGTM

새 인터랙션 모델에 맞춰 프리뷰 파라미터(thipNum) 반영된 점 좋습니다.

app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt (5)

42-42: 새 콜백(onNavigateToSubscriptionList) 도입 적절

스크린 외부로 네비게이션을 위임하는 패턴과 잘 맞습니다. 기본값 제공으로 역호환도 보장됩니다.


50-52: 콜백 전달 체인 정리 완료

ViewModel의 좋아요 토글과 신규 네비게이션 콜백을 Content로 전달하는 구조 명확합니다.


59-61: Content 시그니처 확장 합리적

상위에서 받은 콜백을 콘텐츠로 내려 재사용성을 높였습니다.


193-195: 프리뷰 시그니처 보완 LGTM

새 파라미터 추가로 프리뷰도 안전하게 컴파일됩니다.


100-103: 팔로워 바 클릭 → 구독 목록 네비게이션 연결 확인 ✅
FeedUsersInfoResponse.creatorId가 Long으로 정의되어 있어 OthersSubscription(userId: Long) 호출부와 타입이 일치함을 확인했습니다. 코드 변경을 승인합니다.

app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (5)

14-14: OthersSubscriptionListScreen 매핑 import 추가 OK


20-20: navigateToOthersSubscription 확장 함수 import OK


48-53: FeedScreen → OthersSubscription 네비게이션 연결 정상

FeedScreen의 새 콜백을 NavController 확장으로 라우팅. 일관된 네비게이션 계층입니다.


125-127: 다른 유저 피드 → 구독 목록 네비게이션 연결 OK

FeedOthersScreen에서의 신규 콜백도 동일한 확장을 사용해 일관성 유지되었습니다.


151-158: FeedRoutes.OthersSubscription 파라미터 바인딩 정상 확인

  • app/src/main/java/com/texthip/thip/ui/feed/viewmodel/OthersSubscriptionViewModel.kt:
    private val userId: Long = requireNotNull(savedStateHandle["userId"])
    SavedStateHandle을 통해 userId를 올바르게 수신하고 있습니다.

추가 조치 불필요합니다.

app/src/main/java/com/texthip/thip/ui/feed/screen/OthersSubscriptionListScreen.kt (6)

44-47: 엔트리 포인트 시그니처 정비(Back/프로필 콜백) 좋습니다

NavController 의존 제거로 테스트 용이성과 재사용성이 개선됩니다. 오탈자(Subscrip-tion) 정정도 반갑네요.


67-72: Content에 콜백 주입 구조 명확

상위에서 받은 네비게이션 콜백을 그대로 위임해 계층 책임이 잘 나뉘었습니다.


75-80: Content 시그니처 확장 합리적

onNavigateBack, onProfileClick 모두 UI와 상호작용 흐름에 맞게 배치되었습니다.


116-126: 리스트 아이템 헤더 클릭 → 프로필 이동 연결 OK

AuthorHeader의 onClick을 사용해 상단 전역 클릭으로 통일한 점이 다른 화면과도 일관적입니다.


159-159: 프리뷰 함수명/시그니처 정리 LGTM


172-182: 프리뷰 콜백 인자 추가로 컴파일 안전성 확보

목 데이터/상태도 적절합니다.

app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (2)

155-167: 키보드 가려짐 시 Reply 상태/포커스 정리 로직 적절합니다

IME 가 내려가면 replyingTo 상태와 포커스를 정리하는 흐름이 UI 기대와 일치합니다. WindowInsets.ime.getBottom(LocalDensity.current) > 0 접근도 합리적입니다.


356-357: Reply 클릭 시 입력창 포커스 처리 LGTM

FocusRequester를 사용한 포커스 요청과 입력창에의 연결이 올바르게 구성되어 있습니다.

Also applies to: 376-399

app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt (1)

201-246: 답글 좋아요 API 재사용 검증

toggleReplyLike에서 commentsRepository.likeComment(replyId, ...)를 호출합니다. 동일 엔드포인트로 댓글/답글 좋아요를 모두 처리하도록 설계되었는지 확인해 주세요. 엔드포인트가 분리되어 있다면 분기 필요합니다.

원본 인터페이스에서 reply/parent 식별 방식(파라미터명, path 차이 등)을 확인 부탁드립니다.

app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt (1)

505-513: CommentBottomSheet에 ViewModel 주입으로 API 일관화된 점 좋습니다

콜백 기반에서 ViewModel 주입형으로 전환되며 호출부가 간결해졌습니다. initialize 타이밍도 LaunchedEffect(selectedPostForComment?.postId) 내에서 적절합니다.

app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt (3)

68-82: 키보드/포커스 흐름 도입 LGTM

FocusRequester 연결 + IME 가려짐 시 reply 상태 초기화가 의도와 잘 맞습니다.


124-136: 이벤트 위임을 ViewModel로 일원화한 점 좋습니다

로드모어, 삭제 등 액션이 VM으로 수렴되어 테스트/유지보수성이 향상됩니다. Reply 클릭 시 포커싱도 적절합니다.


225-243: 스크롤 끝 감지 및 로드모어 트리거 로직 적절합니다

LazyListState.layoutInfo 기반의 끝 감지, LaunchedEffect로의 로드모어 트리거가 깔끔합니다. isLoadingMore/isLastPage 게이트도 적절합니다.

Also applies to: 251-286

@Serializable
data class FeedUsersInfoResponse(
val creatorId: Int,
val creatorId: Long,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Int 가정 잔존 여부 점검 필요 (NavType/Bundle/매퍼/DB).

모델은 Long으로 바뀌었지만, 다음 지점에서 Int 전제가 남아 있으면 런타임/네비게이션 버그가 납니다:

  • Compose Navigation: NavType.IntType, getInt/putInt, route 인코딩 시 toInt()
  • Bundle/Intent extras: putInt/getInt
  • 매핑 확장함수: toInt() 캐스팅
  • Room/DB 엔티티/DAO 시그니처: creatorId: Int

아래 스크립트로 잔존 사용을 한 번에 확인해 주세요.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1] creatorId: Int 선언 검색"
rg -n --type=kt -C2 '\bcreatorId\s*:\s*Int\b' || true

echo
echo "[2] NavType/Bundle Int 사용 검색"
rg -n --type=kt -C2 'NavType\.IntType|\bgetInt\(\s*"creatorId"\s*\)|\bputInt\(\s*"creatorId"\s*,' || true

echo
echo "[3] creatorId 관련 toInt() 캐스팅 검색"
rg -n --type=kt -C2 'creatorId[^;\n]*\.toInt\(\)|\.toInt\(\)[^;\n]*creatorId' || true

echo
echo "[4] getLong 사용 현황(전환 여부)"
rg -n --type=kt -C2 '\bgetLong\(\s*"creatorId"' || true

Length of output: 715


NavType/Bundle Int용 검사를 위해 .kt 파일만 지정하여 다시 검색해 보겠습니다.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1] `creatorId: Int` 선언 검색 (.kt 파일 대상)"
rg -n -C2 '\bcreatorId\s*:\s*Int\b' -g '*.kt' || true

echo
echo "[2] NavType.IntType / getInt·putInt 사용 검색"
rg -n -C2 'NavType\.IntType|\bgetInt\(\s*"creatorId"\s*\)|\bputInt\(\s*"creatorId"\s*,' -g '*.kt' || true

echo
echo "[3] `creatorId.toInt()` 캐스팅 검색"
rg -n -C2 'creatorId[^;\n]*\.toInt\(\)|\.toInt\(\)[^;\n]*creatorId' -g '*.kt' || true

echo
echo "[4] getLong으로 전환된 호출 검색"
rg -n -C2 '\bgetLong\(\s*"creatorId"\s*\)' -g '*.kt' || true

Length of output: 2136


creatorId 타입 일관성 유지 필요
모델에서 Long으로 변경된 creatorId가 여전히 Int로 선언된 곳이 발견되었습니다. 런타임/네비게이션 버그를 방지하려면 아래 파일들을 모두 Long으로 변경하고, 매퍼·NavType·Bundle·DB 시그니처도 함께 검토해 주세요.

• app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsDailyGreetingResponse.kt (15줄)
• app/src/main/java/com/texthip/thip/data/model/feed/response/RelatedBooksResponse.kt (16줄)

예시 변경:

-    val creatorId: Int,
+    val creatorId: Long,

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/data/model/feed/response/FeedUsersInfoResponse.kt
around line 7 (creatorId), change the creatorId declaration from Int to Long and
propagate that change to all related code: update the two other occurrences at
app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsDailyGreetingResponse.kt
(line 15) and
app/src/main/java/com/texthip/thip/data/model/feed/response/RelatedBooksResponse.kt
(line 16) to use Long; then update any mappers (DTO↔domain), NavType
implementations/argument parsing, Bundle put/get calls to use putLong/getLong,
and DB schema/DAO signatures and entity fields to Long so types remain
consistent end-to-end.

Comment on lines +157 to 168
val listState = rememberLazyListState()

val isKeyboardVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0

LaunchedEffect(isKeyboardVisible) {
if (!isKeyboardVisible) {
replyingToCommentId = null
replyingToNickname = null
focusManager.clearFocus()
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

VM SideEffect(ScrollToItem) 수신 누락: 답글 작성 후 해당 부모 위치로 스크롤 연동 필요

CommentsViewModel이 ScrollToItem 사이드이펙트를 발행하지만, 이 화면에서는 수집하지 않아 스크롤이 동작하지 않습니다. 또, 수집자가 없으면 ViewModel 쪽 emit이 서스펜드되는 리스크가 있어 UI 일관성을 위해 반드시 수집을 권장합니다.

다음 코드를 val listState 선언 인근에 추가해 주세요:

LaunchedEffect(commentsViewModel.sideEffect, commentsUiState.comments) {
    commentsViewModel.sideEffect.collect { effect ->
        when (effect) {
            is CommentSideEffect.ScrollToItem -> {
                val targetIndex = effect.parentCommentId?.let { id ->
                    commentsUiState.comments.indexOfFirst { it.commentId == id }
                } ?: 0
                if (targetIndex >= 0) {
                    listState.animateScrollToItem(targetIndex)
                }
            }
        }
    }
}

또는, 답글 시작(onReplyClick) 시에도 입력창 포커스와 함께 해당 항목까지 스크롤하도록 보강할 수 있습니다.

🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt around
lines 157 to 168, the screen currently creates listState but does not collect
CommentsViewModel sideEffect(s) so CommentSideEffect.ScrollToItem is ignored
(and the ViewModel emit may suspend); add a LaunchedEffect that collects
commentsViewModel.sideEffect and when receiving CommentSideEffect.ScrollToItem
compute the target index with commentsUiState.comments.indexOfFirst {
it.commentId == effect.parentCommentId } (use 0 or skip if index < 0), then call
listState.animateScrollToItem(targetIndex); ensure this LaunchedEffect depends
on commentsViewModel.sideEffect and commentsUiState.comments so it runs when
data updates; optionally also trigger the same scroll when handling onReplyClick
alongside focusing the input.

Comment on lines +68 to +82
val focusRequester = remember { FocusRequester() }
val listState = rememberLazyListState()

val focusManager = LocalFocusManager.current

val isKeyboardVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0

LaunchedEffect(isKeyboardVisible) {
if (!isKeyboardVisible) {
replyingToCommentId = null
replyingToNickname = null
focusManager.clearFocus()
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

VM의 ScrollToItem SideEffect 수신/스크롤 처리 누락

ViewModel에서 발행하는 ScrollToItem을 수신하지 않아 답글 작성 후 해당 부모 댓글로의 이동이 이뤄지지 않습니다. 또, 수신자가 없으면 VM의 emit이 서스펜드될 수 있으므로 여기서 수집을 권장합니다.

아래 블록을 focusRequester, listState 정의 이후에 추가해 주세요:

LaunchedEffect(viewModel.sideEffect, uiState.comments) {
    viewModel.sideEffect.collect { effect ->
        when (effect) {
            is CommentSideEffect.ScrollToItem -> {
                val index = effect.parentCommentId?.let { id ->
                    uiState.comments.indexOfFirst { it.commentId == id }
                } ?: 0
                if (index >= 0) {
                    listState.animateScrollToItem(index)
                }
            }
        }
    }
}

Also applies to: 124-136

🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt
around lines 68-82 (and apply same for 124-136), the composable never collects
the ViewModel's ScrollToItem side effect so the list doesn't scroll to the
parent comment after replying and the emitter can suspend; add a LaunchedEffect
tied to viewModel.sideEffect and uiState.comments immediately after the
focusRequester and listState definitions that collects viewModel.sideEffect,
handles CommentSideEffect.ScrollToItem by finding the index of the
parentCommentId in uiState.comments (or 0 if null) and calls
listState.animateScrollToItem(index) when index >= 0 so the UI scrolls
correctly.

Comment on lines 146 to 164
CommentTextField(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
hint = stringResource(R.string.reply_to),
input = inputText,
onInputChange = { inputText = it },
onSendClick = {
onSendReply(
inputText,
replyingToCommentId,
replyingToNickname
viewModel.onEvent(
CommentsEvent.CreateComment(
content = inputText,
parentId = replyingToCommentId
)
)
inputText = ""
replyingToCommentId = null
replyingToNickname = null
focusManager.clearFocus()
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

빈 입력 전송 시 UX 결함: 입력/Reply 상태가 불필요하게 초기화됩니다

ViewModel의 createComment는 빈 문자열이면 즉시 return하지만, UI는 빈 입력에도 상태를 초기화합니다. 빈 입력이면 early-return 하세요.

다음 패치를 적용해 주세요:

                 onSendClick = {
-                        viewModel.onEvent(
+                        if (inputText.isBlank()) return@onSendClick
+                        viewModel.onEvent(
                             CommentsEvent.CreateComment(
                                 content = inputText,
                                 parentId = replyingToCommentId
                             )
                         )
                         inputText = ""
                         replyingToCommentId = null
                         replyingToNickname = null
                         focusManager.clearFocus()
                 },
📝 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
CommentTextField(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
hint = stringResource(R.string.reply_to),
input = inputText,
onInputChange = { inputText = it },
onSendClick = {
onSendReply(
inputText,
replyingToCommentId,
replyingToNickname
viewModel.onEvent(
CommentsEvent.CreateComment(
content = inputText,
parentId = replyingToCommentId
)
)
inputText = ""
replyingToCommentId = null
replyingToNickname = null
focusManager.clearFocus()
},
CommentTextField(
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
hint = stringResource(R.string.reply_to),
input = inputText,
onInputChange = { inputText = it },
onSendClick = {
// Prevent resetting state on blank submissions
if (inputText.isBlank()) return@onSendClick
viewModel.onEvent(
CommentsEvent.CreateComment(
content = inputText,
parentId = replyingToCommentId
)
)
inputText = ""
replyingToCommentId = null
replyingToNickname = null
focusManager.clearFocus()
},
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt
around lines 146 to 164, the UI currently clears inputText, replyingToCommentId,
replyingToNickname and clears focus even when the comment is empty; add an
early-return guard that checks if inputText.trim().isEmpty() and if so do
nothing (i.e. do not call viewModel.onEvent and do not reset
replying/input/focus), otherwise proceed to call CommentsEvent.CreateComment and
then clear inputText, replyingToCommentId, replyingToNickname and clear focus.

Comment on lines +45 to +47
private val _sideEffect = MutableSharedFlow<CommentSideEffect>()
val sideEffect = _sideEffect.asSharedFlow()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

SharedFlow emit이 수집자 부재 시 영구 서스펜드될 수 있습니다 → 버퍼/tryEmit로 전환 권장

현재 _sideEffect.emit(...)는 수집자가 없으면 서스펜드됩니다. 본 PR에는 어느 화면에서도 sideEffect.collect가 구현되어 있지 않아(아래 검증 스크립트 참고) 성공 후 코루틴이 멈출 수 있습니다. 버퍼를 주거나 tryEmit로 바꾸세요.

다음 패치를 권장합니다:

-    private val _sideEffect = MutableSharedFlow<CommentSideEffect>()
+    private val _sideEffect = MutableSharedFlow<CommentSideEffect>(
+        replay = 0,
+        extraBufferCapacity = 1
+    )
     val sideEffect = _sideEffect.asSharedFlow()
-                    _sideEffect.emit(CommentSideEffect.ScrollToItem(parentId))
+                    _sideEffect.tryEmit(CommentSideEffect.ScrollToItem(parentId))

다음 스크립트로 sideEffect.collect 수신 여부를 코드베이스 전역에서 확인해 주세요:

Also applies to: 166-167


🏁 Script executed:

#!/bin/bash
# sideEffect 수집 여부/위치 확인
rg -nP -C3 '\bsideEffect\s*\.\s*collect\s*\(' --type=kotlin

# MutableSharedFlow 설정 파라미터 확인
rg -nP -C2 'MutableSharedFlow\s*<\s*CommentSideEffect\s*>\s*\(' --type=kotlin

Length of output: 716


SharedFlow emit이 수집자 부재 시 영구 서스펜드됨 → extraBufferCapacity/tryEmit 적용 필요
전역 검색 결과 sideEffect.collect 호출이 전혀 없어 _sideEffect.emit(...) 시 코루틴이 멈출 위험이 있습니다. 아래와 같이 수정해주세요:

  • MutableSharedFlow 초기화 시 버퍼 용량 추가
  • emit 대신 tryEmit 사용
-    private val _sideEffect = MutableSharedFlow<CommentSideEffect>()
+    private val _sideEffect = MutableSharedFlow<CommentSideEffect>(
+        replay = 0,
+        extraBufferCapacity = 1
+    )
     val sideEffect = _sideEffect.asSharedFlow()
-                    _sideEffect.emit(CommentSideEffect.ScrollToItem(parentId))
+                    _sideEffect.tryEmit(CommentSideEffect.ScrollToItem(parentId))

적용 위치:

  • app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt (45–47, 166–167)
📝 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
private val _sideEffect = MutableSharedFlow<CommentSideEffect>()
val sideEffect = _sideEffect.asSharedFlow()
// In CommentsViewModel.kt around lines 45–47
private val _sideEffect = MutableSharedFlow<CommentSideEffect>(
replay = 0,
extraBufferCapacity = 1
)
val sideEffect = _sideEffect.asSharedFlow()
// …later in the same file around lines 166–167…
_sideEffect.tryEmit(CommentSideEffect.ScrollToItem(parentId))
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt
around lines 45–47 and 166–167, the MutableSharedFlow is created without a
buffer and the code calls emit which will suspend indefinitely if there are no
collectors; change the MutableSharedFlow initialization to include a small
buffer (e.g., MutableSharedFlow<CommentSideEffect>(extraBufferCapacity = 1)) and
replace _sideEffect.emit(...) calls with _sideEffect.tryEmit(...); after tryEmit
check the boolean result and handle failures (log or safely drop the event) so
producers never suspend when no collectors are present.

Comment on lines +34 to 36
viewModel: SplashViewModel = hiltViewModel(),
onNavigateToLogin: () -> Unit = {}
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

기본값에서 hiltViewModel() 호출은 Preview에서 크래시 유발 가능

기본 파라미터로 hiltViewModel()을 호출하면 Compose Preview 환경에서 LocalViewModelStoreOwner/HiltViewModelFactory 부재로 예외가 발생할 수 있습니다. 미리보기에서는 명시적으로 뷰모델을 전달하는 패턴이 안전합니다.

두 가지 대안 중 하나를 권장합니다:

  • 최소 변경: Preview에서만 명시적 ViewModel 전달(아래 73-75 라인 코멘트 참고).
  • 구조 개선: 파라미터를 nullable로 두고 내부에서 fallback 적용.

구조 개선 예시:

-fun SplashScreen(
-    viewModel: SplashViewModel = hiltViewModel(),
-    onNavigateToLogin: () -> Unit = {}
-) {
-    val navigateToLogin by viewModel.navigateToLogin.collectAsStateWithLifecycle()
+fun SplashScreen(
+    viewModel: SplashViewModel? = null,
+    onNavigateToLogin: () -> Unit = {}
+) {
+    // Preview 등 Hilt 미지원 컨텍스트 대비
+    val vm = viewModel ?: runCatching { hiltViewModel<SplashViewModel>() }.getOrNull() ?: SplashViewModel()
+    val navigateToLogin by vm.navigateToLogin.collectAsStateWithLifecycle()
📝 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
viewModel: SplashViewModel = hiltViewModel(),
onNavigateToLogin: () -> Unit = {}
) {
fun SplashScreen(
viewModel: SplashViewModel? = null,
onNavigateToLogin: () -> Unit = {}
) {
// Preview 등 Hilt 미지원 컨텍스트 대비
val vm = viewModel
?: runCatching { hiltViewModel<SplashViewModel>() }
.getOrNull()
?: SplashViewModel()
val navigateToLogin by vm.navigateToLogin.collectAsStateWithLifecycle()
// …rest of your UI logic remains unchanged…
}
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/signin/screen/SplashScreen.kt around
lines 34-36, calling hiltViewModel() as a default parameter can crash Compose
Preview due to missing Hilt/VM factory; change the function signature so the
viewModel parameter is nullable (e.g., viewModel: SplashViewModel? = null) and
inside the composable obtain a real instance with a fallback (val vm = viewModel
?: hiltViewModel()) before use, ensuring previews can pass an explicit mock and
runtime uses Hilt.

Comment on lines +1 to +23
package com.texthip.thip.ui.signin.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import jakarta.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

@HiltViewModel
class SplashViewModel @Inject constructor() : ViewModel() {
private val _navigateToLogin = MutableStateFlow(false)
val navigateToLogin = _navigateToLogin.asStateFlow()

init {
viewModelScope.launch {
delay(3000)
_navigateToLogin.value = true
}
}
} 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.

💡 Verification agent

🧩 Analysis chain

리포 내 jakarta.inject 잔존 여부 확인 권고 스크립트

다른 파일에도 jakarta.inject가 남아있을 수 있습니다. 아래 스크립트로 전체 레포를 점검해 주세요.


🏁 Script executed:

#!/bin/bash
# 레포 내 주입 어노테이션 사용 현황 점검
set -euo pipefail

echo "== jakarta.inject 사용 위치 =="
rg -nP --hidden --no-ignore-vcs '\bjakarta\.inject\.(Inject|Singleton|Named)\b' -g '!**/build/**' || true

echo
echo "== javax.inject 사용 위치 =="
rg -nP --hidden --no-ignore-vcs '\bjavax\.inject\.(Inject|Singleton|Named)\b' -g '!**/build/**' || true

Length of output: 6426


SplashViewModel.kt의 @Inject import 통일 필요

현재 리포 전반에서 javax.inject를 사용 중이며, 해당 파일만 jakarta.inject.Inject를 사용하고 있습니다. 아래처럼 수정해 주세요:

--- a/app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt
+++ b/app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt
@@
- import jakarta.inject.Inject
+ import javax.inject.Inject

만약 전체 코드를 jakarta.inject로 마이그레이션할 계획이라면, 잔존하는 javax.inject import도 모두 동일하게 변경해야 합니다.

📝 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
package com.texthip.thip.ui.signin.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import jakarta.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
@HiltViewModel
class SplashViewModel @Inject constructor() : ViewModel() {
private val _navigateToLogin = MutableStateFlow(false)
val navigateToLogin = _navigateToLogin.asStateFlow()
init {
viewModelScope.launch {
delay(3000)
_navigateToLogin.value = true
}
}
}
package com.texthip.thip.ui.signin.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
@HiltViewModel
class SplashViewModel @Inject constructor() : ViewModel() {
private val _navigateToLogin = MutableStateFlow(false)
val navigateToLogin = _navigateToLogin.asStateFlow()
init {
viewModelScope.launch {
delay(3000)
_navigateToLogin.value = true
}
}
}
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt
lines 1-23, the file imports jakarta.inject.Inject while the rest of the
repository uses javax.inject; replace the jakarta.inject.Inject import with
javax.inject.Inject so the project uses a consistent injection package (or if
you intend to migrate to jakarta.inject, update all other files to jakarta
instead) — update the import statement accordingly and ensure the class
annotation remains @HiltViewModel and constructor injection still works.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import jakarta.inject.Inject
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

Hilt 주입 어노테이션 import 오류: jakarta.inject.Injectjavax.inject.Inject로 교체 필요

Android/Hilt 환경에서는 javax.inject.Inject를 사용합니다. 현재 jakarta.inject.Inject는 일반적으로 종속성이 없고 Hilt와 호환되지 않아 컴파일/런타임 오류가 발생할 수 있습니다.

다음과 같이 수정해주세요:

-import jakarta.inject.Inject
+import javax.inject.Inject
📝 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
import jakarta.inject.Inject
import javax.inject.Inject
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt
around line 6, the Hilt injection import is wrong: replace the current import of
jakarta.inject.Inject with javax.inject.Inject so Hilt/Android DI resolves
correctly; update the import line accordingly and verify there are no other
jakarta.inject usages in this file or module that should be switched to
javax.inject.


init {
viewModelScope.launch {
delay(3000)
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

delay 인자 타입 불일치: 3000(Int) → 3000L(Long)

kotlinx.coroutines.delayLong을 요구합니다. 현재 코드는 컴파일 에러가 납니다.

-            delay(3000)
+            delay(3_000L)
📝 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
delay(3000)
delay(3_000L)
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt
around line 19, the call delay(3000) uses an Int literal but
kotlinx.coroutines.delay requires a Long; change the literal to a Long (e.g.,
delay(3000L)) or explicitly cast to Long so the code compiles.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
app/src/main/java/com/texthip/thip/ui/feed/component/OthersFeedCard.kt (2)

76-87: 좋아요 콜백이 누락되어 작동하지 않습니다

OthersFeedCardonLikeClick 파라미터가 ActionBarButton에 전달되지 않아 좋아요 클릭이 무시됩니다. 상위(FeedOthersScreen)에서 feedId를 캡처해 내려보내고 있으므로, 여기서 그대로 연결해야 합니다.

아래처럼 onLikeClick을 직접 전달하세요.

         ActionBarButton(
             isLiked = feedItem.isLiked,
             likeCount = feedItem.likeCount,
             commentCount = feedItem.commentCount,
             isSaveVisible = true,
-            onLikeClick = {
-//                onLikeClick(feedItem.feedId)
-            },
+            onLikeClick = onLikeClick,
             onBookmarkClick = {
 
             }
         )

95-102: Preview 타입 불일치: feedId가 Long이라면 1L 리터럴 사용 필요

PR 전반에서 creatorIdfeedId가 Long으로 이행되었습니다. PreviewfeedId = 1는 컴파일 오류(또는 암묵 캐스팅 실패) 가능성이 있습니다.

-        feedId = 1,
+        feedId = 1L,
🧹 Nitpick comments (3)
app/src/main/java/com/texthip/thip/ui/feed/component/OthersFeedCard.kt (2)

38-38: 카드 전체 클릭 처리: 액션 영역(좋아요/북마크)과의 제스처 충돌 가능성 점검 권장

부모 Column 전체에 clickable을 부여하면, 하위의 아이콘 버튼들이 클릭을 소비하지 못하는 경우 콘텐츠 클릭 네비게이션이 의도치않게 트리거될 수 있습니다(특히 새로 도입한 댓글 네비게이션 흐름에서 UX 이슈). 현재 ActionBarButton 내부가 확실히 클릭을 소비한다면 문제 없겠지만, 회귀 위험이 있어 분리 권장합니다.

최소 변경으로 부모의 clickable을 제거하고, 본문(Text/이미지) 구간에만 클릭을 부여하는 방안을 제안합니다.

         modifier = modifier
             .fillMaxWidth()
             .padding(horizontal = 20.dp)
-            .clickable { onContentClick() }

본문 블록 쪽(텍스트와 이미지 영역)에만 클릭을 추가하는 예시:

// Text 위에
Text(
    text = feedItem.contentBody,
    // ...
    modifier = Modifier
        .fillMaxWidth()
        .padding(vertical = 16.dp)
        .clickable { onContentClick() }
)

// 이미지가 있는 경우, 이미지 Row에도 클릭을 부여할지 정책에 따라 결정
if (hasImages) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(bottom = 16.dp)
            .clickable { onContentClick() },
        // ...
    ) { /* ... */ }
}

검증 포인트:

  • 좋아요/북마크 클릭 시 댓글 화면으로 이동하지 않는지
  • 본문/이미지 클릭 시에만 댓글 화면으로 이동하는지

40-44: 북마크 클릭 처리 미연결

onBookmarkClick이 비어 있습니다. 이번 PR 스코프 밖이라면 TODO로 명시하거나, 콜백을 파라미터로 받아서 상위에 위임하는 패턴 유지가 좋습니다(현재 onLikeClick과 동일한 구조).

app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt (1)

46-46: 수명주기 인식 수집으로 변경 제안: collectAsStateWithLifecycle()

Compose 화면 수명주기와의 정합을 위해 collectAsStateWithLifecycle 사용을 권장합니다. 화면 비가시 상태에서의 불필요한 수집을 방지합니다.

-import androidx.compose.runtime.collectAsState
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@
-    val uiState by viewModel.uiState.collectAsState()
+    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 38bf5fd and e61a43b.

📒 Files selected for processing (3)
  • app/src/main/java/com/texthip/thip/ui/feed/component/OthersFeedCard.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.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/navigator/navigations/FeedNavigation.kt
🔇 Additional comments (6)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt (6)

42-44: 새 네비게이션 콜백 추가 LGTM

onNavigateToSubscriptionList, onNavigateToFeedComment 추가 및 기본값 제공으로 호출 부담 최소화한 점 좋습니다. 상위 네비게이션 계층과의 연결 방향도 일관적입니다.


51-54: 콜백 전달은 적절하나, 하위 카드에서 실제 호출되는지 확인 필요

여기서 OthersFeedCardonLikeClick/onContentClick을 넘기지만, 카드 내부에서 좋아요 콜백이 호출되지 않으면 기능이 동작하지 않습니다. 현재 OthersFeedCardActionBarButtononLikeClick이 no-op 상태입니다. 제안한 수정대로 카드에서 콜백을 연결해 주세요.

검증 방법(수정 후):

  • 피드 항목의 좋아요를 눌렀을 때 viewModel.changeFeedLike(feedId)가 호출되는지 로그/디버거로 확인
  • 본문 클릭 시에만 onNavigateToFeedComment(feedId) 호출되는지 확인

61-64: 파라미터 구성 적절

FeedOthersContent가 화면-상호작용 콜백을 명확히 수신하도록 정리된 점 좋습니다. 기본값 없는 콜백(onLikeClick, onNavigateToSubscriptionList)은 강제 의존으로 설계되어 오용 여지를 줄입니다.


144-147: 본문 클릭 → 댓글 화면 네비게이션 연결 LGTM

리스트 아이템 본문 클릭 시 댓글 화면으로 이동하는 요구사항과 일치합니다. 스페이서/디바이더 처리도 적절합니다.


196-198: Preview 기본 콜백 주입 적절

미리보기에서 필수 콜백만 주입하고, 선택 콜백은 기본값으로 두어 간결하게 유지한 점 좋습니다.


105-106: 응답 모델의 creatorId가 여전히 Int로 정의되어 있음
userInfo.creatorId는 Long 타입으로 변경된 것이 확인되었으나, 아래 응답 모델에 여전히 Int로 선언된 creatorId가 존재합니다. 이들이 최종 userInfo로 매핑될 때 Long으로 변환되는지, 또는 해당 모델들의 creatorId 타입을 Long으로 변경해야 하는지 검토 부탁드립니다.

  • app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsDailyGreetingResponse.kt:
    val creatorId: Int
  • app/src/main/java/com/texthip/thip/data/model/feed/response/RelatedBooksResponse.kt:
    @SerialName("creatorId") val creatorId: Int

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
app/src/main/java/com/texthip/thip/ui/common/forms/WarningTextField.kt (5)

75-83: (선택) OutlinedTextFieldDefaults.colors 사용으로 속성 의도 명확화

현재 OutlinedTextField에 TextFieldDefaults.colors를 적용하고 indicator 컬러를 제어하고 있는데, Material3에서는 OutlinedTextFieldDefaults.colors의 border 컬러 파라미터(focusedBorderColor, unfocusedBorderColor)를 쓰는 편이 의도 전달과 유지보수에 더 명확합니다. 동작은 동일하게 가져가면서 API 의미를 맞출 수 있습니다.

적용 예(해당 범위 내 교체):

-                colors = TextFieldDefaults.colors(
-                    unfocusedTextColor = colors.White,
-                    focusedTextColor = colors.White,
-                    focusedIndicatorColor = if (showWarning) colors.Red else Color.Transparent,
-                    unfocusedIndicatorColor = if (showWarning) colors.Red else Color.Transparent,
-                    focusedContainerColor = containerColor,
-                    unfocusedContainerColor = containerColor,
-                    cursorColor = colors.NeonGreen
-                ),
+                colors = OutlinedTextFieldDefaults.colors(
+                    unfocusedTextColor = colors.White,
+                    focusedTextColor = colors.White,
+                    focusedBorderColor = if (showWarning) colors.Red else Color.Transparent,
+                    unfocusedBorderColor = if (showWarning) colors.Red else Color.Transparent,
+                    focusedContainerColor = containerColor,
+                    unfocusedContainerColor = containerColor,
+                    cursorColor = colors.NeonGreen
+                ),

추가(파일 상단 import, 기존 TextFieldDefaults import는 제거 가능):

import androidx.compose.material3.OutlinedTextFieldDefaults

46-49: 텍스트/배경 대비 확인 요청

텍스트 컬러를 White로 고정(focused/unfocused)했기 때문에, containerColor(기본 Black)가 밝은 색으로 오버라이드 되는 화면이 있다면 대비가 떨어질 수 있습니다. 실제 사용처에서 containerColor가 밝게 지정되는 케이스가 있는지 확인 부탁드립니다. 필요 시 theme의 onColor(contentColorFor)를 활용하는 방법도 고려해볼 수 있습니다.

Also applies to: 75-83


86-91: (선택) IconButton 사용으로 터치 타깃/접근성 개선

현재 Icon에 clickable을 직접 붙여 터치 타깃이 작습니다(보통 24dp 수준). IconButton을 사용하면 최소 터치 영역, 포커스, 리플 등 접근성/UX가 개선됩니다.

해당 범위 교체:

-                        Icon(
-                            painter = painterResource(id = R.drawable.ic_x_circle_grey),
-                            contentDescription = "Clear text",
-                            modifier = Modifier.clickable { onValueChange("") },
-                            tint = Color.Unspecified
-                        )
+                        IconButton(onClick = { onValueChange("") }) {
+                            Icon(
+                                painter = painterResource(id = R.drawable.ic_x_circle_grey),
+                                contentDescription = "Clear text",
+                                tint = Color.Unspecified
+                            )
+                        }

추가(import):

import androidx.compose.material3.IconButton

60-64: 숫자 필터링 범위 확인(로케일/유니코드 숫자 포함 여부)

Char.isDigit()는 전각/아라비아 숫자 등 유니코드 숫자 전반을 허용합니다. ASCII 숫자만 허용해야 하는 요구(예: PIN, 코드)가 있다면 '0'..'9' 범위로 한정하는 편이 안전합니다. 요구사항에 따라 확인 부탁드립니다. 필요 시 적용 코드 제안 가능합니다.


175-184: (선택) NumberPassword일 때 가시화 정책 결정

KeyboardType.NumberPassword를 사용하지만 visualTransformation이 없어 실제 입력이 마스킹되지 않습니다. 비밀번호 성격이라면 PasswordVisualTransformation() 적용 또는 isPassword 플래그 추가를 고려해 주세요. 현재 프리뷰/사용처 의도에 따라 선택 사항입니다.

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between e61a43b and 97f650a.

📒 Files selected for processing (1)
  • app/src/main/java/com/texthip/thip/ui/common/forms/WarningTextField.kt (1 hunks)
🔇 Additional comments (1)
app/src/main/java/com/texthip/thip/ui/common/forms/WarningTextField.kt (1)

75-83: unfocusedTextColor 추가 👍

포커스 해제 시에도 텍스트 컬러가 White로 유지되도록 한 수정은 UI 일관성과 가독성 측면에서 적절합니다. 실제 현행 스타일(컨테이너 Black)과도 잘 맞습니다.

@Nico1eKim Nico1eKim merged commit 595729a into THIP-TextHip:develop Aug 20, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant