---
# System prepended metadata

title: Readme
tags: [readme]

---

# Readme
###### tags: `readme`

# 쥬스 메이커 🧃

# 목차
[1. 프로젝트 소개](#1-프로젝트-소개)
<br>
[2. 팀원](#2-팀원)
<br>
[3. 타임라인](#3-타임라인)
<br>
[4. 시각화된 프로젝트 구조](#4-시각화된-프로젝트-구조)
<br>
[5. 실행 화면](#5-실행-화면)
<br>
[6. 트러블 슈팅](#6-트러블-슈팅)
<br>
[7. 참고 링크](#7-참고-링크)
<br>
[팀 회고](#팀-회고)
<br>

# 1. 프로젝트 소개
쥬스 메이커 앱에서는 버튼을 통해 원하는 쥬스를 선택하여 만들 수 있습니다.

<br>

# 2. 팀원

| Harry | kokkilE  |
|:----:| :----: |
| <img src="https://i.imgur.com/BYdaDjU.png" width="130" height="130"/> | <img src="https://i.imgur.com/4I8bNFT.png" width="130" height="130"/>     |
| [Github 링크](https://github.com/HarryHyeon) |[Github 링크](https://github.com/kokkilE) |

<br>

# 3. 타임라인

- 23.01.02(월) ~ 23.01.04(수)
    - 과일 가게 클래스 구현
    - 쥬스 메이커 구조체 구현
- 23.01.06(금)
    - 메인화면에서 재고 수정화면으로 화면전환 구현
    - 쥬스 제조시 성공, 실패 alert 구현
- 23.01.09(월) ~ 23.01.10(화)
    - Autolayout 적용
    - 과일의 개수를 나타내는 레이블에 개수 표시 구현
    - 재고 수정화면에서 재고 수정 기능 구현
    - 화면 전환 시 과일의 개수를 나타내는 레이블 업데이트 구현
- 23.01.16(월) ~ 23.01.17(화)
    - Autolayout, 스토리보드 스택뷰 속성 변경
    - 변수명 및 프로퍼티 속성 변경
<br>

# 4. 시각화된 프로젝트 구조
## 폴더 구조
``` swift
├── Controller
|   ├── AppDelegate
|   ├── SceneDelegate
|   ├── JuiceMakeViewController
|   └── StockManagerViewController
├── Model
|   ├── FruitStore
|   ├── JuiceMaker
|   ├── Juice
|   ├── JuiceMakerError
|   ├── Fruit
|   └── UpdateLabelsDelegate
└── View
    ├── Main
    └── Assets
```

<br>

## 클래스 다이어그램
<img src="https://i.imgur.com/KhsVHSh.png"/>


<br>
<br>

# 5. 실행 화면

- 쥬스 주문 버튼을 클릭하면 재료의 재고가 있을 경우 주문에 성공한다.
![](https://i.imgur.com/ldP1lqR.gif)

<br>
<br>

- 쥬스 주문 버튼을 클릭하면 재료의 재고가 없을 경우 주문에 실패하며 재고 수정 화면으로 이동할 수 있는 Alert이 표시된다.
![](https://i.imgur.com/BMYBHCn.gif)


<br>
<br>

- 재고수정 화면에서 과일의 개수를 조정했을 때 첫번째 화면에 수정한 개수가 반영된다.
![](https://i.imgur.com/sPTIH06.gif)

<br>

# 6. 트러블 슈팅
 
## 에러처리 (옵셔널 바인딩)
```swift
func addStock(fruit: Fruit, quantity: Int) throws {
        guard let selectedFruitStock = fruitStock[fruit] else {
            throw FruitStoreError.invalidFruitInput
        }
        
        fruitStock.updateValue(selectedFruitStock + quantity, forKey: fruit)
    }
```
- 위 메서드를 예시로 `fruitStock[fruit]` 딕셔너리 값을 가져올때, 옵셔널 바인딩이 필요했다.
- 저 부분에서 `fruitStock`에서 인덱스로 `Fruit` 타입을 받고, 후에 버튼 입력으로 해당 메서드를 기능하게 한다고 가정했을때 저 부분에서 옵셔널 바인딩이 되지않아 에러를 던져주는 경우는 없을거라 생각이 된다.
- 따라서 guard let 을 통해 옵셔널 바인딩이 되지않을 경우 return 을 해주어 그냥 함수를 종료시키는 것이 맞을지, 아니면 저런식으로 일어날 가능성이 없는 에러를 던져주는 것이 맞는지 고민을 했다.
- 결론적으로, 에러를 던져서 **"이 흐름은 내가 의도한 방향이 아니다, 올바른 흐름이 아니다"** 라고 표시하는 것이 협업하는 팀원과 미래의 내가 봤을 때 명확할 것이라 판단해 에러처리를 했다.

## 쥬스 레시피 타입 프로퍼티에서 연산 프로퍼티로 변경
``` swift
enum Juice {
     case strawberryJuice
     case bananaJuice
     case kiwiJuice
     case pineappleJuice
     case strawberryBananaJuice
     case mangoJuice
     case mangoKiwiJuice

     var recipe: [Fruit: Int] {
         switch self {
         case .strawberryJuice:
             return [.strawberry: 16]
         case .bananaJuice:
             return [.banana: 2]
         case .kiwiJuice:
             return [.kiwi: 3]
         case .pineappleJuice:
             return [.pineapple: 2]
         case .strawberryBananaJuice:
             return [.strawberry: 10, .banana: 1]
         case .mangoJuice:
             return [.mango: 3]
         case .mangoKiwiJuice:
             return [.mango: 2, .kiwi: 1]
         }
     }
 }
```
- 이전에는 주스 레시피를 case를 사용하지 않고 static let 키워드로 사용해 딕셔너리를 저장했다.
- 주스의 레시피는 메서드의 파라미터로 들어가기 때문에 하나의 타입으로써 표현하는 것이 메서드를 사용하기에 더 좋을 것 같아주스 각각의 메뉴명을 케이스로 지정하고 연산 프로퍼티를 통해 딕셔너리를 반환하는 방식으로 바꾸었다.

<br>

## 화면 전환 시 이벤트 전달 방법
두번째 화면에서 재고 수정작업이 끝나고 화면이 `dismiss` 될때, 변경사항을 첫번째 화면의 레이블을 업데이트 하기위해 세 가지 방법을 고려했다.

1. **delegate 패턴** 적용
2. **Notification**

추가적으로 이벤트 전달 방식은 아니지만, `UIViewController`의 **State Method** 활용하여 첫 번째 화면이 Appear될 때 마다 실행하게 할 수 있으므로 편하게 적용이 가능했다. 하지만 이번 프로젝트에서는 이벤트 전달을 구현해보고자 이 방법은 채택하지 않았다.<br>

**Notification** 방식은 이벤트를 전달받는 수신지가 여러 객체일 경우 적용하기 좋지만, 발신지의 post만 보고 코드의 흐름을 파악하기 어렵다는 단점이 있습니다.

**delegate** 패턴은 delegate가 nil일 경우 crash가 날 수 있고, 여러 객체에 이벤트를 알리기 어렵다는 단점이 있습니다.<br>

이번 프로젝트는 이벤트 수신지가 한 군데 뿐이고, **delegate 패턴**을 적용하는 것이 가독성이 좋다고 생각해 **delegate 패턴**을 적용했다.

<br>

# 7. 참고 링크
- [Swift Language Guide - Type Casting](https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html)
- [Swift Language Guide - Access Control](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html)
- [Swift Langauge Guide - Nested Types](https://docs.swift.org/swift-book/LanguageGuide/NestedTypes.html)
- [Swift Language Guide - Error Handling](https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html)
- [Swift Language Guide - Protocol](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html)

<br>

# 팀 회고
## 우리팀이 잘한 점 
- 여러 방법을 고려하고 장단점을 분석해서 좋은 코드를 작성하고자 노력한 점
- 하나의 문제를 해결하기 위해 여러방면에서 시도를 해보았던 점
- 같이 공식문서를 열심히 읽으면서 공부했던 것 !
## 우리팀 개선할 점
- 유지보수가 가능한 코드를 작성하도록 노력하자! 현재는 과일종류 하나만 추가되어도 코드 이곳저곳에 과일종류를 사용하기 때문에 수정하기가 매우 번거롭다. 프로젝트가 끝나더라도 꾸준히 고민해보기.
    
## 팀원 서로 칭찬하기 부분
- 해리 -> 코낄이
    - 코낄이 3주간 정말 고생많았습니다. 코낄이를 보면서 여러가지 방향에서 문제를 바라보는 시각에 한번씩 놀라기도 했고 같이 문제를 해결하면서 많이 배운것 같습니다. 항상 여유있는 모습과 적극적인 자세로 프로젝트를 함께 해주셔서 감사했습니다 ! 다음에 또 봐요 :smile: 
    
- 코낄이 -> 해리
    - 학습할 주제가 발생하면 이해할 때 까지 파고드는 성실함이 있습니다.
    - 알고 있는 지식을 잘 설명해줍니다. :thumbsup:
    - 의견을 잘 수용해줍니다.
    - 상대방에게 칭찬을 많이 해줍니다.
    - 대화를 잘 이끌어줍니다.
    