[우아한테크코스 6기 BE] 3주차 회고 - 로또 🎱

3주차 시작

3주차 미션은 "로또"를 받았다.

작년 5기 때 풀어본 문제기도 했어서 이번 미션은 쉽지 않으까?라는 생각했었다.

하지만 생각과 다르게 이번 미션은 1, 2주차 미션에 비교도 안될만큼 어려움이 있었다.

그 이유는 바로 추가된 많은 요구사항과 지금까지 받은 피드백들을 반영하려다 보니 더 어렵게 느껴지지 않았나 싶다.

또한 로또 추첨 방식에 대해 착각하고 있어 완전히 다른 추첨을 하고 있던 코드를 갈아 엎고 "로또 추첨 방식" 같은 키워드로 검색해보면서 다시 제대로된 추첨 방법으로 수정하곤 했었다.. 😂

 

이번 3주차 로또는 다른 주차 미션과 마찬가지로 피드백과 이전 미션들에서 아쉬웠던 점들을 적극 반영하기 위해 노력했다.

특히 이 많은 내용들을 반영하다보니 코드가 복잡해지고 가독성이 떨어지는 부분이 나왔었는데

아무래도 이 부분을 해결하기 위한 과정에서 어려움을 겪었지 않나 싶다.

하지만 그 만큼 새로운 내용에 대해 학습해보고 고민하며 다양한 시도를 하는 것에 시간가는줄 모르고 재밌게 임했던것 같다 😁

 

제출 코드
 

GitHub - Hyeon0208/java-lotto-6

Contribute to Hyeon0208/java-lotto-6 development by creating an account on GitHub.

github.com

 

이전 회고 되 돌아보기

2023.10.27 - [◼ 기타] - [우아한테크코스 6기 BE] 1주차 회고 (지난 시간을 돌이켜보며)

2023.11.05 - [◼ 기타] - [우아한테크코스 6기 BE] 2주차 회고 - 자동차 경주 🚘


새롭게 알게된 것과 이번에 적용한 것

정적 팩토리 메서드

이전 미션까지는 정적 팩토리 메서드를 적용할 필요성에 대해 느끼지 못해 공부한 내용을 활용하진 못했지만

이번 미션에서는 객체 생성시 복잡한 로직이 필요했기에 정적 팩토리 메서드 활용해보았다.

아래는 정적 팩토리 메서드에 사용하는 이유,언제 사용하면 좋을지 그리고 개인적인 생각들을 작성한 포스팅이다. 

 

[OOP] 정적 팩토리 메서드를 왜 사용하는가? 어떤 상황에 사용하는게 좋을까? (생성자와 차이)

정적 팩토리 메서드란? 쉽게 말해 생성자로 인스턴스를 생성하지 않고, static Method를 사용해 인스턴스를 생성하는 방식이다. 간단한 예시로 여러 장르(genre)를 가지는 게임(Game) 객체에 대한 코

hstory0208.tistory.com

 

검증에 대한 책임을 분리

지금까지는 입력을 받는 역할을 가진 InputView 클래스에서 모든 예외들을 각 입력별로 검증하고 있었다.

정말 입력 값에 대한 검증이라면 문제가 되진 않겠지만, 문제는 도메인 요구 사항에 대한 부분도 입력시 검증하고 있어

너무 많은 책임을 갖고 있었다는 것이다.

그래서 이번 미션에서는 각 도메인 요구 사항에 맞는 예외는 도메인 안에서 검증을 하고

쉼표로 구분된 "1,2,3,4,5,6" 이러한 입력 형식에 대한 예외는 입력시에 검증을 하도록 분리하였다.

이렇게 검증에 대한 책임을 분리하니 각 요구사항에 대해 파악하기 쉬워졌었고,

원시값에 대해서도 검증이 필요하면 자연스럽게 Wrapper 클래스를 만들어 해당 값을 상태로 갖는 클래스가 검증된 값을 갖게 하도록 할 수 있었다.

 

함수형 인터페이스

이번 미션의 위와 같은 요구사항이 추가되었다.

입력은 구매가격, 당첨번호, 보너스 번호 총 3가지를 입력받는데

위 요구사항을 만족하기 위해 3가지 입력값에 대해 while , try ~ catch 구문이 들어갈 필요가 있었다.

하지만 while , try ~ catch 구문은 고정적이고 내부 핵심 로직만 변하고 있다.

AOP 처럼 공통적인 기능을 한 곳에서 관리하고, 핵심 기능만을 분리할 수 없을까?

이와 같은 접근방식으로 알게 된 것이 바로 "함수형 인터페이스"이다.

함수형 인터페이스가 무엇이고 어떻게 활용했는지에 대해선 아래 포스팅에서 설명한다.

 

[Java] 함수형 인터페이스란? 활용 방법에 대해 알아보자

중복되는 코드 중에서 중복을 제거하기 어려운 로직을 제거하기 위해 알아보던 중 AOP 처럼 공통적인 기능을 한 곳에서 관리하고, 핵심 기능만을 분리할 수 없을까에 대해 고민해보다가 함수형

hstory0208.tistory.com

 

 

EnumMap 

 

로또는 위와 같이 일치된 번호의 수 별로 정해진 상금이 있다.

2장의 로또를 샀을 때 2장다 3개의 번호만 일치해 5등이 당첨된다면 5등이 당첨된 로또의 개수는 2개이다.

그래서 Map의 getOrDefault() 메서드를 이용해 이미 당첨된 등수가 있다면 해당 등수에 해당하는 로또 개수를 +1씩 카운팅하였다.

 

HashMap을 사용해도 위와 같은 구현을 할 수 있다.

하지만 로또 등수들에 대한 연관 데이터를 Enum으로 관리함으로 이에 맞는 EnumMap을 사용하고 싶었다.

EnumMap은 Java의 Map 인터페이스를 구현한 클래스 중 하나로, 키로 열거형(Enum)을 사용하는 구현체이다.

EnumMap을 사용했을 때의 장점은 다음과 같았다.

  1. EnumMap은 상수의 순서를 보장해준다.
  2. 내부적으로 배열을 사용해 데이터를 저장해 HashMap에 비해 데이터의 조회와 수정이 빠르다.

아쉬운 점

인터페이스를 꼭 사용할 필요가 있었을까?

저번 자동차 경주 미션에서는 Car 객체를 List<Car>로 관리하는 RacingCars객체가 이동 조건에 따라 이동할 수 있어야 했었다.

이동 조건은 랜덤하게 생성된 번호에 따라 정해지는데 RacingCars객체가 이동하는 기능에 대한 책임이 필요했기 때문에 랜덤 번호를 얻는 방식에 대한 결합도를 인터페이스를 도입하면서 낮출 필요가 있었다.

하지만 이번 경우는 좀 달랐다.

지난 미션에서 배운점에 대해 적용을 한다는 목적으로 이미 인터페이스를 활용해 구현했었지만

나중에 다시 검토하다보니 Lotto 객체를 List<Lotto>로 관리하는 LottoTicket 객체가 로또 번호들을 생성하는 역할을 갖는 것은 어색하다 생각이 들었다.

이 부분은 사실 LottoMachine 같은 객체를 만들어서 해당 객체에서 로또번호를 발급하고 이 발급된 번호들로 LottoTicket 객체를 생성한다면 더욱 다양한 번호들에 대한 테스트 또한 가능했었다.

다음에는 배웠던 부분이나 아쉬웠던 부분에 대해 무조건 다 적용하려기 보다현재 상황에서 필요한가?에 대해 판단하고 나서 적용할 필요가 있을꺼 같다.

 

생성자에 코드를 넣었을 때 문제점

 

기존에 주어진 Lotto의 형식 대로 필자는 겸증 로직을 생성자 생성 시점에 넣었었다.

하지만 생성자에 이러한 코드를 넣는 것은 좋지 않다고 한다.

 

생성자는 객체를 안전한 상태로 초기화하는 것이 주된 목적이다.

생성자 시점에 특정 로직이 들어간다면 이러한 주된 목적에 방해가 될 뿐 아니라 여러 문제가 있다고 한다.

아래 링크는 이에 대한 내용에 대해 설명한 포스팅이다. 

https://lovethefeel.tistory.com/m/159

 

boolean, Predicate 메소드 부정어 네이밍 지양

필자는 if (!isNumber()) {...} 같은 부정조건문 if(isNotNumber()) {...} 같은 긍정조건문으로 메서드를 네이밍해 사용하고 있었다.

이번에 메소드 네이밍을 부정어로 만드는 것보다 긍정어로 만드는 것이 재사용에 용이하다는 피드백과 관련 문서를 받았다.

관련 문서 링크 클릭

이에 대해 다시 생각해보게 되었는데

확실히 재사용면에서는 부정어 네이밍 메서드에 !를 붙여 부정의 부정의 의미를 더해 긍정형을 만드는 것보단

긍정형에 !를 붙여 반대되는 조건을 만드는게 어색함이 없을 것 같다고 생각한다.


확실히 1주차 미션을 시작했을 때와 현재와 비교하면 월등히 성장했음을 느낄 수 있었다.

어렵게만 느꼈던 객체 지향에 대해 객체 지향의 본질에 대해 이해하기 시작하면서

왜 "객체 지향 .. 객체 지향..~" 이라고 하는지 깨닫게 될 수 있었던 성장의 과정이었다.

이제 마지막 4주차 미션만이 남았다.

4주차 미션에서도 이 때 까지의 미션들에 대해 아쉬웠던점과 새롭게 배운점들을 적극적으로 반영할 예정이다.

4주차까지 끝나게 되면 프리코스를 시작한 시점과 끝난 시점에 대한 자신을 돌아 보았을 때 과연 어떤 느낌이 들까?

 

보다 성장했을 나 자신을 기대하며 마지막까지 화이팅 해보자! 💪🏻