Backend/Java

[숫자야구게임 Step1] 실제 애플리케이션이 동작할 코드를 작성하자 & 마무리

Seyun(Marco) 2024. 4. 20. 11:16
728x90

서론

  • 이제 실제 콘솔 애플리케이션이 동작하도록 실제 View와 Controller의 결합을 해보도록 하겠습니다.

View

  • 실제 사용자가 값을 입력하고, 로직이 실행된 뒤의 결과를 출력하기 위한 계층으로 우리는 콘솔 애플리케이션이니 입력은 Scanner 를 통해 받고, 출력은 System.out.println 을 통해 합니다.
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class InputView {
    private static final Scanner scanner = new Scanner(System.in);
    private static final String INPUT_DELIMITER = "";

    private InputView() {
    }

    public static int inputMenu() {
        System.out.println("게임을 새로 시작하려면 1, 종료하려면 9를 입력하세요.");
        return scanner.nextInt();
    }

    public static List<Integer> inputNumbers() {
        System.out.print("숫자를 입력해주세요 : ");
        String inputNumbers = scanner.next();

        return Arrays.stream(inputNumbers.split(INPUT_DELIMITER))
                .map(Integer::parseInt)
                .toList();
    }
}

  • Scanner는 새롭게 매번 생성할 필요가 없습니다. 해당 시스템에서 최초 한번만 생성하면 되기 때문에 상수로 처리합니다.
  • 여기서 가장 고민했던 부분이 “입력된 값을 DTO로 View에서 감싸는게 맞는가?”에 대한 고민이였습니다.
  • 명확한 정답은 없지만, 실제 View를 프론트라고 생각했을때 백엔드 애플리케이션이 실행되는 사이에서 DTO로 변환한다고 생각을 했고, 여기서는 그냥 단순히 값만 넘겨주는것으로 하였습니다.
import baseball.dto.CheckBallResponse;

public class OutputView {

    private OutputView() {
    }

    public static void printExitMessage() {
        System.out.println("애플리케이션이 종료되었습니다.");
    }

    public static void printResult(CheckBallResponse checkBallResponse) {
        if (checkBallResponse.isNotting()) {
            System.out.println("낫싱");
            return;
        }

        System.out.println(checkBallResponse.ballCount() + "볼 " + checkBallResponse.strikeCount() + "스트라이크");
        if (checkBallResponse.isSuccess()) {
            System.out.println("3개의 숫자를 모두 맞히셨습니다.");
            System.out.println("-------게임 종료-------");
        }
    }

    public static void printPickComputerNumbers() {
        System.out.println("컴퓨터가 숫자를 뽑았습니다.");
    }

    public static void printErrorMessage(String message) {
        System.out.println(message);
    }
}

  • 출력도 printResult가 메인인데, 우리가 실제 웹 애플리케이션에서 생각해봣을때도 프론트에서도 분기를 쳐서 보여주는 행동들을 하기 때문에 if문을 통해 처리하도록 하였습니다.

Application

package baseball;

import baseball.controller.BaseBallGameController;
import baseball.domain.Commend;
import baseball.dto.CheckBallResponse;
import baseball.dto.CheckBallsRequest;
import baseball.generator.BaseBallNumberGenerator;
import baseball.generator.BaseBallNumberShuffleGenerator;
import baseball.repository.ComputerRepositoryImpl;
import baseball.view.InputView;
import baseball.view.OutputView;

import java.util.List;

public class GameApplication {
    private static final BaseBallNumberGenerator baseBallNumberGenerator = new BaseBallNumberShuffleGenerator();
    private static final BaseBallGameController baseBallGameController = new BaseBallGameController(new ComputerRepositoryImpl());

    public static void run() {
        Commend commend = Commend.END;
        try {
            do {
                commend = Commend.of(InputView.inputMenu());
                int computerId = baseBallGameController.computerStart(baseBallNumberGenerator);
                OutputView.printPickComputerNumbers();
                gameInProgress(computerId);
            }
            while (commend != Commend.END);
            applicationEnd();
        } catch (Exception e) {
            OutputView.printErrorMessage(e.getMessage());
            run();
        }
    }

    private static void applicationEnd() {
        OutputView.printExitMessage();
    }

    private static void gameInProgress(int computerId) {
        try {
            boolean isFinished = true;

            while (isFinished) {
                List<Integer> userNumbers = InputView.inputNumbers();
                CheckBallsRequest checkBallsRequest = new CheckBallsRequest(userNumbers, computerId);
                CheckBallResponse checkBallDto = baseBallGameController.checkBalls(checkBallsRequest);
                OutputView.printResult(checkBallDto);
                isFinished = !checkBallDto.isSuccess();
            }
        } catch (Exception e) {
            OutputView.printErrorMessage(e.getMessage());
            gameInProgress(computerId);
        }
    }
}

  • 해당 메서드에선 각 View Controller를 연결해서 처리하는 행위를 해줍니다.
  • 실제 웹 애플리케이션과는 다르지만, 여기서 최대한 사용자 입력과 출력에 대한 부분들을 Controller에게 전달하는 것을 목표로 하였습니다.
  • 이렇게 따로 main메서드가 아닌 다른것으로 빼둔 이유는 차후 통합테스트를 작성을 목표로 하고 있어, 용이하게 하기 위함입니다.
  • 여기서는 baseBallNumberGenerator가 여기에 존재하는데 이 이유는 제어할수 없는 값은 최 상단 계층으로 올려 의존이 최소한이 되도록 하는것이 목표이기 때문에 이와 같은 로직으로 작성되었습니다.
public class Application {
    public static void main(String[] args) {
        GameApplication.run();
    }
}

  • 마지막 main 메서드에서는 GameApplication의 run 메서드만 호출하면 끝입니다.

간단한 회고

  • 아주 간단한 로직이 있는 게임을 만들어보는 경험을 해봤습니다.
  • 4년전 우아한테크코스 프리코스에서 진행했던 미션이였는데, 다시 함으로써 기억이 새록새록 났던거 같습니다.
  • 최대한 객체지향적으로 생각하면서 하려고 하니 간단한 로직이지만 복잡성은 있었다고 생각이 들지만, 그만큼 차후 Step2, Step3를 할때 편리함을 느끼고 있습니다.
  • 아울러 Step1을 할때 후회되었던 부분이 있습니다.
    • Computer를 Repository에 넣는게 아니라 Game이라는 객체를 만들어 Computer와 PlayerRecord(유저의 번호, 결과 등)의 Collection을 가지고 있어 처리하는 방식이 훨씬 더 확장성이 있고 더 의미있는 객체지향적인 사고였던거 같습니다.
    • Step2에서 같이 리팩토링을 진행할 예정입니다.
  • 아울러 테스트 코드의 커버리지를 신경쓰면서 진행 하면서 안정적인 애플리케이션을 목표로 가는것이 좋았습니다.

결론

  • 이제 다음 Step2로 넘어갑니다.
  • Step2의 요구사항은 종료가 되기 전까지 게임을 계속 할수 있는데, 종료된 게임의 기록을 볼수 있는 기능입니다. 또한 시작시간과, 종료시간, 횟수를 추가함으로써 진행된 Game의 추가정보를 볼수 있습니다.
  • 간단한 와이어 프레임을 같이 첨부합니다.
게임을 새로 시작하려면 1, 기록을 보려면 2, 종료하려면 9을 입력하세요.
1

컴퓨터가 숫자를 뽑았습니다.

숫자를 입력해주세요 : 123
1볼 1스트라이크
숫자를 입력해주세요: 145
1볼
숫자를 입력해주세요: 671
2볼
숫자를 입력해주세요: 216
1스트라이크
숫자를 입력해주세요: 713
3스트라이크

3개의 숫자를 모두 맞히셨습니다.
-------게임 종료-------

게임을 새로 시작하려면 1, 기록을 보려면 2, 종료하려면 9을 입력하세요.
2

게임 기록
[1] / 시작시간: 2024. 04. 07 23:12 / 종료시간: 2024. 04. 07 23:13 / 횟수: 5
1

1번 게임 결과
숫자를 입력해주세요 : 123
1볼 1스트라이크
숫자를 입력해주세요: 145
1볼
숫자를 입력해주세요: 671
2볼
숫자를 입력해주세요: 216
1스트라이크
숫자를 입력해주세요: 713
3스트라이크

3개의 숫자를 모두 맞히셨습니다.
-------기록 종료-------

게임을 새로 시작하려면 1, 기록을 보려면 2, 종료하려면 9을 입력하세요.
9

애플리케이션이 종료되었습니다
 

GitHub - ksy90101/java-baseball: Java로 진행하는 BaseBall 미션

Java로 진행하는 BaseBall 미션. Contribute to ksy90101/java-baseball development by creating an account on GitHub.

github.com

  • 이 글과 관련된 피드백이 있다면 언제든 댓글로 남겨주세요!
728x90
728x90