Skip to content

Create Week4 Mission 1, 2#23

Open
umckee5696 wants to merge 1 commit intomainfrom
Week04/Yido
Open

Create Week4 Mission 1, 2#23
umckee5696 wants to merge 1 commit intomainfrom
Week04/Yido

Conversation

@umckee5696
Copy link
Copy Markdown
Contributor

@umckee5696 umckee5696 commented Mar 31, 2026

📝 미션 번호

4주차 Misson 1, 2

📋 구현 사항

  • ProductDataStore.kt 파일에 DataStore 읽기/쓰기 로직을 분리하여 상품 데이터를 Gson으로 직렬화해 저장하고 앱 재시작 후에도 유지되도록 구현
  • 구매하기 탭 상품 아이템의 하트 클릭 시 DataStore에 isWished 상태를 반영하고, 위시리스트 화면에서는 isWished가 true인 아이템만 필터링하여 어댑터에 전달
  • Tops & T-Shirts, Sale 탭에 대응하는 빈 Fragment 및 레이아웃 파일 생성 (5주차 탭 연동 준비)

📎 스크린샷

studio64_Yrh84f9d92.mp4

✅ 체크리스트

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

@umckee5696 umckee5696 changed the title feat: 4주차 미션 완료 Create Week4 Mission 1, 2 Mar 31, 2026
@umckee5696 umckee5696 requested a review from Dawon-Y March 31, 2026 16:56
@umckee5696 umckee5696 self-assigned this Mar 31, 2026
@Dawon-Y Dawon-Y requested a review from Copilot April 9, 2026 07:55
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

Week04/Yido 안드로이드 앱의 기본 골격을 만들고, 상품 목록을 DataStore(+Gson)로 영속화하며 위시리스트 필터링/탭 UI를 추가하려는 PR입니다.

Changes:

  • ProductDataStore로 상품 목록 읽기/쓰기(직렬화) 및 isWished 상태 영속화
  • Shop 탭에서 하트 클릭 시 DataStore 반영, Wishlist 화면에서 isWished=true만 표시
  • BottomNavigation/Fragment/레이아웃/리소스 및 Gradle 프로젝트 구성 추가

Reviewed changes

Copilot reviewed 70 out of 97 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
Week04/Yido/settings.gradle.kts Gradle 설정 및 모듈 include 구성
Week04/Yido/gradlew.bat Gradle Wrapper(Windows) 추가
Week04/Yido/gradlew Gradle Wrapper(POSIX) 추가
Week04/Yido/gradle/wrapper/gradle-wrapper.properties Gradle 배포 URL/해시 설정
Week04/Yido/gradle/libs.versions.toml 버전 카탈로그 추가
Week04/Yido/gradle/gradle-daemon-jvm.properties Gradle daemon JVM/toolchain 설정 추가
Week04/Yido/gradle.properties Gradle 공통 프로퍼티 추가
Week04/Yido/build.gradle.kts Top-level Gradle 스크립트 추가
Week04/Yido/build_log.txt 로컬 빌드 실패 로그 추가(커밋 포함)
Week04/Yido/.gitignore 프로젝트 단위 ignore 규칙 추가
Week04/Yido/.vscode/settings.json VS Code Java 설정 추가
Week04/Yido/.idea/.gitignore IntelliJ/AS .idea 일부 ignore 설정
Week04/Yido/.idea/AndroidProjectSystem.xml Android Studio 프로젝트 시스템 설정
Week04/Yido/.idea/compiler.xml 컴파일러/바이트코드 타겟 설정
Week04/Yido/.idea/deviceManager.xml Device Manager 설정
Week04/Yido/.idea/gradle.xml Gradle 연동 설정
Week04/Yido/.idea/inspectionProfiles/Project_Default.xml 인스펙션 프로필 설정
Week04/Yido/.idea/misc.xml 프로젝트 JDK/루트 설정
Week04/Yido/.idea/runConfigurations.xml Run configuration producer 설정
Week04/Yido/.idea/vcs.xml VCS 매핑 설정
Week04/Yido/.idea/deploymentTargetSelector.xml 배포 타겟 선택(로컬 환경 정보 포함)
Week04/Yido/.claude/settings.json Claude 도구 권한 설정
Week04/Yido/.claude/settings.local.json Claude 로컬 권한 설정(로컬 경로/권한 포함)
Week04/Yido/app/.gitignore app 모듈 build ignore 추가
Week04/Yido/app/build.gradle.kts 앱 모듈 Android/의존성 구성(DataStore/Gson 등)
Week04/Yido/app/proguard-rules.pro Proguard 규칙 기본 파일 추가
Week04/Yido/app/src/main/AndroidManifest.xml 앱 매니페스트/런처 액티비티 등록
Week04/Yido/app/src/main/java/com/umc/yido/MainActivity.kt BottomNavigation 기반 Fragment 전환 구현
Week04/Yido/app/src/main/java/com/umc/yido/HomeFragment.kt Home 화면 + 상품 목록 표시(RecyclerView)
Week04/Yido/app/src/main/java/com/umc/yido/HomeProductAdapter.kt Home 상품 어댑터 추가
Week04/Yido/app/src/main/java/com/umc/yido/ShopFragment.kt Shop 화면 + 탭 필터 + 위시 토글/저장
Week04/Yido/app/src/main/java/com/umc/yido/ShopProductAdapter.kt Shop 그리드 어댑터 + 하트 클릭 콜백
Week04/Yido/app/src/main/java/com/umc/yido/WishlistFragment.kt Wishlist 화면 + wished 필터 표시
Week04/Yido/app/src/main/java/com/umc/yido/WishlistProductAdapter.kt Wishlist 어댑터 추가
Week04/Yido/app/src/main/java/com/umc/yido/CartFragment.kt Cart 화면 + Shop 탭 전환 버튼
Week04/Yido/app/src/main/java/com/umc/yido/ProfileFragment.kt Profile 빈 화면 구성
Week04/Yido/app/src/main/java/com/umc/yido/TopsTShirtsFragment.kt Tops & T-Shirts 빈 Fragment 추가
Week04/Yido/app/src/main/java/com/umc/yido/SaleFragment.kt Sale 빈 Fragment 추가
Week04/Yido/app/src/main/java/com/umc/yido/Product.kt Product 모델 추가(isWished/category 포함)
Week04/Yido/app/src/main/java/com/umc/yido/ProductDataStore.kt DataStore(Gson) 기반 상품 목록 영속화 추가
Week04/Yido/app/src/main/res/layout/activity_main.xml FragmentContainer + BottomNavigation 레이아웃
Week04/Yido/app/src/main/res/layout/fragment_home.xml Home 화면 레이아웃
Week04/Yido/app/src/main/res/layout/fragment_shop.xml Shop 탭/리스트 레이아웃
Week04/Yido/app/src/main/res/layout/fragment_wishlist.xml Wishlist 리스트 레이아웃
Week04/Yido/app/src/main/res/layout/fragment_cart.xml Cart 빈 상태/주문 버튼 레이아웃
Week04/Yido/app/src/main/res/layout/fragment_profile.xml Profile 빈 레이아웃
Week04/Yido/app/src/main/res/layout/fragment_tops_tshirts.xml Tops 빈 레이아웃
Week04/Yido/app/src/main/res/layout/fragment_sale.xml Sale 빈 레이아웃
Week04/Yido/app/src/main/res/layout/item_product_home.xml Home 상품 아이템 레이아웃
Week04/Yido/app/src/main/res/layout/item_product_grid.xml Shop 그리드 아이템(+하트) 레이아웃
Week04/Yido/app/src/main/res/layout/item_product_wishlist.xml Wishlist 아이템 레이아웃
Week04/Yido/app/src/main/res/menu/bottom_nav_menu.xml BottomNavigation 메뉴 리소스
Week04/Yido/app/src/main/res/values/strings.xml 문자열 리소스 추가(탭/화면 텍스트 등)
Week04/Yido/app/src/main/res/values/colors.xml 컬러 리소스 추가
Week04/Yido/app/src/main/res/values/themes.xml 테마/BottomNav indicator 제거 스타일 추가
Week04/Yido/app/src/main/res/values-night/themes.xml 나이트 테마 기본 설정
Week04/Yido/app/src/main/res/color/color_bottom_nav.xml Bottom nav 선택/비선택 컬러 selector
Week04/Yido/app/src/main/res/xml/backup_rules.xml 백업 규칙 기본 파일
Week04/Yido/app/src/main/res/xml/data_extraction_rules.xml 데이터 추출 규칙 기본 파일
Week04/Yido/app/src/main/res/drawable/bg_circle_white.xml 원형 흰색 배경 drawable
Week04/Yido/app/src/main/res/drawable/shape_btn_pill_black.xml 버튼 pill shape drawable
Week04/Yido/app/src/main/res/drawable/shape_btn_pill_black_ripple.xml 버튼 ripple drawable
Week04/Yido/app/src/main/res/drawable/img_product_placeholder.xml 상품 placeholder drawable
Week04/Yido/app/src/main/res/drawable/ic_nav_home.xml 하단바 아이콘(홈)
Week04/Yido/app/src/main/res/drawable/ic_nav_shop.xml 하단바 아이콘(쇼핑)
Week04/Yido/app/src/main/res/drawable/ic_nav_wishlist.xml 하단바 아이콘(위시리스트)
Week04/Yido/app/src/main/res/drawable/ic_nav_cart.xml 하단바 아이콘(카트)
Week04/Yido/app/src/main/res/drawable/ic_nav_profile.xml 하단바 아이콘(프로필)
Week04/Yido/app/src/main/res/drawable/ic_heart_outline.xml 하트(빈) 아이콘
Week04/Yido/app/src/main/res/drawable/ic_heart_filled.xml 하트(채움) 아이콘
Week04/Yido/app/src/main/res/drawable/ic_heart_filled_red.xml 하트(빨강 채움) 아이콘
Week04/Yido/app/src/main/res/drawable/ic_cart_empty.xml 카트 빈 상태 아이콘
Week04/Yido/app/src/main/res/drawable/ic_arrow_back.xml 뒤로가기 아이콘
Week04/Yido/app/src/main/res/drawable/ic_search.xml 검색 아이콘
Week04/Yido/app/src/main/res/drawable/ic_launcher_background.xml 런처 아이콘 배경
Week04/Yido/app/src/main/res/drawable/ic_launcher_foreground.xml 런처 아이콘 전경
Week04/Yido/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml Adaptive launcher icon
Week04/Yido/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml Adaptive launcher icon(round)
Week04/Yido/app/src/main/res/mipmap-mdpi/ic_launcher.webp 런처 아이콘(mdpi)
Week04/Yido/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp 런처 아이콘 라운드(mdpi)
Week04/Yido/app/src/main/res/mipmap-hdpi/ic_launcher.webp 런처 아이콘(hdpi)
Week04/Yido/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp 런처 아이콘 라운드(hdpi)
Week04/Yido/app/src/main/res/mipmap-xhdpi/ic_launcher.webp 런처 아이콘(xhdpi)
Week04/Yido/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp 런처 아이콘 라운드(xhdpi)
Week04/Yido/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp 런처 아이콘(xxhdpi)
Week04/Yido/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp 런처 아이콘 라운드(xxhdpi)
Week04/Yido/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp 런처 아이콘(xxxhdpi)
Week04/Yido/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp 런처 아이콘 라운드(xxxhdpi)
Week04/Yido/app/src/androidTest/java/com/umc/yido/ExampleInstrumentedTest.kt 기본 계측 테스트 추가
Week04/Yido/app/src/test/java/com/umc/yido/ExampleUnitTest.kt 기본 유닛 테스트 추가
Files not reviewed (10)
  • Week04/Yido/.idea/.gitignore: Language not supported
  • Week04/Yido/.idea/AndroidProjectSystem.xml: Language not supported
  • Week04/Yido/.idea/compiler.xml: Language not supported
  • Week04/Yido/.idea/deploymentTargetSelector.xml: Language not supported
  • Week04/Yido/.idea/deviceManager.xml: Language not supported
  • Week04/Yido/.idea/gradle.xml: Language not supported
  • Week04/Yido/.idea/inspectionProfiles/Project_Default.xml: Language not supported
  • Week04/Yido/.idea/misc.xml: Language not supported
  • Week04/Yido/.idea/runConfigurations.xml: Language not supported
  • Week04/Yido/.idea/vcs.xml: Language not supported

Comment on lines +52 to +56
viewLifecycleOwner.lifecycleScope.launch {
ProductDataStore.getProducts(requireContext()).collect { products ->
val wishList = products.filter { it.isWished }.toMutableList()
adapter.updateList(wishList)
}
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.

ProductDataStore가 비어 있을 때 위시리스트 화면에서는 기본 상품을 초기화/저장하지 않아서(=Flow가 emptyList를 내보내는 상태) isWished=true인 기본 상품이 있어도 위시리스트가 빈 화면으로 보일 수 있습니다. ShopFragment처럼 빈 경우 기본값을 저장하거나, DataStore 계층에서 초기화 로직을 공통화해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +112
val selectedCategory = when (index) {
1 -> "Tops & T-Shirts"
2 -> "sale"
else -> "전체"
}
val filtered = if (selectedCategory == "전체") {
allProducts.toMutableList()
} else {
allProducts.filter { it.category == selectedCategory }.toMutableList()
}
adapter.updateList(filtered)
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.

현재 탭 필터링이 it.category == "Tops & T-Shirts"/"sale" 같은 문자열 하드코딩에 의존하는데, 기본 상품(defaultProducts)은 category를 설정하지 않아(기본값 "전체") Tops/Sale 탭이 항상 비어 보일 가능성이 큽니다. 기본 데이터에 카테고리를 명시하거나, 카테고리를 enum/상수로 통일해서 오타/대소문자 불일치로 인한 필터 실패를 방지해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +27
val defaultProducts = listOf(
Product(R.drawable.img_shoe_1, "Nike Everyday Plus Cushioned", "Training Ankle Socks\nUS\$10"),
Product(R.drawable.img_shoe_2, "Nike Elite Crew", "Basketball Socks\nUS\$16"),
Product(R.drawable.img_shoe_3, "Nike Air Force 1 '07", "Women's Shoes\nUS\$115", isWished = true),
Product(R.drawable.img_shoe_4, "Jordan Nike Air Force 1 '07 Essentials", "Men's Shoes\nUS\$115", isWished = true),
Product(R.drawable.img_shoe_5, "Air Jordan 1 Mid", "Men's Shoes\nUS\$125")
)
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.

defaultProducts에 카테고리 값이 한 번도 설정되지 않아서(Product.category 기본값이 "전체") Shop 탭의 Tops/Sale 필터가 정상 동작하기 어렵습니다. 기본 상품에 카테고리를 지정하거나, 필터링 기준이 되는 값을 한 곳(상수/enum)에서 관리하도록 조정해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +29
android:src="@drawable/ic_heart_outline"
android:background="@drawable/bg_circle_white"
android:padding="8dp"
android:contentDescription="찜하기"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
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.

contentDescription가 문자열 리소스가 아닌 하드코딩("찜하기")으로 들어가 있어 다국어/접근성 대응이 어렵습니다. 또한 현재는 찜 상태 변경(isWished)에 따른 설명이 바뀌지 않아 스크린리더 사용자에게 혼동을 줄 수 있으니 상태별로 적절한 문자열 리소스를 사용해 주세요.

Copilot uses AI. Check for mistakes.
Comment thread Week04/Yido/build_log.txt
Comment on lines +4 to +11
* Where:
Build file 'C:\Users\Kee\Desktop\UMC 2주차 원본\Yido\app\build.gradle.kts' line: 1

* What went wrong:
An exception occurred applying plugin request [id: 'com.android.application', version: '9.1.0']
> Failed to apply plugin 'com.android.internal.application'.
> Your project path contains non-ASCII characters. This will most likely cause the build to fail on Windows. Please move your project to a different directory. See http://b.android.com/95744 for details. This warning can be disabled by adding the line 'android.overridePathCheck=true' to gradle.properties file in the project directory.

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에 로컬 빌드 실패 로그(build_log.txt)가 포함되어 있는데, 개인 PC 경로(사용자명/디렉터리 구조) 노출 및 저장소 노이즈를 유발합니다. 이 파일은 커밋에서 제외하고 .gitignore로 관리하는 편이 안전합니다.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +32
companion object {
private const val TAG = "LIFE_QUIZ"
}

override fun onAttach(context: Context) {
super.onAttach(context)
Log.d(TAG, "HomeFragment : onAttach")
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "HomeFragment : onCreate")
}
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.

현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 HomeFragment에 맞게 의미 있는 값으로 변경해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +32
companion object {
private const val TAG = "LIFE_QUIZ"
}

override fun onAttach(context: Context) {
super.onAttach(context)
Log.d(TAG, "WishlistFragment : onAttach")
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "WishlistFragment : onCreate")
}
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.

현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 WishlistFragment에 맞게 의미 있는 값으로 변경해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +22
companion object {
private const val TAG = "LIFE_QUIZ"
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "MainActivity : onCreate")
enableEdgeToEdge()
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.

현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 MainActivity에 맞게 의미 있는 값으로 변경해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +29
companion object {
private const val TAG = "LIFE_QUIZ"
}

override fun onAttach(context: Context) {
super.onAttach(context)
Log.d(TAG, "CartFragment : onAttach")
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "CartFragment : onCreate")
}
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.

현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 CartFragment에 맞게 의미 있는 값으로 변경해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +29
companion object {
private const val TAG = "LIFE_QUIZ"
}

override fun onAttach(context: Context) {
super.onAttach(context)
Log.d(TAG, "ProfileFragment : onAttach")
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "ProfileFragment : onCreate")
}
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.

현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 ProfileFragment에 맞게 의미 있는 값으로 변경해 주세요.

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

Dawon-Y commented Apr 12, 2026

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


총평

Week04 요구사항인 “더미 데이터 → DataStore(JSON) 대체 + 비동기 처리 + 하트(위시리스트) 유지 + 탭(Top & T-shirts, sale 빈 Fragment)” 흐름을 전반적으로 잘 구현하셨습니다.
특히 ProductDataStore.getProducts(context).collect { ... } 형태로 DataStore의 Flow를 구독해서 UI를 갱신하고, 구매하기에서 하트 토글 시 saveProducts()로 다시 저장해 앱 재실행 후에도 상태가 유지되도록 연결한 점이 핵심 요구사항에 잘 맞습니다.

1. HomeFragment — DataStore Flow 수집 + RecyclerView 갱신 구조

viewLifecycleOwner.lifecycleScope.launch {
    ProductDataStore.getProducts(requireContext()).collect { products ->
        val list = if (products.isEmpty()) {
            ProductDataStore.defaultProducts
        } else {
            products
        }
        adapter.updateList(list.toMutableList())
    }
}

DataStore 값을 collect로 받아와 어댑터를 갱신하는 흐름이 명확합니다.
값이 비어있을 때 defaultProducts로 fallback 처리한 점도 “첫 실행” 케이스를 고려한 것으로 좋습니다.

(선택 개선)
지금은 빈 값이면 UI만 default로 보여주는데, 아예 한 번 저장(seed)까지 해두면(Home처럼) Shop/Wishlist에서도 “항상 DataStore에 값이 있다”는 전제가 성립해서 로직이 더 단순해질 수 있습니다.

2. MainActivity — BottomNavigation 기반 Fragment 전환 구조

savedInstanceState == null일 때 HomeFragment를 최초로 붙이고,
BottomNav.setOnItemSelectedListener로 각 탭 전환을 처리한 방식은 2주차 흐름을 안정적으로 유지한 형태입니다.

supportFragmentManager.beginTransaction()
    .replace(R.id.main_fragment_container, HomeFragment())
    .commit()

changeToShopTab() 구현

fun changeToShopTab() {
    binding.mainBottomNav.selectedItemId = R.id.nav_shop
}

CartFragment에서 Shop으로 자연스럽게 이동시키는 “상태 변경 기반 이동”이라 UX가 깔끔합니다.
(선택 개선)

.replace()는 탭 이동 시 Fragment가 계속 새로 생성됩니다. 과제 범위에서는 괜찮지만, 상태 유지까지 고려하면 Navigation Component 또는 show/hide 전략도 추후 고려할 수 있습니다.

3. ShopFragment — Grid RecyclerView + 탭 필터링 + 하트 토글 저장

3-1) GridLayoutManager(2열) 적용

binding.shopRvProducts.layoutManager = GridLayoutManager(requireContext(), 2)

구매하기 그리드 요구사항을 충족합니다.

3-2) 하트 토글 → DataStore 저장 흐름이 핵심 요구사항에 부합

adapter = ShopProductAdapter(mutableListOf()) { product ->
    viewLifecycleOwner.lifecycleScope.launch {
        val updated = allProducts.map {
            if (it.name == product.name) it.copy(isWished = !it.isWished) else it
        }
        ProductDataStore.saveProducts(requireContext(), updated)
    }
}

“구매하기에서 하트 클릭 → 저장소 업데이트 → 다른 화면(위시리스트)에서도 반영” 구조가 성립합니다.
DataStore에 저장하므로 앱을 껐다 켜도 유지될 수 있습니다.

(중요 개선)
현재 토글 대상 식별을 name으로 하고 있는데,

if (it.name == product.name)

는 동일 이름 상품이 있을 경우 오동작할 수 있습니다.
가능하면 id 같은 고유키를 Product에 추가해서 id로 토글하는 구조가 더 안전합니다.

3-3) DataStore seed 로직(빈 값이면 저장) 처리

if (products.isEmpty()) {
    ProductDataStore.saveProducts(requireContext(), ProductDataStore.defaultProducts)
} else {
    allProducts.clear()
    allProducts.addAll(products)
    refreshCurrentTab()
}

Shop에서는 “비어있으면 default를 저장”까지 하고 있어서 Home보다 한 단계 더 안정적인 seed 패턴입니다.
저장 후 다음 emit에서 else로 들어와 리스트가 그려지는 흐름이라 정상 동작합니다.

3-4) 탭 필터링 로직이 명확함
탭 UI(색/볼드/indicator) 변경 + 카테고리 필터링까지 한 번에 처리돼 흐름이 잘 보입니다.

4. WishlistFragment — DataStore 기반 위시리스트 렌더링

ProductDataStore.getProducts(requireContext()).collect { products ->
    val wishList = products.filter { it.isWished }.toMutableList()
    adapter.updateList(wishList)
}

“하트가 켜진 상품만 위시리스트에서 보여주기” 요구사항을 정확히 충족합니다.
ShopFragment에서 저장한 결과가 동일 DataStore에서 다시 방출되므로 화면 간 동기화도 성립합니다.

(선택 개선)
위시리스트에서도 products.isEmpty()이면 default seed를 해줄지 정책을 통일하면 더 깔끔합니다(현재는 빈 리스트 그대로 보여줄 가능성이 있음).

5. WishlistProductAdapter — 단순/명확한 바인딩 구조

ViewHolder에서 이미지/이름/가격만 바인딩하는 “위시리스트 전용 최소 UI” 형태라 목적에 맞습니다.
updateList()로 리스트 갱신도 이해하기 쉬운 구현입니다.

(선택 개선)
빈번한 갱신이 많아지면 notifyDataSetChanged() 대신 DiffUtil도 고려할 수 있지만, 과제 범위에서는 현재 방식도 충분합니다.

6. SaleFragment — “빈 Fragment만 만들기” 요구사항 충족

return inflater.inflate(R.layout.fragment_sale, container, false)

5주차 과제를 위한 “sale 탭 빈 화면 Fragment” 요구사항을 만족합니다.

7. Product 모델(Product.kt) — 탭 필터링과의 정합성 체크

val category: String = "전체"   // "전체" | "Tops & T-Shirts" | "sale"

ShopFragment의 필터가 it.category == selectedCategory로 동작하므로, defaultProducts를 만들 때 일부 상품에 "Tops & T-Shirts" / "sale" 카테고리가 실제로 들어가 있어야 탭에서 아이템이 보입니다.
만약 defaultProducts가 전부 category 기본값(“전체”)이라면 Tops/sale 탭은 비어 보일 수 있습니다.


마무리

전체적으로 Week04의 핵심인

  • DataStore Flow 구독 기반 UI 갱신
  • 구매하기 하트 토글 → DataStore 저장 → 위시리스트 반영
  • 앱 재실행 후에도 찜 상태 유지
  • 탭 빈 Fragment 준비 를 잘 구현하셨습니다.

완성도를 한 단계 더 올리려면,

  • Product에 id를 추가하고 하트 토글을 name이 아닌 id로 처리
  • defaultProducts의 category가 실제 탭 분류와 맞게 세팅되어 있는지 확인
    이 두 가지를 추천드립니다.

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

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