# iOS 커리어 스타터 캠프 # 💰 은행창구 매니저 > 프로젝트 기간: 2023.03.06 ~ 2023.03.17 > > 팀원: 👨🏻‍💻[kaki](https://github.com/kak1x), 👨🏻‍💻[송준](https://github.com/kimseongj) | 리뷰어: 👨🏻‍💻[도미닉](https://github.com/AppleCEO) > > 동시성 프로그래밍을 활용하여 은행창구 매니저 콘솔 앱 및 UI 앱 구현 ## 목차 1. [타임라인](#-타임라인) 2. [프로젝트 구조](#-프로젝트-구조) 3. [실행 화면](#-실행-화면) 4. [트러블 슈팅](#-트러블-슈팅) 5. [참고 링크](#참고-링크) <br/> # ⏰ 타임라인 <details> <summary><big>타임라인</big></summary> - 23.03.06 (월): Linked List Queue 구현, Unit Test 구현 (STEP1 PR) <br> - 23.03.07 (화): STEP1 리뷰 사항 수정 (Refactor) <br> - 22.03.08 (수): BankManager 구현 및 실행 (STEP2 PR) <br> - 22.03.09 (목): STEP2 리뷰 사항 수정 (변수명 수정, 함수 분리) <br> - 22.03.10 (금) : DispatchQueue로 은행업무 구현 (STEP3 PR) <br> - 22.03.13 (월) : STEP 3 리뷰 사항 수정 (매직넘버 처리, 함수 분리, 코드 컨벤션) <br> - 23.03.14 (화): STEP4 StackView를 활용한 UI 구현, 로직에 대한 비동기 처리 <br> - 22.03.15 (수): STEP4 Timer 구현, 버튼 구현 (STEP4 PR) <br> - 22.03.16 (목): STEP4 리뷰 사항 수정 </details> <br/> <br/> # ✍🏻 프로젝트 구조 <details> <summary><big>ConsoleApp폴더구조</big></summary> <img src = "https://i.imgur.com/qvSxhAo.png"> </details> <br> <details> <summary><big>UIApp폴더구조</big></summary> <img src = "https://i.imgur.com/uNTmjiy.png"> </details> <br/> <br/> # ✨ 실행 화면 ### 은행창구 ConsoleApp 실행화면 ![콘솔앱실행](https://user-images.githubusercontent.com/51234397/225524048-c3d06915-21e7-4cb8-a79c-61cbc1b9940f.gif) ### 은행창구 ConsoleApp 종료화면 ![콘솔앱종료](https://user-images.githubusercontent.com/51234397/225524059-b7b22114-ba3c-4d03-9a90-d1ceb15a348c.gif) ### 은행창구 UIApp 실행화면 |<center>고객추가 버튼 실행</center>|<center>실행 중 초기화</center>|<center>실행 종료 후 초기화</center>| |---|---|---| | <img src = "https://user-images.githubusercontent.com/88870642/225524404-afb6cce9-9fa5-452b-83f9-ca12bb533eea.gif" width = 200> | <img src = "https://user-images.githubusercontent.com/88870642/225523034-4117d522-f8f3-41ed-a8a9-1b174d1dea5c.gif" width = 200> | <img src = "https://user-images.githubusercontent.com/88870642/225522780-29fa384b-1802-4ed0-ab49-87c6c680d8dd.gif" width = 200> # 🔥 트러블 슈팅 ## ConsoleApp ### 1. 테스트 코드 작성 시 given과 when 구분 >아래와 같이 테스트 코드 작성 시 `given`과 `when`에 무엇을 적어야할지 고민했습니다. `given`은 어떤 상황이 주어지고, `when`은 어떤 코드를 실행한다고 배웠습니다. 이 부분에 있어`enqueue`메서드는 `given`인지, `when`인지 헷갈렸습니다. ```swift // 1번 func test_dequeue됐을경우_처음_enqueue된_데이터가_반환된다() { // given let expectation = "hello" // when sut.enqueue("hello") sut.enqueue("kaki") sut.enqueue("songjun") let result = sut.dequeue()! // then XCTAssertEqual(result, expectation) } // 2번 func test_dequeue됐을경우_처음_enqueue된_데이터가_반환된다() { // given let expectation = "hello" sut.enqueue("hello") sut.enqueue("kaki") sut.enqueue("songjun") // when let result = sut.dequeue()! // then XCTAssertEqual(result, expectation) } ``` ### ⚒️ 해결방법 > given 은 테스트 환경을 만드는 것이고 when 은 테스트하고 싶은 중요 기능을 실행한다라고 나뉘어질 수 있습니다. 위 테스트에서 중요 기능은 dequeue이고 enqueue작업은 테스트 환경을 만들어주는 것이라 생각되어 2번 방법으로 테스트 코드를 작성했습니다. </br> ### 2. 프로그램 실행 시간 확인 > 기존엔 고객 한 명당 0.7초씩 고정적으로 시간을 소요하기 때문에 프로그램 실행 시간을 고객수 * 0.7 초로 계산해주었습니다. 하지만 Step3로 넘어오며 대출 업무 고객은 1.1초, 예금 업무 고객은 0.7초로 각기 다른 시간이 소요되어 기존의 방식을 사용할 수 없었습니다. ### ⚒️ 해결방법 ```swift mutating func processBusiness() { let startTime = CFAbsoluteTimeGetCurrent() while !waitingQueue.isEmpty { guard let client = waitingQueue.dequeue() else { return } processBankTask(client) } group.wait() let wasteTime = CFAbsoluteTimeGetCurrent() - startTime presentBusinessResult(time: wasteTime) } ``` > CFAbsoluteTimeGetCurrent()를 사용하여 시작되는 시점의 시간을 프로그램 실행이 끝났을때의 시간에서 빼주는 것으로 총 소요시간을 계산할 수 있었습니다. ### 3. 비동기 코드를 넘기고 바로 뒤의 코드를 실행시키는 현상 > 업무에 관한 코드를 전부 비동기로 설정해주었더니 비동기 코드를 바로 끝내고 아예 기다리지 않은 채 뒤의 코드를 실행시키는 문제가 발생하였습니다. ![](https://i.imgur.com/seTy6Jj.png) ### ⚒️ 해결방법 ```swift mutating func processBusiness() { while !waitingQueue.isEmpty { guard let client = waitingQueue.dequeue() else { return } processBankTask(client) } group.wait() presentBusinessResult(time: wasteTime) } private func processBankTask(_ client: Client) { if client.requstedTask == .loan { DispatchQueue.global().async(group: group) { loanSemaphore.wait() loanSemaphore.signal() } } else { DispatchQueue.global().async(group: group) { depositSemaphore.wait() depositSemaphore.signal() } } } ``` > DispatchGroup을 이용하여 비동기적으로 처리되는 작업들을 그룹으로 묶어주고, group.wait()을 사용하여 DispatchGroup의 수행이 끝나는것을 기다리게 해주어 문제를 해결하였습니다. ## UIApp ### 1. DispatchQueue -> OperationQueue > 초기화 버튼 기능 구현 시, 초기화를 누르면 스레드가 하던 일도 cancel이 되어야 하는데 DispatchQueue에서 진행중인 스레드를 멈출 방법을 찾을 수 없었습니다. ![](https://camo.githubusercontent.com/5e32a8b9e1f7c27db52a803a1463d447f82f9078eb7a1c0792cd478d3b9ac2ce/68747470733a2f2f692e696d6775722e636f6d2f54686c757770452e706e67) 또한 main 스레드에서 작업 처리 시, qos를 변경해주지 않고 dispatch_semaphore_wait 및 dispatch_group_wait을 사용하는 경우 에러가 발생하였습니다. ### ⚒️ 해결방법 ```swift private let loanQueue = OperationQueue() private let depositQueue = OperationQueue() private func processBankTask(_ client: Client) { if client.requstedTask == .loan { loanQueue.maxConcurrentOperationCount = 1 loanQueue.addOperation { processPersonalBankTask(client) } } else if client.requstedTask == .deposit { depositQueue.maxConcurrentOperationCount = 2 depositQueue.addOperation { processPersonalBankTask(client) } } } ``` > DispatchQueue 대신 OperationQueue를 사용하여 문제를 해결하였습니다. > 또한 DispatchQueue에서 불가능했던 스레드를 멈추는 것 또한 cancelAllOperations() 메서드를 사용하여 해결할 수 있었습니다. ### 2. StackView Alignment 문제 > 스택뷰에 autoLayout값이 정확하게 들어가지 않아 생기는 문제점을 발견했습니다. > alignment를 default값(fill)으로 넣으면 아래 오류 사항처럼 UI가 정상적으로 작동하지 않습니다. ### ⚒️ 해결방법 > alignment값을 top으로 주어 문제를 해결했습니다. |<center>오류 사항</center>| <center>오류 사항 해결</center>| |---|---| |<img src = "https://user-images.githubusercontent.com/88870642/225525993-de7495fc-5afc-430b-820c-d8367255670f.gif" width = 200>|<img src = "https://user-images.githubusercontent.com/88870642/225524404-afb6cce9-9fa5-452b-83f9-ca12bb533eea.gif" width = 200>| <br/> <br/> # 참고 링크 * [Apple Developer Documentation - CFAbsoluteTimeGetCurrent()](https://developer.apple.com/documentation/corefoundation/1543542-cfabsolutetimegetcurrent) * [야곰 닷넷 - 동시성 프로그래밍](https://yagom.net/courses/동시성-프로그래밍-concurrency-programming/) * [Apple Developer Documentation - DispatchGroup](https://developer.apple.com/documentation/dispatch/dispatchgroup) * [Apple Developer Documentation - OperationQueue](https://developer.apple.com/documentation/foundation/operationqueue) * [Apple Developer Documentation - Diagnosing performance issues early](https://developer.apple.com/documentation/xcode/diagnosing-performance-issues-early)