---
# System prepended metadata

title: 만국박람회
tags: [README]

---

# 만국박람회
> 1900년 파리 만국박람회에 한국 출품작을 보여주는 앱입니다.
> 한국 출품작에 대한 리스트를 볼 수 있습니다.
> 한국 출품작 사진, 이름, 간단한 설명을 볼 수 있습니다.
> 한국 출품작을 선택했을 경우 상세화면을 보여줍니다.
> 상세화면에서는 출품작 이름, 사진, 상세한 설명을 보여줍니다.

## 📚 목차
- [팀원소개](#-팀원-소개)
- [타임라인](#-타임라인)
- [실행화면](#-실행화면)
- [트러블 슈팅](#-트러블-슈팅)
- [참고자료](#-참고자료)
- [팀 회고](#-팀-회고)

## 🧑‍💻 팀원 소개
| <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.09.(화) ~ 23.05.11.(목)

| 날짜 | 진행 사항 |
| -------- | -------- |
| 23.05.09.(화)     | FruiteStore 생성 및 과일 재고 변경 메서드 구현.<br/>FruitJuice 열거형 타입 생성 및 isEnoughStock 메서드 생성.<br/>makeFruitJuice 함수 구현, isEnoughStock 열거형 리팩토링.<br/>StockError 열거형 생성, 예외처리 추가.<br/>checkStock 리팩토링, changeStock 리팩토링.<br/>calculateStock 메서드 리팩토링, makeFruitJuice 메서드 리팩토링. |
| 23.05.10.(수)     | Fruite, FruiteJuice 열거형 분리, errorMessage를 message로 변경.<br/>FruitStore 타입 struct로 변경, baseStock을 전달받는 생성자 구현, Ingredient typealias 생성, Recipe typealias 수정, calculateStock 함수의 argument label for에서 fo로 변경.<br/> 오타 수정, 개행 컨벤션 변경, 불필요한 주석 제거. 불필요한 Import 제거     |
| 23.05.11.(목)     | recipe 관련 네이밍 변경, 로직 조건문 변경, 불필요한 개행 삭제.|
| 23.05.12.(금)     | README 작성     |

## 📺 실행화면
- JuiceMaker 실행 화면 <br>
![](https://imgur.com/q9Qji8M.gif)

## 🔨 트러블 슈팅 
1️⃣ **FruitStore의 재고사항인 inventory를 생성하는 과정** <br>
🔒 **문제점** <br>
FruitStore의 재고사항인 inventory를 생성하는 과정에서, 처음에는 모든 과일의 수량을 10으로 주도록 CaseIterable과 reduce를 활용하여 기본값으로 딕셔너리를 생성했습니다. 하지만 이렇게 하니 과일마다 초기재고가 다를 경우를 구현할 수 없었고, 또한 모든 과일가게가 무조건 Fruit 타입 전체의 재고를 가지고 있도록 만들어져 문제가 되었습니다.
```swift!
private var inventory: Dictionary<Fruit, Int> = Fruit.allCases.reduce(into: Dictionary<Fruit, Int>()) { $0[$1] = 10 }
```

🔑 **해결방법** <br>
두 가지 생성자를 만들어 하나에는 모든 과일의 재고를 동일하게 맞출 수 있도록 구현했고, 다른 하나에는 [과일: 수량] 형식의 딕셔너리를 전달받아 inventory를 구성할 수 있도록 하여 해결했습니다.
```swift!
init(stock: [Fruit: Int]) {
    self.inventory = stock
}
    
init(equalizedStock: Int) {
    self.inventory = Fruit.allCases.reduce(into: [:]) {
        $0[$1] = equalizedStock
    }
}
```


2️⃣ **typealias** <br>
🔒 **문제점 2** <br>
타입 알리아스를 만들어 (fruit: 과일, quantity: 수량)의 타입으로 Receipe를 만들었지만, 내부적으로 해당 타입이 Array로 받아져야 하기 때문에(예: [Recipe]) 가독성이 좋지 않은 문제가 있었습니다.
```swift!
typealias Recipe = (fruit: Fruit, quantity: Int)
...
var chagedStock: Array<Recipe> = []
```

🔑 **해결방법** <br>
타입 알리아스를 하나 더 만들어 Ingredient라는 이름으로 Recipe와 Ingredient를 구분지었습니다.
```swift!
typealias Ingredient = (fruit: Fruit, quantity: Int)
typealias Recipe = [Ingredient]
...
var chagedStock: Recipe = []
```


3️⃣ **재고 변경 함수** <br>
🔒 **문제점 3** <br>
주스를 만들 때 일부 재료만 품절일 경우 전체 재고를 변경하지 않도록 구현하기 위해 처음에는 isEnoughStock() 이라는 메서드를 생성하여 Bool값으로 전체 재료의 제작 가능 여부를 리턴하도록 만든 뒤 changeStock() 메서드를 통해 재고를 변경하도록 구현했습니다. 하지만 이럴 경우 모든 재료의 재고를 가져오는 로직을 두 차례 반복해야 했습니다.

```swift!
func isEnoughStock(_ recipes: Array<Recipe>) throws -> Bool {
	for recipe in recipes {
		guard let fruitStock = inventory[recipe.fruit] else { throw StockError.fruitNotFound }
		guard fruitStock > recipe.amount else { throw StockError.outOfStock }
		return true
	}
}

func changeStock(of fruit: Fruit, minus amount: Int) throws {
	guard let fruitStock = inventory[fruit] else { throw StockError.fruitNotFound }
	guard fruitStock > amount else { throw StockError.outOfStock }
	
	inventory[fruit] = fruitStock - amount
}
```

🔑 **해결방법** <br>
isEnoughStock() 메서드를 calculateStock() 메서드로 변경, 재고가 충분하지 않은 경우 오류를 리턴하고 충분할 경우 쥬스에 필요한 값을 뺀 수량을 리턴하여 changeStock()에서는 단순히 재고 수량을 변경만 할 수 있도록 기능을 분리하였습니다.
```swift!
func calculateStock(of fruit: Fruit, quantity: Int) throws -> Int {
    guard let fruitStock = inventory[fruit] else {
        throw StockError.fruitNotFound
    }
    guard fruitStock >= quantity else {
        throw StockError.outOfStock
    }
        
    return fruitStock - quantity
}

mutating func changeStock(of fruit: Fruit, quantity: Int) {
    guard let  = inventory[fruit] else { return }
	inventory[fruit] = quantity
}
```


🤔 **고민했던 점** <br>
JuiceMaker 인스턴스를 통해 FruitStore 내부의 changeStock()에 접근할 수 있도록 메서드를 추가해야 할지 다소 고민이 되었지만, 처음 JuiceMaker 인스턴스를 생성할 때 FruitStore의 인스턴스를 만들어 인자로 전달해주어야 하기 때문에 해당 FruitStore 인스턴스를 사용하면 될 것으로 판단하여 따로 구현하지 않았습니다.


## 📑 참고자료
- [초기화](https://bbiguduk.gitbook.io/swift/language-guide-1/initialization)
- [프로퍼티](https://bbiguduk.gitbook.io/swift/language-guide-1/properties)
- [클래스와 구조체](https://bbiguduk.gitbook.io/swift/language-guide-1/structures-and-classes)
- [열거형](https://bbiguduk.gitbook.io/swift/language-guide-1/enumerations)
- [제어 흐름](https://bbiguduk.gitbook.io/swift/language-guide-1/control-flow)
- [에러 처리](https://bbiguduk.gitbook.io/swift/language-guide-1/error-handling)




## 📝 팀 회고
### 우리 팀
👍 활발한 의견 공유 <br>
👍 팀프로젝트 활동시간대를 정하여 개인 공부 시간에 영향 없도록 한 점 <br>
👍 각자의 부족한 부분을 서로 채워나간 점 <br>
👍 모르는 것이 있으면 부끄러워하지 않고 질문을 서슴없이 한 점 <br>
👍 어려운 상황이나 문제가 닥치면 함께 찾아보고 풀어나간 점 <br>

### 팀원 피드백
**To. Kobe** <br>
👍 여러 차례 의견이나 아이디어를 번복해도 흔쾌히 함께 의견을 나누며 작업해주신 점에 너무 감사드립니다! <br>
👍 UI 구현 부분에 지식이 많이 부족한 저를 위해 이해가 쉽도록 설명을 곁들이며 작업해 주셨습니다.<br>
👍 늘 팀프로그래밍 시간 전에 문서 작성이나 필요한 자료를 준비해두셔서 프로젝트가 훨씬 빠르고 원활히 진행될 수 있었습니다.<br>
👍 제가 놓친 부분들을 예리하게 캐치하여 의견을 제시해 주셨습니다.<br>
👍 막강 친화력을 가지신 코비 덕분에 즐거운 팀프로그래밍 시간이 될 수 있었습니다.<br>

**To. Maxhyunm** <br>
👍 이해가 가지 않는 부분에 대해 여러차례 질문을 드려도 친절하게 답해주셨습니다. <br>
👍 제가 한 질문에 대한 답변을 저의 지식에 맞춰 잘 풀어 설명해주셨습니다. <br>
👍 항상 의견 조율에 대한 의견을 먼저 물어봐주시고 맞춰주셨습니다. <br>
👍 Swift에 대한 많은 방법론을 제시해주시고 먼저 이끌어주셔서 좋은 공부가 되었습니다. <br>
👍 분위기를 너무 좋게 풀어주셨습니다. <br>
