---
tags: README
---
# Juice Maker [STEP 2]
> 과일의 재고를 화면에 출력하고,
> 사용자가 주문 버튼을 누르면 해당 쥬스를 만들기 위해 필요한 과일의 수량을 소진합니다.
> 과일의 재고가 다 떨어졌을 경우 쥬스를 만들 수 없으며,
> 재고수정 버튼을 통해 재고를 추가하는 화면으로 이동합니다.
## 📚 목차
- [팀원소개](#-팀원-소개)
- [타임라인](#-타임라인)
- [실행화면](#-실행화면)
- [트러블 슈팅](#-트러블-슈팅)
- [참고자료](#-참고자료)
## 🧑💻 팀원 소개
| <img src="https://i.imgur.com/PmVVM2M.png" width="200"/> | <img src="https://github.com/devKobe24/BranchTest/blob/main/IMG_5424.JPG?raw=true" width="200" height="200"/> |
| :-: | :-: |
| [**maxhyunm**](https://github.com/maxhyunm) | [**Kobe**](https://github.com/devKobe24) |
## ⏰ 타임라인
프로젝트 진행 기간 | 23.05.11.(목) ~ 23.05.16.(화)
| 날짜 | 진행 사항 |
| -------- | -------- |
| 23.05.11.(목) |'재고수정'버튼을 터치하여 '재고 추가'화면으로 이동하는 기능 구현, StockViewController 생성.<br/>쥬스 주문 버튼 전체 연결 및 Alert 생성.<br/>JuiceMaker 주문 메서드 1차 연결.<br/>버튼 분기처리 및 재고 없을 경우 재고추가 화면으로 이동하는 로직 구현. |
| 23.05.12.(금) | showStock 함수 생성, getInventoryStatus 함수 생성, FruitStore 타입 변경.<br/>쥬스 제조 완료 얼럿 추가, 레이블명 수정, 스토리지 weak 설정.<br/>개행 컨밴션 수정. |
| 23.05.15.(월) | 내비게이션 바 색상 변경, JuiceMaker에 FruitStore 관련 메서드 생성.<br/>addTarget 함수 분리, label, button 변수명 변경, 접근 제어자 추가.<br/>Alert용 Enum 생성, Alert 메서드 분리, replaceStockLabel 메서드명 변경, 재고수정 내비게이션 컨트롤러 삭제 및 Present Modally로 화면전환 변경.<br/>ViewController명 변경.<br/>MainViewController 개행 수정.|
| 23.05.16.(화) | showAlert 중복 코드 삭제, Alert 열거형 이름 변경, 개행 수정, 화면전환 방법 show에서 present로 변경, 접근제어자 추가. |
## 📺 실행화면
- JuiceMaker 실행 화면 <br>

## 🔨 트러블 슈팅
### 1️⃣ 과일별 재고 표시 <br>
**문제점**<br>
FruitStore의 재고를 파악하는 함수를 JuiceMaker에 따로 구현하지 않았더니, 필요할 때마다 FruistStore에 직접 접근해야 하는 문제가 생겼습니다.
```swift=!
// MainViewController.swift
let fruitStore = FruitStore(equalizedStock: 10)
lazy var juiceMaker = JuiceMaker(fruitStore: fruitStore)
...
let fruitStock: [Fruit: Int] = fruitStore.getInventoryStatus()
```
```swift=!
// JuiceMaker.swift
init(fruitStore: FruitStore) {
self.fruitStore = fruitStore
}
```
🔑 **해결방법** <br>
FruitStore 관련 내용을 모두 JuiceMaker를 통해서도 접근할 수 있도록 메서드를 생성했습니다. 또한 전체 과일 재고를 한번에 리턴받을 수 있는 메서드도 추가 구현하여 화면 구성에 용이하도록 했습니다.
```swift=!
// MainViewController.swift
let juiceMaker = JuiceMaker(equalizedStock: 10)
...
let fruitStock: [Fruit: Int] = juiceMaker.getFruitInventoryStatus()
```
```swift=!
// JuiceMaker.swift
init(stock: [Fruit: Int]) {
self.fruitStore = FruitStore(stock: stock)
}
init(equalizedStock: Int) {
self.fruitStore = FruitStore(equalizedStock: equalizedStock)
...
func changeFruitStock(of fruit: Fruit, quantity: Int) {
fruitStore.changeStock(of: fruit, quantity: quantity)
}
func getFruitInventoryStatus() -> [Fruit: Int] {
return fruitStore.getInventoryStatus()
}
```
### 2️⃣ 에러처리 <br>
🔒 **문제점** <br>
makeFruitJuice 메서드에서 에러가 발생했을 경우, 이 내용을 ViewController에 전달하기 위해 해당 에러를 재전달(다시 throw)하는 형식으로 구현하니 불필요한 코드가 발생했습니다.
```swift!
func makeFruitJuice(menu: FruitJuice) throws {
...
do {
for ingredient in recipe {
let changedQuantity = try fruitStore.calculateStock(of: ingredient.fruit, quantity: ingredient.quantity)
...
} catch StockError.fruitNotFound {
print(StockError.fruitNotFound.message)
throw StockError.fruitNotFound
} catch StockError.outOfStock {
print(StockError.outOfStock.message)
throw StockError.outOfStock
} catch {
print(StockError.unKnown.message)
throw StockError.unKnown
...
}
```
🔑 **해결방법** <br>
makeFruitJuice에서 do-catch문을 삭제하고 throws만 남겨 해당 오류를 그대로 전달하도록 변경했습니다.
```swift!
func makeFruitJuice(menu: FruitJuice) throws {
let recipe = menu.juiceRecipe
var changedStock: Recipe = []
for ingredient in recipe {
let changedQuantity = try fruitStore.calculateStock(of: ingredient.fruit,
quantity: ingredient.quantity)
changedStock.append((fruit: ingredient.fruit, quantity: changedQuantity))
}
changedStock.forEach {
fruitStore.changeStock(of: $0.fruit, quantity: $0.quantity)
}
print("\(menu.name) 제조가 완료되었습니다.")
}
```
### 3️⃣ Alert <br>
🔒 **문제점** <br>
얼럿창 처리를 구현하면서 얼럿 메시지를 매직리터럴 처리하니 코드의 가독성이 떨어지고, 유지보수에 용이하지 못할 것 같았습니다.
```swift=!
// ViewController.swift
private func orderJuice(_ menuName: FruitJuice) {
self.storyboard?.instantiateViewController(
identifier: "StockViewController"
) else {
return
}
do {
try juiceMaker.makeFruitJuice(menu: menuName)
showStock()
let alert = UIAlertController(title: "\(menuName.name) 나왔습니다!",
message: "맛있게 드세요!",
preferredStyle: .alert)
let ok = UIAlertAction(title: "야호!",
style: .default,
handler: nil)
alert.addAction(ok)
present(alert, animated: true, completion: nil)
} catch StockError.fruitNotFound {
print(StockError.fruitNotFound.message)
} catch StockError.outOfStock {
let alert = UIAlertController(title: "재료가 모자라요.",
message: "재고를 수정할까요?",
preferredStyle: .alert)
let ok = UIAlertAction(title: "예",
style: .default,
handler: { _ in
self.navigationController?.show(stockViewController, sender: self)
})
let cancel = UIAlertAction(title: "아니오",
style: .default,
handler: nil)
alert.addAction(ok)
alert.addAction(cancel)
present(alert, animated: true, completion: nil)
} catch {
print(StockError.unKnown.message)
}
```
🔑 **해결방법** <br>
얼럿 관련 메시지들을 enum 처리하고 얼럿 내용을 함수로 구분하여 가독성을 높였습니다.
```swift=!
// MainViewController.swift
private func showAlert(type: AlertText) {
...
let alert = UIAlertController(title: type.title,
message: type.message,
preferredStyle: .alert)
switch type {
...
case .outOfStock:
let ok = UIAlertAction(title: AlertActionText.ok.title,
style: .default,
handler: { _ in self.navigationController?.present(stockViewController, animated: true)
})
...
}
```
```swift!
// AlertEnum.swift
enum AlertText {
case menuOut(menu: String)
case outOfStock
case confirmStockChange
var title: String {
switch self {
case .menuOut(menu: let menu):
return "\(menu) 나왔습니다!"
...
}
enum AlertActionText {
case ok
case cancel
case interjection
var title: String {
switch self {
case .ok:
return "예"
...
}
```
🤔 **고민했던 점** <br>
화면전환 기법을 Navigation Controller를 활용한 show로 진행할지, 아니면 Modal을 활용할지에 대하여 고민이 많았습니다. 자동으로 버튼이 구현되는 점, 그리고 UI가 Navigation 형식을 따라 그려져있는 점을 보았을 때는 전자로 구현해야 할 것 같았고, 전체 앱의 흐름을 보았을 때는 후자로 구현하는 것이 맞는 것 같았습니다. 결과적으로 두 가지 모두를 적용해 본 뒤, 흐름에 맞게 Modal을 활용하는 것이 좋겠다는 결론을 내렸습니다.
## 📑 참고자료
- [내비게이션 바](https://developer.apple.com/documentation/uikit/uinavigationbar)
- [모달](https://developer.apple.com/design/human-interface-guidelines/modality)
- [얼럿](https://developer.apple.com/documentation/swiftui/alert)
- [클래스와 구조체](https://bbiguduk.gitbook.io/swift/language-guide-1/structures-and-classes)
- [제어 흐름](https://bbiguduk.gitbook.io/swift/language-guide-1/control-flow)
- [에러 처리](https://bbiguduk.gitbook.io/swift/language-guide-1/error-handling)