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
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0f5b78c
feat: Queue 타입 구현을 위한 Linked-list 타입 구현 후 분리
Jan 31, 2024
d667351
feat: Bank타입 생성, open, processCustomer 기능 추가
yoruck2 Jan 31, 2024
2e54e5f
feat: Banker타입 생성
yoruck2 Jan 31, 2024
bba8e7d
feat: Customer타입 생성
yoruck2 Jan 31, 2024
ec07b48
feat: operation 기능 구현
yoruck2 Jan 31, 2024
90c5eab
.
yoruck2 Jan 31, 2024
beb38a9
chore: 오타 수정
yoruck2 Jan 31, 2024
a1d75de
feat: refactor: 접근제어자와 final 키워드 추가
yoruck2 Jan 31, 2024
0666b40
feat: "입력 : " 출력 추가
yoruck2 Jan 31, 2024
ae792bc
refactor: final 키워드 추가
yoruck2 Jan 31, 2024
002d6e8
feat: TaskType 추가
yoruck2 Feb 5, 2024
f40c08a
refactor: Banker 내 불필요한 속성들 삭제
yoruck2 Feb 5, 2024
ce8a675
refactor: Banker 내 불필요한 속성들 삭제
yoruck2 Feb 5, 2024
939cf90
refactor: taskTime Float -> Double 타입 변경
yoruck2 Feb 5, 2024
fc55a43
refactor: Customer 타입 실패가능생성자로 수정
yoruck2 Feb 5, 2024
7395e99
chore: 불필요한 import 삭제 및 개행 수정
yoruck2 Feb 5, 2024
c35f050
chore: 불필요한 import 삭제
yoruck2 Feb 5, 2024
8d79b57
refactor: prtint문 enum을 통한 메세지 출력으로 변경
yoruck2 Feb 5, 2024
285991c
Revert "chore: 불필요한 import 삭제"
yoruck2 Feb 5, 2024
7a7117c
feat: Messages 타입 추가
yoruck2 Feb 5, 2024
09e4440
refactor: print문 enum을 통한 메세지 출력으로 변경
yoruck2 Feb 5, 2024
2e9d055
chore: 오타수정
yoruck2 Feb 6, 2024
f7a9624
chore: 개행 정리
yoruck2 Feb 6, 2024
6fea095
fix: removeFirst 오류수정
yoruck2 Feb 6, 2024
9d3ae26
fix: 은행원의 수 수정
yoruck2 Feb 6, 2024
7310cda
feat: processTask 추가, serveCustomer 추가
yoruck2 Feb 6, 2024
a14c168
refactor: 접근제어자 설정
yoruck2 Feb 6, 2024
d4a1283
refactor: 원활한 비동기 작업을 위한 dispatchgroup 설정 및 로직 수정
yoruck2 Feb 6, 2024
2bef8df
refactor: Banker타입 간소화
yoruck2 Feb 10, 2024
0953ff9
refator: TakeType의 불필요한 채택 프로토콜 삭제
yoruck2 Feb 10, 2024
c013f5f
chore: 개행 정리 및 @discardableResult 삭제
yoruck2 Feb 10, 2024
b53e24f
refactor: taskType 지정방식 변경에 따른 initializer 수정
yoruck2 Feb 10, 2024
3814d57
refactor: 4개의 큐 -> 1개의 손님큐 , DispatchSemaphore 활용
yoruck2 Feb 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@
objects = {

/* Begin PBXBuildFile section */
1D9C82D72B6A2B1C0047245E /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9C82D62B6A2B1C0047245E /* Node.swift */; };
1D9C82D92B6A2B270047245E /* LinkedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9C82D82B6A2B270047245E /* LinkedList.swift */; };
1D9C82DB2B6A2B2F0047245E /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9C82DA2B6A2B2F0047245E /* Queue.swift */; };
847943632B6A2D3E00F85F0C /* Bank.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847943622B6A2D3E00F85F0C /* Bank.swift */; };
847943652B6A2D4800F85F0C /* Banker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847943642B6A2D4800F85F0C /* Banker.swift */; };
847943672B6A2D5300F85F0C /* Customer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847943662B6A2D5300F85F0C /* Customer.swift */; };
847943692B6A342200F85F0C /* Double+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847943682B6A342200F85F0C /* Double+.swift */; };
849EDAD02B6BB1BA00096E84 /* Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849EDACF2B6BB1BA00096E84 /* Messages.swift */; };
849EDAD22B6BBB2500096E84 /* TaskType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849EDAD12B6BBB2500096E84 /* TaskType.swift */; };
C7694E7A259C3EC00053667F /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7694E79259C3EC00053667F /* main.swift */; };
C7D65D1B259C8190005510E0 /* BankManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D65D1A259C8190005510E0 /* BankManager.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand All @@ -24,9 +32,17 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
1D9C82D62B6A2B1C0047245E /* Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = "<group>"; };
1D9C82D82B6A2B270047245E /* LinkedList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedList.swift; sourceTree = "<group>"; };
1D9C82DA2B6A2B2F0047245E /* Queue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = "<group>"; };
847943622B6A2D3E00F85F0C /* Bank.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bank.swift; sourceTree = "<group>"; };
847943642B6A2D4800F85F0C /* Banker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Banker.swift; sourceTree = "<group>"; };
847943662B6A2D5300F85F0C /* Customer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Customer.swift; sourceTree = "<group>"; };
847943682B6A342200F85F0C /* Double+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+.swift"; sourceTree = "<group>"; };
849EDACF2B6BB1BA00096E84 /* Messages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Messages.swift; sourceTree = "<group>"; };
849EDAD12B6BBB2500096E84 /* TaskType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskType.swift; sourceTree = "<group>"; };
C7694E76259C3EC00053667F /* BankManagerConsoleApp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BankManagerConsoleApp; sourceTree = BUILT_PRODUCTS_DIR; };
C7694E79259C3EC00053667F /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
C7D65D1A259C8190005510E0 /* BankManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BankManager.swift; path = ../../BankManager.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -40,6 +56,27 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
1D9C82D42B6A2AD00047245E /* Queue */ = {
isa = PBXGroup;
children = (
1D9C82D62B6A2B1C0047245E /* Node.swift */,
1D9C82D82B6A2B270047245E /* LinkedList.swift */,
1D9C82DA2B6A2B2F0047245E /* Queue.swift */,
);
path = Queue;
sourceTree = "<group>";
};
1D9C82D52B6A2AF00047245E /* Bank */ = {
isa = PBXGroup;
children = (
849EDAD12B6BBB2500096E84 /* TaskType.swift */,
847943622B6A2D3E00F85F0C /* Bank.swift */,
847943642B6A2D4800F85F0C /* Banker.swift */,
847943662B6A2D5300F85F0C /* Customer.swift */,
);
path = Bank;
sourceTree = "<group>";
};
C7694E6D259C3EC00053667F = {
isa = PBXGroup;
children = (
Expand All @@ -59,8 +96,11 @@
C7694E78259C3EC00053667F /* BankManagerConsoleApp */ = {
isa = PBXGroup;
children = (
C7D65D1A259C8190005510E0 /* BankManager.swift */,
849EDACF2B6BB1BA00096E84 /* Messages.swift */,
847943682B6A342200F85F0C /* Double+.swift */,
C7694E79259C3EC00053667F /* main.swift */,
1D9C82D42B6A2AD00047245E /* Queue */,
1D9C82D52B6A2AF00047245E /* Bank */,
);
path = BankManagerConsoleApp;
sourceTree = "<group>";
Expand Down Expand Up @@ -122,8 +162,16 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1D9C82D92B6A2B270047245E /* LinkedList.swift in Sources */,
849EDAD02B6BB1BA00096E84 /* Messages.swift in Sources */,
849EDAD22B6BBB2500096E84 /* TaskType.swift in Sources */,
847943672B6A2D5300F85F0C /* Customer.swift in Sources */,
847943692B6A342200F85F0C /* Double+.swift in Sources */,
C7694E7A259C3EC00053667F /* main.swift in Sources */,
C7D65D1B259C8190005510E0 /* BankManager.swift in Sources */,
847943652B6A2D4800F85F0C /* Banker.swift in Sources */,
847943632B6A2D3E00F85F0C /* Bank.swift in Sources */,
1D9C82D72B6A2B1C0047245E /* Node.swift in Sources */,
1D9C82DB2B6A2B2F0047245E /* Queue.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
62 changes: 62 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Bank/Bank.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// Bank.swift
// BankManagerConsoleApp
//
// Created by dopamint on 1/31/24.
//

import Foundation

final class Bank {

private var banker = Banker()
private var customerQueue = Queue<Customer>()
private let depositSemaphore: DispatchSemaphore
private let loanSemaphore: DispatchSemaphore
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를 선언했습니다.


init(depositBankerCount: Int, loanBankerCount: Int) {
self.depositSemaphore = DispatchSemaphore(value: depositBankerCount)
self.loanSemaphore = DispatchSemaphore(value: loanBankerCount)
}

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

print("고객 수: \(numberOfCustomers)")
setUpCustomerQueue(count: numberOfCustomers)

while let customer = customerQueue.dequeue() {
switch customer.taskType {
case .deposit:
serveCustomer(semaphore: depositSemaphore, customer: customer)
case .loan:
serveCustomer(semaphore: loanSemaphore, customer: customer)
}
}
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 시킬 수 있습니다. 저희는 해당 메서드를 통해 은행원의 모든 비동기 업무가 종료되는 시점까지 코드를 진행하지 않게 만들었습니다.

let endTime = Date()
let elapsedTime = endTime.timeIntervalSince(startTime).formattedDecimal
Messages.closeBank(customerCount: numberOfCustomers, totalTime: elapsedTime).printMessage()
}

private func serveCustomer(semaphore: DispatchSemaphore, customer: Customer) {
DispatchQueue.global().async(group: group) {
semaphore.wait()
self.banker.processCustomer(customer)
semaphore.signal()
}
}

private func setUpCustomerQueue(count: Int) {
for number in 1...count {
let customer = Customer(number: number)
customerQueue.enqueue(customer)
}
}
}



17 changes: 17 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Bank/Banker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Banker.swift
// BankManagerConsoleApp
//
// Created by dopamint on 1/31/24.
//

import Foundation

final class Banker {

func processCustomer(_ customer: Customer) {
Messages.taskStart(number: customer.number, taskType: customer.taskType.description).printMessage()
Thread.sleep(forTimeInterval: customer.taskType.taskTime)
Messages.taskDone(number: customer.number, taskType: customer.taskType.description).printMessage()
}
}
16 changes: 16 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Bank/Customer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Customer.swift
// BankManagerConsoleApp
//
// Created by dopamint on 1/31/24.
//
final class Customer {
let number: Int
let taskType: TaskType

init(number: Int) {
self.number = number
self.taskType = TaskType.random()
}
}

34 changes: 34 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Bank/TaskType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// WorkType.swift
// BankManagerConsoleApp
//
// Created by dopamint on 2/1/24.
//
enum TaskType {
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
}
}

static func random() -> TaskType {
return Bool.random() ? .loan : .deposit
}
}
12 changes: 12 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Double+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// String+.swift
// BankManagerConsoleApp
//
// Created by dopamint on 1/31/24.
//

extension Double {
var formattedDecimal: String {
return String(format: "%.2f", self)
}
}
41 changes: 41 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Messages.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Messages.swift
// BankManagerConsoleApp
//
// Created by dopamint on 2/1/24.
//

enum Messages {
case menu
case exit
case openBank
case taskStart(number: Int, taskType: String)
case taskDone(number: Int, taskType: String)
case closeBank(customerCount: Int, totalTime: String)
case menuError
case numberError
}

extension Messages {
func printMessage() {
switch self {
case .menu:
print("1 : 은행계정\n2 : 종료")
print("입력 : ", terminator: "")
case .exit:
print("프로그램을 종료합니다.")
case .openBank:
print("은행 문이 열렸습니다.")
case .taskStart(let number, let taskType):
print("\(number) 고객 \(taskType)업무 시작")
case .taskDone(let number, let taskType):
print("\(number) 고객 \(taskType)업무 종료")
case .closeBank(let customerCount, let totalTime):
print("업무가 마감되었습니다. 오늘 업무를 처리한 고객은 총 \(customerCount)명 이며, 총 업무시간은 \(totalTime)초 입니다.")
case .menuError:
print("올바른 메뉴를 선택해주세요.")
case .numberError:
print("숫자를 입력해주세요.")
}
}
}
57 changes: 57 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Queue/LinkedList.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// LinkedList.swift
// BankManagerConsoleApp
//
// Created by yujaehong on 1/31/24.
//

final class LinkedList<T> {
var front: Node<T>?
var rear: Node<T>?

init(front: Node<T>? = nil, rear: Node<T>? = nil) {
self.front = front
self.rear = rear
}

var first: T? {
return front?.value
}

var isEmpty: Bool {
return front == nil
}
}

extension LinkedList {
func append(_ value: T) {
let newNode = Node(value)

if isEmpty {
front = newNode
rear = front
} else {
rear?.next = newNode
rear = newNode
}
}

func removeFirst() -> T? {
if front === rear {
let result = front
front = nil
rear = nil
return result?.value
}
guard let currentFront = front else { return nil }
front = front?.next
return currentFront.value
}

func removeAll() {
front = nil
rear = nil
}
}


15 changes: 15 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Queue/Node.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// Node.swift
// BankManagerConsoleApp
//
// Created by yujaehong on 1/31/24.
//
final class Node<T> {
var value: T
var next: Node?

init(_ value: T, _ next: Node? = nil) {
self.value = value
self.next = next
}
}
37 changes: 37 additions & 0 deletions BankManagerConsoleApp/BankManagerConsoleApp/Queue/Queue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Queue.swift
// BankManagerConsoleApp
//
// Created by yujaehong on 1/31/24.
//

final class Queue<T> {
private var linkedList: LinkedList<T>

init(linkedList: LinkedList<T> = LinkedList()) {
self.linkedList = linkedList
}
}

extension Queue {
func enqueue(_ value: T) {
linkedList.append(value)
}


func dequeue() -> T? {
return linkedList.removeFirst()
}

func clear() {
linkedList.removeAll()
}

func peek() -> T? {
return linkedList.first
}

func isEmpty() -> Bool {
return linkedList.isEmpty
}
}
Loading