# 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">