Skip to content

Conversation

@m6z1
Copy link
Member

@m6z1 m6z1 commented Jan 10, 2026

📌𝘐𝘴𝘴𝘶𝘦𝘴

📎𝘞𝘰𝘳𝘬 𝘋𝘦𝘴𝘤𝘳𝘪𝘱𝘵𝘪𝘰𝘯

  • 마이페이지 ui 를 수정했습니다.

📷𝘚𝘤𝘳𝘦𝘦𝘯𝘴𝘩𝘰𝘵

스크린샷 2026-01-10 오후 2 29 05 스크린샷 2026-01-10 오후 2 29 30 스크린샷 2026-01-10 오후 2 28 54 스크린샷 2026-01-10 오후 3 09 21

💬𝘛𝘰 𝘙𝘦𝘷𝘪𝘦𝘸𝘦𝘳𝘴

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 마이페이지에 개인 도서관 통계 섹션 추가 (읽는 중, 읽음, 읽지 않음, 중단 상태별 책 수 표시)
    • 선호 장르 및 대표 장르 정보 시각화
    • 매력 포인트와 관련 키워드 표시
    • 장르 목록 토글 기능 추가
  • Style

    • 마이페이지 UI 레이아웃 개선 및 재구성

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

@m6z1 m6z1 changed the base branch from develop to feat/772 January 10, 2026 06:10
@github-actions github-actions bot requested a review from s9hn January 10, 2026 06:10
@coderabbitai
Copy link

coderabbitai bot commented Jan 10, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

마이페이지에 MyLibrary 기능을 통합하고 전체 UI를 개편합니다. MyPageFragment에 ViewModel 바인딩, 장르 선호도 어댑터, 이벤트 핸들러를 추가하고, 레이아웃을 재구성하여 라이브러리 통계, 선호 장르, 키워드 칩을 표시하는 기능을 구현합니다.

Changes

Cohort / File(s) 변경 사항 요약
Fragment & ViewModel 통합
app/src/main/java/com/into/websoso/ui/main/myPage/MyPageFragment.kt, app/src/main/java/com/into/websoso/ui/main/myPage/myLibrary/model/MyLibraryUiState.kt
MyPageFragment에 MyLibraryViewModel 바인딩, 장르 선호도 어댑터, 저장소 네비게이션 추가. MyLibraryUiState에 totalBadgeCount 계산 프로퍼티 신규 구현. UI 상태 옵저버 로직으로 로딩/에러 상태, 장르/소설 섹션 가시성 제어. 키워드 칩, 주요 장르 이미지 로딩 등 UI 헬퍼 메서드 다수 추가.
리소스 & 레이아웃
app/src/main/res/drawable/btn_profile_edit.xml, app/src/main/res/layout/fragment_my_page.xml
프로필 편집 아이콘 벡터 드로어블 신규 추가. Fragment 레이아웃을 AppBar 기반에서 Toolbar 기반으로 재구성. 사용자 프로필 영역, 라이브러리 통계 섹션(흥미, 감시 중, 완료, 중단), 주요 장르 디스플레이, 장르 선호도 블록 등을 추가하며 TabLayout 제거.
문자열 리소스
core/resource/src/main/res/values/strings.xml
my\_page\_library, my\_page\_activity 제거. my\_page\_taste\_title, my\_page\_badge 신규 추가.

Sequence Diagram

sequenceDiagram
    participant Fragment as MyPageFragment
    participant ViewModel as MyLibraryViewModel
    participant Observer as Observer(uiState)
    participant UI as UI Components

    Fragment->>ViewModel: viewModels() 초기화
    Fragment->>Fragment: onViewCreated에서 binding 설정
    Fragment->>ViewModel: uiState 옵저버 등록
    
    ViewModel->>Observer: uiState 발행
    Observer->>UI: handleLibraryLoadingState()<br/>(로딩/에러 상태 처리)
    Observer->>UI: updateRestGenrePreferenceVisibility()<br/>(장르 리스트 토글)
    Observer->>UI: applyTextColors()<br/>(매력 포인트 텍스트 스타일링)
    Observer->>UI: updateNovelPreferencesKeywords()<br/>(키워드 칩 추가)
    Observer->>UI: updateDominantGenres()<br/>(주요 장르 이미지 로드)
    Observer->>UI: updateGenreBadgeTitle()<br/>(배지 카운트 색상 지정)
    
    Fragment->>UI: onStorageButtonClick() 이벤트
    UI->>Fragment: FragmentResult로 라이브러리 네비게이션
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Suggested reviewers

  • junseo511
  • yeonjeen

Poem

🐰 내 페이지가 예뻐졌어요,
장르와 키워드가 춤을 춰요,
ViewModel 손을 맞잡고,
UI는 반짝반짝 빛나고,
마이라이브러리와 함께 노래해요! 🎵✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
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.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 마이페이지 UI 수정이라는 주요 변경사항을 명확하게 반영하고 있습니다.
Description check ✅ Passed PR 설명이 템플릿 구조를 따르고 이슈 번호, 작업 설명, 스크린샷을 포함하고 있습니다.
Linked Issues check ✅ Passed 변경사항이 마이페이지 모듈 분리 및 UI 수정이라는 이슈 #773의 목표를 충족하고 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 마이페이지 UI 수정과 관련된 범위 내에 있으며, 범위를 벗어난 변경은 없습니다.

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


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.

@m6z1 m6z1 added 🍯 [FEAT] 새로운 기능을 개발합니다. 🏹 궁사 명지 웹소소 공주의 은밀한 사냥생활 labels Jan 10, 2026
Copy link

@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: 5

🤖 Fix all issues with AI agents
In @app/src/main/java/com/into/websoso/ui/main/myPage/MyPageFragment.kt:
- Around line 236-270: The current logic uses combinedText.split(fixedText) and
then checks splitText.isNotEmpty(), but split always returns a non-empty list so
the else branch never runs; change the guard to check whether fixedText is
actually present (e.g., combinedText.contains(fixedText) or
combinedText.indexOf(fixedText) >= 0) before splitting, and only call split and
build the two SpannableStrings when fixedText exists; otherwise create a single
SpannableString from combinedText and append it to spannableStringBuilder
(referencing combinedText, fixedText, splitText, and spannableStringBuilder in
MyPageFragment).
- Around line 304-314: updateDominantGenres currently assumes topGenres has 3
items which can leave stale images or throw IndexOutOfBoundsException; update
the function to safely handle lists smaller than 3 by first clearing or setting
a placeholder on binding.ivMyLibraryDominantGenreFirstLogo, SecondLogo, and
ThirdLogo, then iterate topGenres with forEachIndexed to compute
updatedGenreImageUrl via binding.root.getS3ImageUrl and call load only for
present indices (use the same when(index) branches you have), leaving the absent
indexes explicitly cleared/placeholdered to avoid stale images and exceptions.

In @app/src/main/res/drawable/btn_profile_edit.xml:
- Around line 1-14: The drawable btn_profile_edit.xml is unused and should be
removed; if you intend to keep it, replace the hardcoded color literals
(#ffffff, #DDDDE3, #949399) with color resource references (e.g., @color/...)
and update any consumers to use the resource variants; locate
btn_profile_edit.xml (the <vector> with two <path> elements) and either delete
the file from the repo or change the android:fillColor and android:strokeColor
attributes to reference defined colors in colors.xml, then run a project-wide
search to confirm no references remain before committing.

In @app/src/main/res/layout/fragment_my_page.xml:
- Around line 333-346: The layout directly indexes
myLibraryViewModel.uiState.topGenres[0] (and similar indexes), which can throw
IndexOutOfBoundsException when the list has fewer than three items; fix by
exposing safe nullable accessors on the UI state (e.g., add
firstGenre/secondGenre/thirdGenre properties that use
topGenres.getOrNull(index)) or implement a binding adapter like safeGet to
return null/default when out of range, and update the TextView bindings to use
those safe properties (e.g., myLibraryViewModel.uiState.firstGenre?.genreName
and firstGenre?.genreCount) so the layout never directly indexes the list.
🧹 Nitpick comments (2)
app/src/main/res/layout/fragment_my_page.xml (2)

156-175: TextView에서 불필요한 layout_weight 속성

wrap_content 너비를 가진 TextView에 layout_weight="1"이 설정되어 있습니다. 부모 LinearLayout에 이미 layout_weight가 적용되어 있으므로, 자식 TextView의 layout_weight는 불필요하며 제거해도 됩니다.

♻️ 제안하는 수정 (예시: 첫 번째 섹션)
                                <TextView
                                    android:id="@+id/tv_my_linear_storage_interesting_count"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
-                                   android:layout_weight="1"
                                    android:text='@{String.valueOf(myLibraryViewModel.uiState.novelStats.interestNovelCount)}'
                                    android:textAppearance="@style/title2"
                                    android:textColor="@color/black"
                                    tools:text="12" />

                                <TextView
                                    android:id="@+id/tv_my_linear_storage_interesting_title"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginTop="2dp"
-                                   android:layout_weight="1"
                                    android:text="@string/my_library_interesting"
                                    android:textAppearance="@style/body5"
                                    android:textColor="@color/black" />

58-67: 접근성: 클릭 가능한 ImageView에 contentDescription 누락

설정 아이콘 등 클릭 가능한 ImageView에 contentDescription이 없습니다. 스크린 리더 사용자를 위해 추가해 주세요.

♻️ 제안하는 수정
                        <ImageView
                            android:id="@+id/iv_my_page_sticky_go_to_setting"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginEnd="12dp"
                            android:padding="10dp"
                            android:src="@drawable/ic_settings"
+                           android:contentDescription="@string/setting_title"
                            app:layout_constraintBottom_toBottomOf="parent"
                            app:layout_constraintEnd_toEndOf="parent"
                            app:layout_constraintTop_toTopOf="parent" />
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eae3f8b and 5a6cc81.

📒 Files selected for processing (5)
  • app/src/main/java/com/into/websoso/ui/main/myPage/MyPageFragment.kt
  • app/src/main/java/com/into/websoso/ui/main/myPage/myLibrary/model/MyLibraryUiState.kt
  • app/src/main/res/drawable/btn_profile_edit.xml
  • app/src/main/res/layout/fragment_my_page.xml
  • core/resource/src/main/res/values/strings.xml
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/com/into/websoso/ui/main/myPage/MyPageFragment.kt (1)
app/src/main/java/com/into/websoso/ui/main/myPage/myLibrary/MyLibraryFragment.kt (7)
  • setupRestGenrePreferenceAdapter (52-54)
  • onStorageButtonClick (197-209)
  • updateRestGenrePreferenceVisibility (114-119)
  • updateNovelPreferencesKeywords (168-183)
  • updateDominantGenres (211-221)
  • applyTextColors (121-166)
  • createKeywordChip (185-195)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (8)
app/src/main/java/com/into/websoso/ui/main/myPage/myLibrary/model/MyLibraryUiState.kt (1)

16-19: LGTM!

totalBadgeCount 계산 로직이 명확하고 올바릅니다. sumOf를 사용한 관용적인 Kotlin 코드입니다.

core/resource/src/main/res/values/strings.xml (1)

218-219: LGTM!

새로운 문자열 리소스가 적절하게 추가되었습니다. my_page_badge%d 포맷 지정자가 올바르게 사용되었습니다.

app/src/main/java/com/into/websoso/ui/main/myPage/MyPageFragment.kt (4)

50-56: LGTM!

ViewModel과 어댑터의 초기화가 적절합니다. viewModels()를 사용한 fragment-scoped ViewModel과 lazy 초기화 패턴이 올바르게 적용되었습니다.


275-290: LGTM!

키워드 중복 방지 로직이 올바르게 구현되었습니다. mutableSetOf을 사용한 기존 키워드 추적이 효율적입니다.


316-337: LGTM!

뱃지 타이틀의 숫자 부분에 색상을 적용하는 로직이 올바르게 구현되었습니다. SpannableStringBuilderForegroundColorSpan 사용이 적절합니다.


349-358: LGTM!

throttleFirst를 사용한 중복 클릭 방지와 FragmentResult를 통한 네비게이션 패턴이 적절합니다.

app/src/main/res/layout/fragment_my_page.xml (2)

12-15: LGTM!

myLibraryViewModel 바인딩 변수가 올바르게 추가되었습니다.


73-82: LGTM!

NestedScrollViewConstraintLayout을 사용한 레이아웃 구조가 적절합니다. fillViewport="true"overScrollMode="never" 설정이 올바르게 적용되었습니다.

Comment on lines 129 to +191
private fun setupObserver() {
myPageViewModel.uiState.observe(viewLifecycleOwner) { uiState ->
when {
uiState.loading -> binding.wllMyPage.setWebsosoLoadingVisibility(true)
uiState.error -> binding.wllMyPage.setErrorLayoutVisibility(true)
uiState.loading -> {
binding.wllMyPage.setWebsosoLoadingVisibility(true)
}

uiState.error -> {
binding.wllMyPage.setErrorLayoutVisibility(true)
}

!uiState.loading -> {
binding.wllMyPage.setErrorLayoutVisibility(false)
binding.wllMyPage.setWebsosoLoadingVisibility(false)
setUpMyProfileImage(uiState.myProfile?.avatarImage.orEmpty())
}
}
}

myLibraryViewModel.uiState.observe(viewLifecycleOwner) { uiState ->
handleLibraryLoadingState(uiState)

if (!uiState.isLoading && !uiState.isError) {
val hasNoGenre = myLibraryViewModel.hasNoPreferences()
val hasNoNovel = !myLibraryViewModel.hasNovelPreferences()

if (hasNoGenre) {
binding.clMyLibraryGenrePreference.isVisible = false
binding.clMyLibraryUnknownPreference.isVisible = true

binding.viewMyPageNovelPreferenceDivider.isVisible = false
binding.clMyLibraryNovelPreference.isVisible = false
binding.clMyLibraryUnknownNovelPreference.isVisible = false
} else {
binding.clMyLibraryGenrePreference.isVisible = true
binding.clMyLibraryUnknownPreference.isVisible = false

binding.viewMyPageNovelPreferenceDivider.isVisible = true

if (hasNoNovel) {
binding.clMyLibraryNovelPreference.isVisible = false
binding.clMyLibraryUnknownNovelPreference.isVisible = true
} else {
binding.clMyLibraryNovelPreference.isVisible = true
binding.clMyLibraryUnknownNovelPreference.isVisible = false
}
}

binding.clMyLibraryAttractivePoints.isVisible =
myLibraryViewModel.hasAttractivePoints()

restGenrePreferenceAdapter.updateRestGenrePreferenceData(uiState.restGenres)
updateRestGenrePreferenceVisibility(uiState.isGenreListVisible)
uiState.novelPreferences?.let { updateNovelPreferencesKeywords(it) }
updateDominantGenres(uiState.topGenres)
updateGenreBadgeTitle(uiState.totalBadgeCount)

applyTextColors(
uiState.translatedAttractivePoints.joinToString(", ") +
getString(my_library_attractive_point_fixed_text),
)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

두 ViewModel의 로딩 상태 간 경쟁 조건 가능성

myPageViewModelmyLibraryViewModel 모두 동일한 wllMyPage 로딩 레이아웃을 제어합니다. 두 옵저버가 독립적으로 로딩 상태를 설정하므로, 한 ViewModel이 로딩을 완료했지만 다른 ViewModel이 아직 로딩 중인 경우 UI 깜빡임이나 불일치가 발생할 수 있습니다.

두 ViewModel의 로딩 상태를 결합하여 처리하는 것을 고려해 보세요:

// 예시: 두 상태를 결합하여 로딩 표시
val isLoading = myPageUiState.loading || myLibraryUiState.isLoading
binding.wllMyPage.setWebsosoLoadingVisibility(isLoading)

Comment on lines +236 to +270
val splitText = combinedText.split(fixedText)

if (splitText.isNotEmpty()) {
val attractivePoints =
SpannableString(splitText[0]).apply {
setSpan(
ForegroundColorSpan(primary100),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(attractivePoints)

val fixedSpannable =
SpannableString(fixedText).apply {
setSpan(
ForegroundColorSpan(gray300),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(fixedSpannable)
} else {
val spannable = SpannableString(combinedText).apply {
setSpan(
ForegroundColorSpan(primary100),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(spannable)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

split 결과 확인 로직 개선 필요

combinedText.split(fixedText)fixedText가 없어도 빈 리스트를 반환하지 않습니다. splitText.isNotEmpty() 조건은 항상 true이므로 else 분기가 실행되지 않습니다.

fixedText가 실제로 포함되어 있는지 확인하려면:

♻️ 제안하는 수정
-        if (splitText.isNotEmpty()) {
+        if (combinedText.contains(fixedText) && splitText.size > 1) {
📝 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
val splitText = combinedText.split(fixedText)
if (splitText.isNotEmpty()) {
val attractivePoints =
SpannableString(splitText[0]).apply {
setSpan(
ForegroundColorSpan(primary100),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(attractivePoints)
val fixedSpannable =
SpannableString(fixedText).apply {
setSpan(
ForegroundColorSpan(gray300),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(fixedSpannable)
} else {
val spannable = SpannableString(combinedText).apply {
setSpan(
ForegroundColorSpan(primary100),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(spannable)
}
val splitText = combinedText.split(fixedText)
if (combinedText.contains(fixedText) && splitText.size > 1) {
val attractivePoints =
SpannableString(splitText[0]).apply {
setSpan(
ForegroundColorSpan(primary100),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(attractivePoints)
val fixedSpannable =
SpannableString(fixedText).apply {
setSpan(
ForegroundColorSpan(gray300),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(fixedSpannable)
} else {
val spannable = SpannableString(combinedText).apply {
setSpan(
ForegroundColorSpan(primary100),
0,
length,
SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
spannableStringBuilder.append(spannable)
}
🤖 Prompt for AI Agents
In @app/src/main/java/com/into/websoso/ui/main/myPage/MyPageFragment.kt around
lines 236 - 270, The current logic uses combinedText.split(fixedText) and then
checks splitText.isNotEmpty(), but split always returns a non-empty list so the
else branch never runs; change the guard to check whether fixedText is actually
present (e.g., combinedText.contains(fixedText) or
combinedText.indexOf(fixedText) >= 0) before splitting, and only call split and
build the two SpannableStrings when fixedText exists; otherwise create a single
SpannableString from combinedText and append it to spannableStringBuilder
(referencing combinedText, fixedText, splitText, and spannableStringBuilder in
MyPageFragment).

Comment on lines +304 to +314
private fun updateDominantGenres(topGenres: List<GenrePreferenceEntity>) {
topGenres.forEachIndexed { index, genrePreferenceEntity ->
val updatedGenreImageUrl = binding.root.getS3ImageUrl(genrePreferenceEntity.genreImage)

when (index) {
0 -> binding.ivMyLibraryDominantGenreFirstLogo.load(updatedGenreImageUrl)
1 -> binding.ivMyLibraryDominantGenreSecondLogo.load(updatedGenreImageUrl)
2 -> binding.ivMyLibraryDominantGenreThirdLogo.load(updatedGenreImageUrl)
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

topGenres가 3개 미만일 때 처리 필요

topGenres 리스트의 크기가 3 미만인 경우, 업데이트되지 않는 ImageView에 이전 이미지가 남아있을 수 있습니다. 또한 레이아웃에서 topGenres[0], topGenres[1], topGenres[2]를 직접 바인딩하고 있어 IndexOutOfBoundsException이 발생할 수 있습니다.

♻️ 제안하는 수정
 private fun updateDominantGenres(topGenres: List<GenrePreferenceEntity>) {
+    // Clear previous images
+    binding.ivMyLibraryDominantGenreFirstLogo.setImageDrawable(null)
+    binding.ivMyLibraryDominantGenreSecondLogo.setImageDrawable(null)
+    binding.ivMyLibraryDominantGenreThirdLogo.setImageDrawable(null)
+    
     topGenres.forEachIndexed { index, genrePreferenceEntity ->
         val updatedGenreImageUrl = binding.root.getS3ImageUrl(genrePreferenceEntity.genreImage)

         when (index) {
             0 -> binding.ivMyLibraryDominantGenreFirstLogo.load(updatedGenreImageUrl)
             1 -> binding.ivMyLibraryDominantGenreSecondLogo.load(updatedGenreImageUrl)
             2 -> binding.ivMyLibraryDominantGenreThirdLogo.load(updatedGenreImageUrl)
         }
     }
 }
🤖 Prompt for AI Agents
In @app/src/main/java/com/into/websoso/ui/main/myPage/MyPageFragment.kt around
lines 304 - 314, updateDominantGenres currently assumes topGenres has 3 items
which can leave stale images or throw IndexOutOfBoundsException; update the
function to safely handle lists smaller than 3 by first clearing or setting a
placeholder on binding.ivMyLibraryDominantGenreFirstLogo, SecondLogo, and
ThirdLogo, then iterate topGenres with forEachIndexed to compute
updatedGenreImageUrl via binding.root.getS3ImageUrl and call load only for
present indices (use the same when(index) branches you have), leaving the absent
indexes explicitly cleared/placeholdered to avoid stale images and exceptions.

Comment on lines +1 to +14
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="27dp"
android:height="27dp"
android:viewportWidth="27"
android:viewportHeight="27">
<path
android:strokeWidth="1"
android:pathData="M13.481,0.5C20.608,0.5 26.5,6.378 26.5,13.494C26.5,20.621 20.621,26.5 13.494,26.5C6.379,26.5 0.5,20.621 0.5,13.494C0.5,6.38 6.366,0.5 13.481,0.5Z"
android:fillColor="#ffffff"
android:strokeColor="#DDDDE3"/>
<path
android:pathData="M18.208,11.335L19.62,9.925C20.319,9.221 20.345,8.451 19.711,7.799L19.205,7.277C18.571,6.637 17.781,6.703 17.095,7.394L15.683,8.79L18.208,11.335ZM10.644,18.926L17.485,12.032L14.973,9.514L8.131,16.381L6.862,19.461C6.72,19.852 7.121,20.283 7.51,20.139L10.644,18.926Z"
android:fillColor="#949399"/>
</vector>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if btn_profile_edit drawable is used anywhere in the codebase
rg -n "btn_profile_edit" --type=xml --type=kotlin

Repository: Team-WSS/WSS-Android

Length of output: 46


🏁 Script executed:

#!/bin/bash
# Broader search for btn_profile_edit with various patterns
echo "=== Searching all file types for btn_profile_edit ==="
rg -n "btn_profile_edit" --max-count=20

echo -e "\n=== Checking if drawable file exists ==="
find . -name "btn_profile_edit.xml" -type f

echo -e "\n=== Searching for profile_edit pattern ==="
rg -n "profile_edit" --type=xml --type=kotlin | head -30

echo -e "\n=== Checking drawable references in my_page files ==="
rg -n "ic_my_page_edit_profile\|edit.*profile" --type=xml --type=kotlin | head -30

Repository: Team-WSS/WSS-Android

Length of output: 3799


미사용 drawable 삭제 필요

이 drawable 파일은 코드베이스의 어떤 곳에서도 참조되지 않습니다. 사용되지 않는 파일이므로 삭제를 권장합니다.

또한 색상값(#ffffff, #DDDDE3, #949399)이 하드코딩되어 있습니다. 유지보수성을 위해 향후에는 색상값을 리소스로 정의하여 @color 참조를 사용해주세요.

🤖 Prompt for AI Agents
In @app/src/main/res/drawable/btn_profile_edit.xml around lines 1 - 14, The
drawable btn_profile_edit.xml is unused and should be removed; if you intend to
keep it, replace the hardcoded color literals (#ffffff, #DDDDE3, #949399) with
color resource references (e.g., @color/...) and update any consumers to use the
resource variants; locate btn_profile_edit.xml (the <vector> with two <path>
elements) and either delete the file from the repo or change the
android:fillColor and android:strokeColor attributes to reference defined colors
in colors.xml, then run a project-wide search to confirm no references remain
before committing.

Comment on lines +333 to +346
android:text="@{myLibraryViewModel.uiState.topGenres[0].genreName}"
android:textAppearance="@style/title3"
android:textColor="@color/black"
tools:text="로판" />

<TextView
android:id="@+id/tv_my_library_dominant_genre_first_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text='@{String.format(@string/my_library_genre_count, myLibraryViewModel.uiState.topGenres[0].genreCount)}'
android:textAppearance="@style/body5"
android:textColor="@color/gray_200_949399"
tools:text="12편" />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

topGenres 배열 직접 접근 시 IndexOutOfBoundsException 발생 가능

데이터 바인딩에서 topGenres[0], topGenres[1], topGenres[2]에 직접 접근하고 있습니다. topGenres 리스트의 크기가 3 미만인 경우 런타임에 IndexOutOfBoundsException이 발생할 수 있습니다.

안전한 접근을 위해 safeGet 바인딩 어댑터를 사용하거나, ViewModel에서 개별 장르 속성을 제공하는 것을 권장합니다:

♻️ ViewModel에서 안전한 속성 제공 예시
// MyLibraryUiState 또는 MyLibraryViewModel에서
val firstGenre: GenrePreferenceEntity? get() = topGenres.getOrNull(0)
val secondGenre: GenrePreferenceEntity? get() = topGenres.getOrNull(1)
val thirdGenre: GenrePreferenceEntity? get() = topGenres.getOrNull(2)

그런 다음 레이아웃에서:

android:text="@{myLibraryViewModel.uiState.firstGenre.genreName}"
🤖 Prompt for AI Agents
In @app/src/main/res/layout/fragment_my_page.xml around lines 333 - 346, The
layout directly indexes myLibraryViewModel.uiState.topGenres[0] (and similar
indexes), which can throw IndexOutOfBoundsException when the list has fewer than
three items; fix by exposing safe nullable accessors on the UI state (e.g., add
firstGenre/secondGenre/thirdGenre properties that use
topGenres.getOrNull(index)) or implement a binding adapter like safeGet to
return null/default when out of range, and update the TextView bindings to use
those safe properties (e.g., myLibraryViewModel.uiState.firstGenre?.genreName
and firstGenre?.genreCount) so the layout never directly indexes the list.

Copy link
Member

@s9hn s9hn left a comment

Choose a reason for hiding this comment

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

고생히셨씁니다!

@m6z1 m6z1 merged commit 326cdca into feat/772 Jan 10, 2026
1 check passed
@m6z1 m6z1 deleted the feat/773 branch January 10, 2026 06:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍯 [FEAT] 새로운 기능을 개발합니다. 🏹 궁사 명지 웹소소 공주의 은밀한 사냥생활

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 마이페이지 ui 수정

3 participants