11/13 wooteco: 나 너무 많은 일이 잇엇어 힘들다진짜

그른손·2023년 11월 13일
0
post-thumbnail


지금 기분

깔끔하게 맞물리지 않는 요구사항은 어떻게 처리해야 할까

  • 할인 유형은 '할인 유형인 것'만 공통점이고, 구체적인 내용이 너무 다르다... 조건도, 할인금액도, 적용 로직도 다르다. 이걸 어떻게 일반화해야 할까? 가 큰 고민이었다.

  • 공통적인 로직은, 예약 정보(예약날짜, 주문, 총 금액)를 토대로 특정 조건을 확인해 특정 할인 내용을 적용한다는 것이다. 일단 거기서 시작해보기로 했다.

  • 원래는 로또 미션에서 했던 것처럼 각 혜택 항목을 객체로 만들어 배열에 담아서 상수화하고, 사용할 때는 이걸 불러와서 반복을 걸어 모든 할인을 적용시키는 식으로 하고싶었다. 근데 로또와는 다르게 할인 조건의 확인과 할인 적용 로직이 너무 판이하게 달라서 그런 식으로는 하기 힘들겠다 싶었다.

  • Benefit 추상 클래스를 만들고, 이를 상속해서 각각의 혜택 항목을 서브클래스로 구현했다.

  • 공통점은 apply 메서드를 실행하면 조건을 확인, 할인내용을 적용한 결과를 반환한다 는 것이다.

  • 한 가지 더 어려웠던 건, 증정 혜택의 처리였다. 증정 혜택은 다른 혜택과 다르게, 직접적으로 금액을 할인하는 게 아니라 증정품을 줌으로써 그 가격만큼 혜택을 주는 것이다. 여기서 두 가지 문제가 생겼다.

  1. 다른 혜택과 다르게 증정품을 별도의 항목에 표시해야 함.
  2. 다른 혜택과 다르게 증정품의 혜택 금액은 실제 할인으로 적용되어서는 안됨
  • 일단 1번의 문제는, ComplimentaryBenefit이 반환하는 결과 객체에 giftItem과 giftCount로 증정품 항목을 추가시켰다. 그리고 Discounter에서 할인 결과를 갈무리 할 때, 적용된 혜택에서 giftItem과 giftCount를 포함하는 결과가 있는지 확인하고 이를 가져와서 Discounter의 할인 결과 필드에 저장한다.
  • 2번의 문제는, 혜택 금액도 출력되어야 하고 할인이 적용된 금액도 출력되어야 하는 데서 좀 더 복잡한 문제가 있다. 처음에는 예상 결제 금액을 표시할 때 여기에 증정품 가액을 합산해주는 식으로 구현했다.
  • 그런데 아무래도 뭔가가 부자연스럽다고 느꼈다. 증정품 가액을 합산하는 과정 자체가 작위적이라고 느꼈고, 이렇게 하면 추후에 증정품 혜택처럼 혜택은 주지만 할인은 하지 않는 항목이 생기면 Discounter를 다시 뜯어고쳐야 한다. (예시로는 글쎄... 포인트 적립 혜택이라든가)
  • 그래서 각 Benefit 클래스가 혜택 금액benefitAmount과 할인금액discountAmount를 각각 갖고있게끔 바꿨다. 이렇게 하면 혜택금액은 별도로 출력을 위해 가지고 있을 수 있고, 총 구매액에서 할인 금액을 차감해서 반환하면 되니까 훨씬 자연스럽다고 생각했다.
  • 물론 이렇게 할 경우, 다른 혜택 항목들은 discountAmount와 benefitAmount가 동일한 값을 가지니, 약간 부자연스럽다고 느낄 수는 있다. 하지만 추가적인 연산을 실행하지 않고 그냥 두 개의 필드에 넣기만 하는 것이니, 괜찮은 트레이드오프라고 판단했다.
  • 결론적으로, 확장성을 고려해서 증정 혜택과 비슷한 다른 항목을 추가해도 Discounter를 수정할 필요 없이 손쉽게 적용할 수 있게끔 처리했다.

Date 객체 다루는 법 익히기

  • Date 객체를 다뤄본 경험이 별로 없어서 조금 어려웠다. 특히 DDayBenefit을 구현할 때, 1일부터 25일까지, Dday에 가까워질수록 할인 금액에 증가값을 합산하는 로직을 구현하면서 애를 많이 먹었다. 구현하고 테스트 통과하길래 됐다! 했는데, 테스트 작성하면서 보니까 Dday가 지난 후에도 동일한 계산으로 할인을 적용하고 있어서 부랴부랴 수정했다...
  • 요일을 계산하는 법을 처음 배웠다! (getDay, 0123456)

일관성 있는 네이밍 하기

  • order와 orders를 혼용하고, price와 moneyAmount를 혼용하는 등... 구현이 오래 걸리니까 같은 걸 가리키는 단어를 다르게 쓰는 경우가 많아졌다. 파일을 하나하나 돌면서 표현이 달라진 부분을 통일해야 했다.
  • 구현에 시간이 너무 오래 걸려서, 함수명 리팩토링을 제대로 하지 못한 것 같아 아쉽다. 그래도 지금까지 해오면서 (함수명 일일히 바꾸기 귀찮아서) 네이밍을 신중하게 고려하는 습관이 생겨 많은 도움을 받았다.

말도 안되는 멍청한 오류들

  • 미션 요구 사항에서 빼먹은 부분이 있었다 : 음료만으로 주문 불가. 마지막 날에 뭐 빼먹은 거 없나 하고 요구사항 훑어보다가 발견해서 화들짝 놀라 바로 적용했다. 테스트에서 이러면 답도 없다...
  • 0개 주문이 왜 되냐고 : 죄송합니다... 경계값 관리(라고 말할 것 까지도 없고 그냥 바보짓 한거지만)를 똑바로 해야겠다
  • {Object object} undefined 개를 주문했습니다 : format.menu 함수는 menuName과 count를 받아 '타파스 1개'처럼 출력해준다. 나는 여기에 {menuName, count}를 넘겨서 이따위로 출력이 됐다 하하 😅
  • 위의 경우도 테스트를 작성하다가 발견했다. 이번에는 출력 형식의 순서가 자유롭게 되어있어서 주어진 ApplicationTest에서는 각 항목의 타이틀이 제대로 출력되는지만 확인하는데, 그러다보니 실제 출력이 잘못되는 경우를 잡아내려면 별도의 테스트를 작성해야 했다. 테스트 작성하면서 잡아낸 오류만 벌써 다섯개다... 테스트는 신이야...

TDD는 못했어요

  • 다음주차에는 TDD 해야지! 하고 저번주 회고에 썼는데... 이거 쉽지 않다. 앱의 구체적인 구현이 아직 확정되지 않은 상태에서, 아직 존재하지 않는 기능을 테스트한다니... 그 개념이 생소해서 받아들이기가 까다로웠던 것 같다. 처음에는 열심히 시도해봤는데... 이러다 기한 넘기겠다 싶어서 빠르게 포기하고 결국엔 저번처럼 구현 => 테스트 순서로 했다.

피드백은 잘 적용했는지?

  • 아마 아닌거같아요...
  • 피드백에서 좀 이해하기 어려웠던 부분은 객체를 객체답게 써라라는 것이었다. (이게 OOP인가?) 객체가 데이터를 단순히 던지게 하지 말고 갖고있는 데이터를 활용해서 직접 일하게 만들라고 되어있었는데,
  • 그걸 적용해서 Menu 객체가 name과 count 필드를 이용해서 ${name} ${count}개와 같이 출력용 문자열을 던지게 만들었는데, 이러고 보니 출력 로직이 모델에 있네? 라는 생각이 들어서 결국엔 {this.#name, this.#count}를 반환하도록 수정했다.
  • 그냥 진짜 단순히 객체가 데이터를 던지는 게 더 편한 상황에서는 어떻게 해야 하지?라는 궁금증이 들었다. 예제에서는 Lotto 클래스를 보여주고 이건 단순히 numbers를 저장하고, 달라고 하면 던지는 역할만 하는 거라 객체가 객체답지 않다고 얘기했는데, 그럼 객체를 객체답게 사용하는 다른 메서드들이 여러개 포함되어있으면 괜찮은건가? 그래도 값을 직접 던지는 건 안되는건가? 헷갈린다...
  • 필드를 많이 쓰지 마라는 것도 잘 안지켜진 것 같다... 여러 값들이 들어있는 객체를 필드에 저장하는 건 사실상 그냥 눈 가리고 아웅이고😅
  • 하지만 여기서도 의문이 든 것이, 저장하고 있는 게 낫다고 판단한 경우는 어떻게 해야 하는가이다. 확장성 면에서, 내가 직접 계산해서 던지는 값들이 나중에 확장되면서 다른 곳에도 필요하게 된다면? 그러면 저장해놓고 불러오는 게 훨씬 경제적이지 않나?
  • DI의 경우에도 그렇다. 2주차에서는 모든 의존성을 외부에서 주입해야지! 하고 그렇게 해봤는데, 이러면 또 가독성이 엄청 나빠졌다. 그렇다고 DI를 적용했을 때 무엇이 좋은지는 내 식견에서 체감하기가 어려웠다. 효과를 느끼지 못하니 왜 하는지 모르고, 왜 하는지 모르니 어디서부터 어디까지 DI를 해야 할지 정하기 힘들었다. 나름의 근거를 갖고 결정하려는 생각으로 내가 이거는 의존성 주입 하면서 이거는 안하는 이유를 줄줄이 써내려가봤지만, 마음 한 켠에서는 진짜 그래?ㅎㅎ라는 생각이 꿈틀거렸다. 내가 어떻게 알아 씨...
  • 결국에는 어디서부터 어디까지? 라는 의문이 아직도 목에 걸린 가시처럼 남아있는 것 같다.

소감은?

  • 드디어 끝이구나... 하는 생각? 시원하기도 하고 조금 아쉬운 거 같기도 하고
  • 프리코스 할 때 제일 열심히 코딩하는 사람이다. 과제가 주어지고 제한시간이 있는 상황이 나를 열심히 하게끔 만드는듯...
  • 처음에 지원서 쓸 때, '성장은 고통 없이는 이루어지지 않는다'라는 말을 썼다. 코드스테이츠에서 회고할 때도 여러 번 했던 말이다. 그도 그럴 게, 나는 코딩을 되게 고통스럽게 시작했다. 헬로 월드도 안찍어보고 무작정 4기 프리코스에 지원해서, 몸을 갈아가면서 발버둥쳤으니... 그 과정이 힘들지 않았다면 거짓말일 거다.
  • 하지만 지금에 와서 느끼는 건, 결국 너도 재밌어서 한 거 아니야? 라는 생각이다. 지난 세월 거진 30년간 알게 된 게, 나는 고통을 잘 참는 사람이 아니고, 내가 흥미 못 느끼는 건 때려죽여도 열심히 못하는 사람이다. 돌이켜보면 그 때 잠도 안 자고 밥도 안 먹고 화장실도 안 가면서 (이거 진짜임... 풀기 전에는 일어날 생각이 안들었음) 과제 붙잡고 늘어졌던 건, 이게 되네? 나 천잰가? 완전 신나는데? 라는 생각이 문제 풀 때마다 펑펑 터져서 그랬던 것 같다.
  • 즐거움 없는 고통은 성장이 아니라 그냥 고문이다. 고통은 성장의 원동력이 아니라 부산물이고, 즐거움이야말로 그 원동력이다. 생각해보면, 실제로 써먹을 일은 없을 앱을 만들면서 이렇게나 많이 고민하고, 공부하게 만드는 건 우테코가 처음이다. 이렇게나 열심히 남의 코드를 본 적도 별로 없었고, 열의에 차서 밤 새도록 코드 리뷰를 주고받았던 적은 한 번도 없었다.
  • 진짜 붙고싶다... 라는 생각을 했다. '여기라면 최선을 다할 수 있을 것 같다'라는 말은 웃기는 소리고 어디에 있든 최선을 다해야 하지만, 우테코라면 정말 즐겁게 최선을 다할 수 있을 것 같아서😅
profile
프론트엔드 개발자

0개의 댓글