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

Week3 [Step2] som #100

Merged
merged 23 commits into from
May 7, 2022
Merged

Week3 [Step2] som #100

merged 23 commits into from
May 7, 2022

Conversation

jsa0224
Copy link

@jsa0224 jsa0224 commented May 4, 2022

@Jager-yoo
Step2를 하면서 정말 머리가 많이 아팠습니다..

어제 예거에게 디엠 드렸던 것처럼, 다른 파일에 있는 객체를 인식하지 못 해서 일단 모든 파일들의 코드를 플레이그라운드에 복붙하면서 진행했었는데, 그럼에도 한계가 오더라고요. 잠시 파라미터를 잊었던 거 같아요. 그 문제는 해결해서 PR을 보내봅니다!

어려웠던 점

저는 원래 이렇게 생각을 했어요.
사람이 주문을 한다. -> 바리스타가 주문을 받는다. -> 바리스타가 커피를 만든다-> 픽업테이블로 손님을 호출한다.
보통 우리가 카페에 가서 커피를 주문하고 받을 때의 방식이죠.
그래서 missKim.buyCoffee() -> barista.order() -> barista.makeCoffee() 이 순으로 main 파일에 호출할려니... coffee프로퍼티는 잘 들어오지만 cashPerson.name 이런 프로퍼티들이 안 들어오기 시작하더라고요.

그 때 인스턴스가 상호작용한다.. 그 의미가 뭔지 알겠더라고요. 순서도는 아니지만 메모장에 써둔 설계도(?)가 무의미해지는 순간이었어요😵‍💫

해보고 싶은 부분

보통 주문을 넣고 바로 커피가 완성되지는 않잖아요:)
만드는 시간이 필요할 거 같은데 주문하고 5분 뒤 라던가, 5분 정도 만드는 시간을 주고 싶어요.
지금은 run을 하면 모든 메서드들이 바로 호출되지만 호출될 때, 시간 차를 만드는 방법은 없을까요?

@Jager-yoo Jager-yoo self-requested a review May 4, 2022 08:23
@jsa0224 jsa0224 changed the base branch from main to ss_4_somsom May 4, 2022 08:25
Comment on lines 32 to 38
func isPayable(_ amount: Int) -> Bool {
if walletInCash > amount {
return false
} else {
return true
}
}
Copy link
Member

Choose a reason for hiding this comment

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

이 부분은 훨~씬 간단하게 코드를 고쳐볼 수 있습니다. ㅎㅎㅎ
단 한 줄로 줄일 수 있어요.

수식 부분의 결과가 true, 아니면 false 로 나오기 때문에, 그 수식 자체를 return 오른쪽에 붙여버리는 거에요.
그러면 그 판단 결과가 리턴값으로 바로 나오게 됩니다! 👍🏻

Suggested change
func isPayable(_ amount: Int) -> Bool {
if walletInCash > amount {
return false
} else {
return true
}
}
func isPayable(_ amount: Int) -> Bool {
return walletInCash >= amount
}

Copy link
Author

Choose a reason for hiding this comment

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

Bool 타입으로 반환 받으려고 하다 보니까 truefalse를 반환값으로 꼭 써야 하는 줄 알았어요.
'크거나 같은'의 의미를 갖는 비교 연산자를 사용하면 되겠군요😮

Comment on lines 24 to 30
func spandCash (amount: Int) {
if isPayable(amount) == false {
print("잔돈이 부족합니다.")
} else {
walletInCash -= amount
}
}
Copy link
Member

Choose a reason for hiding this comment

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

요 메서드는 일단 삭제할게요!
다른 메서드에서 간단하게 처리할 수 있을 것 같아요.

Comment on lines 15 to 16
var walletInCash: Int
init(name: String, age: Int, habit: String? = nil, personalMBTI: String? = nil, walletInCash: Int) {
init(name: String, age: Int, habit: String? = nil, MBTI: String? = nil, walletInCash: Int) {
Copy link
Member

Choose a reason for hiding this comment

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

프로퍼티들 ~ 이니셜라이저 ~ 메서드들

이렇게 각각의 종류들 사이에서는 공백 한 줄 넣어주시면 좋겠습니다!
가독성을 위해서요 ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

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

앗! 공백 넣는 걸 계속 깜빡하는 거 같아요ㅠㅠ 수정했습니다!

Comment on lines 40 to 52
func buyCoffee(_ coffee: Coffee, at coffeeShop: CoffeeShop?) {
if let cafe = coffeeShop {
if let price = cafe.menu[coffee] {
if walletInCash > price {
walletInCash -= price
print("\(coffee)를 구매하였습니다.")
coffeeShop?.order(coffee, by: self)
} else {
print("잔액이 \(price - walletInCash)원 만큼 부족합니다.")
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

  • 이 메서드는 내부에서 if-let 옵셔널 바인딩이 2번이나 중첩돼서 쓰이고 있고
    심지어 그 안에 또 if문이 들어가있어요.
    요약하자면, if문을 3번 중첩했다고 볼 수 있어요. 🥲 중첩이 하나만 깊어져도 가독성이 떨어지기 때문에
    이런 경우엔 if-let 이 아니라 → guard-let 옵셔널 바인딩을 활용해보면 좋겠어요.

  • 그리고 이 메서드에 CoffeeShop 타입을 파라미터로 받잖아요?
    그렇기 때문에, 주문하기 전에 가게가 열었는지, 닫았는지를 판단하는 일과
    주문하고 싶은 메뉴가 가게에 있는지, 없는지 판단하는 일도 소비자의 몫으로 돌려보죠. 😄
    (현실에서도, 가게 문 닫았는지는 지도로 검색해서 알아내고, 메뉴가 있는지도 미리 확인해볼 수 있으니까요.)

  • 이때, CoffeeShop 파라미터를 굳이 옵셔널로 받을 필요도 없어 보여요.
    커피를 주문한다면, 반드시 카페는 있어야 겠죠. 옵셔널로 받지 않는 게 더 깔끔하고 당연해보입니다!

func buyCoffee(_ coffee: Coffee, at coffeeShop: CoffeeShop) {
    guard coffeeShop.openingHours.isEmpty == false else {
        print("가게가 아직 오픈하지 않았습니다.")
        return
    }
    
    guard let price = coffeeShop.menu[coffee] else {
        print("주문한 커피가 메뉴에 없습니다.")
        return
    }
    
    if isPayable(price) {
        walletInCash -= price // 금액 지불이 가능(true)할 때, 돈을 차감합니다.
        coffeeShop.order(coffee, price: price, customerName: name) // 커피샵의 order 메서드에 돈과 소비자의 이름을 파라미터로 보내버립니다.
    } else {
        print("\(coffee) 를 주문하기엔 잔액이 \(price - walletInCash)원 부족합니다.")
    }
}

Copy link
Author

Choose a reason for hiding this comment

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

  • guard문을 스위프트 언어 배우면서 처음 접하는 거라 아직 if문이 더 익숙한가봐요...
    guard문이 확실히 코드가 간결해지긴 해요👍
    그러면 스위프트 언어에서는 if문을 코드의 간결성 때문에 거의 안 쓰나요? 궁금해졌습니다!

  • openingHours를 설정해둔 이유는 실제 카페 문 앞에 [open]/[close] 팻말을 걸어두잖아요.
    그게 참 매력적이고, 꼭 넣어보고 싶은 설정이라 넣게 된 거예요!
    즉, 팻말을 위해 만든 프로퍼티예요☺️
    그 팻말 관리를 고객이 아닌 카페 측에서 하니까 Coffee 소스 파일에 넣어둔 것인데, 지금 보니 기존의 order()메서드에 넣는 게 어색해보이네요 ㅎㅎ...
    이 프로퍼티는 제가 따로 빼서 다른 메서드로 활용해보겠습니다💪

  • 저도 CoffeeShop 파라미터를 굳이 옵셔널로 가져오고 싶지 않았는데, 코드 수정을 거듭하면서 옆에 뜨는 오류문을 확인하며 만들다가 옵셔널로 가져오게 된 거 같아요.
    그때 워낙 멘붕인 상태에서(🤣🤣🤣🤣) 프로그래밍 했기 때문에 정확한 원인은 기억이 나질 않네요...🥲

Copy link
Member

@Jager-yoo Jager-yoo May 6, 2022

Choose a reason for hiding this comment

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

ㅎㅎㅎ 답변 감사합니다.

guard문하고 if문 중에 하나만 쓸 필요는 없어요!
저는 솔직히 반-반 섞어 쓰는 거 같습니다.

둘의 늬앙스 차이가 좀 있기 때문에... 나중 가면 자연스럽게 선택하실 수 있게 될 거에요! 😄
일단 이번 코멘트에서 제가 if문 3중 중첩 을 제거하기 위해 guard문을 2번 사용했는데요,

살펴 보시면, 주문(order)이라는 행위를 하기 까지, 예외 케이스를 먼저 걸러내주는 방식으로 guard문을 사용했어요.
guard문은 early-exit(빠른 종료) 목적으로 쓰이기 좋다는 걸 느껴보셨으면 합니다!

Copy link
Author

Choose a reason for hiding this comment

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

감사합니다:) 감이 오고 있는 거 같아요 ㅎㅎ!

Comment on lines +15 to +21
var pickUpTable: Coffee? {
didSet {
if let coffee = pickUpTable {
print("\(customerName)님, \(coffee)가 완성되어 픽업 테이블에 올라왔습니다.")
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

프로퍼티 옵저버 정말 잘 만드셨어요! 💯

Comment on lines 23 to 24
var customerName: String = ""
init(owner: String, openingHours: String, menu: [Coffee: Int] = [:], barista: Person?) {
Copy link
Member

Choose a reason for hiding this comment

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

여기서도 마찬가지로, 타입 내부의 프로퍼티들 ~ 이니셜라이저 ~ 메서드들은
각각 공백 한 줄을 경계로 분리해주시면 더 좋겠습니다!

Comment on lines 31 to 41
func showMenu() -> String {
var menuDetail = "\(owner) 카페 메뉴판\n"
if menu.isEmpty {
return "현재 메뉴 준비 중입니다."
} else {
for (coffee, price) in menu {
menuDetail += "|| \(coffee) - \(price)원 ||\n"
}
}
return menuDetail
}
Copy link
Member

Choose a reason for hiding this comment

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

이 메서드는 진짜 재미나게 잘 만드셨는데, 실제로 호출되는 부분이 없어서 좀 아쉽더라구요!
편하게 호출할 수 있도록, 리턴값을 없애고 단순 print 출력만 해주는 메서드로 고쳐서
어떻게든 쓸 수 있게 만들어보죠. ㅎㅎㅎ

func showMenu() {
    if menu.isEmpty {
        print("현재 메뉴 준비 중입니다.")
    } else {
        print("☕️☕️☕️ \(owner) 카페 메뉴판 ☕️☕️☕️")
        for (coffee, price) in menu {
            print("|| \(coffee) - \(price)원 ||")
        }
        print("----------------------------") // 메뉴 출력 끝나고 경계선 그어주기!
    }
}

Copy link
Author

Choose a reason for hiding this comment

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

☕️ 커피 이모지까지 있으니 정말 카페 메뉴판이 된 거 같아요!
생각해보니 막상 만들어 놓고 호출을 안 했네요🤣🤣🤣

Comment on lines 43 to 60
func order(_ coffee: Coffee, by customer: Person) -> Coffee? {
if openingHours.isEmpty {
print("아직 오픈 시간이 아닙니다.")
} else {
if let price = menu[coffee] {
if customer.isPayable(price) {
print("\(customer.name)님의 \(coffee)가 주문 되었습니다.")
self.customerName = customer.name
coffeeShopProfit += price
makeCoffee(coffee)
return coffee
} else {
print("잔액이 \(price - customer.walletInCash)만큼 부족합니다.")
}
}
}
return coffee
}
Copy link
Member

Choose a reason for hiding this comment

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

  • 먼저, 메서드의 리턴값을 없애도 괜찮을 것 같아요.
    왜냐면, 내부에서 커피를 만들면, pickUpTable 에 바로 올려버리기 때문에, 리턴값이 쓰이지 않는다는 노란색 ⚠️ 경고도 뜨더라구요!

  • 그리고 메서드 파라미터들 구성을 바꿨어요.
    굳이 Person 타입을 통째로 받지 않고, 손님이 건내는 돈과 손님의 이름을 받으면, price 를 여기서 또 옵셔널 바인딩할 필요가 사라져요!
    그리고 블루보틀 같은 카페에서는, 주문할 때 카드 건내면서, 동시에 손님의 이름을 얘기해줘야 하잖아요?
    그런 컨셉을 생각해봤어요! 😄

  • 오픈 시간이나, isPayable 같은 로직은, 여기선 필요 없어요.
    왜냐면 이미 소비자 측에서 guard문 통해서 다 처리하고 넘어올 거니까, 여기서 괜히 또 해줄 필요가 없어졌어요.

func order(_ coffee: Coffee, price: Int, customerName: String) {
    showMenu() // 이거 여기에 끼워넣었어요. ㅎㅎ
    print("\(customerName) 님의 \(coffee) 주문이 들어왔습니다.")
    self.customerName = customerName
    coffeeShopProfit += price // 파라미터로 들어온 돈을, 가게의 매출에 더해줘요.
    makeCoffee(menu: coffee) // 진짜로 커피를 만드는 메서드 호출
}

Copy link
Author

Choose a reason for hiding this comment

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

코드가 훨씬 간단해졌네요😮
파라미터로 진작 가져올 걸 그랬어요🥲

Comment on lines 62 to 64
func makeCoffee(_ coffee: Coffee) {
print("\(coffee)를 만드는 중입니다.")
pickUpTable = coffee
}
Copy link
Member

Choose a reason for hiding this comment

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

  • 메서드 이름부터 보면, 호출될 때 makeCoffee(coffee) → 이런 식으로, "커피" 단어가 2번 반복되기 때문에, 뒤에 커피가 뭘 의미하는 건지 부정확해보였어요.
    그래서 메서드 파라미터를 생략하지 않고, Argument Labelsmenu를 끼워넣었어요!

  • 그리고 솜이 요청했던... 시간이 조금 걸리게 하는 방법 ⏲️
    간단하게 구현해봤어요.
    참고로 여기서 사용한 Thread.sleep(forTimeInterval:) → 이 메서드는 아주 ☠️ 위험한 ☠️ 메서드입니다.
    작업이 이루어지는 스레드를 잠들게 해서, 강제로 1초 멈춰버리는 겁니다.
    이번 스텝은 화면(UI)도 없고, 손님도 1명이라 사용한 건데, 만약 화면이 있는 앱에서 Thread.sleep 걸어버리면, 슬립되는 동안에는 아예 화면이 멈춰버려서, 유저와 어떠한 interaction 도 할 수가 없게 됩니다. 😅

func makeCoffee(menu coffee: Coffee) {
    // 여기에서 시간 4초 정도 부여하고, 1초 마다 과정을 출력해볼게요!
    Thread.sleep(forTimeInterval: 1)
    print("에스프레소 내리는 중...")
    Thread.sleep(forTimeInterval: 1)
    print("재료 섞는 중...")
    Thread.sleep(forTimeInterval: 1)
    print("포장 중...")
    Thread.sleep(forTimeInterval: 1)
    pickUpTable = coffee // 완성!
}
  • 그러면 안전하게 시간 차를 부여하려면 어떻게 해야 할까요?
    개념을 더 어렵게 만들면 이렇게도 될 수 있습니다.
    바리스타 1명이 커피를 내리는 데 5초가 걸리고, 한 번에 한 커피 밖에 작업을 못 한다고 했을 때
    바리스타가 2~3명으로 늘어나고, 손님이 10명이 몰려와서 막 주문을 집어넣는 다고 생각해보는 거죠...
    이때 필요한 개념이 멀티스레딩과 GCD 인데요...!
    이건 야곰 부트캠프에서도 중반부 돼서야 다루는 내용이고..
    사실 저도 공부가 더 필요한 부분이라서, 이런 게 있구나 하고만 넘어가시면 좋겠어요! 🙏🏻

Copy link
Author

Choose a reason for hiding this comment

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

앗 정말 참고용으로만 봐야겠네요...!

예전의 컴퓨터는 연산기능 밖에 없었는데, 세상의 모든 것을 컴퓨터 안에 담기 위해 객체지향 프로그램이 만들어졌다고 객체지향 프로그램 파트에서 배웠던 것이 기억나서
실제로 커피를 만드는 시간도 구현할 수 있지 않을까? 궁금해져서 질문을 드렸어요 ㅎㅎ

더 성장해서 앱을 만들게 될 때 쓸 수 있게 되겠네요! 알려주셔서 감사합니다☺️

Comment on lines 16 to 20
var yagombucks = CoffeeShop(owner: "야곰", openingHours: "10:00 ~ 20:00", menu: Coffee.menu, barista: misterLee)


missKim.buyCoffee(Coffee.caramelMacchiato, at: yagombucks)
missKim.walletInCash = 4000
missKim.buyCoffee(Coffee.caramelMacchiato, at: yagombucks)
Copy link
Member

Choose a reason for hiding this comment

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

야곰벅스 인스턴스는 굳이 var 로 만들 필요가 없어요!
항상 상수인 let 을 기본으로 생각하시고, 에러가 뜨거나 변경될 필요가 있는 경우만 var 로 선언해주세요. 😄

그리고 메뉴가 3가지나 있고, 돈이 부족한 경우에 예외 처리까지 잘 구현해놨기 때문에
그런 것들을 테스트 하기 위해, 미스킴의 주문을 아래와 같이 변경해봤어요!

Suggested change
var yagombucks = CoffeeShop(owner: "야곰", openingHours: "10:00 ~ 20:00", menu: Coffee.menu, barista: misterLee)
missKim.buyCoffee(Coffee.caramelMacchiato, at: yagombucks)
missKim.walletInCash = 4000
missKim.buyCoffee(Coffee.caramelMacchiato, at: yagombucks)
let yagombucks = CoffeeShop(owner: "야곰", openingHours: "10:00 ~ 20:00", menu: Coffee.menu, barista: misterLee)
missKim.buyCoffee(.caramelMacchiato, at: yagombucks) // 카라멜 마끼아또 시켜보고
missKim.buyCoffee(.americano, at: yagombucks) // 아메리카노도 시켜보고
missKim.buyCoffee(.caffeLatte, at: yagombucks) // 돈 부족한 경우도 테스트해보고

@jsa0224
Copy link
Author

jsa0224 commented May 7, 2022

@Jager-yoo

조금 아쉬운 상태에서 push를 하게 되었습니다.
실제 카페처럼 바리스타가 출근 -> 출근한 바리스타가 팻말을 [open]으로 바꿈 -> 손님이 방문 -> 방문한 손님이 커피를 주문 -> 주문한 커피 계산 -> 바리스타가 주문된 커피를 만드는 동안 손님 대기 -> 바리스타가 완성된 커피를 픽업테이블에 올려둠 -> 손님이 커피를 가져감 -> 커피에 대한 만족도를 손님이 표현-> 마감시간이 끝나고 바리스타가 팻말을 [close]로 바꿈
이렇게 메서드를 만들어 보고 싶었는데... 짧은 시간에 혼자 하기에는 역부족이더라고요. 많이 아쉬워요🥲
카페 조명이나 커피머신으로 프로퍼티를 만들었으면 끝도 없겠어요🤣

캠프가 끝나면 꼭 저렇게 만들어보려고요:)

Comment on lines 53 to 54
func order(_ coffee: Coffee, price: Int, customerName: String) {
openCafe()
Copy link
Member

Choose a reason for hiding this comment

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

openCafe() 라는 메서드가 존재하지 않아서, 컴파일이 안 됩니다. 😢
커밋 찍기 전에 꼭 컴파일 성공 여부를 확인해주세요! 👍🏻

Comment on lines 25 to 31
func spandCash(amount: Int) {
if isPayable(amount) == false {
print("잔돈이 부족합니다.")
} else {
walletInCash -= amount
}
}
Copy link
Member

Choose a reason for hiding this comment

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

이 메서드는 구현은 됐지만, 호출되고 있지 않습니다.
그리고 삭제하더라도, 프로그램 실행에 전혀 문제가 없네요!

따라서, 삭제 커밋 해주시면 좋겠습니다! 😄
그리고 오타도 있네요. ㅎㅎㅎ spand → spend

Comment on lines 62 to 64
func makeCoffee(menu coffee: Coffee) {
pickUpTable = coffee
}
Copy link
Member

Choose a reason for hiding this comment

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

제가 지난 코멘트에서 알려드린 Thread.sleep 사용해서, 커피가 만들어지는 데 시간을 부여해보셔도 좋아요!
위험한 메서드이지만, 사용해보라고 알려드렸던 거에요. 👍🏻

다음 커밋에 포함시켜보세요!! 😄

@jsa0224
Copy link
Author

jsa0224 commented May 7, 2022

@Jager-yoo

수정했습니다! 앞으로 컴파일 에러 여부 꼭 확인하고 push하겠습니다😅

@Jager-yoo
Copy link
Member

정말 긴 과정이었네요!
수고 많으셨습니다 솜! 😄 머지합니다!

@Jager-yoo Jager-yoo merged commit d51e231 into yagom-academy:ss_4_somsom May 7, 2022
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.

None yet

2 participants