JuiceMaker [STEP 3] Logan, Blue
>안녕하세요, 웨더! (@sungpyo) **Team Bu-Lo**의 Blue & Logan입니다! ✋
>쥬스 메이커 프로젝트의 마지막! Step3 과정을 마무리 했습니다.
>목요일에 step3 구현을 완료한 상태였지만, 조금 더 개선해보고 싶은 욕심에 PR 권장 기한을 놓쳐서 늦어진 점 죄송합니다 🥹
이번에도 좋은리뷰 부탁드립니다!
감사합니다!💙
---
## ✅ 요구사항 분석 및 구현사항
**Step 3 재고 수정 기능구현**
- [x] 화면 제목 '재고추가' 및 '닫기' 버튼 구현
- [x] 닫기 터치 시 이전화면으로 복귀
- [x] 재고추가 화면 진입 시 과일의 현재 재고 수량 표시
- [x] +, -를 통한 재고 수정
- [x] iPhone12 외에 다른 시뮬레이터에서도 UI가 정상적으로 보일 수 있도록 오토 레이아웃 적용
## 🗝️ 학습 키워드
- Auto Layout
- Custom Class
- Outlet Collection
- Type Casting
- Stepper
## ☄️ 고민과 해결
1. STEP2에서는 label 및 stepper를 하나씩 Outlet으로 선언 후 연결하여 사용하였습니다.
선언부가 반복되는 문제를 개선하기 위해 `Outlet Collection`으로 개선해 보았습니다!
⏪ **ViewController.swift**의 기존 label 선언부 코드
```swift
@IBOutlet weak var strawberryLabel: UILabel!
@IBOutlet weak var bananaLabel: UILabel!
@IBOutlet weak var pineappleLabel: UILabel!
@IBOutlet weak var kiwiLabel: UILabel!
@IBOutlet weak var mangoLabel: UILabel!
```
⏩ **Outlet Collection**으로 수정
```swift
@IBOutlet var fruitStockLabels: [UILabel]!
```
`Outlet` 대신 `Outlet Collection`을 사용하여 5줄을 사용하여 선언해야했던 라벨들을 한 줄로 줄일 수 있어서 좋았습니다!
2. `protocol`을 이용하여 클래스로 정의되고 Collection으로 엮인 버튼들을 하나로 관리 해주었습니다.
🔍 **Protocols.swift**
```swift
protocol FruitStockManagable {
var fruitName: Fruits { get }
}
protocol JuiceOrderable {
var juice: Juices { get }
}
```
위의 두 `FruitStockManagable`, `JuiceOrderable`은 각 버튼들의 정체성을 확인하는데 꼭 필요한 요소들을 요구사항으로 정의하고 있습니다.
🔍 **FruitLabels.swift, FruitSteppers.swift, JuiceOrderButtons.swift**
```swift
// JuiceMakerVC의 재고를 나타내는 라벨의 클래스 정의중 "딸기"
class StrawberryStockLabel: UILabel, FruitStockManagable {
var fruitName: Fruits = .strawberry
// JuiceMakerVC의 주문 버튼 중 "딸기바나나쥬스"
class StrawberryJuiceButton: UIButton, JuiceOrderable {
var juice: Juices = .strawberry
}
// EditStockVC의 재고 수정하는 버튼 StepperButton의 정의중 딸기 부분
class StrawberryStepper: UIStepper, FruitStockManagable {
var fruitName: Fruits = .strawberry
}
}
```
3. 위에서 진행한 1, 2번의 리팩토링 절차에 따라 메서드 내에서 반복적으로 선언하고 할당해야 했던 작업들을 `for` 혹은 `foreach`를 사용하여 선언, 할당하는 것으로 리팩토링 진행하였습니다.
- 많은 메서드의 리팩토링을 진행하였으나, 한 가지 예시로 보여드리도록 하겠습니다.
⏪ **ViewController.swift**의 기존 `setFruitLabel()` 메서드
```swift
@objc private func setFruitLabel() {
strawberryLabel.text = String(FruitStore.shared.fruits[.strawberry]?.stock ?? 0)
bananaLabel.text = String(FruitStore.shared.fruits[.banana]?.stock ?? 0)
pineappleLabel.text = String(FruitStore.shared.fruits[.pineapple]?.stock ?? 0)
kiwiLabel.text = String(FruitStore.shared.fruits[.kiwi]?.stock ?? 0)
mangoLabel.text = String(FruitStore.shared.fruits[.mango]?.stock ?? 0)
}
```
⏩ **리팩토링**을 진행한 `setFruitLabel()` 메서드
```swift
private func setFruitLabel() {
for fruitStockLabel in fruitStockLabels {
guard let label = fruitStockLabel as? FruitStockManagable,
let stockInt = FruitStore.shared.fruits[label.fruitName]?.stock else { return }
let changeStockIntToString = String(stockInt)
fruitStockLabel.text = changeStockIntToString
}
}
```
`protocol`로 타입캐스팅을 진행해 보는 경험을 로건 덕분에 처음 해볼 수 있었고, protocol의 활용법에 대해 조금 더 알게 된 좋은 경험이었습니다! 🤓
4. Alert 띄우는 메서드를 하나로 통합하였습니다.
- 기존에는 선택지가 1개인(예) 알림창과 선택지가 2개인(예 / 아니오) 알림창을 두개 따로 선언하여 동작했지만 같은 작업을 하는 메서드가 2개인것이 바람직 하지 않다고 생각하여 이후 리팩토링 작업에서 하나로 통합하였습니다.
🔍 **ViewController.swift**
```swift
private func popAlert(with message: String?, error: Error?) {
if message != nil && error != nil { return }
if let message {
let alert = UIAlertController(title: message, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "예", style: .default, handler: nil))
present(alert, animated: true)
}
if let error {
let alert = UIAlertController(title: error.localizedDescription, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "예", style: .default, handler: {_ in
self.moveToEditStockView()}))
alert.addAction(UIAlertAction(title: "아니오", style: .destructive, handler: nil))
present(alert, animated: true)
}
}
```
---
## 🙋 질문
1. `BoilerPlateCode`를 수정하라고 하셨는데 처음듣는 단어라 일단은 "반복되는 코드를 줄여라" 라고 이해하였습니다.
[🔗 위키백과](https://ko.wikipedia.org/wiki/%EC%83%81%EC%9A%A9%EA%B5%AC_%EC%BD%94%EB%93%9C)에 따르면 보일러 플레이트는 `상용구 코드`로 `최소한의 수정만을 거쳐 여러 곳에 필수적으로 사용되는 코드를 말한다. 이와 같은 코드는 최소한의 작업을 하기 위해 많은 분량의 코드를 작성해야 하는 언어에서 자주 사용된다.`라고 명시되어 있는데, 여러 곳에서 반복적으로 사용 시 최소한으로 수정하여 다른 alert에서도 사용할 수 있도록 개선하라고 요청 주신 부분이 맞을까요?
- 저희가 웨더가 제안한 의도와 맞게 코드를 리팩토링한 것인지 궁금합니다!
2. 저희가 `FruitStore`를 싱글턴 패턴으로 구현함으로 인해 데이터 전달에 대한 큰 고민 없이 프로젝트를 진행할 수 있었습니다. 싱글턴의 이점만큼 단점도 있을 것이라 생각되는데요, 현업자 입장에서 바람직한 채택이라 생각하시는지 궁금합니다.
- 현업에서는 싱글턴 패턴을 지양하는 분위기라는 말을 들었지만, 저희 프로젝트가 정말 심플하기도 해서 문제되지 않을것 같다 판단하여 싱글턴을 이용했습니다.