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

[iOS][Aiden, BMO] team-07 sideDish 2주차 PR 02 #64

Open
wants to merge 15 commits into
base: team-07
Choose a base branch
from

Conversation

BMO5
Copy link

@BMO5 BMO5 commented Apr 30, 2021

작업 내용

상세페이지 구현

  • 상세페이지 클릭 시 상세페이지 API요청
  • 요청된 데이터를 통해 상세페이지 UI 구현 (ScrollView 사용)

메뉴 주문하기 구현

  • 재고가 없을 시 "일시 품절" 상태
  • 주문한 수량이 재고를 초과할 시 알람
  • 주문에 성공할 시 초기 페이지로 되돌아가기

수정사항

  • Cell이 재사용 될 때, 이미지가 잘못 오버랩 되는 경우를 대비하여 이미지를 초기화 시키는 함수를 따로 만들었었는데,
    코멘트에 남겨주신 prepareForReuse가 셀이 재사용 될 때 호출되기에 이곳에 초기화 코드를 작성하였습니다.

  • 본래는 API 요청을 하는 객체에 url이 들어있었는데, URL 객체를 분리하였습니다.

  • data를 가진 객체의 이름을 구체화하였습니다. Data, Get과 같이 의미가 포괄적인 부분은 계속해서 신경써야겠습니다.


고민과 해결

  • Network를 ParsingManager.decodeData와 같이 제네릭 타입으로 받을 수 있도록 하려고 했는데 잘 되지 않았습니다.
    type에서 에러가 나는 것 같은데 조금 더 공부해보고 할 수 있다면 다음 프로젝트에 적용해볼 생각입니다.

  • FetchMenuResponseUseCase에서 네트워크를 통해 데이터를 가져오는걸로 구현을 했었는데, 지난 PR의 피드백을 보고 수정을 하려 했습니다.
    하지만 저희가 처음부터 MVVM을 사용하려고 의도했던 것이 아니라 ViewModel을 통해 CollectionView를 깔끔히 하면 좋을 것 같아서,
    개발 도중에 이를 살짝 도입한 것이라 현재 클린아키텍처와 구조에 대해 명확히 인지를 하지 못해 수정하지 못했습니다.


질문거리

  • 현재 DetailMenuViewController에서 주문하기 버튼인 orderButton을 누르면 네트워크를 통해 Request를 날리게 됩니다.
    지난번 PR에서 현재 구조에서는 뷰컨트롤러에서 네트워크 로직을 가지고 있는 것이 적절한지 생각해보면 좋겠다고 하셨는데, 이처럼 뷰를 통해 네트워크를 보내는 것은 괜찮은지, 아니면 이 부분 또한 분리해서 다른 단에서 하는 것이 좋은지 궁금합니다!

@Sonjh1306 Sonjh1306 requested a review from mienne April 30, 2021 09:53
@Sonjh1306 Sonjh1306 added the review-iOS iOS 리뷰 label Apr 30, 2021
}

override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = true
Copy link

Choose a reason for hiding this comment

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

super.viewWillDisappear 호출이 빠진 이유가 있을까요? 아니면 코드 작성사하다가 실수로 빠진걸까요?

self.detailScrollView.normalPrice.attributedText = self.detailMenuViewModel.salePrice
self.detailScrollView.deliveryInfo.text = self.detailMenuViewModel.deliveryInfo
self.detailScrollView.deliveryFee.text = self.detailMenuViewModel.deliveryFee
self.detailScrollView.totalPrice.text = self.detailMenuViewModel.normalPrice
Copy link

Choose a reason for hiding this comment

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

DetailMenuViewController 외에도 여러 뷰컨트롤러에서 detailScrollView.deliveryFee.text 값을 설정한다고 가정하고 추후 이 곳이 변경되어야 할 때 detailScrollView.deliveryFee.text 가지고 있는 모든 뷰컨트롤러를 찾아서 수정해야 합니다. 결론적으로 수정해야 할 범위가 넓어집니다. 그러나 detailScrollView.setup(detailMenuViewModel) 같이 detailScrollView가 내부 로직을 가지고 있다면 수정해야 할 범위도 detailScrollView 내부 즉, 한 곳만 수정하면 되죠! 이처럼 관련된 로직이 한 곳으로 모여 있다면 수정해야 할 범위도 좁아지고 변경 대상도 명확해집니다. 극단적으로 높은 응집도가 항상 좋은 것은 아니지만 적어도 응집도가 높은 객체가 많을수록 좋은 설계가 될 확률이 높습니다. 한 가지 예로 obj.prop = value 처럼 속성을 가져와서 값을 셋팅해야 하는 경우가 많아진다면 과연 이 곳에서 로직을 가지고 있는게 적당한지 의심해 볼 여지가 있습니다!

self.detailScrollView.orderButton.setTitle("일시 품절", for: .normal)
self.detailScrollView.orderButton.setTitleColor(.systemGray2, for: .normal)
self.detailScrollView.orderButton.backgroundColor = UIColor.systemGray5
self.detailScrollView.orderButton.isEnabled = false
Copy link

Choose a reason for hiding this comment

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

이 부분도 위와 같은 이유와 DetailMenuViewController 객체 입장으로 생각한다면 detailMenuViewModel.stock 이 없을 때 detailScrollView의 orderButton이 어떻게 변해야 할지 알 필요가 없죠! 그렇기 때문에 orderButton이 바뀌는 로직을 detailScrollView로 숨기게 좋습니다🙂

Copy link

Choose a reason for hiding this comment

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

추가적으로 configureThumbnailImageSize, configureBadges, configureThumbnailImage 도 뷰컨트롤러에서 가지고 있어야 할지, detailScrollView 가지고 있어야 할지 생각해볼 필요가 있습니다🤔


session.dataTask(with: request){ (_, response, _) in
guard let response = response as? HTTPURLResponse else { return }
if (200 ..< 299) ~= response.statusCode {
Copy link

Choose a reason for hiding this comment

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

(200 ..< 299) ~= response.statusCode 조건 잘 활용하셨네요👏

@@ -34,4 +27,41 @@ class DataTaskManager {
}.resume()
}

static func sendDetailRequest(url: URL?, completion: @escaping (Result<DetailMenu, Error>) -> Void) {
guard let url = url else {
print("The URL is inappropriate.")
Copy link

@mienne mienne May 3, 2021

Choose a reason for hiding this comment

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

앱 디버깅을 위해서 에러 메세지를 남길 때 print도 사용할 수 있지만 로깅 관련 오픈소스나 Apple 지원하는 OSLog 프레임워크를 활용하여 상세한 로그를 남길 수 있습니다. 시간이 될 때 Unified Logging and Activity Tracing WWDC 영상 보세요🙂

}

func setBorderWidth() {
let gray = UIColor(red: 100/255, green: 100/255, blue: 100/255, alpha: 0.3).cgColor
Copy link

@mienne mienne May 3, 2021

Choose a reason for hiding this comment

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

실제 프로젝트 진행하다보면 자주 사용하는 색상이 생깁니다. 그래서 주로 사용하는 색상은 한 곳에서 관리합니다. UIColor extension 할 수 있고 Color Asset으로도 관리할 수 있습니다. 이 부분은 꼭 알고 넘어가세요.

override func awakeFromNib() {
super.awakeFromNib()
setupBadgeShape()
}

private func setupBadgeLabelConstraint() {

override func prepareForReuse() {
Copy link

Choose a reason for hiding this comment

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

super.prepareForReuse() 의도적으로 호출을 제외하는 것이 아니라면 가급적 호출하는 코드는 꼭 넣어주세요.

@@ -42,28 +45,19 @@ class MenuCell: UICollectionViewCell {
func configure(menu: MenuViewModel) {
self.titleLabel.text = menu.title
self.bodyLabel.text = menu.body
self.currentPriceLabel.text = menu.sPrice
self.currentPriceLabel.text = "\(menu.sPrice)원"
self.pastPriceLabel.attributedText = menu.nPrice
guard let url = URL(string: menu.image) else { return }
Copy link

Choose a reason for hiding this comment

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

URL(string: menu.image) nil이라면 아래 코드 중에서 setBadge 함수가 호출되지 않아도 괜찮은가요?


@IBAction func pressedOrderButton(_ sender: UIButton) {
if self.orderViewModel.isOrderAvailable(stock: self.detailMenuViewModel.stock) {
APIRequestManager.orderPost(orderCount: OrderMenuRequest(count: self.orderViewModel.orderCount), url: URLManager.detailMenu(categoryId: self.detailMenuViewModel.categoryId, detailHash: self.detailMenuViewModel.detailHash), completion: { result in
Copy link

Choose a reason for hiding this comment

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

새로운 코드를 작성할 때 본인이 어떻게 작성했는지 늘 염두하고 일관적으로 작성하는 것이 좋습니다. 그래야 새로운 사람이 오더라도 그 기준을 보고 금방 적응할 수 있습니다. FetchDetailMenuUseCase 작성한 것처럼 이 부분도 새로운 UseCase를 분리할 수 있지 않을까요?

let viewModelList: [MenuViewModel] = menuList.map() { menu in
let nPrice = stringToAttributedString(menu.nPrice)
let viewModel = MenuViewModel(image: menu.image, title: menu.title, body: menu.description, sPrice: menu.sPrice, nPrice: nPrice, badges: menu.badge ?? [])
let price = menu.normalPrice == nil ? nil : String(menu.normalPrice!)
Copy link

Choose a reason for hiding this comment

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

강제 언래핑은 가급적 사용 자제하세요. 값이 잘못 들어온다면 앱 크래시가 발생합니다.

Suggested change
let price = menu.normalPrice == nil ? nil : String(menu.normalPrice!)
var price: String?
if let normalPrice = menu.normalPrice {
price = String(normalPrice)

@mienne
Copy link

mienne commented May 3, 2021

현재 DetailMenuViewController에서 주문하기 버튼인 orderButton을 누르면 네트워크를 통해 Request를 날리게 됩니다.
지난번 PR에서 현재 구조에서는 뷰컨트롤러에서 네트워크 로직을 가지고 있는 것이 적절한지 생각해보면 좋겠다고 하셨는데, 이처럼 뷰를 통해 네트워크를 보내는 것은 괜찮은지, 아니면 이 부분 또한 분리해서 다른 단에서 하는 것이 좋은지 궁금합니다!

본인 기준에 따라 뷰에서 네트워크 로직을 가져도 되고 가지지 않아도 됩니다. View, ViewController, ViewModel, Model, UseCase, Repository 등 각 객체의 역할에 대한 책임을 어디까지 할당할 것인가에 대한 문제라고 생각합니다. 제 기준에서 View에서 네트워킹을 바로 호출하지 않습니다. 왜냐하면 View의 역할은 값을 받아 표시만 잘하면 되기 때문에 View에서 표시하는 값들이 네트워킹 호출인지 로컬 스토리지 로직인지 알 필요 없다고 생각합니다. 이 부분은 제 기준으로 설명드렸지만 객체 역할, 책임을 어디까지 설정할지 많은 연습과 공부가 필요합니다. 저도 아직 어렵다고 느끼고 있습니다🤣

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review-iOS iOS 리뷰
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants