-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[둘리] 1, 2단계 쇼핑 장바구니 제출합니다. #20
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쇼핑 장바구니 1,2단계 미션하시느라 고생하셨습니다. 👍
고민해볼만한 의견들을 코맨트로 작성하였으니, 충분히 고민해보시고 도전해보세요. 💪
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
binding = ActivityCartBinding.inflate(layoutInflater) | ||
setContentView(binding.root) | ||
supportActionBar?.setDisplayHomeAsUpEnabled(true) | ||
supportActionBar?.title = "Cart" | ||
presenter = CartPresenter(this, CartDbRepository(this), ProductMockRepository) | ||
presenter.fetchProducts() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onCreate 함수는 두 가지 이상의 일을 하고 있어요.
하나의 함수에는 한 가지 일만 하도록 개선해보면 어떨까요? (이하 관련 내용 동일)
import woowacourse.shopping.model.ProductModel | ||
import woowacourse.shopping.util.PriceFormatter | ||
|
||
sealed class CartItemViewHolder(view: View): RecyclerView.ViewHolder(view) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
유사 ViewHolder를 sealed class로 구현한 것이 인상깊네요. 👍
지금도 좋지만, 미션과 관계없이 시간적으로 여유가 생긴다면 제네릭을 활용하여 BaseViewHolder를 만들어보셔도 좋을 것 같아요.
} | ||
} | ||
|
||
class CartPaginationViewHolder(private val binding: ItemCartPaginationBinding, onItemClick: CartAdapter.OnItemClick) : CartItemViewHolder(binding.root) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class CartPaginationViewHolder(private val binding: ItemCartPaginationBinding, onItemClick: CartAdapter.OnItemClick) : CartItemViewHolder(binding.root) { | |
class CartPaginationViewHolder( | |
private val binding: ItemCartPaginationBinding, | |
onItemClick: CartAdapter.OnItemClick | |
) : CartItemViewHolder(binding.root) { |
코드가 가로로 길어지는 경우에는, 코드를 가로로 스크롤해서 보아야하기 때문에 불편한 점이 있습니다.
그렇기에 개행을 해주시면 코드를 가로로 스크롤하지 않고 볼 수 있습니다. (이하 관련 내용 동일)
return emptyList() | ||
} | ||
|
||
fun undoItems(): List<CartProduct> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undo는 "되돌리기"의 느낌이 강해요.
undo 보다는 prev가 더 적합해보이네요.
} | ||
} | ||
|
||
private fun convertIdToProductModel(cartProducts: List<CartProduct>) = cartProducts.map { productRepository.find(it.id) }.map { it.toUiModel() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filter, map 과 같은 컬렉션 함수들은 매번 새로운 컬렉션을 만들어내요.
map을 한번만 사용하도록 개선해보면 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
asSequence를 사용하여 중간에 새로운 컬랙션을 생성하지 않고, 마지막 연산에서 생성하도록 수정했습니다!
android:layout_width="match_parent" | ||
android:layout_height="match_parent" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:layout_width="0dp" | |
android:layout_height="0dp" |
ConstraintLayout의 자식뷰는 match_parent 대신 0dp 사용을 권장합니다. (이하 관련 내용 동일)
ref. https://developer.android.com/develop/ui/views/layout/constraint-layout#adjust-the-view-size
app:layout_constraintTop_toBottomOf="@id/img_product" | ||
tools:text="동원참치" /> | ||
|
||
<androidx.constraintlayout.widget.ConstraintLayout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<androidx.constraintlayout.widget.ConstraintLayout | |
<View |
라인을 그리고 싶다면, 가벼운 View를 활용하는 것을 권장합니다.
app:layout_constraintTop_toTopOf="@id/text_price_label" | ||
tools:text="99,800원" /> | ||
|
||
<androidx.appcompat.widget.AppCompatButton |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppCompatButton과 Button의 차이점은 무엇일까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Button을 사용하면 background 속성이 적용되지 않아 AppCompatButton을 사용했습니다.
AppCompatButton 코드를 직접 뜯어봤을 때
Allows dynamic tint of its background via the background tint methods in androidx.core.view.ViewCompat.
Allows setting of the background tint using R.attr.backgroundTint and R.attr.backgroundTintMode.
Allows setting of the font family using android.R.attr.fontFamily
문구를 확인할 수 있었습니다!
background의 색상을 설정할 수 있고 R.attr.fontFamily를 사용한 font family 설정도 허용됩니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppCompatButton 코드까지 직접 뜯어서 보시고 좋습니다. 👍
앞으로 개발하시다보면 보시게 될 AppCompat
은 Application Compatibility의 약자로, "앱 호환성"을 의미합니다.
안드로이드는 OS 버전별로 API가 Deprecated 되기도 하며, API가 변경되기도 합니다.
그렇기에 개발자가 직접 버전 분기 코드를 작성해서 구현을 해줘야하는 번거로움이 있었습니다.
아마 영화극장 미션을 하시면서 보셨을 거에요.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
// 권한 요청 거부한 경우
} else {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
} else {
// 안드로이드 12 이하는 Notification에 관한 권한 필요 없음
}
매번 버전마다 분기처리를 해줘야하는 번거로움을 줄이기 위해, 구글에서 AppCompat
이라는 라이브러리를 지원하기 시작했습니다.
해서, AppCompat을 사용하면 버전분기코드를 작성하지 않아도 내부에서 알아서 버전별로 분기처리를 해주게 되었습니다.
정리하면, AppCompat은 호환성과 관련되어있기 때문에, 아쉽게도 둘리가 수정한 방향은 살짝 빗나갔다고 볼 수 있습니다. 😅
추가로, 구글이 머테리얼 디자인 컴포넌트(MDC)를 적용하게 되면서 background 속성으로 색상 변경을 허용하지 않게 되었으며, background가 아닌 backgroundTint 속성을 적용하면 색상이 변경될 것입니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이런 이유로 Compat이 이름에 붙었던거군요!
말씀하신대로 Button에서 backgroundTint를 사용하면 색상이 변경되는 것을 확인했는데요, 그렇다면 지금 디자인처럼 테두리가 있는 버튼일 경우에는 별도의 drawable을 만들어서 AppCompatButton을 사용해야하는 것인가요?
내부에서 분기처리 해주는 AppCompatButton이 Button보다 좋다고 말할 수 있나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
지금 디자인처럼 테두리가 있는 버튼일 경우에는 별도의 drawable을 만들어서 AppCompatButton을 사용해야하는 것인가요?
A. 네 맞습니다.
내부에서 분기처리 해주는 AppCompatButton이 Button보다 좋다고 말할 수 있나요?
A. 좋은 질문이에요!
Activity의 코드를 보시면, AppCompatActivity
라는 것을 상속받을거에요.
AppCompatActivity를 상속받게 되면, View를 Inflate하는 과정에서 제일 먼저 AppComatXXX를 사용하도록 구현되어 있어요.
그래서, AppCompatButton이 아닌 Button을 사용해도, 내부적으로는 AppCompatButton을 사용합니다. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇군요..! 안드로이드의 세계는 알아야할 것이 참 많네요ㅎㅎ 감사합니다!!😄
android:layout_marginHorizontal="15dp" | ||
android:layout_marginVertical="15dp" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
android:layout_marginHorizontal="15dp" | |
android:layout_marginVertical="15dp" | |
android:padding="15dp" |
상하좌우 패딩이 동일하다면, padding 속성을 활용할 수 있어요
type="woowacourse.shopping.view.productlist.ProductListAdapter.OnItemClick" /> | ||
</data> | ||
|
||
<androidx.constraintlayout.widget.ConstraintLayout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가급적이면 들여쓰기를 잘 해주시는 것이 좋습니다.
들여쓰기가 없으니 최상단 element로 느껴지네요.
xml은 특히 작업을 하신 후에 자동포메팅을 사용하시는 것을 권장합니다.
A. 아무래도 미션의 의도는 제임스나 레아가 가장 잘 알지 않을까 싶습니다. 😅 |
A. 간단하게 생각하시면 됩니다. |
페이지가 제대로 넘어가지 않는 오류가 코드 단 두 줄 순서 때문이었습니다...😭 Generic을 사용한 BaseViewHolder를 어떻게 적용해야하는지 이해를 잘 못해서 우선 수업 시간에 배운 방법을 활용하여 수정해보았습니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
피드백 반영하시느라 고생하셨습니다. 👍
추가로 고민해볼만한 의견들을 코맨트로 작성하였으니, 충분히 고민해보시고 도전해보세요. 💪
다음 미션을 진행하시면서, 추가로 작성된 코맨트들도 반영해주세요. 🙏
@@ -50,7 +50,7 @@ class CartPresenter( | |||
} | |||
|
|||
private fun convertIdToProductModel(cartProducts: List<CartProduct>) = | |||
cartProducts.map { productRepository.find(it.id) }.map { it.toUiModel() } | |||
cartProducts.asSequence().map { productRepository.find(it.id) }.map { it.toUiModel() }.toList() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cartProducts.asSequence().map { productRepository.find(it.id) }.map { it.toUiModel() }.toList() | |
cartProducts.map { productRepository.find(it.id).toUiModel() } |
지금도 좋지만, 이렇게 작성해도 되지 않을까 싶네요. 😄
@@ -32,7 +32,7 @@ class CartActivity : AppCompatActivity(), CartContract.View { | |||
|
|||
private fun setUpActionBar() { | |||
supportActionBar?.setDisplayHomeAsUpEnabled(true) | |||
supportActionBar?.title = "Cart" | |||
supportActionBar?.title = TITLE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
액션바의 타이틀은 strings.xml에서 관리하는 것이 더 적합하지 않을까요?
// 최근 본 항목 | ||
val viewedProducts = recentViewedRepository.findAll().reversed() | ||
val viewedProductsItem = | ||
ProductListViewItem.RecentViewedItem(viewedProducts.map { convertIdToProductModel(it) }) | ||
productsListItems.add(viewedProductsItem) | ||
// 상품 리스트 | ||
productsListItems.addAll(products.map { ProductListViewItem.ProductItem(it) }) | ||
// 더보기 | ||
if(productListPagination.isNextEnabled) productsListItems.add(ProductListViewItem.ShowMoreItem()) | ||
view.showProducts(productsListItems) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드에 주석을 추가하여 각 코드블럭이 어떤 것을 의미하는지 이해하기가 쉬워졌네요. 👍
하지만 지금과 같은 코드 주석은, 오히려 하나의 함수가 두 가지 이상의 일을 한다는 것을 의미하기도 합니다.
그렇다면 함수는 한 가지 일만 하도록 개선해보면 어떨까요? (이하 관련 내용 동일)
val items = slot<List<CartViewItem>>() | ||
every { view.showProducts(capture(items)) } just runs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slot을 사용하여 실제 객체를 감싸도록 구현해주셨네요.
더미객체를 사용하지 않고 slot을 사용하게 되면 어떤 이점이 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
더미객체를 사용하는 것은 verify { view.showProducts(dummyObject) }
검증 코드를 사용하는 것을 말씀하시는 걸까요?!
verify만 사용하여 검증하는 것은 정말 그 함수가 실행됐는지와, 인자가 다르다면 어떤 인자가 다른지까지는 알 수 있습니다.
다만 slot을 사용하면 실제 들어가는 인자를 가져올 수 있기 때문에, 좀 더 구체적으로 테스트를 진행할 수 있다고 생각합니다.
assertEquals(itemsExpected.subList(0, 21), items.captured.subList(0, 21))
assertEquals(itemsExpected[itemsExpected.lastIndex].javaClass, items.captured[items.captured.lastIndex].javaClass)
위 코드는 지금 수정한 CartPresenterTest의 일부인데요! items의 마지막은 data class가 아닌 '더보기' 객체라, 단순히 비교가 불가능했습니다.
따라서 저는 마지막 요소만 같은 클래스인지 확인하도록 코드를 작성했습니다.
itemExpected가 정말로 실행됐는지 확인하기위해 verify를 사용했다면 '더보기' 객체가 달라 test가 fail되었을 것입니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
상황에 맞게 slot에 대해서 잘 알고 쓰셨네요. 👍
그러나, cartItems가 멤버 변수이다보니 fetchProducts 함수를 테스트하기가 어렵기에 slot을 사용하여 테스트 코드를 작성한 것으로 보이네요.
혹시 이 테스트 코드는 반드시 성공하기 위해 작성된 코드라고 느껴지지 않으신가요?
내가 원하는 데이터를 가지고 함수를 호출하고, 그 결괏값이 내가 원하는 데이터가 맞는지를 검증하는 방향으로 개선해보면 어떨까요?
참고로 slot과 같은 것을 "스파이" 기법이라고 합니다.
"스파이"는 실제로 사용하는 객체를 시뮬레이션하는 것이 아니라, 감시하고 있는 근본 객체와의 모든 상호작용을 기록합니다.
여기에서는 view.showProducts
함수의 파라미터로 사용한 객체의 모든 상호작용을 기록합니다.
그렇기에 "스파이"는 성공을 위한 테스트 코드를 작성하기 쉽습니다.
이러한 이유로 가급적이면 "스파이"는 현업에서는 잘 사용하지 않기도 합니다. 😅
val productsExpected = products.subList(0, 20).map { it.toUiModel() } | ||
assertEquals(viewedProductExpected, viewedProductsActual.captured) | ||
assertEquals(productsExpected, productsActual.captured) | ||
val itemsExpected = ProductListViewItem.RecentViewedItem(listOf(0, 1, 2).map { id -> products.find { it.id == id }?.toUiModel() }.sortedByDescending { it?.id } as List<ProductModel>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected에 로직이 들어가 있네요.
테스트 코드에는 순수 input과 output에 대해 작성해야 하며, 로직을 드러내면 안됩니다.
그렇게 되면 내부 구현 방식이 조금이라도 변경되게 되면, 테스트가 깨질 가능성이 높습니다.
author woowahan-pjs <[email protected]> 1683522324 +0900 committer hyemdooly <[email protected]> 1684889735 +0900 feat: set up the project [둘리] 1, 2단계 쇼핑 장바구니 제출합니다. (woowacourse#20) * docs: 기능 목록 작성 * feat: Domain 모듈 생성 * feat: 상훔 목록을 관리하는 Repository Interface 생성 * feat: 장바구니를 관리하는 CartRepository Interface 생성 * feat: 최근 본 상품 목록을 관리하는 Repository Interface 생성 * feat: 상품 정보를 담는 Product, 가격을 담는 Price 생성 * feat: 상품의 정보를 리턴하는 ProductMockRepository 구현 * feat: ProductListActivity 이미지 제외한 화면 구성 완료 * feat: 메인 색깔 변경 * feat: 액션 바에 장바구니 바로가기 메뉴 추가 * feat: 장바구니 화면의 액션바 안의 백버튼을 누르면 뒤로 이동하는 기능 추가 * feat: 상품 이미지 Glide 적용 * refactor: 패키지 이동 * refactor: ProductListActivity MVP 패턴 적용 * refactor: 장바구니 아이템 구현 * feat: 장바구니 리스트 뷰 구현 * feat: 상품 상세 뷰 구현 * feat: 장바구니 DataBase 생성 로직 구현 * feat: CartDbRepository 구현 * feat: ProductList OnClick 추가, DB 적용 * feat: 상품 상세 페이지 액션 바에 뒤로가기 버튼 추가 * refactor: MVP 패턴으로 변경 * feat: 최근 본 상품 리스트 추가 * feat: 최근 본 상품 datebase 기능 추가 * feat: CartProduct DB 적용, 삭제 기능 추가 * refactor: find 리턴값 수정 * refactor: notifyItemRemoved 적용 * feat: 페이지네이션 구현 중 * feat: 더보기 구현중 * feat: 더보기 구현 완료 * feat: 카트 페이지네이션 구현 중 * test: ProductListPresenterTest 작성 * refactor: ProductDetailPresenter cartRepository 추상화 적용 * test: ProductDetailPresenter 테스트 추가 * refactor: Pagination 적용중 * refactor: Pagination 적용중 * feat: Pagination Button onClick 수정 * fix: 페이지가 넘어가지 않는 오류 수정 * refactor: onCreate 코드 함수 분리 * refactor: 너무 긴 코드 개행 * refactor: 상수 선언 * refactor: constraintLayout 자식 뷰 match_parent -> 0dp로 수정 * refactor: xml formatting * refactor: asSequence 사용하여 map 호출 시간 단축 * refactor: Next, Prev Pagination 분리 * refactor: CartPageStatus 생성, CartAdapter 리팩터링 * refactor: LayoutManager xml로 이동 * refactor: 상수 선언 * refactor: CartAdapter에서 CartViewItem 사용하여 Items를 하나로 묶음 * refactor: ProductListActivity 리사이클러뷰 리팩터링 * test: package 이동, ProductListPresenterTest 수정, CartPresenterTest 작성 * refactor: 테스트 코드에서 whildcard import 수정 --------- Co-authored-by: hyunji1203 <[email protected]> refactor: 함수 간소화 refactor: supportActionBar Label 설정 삭제, manifest 이용 refactor: test 코드 수정 feat: ProductList 상품 장바구니 추가 뷰 수정 docs: 3단계 요구사항 정리 feat: 장바구니 버튼 생성 feat: ProductDetail 수량 선택 Dialog 구현 feat: CartActivity 하단 뷰 구현 feat: CartActivity Item layout 변경사항 수정 feat: cart badge 생성 feat: ProductListActivity List Count 구현 feat: ProductListActivity AppBar Cart Badge 구현 feat: ProductDetailActivity 마지막으로 본 상품 startActivity feat: ProductDetailActivity 다른 액티비티 실행 시 Stack 조절 feat: Cart 아이콘 장바구니 비었으면 Gone으로 수정 feat: ProductListPresenter에서 삭제했던 최근 본 상품 복구 refactor: domain 코드 이동, ktlintformat feat: CartActivity 상품 개수 업데이트 구현 feat: CartActivity, CartSystem 선택 구현 (리팩토링 필수) feat: ProductListActivity <-> CartActivity 데이터 동일하게 연동 refactor: CartActivity LiveData 활용 리팩터링 refactor: DataBinding format 수정 fix: CartActivity 버그 수정 refactor: ProductListActivity Presenter 리팩터링 fix: ProductListActivity Presenter 리팩터링 및 버그 수정 refactor: CartPresenterTest 코드 수정에 맞게 리팩터링 및 수정 refactor: ProductDetailPresenterTest 리팩터링 및 수정 refactor: ProductListPresenterTest 수정, CartPresenterTest 테스트 통과 안되는 부분 수정 refactor: formatting feat: 4단계 MockServer 구현 및 적용 refactor: 강제종료 버그 수정, 패키지 정리 fix: 최근 본 상품 반대로 나오는 버그 수정, ProductDetialActivity 다이얼로그 dismiss 추가 fix: 갯수 제한 걸리는 버그 수정 docs: README 업데이트
author woowahan-pjs <[email protected]> 1683522324 +0900 committer hyemdooly <[email protected]> 1684889735 +0900 feat: set up the project [둘리] 1, 2단계 쇼핑 장바구니 제출합니다. (woowacourse#20) * docs: 기능 목록 작성 * feat: Domain 모듈 생성 * feat: 상훔 목록을 관리하는 Repository Interface 생성 * feat: 장바구니를 관리하는 CartRepository Interface 생성 * feat: 최근 본 상품 목록을 관리하는 Repository Interface 생성 * feat: 상품 정보를 담는 Product, 가격을 담는 Price 생성 * feat: 상품의 정보를 리턴하는 ProductMockRepository 구현 * feat: ProductListActivity 이미지 제외한 화면 구성 완료 * feat: 메인 색깔 변경 * feat: 액션 바에 장바구니 바로가기 메뉴 추가 * feat: 장바구니 화면의 액션바 안의 백버튼을 누르면 뒤로 이동하는 기능 추가 * feat: 상품 이미지 Glide 적용 * refactor: 패키지 이동 * refactor: ProductListActivity MVP 패턴 적용 * refactor: 장바구니 아이템 구현 * feat: 장바구니 리스트 뷰 구현 * feat: 상품 상세 뷰 구현 * feat: 장바구니 DataBase 생성 로직 구현 * feat: CartDbRepository 구현 * feat: ProductList OnClick 추가, DB 적용 * feat: 상품 상세 페이지 액션 바에 뒤로가기 버튼 추가 * refactor: MVP 패턴으로 변경 * feat: 최근 본 상품 리스트 추가 * feat: 최근 본 상품 datebase 기능 추가 * feat: CartProduct DB 적용, 삭제 기능 추가 * refactor: find 리턴값 수정 * refactor: notifyItemRemoved 적용 * feat: 페이지네이션 구현 중 * feat: 더보기 구현중 * feat: 더보기 구현 완료 * feat: 카트 페이지네이션 구현 중 * test: ProductListPresenterTest 작성 * refactor: ProductDetailPresenter cartRepository 추상화 적용 * test: ProductDetailPresenter 테스트 추가 * refactor: Pagination 적용중 * refactor: Pagination 적용중 * feat: Pagination Button onClick 수정 * fix: 페이지가 넘어가지 않는 오류 수정 * refactor: onCreate 코드 함수 분리 * refactor: 너무 긴 코드 개행 * refactor: 상수 선언 * refactor: constraintLayout 자식 뷰 match_parent -> 0dp로 수정 * refactor: xml formatting * refactor: asSequence 사용하여 map 호출 시간 단축 * refactor: Next, Prev Pagination 분리 * refactor: CartPageStatus 생성, CartAdapter 리팩터링 * refactor: LayoutManager xml로 이동 * refactor: 상수 선언 * refactor: CartAdapter에서 CartViewItem 사용하여 Items를 하나로 묶음 * refactor: ProductListActivity 리사이클러뷰 리팩터링 * test: package 이동, ProductListPresenterTest 수정, CartPresenterTest 작성 * refactor: 테스트 코드에서 whildcard import 수정 --------- Co-authored-by: hyunji1203 <[email protected]> refactor: 함수 간소화 refactor: supportActionBar Label 설정 삭제, manifest 이용 refactor: test 코드 수정 feat: ProductList 상품 장바구니 추가 뷰 수정 docs: 3단계 요구사항 정리 feat: 장바구니 버튼 생성 feat: ProductDetail 수량 선택 Dialog 구현 feat: CartActivity 하단 뷰 구현 feat: CartActivity Item layout 변경사항 수정 feat: cart badge 생성 feat: ProductListActivity List Count 구현 feat: ProductListActivity AppBar Cart Badge 구현 feat: ProductDetailActivity 마지막으로 본 상품 startActivity feat: ProductDetailActivity 다른 액티비티 실행 시 Stack 조절 feat: Cart 아이콘 장바구니 비었으면 Gone으로 수정 feat: ProductListPresenter에서 삭제했던 최근 본 상품 복구 refactor: domain 코드 이동, ktlintformat feat: CartActivity 상품 개수 업데이트 구현 feat: CartActivity, CartSystem 선택 구현 (리팩토링 필수) feat: ProductListActivity <-> CartActivity 데이터 동일하게 연동 refactor: CartActivity LiveData 활용 리팩터링 refactor: DataBinding format 수정 fix: CartActivity 버그 수정 refactor: ProductListActivity Presenter 리팩터링 fix: ProductListActivity Presenter 리팩터링 및 버그 수정 refactor: CartPresenterTest 코드 수정에 맞게 리팩터링 및 수정 refactor: ProductDetailPresenterTest 리팩터링 및 수정 refactor: ProductListPresenterTest 수정, CartPresenterTest 테스트 통과 안되는 부분 수정 refactor: formatting feat: 4단계 MockServer 구현 및 적용 refactor: 강제종료 버그 수정, 패키지 정리 fix: 최근 본 상품 반대로 나오는 버그 수정, ProductDetialActivity 다이얼로그 dismiss 추가 fix: 갯수 제한 걸리는 버그 수정 docs: README 업데이트 docs: docs 삭제
안녕하세요, 리뷰어님! 다시 만나뵙게 되어 반갑습니다.ㅎㅎ
이번 미션은 시간이 부족하여 Cart의 Pagination이 원활하게 동작하지 않습니다.
버튼 Enabled 설정은 제대로 되었으나, 페이지 요소 변경이 잘 되지 않습니다. 이점 참고 부탁드립니다.
원활하게 작동하지 않는 부분은 다음 리뷰때 제대로 반영하여 다시 리뷰요청 드리겠습니다.