mene79
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 🏦 은행 창구 매니저 ## 📖 목차 1. [소개](#-소개) 2. [Tree](#-tree) 3. [타임라인](#-타임라인) 4. [고민한 점](#-고민한-점) 5. [실행 화면](#-실행-화면) 6. [트러블 슈팅](#-트러블-슈팅) 7. [참고 링크](#-참고-링크) ## 🌱 소개 `Mangdi`와 `mene` 가 만든 은행 창구 매니저 앱입니다. - 비동기 처리를 이용해(GCD) 고객 대기열의 고객을 3명의 직원이 맡은 업무대로 동시에 처리합니다. - **KeyWords** - `Data Type` - `Linked-list`, `Queue` - `Concurrency` - `GCD`, `sync`, `async`, `Thread` - `Package Manager` - `CocoaPods`, `Carthage`, `Swift Package Manager` - `Closure capture`, `Capture list` - `코드로 화면 그리기` ## 💻 개발환경 및 라이브러리 [![swift](https://img.shields.io/badge/swift-5.6-orange)]() [![xcode](https://img.shields.io/badge/Xcode-13.4.1-blue)]() [![sindresorhus's SwiftLint config](https://img.shields.io/badge/SwiftLint-0.49.1-green.svg)](https://github.com/realm/SwiftLint) ## 🧑🏻‍💻 팀원 |<img src="https://avatars.githubusercontent.com/u/49121469" width=160>|<img src = "https://avatars.githubusercontent.com/u/84453688?v=4" width=160>| |:--:|:--:| |[Mangdi](https://github.com/MangDi-L)|[Mene](https://github.com/JaeKimdev)| ## 🌲 Tree ``` ── BankManagerUIApp    ├── AppDelegate.swift    ├── Base.lproj    ├── Controller    │   └── ViewController.swift    ├── Info.plist    ├── Model    │   ├── Assets.xcassets    │   │   ├── AccentColor.colorset    │   │   │   └── Contents.json    │   │   ├── AppIcon.appiconset    │   │   │   └── Contents.json    │   │   └── Contents.json    │   ├── Bank.swift    │   ├── Customer.swift    │   ├── CustomerQueue.swift    │   └── Node.swift    ├── SceneDelegate.swift    └── View    └── Base.lproj    ├── LaunchScreen.storyboard    └── Main.storyboard ``` ## ⏰ 타임라인 <details> <summary>Step 1 타임라인</summary> - **221031** - 코코아팟 설치 및 SwiftLint 라이브러리 적용 후 warning이 뜨지않도록 swiftLint rule 수정 - Node & CustomerQueue 타입 구현하고 CustomerQueue Unit Test - Step1 PR 리뷰 요청 - [PR보러가기](https://github.com/yagom-academy/ios-bank-manager/pull/218) - **221101** - 리뷰 내용 반영하여 리팩토링 - Node 타입의 front와 rear에 접근제어자 설정하고 테스트 코드 수정 - CustomerQueue dequeue 메서드 조건문에 옵셔널 활용하도록 리팩토링 </details> <details> <summary>Step 2 타임라인</summary> - **221102** - Node & CustomerQueue 파일 ConsoleApp으로 복사 - Customer타입 구현 - BankManager의 selectMenu, addCustomer, processTask메서드 구현 - 한번 실행한 후에 다시 1번을 선택했을 때 작동하지 않던 오류 수정 - Bank와 BankManager의 역할분리 및 n명의 매니저가 대응하도록 수정 - Step2 PR 리뷰 요청 - [PR보러가기](https://github.com/yagom-academy/ios-bank-manager/pull/229) - 리뷰 내용 반영하여 리팩토링 - Bank 타입의 addCustomer 메서드 forEach 사용하도록 리팩토링 </details> <details> <summary>Step 3 타임라인</summary> - **221107** - Customer 타입에 고객 요구사항 `deposit`과 `loan` Enum으로 구현 - Bank에서 BankManager 삭제하고 Semaphore를 이용하여 은행원 3명이 비동기로 업무를 처리하도록 구현 - Bank에서 고객처리 시간을 측정하는 `processTime` 메서드 구현 - Step3 PR 리뷰 요청 - [PR보러가기](https://github.com/yagom-academy/ios-bank-manager/pull/243) - 리뷰 내용 반영하여 리팩토링 </details> <details> <summary>Step 4 타임라인</summary> - **221109** - 이전 STEP에서 구현했던 ConsoleApp의 파일들을 UIApp으로 이동 - 코드로 UI 구현 - **221110** - Bank타입에 10명의 customer를 추가해주는 `addTenCustomer` 메서드 구현 - Bank의 고객 대기열을 비워주고 waitingNumber를 초기화 하는 `resetAll` 메서드 구현 - CustomerQueue타입에서 대기열의 고객 배열을 반환하는 `takeAll` 메서드 구현 - `displayWaitingCustomers` 메서드 구현 - ScrollView에 추가할 customer 레이블을 생성하는 `makeCustomerLabel` 메서드 구현 - `고객 추가` 버튼과 `초기화` 버튼에 `addTenCustomerButton`, `allClearButton` 메서드 생성하고 연결 - 변경사항 UI 반영을 위해 Bank 타입의 메서드 ViewController로 이동 - 고객 추가 버튼 클릭 시 waitingScrollView에 여러번 추가되던 오류 수정 - **221111** - 고객 추가 버튼 클릭 시 STEP3 에서 작성했던 Print문구가 다 출력 된 후 UI에 반영되던 문제 수정 - UI상에 고객 처리 상태에 따라 대기중, 업무중 ScrollView로 이동하고 사라지도록 구현 </details> ## 👀 고민한 점 ### Step 1 - 맨처음 클론했을때 ios-bank-manager 폴더 안에 `BankManagerUIApp`과 `BankManagerConsoleApp` 폴더가 있었습니다. 이중에 어느 폴더로 Step1을 시작할지 고민했습니다. - 요구사항에 따로 Console을 이용해서 진행하라는 문구가 없었기 때문에 UIApp 프로젝트에 진행하였습니다. - `SwiftLint` 설치 시, 어떤 패키지 매니저를 이용할지 고민했습니다. - `Swift Package Manage`로 라이브러리를 관리할까 생각했는데 호환성문제와 자잘한 버그들이 여전히 존재한다는 이야기를 들은 적이 있어서, 원조적이고 잘 작동하는 `CocoaPods`을 채택해서 사용했습니다. - UItest 작성 시 `String Literal`을 제거하기 위해서 테스트 데이터를 상수로 선언하고 XCTAssertEqual로 비교할 때 사용해 주었습니다. ### Step 2 - Bank 타입에서 은행을 열었을 때 처리할 작업을 나타내는 메서드 네이밍을 `open`으로 하려고 하다가 `open`이 시스템 예약어라서 다른 이름을 사용하기 위해 `openUp`이라는 이름으로 설정해 주었습니다. - UIApp에서 만들었던 파일을 ConsoleApp으로 가져오는 방법을 고민해 보았습니다. - `BankManager` 타입의 `allocateCustomer `메서드에서 모든 고객을 처리한 후 처리시간이 노출될 때 소숫점 2자리까지만 노출되도록 `String(format: "%.2f")`을 적용해 주었습니다. ### Step 3 - `Escaping closure captures mutating 'self' parameter` 에러가 발생하여 제거하는 방법에 대해 고민했습니다. - 클로저를 캡쳐하면 참조 / 값 타입에 상관없이 참조 타입으로 저장이 되고, 값 타입으로 사용하고 싶다면 캡쳐 리스트를 사용해서 저장해야 한다는 것을 새롭게 알게 되었습니다. 이 경우, 캡쳐 리스트에 캡쳐된 변수는 let으로 전달됩니다. - 업무가 완전히 종료되기 전에 마감 메세지가 노출되는 문제를 해결하기 위해 고민해 보았습니다. ### Step 4 - ViewController에서 Bank타입의 메서드가 UI관련 처리를 진행할수 없다보니 어떻게 해결해야할지 고민했습니다. - Bank에서 사용하는 메서드를 ViewController에서 정의해 주어서 UI관련 동시성 처리를 했습니다. 이로써 Bank관련된 메서드를 호출할 일이 없어졌기 때문에 파일을 삭제했습니다. - 긴 코드를 접어서 보는 방법이 좋을지 고민했습니다. 💡코드 접어서 보는 방법 `option + command + ⬅️` - 코드로 UI를 구현할때 view 하나 하나를 직접 코드로 작성해서 뷰 관련 코드가 길어지게 되었습니다. ```swift let mainStackView: UIStackView = { let stackView = UIStackView() stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .vertical stackView.spacing = 8 return stackView }() ... let buttonStackView: UIStackView = { let stackView = UIStackView() stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .horizontal return stackView }() ``` 접어두기 기능을 사용하면 구현한 UI요소들이 한눈에 들어오게됩니다. ![](https://i.imgur.com/zKTRBra.png) 안에 어떻게 구현되어있는지 보이지 않는 단점이 있지만 구현이 다 끝나 더이상 건드릴 필요가 없는 경우라면 이렇게 해주는것도 괜찮겠다는 생각이 들었습니다. 접힌상태로 커밋하고 다른사람이 받아올 때에는 적용이 되지 않아, 자신에게만 유효한 기능같아요 😂 ## 📱 실행 화면 |<img src = "https://i.imgur.com/QmFU5kM.gif)" width=640>|![](https://i.imgur.com/tPDmxV1.png)|![](https://i.imgur.com/Nis2Mpo.png)| |:--:|:--:|:--:| |대기열에 들어있는 고객을<br>순서대로 처리합니다|2번 선택시<br>프로그램 종료|예외 처리 | ## ❓ 트러블 슈팅 ### Step 1 <details> <summary>`SwiftLint` 적용을 위해 코코아팟 설치 시 Ruby version문제로 설치되지 않았습니다.</summary> ![](https://camo.githubusercontent.com/3325a7de9480141339aa46fafb2e70068eb250812bc8aeb4a94b669f9251c0b9/68747470733a2f2f692e696d6775722e636f6d2f34666a4a4e61582e706e67) `xcode-select`도 이미 깔려있었고, `brew`를 이용해서도 권한 문제로 설치가 되지 않아 root 권한을 주고 Ruby 최신 버전을 설치해보려고 했지만 너무 위험하다는 메세지와 함께 설치가 되지 않았습니다. 모든 소프트웨어 업데이트와 `xcode-select` 재설치를 통해 해결하였습니다. </details> ### Step 2 <details> <summary>UIApp에서 만들었던 파일을 ConsoleApp으로 가져오는 방법을 고민해 보았습니다.</summary> <br> UIApp에서 만든 Node, CustomerQueue파일을 ConsoleApp으로 복사하기위해 파일을 끌어서 놨는데 ConsoleApp의 Node, CustomerQueue파일의 내용을 변경할때 UIApp에서의 Node, CustomerQueue내용이 같이 변경된다는 점을 발견했습니다. 이를 해결하기 위해여 아래와 같이 진행했습니다. <img src="https://i.imgur.com/ZCu8pbB.png" width=400> finder에서 consoleApp폴더로 파일을 붙여넣기 한 상태로, finder에서는 붙여넣기한 파일들이 보이지만 아래 사진과 같이 Xcode에는 반영이 안된걸 알수있습니다. <img src="https://i.imgur.com/Livq2wW.png" width=400> (적용이 안된상태) <img src="https://i.imgur.com/7TRXOZx.png" width=600> finder에 추가한 파일들을 Xcode로 끌어다가 놓으면 이런 창이 뜨게 되는데, 여기서 `Copy Items if needed`를 체크해주고 finish 버튼을 클릭하면 <img src="https://i.imgur.com/fM0mOjf.png" width=400> 파일이 잘 들어온걸 확인할 수 있었고 더이상 AppUI의 파일과 내용을 공유하지않았습니다. </details> ### Step 3 <details> <summary>은행 업무를 처리하는 은행원을 어떻게 할당해 주어야 할지 고민해 보았습니다.</summary> <br> 처음에는 BankManager 타입이 업무를 처리하는 은행원이라고 생각해서 3명의 BankManager를 생성하고 forEach문으로 은행 대기열에서 고객을 한명씩 배정해주는 방법으로 구현했습니다. 이러한 경우 대기열에 1,2,3번 고객이 모두 대출 고객인 경우 3명이 모두 할당이 된 상태에서 하나의 쓰레드만 사용 할 수 있었기 때문에 동시에 처리되지 않고 기다리게 되어 원하는 대로 구현이 되지 않았습니다. (예금은 2명, 대출은 1명이 처리) ```swift private mutating func allocateCustomer() { DispatchQueue.global().sync { while customerQueue.isEmpty == false { self.manager.forEach { manager in guard let customer = customerQueue.peek() else { return } manager.processTask(customer: customer) customerQueue.dequeue() } } } } ``` 이를 어떻게 처리해주어야 할까 고민하다가 세마포어에 대해 알게 되었고, 접근 가능한 스레스의 수를 설정해 줄 수 있어서 세마포어의 수가 은행원을 대체할 수 있지 않을까 라는 생각이 들었고 `depositSemaphore`와 `loanSemaphore`를 생성할 때 각각 2명 / 1명으로 설정해 주어서 처리해 주도록 변경하였습니다. ```swift private mutating func allocateCustomer() { let group = DispatchGroup() let depositSemaphore = DispatchSemaphore(value: 2) let loanSemaphore = DispatchSemaphore(value: 1) while customerQueue.isEmpty == false { guard let customer = customerQueue.dequeue() else { return } if customer.requestingTask == .deposit { DispatchQueue.global().async(group: group) { [self] in depositSemaphore.wait() DispatchQueue.global().sync { manager.processTask(customer) } depositSemaphore.signal() } } else { DispatchQueue.global().async(group: group) { [self] in loanSemaphore.wait() DispatchQueue.global().sync { manager.processTask(customer) } loanSemaphore.signal() } } } } ``` </details> <details> <summary>업무가 완전히 종료되기 전에 마감 메세지가 노출되는 문제 방법을 고민해 보았습니다.</summary> - DispatchGroup를 사용하지 않고 작성 시, 업무배정이 완료되면 마감 메세지가 먼저 노출되는 문제가 있어, DispatchGroup의 wait 메서드를 이용하여 모든 작업이 완료된 후 노출되도록 하였습니다. |![](https://i.imgur.com/W0DVGfN.png)|![](https://i.imgur.com/nRsrBa6.png)| |:--:|:--:| |수정 전|수정 후| </details> ### Step 4 - STEP3 에서 구현해 두었던 print문이 다 찍히고 처리가 완료된 후에 UI `대기중` 스크롤 뷰에 추가되는 문제를 해결하기 위해 고민해 보았습니다. - 이전 스탭에서 완료메세지가 먼저 노출되는 문제 때문에 걸어두었던 group.wait를 확인하고 제거 - 대기중에 있던 고객 레이블을 빼서 업무중에 넣어주는 방법에 대해 고민해 보았습니다. - 고객 레이블 생성 시, tag를 달아주어 작업이 완료된 레이블인지 확인하고 제거 - 타이머 구현 후, 어느 시점에 resume, suspend를 호출해야 하는지 고민해 보았습니다. - 타이머 작동 시, 글자가 떨리는 것처럼 노출되는 문제 - 고정폭 글꼴 사용 ## 🔗 참고 링크 [Swift Language Guide - Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) [Concurrency Programming Guide](https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html) [WWDC2016 - Concurrent Programming With GCD in Swift 3](https://developer.apple.com/videos/play/wwdc2016/720/) [야곰 닷넷 - 동시성 프로그래밍](https://yagom.net/courses/%eb%8f%99%ec%8b%9c%ec%84%b1-%ed%94%84%eb%a1%9c%ea%b7%b8%eb%9e%98%eb%b0%8d-concurrency-programming/) [야곰 닷넷 - 오토 레이아웃 정복하기](https://yagom.net/courses/autolayout/) --- [🔝 맨 위로 이동하기](#-은행-창구-매니저) ###### tags: `README`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully