우아한 테크 코스

[백엔드]우아한 테크 코스 6기 프리코스 3주차 회고록

Choony 2023. 11. 18. 16:51

어느덧 벌써 3주차를 회고하는 날이 되었다. (이미 프리코스는 끝났다. ㅋㅋ)

 

로또 미션을 진행하면서 고민했던 내용들에 대한, 얻었던 지식에 대한 이야기를 풀어나가고자 한다.

 

필요한 객체

 

객체지향은 말그대로 '객체'들의 소통으로 프로그램을 돌아가게 하는것이다.

그러니 객체지향스러운 프로그램을 만들기 위해선 제일 먼저 어떤 객체가 필요한지 파악하는건 자연스럽고 중요한 행동이다.

 

'굿노트'를 구매하고 프리코스를 진행하며 드디어 뽕뽑는거같다.

나는 코딩을 하기전 이렇게 낙서형식으로 필요한게 무엇인지 적어보는 과정을 필수적으로 진행한다.

내가 머리가 나쁜건지 머릿속에 있는 내용들이 도무지 정리가 안되기 때문에... 이렇게라도 정리를 해야 마음이 한결 편해진다.

 

자, 일단 객체들의 역할은 저기 적혀있는게 다다. 최대한 객체들에게 최소한(필수)의 역할만 하게 객체 설계를 해보았다.

 

또한 요새 꽂힌게 '일급 컬렉션' 과 원시 타입을 포장하는 것이다. 이를 사용하며 얻는 이점은 매우 많다.

 

원시 타입을 포장하며 얻는 이점에 대해 간단하게 나열해보자면

- 자신의 상태를 객체 스스로 관리할 수 있다.

- 코드의 유지보수에 도움이 된다.

- 자료형에 구애받지 않는다.

 

이정도가 있는데 꼭 꼭 아래의 링크를 들어가 정독하길 바란다. ( 꼭 보세요 )

https://tecoble.techcourse.co.kr/post/2020-05-29-wrap-primitive-type/

 

원시 타입을 포장해야 하는 이유

변수를 선언하는 방법에는 두 가지가 있다. 원시 타입의 변수를 선언하는 방법과, 원시 타입의 변수를 객체로 포장한 변수를 선언하는 방법이 있다. (Collection…

tecoble.techcourse.co.kr

 

지금 보니 View나 Controller에 대한 객체는 적어놓질 않았는데.. 

이 그림 하나로 충분히 이해 될거같다!! 

 

재입력을 받으시오.

 

1주차와 2주차 미션에서는 사용자가 잘못된 입력을 했을 경우 오류를 발생시키고 종료시켰다. 이와 반대로 3주차부터는 잘못 입력한 부분부터 재입력을 받으라는 요구사항이 추가되었다.

 

내가 1주차부터 프로젝트에 있어 채택한 설계 방식은 MVC 패턴이었다. 크게 Model과 View와 Controller로 이루어져 있으며 service에서 비즈니스 로직을 담당하는 구조이다.

 

오류가 발생할 수 있는 상황은 두가지가 있다.

1. View단에서 유효성 검사에서 걸리는 경우

2. Domain단에서 객체를 생성할 때 유효성 검사에서 걸리는 경우

 

이렇게 두 개의 층에서 오류가 발생할 수 있으니 어떻게 재입력을 받아야 할지 머리가 복잡했다.

 

저 바로 위의 객체들의 관계를 표현한 그림에서도 고민한 내용에 대해 볼 수 있다. (저 방식을 택했다.)

 

간단하게 말해보자면 controller에서 모든 예외에 대해 try-catch를 이용하여 재귀적으로 재입력을 받는 방식을 택하였다.

(controller가 각 계층에 있어서 징검다리 역할을 한다고 생각했기 때문이다)

 

이는 로또 구입 금액을 입력하는 Controller의 메서드이다. 

이 코드에선 예외를 catch했을 때 재입력을 받기 위해 재귀적 방식을 사용했는데 코드 리뷰를 받다보니 오버플로우가 발생 할 수도 있겠다는 생각이 들었고 while(true)를 이용한 무한 반복을 이용하는 방식이 좀 더 좋았던거 같다.

 

EnumMap 너 뭐냐?

자바에서 Map을 떠올린다면 당연 가장 먼저 떠오르는 것은 HashMap이다. 하지만 EnumMap이란 놈도 있더라?!

어떻게 알게 됐는지 살펴보자.

 

이는 로또의 각 등수에 대한 매칭 넘버 갯수와 당첨 금액을 저장한 Enum이다. 

나는 이 Enum을 이용하여 각 등수에 몇 번 당첨됐는지 저장하고 싶었고, 그때 떠오른 방식이 Map을 이용한 저장 방식이었다.

 

Map<Ranking, Integer>를 이용하여 각 랭킹과 당첨 횟수를 저장하는 것인데 뭔가 마음에 내키지 않았다. 왜인지는 모르겠다. 그냥 마음에 안들었다. Enum과 Map이 뭔가 쿵짝 할 수 있을게 없을까 끄적이던 도중 어이없게 EnumMap의 존재를 알게되었고 바로 채택하였다.

 

EnumMap은 간단하게 Enum을 key로 갖는 map 자료구조이다.

찾아보니 장점은 이렇게 있더라.

- 딱 Enum에 정해진 값들로만 이루어진 map이기 때문에 hashmap에서 고려해야 할 해시값 충돌을 걱정할 필요가 없다.

- 속도가 매우 빠르다.

 

그러니 여러분들도 Enum을 key로 가져야 하는 상황이 온다면 EnumMap을 꼭 고려해보길 바란다. 학습은 구글링을통해 !

 

코드리뷰

기다리고 기다린 코드리뷰!!!!! 이번에도 많은 리뷰를 해주셔서 아주 감사드립니다 👍

 

1번 리뷰

이는 나도 아주 뼈아프게 동감하는 리뷰이다.

현재 저 코드상 getBonusNumber메서드에서 makeWinningLotto메서드를 호출하고 있는데 이는 누가봐도 잘못된 구조이다. 

초반 재입력을 위한 설계가 부족했던 탓에 유효성 검사와 재입력을 모두 고려했을때 저 코드로밖에 충족이 안된 상황이다.... 설계... 똑바로 하자...

 

2번 리뷰

위에서도 말했듯 재귀적 호출의 오버플로우에 대한 리뷰였다. 나도 저 말에 동감하는 바다. 재귀적 호출이기 때문에 계속해서 잘못된 입력이 들어올 경우 콜스택이 계속 쌓일것이다... 물론 JVM에서 이를 어떠헥 관리해주는지는 찾아봐야 겠지만 아직은 잘 모르겠다. (아는 사람 답좀요 ㅠ_ㅠ)

 

이때 함수형 인터페이스에 대한 리뷰가 함께 들어왔다. 저건 어떤 뜻일까?

Java8부터 함수형 인터페이스를 사용할 수 있는데 Java에서 제공해주는 함수형 인터페이스로는  Supplier, Predicate, Consumer등이 있다. 각 인터페이스에 대해서는 각자 학습해보길 바란다. 

 

내 코드에서 적용해 볼 수 있는건 아무 파라미터를 받지 않는 상황이기에 Supplier를 적용해보는게 좋을거같다.

public void generateLottoUsePurchaseAmount() {
        int purchaseAmount = readPurchaseAmount(v -> generateLottoUsePurchaseAmount());
        makeLottoByPurchaseAmount(purchaseAmount);
    }

private int readPurchaseAmount(Function<Void, Integer> onError) {
    try {
        // 여기에서 구매 금액을 읽는 로직
        return /* 읽은 구매 금액 */;
    } catch (IllegalArgumentException e) {
        outputView.printError(e);
        return onError.apply(null);
    }
}

private void makeLottoByPurchaseAmount(int purchaseAmount) {
    // 구매 금액을 이용한 로또 생성 로직
}

이런식으로 재귀적호출을 함수형 인터페이스를 이용하여 바꿀 수 도 있다. 

좋은 의견 주셔서 감사합니다!!

 

리뷰 4번

나는 로또 번호의 매칭 넘버 개수를 구하기 위해 LottoHandler라는 객체를 새로 만들었으며 여기서 Lotto의 번호를 getter를 이용하여 사용했는데,,,, '객체를 객체스럽게 사용하자'라는 피드백을 받고나선 난 그렇게 사용하지 못하고 있구나 깨달았다.

로또 매칭 넘버 개수를 가져오는 정도는 Lotto 클래스에서 하는게 좋다고 생각한다. getter를 이용하여 데이터를 가져온다면 Lotto는 그저 넘버 저장소 아닐까?! 객체답게 사용하자..!!

 

 

 

이번 3주차도 재밌었고 많은걸 배웠다.. 프리코스 최고 🙌 👍