# 쥬스 메이커 STEP 1
- JuiceMaker 내부에 프로퍼티로 FruitStore 객체를 소유할 수 있도록 구성
- Juice 종류 및 레시피 Enum 등의 중첩 타입으로 생성 (연산 프로퍼티 활용 가능)
- Error 별도로 생성(전체 오류 공통 관리)
- FruitStore 초기화시 판매할 과일과 재고(초기수량 10개) 설정
- JuiceMaker 초기화시 판매할 주스 설정
- Juice 판매 메서드 구현
## 캠프사이트 공지
Step 1 : 쥬스 메이커 타입 정의
FruitStore.swift 파일에 과일의 재고를 관리하는 FruitStore 타입을 정의합니다.
FruitStore는 다음의 조건을 충족해야 합니다.
FruitStore가 관리하는 과일의 종류 : 딸기, 바나나, 파인애플, 키위, 망고
각 과일의 초기 재고 : 10개
각 과일의 수량 n개를 변경하는 기능이 있습니다.
이 외에 더 필요한 기능, 특성, 타입이 있으면 자유롭게 추가해도 좋습니다.
JuiceMaker.swift 파일에 다음의 조건을 충족하는 JuiceMaker 타입을 정의합니다.
FruitStore의 과일을 사용해 과일쥬스를 제조합니다.
딸기쥬스 : 딸기 16개 소모
바나나쥬스 : 바나나 2개 소모
키위쥬스 : 키위 3개 소모
파인애플 쥬스 : 파인애플 2개 소모
딸바쥬스 : 딸기 10개 + 바나나 1개 소모
망고 쥬스 : 망고 3개 소모
망고키위 쥬스 : 망고 2개 + 키위 1개 소모
과일의 재고가 부족하면 과일쥬스를 제조할 수 없습니다.
JuiceMaker는 FruitStore를 소유하고 있습니다.
이 외에 더 필요한 기능, 특성, 타입이 있으면 자유롭게 추가해도 좋습니다.
(선택사항) 필요한 경우 오류처리(throw, try-catch)를 구현합니다.
💡 STEP1의 첫 PR 전송은 첫째주 목요일 오후(17:00)까지를 목표로 합니다.
---
안녕하세요 밈(@joSH0318) 🙋♂️
쥬스메이커 리뷰를 받게 된 maxhyunm, kobe 입니다.
3주동안 잘 부탁드립니다 🙌
## 고민했던 점
- Fruit 재고 관리
- Fruit를 열거형으로 구현하면서, 열거형 내부 항목을 모두 Dictionary로 끌고 와서 초기값을 설정하는 방법이 뭐가 있을지 고민했습니다. 결과적으로 CaseIterable과 reduce를 사용하는 방법을 선택했습니다.
- 동일한 로직으로 재고를 체크하는 로직이 두 함수로 분리 되어 있었는데, 동일한 로직을 두 함수에서 사용하는 점이 비효율적이라고 느껴 calcluateStock 함수 내부에 하나로 구현하였습니다. 또한 calculateStock을 전부 통과한 경우에만 changeStock으로 넘어갈 수 있도록 구현하여, 레시피 중 일부만 재고가 부족할 경우에도 제조 불가로 넘어가도록 했습니다.
- changeStock을 구현하면서 처음에는 재고가 줄어드는 처리를 하는 함수라고만 생각했습니다. 하지만 화면단을 확인해 보니 -와 +를 처리하는 부분이 포함되어 있는 것으로 보여, 단순히 변경할 재고 수량 n을 전달받아 재고 상태를 바꾸는 방식으로 구현하게 됐습니다.
- JuiceMaker 쥬스 별 레시피 관리
- 메뉴 별로 사용되는 과일이 다르고, 여러 가지 과일을 혼합해서 사용하는 경우도 있어 Dictionary로는 구현에 한계가 있다고 생각되었습니다. 때문에 typealias를 통해 fruit와 quantity로 이루어진 타입 형태를 생성하고, 이를 담은 Array로 레시피를 정리했습니다.
- JuiceMaker가 FruitStore를 소유하도록 만들기
- 처음에는 중첩 타입으로 구현하는 방법을 생각했는데, 레파지토리를 확인해 보니 파일이 나뉘어 있어서 약간의 고민 끝에 JuiceMaker 내부에 프로퍼티로 FruitStore 인스턴스를 가지도록 구현했습니다.
## 조언을 얻고 싶은 부분
- 모델 구현
- 모델 구현시 View단의 화면을 참고하여 구성해야 할까요?
- MVC 패턴
- MVC 패턴 구현시 MVC 중 어떤 것을 먼저 구현해야 하는지 궁금합니다.
## 참고사항
- typealias Recipe의 위치
- 해당 형태를 FruiteStore와 JuiceMaker 어디서든 사용할 수 있을 것이라 판단해 전역으로 위치를 빼둔 상태입니다. 어색하다 생각되시면 JuiceMaker 내부로 이동하겠습니다.
- print문
- 현재 화면단 로직 처리가 없는 상태라, 프로그램 구현이 정상적으로 됐음을 확인하기 위해 print 문을 넣어두었습니다. 불필요하다 판단되시면 삭제하도록 하겠습니다!
---
# 첫 번째 PR 관련 코멘트 정리

- 답변
- errorMessage -> message로 변경하도록 하겠습니다!

- 답변
- 모델은 애플리케이션의 데이터 및 비즈니스 로직 관리를 담당합니다. 때문에 에러 또한 애플리케이션에서의 데이터 및 비즈니스 로직이라고 생각해서 Model에 두었습니다.
- 에러가 발생할 때 에러 로그를 수집, 기록, 분석하는데 사용할 수 있는 정보 또는 사실이라고 생각하여 에러를 데이터라고 생각했습니다.
- 에러는 애플리케이션의 동작을 제어하는 규칙, 알고리즘 및 프로세스의 집합을 나타내기도 합니다 때문에 에러를 비즈니스 로직이라고 생각했습니다.
- 📓 데이터란?
- 일반적으로 데이터는 통찰력을 얻거나 결정을 내리는 데 수집, 기록, 분석 및 사용할 수 있는 모든 정보 또는 사실 집합을 의미합니다.
- 숫자 값, 텍스트, 이미지, 오디오, 도는 비디오를 포함하여 다양한 형식을 취할 수 있습니다.
- 데이터는 연구, 분석, 마케팅 및 의사 결정을 포함하여 광범위한 목적으로 사용될 수 있습니다.
- 📓 비즈니스 로직이란?
- 응용 프로그램의 동작을 제어하는 규칙, 알고리즘 및 프로세스 집합을 나타냅니다. 이러한 규칙 및 프로세스는 일반적으로 애플리케이션이 서비스를 제공하는 특정 비즈니스 또는 산업에 따라 다릅니다.
- 비즈니스 로직의 예로는 유효성 검사 규칙, 데이터 엑세스 규칙, 계산 및 워크플로가 있습니다.
- 예를 들어 은행 어플리케이션에서 비즈니스 로직에는 계정 정보의 유효성을 검사하고 이자율을 계산하고 트랜잭션을 처리하기 위한 규칙이 포함될 수 있습니다.
- 전자 상거래 애플리케이션에서 비즈니스 로직에는 주문 처리, 재고 관리 및 지불 처리를 위한 규칙이 포함되어 있습니다.
- 사용자 인터페이스 및 애플리게이션의 다른 측명에서 비즈니스 로직을 분리함으로써 모델을 재사용 및 유지 관리할 수 있도록 설계하여 애플리케이션을 보다 쉽게 수정하고 업데이트 할 수 있습니다.

- 답변
- `var inventory: [Fruit: Int] = Fruit.allCases.reduce(into: [:]) { $0[$1] = 10 }` 형태로 변경하도록 하겠습니다.
- (2번 질문)
- 장점은 Fruit 열거형에 포함된 모든 항목을 일일이 작성하지 않고도 딕셔너리에 저장할 수 있다는 점입니다.
- 단점은 말씀하신대로 매번 초기값이 10으로 고정된다는 점인데, 이 부분은 변수로 초기값을 분리하여 생성자에서 전달받으면 어떨까? 라고 생각하여 init을 추가해 보려고 합니다.

- 답변
- throw 이후의 작성된 코드가 한 줄로 작성될 만큼 짧고 throw 이후에 어떤 에러가 던져지는지 한 번에 볼 수 있다고 생각되어 한 줄로 코드를 작성해봤었습니다. 이번에는 개행을 하여 코드를 작성해보도록 하겠습니다.

- 답변
- 두 메서드의 argument label인 for 와 of를 일관성 있게 하나로 바꿔보도록 하겠습니다.

- 답변
- (1번)
- juiceRecipe 프로퍼티를 recipe로 바꿔보겠습니다.
- (2번)
- typealias의 명칭을 Ingredient로 변경하고, Ingredient를 모은 Array 타입의 명칭을 Recipe으로 변경하도록 하겠습니다.

- 답변
- case 조건에 사용한 dot syntax 로 return도 바꿔보겠습니다

- 답변
- 접근제어자 누락된 것을 고치겠습니다.

- 답변
- (1번)
- 네 shorthand form으로 사용해서 고쳐보겠습니다.
- (2번)
- 오타인 부분을 changedStock로 바꾸도록 하겠습니다.

- 답변
- 위의 Array<Recipe> 말씀주신 부분에 답변 드린 내용대로, 지금의 Recipe 타입을 Ingredient로 변경하고 Ingredient를 담은 Array 타입을 Recipe로 선언하여 해결해 보겠습니다!

- 답변
- 클로저를 사용한 부분이나 길어지는 부분들은 공통적으로 개행 컨벤션 정리하여 적용해보도록 하겠습니다!
- calculateStock을 거친 후 changeStock을 통과시키는 이유는, 딸바쥬스나 망고키위쥬스처럼 두 가지 이상의 재료가 들어간 경우 둘 중 하나라도 재고가 부족하다면 아예 수량을 마이너스시키지 않기 위해서입니다. 만약 changeStock에서 바로 처리해버리게 되면 딸바쥬스 중 바나나만 부족한 상황에서 딸기를 먼저 마이너스처리해버릴 수 있고, 이를 롤백시키는 내용을 구현하는 것보다는 앞서 체크를 한 뒤 해당 수량으로 변경을 하도록 구현하는 편이 흐름상 더 간략하다는 생각이 들었습니다. 또한 처음에는 calculateStock 대신 모든 재료의 수량이 충분한지만을 확인하는 Bool타입 메서드를 구현했었으나, 이럴 경우 changeStock에서 전체 재고의 수량을 다시 확인 후 마이너스처리를 해야 해서 코드의 중복이 발생하였습니다. 이에 따라 재고 확인 후 변경할 수량을 리턴하는 calculateStock을 구현하게 되었습니다.
---
## 두 번째 PR

- 답변
- 재고를 변경하는 로직은 FruitStore 내부의 changeStock()으로 구현되어 있습니다! 재고를 n개로 바꾸고 싶다면 n이라는 quantity를 전달받아 해당 수량으로 재고를 바꾸는 메서드입니다. 쥬스를 만들 때는 재고를 계산해서 빼야 하고, 또 재고가 부족할 경우 에러를 리턴해야 하기 때문에 changeStock()을 호출하기 전에 calculateStock()을 호출하여 변경할 수량을 먼저 구하는 형식으로 구현했지만, 재고 변경의 경우 단순히 changeStock()만 호출하면 될 것으로 생각하였습니다. 혹시 재고를 변경하는 로직을 JuiceMaker를 통해 실행할 수 있도록 추가하라는 말씀이실까요?

- 답변
- ingredient와 recipe라는 네이밍으로 수정하도록 하겠습니다.

- 답변
- 불필요한 개행은 삭제 처리하도록 하겠습니다.

- 답변
- 불필요한 개행은 삭제 처리하도록 하겠습니다.

- 답변
- `fruitStock == quantity`일 경우에는 `error`을 던지면 안되는 상황입니다. 이부분에 대해서 조건부를 `fruitStock >= quantity`로 바꿔 처리하도록 하겠습니다.