Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

은행 창구 관리 앱 [Step3] 이지, hong #92

Open
wants to merge 33 commits into
base: d_Hong
Choose a base branch
from

Conversation

ujhong7
Copy link

@ujhong7 ujhong7 commented Feb 6, 2024

@LeeZion94
안녕하세요 시온~~! 오래 기다리셨습니다..
STEP 3 PR 보내드립니다~


📝구현 사항

프로퍼티

let depositBankerCount: Int
let loanBankerCount: Int
var totalDepositWorkingTime: Double 
var totalLoanWorkingTime: Double

예금 은행원과 대출 은행원의 수를 나타내는 프로퍼티와 각각의 업무 시간을 측정하는 프로퍼티를 가지고 있습니다.

Queue타입으로 구현한 대기열들

private var depositBankerQueue = Queue<Banker>()
private var loanBankerQueue = Queue<Banker>()
private var depositCustomerQueue = Queue<Customer>()
private var loanCustomerQueue = Queue<Customer>()

큐를 이용하여 예금 은행원과 대출 은행원, 예금 고객, 대출 고객을 관리합니다.
업무가 진행되기 위해서는 손님 뿐만 아니라 현재 업무가 가능한 은행원 (일하고 있지 않은 은행원)도 동시에 필요하기 때문에 업무에 맞는 은행원 큐를 따로 만들어 현재 업무가 가능한 은행원의 대기열을 구현했습니다.
각각의 큐는 setUpBankerQueuesetUpCustomerQueue를 통해 은행 개점과 함께 반복문으로 enqueue되며 채워집니다.

업무구분을 위한 TaskType

enum TaskType: CaseIterable, CustomStringConvertible {
    case loan
    case deposit
}

extension TaskType {
    var description: String {
        switch self {
        case .loan:
            return "대출"
        case .deposit:
            return "예금"
        }
    
    }
    
    var taskTime: Double {
        switch self {
        case .loan:
            return 1.1
        case .deposit:
            return 0.7
        }
    }
}

업무 종류에 따라 각각의 enum 케이스를 만들고, 필요한 계산속성을 구현했습니다.

업무시간 측정을 위한 속성

private var totalDepositWorkingTime: Double
private var totalLoanWorkingTime: Double

...

let formattedTotalWorkingTime = max(totalLoanWorkingTime, totalDepositWorkingTime).formattedDecimal
        Messages.closeBank(customerCount: numberOfCustomers, totalTime: formattedTotalWorkingTime).printMessage()

예금업무와 대출업무 소요시간을 각각 다른 변수에 저장해 두 시간중 더 오래걸린 시간을 총 업무시간으로 표현했습니다.
Double 타입의 확장을 통해 두자리수로 잘라 출력되도록 했습니다.

업무 진행을 위한 메서드와 비동기 처리

func open() {
...
while !depositCustomerQueue.isEmpty() || !loanCustomerQueue.isEmpty() {
    serveCustomer(bankerQueue: depositBankerQueue, customerQueue: depositCustomerQueue)
    serveCustomer(bankerQueue: loanBankerQueue, customerQueue: loanCustomerQueue)
}
group.wait()

...
}
...
private func serveCustomer(bankerQueue: Queue<Banker>, customerQueue: Queue<Customer>) {
        if bankerQueue.isEmpty() == false {
            guard let customer = customerQueue.dequeue(),
                  let banker = bankerQueue.dequeue()
            else { return }
            processTask(banker: banker, customer: customer, bankerQueue: bankerQueue)
        }
    }

private func processTask(banker: Banker, customer: Customer, bankerQueue: Queue<Banker>) {
        DispatchQueue.global().async(group: group)  { [self] in
            totalDepositWorkingTime += banker.processCustomer(customer)
            bankerQueue.enqueue(Banker(taskType: banker.taskType))
        }
    }
  • serveCustomer : customerQueuebankerQueue 에서 dequeue 된 각각의 customerbankerprocessTask 메서드로 전달 해 업무를 진행합니다. (손님과 은행원이 만났습니다.)
  • processTask : serveCustomer 에서전달받은 bankercustomer 를 가지고 processCustomer 를 통해 업무를 진행합니다. (은행원이 손님의 업무를 처리해줍니다.) 업무가 마무리되면 다시 bankerQueuebanker 가 enqueue 되며 다음 손님을 기다립니다.
    이때, 업무의 종료 여부와 상관없이 Bank 내의 다른 업무들이 진행될 수 있도록 DispatchQueue.global().async 를 통해 다른 스레드에서 비동기 작업을 하도록 구현했습니다.

🤔고민했던 점들

  • 비동기 처리의 시행착오들
    처음 비동기 처리를 위한 코드를 작성할 때 어려웠던 점은 오류예측과 오류의 원인을 파악하는 것이 힘들다는 것 이었습니다. 그래서 여러가지 오류를 만나고 비동기 처리의 범위를 다양하게 바꿔보면서 시도해 보았습니다. 그중 가장 해결하기 힘들었던 것은 다음과 같은 오류입니다.
    반복문을 빠져나가 업무 종료메세지를 띄운 후에도 비동기 처리 작업이 남아있어 뒤늦게 고객업무 종료 메세지를 띄우게 됩니다.
    image
    image
    (위와같은 상황입니다.)
    출처 : https://sujinnaljin.medium.com/ios-%EC%B0%A8%EA%B7%BC%EC%B0%A8%EA%B7%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-gcd-8-37146743787f

    그래서 저희는 DispatchGroup 을 활용해 비동기 업무까지 모두 종료된 시점까지 코드가 진행되지 않도록 하여 문제를 해결했습니다.

  • Queue를 전역적으로 사용했을 때 문제점..?
    image
    첫번째 실행 후 큐가 초기화 되지 않아 두번째 실행부터 은행원이 남아있게 되어 다음과 같은 문제가 발생합니다.
    은행 업무가 종료된 후에도 은행원이 큐에 남아있기 때문에, 다음 은행 업무를 수행할 때 이미 사용된 은행원이 재사용됩니다.

func open(){
    ...
    depositBankerQueue.clear()
    loanBankerQueue.clear()
}

초기화를 통해 은행 업무가 종료된 후에 은행원 큐가 비워지고 다음 은행 업무를 수행할 때 새로운 은행원이 생성되어 큐에 들어갑니다.
큐를 전역적으로 사용하지 않고 어떻게 활용할 수 있을까요..?

  • 은행원 타입이 꼭 필요할까?
    처음부터 현실세계를 반영한다는 느낌으로 각각의 타입을 만들었습니다.(업무 가능한 은행원 대기열을 위해) 그러나 현재 코드의 banker는 customer가 dequeue 될 수있는 조건으로서 쓰이는 것 같기도 하고 함수로 대체될수 있지않을까.. 란 생각을 하기도 했습니다.
    이런 생각으로 코드를 짜다보니 쓸데없이 코드가 많아지거나 성능상 불이익이 있는 방법으로 만들고 있는게 아닐까란 생각이 들었습니다.
    상황에 따라 많은 정답들이 있겠지만, 시온은 설계단계에서 어떤 것을 더 중점으로 하시는지, 그 기준이 궁금합니다!
    개발자가 이해하기 쉬운코드 VS 간결한 or 조금더 나은 성능의 코드

  • TaskType vs Task
    �'업무 종류' 를 TaskType 으로 네이밍 했는데, TaskType 자체가 타입 이기 때문에 개발자 입장에서 'TaskType 타입' 으로 불러야 할 것 같아 Task로 바꾸는것이 맞을까? 란 생각을 해보았습니다..

@LeeZion94
Copy link

LeeZion94 commented Feb 7, 2024

안녕하세요 이지, 홍 step3 구현해주시느라 너무 고생많으셨습니다~ ㅎㅎ!!!

다른 스텝보다도 이해하기 어려운 것들이 많았음에도 불구하고 결과 값이 잘 나오게 구현해주신 것 같아요!!
코드 정리부터 컨벤션 그리고 가독성까지 대부분의 것들을 고민하시고 구현해주셨다는 느낌을 많이 받았습니다!! 고생하셨어요!

작성해주신 코드나 혹은 동작들에 대해서 몇가지 질문들이 있어서 남겨놓을게요!!

  1. 요구사항
image
  • 고객의 숫자가 특정 숫자 이상으로 넘어가게 될 경우에는 업무 시작 및 종료의 print가 요구사항과는 맞지 않게 출력되고 있는 것으로 확인이 되고있어요! 가독성 좋고 깔끔히 잘 구현을 해주셨지만 요구사항에 대한 확인 또한 매우 중요하다고 생각해요!
  1. 개행
  • 전체적으로 개행을 많이 사용하지 않으신 것 같아요! 물론 개행을 많이 하지 않는 것을 컨벤션으로 생각하실 수 있겠지만 분기할 때 혹은 변수 선언 후 혹은 return 전에와 같이 개행을 적당한 시점에 넣는 것은 코드의 가독성을 상승시킬 수 있는 방법이라고 생각해요!
  1. 사용하신 Queue의 갯수
  • Banker별로 혹은 Customer 별로 사용해주신 Queue의 갯수가 업무 타입별로 존재하다 보니 총 4개의 Queue를 관리하게 된 것으로 보여요. Queue의 갯수를 Banker, Customer별 각 1개씩으로 통합하고 타입 내부에서 업무에 해당하는 값을 가지게 한다면 관리해야하는 Queue의 갯수를 줄일 수 있고 또한 Queue의 갯수가 줄기 때문에 은행업무를 담당하는 로직자체에도 조금 더 가독성 좋게 코드 작성이 가능할 것 같다고 생각했어요!

이외의 질문사항이나 피드백 사항은 코멘트로 남겨둘게요! 다시 한번 고생하셨습니다!

private var loanBankerQueue = Queue<Banker>()
private var depositCustomerQueue = Queue<Customer>()
private var loanCustomerQueue = Queue<Customer>()
private let group = DispatchGroup()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DispatchGroup을 Bank 타입 내부 변수로 선언해주신 이유는 무언인가요?

Copy link
Author

@ujhong7 ujhong7 Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

작업별로 나뉘어진 메서드에서 접근하기 위해 Bank타입에 DispatchGroup를 선언했습니다.


let formattedTotalWorkingTime = max(totalLoanWorkingTime, totalDepositWorkingTime).formattedDecimal
Messages.closeBank(customerCount: numberOfCustomers, totalTime: formattedTotalWorkingTime).printMessage()
depositBankerQueue.clear()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clear 메서드가 필요한 이유는 Banker를 setUp하는 부분을 계속해서 반복해야하는 open이 호출되면서 중복으로 호출되는 것이 문제로 보여요! 이 부분을 clear를 사용하지 않는다면 어떻게 해결할 수 있을까요?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if문을 활용한 방법

private func setUpBankerQueue(depositBankerCount: Int, loanBankerCount: Int) {
        if depositBankerQueue.isEmpty() && loanBankerQueue.isEmpty() {
            for _ in 1...depositBankerCount {
                depositBankerQueue.enqueue(Banker(taskType: .deposit))
            }
            for _ in 1...loanBankerCount {
                loanBankerQueue.enqueue(Banker(taskType: .loan))
            }
        }
    }

init에서 생성하는 방법

init(depositBankerCount: Int, loanBankerCount: Int) {
        for _ in 1...depositBankerCount {
            depositBankerQueue.enqueue(Banker(taskType: .deposit))
        }
        for _ in 1...loanBankerCount {
            loanBankerQueue.enqueue(Banker(taskType: .loan))
        }
        self.depositBankerCount = depositBankerCount
        self.loanBankerCount = loanBankerCount
        self.totalDepositWorkingTime = 0.0
        self.totalLoanWorkingTime = 0.0
    }

두가지 방법을 더 작성해 보았습니다. 저희는 생성자를 활용한 방법이 깔끔하고 일을 줄이는 것 같습니다! ㅎㅎ

}
group.wait()

let formattedTotalWorkingTime = max(totalLoanWorkingTime, totalDepositWorkingTime).formattedDecimal

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Time을 계산하는 로직이 실질적으로 업무시간을 체크하는 것과 조금 상이해보이는 것 같아요. 각각의 업무 Time을 요구사항에서 요구하진 않으니 Time을 1개로 관리하고 출력하는 것은 어떨꺄요?

Copy link

@yoruck2 yoruck2 Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

시간 측정은 관점에 따라 달라 질 수 있는데, 저희는 은행의 모든 업무가 끝날때 까지의 시간을 각각의 은행원들의 업무시간을 더해 측정하는 방식으로 만들었습니다. 예금과 대출의 Time을 따로 둔 것은 각자 따로 더해서 더 오래걸린 업무가 실제 총 은행 업무시간이라 생각했기 때문입니다.
하지만 다시 생각해보니 총 은행업무시간과는 다를 수 있다는 걸 깨달았습니다. 심지어 비동기적으로 업무를 처리하는 예금 은행원의 경우엔 저희의 의도와 정반대로 측정하고 있기 때문에 Date 메서드를 활용해 실제 코드의 실행시간을 측정하는 방향으로 다시 만들어 보았습니다!

let startTime = Date()
        Messages.openBank.printMessage()
        let numberOfCustomers = Int.random(in: 10...30)

        print("고객 수: \(numberOfCustomers)")
        setUpBankerQueue(depositBankerCount: depositBankerCount, loanBankerCount: loanBankerCount)
        setUpCustomerQueue(count: numberOfCustomers)
        
        while !depositCustomerQueue.isEmpty() || !loanCustomerQueue.isEmpty() {
            serveCustomer(bankerQueue: depositBankerQueue, customerQueue: depositCustomerQueue)
            serveCustomer(bankerQueue: loanBankerQueue, customerQueue: loanCustomerQueue)
        }
        group.wait()
        let endTime = Date()
        let elapsedTime = endTime.timeIntervalSince(startTime)
        let formattedTotalWorkingTime = elapsedTime.formattedDecimal
        Messages.closeBank(customerCount: numberOfCustomers, totalTime: formattedTotalWorkingTime).printMessage()

image
그 결과 의도한 대로 실제 소요시간이 출력되는것을 확인 할 수 있었습니다

serveCustomer(bankerQueue: depositBankerQueue, customerQueue: depositCustomerQueue)
serveCustomer(bankerQueue: loanBankerQueue, customerQueue: loanCustomerQueue)
}
group.wait()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dispatchgroup에서의 enter, leave, enotify, wait에 대해서 각각 설명해주시고 이번 프로젝트에서는 왜 wait을 사용하셔서 구현했는지 이유를 설명해주세요

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • enter() : Dispatchgroup에서 추적하려는 작업이나 코드 블록의 시작을 나타냅니다. 내부 task reference count 를 1 증가시켜 작업이 아직 끝나지 않았음을 알려줍니다.

  • leave() : Dispatchgroup 에서 추적하는 작업의 끝을 나타내며 task reference count 를 1 감소시켜 0이 될 시 작업이 끝났음을 알려줍니다. enterleave 는 서로 짝지어 쓰이면서 주로 디스패치 그룹에 비동기 작업이 포함된 task 를 보낼 때 해당 작업의 끝나는 지점을 알려주기 위해 사용합니다.

  • notify(queue:) :Dispatchgroup 에 추가된 모든 작업이 완료될 때 실행될 클로저를 예약할 때 사용됩니다. 또한 실행될 클로저가 실행되는 큐를 지정할 수 있습니다.

  • wait() : Dispatchgroup 이 추적하는 모든 작업이 완료때까지 현재 스레드를 block 시킬 수 있습니다. 저희는 해당 메서드를 통해 은행원의 모든 비동기 업무가 종료되는 시점까지 코드를 진행하지 않게 만들었습니다.

}

private func processTask(banker: Banker, customer: Customer, bankerQueue: Queue<Banker>) {
DispatchQueue.global().async(group: group) { [self] in
Copy link

@LeeZion94 LeeZion94 Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. DispatchQueue란 무엇인가요?
  2. global() 이란 어떤 키워드이고 어떨 때 사용되나요?
  3. sync와 async의 차이점은 무엇인가요?
  4. 여기서의 group은 왜 사용해주신 건가요?
  5. CaptureList를 사용해주셨네요? 사용해주신 CaptureList는 무엇이고 왜 사용해주셨나요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. DispatchQueue:
    DispatchQueue는 Grand Central Dispatch(GCD)에서 제공하는 기능 중 하나로, 비동기적으로 작업을 실행하는 데 사용됩니다.
    ◦ 큐(Queue)는 작업을 순서대로 처리하거나, 동시에 여러 작업을 실행할 수 있도록 도와줍니다.

  2. global():
    global()은 DispatchQueue의 static 메서드로, 글로벌(dispatch global) 큐를 반환합니다.
    ◦ 글로벌 큐는 시스템 전체에서 사용할 수 있는 큐로, 다양한 QoS(Quality of Service) 레벨을 제공하여 작업을 효과적으로 스케줄링할 수 있습니다.

  3. sync와 async의 차이:
    sync: 현재 스레드에서 작업을 동기적으로 실행합니다. 해당 작업이 완료될 때까지 대기하며, 다음 코드는 해당 작업이 끝날 때까지 실행되지 않습니다.
    async: 작업을 비동기적으로 실행합니다. 현재 스레드에서 대기하지 않고, 작업을 큐에 추가한 후 즉시 다음 코드를 실행합니다.

  4. group 사용 이유:
    DispatchGroup은 여러 비동기 작업을 그룹화하고, 해당 그룹 내에서 모든 작업이 완료될 때까지 대기하도록 하는 데 사용됩니다.
    ◦ 여러 큐에서 실행되는 작업들을 조율하여, 모든 작업이 완료될 때까지 기다릴 수 있게 해줍니다.
    ◦ deposit 큐와 loan 큐가 끝나는 시점이 다르기 때문에 둘이 모두 끝나는 시점에 group.wait()시킨 후 업무 마감 정보를 출력하기 위해서입니다.

  5. CaptureList:
    ◦ CaptureList는 클로저 내에서 외부 변수를 참조할 때 발생하는 strong reference cycle(강한 참조 순환)을 방지하는 데 사용됩니다.
    ◦ [weak self]나 [unowned self]와 같이 사용하여 클로저가 self를 강한 참조하지 않도록 하고, 메모리 누수를 방지합니다.
    ◦ 코드에서 [self]를 사용하여 클로저 내에서 self를 캡처하면서도 strong reference cycle을 방지할 수 있습니다.

}

private func serveCustomer(bankerQueue: Queue<Banker>, customerQueue: Queue<Customer>) {
if bankerQueue.isEmpty() == false {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Queue에서 Dequeue하는 로직이 조금 이해하기 힘든 것 같아요. 사용하는 Queue의 갯수를 줄인다면 조금 더 이해하기 쉬운 로직으로 개선이 가능할 것 같습니다.

Copy link

@yoruck2 yoruck2 Feb 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여러가지 시도를 해보다가 결국 은행원 큐를 없애고 손님 큐 하나로 만들어 보았습니다.
Banker는 업무를 진행하는 메서드만 소유하고 있고 손님의 업무 타입에 따라 비동기 메서드 processCustomer를 실행합니다.
은행원의 수는 DateSemaphore를 활용해 해당 메서드에 접근할 수 있는 스레드를 제한 했습니다.
결과적으로 손님을 데려다 일하는 은행원을 스레드로 보는 관점으로 수정해 좀 더 간결한 코드가된 것 같습니다!

class Bank {
    ...
    private let depositSemaphore: DispatchSemaphore
    private let loanSemaphore: DispatchSemaphore
    ...
    init(depositBankerCount: Int, loanBankerCount: Int) {
        self.depositSemaphore = DispatchSemaphore(value: depositBankerCount)
        self.loanSemaphore = DispatchSemaphore(value: loanBankerCount)
    }
    
    func open() {
	...
        while let customer = customerQueue.dequeue() {
            switch customer.taskType {
            case .deposit:
                serveCustomer(semaphore: depositSemaphore, customer: customer)
            case .loan:
                serveCustomer(semaphore: loanSemaphore, customer: customer)
            }
        }
	...
    }
    
    ...
    
    private func serveCustomer(semaphore: DispatchSemaphore, customer: Customer) {
        DispatchQueue.global().async(group: group) {
            semaphore.wait()
            self.banker.processCustomer(customer)
            semaphore.signal()
        }
    }
}


init?(number: Int) {
self.number = number
guard let randomTaskType = TaskType.allCases.randomElement() else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Customer의 생성이 실패하는 경우가 존재해야하나요?
위의 코드는 allCases를 사용하시려다보니 Optional Binding으로 인해 발생한 불필요한 init?으로 보여요 ㅎㅎ
다르게 해결해 볼 수 있지 않을까요?

Copy link
Author

@ujhong7 ujhong7 Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. TaskType에 random메서드를 만들어 allCases를 사용하지않고 loan과 deposit 두 경우에 대해서만 랜덤하게 선택할 수 있도록 했습니다.
final class Customer {
    let number: Int
    let taskType: TaskType
    
    init(number: Int) {
        self.number = number
        self.taskType = TaskType.random()
    }
}

enum TaskType: CaseIterable, CustomStringConvertible {
    case loan
    case deposit
}

enum TaskType: CaseIterable, CustomStringConvertible {
    case loan
    case deposit
}

extension TaskType {
    var description: String {
        switch self {
        case .loan:
            return "대출"
        case .deposit:
            return "예금"
        }
    
    }
    
    static func random() -> TaskType {
            return Bool.random() ? .loan : .deposit
        }
    
    var taskTime: Double {
        switch self {
        case .loan:
            return 1.1
        case .deposit:
            return 0.7
        }
    }

}

or

  1. fatalError를 사용해서 예외처리를 했습니다.. 그런데 fatalError는 프로그램을 중단시키기 때문에 애플리케이션이 예기치 않은 상황에 직면할 때 적절한 조치를 취할 수 없게 되기때문에 별로 좋지 않은 방법 같은데 어떻게 생각하시나요?..
    init(number: Int) {
            self.number = number

            if let randomTaskType = TaskType.allCases.randomElement() {
                self.taskType = randomTaskType
            } else {
                // allCases가 비어있거나 nil을 반환할 경우에 대한 예외 처리
                fatalError("TaskType.allCases is empty or returned nil")
            }
        }

//
// Created by dopamint on 2/1/24.
//
enum TaskType: CaseIterable, CustomStringConvertible {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CustomStringConvertible 은 어떤 역할을 하고 있나요?
그리고 왜 사용되어야 하나요? description을 그냥 추가해주기만 하면 안되는 걸까요?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CustomStringConvertible 은 특정 type을 textual한 표현으로 커스텀해주는 protocol입니다.

struct Sample: CustomStringConvertible {
    var description: String {
        return "description"
    }
}
let a = Sample()

print(a)        // description
print(a.description)        //description

예를 들어 위처럼 CustomStringConvertible 채택 시 인스턴스를 출력하면 description 의 string이 출력되는것을 확인 할 수 있습니다.
저희의 코드에서는 굳이 CustomStringConvertible 을 채택하지 않아도 상관 없을 것 같습니다!


private func setUpCustomerQueue(count: Int) {
for number in 1...count {
guard let customer = Customer(number: number) else { return }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Customer의 생성이 실패하면 Queue의 설정자체가 return 되는 로직으로 보여요. 의도한 부분이 맞으실까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Customer 클래스 생성자가 옵셔널로 만들어져서 바인딩을 위해서 작성했습니다.. 생성자 수정해 바인딩이 필요없어져 삭제했습니다.

@yoruck2
Copy link

yoruck2 commented Feb 10, 2024

질문 해주신 부분 정리해 봤습니다~

  1. 요구사항
    저희도 출력 시 불필요한 개행이 생기는것이 의아했는데 xcode 터미널 상의 문제라고 판단했습니다.. 뭔가 출력된 후 일정 시간이 지나면 자동개행이 되더라구요..!
    print의 terminator 속성도 수정해보고 방법을 찾아봤는데 해결방법은 찾지 못했습니다..🥲

  2. 개행
    의견을 반영 해 조금 수정 해 보았습니다! 그런데 리펙토링 이후 리턴문과 바인딩이 많이 사라져 큰 차이는 없게 되었네요..😅

  3. 사용하신 Queue의 갯수
    위 코멘트에 남겨놓았습니다! 확실히 코드가 간결해질 수 있었어요!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants