Published on

우아한테크코스 프리코스 3주차 회고

Authors
  • avatar
    Name
    ywj9811
    Twitter

우아한테크코스 프리코스 3주차가 끝났다.

이제 세번째 주차가 끝이 나고 마지막 주차를 향해 달려가고 있다.

사실 이번 주차는 개인적으로 아쉬움이 정말 많이 남았고 많은 내용을 배운 기간이었다.

점점 내용이 어려워 지기도 하고 마지막 주차가 기대…두렵…기도 하다

wtc3

세번째 주차의 시작

두근두근 하는 마음과 함께 이번 주차는 어떤 문제가 나를 반겨줄지 확인을 하러 갔다.

이번 주차는 java-lotto 라는 미션을 받았다.

로또의 기능을 구현하는 미션이라… 기능 구현 자체는 그렇게 어렵지는 않을 것 같다고 생각하였다.

(이후에 큰 복병을 만났다..)


세번째 주차를 시작하며 나의 목표를 세웠다.

  1. 클래스 분리를 하여 하나의 클래스에서 모든 일을 부담하지 않도록 하자.
  2. 단위 테스트에 대해 연습을 하자
  3. 값을 하드 코딩하지 말자

이렇게 크게 3가지의 목표를 가지고 제시 해주는 기능 목록을 지키고자 하였다.


🚀 기능 요구 사항

로또 게임 기능을 구현해야 한다. 로또 게임은 아래와 같은 규칙으로 진행된다.

로또 번호의 숫자 범위는 1~45까지이다.
- 1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다.
- 당첨 번호 추첨 시 중복되지 않는 숫자 6개와 보너스 번호 1개를 뽑는다.
- 당첨은 1등부터 5등까지 있다. 당첨 기준과 금액은 아래와 같다.
		- 1: 6개 번호 일치 / 2,000,000,000		- 2: 5개 번호 + 보너스 번호 일치 / 30,000,000		- 3: 5개 번호 일치 / 1,500,000		- 4: 4개 번호 일치 / 50,000		- 5: 3개 번호 일치 / 5,000
  • 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다.

  • 로또 1장의 가격은 1,000원이다.

  • 당첨 번호와 보너스 번호를 입력받는다.

  • 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다.

  • 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 종료한다.

  • 문제 상세 내용

    입출력 요구 사항

    입력

    • 로또 구입 금액을 입력 받는다. 구입 금액은 1,000원 단위로 입력 받으며 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다.
    14000
    
    • 당첨 번호를 입력 받는다. 번호는 쉼표(,)를 기준으로 구분한다.
    1,2,3,4,5,6
    
    • 보너스 번호를 입력 받는다.
    7
    

    출력

    • 발행한 로또 수량 및 번호를 출력한다. 로또 번호는 오름차순으로 정렬하여 보여준다.
    8개를 구매했습니다.
    [8, 21, 23, 41, 42, 43]
    [3, 5, 11, 16, 32, 38]
    [7, 11, 16, 35, 36, 44]
    [1, 8, 11, 31, 41, 42]
    [13, 14, 16, 38, 42, 45]
    [7, 11, 30, 40, 42, 43]
    [2, 13, 22, 32, 38, 45]
    [1, 3, 5, 14, 22, 45]
    
    • 당첨 내역을 출력한다.
    3개 일치 (5,000) - 14개 일치 (50,000) - 05개 일치 (1,500,000) - 05개 일치, 보너스 볼 일치 (30,000,000) - 06개 일치 (2,000,000,000) - 0
    • 수익률은 소수점 둘째 자리에서 반올림한다. (ex. 100.0%, 51.5%, 1,000,000.0%)
    총 수익률은 62.5%입니다.
    
    • 예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 "[ERROR]"로 시작해야 한다.
    [ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.
    

    실행 결과 예시

    구입금액을 입력해 주세요.
    8000
    
    8개를 구매했습니다.
    [8, 21, 23, 41, 42, 43]
    [3, 5, 11, 16, 32, 38]
    [7, 11, 16, 35, 36, 44]
    [1, 8, 11, 31, 41, 42]
    [13, 14, 16, 38, 42, 45]
    [7, 11, 30, 40, 42, 43]
    [2, 13, 22, 32, 38, 45]
    [1, 3, 5, 14, 22, 45]
    
    당첨 번호를 입력해 주세요.
    1,2,3,4,5,6
    
    보너스 번호를 입력해 주세요.
    7
    
    당첨 통계
    ---
    3개 일치 (5,000) - 14개 일치 (50,000) - 05개 일치 (1,500,000) - 05개 일치, 보너스 볼 일치 (30,000,000) - 06개 일치 (2,000,000,000) - 0총 수익률은 62.5%입니다.
    

문제 기능 요구 사항의 경우 그렇게 어려운 내용은 아니었다.

하지만 추가된 요구 사항을 지키면서 코드를 작성하다 보니 어려움을 많이 만난 듯 하다.

가장 큰 어려움이 되었던 내용을 꼽자면 이것일 듯 하다.

  • indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다.
  • 메소드의 길이가 15라인을 넘어가지 않도록 구현한다.
  • else를 사용하지 않는다.
  • Java Enum을 적용한다.

위 4가지를 지키면서 코드를 작성하는 것에서 가장 큰 어려움을 느꼈다.

코드를 작성하다 보면 자꾸 depth가 3이 넘어가는 순간이 찾아오고 내 메소드가 어느순간 15줄을 넘어가고 있고 그런다.

이런 문제는 수많은 리팩토링을 거치면서 해결했다.

위의 어려움 중에서 Enum을 적용한다 → Enum을 어떻게 사용해야 할까?

정말 큰 어려움이었다.

동시에 큰 배움이었다.

처음에는 그저 단순하게 Enum은 열거형 이렇게만 알고 있을 뿐 따로 사용해본적이 없었다.

이번 기회를 통해서 Enum에 대해서 공부하게 되었다.

Enum클래스는 무엇을 대신하여 사용하면 좋을까

기존의 클래스 내부에서 값들을 열거하는 형식으로 코딩하는 것보다 훨씬 많은 장점을 가지고 있다.

그렇다면 나의 코드에서 열거형으로 작성되어있는 부분을 대신하면 되지 않을까?

이런 생각을 가지게 되었다.

따라서 출력에서 사용될 5개 일치, 보너스 볼 일치 (30,000,000원) 이런 내용을 담을 수 있는 Enum을 생성하게 되었다.

물론 지나고 나서 생각해 보니 Enum 클래스에 대한 사용이 너무 능숙하지 않았던 것 같다는 생각이 든다.

앞으로 더 열심히 공부하여 능숙한 사용을 할 필요가 있겠다는 생각이 남는 부분이었다.


본격적으로 만난 문제

이주차까지 수월하게 진행을 했으나 이번 주차 부터 치명적인 문제를 만나기 시작했다.

  1. 해결한 문제

    기능 테스트 중에 로또 번호를 오름차순으로 정렬한 부분에서 UnsupportedOperationException 를 만나게 됐다.

    어째서 발생한 것일까

    테스트 케이스에서 List.of() 를 통해서 값을 넣어주고 있는데 List.of() 는 고정된 값이라 내용이 변경될 수 없다.

    그래서 내가 정렬을 하려고 작성한 Collections.sort 에서 예외가 발생하고 있는 것이었다.

    해결 방법은 new ArrayList<>(List.of()) 이렇게 묶어줘야 한다.

    해결 방법을 알게 되었으니 테스트 코드를 분석하고 해당 문제 사항을 위와 같은 방법으로 처리해서 해결했다!

    모두 풀어쓰자니 너무 길어지기 때문에 링크를 남겨놓을 것이다.

    테스트 케이스에서 발생하는 오류들 (해결한 문제) · ywj9811/java-lotto Wiki

  2. 결국 해결하지 못하고 제출한 문제 (현재는 해결함)

    예외 테스트 중 1000j 가 입력되었을 때 처리를 하지 못하는 문제가 발생했다!

    • 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 종료한다.

    분명 위와 같은 요구 사항을 읽고 예외 상황에서 IllegalArgumentException 를 발생 시키고 [ERROR] 로 시작하는 에러 메시지를 출력했다.

    하지만 테스트가 FAIL이 되는 것이었다.

    [ERROR] 숫자 외의 값이 입력되었습니다.
    
    java.lang.IllegalArgumentException: [ERROR] 숫자 외의 값이 입력되었습니다.
    
    	at lotto.exception.BuyMoneyException.notNumber(BuyMoneyException.java:14)
    	at lotto.exception.BuyMoneyException.fullException(BuyMoneyException.java:25)
    	at lotto.view.InputOutputView.buyMoney(InputOutputView.java:24)
    	at lotto.service.RelativeLottoService.relativeLottoNumber(RelativeLottoService.java:35)
    	at lotto.container.LottoGameController.gameStart(LottoGameController.java:13)
    	at lotto.Application.main(Application.java:10)
    	at lotto.ApplicationTest.runMain(ApplicationTest.java:59)
    	at camp.nextstep.edu.missionutils.test.NsTest.run(NsTest.java:35)
    	at camp.nextstep.edu.missionutils.test.NsTest.runException(NsTest.java:40)
    	at lotto.ApplicationTest.lambda$예외_테스트$1(ApplicationTest.java:52)
    	at org.junit.jupiter.api.AssertTimeout.lambda$assertTimeoutPreemptively$2(AssertTimeout.java:102)
    	at org.junit.jupiter.api.AssertTimeout.lambda$assertTimeoutPreemptively$4(AssertTimeout.java:138)
    	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    	at java.base/java.lang.Thread.run(Thread.java:834)
    
    Process finished with exit code -1
    

    이렇게 나오는 상황이었다.

    이 문제를 해결하기 위해서 꼬박 3일을 투자 했었다.

    물론 해결은 못했었다.

    이 문제를 분석하기 위해서 우선 테스트 코드를 다시 한번 분석을 하였고 새로운 사실을 많이 알게되었다.

    문제를 해결하지 못해도 굉장히 유익한 순간이었다고 생각한다.


    💡해결

    결론적으로 현재는 해결했기에 어떻게 해결했고 무엇이 문제였는지 이야기를 하고자 한다.

    그것은 바로 나의 기본이 부족했기 때문이다.

    우선 나는 IllegalArgumentException 을 발생시키고 있었다.

    이것은 올바른 내용이다.

    하지만 아주 중요한 것을 빠뜨렸다.

    발생을 시키지만 발생된 예외를 잡아서 처리하지 않고 있던 것이었다.

    try {
    	inputPrice = Integer.parseInt(userInput);
    } catch (NumberFormatException exception) {
    	throw new IllegalArgumentException("[ERROR] 숫자를 입력해야 합니다");
    }
    

    이 코드를 예시로 보도록 하자.

    위 코드에서는 숫자가 아닌 경우 IllegalArgumentException 을 던지고 있다.

    나는 여기서 끝이 난 것이다.

    하지만 위의 코드는 예외를 발생을 던져주고 있지만 그 예외를 받아서 처리하는 부분이 없다.

    따라서 이렇게 예외를 던져주고 난 이후 해당 예외를 잡아서 처리하는 단계가 있어야 하는 것이었다.

    정말 큰 충격을 받았다.

    내가 잘못 알고 있는 내용에 사로잡혀 더 많은 탐구를 하지 못하고 한 곳에서 멈춘 상태로 며칠을 보낸 것이었다.

    정말 많은 반성을 하게 된 세번째 주차였다.

    해결 과정에 대한 자세한 내용

    자세한 내용은 위 링크를 참조하길 바란다.