Conversation
📝 WalkthroughWalkthrough프로필 화면에 섹션 기반 UI 상태와 이스터에그 탈퇴 흐름(easterEggWithdraw → AuthRepository.withdraw → AuthApi DELETE /api/v1/auth/withdraw → preferencesManager.clearAll → 앱 재시작) 및 관련 네비게이션·DTO·컴포넌트·레이아웃 변경이 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as ProfileScreen
participant VM as ProfileViewModel
participant Repo as AuthRepository
participant API as AuthApi
participant Prefs as PreferencesManager
participant MA as MainActivity
UI->>UI: 프로필 이미지 다중 클릭 (이스터에그)
UI->>VM: easterEggWithdraw()
VM->>Repo: withdraw()
Repo->>API: DELETE /api/v1/auth/withdraw
API-->>Repo: WithdrawResponseDto
Repo->>Prefs: clearAll()
Repo-->>VM: Result.Success
VM->>UI: ProfileSideEffect.WithdrawSuccess
UI->>UI: Context.findActivity()
UI->>MA: MainActivity.restartApplication()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@app/src/main/java/com/flint/data/api/AuthApi.kt`:
- Around line 24-25: The withdraw() API method currently returns
WithdrawResponseDto directly but must follow the project's standard response
envelope; change its return type to BaseResponse<WithdrawResponseDto> (or
BaseResponse<Unit> if the body contains no data) to match other methods like
signup and socialVerify, update the AuthApi.withdraw() signature and any related
call sites/imports to use BaseResponse, and ensure WithdrawResponseDto is used
only as the inner generic payload type.
In `@app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt`:
- Around line 79-81: Handle the case where (context.findActivity() as?
MainActivity) returns null in the ProfileSideEffect.WithdrawSuccess branch:
instead of silently doing nothing, detect the null, log the failure (include
context or class info) and perform a safe fallback such as showing a user-facing
message (e.g., Toast/snackbar) or navigating to the auth/login flow so the app
state isn't left inconsistent; ensure you invoke
MainActivity.restartApplication() only when the cast succeeds and otherwise run
the fallback and logging.
- Around line 207-218: SavedContentsSection is always rendered causing an empty
header when there are no saved items; wrap the item { ...
SavedContentsSection(...) } block with a conditional that checks
uiState.savedContents.contents.isNotEmpty() so it only renders when there are
saved contents, matching the behavior used for createCollections and
savedCollections.
In `@app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt`:
- Around line 91-99: The easterEggWithdraw flow lacks user feedback on failure
and runs without confirmation; update ProfileViewModel by adding a failure side
effect (e.g., ProfileSideEffect.WithdrawFailure that carries an error/message)
and emit it from the authRepository.withdraw().onFailure handler instead of just
Timber.e(it), and change the trigger path so that easterEggWithdraw only
performs authRepository.withdraw() after an explicit user confirmation from a
modal (use CollectionCreateContentDeleteModal or a new ConfirmWithdraw modal) —
keep the actual withdraw call in easterEggWithdraw (or rename to
performWithdraw) and expose a separate showWithdrawConfirm() action that the
View uses to present the dialog and call easterEggWithdraw/performWithdraw on
confirm.
🧹 Nitpick comments (2)
app/src/main/java/com/flint/domain/repository/AuthRepository.kt (1)
41-46:removeString(ACCESS_TOKEN)호출이 중복됩니다.
clearAll()이ACCESS_TOKEN을 포함한 모든 데이터를 삭제하므로 Line 44의removeString(ACCESS_TOKEN)호출은 불필요합니다.♻️ 중복 제거 제안
suspend fun withdraw(): Result<Unit> = suspendRunCatching { api.withdraw() - preferencesManager.removeString(ACCESS_TOKEN) preferencesManager.clearAll() }app/src/main/java/com/flint/presentation/MainActivity.kt (1)
65-72:restartApplication()공개 노출 시 주의 사항함수가 public으로 변경되어 외부에서 호출 가능해졌습니다. 이 변경은 Easter Egg 탈퇴 흐름을 지원하기 위해 필요하지만, 몇 가지 고려 사항이 있습니다:
- Line 68의
intent!!강제 언래핑은getLaunchIntentForPackage가 null을 반환할 경우 NPE를 발생시킬 수 있습니다.- 이 함수가 의도치 않게 호출되지 않도록 주의가 필요합니다.
♻️ null 안전성 개선 제안
fun restartApplication() { val packageManager: PackageManager = this.packageManager val intent = packageManager.getLaunchIntentForPackage(this.packageName) - val componentName = intent!!.component - val mainIntent = Intent.makeRestartActivityTask(componentName) - this.startActivity(mainIntent) - exitProcess(0) + intent?.component?.let { componentName -> + val mainIntent = Intent.makeRestartActivityTask(componentName) + this.startActivity(mainIntent) + exitProcess(0) + } }
| @DELETE("/api/v1/auth/withdraw") | ||
| suspend fun withdraw(): WithdrawResponseDto |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the AuthApi.kt file to see all methods and their return types
fd "AuthApi.kt" --type f --exec cat -n {}Repository: imflint/Flint-Android
Length of output: 1116
🏁 Script executed:
# Also search for WithdrawResponseDto to understand its structure
fd "WithdrawResponseDto" --type f --exec cat -n {}Repository: imflint/Flint-Android
Length of output: 420
🏁 Script executed:
# Search for BaseResponse usage patterns in API files
rg "BaseResponse|@DELETE|@POST|@GET" app/src/main/java/com/flint/data/api/ -A1 --type kotlinRepository: imflint/Flint-Android
Length of output: 8866
withdraw() 메서드의 응답 타입 래핑 불일치 수정 필요
withdraw() 함수는 WithdrawResponseDto를 직접 반환하지만, 다른 모든 API 메서드(signup, socialVerify 포함)와 전체 코드베이스의 패턴에서는 BaseResponse<T> 래핑을 사용합니다. WithdrawResponseDto는 status와 message 필드만 포함하는 응답 엔벨로프 구조이므로, 표준 API 응답 패턴에 맞게 BaseResponse<WithdrawResponseDto> 또는 BaseResponse<Unit>로 수정해야 합니다.
🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/data/api/AuthApi.kt` around lines 24 - 25, The
withdraw() API method currently returns WithdrawResponseDto directly but must
follow the project's standard response envelope; change its return type to
BaseResponse<WithdrawResponseDto> (or BaseResponse<Unit> if the body contains no
data) to match other methods like signup and socialVerify, update the
AuthApi.withdraw() signature and any related call sites/imports to use
BaseResponse, and ensure WithdrawResponseDto is used only as the inner generic
payload type.
There was a problem hiding this comment.
서버에서 data가 안와서 BaseResponse를 쓸 수 없음;
data를 nullable로 두기보다는 그냥 따로 관리하는 게 나을듯. 서버한테 지금 말하기 힘든 상황이니까 나중에 고칠게
There was a problem hiding this comment.
@nahy-512, 이해했습니다! 백엔드 API가 표준 응답 형식을 따르지 않는 상황이라면 현재 방식이 합리적입니다.
나중에 백엔드 팀과 조율이 가능할 때 통일된 응답 형식으로 수정하시면 좋을 것 같습니다. 필요하시다면 이 기술 부채를 추적하기 위한 이슈를 생성해드릴 수도 있습니다.
✏️ Learnings added
Learnt from: nahy-512
Repo: imflint/Flint-Android PR: 189
File: app/src/main/java/com/flint/data/api/AuthApi.kt:24-25
Timestamp: 2026-01-23T08:53:45.699Z
Learning: In `app/src/main/java/com/flint/data/api/AuthApi.kt`, the `withdraw()` endpoint returns `WithdrawResponseDto` directly instead of `BaseResponse<WithdrawResponseDto>` because the backend API does not follow the standard response envelope pattern for this specific endpoint. This is a known exception planned for future alignment with backend team.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| is ProfileSideEffect.WithdrawSuccess -> { | ||
| (context.findActivity() as? MainActivity)?.restartApplication() | ||
| } |
There was a problem hiding this comment.
안전한 캐스트 실패 시 사용자 피드백 누락 가능성
as? MainActivity가 실패할 경우 restartApplication()이 호출되지 않아 사용자가 회원탈퇴 후 불일치 상태에 놓일 수 있습니다. 캐스트 실패 시 최소한 로깅하거나 대체 동작을 수행하는 것이 좋습니다.
🐛 권장 수정안
is ProfileSideEffect.WithdrawSuccess -> {
- (context.findActivity() as? MainActivity)?.restartApplication()
+ val activity = context.findActivity() as? MainActivity
+ if (activity != null) {
+ activity.restartApplication()
+ } else {
+ // Fallback: 최소한 앱을 종료하거나 에러 로그 기록
+ android.util.Log.e("ProfileRoute", "MainActivity cast failed during withdrawal")
+ }
}🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt` around
lines 79 - 81, Handle the case where (context.findActivity() as? MainActivity)
returns null in the ProfileSideEffect.WithdrawSuccess branch: instead of
silently doing nothing, detect the null, log the failure (include context or
class info) and perform a safe fallback such as showing a user-facing message
(e.g., Toast/snackbar) or navigating to the auth/login flow so the app state
isn't left inconsistent; ensure you invoke MainActivity.restartApplication()
only when the cast succeeds and otherwise run the fallback and logging.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd "ProfileScreen.kt" --type fRepository: imflint/Flint-Android
Length of output: 209
🏁 Script executed:
wc -l "app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt"Repository: imflint/Flint-Android
Length of output: 134
🏁 Script executed:
cat -n "app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt" | head -230 | tail -100Repository: imflint/Flint-Android
Length of output: 4126
🏁 Script executed:
rg -n "fun SavedContentsSection" --type=kotlin -B2 -A20Repository: imflint/Flint-Android
Length of output: 2737
🏁 Script executed:
wc -l "app/src/main/java/com/flint/core/designsystem/component/listView/SavedContentsSection.kt"Repository: imflint/Flint-Android
Length of output: 157
🏁 Script executed:
cat -n "app/src/main/java/com/flint/core/designsystem/component/listView/SavedContentsSection.kt"Repository: imflint/Flint-Android
Length of output: 5000
SavedContentsSection에 빈 상태 조건부 렌더링 추가 필요
createCollections과 savedCollections 섹션은 리스트가 비어있을 때 렌더링을 건너뛰지만, SavedContentsSection은 항상 렌더링됩니다. SavedContentsSection 컴포넌트는 빈 상태를 내부적으로 처리하지 않으므로, 내용이 없을 때 헤더와 설명만 표시되는 빈 섹션이 나타납니다. 다른 섹션과의 일관성을 위해 if (uiState.savedContents.contents.isNotEmpty()) 조건을 추가하세요.
🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt` around
lines 207 - 218, SavedContentsSection is always rendered causing an empty header
when there are no saved items; wrap the item { ... SavedContentsSection(...) }
block with a conditional that checks uiState.savedContents.contents.isNotEmpty()
so it only renders when there are saved contents, matching the behavior used for
createCollections and savedCollections.
| fun easterEggWithdraw() = viewModelScope.launch { | ||
| authRepository.withdraw() | ||
| .onSuccess { | ||
| _sideEffect.emit(ProfileSideEffect.WithdrawSuccess) | ||
| } | ||
| .onFailure { | ||
| Timber.e(it) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for withdrawal/delete operations and confirmation dialogs
rg -n "withdraw|delete.*account|탈퇴" --type=kt -C 3Repository: imflint/Flint-Android
Length of output: 91
🏁 Script executed:
# Also check the ProfileSideEffect enum to see available side effects
fd "ProfileSideEffect" --type=kt -x cat -n {}Repository: imflint/Flint-Android
Length of output: 234
🏁 Script executed:
# Search for confirmation dialog implementations
rg -n "confirm|dialog|alert" --type=kt -i | head -50Repository: imflint/Flint-Android
Length of output: 91
🏁 Script executed:
# Look for similar account/auth critical operations
rg -n "onFailure|onSuccess" app/src/main/java/com/flint/presentation/profile/ --type=kt -C 2Repository: imflint/Flint-Android
Length of output: 91
🏁 Script executed:
# Find the ProfileViewModel file
find app -name "ProfileViewModel.kt" -o -name "*ProfileSideEffect*"Repository: imflint/Flint-Android
Length of output: 214
🏁 Script executed:
# Search for withdrawal-related code
rg -n "withdraw|delete" --max-count 20Repository: imflint/Flint-Android
Length of output: 561
🏁 Script executed:
# Search for confirmation dialogs in the codebase
rg -n "AlertDialog|ConfirmDialog|confirm" | head -30Repository: imflint/Flint-Android
Length of output: 776
🏁 Script executed:
# Look for onFailure patterns in profile-related code
rg -n "onFailure|onSuccess" app/src/main/java/com/flint/presentation/profile/ -C 2 | head -50Repository: imflint/Flint-Android
Length of output: 2596
🏁 Script executed:
# Examine ProfileSideEffect to see available side effects
cat -n app/src/main/java/com/flint/presentation/profile/sideeffect/ProfileSideEffect.ktRepository: imflint/Flint-Android
Length of output: 400
🏁 Script executed:
# Check how collection deletion is handled with confirmation
cat -n app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentDeleteModal.ktRepository: imflint/Flint-Android
Length of output: 1236
🏁 Script executed:
# Look at the profile screen to see if confirmation is used elsewhere
find app -name "*ProfileScreen*" -o -name "*ProfileView*"Repository: imflint/Flint-Android
Length of output: 278
🏁 Script executed:
# Search for other withdraw-related functions to understand the full context
rg -n "easterEgg|Easter" --max-count 10Repository: imflint/Flint-Android
Length of output: 844
🏁 Script executed:
# Examine ProfileTopSection to see how easterEggWithdraw is triggered
cat -n app/src/main/java/com/flint/presentation/profile/component/ProfileTopSection.kt | head -150Repository: imflint/Flint-Android
Length of output: 5817
🏁 Script executed:
# Check ProfileScreen for any confirmation dialogs
cat -n app/src/main/java/com/flint/presentation/profile/ProfileScreen.ktRepository: imflint/Flint-Android
Length of output: 12139
🏁 Script executed:
# Look for any withdrawal-related confirmation dialogs
rg -n "WithdrawSuccess|easterEgg" -B 5 -A 5Repository: imflint/Flint-Android
Length of output: 4481
탈퇴 실패 시 사용자 피드백 부재 및 확인 없이 실행되는 구조 개선 필요
현재 구현에서 두 가지 문제가 있습니다:
-
탈퇴 실패 시 UI 피드백 부재:
ProfileSideEffect에는WithdrawSuccess만 정의되어 있고 실패 케이스는Timber.e(it)로만 기록됩니다. 사용자가 탈퇴 성공 여부를 알 수 없습니다. -
확인 다이얼로그 없이 실행됨: 프로필 이미지를 5회 클릭하면 즉시
easterEggWithdraw()가 실행되며, 계정 삭제는 되돌릴 수 없는 중요한 작업이므로CollectionCreateContentDeleteModal같은 확인 다이얼로그가 필요합니다.
🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt` around
lines 91 - 99, The easterEggWithdraw flow lacks user feedback on failure and
runs without confirmation; update ProfileViewModel by adding a failure side
effect (e.g., ProfileSideEffect.WithdrawFailure that carries an error/message)
and emit it from the authRepository.withdraw().onFailure handler instead of just
Timber.e(it), and change the trigger path so that easterEggWithdraw only
performs authRepository.withdraw() after an explicit user confirmation from a
modal (use CollectionCreateContentDeleteModal or a new ConfirmWithdraw modal) —
keep the actual withdraw call in easterEggWithdraw (or rename to
performWithdraw) and expose a separate showWithdrawConfirm() action that the
View uses to present the dialog and call easterEggWithdraw/performWithdraw on
confirm.
There was a problem hiding this comment.
p3: clearAll만으로 충분하지 않을까요?
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt`:
- Around line 181-234: The when branch handling uiState.sectionData in
ProfileScreen.kt currently leaves the else branch empty so UiState.Error shows
nothing; replace the else with an explicit is UiState.Error branch that renders
an error state item (e.g., an error message and a retry button) and wire the
retry button to the existing onRefreshClick (or a new onRetry callback if
preferred) so users see feedback and can retry when sectionData is Error; locate
the when block around ProfileKeywordSection / CollectionSection /
SavedContentsSection and add the UiState.Error UI there.
♻️ Duplicate comments (2)
app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt (2)
86-93: 이전 리뷰에서 지적된 내용과 동일합니다.
MainActivity캐스트 실패 시 fallback 로직이 아직 구현되지 않았습니다. TODO 주석만 남아 있고 실제 대체 동작(예: 로그인 화면으로 이동, 토스트 메시지 표시 등)이 없어 회원탈퇴 후 앱이 불일치 상태에 놓일 수 있습니다.
219-230: 이전 리뷰에서 지적된 내용과 동일합니다.
SavedContentsSection은savedCollections와 달리 빈 상태 체크 없이 항상 렌더링됩니다. 내용이 없을 때 빈 섹션이 표시되는 일관성 문제가 있습니다.
| when (val sectionData = uiState.sectionData) { | ||
| is UiState.Loading -> { | ||
| item { | ||
| FlintLoadingIndicator( | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .padding(top = topHeightDp / 2) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| ProfileKeywordSection( | ||
| nickname = uiState.profile.nickname, | ||
| keywordList = uiState.keywords, | ||
| onRefreshClick = onRefreshClick, | ||
| modifier = Modifier.fillMaxWidth(), | ||
| ) | ||
| } | ||
| is UiState.Success -> { | ||
| item { | ||
| Spacer(Modifier.height(20.dp)) | ||
|
|
||
| item { | ||
| if (uiState.createCollections.collections.isNotEmpty()) { | ||
| Spacer(Modifier.height(48.dp)) | ||
|
|
||
| CollectionSection( | ||
| title = "${userName}님의 컬렉션", | ||
| description = "${userName}님이 생성한 컬렉션이에요", | ||
| onItemClick = onCollectionItemClick, | ||
| isAllVisible = true, | ||
| onAllClick = onCreatedCollectionMoreClick, | ||
| collectionListModel = uiState.createCollections, | ||
| ) | ||
| } | ||
| } | ||
| ProfileKeywordSection( | ||
| nickname = uiState.profile.nickname, | ||
| keywordList = sectionData.data.keywords, | ||
| onRefreshClick = onRefreshClick, | ||
| modifier = Modifier.fillMaxWidth(), | ||
| ) | ||
| } | ||
|
|
||
| item { | ||
| if (uiState.savedCollections.collections.isNotEmpty()) { | ||
| Spacer(Modifier.height(48.dp)) | ||
|
|
||
| CollectionSection( | ||
| title = "저장한 컬렉션", | ||
| description = "${userName}님이 저장한 컬렉션이에요", | ||
| onItemClick = onCollectionItemClick, | ||
| isAllVisible = true, | ||
| onAllClick = onSavedCollectionMoreClick, | ||
| collectionListModel = uiState.savedCollections, | ||
| ) | ||
| } | ||
| } | ||
| item { | ||
| if (sectionData.data.savedCollections.collections.isNotEmpty()) { | ||
| Spacer(Modifier.height(48.dp)) | ||
|
|
||
| item { | ||
| Spacer(Modifier.height(48.dp)) | ||
| CollectionSection( | ||
| title = "저장한 컬렉션", | ||
| description = "${userName}님이 저장한 컬렉션이에요", | ||
| onItemClick = onCollectionItemClick, | ||
| isAllVisible = true, | ||
| onAllClick = onSavedCollectionMoreClick, | ||
| collectionListModel = sectionData.data.savedCollections, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| item { | ||
| Spacer(Modifier.height(48.dp)) | ||
|
|
||
| SavedContentsSection( | ||
| title = "저장한 작품", | ||
| description = "${userName}님이 저장한 작품이에요", | ||
| contentModelList = sectionData.data.savedContents, | ||
| onItemClick = onContentItemClick, | ||
| isAllVisible = false, | ||
| onAllClick = {}, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| SavedContentsSection( | ||
| title = "저장한 작품", | ||
| description = "${userName}님이 저장한 작품이에요", | ||
| contentModelList = uiState.savedContents, | ||
| onItemClick = onContentItemClick, | ||
| isAllVisible = false, | ||
| onAllClick = {}, | ||
| else -> {} | ||
| } |
There was a problem hiding this comment.
UiState.Error 처리 누락
else 분기(line 233)가 비어 있어 sectionData가 Error 상태일 때 사용자에게 아무런 피드백이 표시되지 않습니다. 에러 상태에서 재시도 버튼이나 에러 메시지를 표시하는 것이 UX 측면에서 바람직합니다.
🐛 권장 수정안
- else -> {}
+ is UiState.Error -> {
+ item {
+ // 에러 UI 표시 (예: 재시도 버튼)
+ // ErrorSection(onRetryClick = onRefreshClick)
+ }
+ }
+
+ else -> {}🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt` around
lines 181 - 234, The when branch handling uiState.sectionData in
ProfileScreen.kt currently leaves the else branch empty so UiState.Error shows
nothing; replace the else with an explicit is UiState.Error branch that renders
an error state item (e.g., an error message and a retry button) and wire the
retry button to the existing onRefreshClick (or a new onRetry callback if
preferred) so users see feedback and can retry when sectionData is Error; locate
the when block around ProfileKeywordSection / CollectionSection /
SavedContentsSection and add the UiState.Error UI there.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt`:
- Around line 186-191: The loading indicator can "jump" because topHeightPx
starts at 0 and first composition applies padding(top = 0.dp) then recomposes;
update the logic around FlintLoadingIndicator to avoid using a zero initial
inset by either initializing topHeightPx/topHeightDp to a sensible default or
conditionally applying padding only when topHeightPx != 0 (e.g., skip or apply
an alternative layout while topHeightPx == 0) so the indicator's vertical
position is stable across the first frame; adjust references to
topHeightPx/topHeightDp and the FlintLoadingIndicator invocation accordingly.
♻️ Duplicate comments (3)
app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt (3)
88-95: 회원탈퇴 fallback 처리 미완성TODO 주석으로 fallback 처리가 필요함을 표시했지만, 실제 사용자 피드백이 없어 캐스트 실패 시 앱이 불일치 상태에 놓일 수 있습니다. 최소한 로그 기록이나 Toast 메시지를 추가하는 것을 권장합니다.
236-247: SavedContentsSection 빈 상태 조건부 렌더링 누락
createCollections과savedCollections섹션은 리스트가 비어있을 때 렌더링을 건너뛰지만,SavedContentsSection은 항상 렌더링됩니다. 일관성을 위해if (sectionData.data.savedContents.contents.isNotEmpty())조건을 추가하세요.
250-250: UiState.Error 처리 누락
else분기가 비어 있어sectionData가Error상태일 때 사용자에게 아무런 피드백이 표시되지 않습니다. 에러 상태에서 재시도 버튼이나 에러 메시지를 표시하는 것이 UX 측면에서 바람직합니다.
🧹 Nitpick comments (1)
app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt (1)
9-9: 사용되지 않는 import 정리 권장
Alignment,Color,Colorsimport가 추가되었으나 파일 내에서 직접 사용되지 않는 것으로 보입니다. 사용하지 않는 import는 제거하는 것이 좋습니다.Also applies to: 23-23, 41-41
| FlintLoadingIndicator( | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .padding(top = topHeightDp / 2) | ||
| ) | ||
| } |
There was a problem hiding this comment.
로딩 인디케이터 초기 위치 깜빡임 가능성
topHeightPx가 초기값 0으로 시작하여 첫 프레임에서는 padding(top = 0.dp)이 적용된 후 레이아웃 측정 완료 시 재구성됩니다. 로딩 상태가 짧으면 문제없지만, 긴 경우 인디케이터 위치가 순간적으로 튀어 보일 수 있습니다.
🐛 초기값 설정 제안
- var topHeightPx by remember { mutableIntStateOf(0) }
+ var topHeightPx by remember { mutableIntStateOf(306.dp.roundToPx()) } // ProfileTopSection 기본 높이또는 topHeightPx == 0일 때 별도 처리를 추가하세요.
🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt` around
lines 186 - 191, The loading indicator can "jump" because topHeightPx starts at
0 and first composition applies padding(top = 0.dp) then recomposes; update the
logic around FlintLoadingIndicator to avoid using a zero initial inset by either
initializing topHeightPx/topHeightDp to a sensible default or conditionally
applying padding only when topHeightPx != 0 (e.g., skip or apply an alternative
layout while topHeightPx == 0) so the indicator's vertical position is stable
across the first frame; adjust references to topHeightPx/topHeightDp and the
FlintLoadingIndicator invocation accordingly.
📮 관련 이슈
📌 작업 내용
프로필 QA 이슈 수정
회원탈퇴 API 연동 (이스터에그)
프로필 로딩 범위 수정
📸 스크린샷
ProfileScreen 구분

Screen_recording_20260123_172301.mp4
Screen_recording_20260123_172907.mp4
🫛 To. 리뷰어
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.