---
# System prepended metadata

title: "\U0001F379 캠퍼들의 목마름을 책임진다! 쥬스 메이커"

---


# 🍹 캠퍼들의 목마름을 책임진다! 쥬스 메이커


## 📖 목차
1. [팀 소개](#-팀-소개)
2. [기능 소개](#-기능-소개)
3. [Diagram](#-Diagram)
4. [폴더 구조](#-폴더-구조)
5. [타임라인](#-타임라인)
6. [트러블 슈팅](#-트러블-슈팅)
7. [기술적 도전](#-기술적-도전)
8. [참고 링크](#-참고-링크)


## 🌱 팀 소개
 |[Wonbi](https://github.com/wonbi92)|[woong](https://github.com/iOS-Woong)|
 |:---:|:---:|
| <img width="180px" img style="border: 2px solid lightgray; border-radius: 90px;-moz-border-radius: 90px;-khtml-border-radius: 90px;-webkit-border-radius: 90px;" src="https://avatars.githubusercontent.com/u/88074999?v=4">| <img width="180px" img style="border: 2px solid lightgray; border-radius: 90px;-moz-border-radius: 90px;-khtml-border-radius: 90px;-webkit-border-radius: 90px;" src="https://avatars.githubusercontent.com/u/96489602?v=4">|

- `Wonbi`와 `woong` 팀이 만든 쥬스 메이커입니다.


## ⚒️ 기능 소개
 |**주스 주문화면**|**재고 수정 화면**|
 |:---:|:---:|
 |![](https://i.imgur.com/pEexHat.png)|![](https://i.imgur.com/Kn9saKC.png)|
 |**주문 성공(alert)**|**주문 실패(alert)**|
 |![](https://i.imgur.com/YQFHlMg.gif)|![](https://i.imgur.com/KQxT7zK.gif)|
 |**재고수정 이동(alert을 통한 이동)**|**재고수정 이동(button을 통한 이동)**|
 |![](https://i.imgur.com/UedTJUE.gif)|![](https://i.imgur.com/exjbEaf.gif)
|
 |**재고수정 적용(적용버튼)**|**재고수정 취소(취소버튼)**|
 |![](https://i.imgur.com/CVodTQk.gif)|![](https://i.imgur.com/QfJUT8E.gif)|
 
 
## 👀 Diagram

### 🧬 Class Diagram
![JuiceMaker ClassDiagram](https://i.imgur.com/DOguFss.jpg)

### 🧬 Flow Chart
![JuiceMaker Flow Chart](https://i.imgur.com/WyO9bG8.png)

## 🗂 폴더 구조
> Modal : 앱 구동 로직에 필요한 모델
> View : 화면을 구성하는 뷰
> Controller : 화면의 이벤트와 전환을 컨트롤하는 컨트롤러

![](https://i.imgur.com/MC0mnDp.png)


## ⏰ 타임라인

### 👟 Step 1
- class 타입의`FruitStore` 구현
- struct 타입의 `JuiceMaker`구현
- enum 타입의 `Juice`구현
- enum 타입의 `Fruit`구현
- error를 채택한 enum 타입의 `StockError`구현

<details>
<summary>Details</summary>
<div markdown="1">

#### **220829**
- `Fruit`
    - 타입 케이스 구현
- `FruitStore`
    - 의 재고 체크 메서드 구현
    - 재고 개수 변경하는 메서드, 
    패턴 구현
- `StockError`
    - 에러 케이스 구현
    
#### **220830**
- `Juice`
    - 타입 케이스 구현
- `FruitStore`
    - `JuiceMaker`에게 과일을 전달하는 메서드 구현
    - 메서드 로직 변경 및 컨벤션 수정
- `JuiceMaker`
    - 주스 만드는 메서드 구현
    - 프로퍼티 기본값 설정
    - 컨벤션 수정
    
#### **220831**
- 메서드 및 파라미터명 리팩토링
- 버그 수정
- `Fruit`
    - 타입 재정의    
- `FruitStore` 
    - 재고 확인하는 메서드 로직 수정
    - 재고를 변경하는 메서드 로직 수정
    - 과일을 전달하는 메서드 로직 수정  
    
#### **220901**   
- 메서드 및 파라미터명 리팩토링  
- `Fruit`
    - 타입 재정의
- `FruitStore` 
    - 싱글톤 삭제
    - 재고를 변경하는 메서드 리팩토링
- `JuiceMaker`  
    - 쥬스를 만드는 메서드 로직 수정
- `StockError`
    - 삭제
    
#### **220902**   
- 메서드 및 파라미터명 리팩토링  
- `Recipe`
    - 타입 케이스 구현
- `FruitStore`
    - 이니셜라이저 로직 수정
    - 재고 관리하는 메서드 로직 수정
    
#### **220904** 
- `Recipe`
    - 삭제
- `Juice`
    - 재료와 필요갯수를 나타내는 프로퍼티 추가
</div>
</details>

### 👟 Step 2
- `MainViewController` 이벤트에 대응하는 로직 구현
- `EditViewController` 생성
- `EditView` 오토레이아웃 설정
- 모달 방식의 화면전환 구현

<details>
<summary>Details</summary>
<div markdown="1">
    
#### **220905** 
- 세그웨이 방식으로 화면 전환구현
- `MainViewController`
    - 버튼 이벤트에 대응하는 메서드 구현
    - 결과에 맞는 Alert 구현
    - 뷰의 레이블과 모델 내부의 재고값을 연결
- `EditViewController`
    - 생성
    - 오토레이아웃 설정
    - 취소 버튼 구현

#### **220906**
- 코드 네이밍 리팩토링
- 코드를 통한 화면전환 방식으로 리팩토링
- `MainViewController`
    - `NotificationCenter`를 통해 재고 변경시마다 화면 레이블에 동기화되도록 구현
    - 주문 버튼을 식별하는 로직 구현

#### **220907**
- `MainViewController`
    - KVO를 활용하여 재고를 나타내는 로직 구현

#### **220908**
- `MainViewController`
    - KVO를 통해 Alert을 띄우도록 리팩토링
    - 화면 전환 시 데이터 전달을 위한 `StockStorage`타입을 싱글톤 패턴으로 구현
    - 주문 버튼을 식별하고 주문이 성공했는지 확인하는 메서드 기능분리

#### **220912**
- 네이밍 리팩토링, 소스파일 정리
- `MainViewController`
    - KVO 제거 
    - 메서드를 통해 값을 전달하도록 리팩토링

</div>
</details>

### 👟 Step 3
- `EditViewController` 이벤트에 대응하는 로직 구현
- `AlertMessage`네임 스페이스 구현
- Alert 메서드 하나로 통합
- 객체 캡슐화

<details>
<summary>Details</summary>
<div markdown="1">

#### **220913**
- 오토레이아웃 적용
- `Response`
    - 네임 스페이스 enum타입 구현
- `EditViewController`
    - `Stepper`의 `value`를 설정하는 메서드 구현
    - `Stepper` 이벤트에 대응하는 로직 구현
- `FruitStore`
    - 재고 변경하는 메서드 로직 리팩토링

#### **220915**
- `AlertMessage`
    - 타입 명을 `Response`에서 `AlertMessage`로 교체
    - Alert에 관련된 모든 메시지 추가
- `EditViewController`
    - `Stepper` 이벤트에 대응하는 로직 리팩토링
    - Alert 메서드 하나로 통합

#### **220916**
- `EditViewController`
    - 내부 프로퍼티 `stock`을 `FruitStore` 내부로 캡슐화
        
</div>
</details>


## 🏃🏻 기술적 도전

[싱글톤](https://github.com/wonbi92/ios-juice-maker/wiki/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-&-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%8F%84%EC%A0%84#%EF%B8%8F-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4)

[NotificationCenter](https://github.com/wonbi92/ios-juice-maker/wiki/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-&-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%8F%84%EC%A0%84#%EF%B8%8F-notificationcenter)

[KVO패턴](https://github.com/wonbi92/ios-juice-maker/wiki/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-&-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%8F%84%EC%A0%84#%EF%B8%8F-kvo-%ED%8C%A8%ED%84%B4)

[Delegate패턴](https://github.com/wonbi92/ios-juice-maker/wiki/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-&-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%8F%84%EC%A0%84#%EF%B8%8F-delegate-%ED%8C%A8%ED%84%B4)


## 🚀 트러블 슈팅

[화면 전환시 메서드를 통한 데이터 전달](https://github.com/wonbi92/ios-juice-maker/wiki/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-&-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%8F%84%EC%A0%84#-%ED%99%94%EB%A9%B4-%EC%A0%84%ED%99%98%EC%8B%9C-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%ED%86%B5%ED%95%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC)

[Alert 함수를 여러개로 분리하는 것이 아닌 하나의 함수로 통합 관리](https://github.com/wonbi92/ios-juice-maker/wiki/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-&-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%8F%84%EC%A0%84#-alert-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%97%AC%EB%9F%AC%EA%B0%9C%EB%A1%9C-%EB%B6%84%EB%A6%AC%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%95%84%EB%8B%8C-%ED%95%98%EB%82%98%EC%9D%98-%ED%95%A8%EC%88%98%EB%A1%9C-%ED%86%B5%ED%95%A9-%EA%B4%80%EB%A6%AC)

[FruitStore에 임시재고를 구현함으로써 캡슐화](https://github.com/wonbi92/ios-juice-maker/wiki/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-&-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%8F%84%EC%A0%84#-fruitstore%EC%97%90-%EC%9E%84%EC%8B%9C%EC%9E%AC%EA%B3%A0%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%A8%EC%9C%BC%EB%A1%9C%EC%8D%A8-%EC%BA%A1%EC%8A%90%ED%99%94)

[rawValue의 사용](https://github.com/wonbi92/ios-juice-maker/wiki/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-&-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%8F%84%EC%A0%84#-rawvalue%EC%9D%98-%EC%82%AC%EC%9A%A9)


## 🔗 참고 링크

[Swift API Design Guidelines - Naming](https://swift.org/documentation/api-design-guidelines/)  
[Swift Language Guide - Initialization](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html)  
[Swift Language Guide - Access Control](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html)  
[Swift Language Guide - Nested Types](https://docs.swift.org/swift-book/LanguageGuide/NestedTypes.html)  
[Swift Language Guide - Type Casting](https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html)  
[Swift Language Guide - Error Handling](https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html)  
 
---

[🔝 맨 위로 이동하기](#-쥬스-메이커)


1. 싱글톤
2. NotificationCenter
3. KVO
4. Delegate
5. Data Send From Method 
6. 캡슐화?




### 싱글톤 패턴의 활용

```swift
class FruitStore {
    static var stockManager = FruitStore()
    var stock: [Fruit: Int] = Fruit.defaultStock
    
    private init() {}
}
    
struct JuiceMaker {
    let stockManager = FruitStore.stockManager
    
    func requestFruit() -> Int {
        let numberOfFruits = stockManager.stock[.strawberry]
        return numberOfFruits
    }
}
```

처음에 과일을 저장하고있는 FruitStore 클래스의 처리를 `싱글톤패턴`을 활용하여 과일의 수량을 저장하고 있는 배열 인스턴스를 모든 뷰컨트롤러 혹은 모델에서 편리하게 접근 가능하도록 구현 하려고 하였습니다.

하지만, 싱글톤 인스턴스에 너무 많은 데이터가 연결될 경우에 다른 클래스의 인스턴스 간 결합도가 높아질 것을 우려했고,

여러군데에서 싱글톤 객체를 접근하여 값을 바꾸게 될 경우 의도하지 않은 결과가 나올 것이므로 

이 방법을 채택하지 않고, FruitStore의 데이터가 필요할 경우, 각 타입 내에서private으로 FruitStore를 생성했습니다. 따라서, 전역 접근을 차단하여 안전하게 관리하였습니다.


### NotificationCenter

```swift
// MainViewController
private func settingObserver() {
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(updateStockCount(_:)),
                                           name: .changedStockCount,
                                           object: nil)
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(madeJuiceAlert(_:)),
                                           name: .madeJuiceAlert,
                                           object: nil)
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(failedAlert(_:)),
                                           name: .failedAlert,
                                           object: nil)
}

// JuiceMaker
func makeJuice(_ juice: Juice) {
        if store.canSupplyRequest(ingredient: juice.ingredient) {
            NotificationCenter.default.post(name: .madeJuiceAlert,
                                            object: nil,
                                            userInfo: ["JuiceName" : juice.name])
        } else {
            NotificationCenter.default.post(name: .failedAlert,
                                            object: nil)
        }
    }
}

// FruitStore
private(set) var stock: [Int] {
    didSet {
        NotificationCenter.default.post(name: .changedStockCount,
                                        object: nil,
                                        userInfo: nil)
    }
}
```

- 재고가 수정될 때마다 수정된 재고를 화면에 업데이트 해주는 로직과 쥬스 제조의 성공 실패여부를 알려주는 로직을 `NotificationCenter`를 통해 구현해 보았습니다.
- `NotificationCenter`의 장점은 코더가 원하는 시점에 옵저버를 추가하거나 알림을 발송할 수 있다는 점이라 생각했습니다. 또한, 다른 타입의 인스턴스나 프로퍼티에 접근하지 않고 완전히 분리된 상태에서도 포스팅된 정보들을 옵저버를 통해 받아낼 수 있는 점이 장점이라 생각했습니다.
- 하지만, `NotificationCenter`는 따로 설정하지 않는이상 앱 내에 모든 부분에 post를 날리는 로직으로 구현되어있고, 심지어 다른 모듈에도 post가 되기 때문에, 앱의 상태변화를 앱 전체 혹은, 다른 앱에 "알리는" 객체라는 생각이 들었습니다. 
- 예를 들어, 핸드폰을 사용하는 중에 전화가 오면, `NotificationCenter`를 통해 전화가 수신되었다는 사실을 알리고, 그에 맞게 사용중인 앱이 일시정지되도록 하는 방식으로 사용되는 것이 올바른 사용방법이라 생각했습니다.
- 이러한 이유 때문에 `NotificationCenter`는 최종적으로 채택하지 않게 되었습니다.


### KVO
```swift
// FruitStore
class FruitStore: NSObject {
    @objc dynamic private(set) var stock: [Int]
}

// MainViewController
class MainViewController: UIViewController {
    @objc let store = FruitStore(stockCount: 10)
    
     override func viewDidLoad() {
        super.viewDidLoad()
        stockChangeObserver = observe(\.store.stock,
                                       options: [.new, .initial],
                                       changeHandler: { (object, stock) in
            guard let newStock = stock.newValue else { return }
            self.updateStockCount(stock: newStock)
        })
    }
}
```
- 또 다른 데이터의 변화를 체크하는 방법인 KVO 패턴을 통해 재고가 수정될 때 마다 수정된 재고를 화면에 업데이트 해주는 로직을 구현해 보았습니다.
- KVO의 장점은 두 객체간 정보를 동기화 하는것이 매우 쉽다는 것 있었습니다.
- 하지만, 반드시 그 객체의 인스턴스를 가지고 있어야만 데이터의 추적이 가능하다는 단점이 있었고 `NSObject`를 상속 받아 Objective-C 런타임을 이용해 값을 추적하기에 간단한 값 변화를 위해 이렇게까지 많은 리소스를 사용하는 것에 대한 의문이 생겼습니다. 
- 게다가 KVO의 장점으로 많이들 거론하는 `Nasted Type`의 값 변화도 추적이 가능하다는 점도 저희의 앱에서는 굳이 장점으로 작용하지 않았습니다. 
- 결론적으로, 간단한 값 변화를 옵저빙 하기위해 많은 코드줄과 리소스를 사용한다 판단되어 채택하지 않았고, 값이 변화하는 경우에 재고를 화면에 업데이트 하는 메서드를 호출하는 방식으로 구현하게 되었습니다.



### Delegate패턴

```swift
//editViewController - 채택 및 요구메서드 구현
extension EditViewController: SendDataDelegate {
    func sendStock(_ stock: [Int]) {
        self.stock = stock
    }
}

//ViewController - 프로토콜 선언 및 프로퍼티 생성
protocol SendDataDelegate: AnyObject {
    func sendStock(_ stock: [Int])
}

class MainViewController: UIViewController {
    weak var delegate: SendDataDelegate?

    @IBAction private func tappedModifyBarButton(_ sender: Any) {
        // EditViewController를 인스턴스화 함.
    
        delegate = editViewController
        delegate?.sendStock(store.stock)
    
        present(editViewController, animated: true)
    }
}
```

최초의 설계에선 뷰컨트롤러 간 데이터를 전달하는 방식으로 직접 해당 프로퍼티에 접근해 data를 전달하는 방식으로 데이터를 전달했습니다.

하지만, 다른 뷰 컨트롤러의 프로퍼티에 직접 접근하고 해당 컨트롤러에 의존하고 있는 형태임과 
동시에 프로퍼티에 직접접근을하거나 직접 값을 설정하기에 결합도가 높아진다고 판단했고,

위 사항이 우려되어 `Delegate`를 이용하여 각 뷰컨트롤러 간 데이터를 주고받도록 수정하였습니다.

하지만, 간단한 데이터만 전송하는 로직에 `Delegate`를 사용하는 것은 너무 많은 코드를 작성하게 된다는 단점이 있었고 코드의 가독성을 많이 해친다 판단하여 최종적으로는 채택하지 않았고 데이터를 메서드를 통해 주고받게 하였습니다.

### 화면 전환시 메서드를 통한 데이터 전달
```swift
// MainViewController
class MainViewController: UIViewController {
    private let store = FruitStore(stockCount: 10)
    private lazy var juiceMaker = JuiceMaker(store: store)

    @IBAction private func tappedModifyBarButton(_ sender: Any) {
        // EditViewController를 인스턴스화 함.
    
        editViewController.setStore(from: store)
    
        present(editViewController, animated: true)
    }
}

// EditViewController
class EditViewController: UIViewController {
    private var store: FruitStore?
    
    func setStore(from store: FruitStore) {
        self.store = store
    }
}
```
- 이번 프로젝트의 핵심 키워드라는 생각이 들었습니다. 화면 전환시 데이터를 전달하는 여러가지 방법에 대해 공부하였고 그중에 저희가 최종적으로 채택한 방법은 "메서드를 통한 데이터 전달"방식 이었습니다.
- 다른 여러가지 방법들 역시 장단점이 있었지만, 가벼운 데이터 하나를 넘기기 위해 코드가 너무 길어지기도 하고, 사용시 장점보다는 단점이 더 부각되는거 같아 가장 간단하면서도 효율적인 방법을 채택하게 되었습니다.
- `FruitStore`는 `class`이기 때문에, 참조 타입입니다. 따라서 `MainViewController`에서 `EditViewController`를 `present`하기 전에 `EditViewController`의 프로퍼티 `store`가 `MainViewController`의 `store`를 참조하도록 하였습니다. 
- 이 결과 앱에는 여전히 하나의 `store`만 존재하게 되어 각 컨트롤러간 결합도를 낮추고, 재고의 무결성을 지켰으며, 나아가 재고의 변경에 따른 뷰의 변화도 간단한 코드로 구현이 가능해졌습니다.



### FruitStore에 임시재고를 구현함으로써 캡슐화
```swift
// FruitStore
class FruitStore {
    private(set) var stock: [Int]
    private(set) var queuedStock = [Int]()
    
    func setQueuedStock() {
        queuedStock = stock
    }
    
    func changeQueueStock(fruitIndex: Int, count: Int) {
        queuedStock[fruitIndex] = count
    }
    
    func confirmChange() {
        stock = queuedStock
        queuedStock = [Int]()
    }
}

// EditViewController
class EditViewController: UIViewController {
    private var store: FruitStore?
    
    
    @IBAction private func tappedCancelButton(_ sender: UIBarButtonItem) {
        dismiss(animated: true)
    }
    @IBAction private func tappedApplyButton(_ sender: UIButton) {
        store?.confirmChange()
        dismiss(animated: true)
    }
    
    
    @IBAction private func tappedStepper(_ sender: UIStepper) {
        let fruitIndex = sender.tag, newCount = sender.value
        
        fruitCountLabels?[fruitIndex].text = Int(newCount).description
        store?.changeQueueStock(fruitIndex: fruitIndex, count: Int(newCount))
    }
```
- 기존 코드에서는 `EditViewController`에서 임시 재고를 가지고 있고, 그 재고를 '적용' 버튼을 눌렀을 때 참조하고 있는 재고에 적용하도록 구현하였습니다.
- 하지만, `EditViewController`가 임시 재고를 가지고 있는 것이 객체지향의 "캡슐화"에 적합하지 않은 방법이라는 생각이 들어서 이를 `FruitStore`에서 직접 처리하도록 리팩토링을 진행해 보았습니다.
- 이 역시 메서드를 통해서만 값이 변경되도록 하여 결합도를 높이지 않으면서 값이 바뀌도록 구현하였습니다.

### rawValue의 사용
```swift
enum Juice: String {
    case strawberryJuice = "딸기 주스"
    case bananaJuice = "바나나 주스"
    case kiwiJuice = "키위 주스"
    case pineappleJuice = "파인애플 주스"
    case mangoJuice = "망고 주스"
    case strawberryBananaJuice = "딸바 주스"
    case mangoKiwiJuice = "망키 주스"

    var name: String {
        switch self {
        case .strawberryJuice:
            return "딸기 주스"
        case .bananaJuice:
            return "바나나 주스"
        case .kiwiJuice:
            return "키위 주스"
        case .pineappleJuice:
            return "파인애플 주스"
        case .mangoJuice:
            return "망고 주스"
        case .strawberryBananaJuice:
            return "딸바 주스"
        case .mangoKiwiJuice:
            return "망키 주스"
        }
    }
}
```
- `enum`타입의 `rawValue`사용은 매우 유용합니다. 하지만 이 `rawValue`라는 매직 리터럴을 바로 사용하는 것은 코드의 가독성을 떨어뜨린다는 생각이 들었습니다.

- 그래서 처음에는 연산 프로퍼티를 이용해 `switch`문으로 `rawValue`를 활용하는 프로퍼티를 만들게 되었는데, 매직 리터럴을 사용하지 않게 되어 코드의 가독성을 올려주긴 했지만, 코드가 너무 길어지는 단점이 있었습니다.
```swift
enum Juice: String {
    case strawberryJuice = "딸기 주스"
    case bananaJuice = "바나나 주스"
    case kiwiJuice = "키위 주스"
    case pineappleJuice = "파인애플 주스"
    case mangoJuice = "망고 주스"
    case strawberryBananaJuice = "딸바 주스"
    case mangoKiwiJuice = "망키 주스"

    var name: String {
        self.rawValue
    }
}
```
- 그리고 피드백을 통해 다음과 같이 수정하게 되었습니다. 너무.. 아름답습니다...✨✨ 이런 작지만 기발한 아이디어가 코드를 더 샤프하게 작성할 수 있다는 점을 알게 되었습니다.






### Alert 함수를 여러개로 분리하는 것이 아닌 하나의 함수로 구현
```swift
private func resultAlert(isSuccess: Bool, juiceName: String = "") {
        let alert = UIAlertController(title: nil,
                                      message: isSuccess ? juiceName + AlertMesaage.successMesaage : AlertMesaage.failureMesaage,
                                      preferredStyle: .alert)
        let comfirmAction = UIAlertAction(title: isSuccess ? AlertMesaage.successConfirm : AlertMesaage.failureConfirm,
                                          style: .default,
                                          handler: isSuccess ? nil : { _ in self.tappedModifyBarButton(()) })
        
        alert.addAction(comfirmAction)
        
        if !isSuccess {
            let noAction = UIAlertAction(title: AlertMesaage.failureCencel, style: .cancel)
            alert.addAction(noAction)
        }
        
        present(alert, animated: true)
    }
```

- 기존에는 `실패경우의 Alert` & `성공경우 Alert`의 메서드로 2개로 분리하여 각각 구현하여 조건에 따라 별개의 메서드가 실행되도록 하였습니다.
- 하지만, 코드의 간결성을 위해 로직구현부에서 Bool 타입을 전달인자로 받아 실패 할 경우에 UIAlertController에 noAction 변수가 추가되도록 구현하였습니다.