utils의 이해 & 제너릭 인터페이스를 활용한 예외처리

2022년 03월 02일

TOC

Utils의 이해

우아한테크코스의 로또 미션을 진행하던중 다음과 같은 피드백을 받았습니다.

정리를 해보면 utils에는 비즈니스 로직과 직접적인 관계가 없고 범용적으로 사용할 수 있는 도구들이 있어야한다.는 피드백입니다. 하지만 제가 utils 디렉터리에 작성한 InputValidation.java 파일에서는 어떤 작업들을 하였을까요?

해당 파일에 위치한 메서드들은 다음과 같습니다.

  • 입력 값이 정수가 아닐경우 예외상황 throw
  • 입력 금액이 1000원 이하일 경우 예외상황 throw
  • 당첨번호의 입력값이 6개 이하일 경우 예외상황 throw
  • 각각의 입력값을 integer 또는 Set으로 변경하여 값을 반환

해당 메서드의 작업 내용을 보면 입력 금액이 1000원 이하일 경우 예외상황 throw, 당첨번호의 입력값이 6개 이하일 경우 예외상황 throw의 경우는 확실히 비즈니스 로직과 직접적인 관계를 갖고 있는 것을 알 수 있습니다. 또한 각각의 입력값을 integer 또는 Set<Integer>으로 변경하여 값을 반환도 어떻게 보면 view에서 입력을 받는 작업 뿐만 아니라 비즈니스 로직에서 실제로 사용할 값들을 가공하는 일을 하고있다고 볼 수 있습니다.

이러한 내용들은 Utils가 아닌 각각의 domain에 위치시키는 것이 맞습니다. 즉, 입력 금액이 1000원 이하일 경우 예외상황 throw의 경우는 실질적으로 금액을 사용하는 Store클래스에, 당첨번호의 입력값이 6개 이하일 경우 예외상황 throw은 실질적으로 당첨번호를 입력받아 저장을 하는 WinningNumbers에 위치하여야합니다.

📌 utils에는 비즈니스 로직과 직접적인 관계가 없고 범용적으로 사용할 수 있는 도구들이 있어야한다.

제너릭 인터페이스를 활용한 예외처리

앞서 소개한 utils에 위치하였던 InputValidation.java의 메서드들을 각각의 domain에 분리를 하다보니, InputView에서 예외를 잡고 다시 입력을 받아주는 메서드를 호출하는데 사용했던 try-catch가 컨트롤러로 이동하게 되었습니다.

컨트롤러에서 각각의 입력에 따라 생성되는 try-catch문들을 controller코드를 더욱 복잡해보이게 한다는 생각이 들었습니다.

    public static int inputPrice() {
        try {
            System.out.println(INPUT_PRICE);
            return InputValidation.validatePrice(scanner.nextLine());
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
            return inputPrice();
        }
    }

    public static Set<Integer> inputWinningLottoNumbers() {
        try {
            System.out.println(INPUT_WINNING_LOTTO_NUMBERS);
            return InputValidation.validateWinningNumber(scanner.nextLine());
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
            return inputWinningLottoNumbers();
        }
    }

    public static int inputBonus() {
        try {
            System.out.println(INPUT_BONUS_NUMBER);
            return InputValidation.validateBonusNumber(scanner.nextLine());
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
            return inputBonus();
        }
    }

해당 코드를 줄여보고자 다른 크루들은 어떠한 방법으로 해당 로직을 작성하였는가 크루들의 코드를 구경하던중 제너릭 인터페이스를 활용하여 반복된 try-catch문을 줄인 필즈의 미션 코드 를 봤습니다.

필즈의 코드를 보고 저의 코드에도 반영을 해보며 try-catch의 반복을 줄여봤습니다.

public class InputView {
  ...

    public interface IndividualInput<T> {
        T get();
    }

    public static <T> T commonInputProcess(IndividualInput<T> individualInputs) {
        try {
            return individualInputs.get();
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
            return commonInputProcess(individualInputs);
        }
    }
  ...
}

public class LottoGameController {
    public void run() {
        final Lottos purchasedLotto = InputView.commonInputProcess(
                () -> Store.purchaseLottos(InputView.inputPrice()));
      ...
    }
}

코드는 위와 같이 InputView에 제너릭 인터페이스와 try-catch를 처리해주는 제너릭 메서드를 추가해주면 해당 메서드를 통해 반복된 try-catch문을 제거할 수 있었습니다.

자세한 코드는 렉스의 로또 미션 에서 확인하실 수 있습니다.

Buy me a coffeeBuy me a coffee
Written by

@Seongwon

기술공유를 통해 새로운 가치 창조을 추구하는 백엔드 개발자 오성원입니다.
©SeongwonOh