Skip to content

[Fix/#183] 프로필 QA 이슈 수정#189

Merged
nahy-512 merged 19 commits intodevelopfrom
fix/#183-profile-qa
Jan 23, 2026
Merged

[Fix/#183] 프로필 QA 이슈 수정#189
nahy-512 merged 19 commits intodevelopfrom
fix/#183-profile-qa

Conversation

@nahy-512
Copy link
Contributor

@nahy-512 nahy-512 commented Jan 23, 2026

📮 관련 이슈

📌 작업 내용

📸 스크린샷

ProfileScreen 구분
image

타인 프로필, UI 이슈 회원탈퇴 이스터에그
Screen_recording_20260123_172301.mp4
Screen_recording_20260123_172907.mp4

🫛 To. 리뷰어

Summary by CodeRabbit

  • 새로운 기능
    • 프로필에서 회원 탈퇴 흐름 추가 및 탈퇴 성공 시 앱 재시작 처리(이스터에그로 실행 가능)
  • UI
    • 프로필 레이아웃 개선(상단 바·뒤로가기 노출, 상단 영역 확대, 로딩 인디케이터 색상 적용), 키워드 칩·문구·간격 조정, 일부 목록 및 항목 패딩 개선
  • 네비게이션
    • 프로필 뒤로가기 연결 보완 및 스플래시 이동용 편의 내비게이션 추가
  • 기타
    • 프로필 상태 관리 구조 및 뷰모델·사이드이펙트 개선, 소소한 입력 필드/포커스 동작 개선

✏️ Tip: You can customize this high-level summary in your review settings.

@nahy-512 nahy-512 self-assigned this Jan 23, 2026
@nahy-512 nahy-512 requested a review from a team as a code owner January 23, 2026 07:10
@nahy-512 nahy-512 added Fix 🐛 버그, UI 오류, 오타, 명세와의 불일치 등을 수정 Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 labels Jan 23, 2026
@nahy-512 nahy-512 linked an issue Jan 23, 2026 that may be closed by this pull request
3 tasks
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

📝 Walkthrough

Walkthrough

프로필 화면에 섹션 기반 UI 상태와 이스터에그 탈퇴 흐름(easterEggWithdraw → AuthRepository.withdraw → AuthApi DELETE /api/v1/auth/withdraw → preferencesManager.clearAll → 앱 재시작) 및 관련 네비게이션·DTO·컴포넌트·레이아웃 변경이 추가되었습니다.

Changes

Cohort / File(s) Change Summary
Core extensions & primitives
app/src/main/java/com/flint/core/common/extension/ContextExt.kt, app/src/main/java/com/flint/core/designsystem/component/indicator/FlintLoadingIndicator.kt
Context.findActivity() 확장 추가; FlintLoadingIndicatormodifier 파라미터 추가 및 FlintTheme 색상 적용
Auth API & DTO
app/src/main/java/com/flint/data/api/AuthApi.kt, app/src/main/java/com/flint/data/dto/auth/response/WithdrawResponseDto.kt
@DELETE("/api/v1/auth/withdraw") suspend fun withdraw() 추가 및 WithdrawResponseDto @Serializable 데이터 클래스 추가
Auth repository
app/src/main/java/com/flint/domain/repository/AuthRepository.kt
suspend fun withdraw(): Result<Unit> 추가 — API 호출 후 preferencesManager.clearAll() 호출
Profile ViewModel & side-effects
app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt, app/src/main/java/com/flint/presentation/profile/sideeffect/ProfileSideEffect.kt
AuthRepository 주입, easterEggWithdraw() 추가; ProfileSideEffectsealed로 변경하고 WithdrawSuccess 추가; 섹션 기반 UI 상태 로직으로 재구성
Profile UI & components
app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt, app/src/main/java/com/flint/presentation/profile/component/ProfileTopSection.kt, app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordChip.kt, app/src/main/java/com/flint/presentation/profile/component/...
navigateUp/onBackClick 연결, Box+LazyColumn 레이아웃으로 변경, 프로필 이미지 다중클릭 이스터에그 추가(onEasterEggWithdraw), 높이·패딩·문구 조정 및 Preview 추가
Navigation & Main
app/src/main/java/com/flint/presentation/profile/navigation/ProfileNavigation.kt, app/src/main/java/com/flint/presentation/main/MainNavHost.kt, app/src/main/java/com/flint/presentation/splash/navigation/SplashNavigation.kt, app/src/main/java/com/flint/presentation/MainActivity.kt
profileNavGraphnavigateUp 파라미터 추가 및 전달, MainActivity.restartApplication() 공개화, NavController.navigateToSplash 확장 추가
UI state models
app/src/main/java/com/flint/presentation/profile/uistate/ProfileUiState.kt
ProfileUiState를 섹션 기반(sectionData: UiState<ProfileSectionData>)으로 재구성하고 ProfileSectionData 추가
Design system text fields
app/src/main/java/com/flint/core/designsystem/component/textfield/CollectionInputTextField.kt, app/src/main/java/com/flint/core/designsystem/component/textfield/FlintBasicTextField.kt
패딩/heightIn 조정 및 클릭 시 포커스 요청 추가; grapheme-aware maxLength 검증 추가
Layout tweaks
app/src/main/java/com/flint/presentation/collectionlist/component/CollectionFileItem.kt, app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordSection.kt
텍스트 컬럼에 수평 패딩 추가; 텍스트 오타("키워드에요" → "키워드예요") 수정

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()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

🔖 API

Suggested reviewers

  • giovannijunseokim

Poem

🐰 톡톡, 세 번 누르면 비밀 길이 보이네,
작은 클릭이 서버에 작별을 전하네,
기억은 비우고 새로 시작하는 밤,
당근 들고 토끼가 박수 치네 🥕✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive PR의 대부분 변경 사항은 이슈 #183의 프로필 QA 반영 범위 내에 있으나, 회원탈퇴 API 연동(이스터에그)과 FlintBasicTextField 포커싱 관련 변경은 이슈에 명시되지 않은 추가 범위입니다. 회원탈퇴 이스터에그 기능과 BasicTextField 포커싱 개선이 이슈 #183의 범위에 포함되어 있는지 확인하거나, 별도의 이슈로 분리하여 관리할 것을 권장합니다.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목 '[Fix/#183] 프로필 QA 이슈 수정'은 연결된 이슈 #183과 관련되며 변경 사항의 주요 내용(프로필 QA 이슈 수정)을 명확하게 설명합니다.
Description check ✅ Passed PR 설명은 템플릿 구조를 따르며 관련 이슈, 작업 내용, 스크린샷을 포함하고 있으며 주요 변경 사항이 적절하게 문서화되어 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경 사항들이 이슈 #183의 요구사항을 충족합니다: 타인 프로필 뒤로가기 버튼 추가, ProfileSmallKeywordChip 패딩 조정, 텍스트 오타 수정이 모두 구현되었습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

❤️ Share

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

Copy link
Contributor

@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: 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 탈퇴 흐름을 지원하기 위해 필요하지만, 몇 가지 고려 사항이 있습니다:

  1. Line 68의 intent!! 강제 언래핑은 getLaunchIntentForPackage가 null을 반환할 경우 NPE를 발생시킬 수 있습니다.
  2. 이 함수가 의도치 않게 호출되지 않도록 주의가 필요합니다.
♻️ 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)
+    }
 }

Comment on lines +24 to +25
@DELETE("/api/v1/auth/withdraw")
suspend fun withdraw(): WithdrawResponseDto
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 23, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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 kotlin

Repository: imflint/Flint-Android

Length of output: 8866


withdraw() 메서드의 응답 타입 래핑 불일치 수정 필요

withdraw() 함수는 WithdrawResponseDto를 직접 반환하지만, 다른 모든 API 메서드(signup, socialVerify 포함)와 전체 코드베이스의 패턴에서는 BaseResponse<T> 래핑을 사용합니다. WithdrawResponseDtostatusmessage 필드만 포함하는 응답 엔벨로프 구조이므로, 표준 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

서버에서 data가 안와서 BaseResponse를 쓸 수 없음;
data를 nullable로 두기보다는 그냥 따로 관리하는 게 나을듯. 서버한테 지금 말하기 힘든 상황이니까 나중에 고칠게

Copy link
Contributor

Choose a reason for hiding this comment

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

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

Comment on lines 79 to 81
is ProfileSideEffect.WithdrawSuccess -> {
(context.findActivity() as? MainActivity)?.restartApplication()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

안전한 캐스트 실패 시 사용자 피드백 누락 가능성

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.

Comment on lines 207 to 218
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd "ProfileScreen.kt" --type f

Repository: 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 -100

Repository: imflint/Flint-Android

Length of output: 4126


🏁 Script executed:

rg -n "fun SavedContentsSection" --type=kotlin -B2 -A20

Repository: 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에 빈 상태 조건부 렌더링 추가 필요

createCollectionssavedCollections 섹션은 리스트가 비어있을 때 렌더링을 건너뛰지만, 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.

Comment on lines +91 to +99
fun easterEggWithdraw() = viewModelScope.launch {
authRepository.withdraw()
.onSuccess {
_sideEffect.emit(ProfileSideEffect.WithdrawSuccess)
}
.onFailure {
Timber.e(it)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for withdrawal/delete operations and confirmation dialogs
rg -n "withdraw|delete.*account|탈퇴" --type=kt -C 3

Repository: 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 -50

Repository: 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 2

Repository: 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 20

Repository: imflint/Flint-Android

Length of output: 561


🏁 Script executed:

# Search for confirmation dialogs in the codebase
rg -n "AlertDialog|ConfirmDialog|confirm" | head -30

Repository: 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 -50

Repository: 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.kt

Repository: 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.kt

Repository: 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 10

Repository: 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 -150

Repository: 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.kt

Repository: imflint/Flint-Android

Length of output: 12139


🏁 Script executed:

# Look for any withdrawal-related confirmation dialogs
rg -n "WithdrawSuccess|easterEgg" -B 5 -A 5

Repository: imflint/Flint-Android

Length of output: 4481


탈퇴 실패 시 사용자 피드백 부재 및 확인 없이 실행되는 구조 개선 필요

현재 구현에서 두 가지 문제가 있습니다:

  1. 탈퇴 실패 시 UI 피드백 부재: ProfileSideEffect에는 WithdrawSuccess만 정의되어 있고 실패 케이스는 Timber.e(it)로만 기록됩니다. 사용자가 탈퇴 성공 여부를 알 수 없습니다.

  2. 확인 다이얼로그 없이 실행됨: 프로필 이미지를 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.

Copy link
Contributor

@giovannijunseokim giovannijunseokim left a comment

Choose a reason for hiding this comment

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

LGTM 🥇

Comment on lines 44 to 45
Copy link
Contributor

Choose a reason for hiding this comment

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

p3: clearAll만으로 충분하지 않을까요?

Copy link
Contributor

@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: 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: 이전 리뷰에서 지적된 내용과 동일합니다.

SavedContentsSectionsavedCollections와 달리 빈 상태 체크 없이 항상 렌더링됩니다. 내용이 없을 때 빈 섹션이 표시되는 일관성 문제가 있습니다.

Comment on lines 181 to 234
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 -> {}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

UiState.Error 처리 누락

else 분기(line 233)가 비어 있어 sectionDataError 상태일 때 사용자에게 아무런 피드백이 표시되지 않습니다. 에러 상태에서 재시도 버튼이나 에러 메시지를 표시하는 것이 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.

Copy link
Contributor

@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: 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 빈 상태 조건부 렌더링 누락

createCollectionssavedCollections 섹션은 리스트가 비어있을 때 렌더링을 건너뛰지만, SavedContentsSection은 항상 렌더링됩니다. 일관성을 위해 if (sectionData.data.savedContents.contents.isNotEmpty()) 조건을 추가하세요.


250-250: UiState.Error 처리 누락

else 분기가 비어 있어 sectionDataError 상태일 때 사용자에게 아무런 피드백이 표시되지 않습니다. 에러 상태에서 재시도 버튼이나 에러 메시지를 표시하는 것이 UX 측면에서 바람직합니다.

🧹 Nitpick comments (1)
app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt (1)

9-9: 사용되지 않는 import 정리 권장

Alignment, Color, Colors import가 추가되었으나 파일 내에서 직접 사용되지 않는 것으로 보입니다. 사용하지 않는 import는 제거하는 것이 좋습니다.

Also applies to: 23-23, 41-41

Comment on lines +186 to +191
FlintLoadingIndicator(
modifier = Modifier
.fillMaxSize()
.padding(top = topHeightDp / 2)
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

로딩 인디케이터 초기 위치 깜빡임 가능성

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.

@nahy-512 nahy-512 merged commit c42da08 into develop Jan 23, 2026
2 checks passed
@nahy-512 nahy-512 deleted the fix/#183-profile-qa branch January 23, 2026 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 Fix 🐛 버그, UI 오류, 오타, 명세와의 불일치 등을 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fix] 프로필 QA 반영

2 participants

Comments