Conversation
There was a problem hiding this comment.
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
| viewLifecycleOwner.lifecycleScope.launch { | ||
| ProductDataStore.getProducts(requireContext()).collect { products -> | ||
| val wishList = products.filter { it.isWished }.toMutableList() | ||
| adapter.updateList(wishList) | ||
| } |
There was a problem hiding this comment.
ProductDataStore가 비어 있을 때 위시리스트 화면에서는 기본 상품을 초기화/저장하지 않아서(=Flow가 emptyList를 내보내는 상태) isWished=true인 기본 상품이 있어도 위시리스트가 빈 화면으로 보일 수 있습니다. ShopFragment처럼 빈 경우 기본값을 저장하거나, DataStore 계층에서 초기화 로직을 공통화해 주세요.
| 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) |
There was a problem hiding this comment.
현재 탭 필터링이 it.category == "Tops & T-Shirts"/"sale" 같은 문자열 하드코딩에 의존하는데, 기본 상품(defaultProducts)은 category를 설정하지 않아(기본값 "전체") Tops/Sale 탭이 항상 비어 보일 가능성이 큽니다. 기본 데이터에 카테고리를 명시하거나, 카테고리를 enum/상수로 통일해서 오타/대소문자 불일치로 인한 필터 실패를 방지해 주세요.
| 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") | ||
| ) |
There was a problem hiding this comment.
defaultProducts에 카테고리 값이 한 번도 설정되지 않아서(Product.category 기본값이 "전체") Shop 탭의 Tops/Sale 필터가 정상 동작하기 어렵습니다. 기본 상품에 카테고리를 지정하거나, 필터링 기준이 되는 값을 한 곳(상수/enum)에서 관리하도록 조정해 주세요.
| 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" |
There was a problem hiding this comment.
contentDescription가 문자열 리소스가 아닌 하드코딩("찜하기")으로 들어가 있어 다국어/접근성 대응이 어렵습니다. 또한 현재는 찜 상태 변경(isWished)에 따른 설명이 바뀌지 않아 스크린리더 사용자에게 혼동을 줄 수 있으니 상태별로 적절한 문자열 리소스를 사용해 주세요.
| * 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. | ||
|
|
There was a problem hiding this comment.
PR에 로컬 빌드 실패 로그(build_log.txt)가 포함되어 있는데, 개인 PC 경로(사용자명/디렉터리 구조) 노출 및 저장소 노이즈를 유발합니다. 이 파일은 커밋에서 제외하고 .gitignore로 관리하는 편이 안전합니다.
| 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") | ||
| } |
There was a problem hiding this comment.
현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 HomeFragment에 맞게 의미 있는 값으로 변경해 주세요.
| 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") | ||
| } |
There was a problem hiding this comment.
현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 WishlistFragment에 맞게 의미 있는 값으로 변경해 주세요.
| companion object { | ||
| private const val TAG = "LIFE_QUIZ" | ||
| } | ||
|
|
||
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
| Log.d(TAG, "MainActivity : onCreate") | ||
| enableEdgeToEdge() |
There was a problem hiding this comment.
현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 MainActivity에 맞게 의미 있는 값으로 변경해 주세요.
| 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") | ||
| } |
There was a problem hiding this comment.
현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 CartFragment에 맞게 의미 있는 값으로 변경해 주세요.
| 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") | ||
| } |
There was a problem hiding this comment.
현재 TAG가 "LIFE_QUIZ"로 되어 있고 라이프사이클 로그가 다수 남아 있어 앱 로그 노이즈가 큽니다. 릴리즈 기준으로는 로그를 제거하거나 디버그에서만 출력되도록 정리하고, TAG도 ProfileFragment에 맞게 의미 있는 값으로 변경해 주세요.
|
안녕하세요! 4주차 과제 수행하시느라 고생 많으셨습니다💚 총평Week04 요구사항인 “더미 데이터 → DataStore(JSON) 대체 + 비동기 처리 + 하트(위시리스트) 유지 + 탭(Top & T-shirts, sale 빈 Fragment)” 흐름을 전반적으로 잘 구현하셨습니다. 1. HomeFragment — DataStore Flow 수집 + RecyclerView 갱신 구조DataStore 값을 collect로 받아와 어댑터를 갱신하는 흐름이 명확합니다. (선택 개선) 2. MainActivity — BottomNavigation 기반 Fragment 전환 구조savedInstanceState == null일 때 HomeFragment를 최초로 붙이고, changeToShopTab() 구현 CartFragment에서 Shop으로 자연스럽게 이동시키는 “상태 변경 기반 이동”이라 UX가 깔끔합니다. .replace()는 탭 이동 시 Fragment가 계속 새로 생성됩니다. 과제 범위에서는 괜찮지만, 상태 유지까지 고려하면 Navigation Component 또는 show/hide 전략도 추후 고려할 수 있습니다. 3. ShopFragment — Grid RecyclerView + 탭 필터링 + 하트 토글 저장3-1) GridLayoutManager(2열) 적용 구매하기 그리드 요구사항을 충족합니다. 3-2) 하트 토글 → DataStore 저장 흐름이 핵심 요구사항에 부합 “구매하기에서 하트 클릭 → 저장소 업데이트 → 다른 화면(위시리스트)에서도 반영” 구조가 성립합니다. (중요 개선) 는 동일 이름 상품이 있을 경우 오동작할 수 있습니다. 3-3) DataStore seed 로직(빈 값이면 저장) 처리 Shop에서는 “비어있으면 default를 저장”까지 하고 있어서 Home보다 한 단계 더 안정적인 seed 패턴입니다. 3-4) 탭 필터링 로직이 명확함 4. WishlistFragment — DataStore 기반 위시리스트 렌더링“하트가 켜진 상품만 위시리스트에서 보여주기” 요구사항을 정확히 충족합니다. (선택 개선) 5. WishlistProductAdapter — 단순/명확한 바인딩 구조ViewHolder에서 이미지/이름/가격만 바인딩하는 “위시리스트 전용 최소 UI” 형태라 목적에 맞습니다. (선택 개선) 6. SaleFragment — “빈 Fragment만 만들기” 요구사항 충족5주차 과제를 위한 “sale 탭 빈 화면 Fragment” 요구사항을 만족합니다. 7. Product 모델(Product.kt) — 탭 필터링과의 정합성 체크ShopFragment의 필터가 it.category == selectedCategory로 동작하므로, defaultProducts를 만들 때 일부 상품에 "Tops & T-Shirts" / "sale" 카테고리가 실제로 들어가 있어야 탭에서 아이템이 보입니다. 마무리전체적으로 Week04의 핵심인
완성도를 한 단계 더 올리려면,
코드 리뷰 확인해보시고 궁금한 점 있으면 언제든지 말씀해주세요! |
📝 미션 번호
4주차 Misson 1, 2
📋 구현 사항
📎 스크린샷
studio64_Yrh84f9d92.mp4
✅ 체크리스트