-
Notifications
You must be signed in to change notification settings - Fork 72
[하티] 1, 2단계 쇼핑 장바구니 제출합니다. #19
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
Conversation
- 아이템이 없으면 영역 자체가 보이지 않게 된다
galcyurio
left a comment
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.
offset 기반의 페이징 처리와 MVP 패턴으로 잘 구현해주셨네요 👏
이번 미션에서도 다른 미션과 마찬가지로 테스트 코드를 작성해보세요.
현재 발생하는 오류에 대해서는 질문을 남겨두었으니 최대한 질문에 답해보고 마지막으로 힌트를 열어보세요.
| override fun onCreate(db: SQLiteDatabase?) { | ||
| db?.execSQL(SQL_CREATE_ENTRIES) | ||
| } |
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.
그리고 CartRepositoryImpl에서 CartDbHelpe가 제대로 create되지 않아 오류가 발생하는 경우가 있었는데,
CartDbHelperTest를 실행하고 다시 app을 실행하면 제대로 동작하더라구요.. 원인은 아직 찾지 못했지만, 혹시 몰라 말씀드려요!
각 질문에 대해 스스로 답변해보세요.
onCreate()함수는 언제 호출되는걸까요?- RecentlyViewedProductDbHelper, CartDbHelper의
onCreate()함수가 둘 다 호출되나요? - 하나의 DbHelper의
onUpgrade()에서 버전이 하나 올라갔으면 다른 하나의onUpgrade()에서는 어떻게 될까요?
힌트
onCreate()와 onUpgrade()를 하나로 합쳐볼까요?
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.
추가로 Robolectric을 이용해서 DB가 제대로 생성되었는지 확인하는 테스트코드를 작성해볼까요?
Robolectric 대신 androidTest 디렉토리에서 진행하셔도 좋습니다.
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() 함수는 언제 호출되는걸까요?
readableDatabase 혹은 writableDatabase 가 실행될 때, 데이터베이스가 존재하지 않는 경우에만 open 됩니다. 즉, 시스템 안에 database가 없는 경우 딱 한 번만 호출됩니다.
RecentlyViewedProductDbHelper, CartDbHelper의 onCreate() 함수가 둘 다 호출되나요?
에뮬레이터에서 데이터를 지우고 처음부터 로그를 찍어보았는데, 앱을 실행하면 RecentlyViewedProductDbHelper는 onCreate() 되었지만 CartDbHelper의 onCreate()는 호출되지 않았어요.
저는 이미 RecentlyViewedProductDbHelper에서 db가 생성되었기 때문에 실행이 안 되는 것 같다고 이해하고 있는데, 이 이유가 맞을까요? 🤔
하나의 DbHelper의 onUpgrade()에서 버전이 하나 올라갔으면 다른 하나의 onUpgrade()에서는 어떻게 될까요?
역시나 다른 하나의 onUpgrade() 역시 호출되지 않을 것 같아요. 버전이 올라가면 그에 맞게 처음 (이 코드상에서는 RecentlyViewedProductDbHelper에서 db를 오픈할 때)에 onUpgrade가 실행이 될 것이고, 그 이후에 open을 할 때는 이미 버전이 업그레이드 된 db가 존재하기 때문에 호출되지 않을 것 같아요.
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.
하나의 프로젝트에서 하나의 데이터베이스만 만들고 관리할 수 있도록 프로젝트 내에 DBHelper 파일을 하나만 만들었고,
그 안에서 여러 개의 테이블을 한 번에 모두 생성하도록 수정해보았습니다! 커밋 내역
이렇게 하는 방법이 맞을까요??
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.
의도대로 잘 해주셨습니다 👍️
이번에 SQLite에서 DB가 어떻게 생성되고 업데이트되는지에 대해서는 확실하게 이해하셨네요!
...in/java/woowacourse/shopping/database/recentlyviewedproduct/RecentlyViewedProductDbHelper.kt
Outdated
Show resolved
Hide resolved
| ) : ProductDetailContract.Presenter { | ||
| override fun loadProduct(productId: Long) { | ||
| productRepository.findById(productId)?.run { | ||
| view.setProduct(ProductDetailUIState.from(this)) |
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.
UiState를 분리했을 때 어떤 이점이 있을까요?
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.
사실 지금까지 UiState를 분리하는 것의 이점을 크게 느끼지는 못했던 것 같아요..! 현재 미션에서 느끼기에는, 우선 뷰에 그려질 때 필요한 상태들이 무엇인지 직관적으로 알 수 있다는 장점이 있는 것 같아요. 또한 상태들을 view에 전달할 때 readable하게 전달하여 부수효과를 줄일 수 있지 않을까 하는 생각이 듭니다!
공식문서나 블로그 등을 찾아보면서 느낀 점은 MVVM 패턴에서 관찰 가능한 데이터와 함께 주로 쓰이는 것 같고, 화면의 상태를 나타내는 1개만의 스트림을 둠으로써 ui가 변경되는 지점을 명확히 하고 가독성을 높여준다는 것 같았어요!
아직 잘 와닿지는 않지만, 이러한 내용들이 제가 잘 이해하고 있는 게 맞을까요? :) ㅠㅠ
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.
말씀하신 것들 모두 맞습니다!
개인적으로 UiState가 유용했던 경험은 복잡한 형태의 UI를 만들 때 였습니다.
2~3개의 domain DTO를 받아서 UiState를 만들면 테스트도 쉽고 가독성도 좋았거든요 ㅎㅎ
app/src/main/java/woowacourse/shopping/ui/products/ProductListPresenter.kt
Show resolved
Hide resolved
| presenter.loadProducts(PAGE_SIZE, offset) | ||
| offset += PAGE_SIZE | ||
| } | ||
|
|
||
| private fun initLoadingButton() { | ||
| binding.btnLoading.setOnClickListener { | ||
| presenter.loadProducts(PAGE_SIZE, offset) | ||
| offset += PAGE_SIZE |
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.
페이징 처리와 관련된 코드들을 별도 클래스나 함수로 분리해볼까요?
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.
피드백이 반영되지 않았습니다~
galcyurio
left a comment
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(db: SQLiteDatabase?) { | ||
| db?.execSQL(SQL_CREATE_ENTRIES) | ||
| } |
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.
의도대로 잘 해주셨습니다 👍️
이번에 SQLite에서 DB가 어떻게 생성되고 업데이트되는지에 대해서는 확실하게 이해하셨네요!
| } | ||
|
|
||
| @Test | ||
| fun 장바구니에_담은_상품을_삭제하면_장바구니_목록에_해당_상품이_삭제된다() { |
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.
@Test
fun `이렇게 할 수도 있어요`() {
}|
|
||
| @Test | ||
| fun 장바구니에_담은_상품을_삭제하면_장바구니_목록에_해당_상품이_삭제된다() { | ||
| every { view.setCartItems(any()) } answers { } |
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.
relaxed mock에 대해서 공부해보고 불필요한 stubbing 코드들을 제거해볼까요?
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.
relaxed mock을 사용하게 되면 메소드들이 0, false, "" 와 같은 값을 반환하게 되어서 굳이 번거롭게 every를 사용할 필요가 없어집니다! 다음 단계에 반영하겠습니다 :)
| presenter.deleteCartItem(2) | ||
| val product = FakeProductRepository.findById(2) | ||
|
|
||
| Assert.assertEquals(false, FakeCartRepository.findAll().contains(product)) |
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.
테스트 코드에서는 가독성이 중요합니다.
조금 더 가독성을 향상시켜주는 assertion library를 활용해보시면 어떨까요?
개인적으로는 Truth를 추천드립니다.
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.
테스트 코드에서는 assert문을 작성할 때, 보통 static import 기능을 활용해요!
| ) : ProductDetailContract.Presenter { | ||
| override fun loadProduct(productId: Long) { | ||
| productRepository.findById(productId)?.run { | ||
| view.setProduct(ProductDetailUIState.from(this)) |
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.
말씀하신 것들 모두 맞습니다!
개인적으로 UiState가 유용했던 경험은 복잡한 형태의 UI를 만들 때 였습니다.
2~3개의 domain DTO를 받아서 UiState를 만들면 테스트도 쉽고 가독성도 좋았거든요 ㅎㅎ
| presenter.loadProducts(PAGE_SIZE, offset) | ||
| offset += PAGE_SIZE | ||
| } | ||
|
|
||
| private fun initLoadingButton() { | ||
| binding.btnLoading.setOnClickListener { | ||
| presenter.loadProducts(PAGE_SIZE, offset) | ||
| offset += PAGE_SIZE |
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.
피드백이 반영되지 않았습니다~
| import woowacourse.shopping.domain.Product | ||
| import woowacourse.shopping.repository.CartRepository | ||
|
|
||
| object FakeCartRepository : CartRepository { |
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.
mock 객체 대신에 Fake 구현체를 활용하셨네요 👍️
안녕하세요, 토리! :)
이번 안드로이드 장바구니 미션 피드백 받게 된 하티라고 합니다! 😄
다시 뵙게 되어서 반가워요!!
이번 미션을 진행하면서는 시간 부족을 많이 느꼈어요..
그래서 아마도 구현하지 못한 기능이나 버그들이 많이 남아있을 것 같아요 :(
그리고
CartRepositoryImpl에서CartDbHelpe가 제대로 create되지 않아 오류가 발생하는 경우가 있었는데,CartDbHelperTest를 실행하고 다시 app을 실행하면 제대로 동작하더라구요.. 원인은 아직 찾지 못했지만, 혹시 몰라 말씀드려요!이번 미션에서는 시간도 부족했고, 여러모로 아쉬움이 많이 남는 것 같습니다.
그럼에도 리뷰 많이 주시면 열심히 반영하면서 개선해볼게요!
미리 리뷰에 감사드립니다 ;>