# ๐ฆIos Bank Manager๐ฆ
## ๐๏ธ๋ชฉ์ฐจ
1. [์๊ฐ](#-์๊ฐ)
2. [๊ฐ๋ฐํ๊ฒฝ ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ](#-๊ฐ๋ฐํ๊ฒฝ-๋ฐ-๋ผ์ด๋ธ๋ฌ๋ฆฌ)
3. [ํ์](#-ํ์)
4. [ํ์๋ผ์ธ](#-ํ์๋ผ์ธ)
5. [ํ์ผ๊ตฌ์กฐ](#-ํ์ผ๊ตฌ์กฐ)
6. [UML](#-UML)
7. [์คํํ๋ฉด](#-์คํ-ํ๋ฉด)
8. [ํธ๋ฌ๋ธ ์ํ
๋ฐ ๊ณ ๋ฏผ](#-ํธ๋ฌ๋ธ-์ํ
-๋ฐ-๊ณ ๋ฏผ)
9. [์ฐธ๊ณ ๋งํฌ](#-์ฐธ๊ณ -๋งํฌ)
## ๐ ์๊ฐ
[Aaron](https://github.com/hashswim), [Tottale](https://github.com/tottalE)์ ์ํ ์ฐฝ๊ตฌ ๋งค๋์ ํ๋ก์ ํธ์
๋๋ค.
## ๐ป ๊ฐ๋ฐํ๊ฒฝ ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
[]()
[]()
![]()
## ๐ง ํ์
|<img src = "https://i.imgur.com/I8UdM0C.png" width=200 height=170>|<img src = "https://i.imgur.com/ZykY6Vn.png" width=200 height=170>
|:--:|:--:|
|[Aaron](https://github.com/hashswim)|[Tottale](https://github.com/tottalE)|
## ๐ ํ์๋ผ์ธ
### STEP1
- cocoapods `swiftlint` setting
- `Linked List`, `Node`, `Queue` ํด๋์ค ์์ฑ ๋ฐ ๊ธฐ๋ณธ ๊ตฌํ ์ ์
- `Linked List`์ `Push`, , `Clear`, `RemoveAll`, `Peek`, `isEmpty` ๊ธฐ๋ฅ ๋ฉ์๋ ๊ตฌํ
- `Queue`์ `Enqueue`, `Dequeue`, `Clear`, `Peek`, `isEmpty` ๊ธฐ๋ฅ ๋ฉ์๋ ๊ตฌํ
- `Queue` ํด๋์ค์ ๋ํ ์ ๋ ํ
์คํธ ์งํ
### STEP2
- `Bank`, `Customer`, `BankClerk` ํด๋์ค ์ ์๋ก ๊ณ ๊ฐ, ์ํ, ์ํ์ ํ์
๊ตฌํ
- ์ํ ๋ด์ `open()`, `close()`, `menu()` ๊ตฌํ
- ์ํ์์ 10~30์ฌ์ด์ ๋๋คํ ๋ช
์์ ์๋๋ค์ ์์ฑ
- ์ํ์์ ์ํ์์ด ์๋๋ค์ ์ฒ๋ฆฌํ๋๋ก ๋ก์ง ๊ตฌํ
- CFAbsoluteTimeGetCurrent() ์ ์ฌ์ฉํ์ฌ ์์ ๋ฐ ์ข
๋ฃ ์๊ฐ์ ์ธก์ ํด์ ์๋ฃ์์ ๋ณด์ฌ์ฃผ๋๋ก ํจ
- CFAbsoluteTime์ extension์ผ๋ก fomat๋ 2์๋ฆฌ ์์์ ์ string์ ๋ฐํํ๋๋ก ๊ตฌํ
### STEP3
- ์๊ธ๊ณผ ๋์ถ ์
๋ฌด ํ ๋ถ๋ฆฌ
- ๋์ถ๊ณผ ์๊ธ ํ ๊ฐ๊ฐ ๋์์ ์ผ๋ก work ๋ฉ์๋ ์ฒ๋ฆฌ ๊ตฌํ
- ํ์ฅ์ฑ๊ณผ ๋คํ์ฑ์ ๊ณ ๋ คํ์ฌ workable ํ๋กํ ์ฝ ๋ฐ ์๊ธ, ๋์ถ ์ํ์ ๊ตฌ์กฐ์ฒด ๊ตฌํ
- ์ํ์ ํ์
๋๊ฐ์ง(DepositClerk, LoanClerk)๋ก ๋ถ๋ฆฌ, Workable protocol ์์ฑ
- serial Queue๋ฅผ ํ์ฉํ์ฌ Race Condition์ ๋ฐฉ์งํด ์ค.
- Workable ํ๋กํ ์ฝ ๋ด๋ถ์ scheduleWork() ํจ์๋ฅผ ์ ์ํ์ฌ DispatchWorkItem์ ์์ฑํด์ฃผ๋๋ก ํจ.
- Bank init()์์ ์ํ์ ์์, ์๊ธ์ธ์์๋ฅผ ์
๋ ฅํ๋ฉด ๋์ถ ๋ฐ ์๊ธ ์ํ์์ด ์๋ ์์ฑ๋๋๋ก ํด์ค.
### STEP4
- UIStackView ๊ธฐ์ค์ผ๋ก autoLayout์ ์ก์์ฃผ๊ณ UI๋ฅผ ๋ง๋ค์ด์ค.
- View์ ๋ง๋๋ก controller์ model์ ์์ ํด ์ค.
- ์ปค์คํ
๋ทฐ๋ฅผ ๋ง๋ค์ด ์ ์ฉํด ์ค.
## ๐พ ํ์ผ๊ตฌ์กฐ
```
โโโ BankManagerConsoleApp/
โโโ common
โ โโโCFAbsoluteTime+
|
โโโ Appdelegate
โโโ SceneDelegate
โโโ Model
โ โโโ BankManager
โ โโโ LinkedList
โ โโโ Queue
โ โโโ Node
โ โโโ Bank
โ โโโ Customer
โ โโโ BankClerk
โ
โโโ View/
โ โโโ Main
โโโ Controller/
```
## ๐ UML
> 
## ๐ป ์คํ ํ๋ฉด
<!-- |์ผ๋ฐ ํ๋ฉด|๋ค์ด๋๋ฏน ํ์
์ ์ฉํ๋ฉด|
|:----:|:----:|
|<img src="https://i.imgur.com/vE9Xqbh.gif" width="300px">|<img src="https://i.imgur.com/68Y8ozn.gif" width="300px">| -->
> <img src="https://i.imgur.com/HZ4T4NK.gif" width="600px">
## ๐ฏ ํธ๋ฌ๋ธ ์ํ
๋ฐ ๊ณ ๋ฏผ****
### 1. `push ๋ฉ์๋ ๋ก์ง`
```swift!
class LinkedList<T> {
...
func push() {
if self.isEmpty {
self.head = node
self.tail = node
return
}
self.tail?.next = node
self.tail = node
}
...
}
```
- `self.tail = node` ์ ์ค๋ณต์ ์์ ๊ธฐ ์ํด
[๋ค์](https://github.com/yagom-academy/ios-bank-manager/pull/214/commits/eba1e9f052f874584e140a9d039c101a3243da00)๊ณผ ๊ฐ์ด ์์ ํ์ผ๋ ์ฝ๋์ ๊ฐ๋
์ฑ๊ณผ ์ง๊ด์ฑ์ด ๋จ์ด์ ธ ์๋์ ๊ฐ์ด ์์ ํจ
```swift!
class LinkedList<T> {
...
func push() {
if self.isEmpty {
self.head = node
} else {
self.tail?.next = node
}
self.tail = node
...
}
```
### 2. delay ๊ตฌํ
```swift
struct BankClerk {
func work(for customer: Customer) {
print("\(customer.number)๋ฒ ๊ณ ๊ฐ ์
๋ฌด ์์")
usleep(useconds_t(700000))
print("\(customer.number)๋ฒ ๊ณ ๊ฐ ์
๋ฌด ์๋ฃ")
}
}
```
- `Timer`, `asyncAfter`, `DispatchSourceTimer` ๋ฑ์ ๋ฐฉ๋ฒ์ ๊ณ ๋ คํ์ง๋ง Step 2์ ์๊ตฌ์ฌํญ์๋ง ๋ง๋๋ก `usleep`์ ์ฌ์ฉํ์ฌ ๊ตฌํ
### 3. ์ํ์์ ์ญํ
์ด์ ์ฝ๋์์๋ ์ํ์์ ์ญํ ์ด ๋ฌด์์ด๋ ์๊ด์์ด ์ํ์์ print๋ง์ ๋ด๋นํ์์ต๋๋ค. ๊ทธ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
```swift
struct BankClerk {
func work(for customer: Customer) {
print("\(customer.number)๋ฒ ๊ณ ๊ฐ ์
๋ฌด ์์")
}
}
```
ํ์ง๋ง, ๊ฐ์ฒด์งํฅ์ ์ธ ์ค๊ณ๊ฐ ์๋๋ผ๋ ์๊ฐ์ด ๋ค์๊ณ , ์ํ์์ enum ํ์
์ผ๋ก ๋์ถ ๋ฐ ์๊ธ ํํ๋ก ๊ตฌ๋ถํด์ฃผ๋ ๊ฒ์ด ์๋ Workable ํ๋กํ ์ฝ์ ๋ฐ๋ฅด๋ ๋์ถ ์ํ์, ์๊ธ ์ํ์์ผ๋ก ๋ง๋ค์ด์ฃผ๋ ๊ฒ์ด ๋ ๋ซ๋ค๊ณ ํ๋จํ์์ต๋๋ค.
```swift
protocol Workable {
var service: Service { get }
var processingTime: Double { get }
func work(for customer: Customer)
func scheduleWork(from customerQueue: Queue<Customer>) -> DispatchWorkItem
static var serviceQueue: DispatchQueue { get }
}
```
์์ ๊ฐ์ ํํ๋ก ํ๋กํ ์ฝ์ ์์ฑํด ์ํ์์ด ์ผ์ ์ค์ผ์ฅด๋ง ํ์ฌ ์ผ์ ํ ์ ์๋๋ก ํ๊ณ ๊ฐ๊ฐ์ ํ์
ํ๋กํผํฐ๋ก serviceQueue๋ฅผ ์ค์ ํ์ฌ race condition์ ๋ฐฉ์งํ ์ ์๋ serial queue๋ฅผ ๋ง๋ค์ด ์ฃผ์์ต๋๋ค.
### 4. ๊ฐ์ ํ ์ ๊ทผ์ ๋ํ race condtion ๋ฐฉ์ง
- `DispatchQueue.gloabl().async`๋ฅผ ์ฌ์ฉํด ๋์์ ์ผ๋ก ์ฒ๋ฆฌํ ๊ฒฝ์ฐ ๊ฐ์ ๊ณ ๊ฐ ๋๊ธฐ์ด์ ์ ๊ทผํ๊ฒ ๋์ด race condition์ด ๋ฐ์ํ ์ ์์ต๋๋ค. <br>
์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์ `Dispatch Gruop`์ ์ฌ์ฉํด ํด๋น ๊ทธ๋ฃน์ task๊ฐ ๋๋๊ธฐ ์ ๊น์ง ๋๊ธฐ์ ์ผ๋ก ๊ณ ๊ฐ์ ๋ฐ์ race condition์ ๋ฐฉ์งํ์ต๋๋ค.
```swift
func serve() {
let group = DispatchGroup()
clerks.forEach { clerk in
switch clerk.service {
case .loan:
DispatchQueue.global().async(group: group, execute: clerk.scheduleWork(from: loanQueue))
case .deposit:
DispatchQueue.global().async(group: group, execute: clerk.scheduleWork(from: depositQueue))
}
}
group.wait()
}
```
```swift
func scheduleWork(from customerQueue: Queue<Customer>) -> DispatchWorkItem {
let depositWorkItem = DispatchWorkItem {
while customerQueue.isEmpty == false {
var customer: Customer?
Self.serviceQueue.sync {
customer = customerQueue.dequeue()
}
guard let customer = customer else {
return
}
self.work(for: customer)
}
}
return depositWorkItem
}
```
## ๐ ์ฐธ๊ณ ๋งํฌ
[Swift ์ง์ฐ์คํ ์คํ - NSTimer, asyncAfter, DispatchSourceTimer
](https://es1015.tistory.com/405)
[Refactoring: Replace Enum with Polymorphism](https://medium.com/swift-fox/refactoring-replace-enum-with-polymorphism-c4803baeba07)