Skip to content
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

Merged
merged 52 commits into from
May 14, 2023

Conversation

hyemdooly
Copy link

@hyemdooly hyemdooly commented May 11, 2023

안녕하세요, 리뷰어님! 다시 만나뵙게 되어 반갑습니다.ㅎㅎ
이번 미션은 시간이 부족하여 Cart의 Pagination이 원활하게 동작하지 않습니다.
버튼 Enabled 설정은 제대로 되었으나, 페이지 요소 변경이 잘 되지 않습니다. 이점 참고 부탁드립니다.
원활하게 작동하지 않는 부분은 다음 리뷰때 제대로 반영하여 다시 리뷰요청 드리겠습니다.

  1. 페이지네이션의 캐싱과정에서 이전 아이템들과 현재 아이템, 이후 아이템을 모두 따로 저장하여 보관하려고 했는데, 요소를 이미 로딩한 후에 데이터베이스에 변경이 있는 경우 변경 사항이 적용되지 않아 실시간으로 데이터베이스에서 불러올 수 밖에 없었습니다. 이번 미션의 의도가 이것이 맞는지 잘 모르겠습니다.
  2. UIModel과 DomainModel에서 계속 혼동이 왔습니다. Parcelable을 위해서 UIModel을 따로 만들어주었는데, 내용물은 모두 같고 implement만 달라지는 것이라 제가 맞게 잘 설계한건지 모르겠습니다. 어느 범위까지 UIModel을 사용해야할까요?

hyemdooly and others added 30 commits May 9, 2023 14:37
Copy link

@BeokBeok BeokBeok left a comment

Choose a reason for hiding this comment

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

쇼핑 장바구니 1,2단계 미션하시느라 고생하셨습니다. 👍
고민해볼만한 의견들을 코맨트로 작성하였으니, 충분히 고민해보시고 도전해보세요. 💪

Comment on lines 18 to 26
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()
}

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) {

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) {

Choose a reason for hiding this comment

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

Suggested change
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> {

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() }

Choose a reason for hiding this comment

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

filter, map 과 같은 컬렉션 함수들은 매번 새로운 컬렉션을 만들어내요.
map을 한번만 사용하도록 개선해보면 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

asSequence를 사용하여 중간에 새로운 컬랙션을 생성하지 않고, 마지막 연산에서 생성하도록 수정했습니다!

Comment on lines 18 to 19
android:layout_width="match_parent"
android:layout_height="match_parent"

Choose a reason for hiding this comment

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

Suggested change
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

Choose a reason for hiding this comment

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

Suggested change
<androidx.constraintlayout.widget.ConstraintLayout
<View

라인을 그리고 싶다면, 가벼운 View를 활용하는 것을 권장합니다.

app:layout_constraintTop_toTopOf="@id/text_price_label"
tools:text="99,800원" />

<androidx.appcompat.widget.AppCompatButton

Choose a reason for hiding this comment

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

AppCompatButton과 Button의 차이점은 무엇일까요?

Copy link
Author

@hyemdooly hyemdooly May 13, 2023

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 설정도 허용됩니다!

Copy link

@BeokBeok BeokBeok May 13, 2023

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 속성을 적용하면 색상이 변경될 것입니다.

Copy link
Author

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보다 좋다고 말할 수 있나요?

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을 사용합니다. 😄

Copy link
Author

Choose a reason for hiding this comment

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

그렇군요..! 안드로이드의 세계는 알아야할 것이 참 많네요ㅎㅎ 감사합니다!!😄

Comment on lines 20 to 21
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="15dp"

Choose a reason for hiding this comment

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

Suggested change
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="15dp"
android:padding="15dp"

상하좌우 패딩이 동일하다면, padding 속성을 활용할 수 있어요

type="woowacourse.shopping.view.productlist.ProductListAdapter.OnItemClick" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout

Choose a reason for hiding this comment

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

가급적이면 들여쓰기를 잘 해주시는 것이 좋습니다.
들여쓰기가 없으니 최상단 element로 느껴지네요.
xml은 특히 작업을 하신 후에 자동포메팅을 사용하시는 것을 권장합니다.

@BeokBeok
Copy link

페이지네이션의 캐싱과정에서 이전 아이템들과 현재 아이템, 이후 아이템을 모두 따로 저장하여 보관하려고 했는데, 요소를 이미 로딩한 후에 데이터베이스에 변경이 있는 경우 변경 사항이 적용되지 않아 실시간으로 데이터베이스에서 불러올 수 밖에 없었습니다. 이번 미션의 의도가 이것이 맞는지 잘 모르겠습니다.

A. 아무래도 미션의 의도는 제임스나 레아가 가장 잘 알지 않을까 싶습니다. 😅
데이터를 불러오는 방식에는 앱의 컨셉에 따라 달라질 것 같습니다.
지금과 같은 경우에는 관련 Activity가 종료되면 그 때 불러오도록 하는 방향으로 하는 것이 좋을 것 같습니다.
아래 글이 도움이 될 것 같아요.
ref. https://developer.android.com/training/basics/intents/result

@BeokBeok
Copy link

UIModel과 DomainModel에서 계속 혼동이 왔습니다. Parcelable을 위해서 UIModel을 따로 만들어주었는데, 내용물은 모두 같고 implement만 달라지는 것이라 제가 맞게 잘 설계한건지 모르겠습니다. 어느 범위까지 UIModel을 사용해야할까요?

A. 간단하게 생각하시면 됩니다.
View와 관련되어 있거나, View에서 보여주기 위한 데이터가 필요한 경우 만들어주시면 됩니다.
Parcelable은 View와 관련이 있기 때문에 분리해주시는 것이 좋습니다.
Parcelable 떄문에 별도로 만드는 것이 부담되신다면, 데이터를 각각 intent로 전달해주시면 됩니다. 😄

@hyemdooly
Copy link
Author

hyemdooly commented May 13, 2023

페이지가 제대로 넘어가지 않는 오류가 코드 단 두 줄 순서 때문이었습니다...😭
지금은 장바구니까지 모든 기능이 제대로 작동되고 있는 상태입니다!

Generic을 사용한 BaseViewHolder를 어떻게 적용해야하는지 이해를 잘 못해서 우선 수업 시간에 배운 방법을 활용하여 수정해보았습니다.
Adapter에서 아이템을 Presenter에 있는 것을 참조하여, Presenter에서 목록을 바꾸면 notify를 통해 갱신할 수 있게 설계했는데요, 조금 복잡한 느낌이 드네요...

Copy link

@BeokBeok BeokBeok left a 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()

Choose a reason for hiding this comment

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

Suggested change
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

Choose a reason for hiding this comment

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

액션바의 타이틀은 strings.xml에서 관리하는 것이 더 적합하지 않을까요?

Comment on lines 20 to 29
// 최근 본 항목
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)

Choose a reason for hiding this comment

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

코드에 주석을 추가하여 각 코드블럭이 어떤 것을 의미하는지 이해하기가 쉬워졌네요. 👍
하지만 지금과 같은 코드 주석은, 오히려 하나의 함수가 두 가지 이상의 일을 한다는 것을 의미하기도 합니다.
그렇다면 함수는 한 가지 일만 하도록 개선해보면 어떨까요? (이하 관련 내용 동일)

Comment on lines +88 to +89
val items = slot<List<CartViewItem>>()
every { view.showProducts(capture(items)) } just runs

Choose a reason for hiding this comment

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

slot을 사용하여 실제 객체를 감싸도록 구현해주셨네요.
더미객체를 사용하지 않고 slot을 사용하게 되면 어떤 이점이 있을까요?

Copy link
Author

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되었을 것입니다.

Copy link

@BeokBeok BeokBeok May 15, 2023

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>)

Choose a reason for hiding this comment

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

expected에 로직이 들어가 있네요.
테스트 코드에는 순수 input과 output에 대해 작성해야 하며, 로직을 드러내면 안됩니다.
그렇게 되면 내부 구현 방식이 조금이라도 변경되게 되면, 테스트가 깨질 가능성이 높습니다.

@BeokBeok BeokBeok merged commit a2c85ac into woowacourse:hyemdooly May 14, 2023
hyemdooly pushed a commit to hyemdooly/android-shopping-cart that referenced this pull request May 24, 2023
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 업데이트
hyemdooly pushed a commit to hyemdooly/android-shopping-cart that referenced this pull request May 24, 2023
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 삭제
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.

None yet

3 participants