Skip to content

Create Week04 Mission01,02#24

Open
Hyerin-Jeong wants to merge 2 commits intomainfrom
Week04/LinLin
Open

Create Week04 Mission01,02#24
Hyerin-Jeong wants to merge 2 commits intomainfrom
Week04/LinLin

Conversation

@Hyerin-Jeong
Copy link
Copy Markdown
Contributor

📝 미션 번호

4주차 Misson 1,2

📋 구현 사항

  • 홈 / 구매하기 / 위시리스트 상품 데이터를 DataStore에 JSON으로 저장해서 쓰도록 바꿈
  • 구매하기에서 하트 누르면 위시리스트에 반영되고, 앱 껐다 켜도 유지됨
  • 구매하기에 탭(전체 / Tops & T-shirts / sale) 넣고, 뒤 두 탭은 빈 화면 Fragment만 둠

📎 스크린샷

umc_week04.mp4

✅ 체크리스트

  • [o] Merge 하려는 브랜치가 올바르게 설정되어 있나요?
  • [o] 에뮬레이터 또는 실제 기기에서 정상 동작하나요?
  • [o] 불필요한 주석 및 Log가 제거되었나요?

🤔 질문 사항

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

홈/구매하기/위시리스트의 상품 데이터를 DataStore(JSON) 기반으로 전환하고, 구매하기 하트 토글이 위시리스트에 반영되며 앱 재실행 후에도 유지되도록 구성한 PR입니다. 또한 구매하기 화면에 탭(전체 / Tops & T-shirts / sale)을 추가하고, 일부 탭은 플레이스홀더 Fragment로 처리했습니다.

Changes:

  • 상품 더미 데이터를 DataStore(Preferences) + JSON(Gson)으로 시딩/조회/갱신하도록 구현
  • 구매하기 하트 토글 ↔ 위시리스트 동기화 및 위시리스트 화면 표시
  • BottomNavigation + Shop 탭 UI/Fragment 구성 추가

Reviewed changes

Copilot reviewed 60 out of 89 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
Week04/LinLin/settings.gradle.kts Gradle 설정/리포지토리 및 모듈 포함 구성 추가
Week04/LinLin/build.gradle.kts 루트 Gradle 플러그인 alias 구성
Week04/LinLin/gradle.properties Gradle 전역 옵션 추가
Week04/LinLin/gradlew Gradle wrapper 실행 스크립트(POSIX) 추가
Week04/LinLin/gradlew.bat Gradle wrapper 실행 스크립트(Windows) 추가
Week04/LinLin/gradle/gradle-daemon-jvm.properties Toolchain/daemon JVM 설정 파일 추가
Week04/LinLin/gradle/libs.versions.toml 버전 카탈로그(라이브러리/플러그인) 추가
Week04/LinLin/gradle/wrapper/gradle-wrapper.properties Gradle 배포 URL/해시 등 wrapper 설정 추가
Week04/LinLin/gradle/wrapper/gradle-wrapper.jar Gradle wrapper jar 추가
Week04/LinLin/app/build.gradle.kts Android 앱 모듈 빌드 설정/의존성 추가
Week04/LinLin/app/proguard-rules.pro ProGuard 기본 규칙 파일 추가
Week04/LinLin/app/src/main/AndroidManifest.xml 앱/액티비티 선언 및 테마/아이콘 설정 추가
Week04/LinLin/app/src/main/java/com/example/week02/MainActivity.kt BottomNavigation 기반 Fragment 전환 및 주문 버튼 이벤트 처리
Week04/LinLin/app/src/main/java/com/example/week02/data/ProductItem.kt 상품 UI 모델 정의
Week04/LinLin/app/src/main/java/com/example/week02/data/ProductDummyData.kt 홈/구매하기 더미 상품 데이터 제공
Week04/LinLin/app/src/main/java/com/example/week02/data/ProductPreferencesRepository.kt DataStore(JSON) 시딩/조회/하트 토글/위시리스트 동기화
Week04/LinLin/app/src/main/java/com/example/week02/ui/home/HomeFragment.kt 홈 화면 + 날짜 표시 + 가로 상품 리스트 바인딩
Week04/LinLin/app/src/main/java/com/example/week02/ui/home/HomeNewProductAdapter.kt 홈 가로 상품 RecyclerView 어댑터
Week04/LinLin/app/src/main/java/com/example/week02/ui/shop/ShopFragment.kt 구매하기 탭 구성 및 탭별 child fragment 교체
Week04/LinLin/app/src/main/java/com/example/week02/ui/shop/ShopGridFragment.kt 구매하기 전체 탭 그리드 + 하트 토글 연동
Week04/LinLin/app/src/main/java/com/example/week02/ui/shop/ShopProductAdapter.kt 구매하기/위시리스트 공용 상품 그리드 어댑터(하트 포함)
Week04/LinLin/app/src/main/java/com/example/week02/ui/shop/ShopTabPlaceholderFragment.kt 구매하기 빈 탭용 플레이스홀더 Fragment
Week04/LinLin/app/src/main/java/com/example/week02/ui/wishlist/WishlistFragment.kt 위시리스트 그리드 표시(DataStore Flow 수집)
Week04/LinLin/app/src/main/java/com/example/week02/ui/cart/CartFragment.kt 장바구니 화면 및 주문하기 버튼 이벤트 전달
Week04/LinLin/app/src/main/java/com/example/week02/ui/cart/OnCartOrderListener.kt CartFragment → Activity 이벤트 전달 인터페이스
Week04/LinLin/app/src/main/java/com/example/week02/ui/profile/ProfileFragment.kt 프로필 화면 및 수정 화면 이동
Week04/LinLin/app/src/main/java/com/example/week02/ui/profile/ProfileEditFragment.kt 프로필 수정 화면(기본 UI)
Week04/LinLin/app/src/main/res/layout/activity_main.xml 메인 컨테이너 + BottomNavigation 레이아웃
Week04/LinLin/app/src/main/res/layout/fragment_home.xml 홈 UI(배너, 날짜, 가로 리스트)
Week04/LinLin/app/src/main/res/layout/fragment_shop.xml 구매하기 탭 + child container 레이아웃
Week04/LinLin/app/src/main/res/layout/fragment_shop_grid.xml 구매하기 그리드 RecyclerView 레이아웃
Week04/LinLin/app/src/main/res/layout/fragment_shop_tab_placeholder.xml 구매하기 빈 탭 플레이스홀더 레이아웃
Week04/LinLin/app/src/main/res/layout/fragment_wishlist.xml 위시리스트 RecyclerView 레이아웃
Week04/LinLin/app/src/main/res/layout/fragment_cart.xml 장바구니 빈 상태 UI + 주문하기 버튼
Week04/LinLin/app/src/main/res/layout/fragment_profile.xml 프로필 화면 UI
Week04/LinLin/app/src/main/res/layout/fragment_profile_edit.xml 프로필 수정 화면 UI
Week04/LinLin/app/src/main/res/layout/item_home_new_product.xml 홈 가로 리스트 아이템 레이아웃
Week04/LinLin/app/src/main/res/layout/item_shop_product.xml 구매하기/위시리스트 상품 카드 아이템 레이아웃
Week04/LinLin/app/src/main/res/menu/bottom_nav_menu.xml 하단 네비게이션 메뉴 정의
Week04/LinLin/app/src/main/res/values/strings.xml 앱 이름 문자열 추가
Week04/LinLin/app/src/main/res/values/colors.xml 기본 색상 리소스 추가
Week04/LinLin/app/src/main/res/values/themes.xml 앱 테마 정의
Week04/LinLin/app/src/main/res/values/styles.xml BottomNav 스타일(ActiveIndicator) 추가
Week04/LinLin/app/src/main/res/color/nav_item_color.xml BottomNav 선택/비선택 색 selector 추가
Week04/LinLin/app/src/main/res/xml/backup_rules.xml 백업 규칙 파일 추가
Week04/LinLin/app/src/main/res/xml/data_extraction_rules.xml 데이터 추출 규칙 파일 추가
Week04/LinLin/app/src/main/res/drawable/bg_tab_transparent.xml TabLayout 배경 shape 추가
Week04/LinLin/app/src/main/res/drawable/bg_product_placeholder.xml 상품 이미지 플레이스홀더 shape 추가
Week04/LinLin/app/src/main/res/drawable/ic_home.xml 홈 아이콘 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_shop.xml 구매하기 아이콘 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_wishlist.xml 위시리스트 아이콘 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_profile.xml 프로필 아이콘 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_search.xml 검색 아이콘 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_cart.xml 장바구니(원형) 아이콘 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_cart_empty.xml 장바구니 아이콘(24dp) 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_heart_outline.xml 하트 아웃라인 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_heart_filled.xml 하트 채움 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_launcher_background.xml 런처 아이콘 배경 벡터 추가
Week04/LinLin/app/src/main/res/drawable/ic_launcher_foreground.xml 런처 아이콘 전경 벡터 추가
Week04/LinLin/app/src/main/res/drawable/img_home_banner.png 홈 배너 이미지 리소스 추가
Week04/LinLin/app/src/main/res/drawable/img_air_jordan_xxxvi.png 상품 이미지 리소스 추가
Week04/LinLin/app/src/main/res/drawable/img_nike_air_force_1_07.png 상품 이미지 리소스 추가
Week04/LinLin/app/src/main/res/drawable/img_jordan_nike_af1_07_essentials.png 상품 이미지 리소스 추가
Week04/LinLin/app/src/main/res/drawable/img_nike_elite_crew.png 상품 이미지 리소스 추가
Week04/LinLin/app/src/main/res/drawable/img_nike_everyday_plus_cushioned.png 상품 이미지 리소스 추가
Week04/LinLin/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml 적응형 런처 아이콘 정의 추가
Week04/LinLin/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml 적응형 라운드 런처 아이콘 정의 추가
Week04/LinLin/app/src/main/res/mipmap-mdpi/ic_launcher.webp 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp 라운드 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-hdpi/ic_launcher.webp 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp 라운드 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-xhdpi/ic_launcher.webp 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp 라운드 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp 라운드 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp 런처 아이콘(webp) 추가
Week04/LinLin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp 라운드 런처 아이콘(webp) 추가
Week04/LinLin/.idea/AndroidProjectSystem.xml IDE 프로젝트 시스템 설정 파일 추가
Week04/LinLin/.idea/assetWizardSettings.xml IDE 에셋 위저드 설정 파일 추가
Week04/LinLin/.idea/compiler.xml IDE 컴파일러 설정 추가
Week04/LinLin/.idea/deploymentTargetSelector.xml IDE 배포 타겟 선택 설정 추가
Week04/LinLin/.idea/deviceManager.xml IDE 디바이스 매니저 설정 추가
Week04/LinLin/.idea/gradle.xml IDE Gradle 연동 설정 추가
Week04/LinLin/.idea/misc.xml IDE 프로젝트 JDK/루트 설정 추가
Week04/LinLin/.idea/runConfigurations.xml IDE 런 구성 무시 프로듀서 설정 추가
Week04/LinLin/.idea/runConfigurations/app.xml IDE 앱 런 구성 추가
Week04/LinLin/.idea/vcs.xml IDE VCS 매핑 설정 추가
Week04/LinLin/.idea/workspace.xml IDE 워크스페이스(개인 설정) 파일 추가
Week04/LinLin/.idea/caches/deviceStreaming.xml IDE 캐시/디바이스 스트리밍 설정 파일 추가
Files not reviewed (12)
  • Week04/LinLin/.idea/AndroidProjectSystem.xml: Language not supported
  • Week04/LinLin/.idea/assetWizardSettings.xml: Language not supported
  • Week04/LinLin/.idea/caches/deviceStreaming.xml: Language not supported
  • Week04/LinLin/.idea/compiler.xml: Language not supported
  • Week04/LinLin/.idea/deploymentTargetSelector.xml: Language not supported
  • Week04/LinLin/.idea/deviceManager.xml: Language not supported
  • Week04/LinLin/.idea/gradle.xml: Language not supported
  • Week04/LinLin/.idea/misc.xml: Language not supported
  • Week04/LinLin/.idea/runConfigurations.xml: Language not supported
  • Week04/LinLin/.idea/runConfigurations/app.xml: Language not supported
  • Week04/LinLin/.idea/vcs.xml: Language not supported
  • Week04/LinLin/.idea/workspace.xml: Language not supported

Comment thread Week04/LinLin/gradlew
Comment on lines +116 to +118

CLASSPATH="\\\"\\\""

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

CLASSPATH 값이 "\\\"\\\""로 설정되어 있어 정상적인 Gradle Wrapper 실행에 문제를 만들 수 있습니다. 일반적으로는 gradle/wrapper/gradle-wrapper.jar 경로를 가리키도록 설정되거나, -jar 옵션만 쓸 경우 -classpath 인자를 제거하는 형태가 됩니다. Gradle에서 생성한 기본 wrapper 스크립트로 재생성/교체하는 쪽을 권장합니다.

Copilot uses AI. Check for mistakes.
Comment thread Week04/LinLin/gradlew.bat
Comment on lines +71 to +77
@rem Setup the command line

set CLASSPATH=


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Windows용 wrapper에서도 set CLASSPATH=로 비어 있는데, 아래 실행 커맨드에서 -classpath "%CLASSPATH%"를 전달하고 있습니다. -jar를 사용할 거라면 -classpath를 제거하거나, 사용할 거면 CLASSPATH를 wrapper jar 경로로 채우는 등 스크립트를 표준 형태로 맞춰 주세요(현재 상태는 환경에 따라 실행 실패/오동작 위험이 있습니다).

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +6
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidLayouts">
<shared>
<config />
</shared>
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

사용자 로컬 환경에 종속적인 .idea/workspace.xml이 커밋에 포함되어 있습니다. 다른 개발자 환경/CI에 불필요한 충돌을 유발하므로 저장소에서 제거하고, **/.idea/workspace.xml 등 하위 프로젝트까지 무시하도록 ignore 패턴을 보완하는 것을 권장합니다.

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +11
import android.util.Log
import android.view.MenuItem
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import com.example.week02.databinding.ActivityMainBinding
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

PR 체크리스트에 '불필요한 Log 제거'가 있는데, MainActivity에 생명주기 Log.d가 다수 남아 있습니다. 디버그 용도라면 BuildConfig.DEBUG 가드로 감싸거나 제거해 주세요. 또한 MenuItem, Fragment import는 현재 코드에서 사용되지 않아 정리하는 게 좋습니다.

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +38
<ImageView
android:id="@+id/iv_heart"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@null"
android:padding="2dp"
app:layout_constraintEnd_toEndOf="@id/iv_product"
app:layout_constraintTop_toTopOf="@id/iv_product"
tools:src="@drawable/ic_heart_outline" />
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

클릭 가능한 하트 아이콘(iv_heart)의 contentDescription@null로 되어 있어 스크린리더에서 의미가 전달되지 않습니다. 접근성을 위해 '위시리스트 추가/삭제'처럼 상태에 맞는 설명을 제공하고(가능하면 string 리소스 사용), 토글 시 함께 업데이트해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +7
<item android:id="@+id/homeFragment" android:title="홈" android:icon="@drawable/ic_home" />
<item android:id="@+id/shopFragment" android:title="구매하기" android:icon="@drawable/ic_shop" />
<item android:id="@+id/wishlistFragment" android:title="위시리스트" android:icon="@drawable/ic_wishlist" />
<item android:id="@+id/cartFragment" android:title="장바구니" android:icon="@drawable/ic_cart" />
<item android:id="@+id/profileFragment" android:title="프로필" android:icon="@drawable/ic_profile" />
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

BottomNavigation 메뉴의 android:title이 문자열 리소스가 아닌 하드코딩 문자열로 들어가 있습니다. 다국어/일관된 관리 관점에서 @string/...으로 분리해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +10
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Week02">
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

backup_rules.xml/data_extraction_rules.xml을 추가했지만 AndroidManifest.xml<application>android:fullBackupContent / android:dataExtractionRules 속성으로 연결되어 있지 않아 현재는 적용되지 않습니다. 실제로 백업/복구 정책을 의도했다면 manifest에 해당 속성을 추가해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +31
private fun parseList(json: String?): List<ProductItem> {
if (json.isNullOrBlank()) return emptyList()
return gson.fromJson(json, listType) ?: emptyList()
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

DataStore에 저장된 JSON이 깨졌거나 스키마가 바뀐 경우 gson.fromJson에서 예외가 발생해 Flow 수집 시 앱이 크래시날 수 있습니다. parseList에서 try/catchJsonSyntaxException 등을 처리해 빈 리스트로 폴백하거나, 시드를 재생성하는 방식으로 복구 로직을 넣는 것이 안전합니다.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceStreaming">
<option name="deviceSelectionList">
<list>
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

.idea/caches/deviceStreaming.xml은 Android Studio가 생성하는 캐시/로컬 디바이스 스트리밍 설정 파일로, 사용자 환경에 종속적이고 용량도 커서 버전 관리에 포함하지 않는 것이 일반적입니다. 저장소에서 제거하고 **/.idea/caches/를 무시하도록 ignore 패턴을 추가하는 것을 권장합니다.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
plugins {
alias(libs.plugins.android.application)
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

프로젝트가 Kotlin 소스(.kt)를 포함하고 있는데 app/build.gradle.kts에 Kotlin Android 플러그인(예: org.jetbrains.kotlin.android) 적용이 보이지 않습니다. 이 상태에서는 Kotlin 컴파일 태스크가 구성되지 않아 빌드가 실패할 가능성이 큽니다. 버전 카탈로그에 Kotlin 플러그인을 추가하고 plugins 블록에 함께 적용해 주세요.

Copilot uses AI. Check for mistakes.
@Dawon-Y
Copy link
Copy Markdown
Contributor

Dawon-Y commented Apr 12, 2026

안녕하세요! 4주차 과제 수행하시느라 고생 많으셨습니다💚
작성해주신 코드에 대한 리뷰를 정리했으니 참고하시면 좋을 것 같습니다.


총평

ShopProductAdapter를 구매하기(하트 토글 필요)와 위시리스트(표시 위주)에서 함께 쓸 수 있도록, showHeart와 onHeartClick(옵션 콜백)을 조합해 설계하신 점이 좋습니다.
즉, Week04 요구사항인 “구매하기에서 하트 토글 → DataStore 저장 → 위시리스트 반영 + 유지” 흐름에서, Adapter가 UI 렌더링/클릭 전달 역할을 깔끔하게 맡고 있습니다.

1. 어댑터 생성자 설계(재사용성)

class ShopProductAdapter(
    private val items: MutableList<ProductItem>,
    private val onHeartClick: ((position: Int) -> Unit)? = null,
)

onHeartClick을 nullable + 기본값 null로 둬서,

ShopGrid에서는 onHeartClick을 넘겨 DataStore 토글을 외부에서 처리할 수 있고,
Wishlist에서는 콜백 없이 생성해 단순 표시용으로 재사용할 수 있습니다.
이 구조 덕분에 WishlistFragment에서 ShopProductAdapter(wishlistItems)로 생성하는 방식도 자연스럽게 성립합니다.

2. ViewBinding 기반 바인딩(RecyclerView 기본기)

class VH(val binding: ItemShopProductBinding) : RecyclerView.ViewHolder(binding.root)

ViewBinding을 사용해 onBindViewHolder에서 각 UI를 채우는 흐름이 정석입니다.
subtitle, colorsLabel처럼 nullable 값에 대해 VISIBLE/GONE로 분기해 UI가 깨끗하게 보이도록 처리하신 점이 좋습니다.

3. 화면별 UI 제어(showHeart)

if (item.showHeart) {
    b.ivHeart.visibility = View.VISIBLE
    ...
} else {
    b.ivHeart.visibility = View.GONE
    b.ivHeart.setOnClickListener(null)
}

showHeart로 하트 표시 여부를 명확하게 제어하고, 하트를 숨길 때는 클릭 리스너도 제거해서 불필요한 이벤트를 방지했습니다.

4. 하트 아이콘 상태 렌더링(heartFilled)

b.ivHeart.setImageResource(
    if (item.heartFilled) R.drawable.ic_heart_filled else R.drawable.ic_heart_outline,
)

heartFilled에 따라 아이콘을 바꾸는 방식이 직관적이며, UI 요구사항과도 잘 맞습니다.

5. 클릭 처리 방식(콜백 우선 + fallback)

b.ivHeart.setOnClickListener {
    val pos = holder.bindingAdapterPosition
    if (pos == RecyclerView.NO_POSITION) return@setOnClickListener
    if (onHeartClick != null) {
        onHeartClick.invoke(pos)
    } else {
        val current = items[pos]
        items[pos] = current.copy(heartFilled = !current.heartFilled)
        notifyItemChanged(pos)
    }
}

bindingAdapterPosition + NO_POSITION 방어를 넣어 클릭 안정성이 좋습니다.
onHeartClick이 있으면 외부로 위임하고, 없으면 어댑터 내부에서 토글 처리하는 fallback도 구현해 두셨습니다.
다만 Week04 과제의 “앱 종료 후에도 유지” 요구사항을 엄밀히 만족하려면, 실제 구매하기 화면에서는 반드시 onHeartClick을 통해 DataStore를 갱신하는 경로를 사용해야 합니다(로컬 토글만으로는 유지가 안 됨).
현재 ShopGridFragment에서 repo.toggleShopHeart(id)로 연결해 둔 흐름이라면 방향은 맞습니다.

6. (선택 개선) 콜백 파라미터를 position 대신 id로

현재 onHeartClick이 (position: Int) -> Unit인데, 구조적으로는:

onHeartClick: (productId: Int) -> Unit 처럼 id 기반으로 넘기는 편이 더 안전합니다.
이유는 position은 리스트 갱신/필터링/정렬 시 의미가 바뀔 수 있지만, id는 안정적으로 동일 상품을 가리키기 때문입니다.


마무리

이 ShopProductAdapter는

  • 화면별 하트 표시 제어(showHeart)
  • 상태 렌더링(heartFilled)
  • 클릭 안전장치(bindingAdapterPosition 방어)
  • DataStore 연동을 위한 외부 콜백 구조(onHeartClick) 를 갖추고 있어서 Week04 요구사항을 구현하기에 좋은 형태입니다.

코드 리뷰 확인해보시고 궁금한 점 있으면 언제든지 말씀해주세요!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants