안녕하세요 라자냐 @wonhee009 **탑킴, 허황** 입니다😊 STEP 1 구현이 끝나 PR 드립니다😁 잘 부탁드립니다🤩 ### STEP 1 UML ![](https://i.imgur.com/yF3Umm3.jpg) ### 구현 내용 - 과일의 종류를 표현하는 FruitType 타입 정의 - 딸기, 바나나, 파인애플, 키위, 망고 - 과일의 재고를 관리하는 FruitStore 타입 정의 - 각 과일의 초기 재고 : 10개 - 각 과일의 수량 n개를 변경하는 기능 - 과일 쥬스를 제조하는 JuiceRecipes 타입 정의 - 딸기쥬스 : 딸기 16개 소모 - 바나나쥬스 : 바나나 2개 소모 - 키위쥬스 : 키위 3개 소모 - 파인애플 쥬스 : 파인애플 2개 소모 - 딸바쥬스 : 딸기 10개 + 바나나 1개 소모 - 망고 쥬스 : 망고 3개 소모 - 망고키위 쥬스 : 망고 2개 + 키위 1개 소모 - FruitStore 과일을 사용해 과일쥬스를 제조하는 JuiceMaker 타입 정의 - 과일의 재고가 부족하면 과일쥬스를 제조 불가 ### 고민했던 점 - 확장에 용의한 코드 > 쥬스의 종류를 구현할 때 처음에는 열거형으로 구현했지만, 나중에 쥬스가 추가 될 경우 열거형을 수정해야하기 때문에 쥬스 별로 RecipeProtocol 프로토콜을 채택한 struct를 구현했습니다. > struct로 구현할 경우 쥬스가 추가될 때마다 새로운 쥬스 타입을 생성해주면 되기 때문에 확장에 용의한 코드라 판단하고 진행했습니다. > 라자냐의 생각이 궁금합니다🧐 - FruitStore 타입 struct VS class > struct로 구현 할 경우 immutable하게 값을 사용 할 수 있을 것이라 판단하여, 진행 하였으나, 테스트 코드 작성 시 이로 인해 오히려 혼선이 생기게 되어 class로 전환 하게 되었습니다. ![](https://i.imgur.com/HnP1BV8.png) ### 조언을 얻고 싶은 부분 - 테스트 파일의 위치 > Unit Test를 진행할 때 Tests 타겟을 추가하고 그 하위에 Unit Test 파일을 생성해서 사용하게 되는데 Unit Test 파일과 프로덕션 코드가 한 그룹에 있는게 어떤 프로덕션 코드가 테스트 진행되었는지 명확하게 알 수 있는 것 같습니다. > Unit test 파일을 위 상황 같이 배치해도 괜찮을까요?? > 배치하게 된다면 Tests 타겟은 추가하지 않고 Unit test 파일만 생성해서 테스트를 진행해도 괜찮은지 궁금합니다🧐 > 예시) 킥스타터 github 참고 이미지 [링크](https://github.com/kickstarter/ios-oss/tree/main/Kickstarter-iOS/DataSources) > ![](https://i.imgur.com/4TbRXWc.png) - 문서화 주석 > 함수의 로직을 문서화 주석으로 남겨도 괜찮은지 궁금합니다🧐 > 부연설명 드리자면 과일의 개수가 부족한지 확인하는 메서드 내부에서 Dictionary 메서드 중 [merging](https://developer.apple.com/documentation/swift/dictionary/3127173-merging)을 사용했습니다. > merging을 사용해서 두 딕셔너리의 value의 차이를 구하고 > filter를 이용해서 value가 음수인 key의 개수를 구하고 > value가 음수인 key의 개수가 0보다 크다면 .failure를 반환 > 하게 구현했는데 이러한 로직은 코드를 보고 파악하는 것 보다 문서화 주석을 사용하는게 다른 사람이나, 본인이 봤을 때 코드를 파악하기 수월하겠다고 생각했습니다. - Result 타입 캐스팅 > Result<Void, Error> <-> Result<Void, CustomError> 사이의 캐스팅이 불가능 한 이유가 궁금 합니다. > CustomError는 Error 프로토콜을 채택 하고 있습니다. > 오류 문구 : Cannot convert return expression of type 'Result<Void, CustomError>' to return type 'Result<Void, Error>' -------------------- ## 리뷰 답변 ### 확장에 용이한 코드 해당 부분은 확장보다는 유지보수 면에서 좀 더 의미가 있지 않나?라는 생각이 들었습니다. 주스가 추가 되었을때 열거형을 수정(케이스 추가)하는 부분이나 주스 struct를 추가하는 부분이나 저는 비용이 비슷한거 같거든요. 물론, 주스별로 뭔가 다른 기능이 추가되는거라면 확장면에서는 좀 더 좋은 코드라고 생각합니다. 그럼 과일에 대해서는 열거형을 채택하신 이유는 무엇일까용?.?🤔 > 코드에 통일성을 유지하려면 과일도 별도의 타입으로 생성하는게 맞다고 판단됩니다😭 > 라자냐께서 말씀해주신 과일도 별도의 타입으로 생성할 고민해봤지만, > 열거형을 사용하게되면 FruitStore 타입의 Inventory 프로퍼티의 타입이 [FruitType: Int]을 사용하기 때문에 과일의 초기수량을 넣어줄 때 `CaseIterable`를 채택한 열거형을 사용하면 간편하게 초기수량을 넣어줄 수 있다고 판단해서 열거형을 사용했습니다. ### Result 타입 캐스팅 현재 FruitStoreError에서는 Error를 채택하지 않는거로 보이는데용?.? > FruitStoreError는 LocalizedError를 채택하며 이는 Error를 상속 받습니다. > JuiceMaker의 make 함수를 소스 1과 같이 작성하고 싶었는데요. > 캐스팅이 안되는 문제로 소스 2와 같이 작성되었습니다. 이유가 무엇일까요? 소스 1 ```swift= mutating func make(with recipe: RecipeProtocol) -> Result<Void, Error> { return fruitStore.use(of: recipe.items) // return type Result<Void, FruitStoreError> } ``` 소스 2 ```swift= mutating func make(with recipe: RecipeProtocol) -> Result<Void, Error> { let result = fruitStore.use(of: recipe.items) switch result { case .success(): return .success(Void()) case .failure(let error): return .failure(error) } } ``` 의존성을 줄이고 테스트가 쉬운 구조를 만들기 위해 작성 했습니다. 예를 들면 FruitStoreMock을 주입하는 경우 입니다.