# ios-bank-manager Jason&Avery // 아래부터 복붙해서 사용 # 은행창구 관리앱 [Step2] Jason, Avery > 안녕하세요 웨더(@SungPyo) 주말 잘 보내셨나요?! 😀 저희는 이번 한 주동안 스탭 2를 구현하면서 다양한 공부를 진행할 수 있었어요. > 특히 다음 스탭으로 나아가기 위해 어떻게 코드를 구현해야 확장성이 좋고, 객체지향적인 프로그래밍을 할 수 있는지 아직 마냥 쉽지만은 않았어요. > 저희에 고민에 관한 부분은 아래에 적어놓았으니 웨더의 피드백을 받아보고 싶어요~! 이번 스탭 리뷰도 잘 부탁드리고 좋은 하루 보내시길 바라요 💐 ## 📌 Step2 구현사항 ### 타입 구현 - 은행, 고객 - [x] 은행에는 n명의 은행원이 근무해요 - [x] 은행에는 n명의 고객이 업무처리를 위해 대기해요 - [x] 고객의 대기열은 Step 1에서 구현한 Queue 타입을 활용해요 - [x] 모든 고객의 업무가 끝나면 은행업무를 마감해요 - [x] 업무를 마감할 때 "업무가 마감되었습니다. 오늘 업무를 처리한 고객은 총 XX명이며, 총 업무시간은 XX초입니다."라고 출력해요 - [x] 은행원은 고객의 업무를 처리해요 - [x] 각 고객의 업무를 처리하는 데 걸리는 시간은 0.7초에요 - [x] 은행원이 한 번에 처리할 수 있는 고객은 한 명이에요 - [x] 대기중인 고객의 업무처리를 시작할 때 아래와 같이 출력해요 "3번 고객 업무 시작" - [x] 고객의 업무를 처리하면 아래와 같이 출력해요 "5번 고객 업무 완료" ### 콘솔앱 구현 - [x] Step 2의 은행에는 한 명의 은행원이 근무해요. - [x] 은행원은 한 번에 한 명의 고객의 업무를 처리할 수 있어요. - [x] 앱을 실행하면 두 개의 메뉴를 출력해요. 1 : 은행 개점 2 : 종료 - [x] 사용자가 1을 입력하면 은행을 개점하고 10명~30명의 고객이 방문해요. 10~30 사이의 임의의 수 만큼의 고객의 업무를 처리하면 은행문이 닫히고 다시 메뉴를 출력해요. - [x] 사용자가 2를 입력하면 프로그램을 종료해요. ### 🚀 스텝 핵심 경험 - Queue의 활용 - 타입 추상화 및 일반화 ### 🎯 트러블 슈팅 #### 1️⃣ 소수점 맞추기 요구사항 출력에서 아래와 같이 최대 소수점 2자리까지만 보이게하기 위해서 어떻게 구현해봐야할지 고민해보았어요. > 업무가 마감되었습니다. 오늘 업무를 처리한 고객은 총 15명이며, 총 업무시간은 10.3초입니다. ```Swift // ceil() - 소수점 이하를 모두 버리고 정수부에 +1을 해준다. public func ceil(_: Double) -> Double // floor() - 소수점 이하를 버린다. public func floor(_: Double) -> Double // round() - 소수점 이하를 반올림한다. (0.3이상은 1로 올리고 미만은 버린다.) public func round(_: Double) -> Double ``` 위와 같이 Darwin.C.math 파일에 정의된 메서드를 사용할 수도 있었지만 NumberFormatter로 정의하여 사용했어요. roundingMode를 halfUp로 지정하여 불필요한 소숫점은 없애고 반올림하도록 했어요. 😊 ```Swift private static func calculate(spend toalCount: Int) -> String { let numberFormatter = NumberFormatter() numberFormatter.roundingMode = .halfUp numberFormatter.maximumSignificantDigits = 4 let totalLeadTime = Requirement.leadTime * Double(toalCount) guard let totalSpend = numberFormatter.string(for: totalLeadTime) else { return Errors.failOfFormatToString.localizedDescription } return totalSpend } ``` #### 2️⃣ 매직넘버 사용하지 않기 지난 주 OOP의 좋은 습관에 대해 학습하게 되었을 때 매직넘버의 사용을 지양해야한다고 배웠어요. 따라서 이번 프로젝트에서는 매직넘버 사용을 최소로 하기 위해 처음에는 enum으로 타입을 정의해보았어요. 그런데 case로 사용하니 에러가 발생하여 NameSpace에 대해 학습하여 해결하게 되었어요. ```Swift // ⛔️ enum Requirement: Double { case leadTime = 0.7 enum CustomerCount: Int { case defaultCustomer = 1 case minimum = 10 case maximum = 30 init?(rawValue: Int) { self.init(rawValue: rawValue) } } init?(rawValue: Double) { self.init(rawValue: rawValue) } } ``` 위와 같이 작성하였을 때 호출부에서 rawValue를 사용하지 않는 방법을 적용하고 싶어 초기화를 진행하는데 쉽지 않았어요. 🥲 ```Swift enum Requirement { enum CustomerCount { static let defaultCustomer: Int = 1 static let minimum: Int = 10 static let maxmimum: Int = 30 } static let leadTime: Double = 0.7 } ``` 리펙토링으로 위와 같이 진행하였고 여기서 열거형의 타입을 직접 프로퍼티에 붙여주지 않으면 아래와 같이 에러가 발생하여 RawValue에 대해 직접 명시해달라고는 에러가 발생하는데, 이 부분은 추후에 좀 더 학습해봐야할 것 같아요. 학습할 만한 좋은 키워드가 있다면 추천 부탁드려요. 🌱 <img src="https://user-images.githubusercontent.com/92699723/221500411-cb89a421-4acf-48e1-a2dd-29f3426e62fc.png" width = "500"> #### 3️⃣ Thread Delay 사용하기 고민하다 Console앱의 main Thread를 다룬다고 이해하여 Thread에 delay를 주는 방법으로 사용하였어요. 처음에는 `global().asyncAfter`로 해결을 하였는데 순차적으로 출력이 되어야하여 `main.sync`에서 처리하려고했는데 `main.sync`를 직접 호출하면 Deadlock 상태에 빠지게 된다는 사실을 놓쳐서 아래와 같이 해결하였어요. ```Swift Thread.sleep(forTimeInterval: Requirement.leadTime) ``` ### 🙋🏻 질문 사항 ### 1. 추후 Step을 진행할 때 은행원(Teller)이 N명이라는 요구사항에 대해 고민해보았어요. 저희는 2가지 방향으로 생각을 해보았는데, 요구사항에서는 B 방식을 채택하는 것 같다고 생각했어요. 이 방법이 올바른 방향으로 생각되는지, 웨더의 의견은 어떠하신지 궁금해요. 🤔 #### #### 🅰️ 1 : N 의인화로 표현하자면 한 명의 은행원이 여러 업무(ex. 예금, 대출, 출금 등)를 번갈아가면서 수행해요. <img src = "https://user-images.githubusercontent.com/92699723/221502702-e6038202-f7cc-4513-8507-09c95562c2da.png" width = 350> #### 🅱️ N : 1 각 인스턴스가 하나의 업무만을 맡아서 처리해요. <img src = "https://user-images.githubusercontent.com/92699723/221502697-ebb042ef-5043-4433-b4dd-e95ccc604ee5.png" width = 350> ### 💻 실행 결과 <img src="https://user-images.githubusercontent.com/92699723/221496967-480e9287-2962-4994-af7e-ffdbe68ae15f.mov">