# ๐ŸฆIos Bank Manager๐Ÿฆ ## ๐Ÿ—’๏ธŽ๋ชฉ์ฐจ 1. [์†Œ๊ฐœ](#-์†Œ๊ฐœ) 2. [๊ฐœ๋ฐœํ™˜๊ฒฝ ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ](#-๊ฐœ๋ฐœํ™˜๊ฒฝ-๋ฐ-๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ) 3. [ํŒ€์›](#-ํŒ€์›) 4. [ํƒ€์ž„๋ผ์ธ](#-ํƒ€์ž„๋ผ์ธ) 5. [ํŒŒ์ผ๊ตฌ์กฐ](#-ํŒŒ์ผ๊ตฌ์กฐ) 6. [UML](#-UML) 7. [์‹คํ–‰ํ™”๋ฉด](#-์‹คํ–‰-ํ™”๋ฉด) 8. [ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… ๋ฐ ๊ณ ๋ฏผ](#-ํŠธ๋Ÿฌ๋ธ”-์ŠˆํŒ…-๋ฐ-๊ณ ๋ฏผ) 9. [์ฐธ๊ณ ๋งํฌ](#-์ฐธ๊ณ -๋งํฌ) ## ๐Ÿ‘‹ ์†Œ๊ฐœ [Aaron](https://github.com/hashswim), [Tottale](https://github.com/tottalE)์˜ ์€ํ–‰ ์ฐฝ๊ตฌ ๋งค๋‹ˆ์ € ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ## ๐Ÿ’ป ๊ฐœ๋ฐœํ™˜๊ฒฝ ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ [![swift](https://img.shields.io/badge/swift-5.6-orange)]() [![xcode](https://img.shields.io/badge/Xcode-13.4.1-blue)]() ![]() ## ๐Ÿง‘ ํŒ€์› |<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 > ![](https://i.imgur.com/lY3ttMO.jpg) ## ๐Ÿ’ป ์‹คํ–‰ ํ™”๋ฉด <!-- |์ผ๋ฐ˜ ํ™”๋ฉด|๋‹ค์ด๋‚˜๋ฏน ํƒ€์ž… ์ ์šฉํ™”๋ฉด| |:----:|:----:| |<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)