# 은행창구 매니저
@havilog
안녕하세요 하비! 코낄이, 베토입니다.
은행창구 매니저 프로젝트의 step1 첫 PR 보냅니다. 2주간 잘부탁드립니다! :smile:
## 고민했던 점 & 조언받고 싶은 점
### 어디서 시작을 해야하는지
처음에 시작할 때 프로젝트 파일을 찾아보니 UIApp과 ConsoleApp 두개로 나누어져있었습니다. 저희입장에서는 처음보는 형태라 어디서 코드를 작성해야할지 몰라서 일단 storyborad가 있는 UIApp프로젝트 파일에서 시작을 하였습니다. 다 작성하고 고민을 해보니 UIApp에서 작성해야 하는지 의문이 들어서 선배기수의 코드를 보고 다른 캠퍼분들의 의견(UI가 없기 때문에 UIApp에서 작성하는 것은 아닌 것 같다는 의견)을 들어서 ConsoleApp 프로젝트 파일에서 시작하는 것이 맞는거 같아서 처음 커밋으로 돌아와 다시 작성하였습니다. ConsoleApp에서 시작하는 것이 맞는지 궁금합니다. 어떤 기준이 있다면 어떤 기준으로 선택해야 하는지 궁금합니다.
## 해결하지 못한 점
### Unit Test의 타겟 import 에러
UIApp 환경에서 테스트하던것과 마찬가지로 `@testable import`로 테스트 대상의 타겟을 `import`해서 시험하고자 했으나, 다음과같은 에러가 발생했습니다.
<img src="https://i.imgur.com/7N4TEtM.png" width=600>
`BankManagerConsoleApp` 타겟을 인식하지 못하는 것으로 보여 타겟 설정에서 여러 시행착오를 해봤으나 결국 해결하지 못하고, 다음과 같이 테스트 대상 파일에서 Target Membership을 추가하는 방법으로 테스트했습니다.
<img src="https://i.imgur.com/hY2vjsj.png" width=180>
그러나 위 방법은 번거로울 뿐더러 같은 프로젝트 내에 있는 타겟을 인식하지 못하는것은 부자연스럽게 느껴집니다.
위 내용과 관련해서 저희가 살펴봐야할 부분이 있을까요?
---
- LinkedList를 struct로 구현하신 근거에는 어떤 것들이 있을까요?
[Choosing Between Structures and Classes](https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes#Use-Structures-When-You-Dont-Control-Identity)에 따라 `LinkedList` 타입을 클래스로 구현할 필요가 없다고 판단해 구조체로 구현하였습니다.
- 해당 LinkedList에 들어올 제네릭한 T는 아무런 protocol을 준수하지 않아도 될지 고민해보셨을까요?
이번 스텝에서는 다양한 타입의 데이터를 취급할 수 있도록 제네릭으로 구현하는데 초점을 맞춰 protocol에 대해선 고민해보질 않았습니다. :cry: 하지만 LinkedList에는 은행 창구 관련 어떤 대기열의 타입이 저장될 것이라고 예상할 수 있을 것 같고, 그렇다면 특정 프로토콜을 준수하도록 구현할 필요가 있을 것 같습니다.
- 제네릭은 어떤 역할을 하고 있는지 알고 계실까요?
다양한 타입에도 작동하는 코드를 작성하고 싶을 때 제네릭을 사용하는 것으로 알고 있습니다. 저희가 queue에 데이터를 넣을 때 어떠한 타입의 데이터가 올지 모르기 때문에 제네릭을 사용하여 다양한 타입을 받을 수 있게 코드를 작성하였습니다.
- mutating이 어떻게 동작하는지 설명해주실 수 있으실까요?
[swift-book/methods](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/methods/)에 따르면 mutating이 동작하는 원리는 메서드 종료 될 때 기존의 인스턴스를 프로퍼티가 변경된 인스턴스로 대체한다고 합니다. 값이 자주 변경되는 인스턴스라고 한다면 class로 구현하는 것이 좋을까요?
- Node를 class로 구현하신 근거는 어떤게 있을까요?
[Choosing Between Structures and Classes](https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes#Use-Structures-When-You-Dont-Control-Identity) 아티클의 `Use Classes When You Need to Control Identity`에 따라 클래스로 구현하였습니다.
`Node` 타입은 Node 타입 자체에서 `next` 프로퍼티에 `Node`의 참조를 저장하고 있고,
``` swift
final class Node<T> {
let data: T
var next: Node?
```
`LinkedList` 타입에서도 `head`, `tail` 프로퍼티에 `Node`의 참조를 저장할 필요가 있기 때문에 클래스로 구현하였습니다.
``` swift
struct LinkedList<T> {
private(set) var head: Node<T>?
private(set) var tail: Node<T>?
```
- 제네릭의 변수 명을 T로 설정하신 이유가 있을까요?
[generics](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/)의 예제를 따라 딱히 고민하지 않고 T로 설정하였습니다. 하비의 의견을 받고 찾아보니, T는 Type의 첫 글자를 의미한다고 하네요.
이번 프로젝트에 더 적절한 제네릭 이름으로 변경해볼 수 있을지 고민해보도록 하겠습니다.
---
@havilog
안녕하세요 하비! 코낄이, 베토입니다.
은행창구 매니저 프로젝트의 step2 PR 보냅니다.
## 고민했던 점 & 조언받고 싶은 점
### struct, class
* 저번 PR때 sturct와 class의 차이점에 대해서 고민을 해서 타입을 결정하였습니다. 저희는 나름대로 근거를 가지고 struct를 선택하였는데 코드를 작성하다 보니 mutating 키워드를 사용해서 값을 변경해야 하는 일이 많았습니다.
* mutating func가 실행될 때 마다 인스턴스가 복사되니까 성능상의 손해가 있을 것이라 생각했는데, WWDC에서 mutating 관련한 언급은 나오지 않았습니다. :thinking:
* 일반적으로 타입을 선택하는데 있어서 mutating은 고려사항이 아닌지, 하비의 의견을 여쭤보고 싶습니다.
### 고객 수와 업무시간 출력
* 이번 요구 사항에서 총 몇명인지, 총 걸린 시간이 얼마인지 출력하는 요구사항이 있었습니다. 실행 예시를 보면 업무 시간은 소수점 두 자리까지 표기하고 있어서 NumberFormatter로 숫자 데이터의 형식을 설정하였습니다.
``` swift
guard let totalClient = numberOfClient.numberFormat(),
let totalWorkTime = (Double(numberOfClient) * BankTeller.requiredTime).numberFormat() else { return }
print("업무가 마감되었습니다. 오늘 업무를 처리한 고객은 총 \(totalClient)명이며, 총 업무시간은 \(totalWorkTime)초입니다.")
```
* 그런데 다음과 같이 String 함수로도 저희가 원하는 형식을 설정할 수 있다는 것을 알게 되었습니다.
``` swift
let workTime = String(format: "%.2f", (Double(numberOfClient) * BankTeller.requiredTime))
```
* 하지만 숫자 데이터이기 때문에 형식을 설정하기 위해 NumberFormatter를 적용하는 것이 자연스럽다고 생각해 NumberFormatter를 적용하였습니다.
### 실제 0.7초 계산
* 각 은행원이 고객의 업무를 처리하는 데에 걸리는 시간이 0.7초라고 하여 실제로 작업시작 print문을 실행하고 0.7초 지난 후 작업완료 print문이 나오게 하였습니다. 이렇게 실제로 저희가 0.7초를 기다리는 과정을 수행해야하는지 아니면 그냥 총 몇명인지 계산하여 0.7을 곱한 숫자만 보여줘도 되는지 예시에서 정확하게 알 수가 없어 질문드립니다.
---
- Array를 예로 들면 copy on write를 통해 메모리 관련 최적화가 일어나지만,
해당 Bank같은 경우에는 numberOfClient가 바뀌는 일이 잦기도 하고, bankTeller의 배열과 Queue또한 재할당되면, struct로써의 장점이 좀 희미해질거 같다는 생각이 드네요.
또한 openBank가 불필요하게 mutating함수가 됨으로써 BankManager에서 들고있는 bank프로퍼티가 variable이 됨으로써 외부에서 bank를 바꿔버릴 가능성도 존재하구요.
-
- mutating이 자주 일어나서 프로퍼티의 CoW 장점이 희미해진다는 점과 BankManager 내 bank프로퍼티가 variable 됨으로써 변경될 수 있는 점을 고려해서 bank타입을 struct에서 class로 수정하였습니다.