From 88bdaa5096ca86fedb043c68b95e655256eebbfa Mon Sep 17 00:00:00 2001 From: sapiensnoah Date: Wed, 28 Jan 2026 16:12:58 +0900 Subject: [PATCH 01/27] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20README=EC=97=90=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d7e8aee..dc3fae95 100644 --- a/README.md +++ b/README.md @@ -1 +1,50 @@ -# java-baseball-precourse \ No newline at end of file +# java-baseball-precourse + +크게 게임 초기화(컴퓨터), 사용자 입력, 게임 로직(판정), 게임 종료 및 재시작, 예외처리 5가지로 기능을 나누었습니다. + +1. 게임 초기화 (컴퓨터 숫자 생성) +2. 사용자 입력 +3. 게임 판정 및 힌트 출력 +4. 게임 종료 및 재시작 +5. 예외 처리 및 유효성 검사 + +## 기능 설명 +### 1. 게임 초기화 (컴퓨터 숫자 생성) +게임이 시작되면 컴퓨터는 아래의 규칙에 따라 숫자를 생성해야합니다. +- [ ] 랜덤 수 생성: 1부터 9까지의 서로 다른 수로 이루어진 3자리 수를 임의로 선택합니다. + - [ ] 각 자리의 숫자가 중복될 수 없다. + - [ ] 은 포함되지 않는다. + +### 2. 사용자 입력 +사용자로부터 3자리의 숫자를 입력받는 기능입니다. +- [ ] 입력 요청: `숫자를 입력 해주세요`와 같은 문구로 사용자의 입력을 유도합니다. +- [ ] 입력값 수신: 사용자가 입력한 문자열(숫자)를 받아옵니다. + +### 3. 게임 판정 및 힌트 출력 +컴퓨터의 수 플레이어의 수를 비교하여 결과를 계산하고 출력합니다. + +- [ ] 비교 로직 + - [ ] `스트라이크`: 같은 수가 같은 자리에 있는 경우 + - [ ] `볼`: 같은 수가 다른 자리에 있는 경우 + - [ ] `낫싱`: 같은 수가 전혀 없는 경우 +- [ ] 결과 출력 + - [ ] 계산된 볼과 스트라이크 개수를 출력합니다. + - [ ] 하나도 맞지 않을 경우 `낫싱`을 출력합니다. + - [ ] 스트라이크와 볼이 같이 있는 경우, 보통 볼을 먼저 출력하고 스트라이크를 나중에 출력하는 형식을 따릅니다. + +### 4. 게임 종료 및 재시작 +3개의 숫자를 모두 맞혔을 때의 처리 로직입니다. +- [ ] 정답 처리: 3스트라이크일 경우, `3개의 숫자를 모두 맞히셨습니다! 게임종료` 메세지를 출력합니다. +- [ ] 재시작/종료 선택: 게임 종료 후 재시작 여부를 묻습니다. +- [ ] `1` 입력 시: 게임을 처음부터 다시 시작. 새로운 랜덤 숫자 생성 +- [ ] `2` 입력 시: 애플리케이션 완전히 종료 + +### 5. 예외 처리 및 유효성 검사 +사용자가 잘못된 값을 입력했을 때 프로그램이 종료되지 않고 알림을 준 뒤 다시 진행되어야 합니다. +- [ ] 숫자가 아닌 문자가 포함된 경우. +- [ ] 3자리가 아닌 경우 (2자리 이하 또는 4자리 이상) +- [ ] 중복된 숫자가 있는 경우 (예: 112) +- [ ] 1~9 범위를 벗어난 숫자(0)가 포함된 경우 + +에러처리는 `[ERROR]`로 시작하는 에러 메시지를 출력합니다. +에러 발생 후 프로그램이 강제 종료되지 않아야 하고, 다시 `플레이어 입력 기능`으로 돌아가야합니다. \ No newline at end of file From cf5eca7c870b6d8c1d5a62738880d2caf2ecb6df Mon Sep 17 00:00:00 2001 From: sapiensnoah Date: Wed, 28 Jan 2026 16:54:25 +0900 Subject: [PATCH 02/27] =?UTF-8?q?feat:=20=EB=9E=9C=EB=8D=A4=20=EC=88=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +-- src/main/java/controller/GameController.java | 8 ++++ src/main/java/model/BaseballNumber.java | 47 +++++++++++++++++++ .../java/model/RandomNumberGenerator.java | 27 +++++++++++ 4 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 src/main/java/controller/GameController.java create mode 100644 src/main/java/model/BaseballNumber.java create mode 100644 src/main/java/model/RandomNumberGenerator.java diff --git a/README.md b/README.md index dc3fae95..a0e37523 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ ## 기능 설명 ### 1. 게임 초기화 (컴퓨터 숫자 생성) 게임이 시작되면 컴퓨터는 아래의 규칙에 따라 숫자를 생성해야합니다. -- [ ] 랜덤 수 생성: 1부터 9까지의 서로 다른 수로 이루어진 3자리 수를 임의로 선택합니다. - - [ ] 각 자리의 숫자가 중복될 수 없다. - - [ ] 은 포함되지 않는다. +- [x] 랜덤 수 생성: 1부터 9까지의 서로 다른 수로 이루어진 3자리 수를 임의로 선택합니다. + - [x] 각 자리의 숫자가 중복될 수 없다. + - [x] 0 은 포함되지 않는다. ### 2. 사용자 입력 사용자로부터 3자리의 숫자를 입력받는 기능입니다. @@ -47,4 +47,5 @@ - [ ] 1~9 범위를 벗어난 숫자(0)가 포함된 경우 에러처리는 `[ERROR]`로 시작하는 에러 메시지를 출력합니다. + 에러 발생 후 프로그램이 강제 종료되지 않아야 하고, 다시 `플레이어 입력 기능`으로 돌아가야합니다. \ No newline at end of file diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java new file mode 100644 index 00000000..72690835 --- /dev/null +++ b/src/main/java/controller/GameController.java @@ -0,0 +1,8 @@ +/** + * 애플리케이션 진입점 + */ +public class GameController { + public static void main(String[] args) { + System.out.println("asdfasdfasdfsdf"); + } +} diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java new file mode 100644 index 00000000..6988d5b9 --- /dev/null +++ b/src/main/java/model/BaseballNumber.java @@ -0,0 +1,47 @@ +import java.util.List; + +/** + * 숫자 3개를 담고 있는 객체 + * Computer, Referee, View 등의 객체에서 사용됩니다. + */ +public class BaseballNumber { + // 생성한 숫자라는 것은 숫자를 픽한것 뿐만 아니라 순서 정보도 가지고 있어야한다. + private final List numbers; + + public BaseballNumber(List numbers) { + validate(numbers); + this.numbers = numbers; + } + + private void validate(List numbers) { + validateSize(numbers); + validateRange(numbers); + validateDuplicate(numbers); + } + + private void validateSize(List numbers) { + // TODO: 검증로직 작성 + } + + private void validateRange(List numbers) { + // TODO: 검증로직 작성 + } + + private void validateDuplicate(List numbers) { + // TODO: 검증로직 작성 + } + + // 유틸리티 메서드들 + + //특정 위치의 숫자를 가져오기 + public int getNumber(int index) { + return numbers.get(index); + } + + // 특정 숫자가 포함되어 있는지 확인 + public boolean contain(int number) { + return numbers.contains(number); + } + + // TODO: 불변리스트 반환 메서드 작성 +} diff --git a/src/main/java/model/RandomNumberGenerator.java b/src/main/java/model/RandomNumberGenerator.java new file mode 100644 index 00000000..c50787ac --- /dev/null +++ b/src/main/java/model/RandomNumberGenerator.java @@ -0,0 +1,27 @@ +package model; + +import java.util.*; + +public class RandomNumberGenerator { + + private static RandomNumberGenerator instance = new RandomNumberGenerator(); + private static Random random = new Random(); + private HashSet numbers = new HashSet<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9)); + + + private RandomNumberGenerator() { + } + + public static RandomNumberGenerator getInstance() { + return instance; + } + + public BaseballNumber generate() { + Set uniqueNumbers = new LinkedHashSet<>(); + while (uniqueNumbers.size() < 3) { + uniqueNumbers.add(random.nextInt(9) + 1); + } + + return new BaseballNumber(new ArrayList<>(uniqueNumbers)); + } +} From 957b29320852ea59cae6505732047d0dd2c4dead Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 17:05:03 +0900 Subject: [PATCH 03/27] =?UTF-8?q?feat:=20=EB=9E=9C=EB=8D=A4=20=EC=88=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EA=B2=80=EC=A6=9D=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/BaseballNumber.java | 18 ++++++++++++++++++ src/main/java/model/Constant.java | 8 ++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/main/java/model/Constant.java diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index 6988d5b9..c7e8dbb0 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -1,4 +1,10 @@ +package model; + +import java.util.HashSet; import java.util.List; +import java.util.Set; + +import static model.Constant.*; /** * 숫자 3개를 담고 있는 객체 @@ -21,14 +27,26 @@ private void validate(List numbers) { private void validateSize(List numbers) { // TODO: 검증로직 작성 + if (numbers.size() != NUMBERS_SIZE) { + throw new IllegalArgumentException("[ERROR] 숫자는 3자리여야 합니다."); + } } private void validateRange(List numbers) { // TODO: 검증로직 작성 + for (int number: numbers) { + if (number < NUMBER_MIN_RANGE || number > NUMBER_MAX_RANGE) { + throw new IllegalArgumentException("[ERROR] 숫자는 1부터 9까지의 수여야 합니다."); + } + } } private void validateDuplicate(List numbers) { // TODO: 검증로직 작성 + Set nonDuplicateNumbers = new HashSet<>(numbers); + if (nonDuplicateNumbers.size() != NUMBERS_SIZE) { + throw new IllegalArgumentException("[ERROR] 숫자는 중복될 수 업습니다."); + } } // 유틸리티 메서드들 diff --git a/src/main/java/model/Constant.java b/src/main/java/model/Constant.java new file mode 100644 index 00000000..2c56485a --- /dev/null +++ b/src/main/java/model/Constant.java @@ -0,0 +1,8 @@ +package model; + +public class Constant { + + public static final int NUMBERS_SIZE = 3; + public static final int NUMBER_MIN_RANGE = 1; + public static final int NUMBER_MAX_RANGE = 9; +} From edada6c92be2fbe46b9224786ef78d6f90a2d966 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 17:39:23 +0900 Subject: [PATCH 04/27] =?UTF-8?q?feat:=20=EB=B9=84=EA=B5=90=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B2=B0=EA=B3=BC=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++++++++-------- src/main/java/dto/GameResult.java | 4 ++++ src/main/java/model/Referee.java | 4 ++++ src/main/java/view/InputView.java | 4 ++++ src/main/java/view/OutputView.java | 27 +++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 src/main/java/dto/GameResult.java create mode 100644 src/main/java/model/Referee.java create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/OutputView.java diff --git a/README.md b/README.md index a0e37523..9935ea1c 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ ### 3. 게임 판정 및 힌트 출력 컴퓨터의 수 플레이어의 수를 비교하여 결과를 계산하고 출력합니다. -- [ ] 비교 로직 - - [ ] `스트라이크`: 같은 수가 같은 자리에 있는 경우 - - [ ] `볼`: 같은 수가 다른 자리에 있는 경우 - - [ ] `낫싱`: 같은 수가 전혀 없는 경우 -- [ ] 결과 출력 - - [ ] 계산된 볼과 스트라이크 개수를 출력합니다. - - [ ] 하나도 맞지 않을 경우 `낫싱`을 출력합니다. - - [ ] 스트라이크와 볼이 같이 있는 경우, 보통 볼을 먼저 출력하고 스트라이크를 나중에 출력하는 형식을 따릅니다. +- [x] 비교 로직 + - [x] `스트라이크`: 같은 수가 같은 자리에 있는 경우 + - [x] `볼`: 같은 수가 다른 자리에 있는 경우 + - [x] `낫싱`: 같은 수가 전혀 없는 경우 +- [x] 결과 출력 + - [x] 계산된 볼과 스트라이크 개수를 출력합니다. + - [x] 하나도 맞지 않을 경우 `낫싱`을 출력합니다. + - [x] 스트라이크와 볼이 같이 있는 경우, 보통 볼을 먼저 출력하고 스트라이크를 나중에 출력하는 형식을 따릅니다. ### 4. 게임 종료 및 재시작 3개의 숫자를 모두 맞혔을 때의 처리 로직입니다. diff --git a/src/main/java/dto/GameResult.java b/src/main/java/dto/GameResult.java new file mode 100644 index 00000000..4787ec4f --- /dev/null +++ b/src/main/java/dto/GameResult.java @@ -0,0 +1,4 @@ +package dto; + +public class GameResult { +} diff --git a/src/main/java/model/Referee.java b/src/main/java/model/Referee.java new file mode 100644 index 00000000..c4003340 --- /dev/null +++ b/src/main/java/model/Referee.java @@ -0,0 +1,4 @@ +package model; + +public class Referee { +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000..2f71376e --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,4 @@ +package view; + +public class Inputview { +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000..bda191ad --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,27 @@ +package view; + +import dto.GameResult; + +public class OutputView { + + public void printResult(GameResult result) { + if (result.isNothing()) { + System.out.println("낫싱"); + return; + } + + StringBuilder sb = new StringBuilder(); + if (result.getBall() > 0) { + sb.append(result.getBall()).append("볼 "); + } + if (result.getStrike() > 0) { + sb.append(result.getStrike()).append("스트라이크 "); + } + + System.out.println(sb.toString().trim()); + } + + public void printGameSuccess() { + System.out.println("3개의 숫자를 모두 맞추셨습니다! 게임 종료"); + } +} From 7aa1606682aaa6edad47ac0b4a4a4cf98013a208 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 17:40:11 +0900 Subject: [PATCH 05/27] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=ED=8C=90?= =?UTF-8?q?=EC=A0=95=20=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Referee.java | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/model/Referee.java b/src/main/java/model/Referee.java index c4003340..079545f3 100644 --- a/src/main/java/model/Referee.java +++ b/src/main/java/model/Referee.java @@ -1,4 +1,26 @@ package model; +import dto.GameResult; + public class Referee { + + public GameResult judge(BaseballNumber computer, BaseballNumber user) { + int balls = 0; + int strikes = 0; + + for (int i = 0; i < Constant.NUMBERS_SIZE; i++) { + int userNumber = user.getNumber(i); + + // 스트라이크인지 확인 (위치와 숫자가 모두 같음); + if (computer.getNumber(i) == userNumber) { + strikes++; + continue; // 볼 확인은 건너뜀 + } + if (computer.contain(userNumber)) { + balls++; + } + } + + return new GameResult(balls, strikes); + } } From ec9efce9ae3794fa078c7c1345d3793f81ec7d2b Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:02:28 +0900 Subject: [PATCH 06/27] =?UTF-8?q?add:=20=EA=B2=8C=EC=9E=84=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/dto/GameResult.java | 23 +++++++++++++++++++++++ src/main/java/model/InputConverter.java | 4 ++++ 2 files changed, 27 insertions(+) create mode 100644 src/main/java/model/InputConverter.java diff --git a/src/main/java/dto/GameResult.java b/src/main/java/dto/GameResult.java index 4787ec4f..32135cf8 100644 --- a/src/main/java/dto/GameResult.java +++ b/src/main/java/dto/GameResult.java @@ -1,4 +1,27 @@ package dto; public class GameResult { + private final int ball; + private final int strike; + + public GameResult(int ball, int strike) { + this.ball = ball; + this.strike = strike; + } + + public boolean isNothing() { + return ball == 0 && strike == 0; + } + + public boolean isThreeStrike() { + return strike == 3; + } + + public int getBall() { + return ball; + } + + public int getStrike() { + return strike; + } } diff --git a/src/main/java/model/InputConverter.java b/src/main/java/model/InputConverter.java new file mode 100644 index 00000000..5cc0e73b --- /dev/null +++ b/src/main/java/model/InputConverter.java @@ -0,0 +1,4 @@ +package model; + +public class InputConverter { +} From ae602f6cda584e5532802a9030e33b0e5ed03bb8 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:03:15 +0900 Subject: [PATCH 07/27] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/InputConverter.java | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/model/InputConverter.java b/src/main/java/model/InputConverter.java index 5cc0e73b..ece2d58d 100644 --- a/src/main/java/model/InputConverter.java +++ b/src/main/java/model/InputConverter.java @@ -1,4 +1,27 @@ package model; +import java.util.ArrayList; +import java.util.List; + public class InputConverter { + + private InputConverter() {} + + public static List convertStringToIntegerList(String input) { + // 사용자의 공백 입력 실수 방지 + String trimmedInput = input.trim(); + + List numbers = new ArrayList<>(); + + for (char character : trimmedInput.toCharArray()) { + if (!Character.isDigit(character)) { + throw new IllegalArgumentException("[ERROR] 숫자만 입력해주세요"); + } + + int number = Character.getNumericValue(character); + numbers.add(number); + } + + return numbers; + } } From 265cdf5d6f5d0d369db55950a999d3f55d825f6d Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:03:42 +0900 Subject: [PATCH 08/27] =?UTF-8?q?feat:=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EB=B7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/view/OutputView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index bda191ad..052ceaab 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -24,4 +24,8 @@ public void printResult(GameResult result) { public void printGameSuccess() { System.out.println("3개의 숫자를 모두 맞추셨습니다! 게임 종료"); } + + public void printError(String message) { + System.out.println(message); + } } From 6079eb449b19c1d642616e34e5acfed69a089c43 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:06:20 +0900 Subject: [PATCH 09/27] =?UTF-8?q?chore:=20=EC=83=9D=EC=84=B1=EC=9E=90=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/RandomNumberGenerator.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/model/RandomNumberGenerator.java b/src/main/java/model/RandomNumberGenerator.java index c50787ac..6678e951 100644 --- a/src/main/java/model/RandomNumberGenerator.java +++ b/src/main/java/model/RandomNumberGenerator.java @@ -4,16 +4,10 @@ public class RandomNumberGenerator { - private static RandomNumberGenerator instance = new RandomNumberGenerator(); - private static Random random = new Random(); - private HashSet numbers = new HashSet<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9)); + private final Random random; - - private RandomNumberGenerator() { - } - - public static RandomNumberGenerator getInstance() { - return instance; + public RandomNumberGenerator() { + this.random = new Random(); } public BaseballNumber generate() { From 09561bec6f1f89ae3be1c42e8d7701ddafb0eb3d Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:06:50 +0900 Subject: [PATCH 10/27] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=ED=81=B4=EB=9E=98=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/view/InputView.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 2f71376e..8819a499 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,4 +1,20 @@ package view; -public class Inputview { +import java.util.Scanner; + +public class InputView { + private static final String INPUT_MESSAGE = "숫자를 입력해 주세요 : "; + private final Scanner scanner; + + public InputView() { + this.scanner = new Scanner(System.in); + } + + public String inputNumbers() { + System.out.println(INPUT_MESSAGE); + + String input = scanner.nextLine(); + + return input.trim(); + } } From e6d3e7d52716edaeb20802f143215a018319898f Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:07:09 +0900 Subject: [PATCH 11/27] =?UTF-8?q?chore:=20=EB=B6=88=EB=B3=80=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B0=98=ED=99=98=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/BaseballNumber.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index c7e8dbb0..00ae1cdc 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -1,5 +1,6 @@ package model; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -62,4 +63,7 @@ public boolean contain(int number) { } // TODO: 불변리스트 반환 메서드 작성 + public List getNumbers() { + return Collections.unmodifiableList(numbers); + } } From 42fd1366b83a39f17a40b21ff1ad0cbad3fdde8d Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:07:20 +0900 Subject: [PATCH 12/27] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +-- src/main/java/controller/GameController.java | 52 ++++++++++++++++++-- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9935ea1c..c7dcce43 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,10 @@ ### 5. 예외 처리 및 유효성 검사 사용자가 잘못된 값을 입력했을 때 프로그램이 종료되지 않고 알림을 준 뒤 다시 진행되어야 합니다. -- [ ] 숫자가 아닌 문자가 포함된 경우. -- [ ] 3자리가 아닌 경우 (2자리 이하 또는 4자리 이상) -- [ ] 중복된 숫자가 있는 경우 (예: 112) -- [ ] 1~9 범위를 벗어난 숫자(0)가 포함된 경우 +- [x] 숫자가 아닌 문자가 포함된 경우. +- [x] 3자리가 아닌 경우 (2자리 이하 또는 4자리 이상) +- [x] 중복된 숫자가 있는 경우 (예: 112) +- [x] 1~9 범위를 벗어난 숫자(0)가 포함된 경우 에러처리는 `[ERROR]`로 시작하는 에러 메시지를 출력합니다. diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 72690835..d32a0c29 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,8 +1,50 @@ -/** - * 애플리케이션 진입점 - */ +package controller; + +import dto.GameResult; +import model.BaseballNumber; +import model.InputConverter; +import model.RandomNumberGenerator; +import model.Referee; +import view.InputView; +import view.OutputView; + + public class GameController { - public static void main(String[] args) { - System.out.println("asdfasdfasdfsdf"); + + private final InputView inputView; + private final OutputView outputView; + private final RandomNumberGenerator randomNumberGenerator; + private final Referee referee; + + public GameController() { + this.inputView = new InputView(); + this.outputView = new OutputView(); + this.randomNumberGenerator = new RandomNumberGenerator(); + this.referee = new Referee(); } + + public void run() { + BaseballNumber computerNumber = randomNumberGenerator.generate(); + + System.out.println("컴퓨터 숫자 생성 완료 (테스트용): " + computerNumber.getNumbers()); + } + + private void play(BaseballNumber computerNumber) { + while(true) { + try { + String inputString = inputView.inputNumbers(); + BaseballNumber userNumber = new BaseballNumber(InputConverter.convertStringToIntegerList(inputString)); + GameResult result = referee.judge(computerNumber, userNumber); + outputView.printResult(result); + + if (result.isThreeStrike()) { + outputView.printGameSuccess(); + break; + } + } catch (IllegalArgumentException e) { + outputView.printError(e.getMessage()); + } + } + } + } From 2c20035d03556fae4ba9f849980aadc69312661c Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:09:24 +0900 Subject: [PATCH 13/27] =?UTF-8?q?feat:=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=A7=84=EC=9E=85=EC=A0=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/Application.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000..6ff8c393 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,9 @@ +import controller.GameController; + +public class Application { + public static void main(String[] args) { + GameController gameController = new GameController(); + + gameController.run(); + } +} From 80b587758ad67b888fe3c3ff090c74265538001b Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:12:14 +0900 Subject: [PATCH 14/27] =?UTF-8?q?feat:=20=EC=9E=AC=EC=8B=9C=EB=8F=84=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/view/InputView.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 8819a499..65870abf 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -4,6 +4,7 @@ public class InputView { private static final String INPUT_MESSAGE = "숫자를 입력해 주세요 : "; + private static final String RESTART_MESSAGE = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; private final Scanner scanner; public InputView() { @@ -17,4 +18,9 @@ public String inputNumbers() { return input.trim(); } + + public String inputRestartCommand() { + System.out.println(RESTART_MESSAGE); // 안내 문구 출력 + return scanner.nextLine().trim(); // 입력받은 값(1 또는 2) 반환 + } } From cdc080e931dbb6a8489c5b98ce4fcea2fe2595c2 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:12:27 +0900 Subject: [PATCH 15/27] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=EC=8B=9C?= =?UTF-8?q?=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/GameController.java | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index d32a0c29..ae9cd8e3 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -24,9 +24,25 @@ public GameController() { } public void run() { - BaseballNumber computerNumber = randomNumberGenerator.generate(); + System.out.println("숫자 야구 게임을 시작합니다."); // 게임 시작 문구 (선택 사항) - System.out.println("컴퓨터 숫자 생성 완료 (테스트용): " + computerNumber.getNumbers()); + // 게임 전체 반복 (재시작 로직) + while (true) { + // 1. 컴퓨터 숫자 생성 + BaseballNumber computerNumber = randomNumberGenerator.generate(); + + // 2. 한 판 플레이 (3스트라이크 맞출 때까지 반복) + play(computerNumber); + + // 3. 게임 종료 후 재시작 여부 확인 + // "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요." + String restartCommand = inputView.inputRestartCommand(); + + if (restartCommand.equals("2")) { + break; // while문 탈출 -> 프로그램 종료 + } + // 1번이면 while문 처음으로 돌아가서 새 숫자 생성 + } } private void play(BaseballNumber computerNumber) { From 57069736fa29f1437c9adc45c7ad3673aa387fe3 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:13:09 +0900 Subject: [PATCH 16/27] =?UTF-8?q?fix:=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/BaseballNumber.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index 00ae1cdc..af0e9579 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -46,7 +46,7 @@ private void validateDuplicate(List numbers) { // TODO: 검증로직 작성 Set nonDuplicateNumbers = new HashSet<>(numbers); if (nonDuplicateNumbers.size() != NUMBERS_SIZE) { - throw new IllegalArgumentException("[ERROR] 숫자는 중복될 수 업습니다."); + throw new IllegalArgumentException("[ERROR] 숫자는 중복될 수 없습니다."); } } From be8efd87abdac078ddee0dcea543effcfd3bfaa4 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:22:40 +0900 Subject: [PATCH 17/27] =?UTF-8?q?add:=20=EC=97=90=EB=9F=AC=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=83=81=EC=88=98=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/BaseballNumber.java | 9 +++++---- src/main/java/model/ErrorMessage.java | 11 +++++++++++ .../java/model/{Constant.java => GameConfig.java} | 2 +- src/main/java/model/InputConverter.java | 4 +++- src/main/java/model/Referee.java | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 src/main/java/model/ErrorMessage.java rename src/main/java/model/{Constant.java => GameConfig.java} (86%) diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index af0e9579..516ace1b 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -5,7 +5,8 @@ import java.util.List; import java.util.Set; -import static model.Constant.*; +import static model.ErrorMessage.*; +import static model.GameConfig.*; /** * 숫자 3개를 담고 있는 객체 @@ -29,7 +30,7 @@ private void validate(List numbers) { private void validateSize(List numbers) { // TODO: 검증로직 작성 if (numbers.size() != NUMBERS_SIZE) { - throw new IllegalArgumentException("[ERROR] 숫자는 3자리여야 합니다."); + throw new IllegalArgumentException(INVALID_SIZE); } } @@ -37,7 +38,7 @@ private void validateRange(List numbers) { // TODO: 검증로직 작성 for (int number: numbers) { if (number < NUMBER_MIN_RANGE || number > NUMBER_MAX_RANGE) { - throw new IllegalArgumentException("[ERROR] 숫자는 1부터 9까지의 수여야 합니다."); + throw new IllegalArgumentException(OUT_OF_RANGE); } } } @@ -46,7 +47,7 @@ private void validateDuplicate(List numbers) { // TODO: 검증로직 작성 Set nonDuplicateNumbers = new HashSet<>(numbers); if (nonDuplicateNumbers.size() != NUMBERS_SIZE) { - throw new IllegalArgumentException("[ERROR] 숫자는 중복될 수 없습니다."); + throw new IllegalArgumentException(DUPLICATE_NUMBER); } } diff --git a/src/main/java/model/ErrorMessage.java b/src/main/java/model/ErrorMessage.java new file mode 100644 index 00000000..30579b6a --- /dev/null +++ b/src/main/java/model/ErrorMessage.java @@ -0,0 +1,11 @@ +package model; + +public class ErrorMessage { + public static final String ERROR_PREFIX = "[ERROR] "; + public static final String NOT_NUMBER = ERROR_PREFIX + "숫자만 입력해주세요"; + public static final String OUT_OF_RANGE = ERROR_PREFIX + "숫자는 1부터 9까지의 수여야 합니다."; + public static final String DUPLICATE_NUMBER = ERROR_PREFIX + "숫자는 중복될 수 없습니다."; + public static final String INVALID_SIZE = ERROR_PREFIX + "숫자는 3자리여야 합니다."; + + private ErrorMessage() { } // 인스턴스 생성 방지 +} \ No newline at end of file diff --git a/src/main/java/model/Constant.java b/src/main/java/model/GameConfig.java similarity index 86% rename from src/main/java/model/Constant.java rename to src/main/java/model/GameConfig.java index 2c56485a..dce894d7 100644 --- a/src/main/java/model/Constant.java +++ b/src/main/java/model/GameConfig.java @@ -1,6 +1,6 @@ package model; -public class Constant { +public class GameConfig { public static final int NUMBERS_SIZE = 3; public static final int NUMBER_MIN_RANGE = 1; diff --git a/src/main/java/model/InputConverter.java b/src/main/java/model/InputConverter.java index ece2d58d..eb2b0c33 100644 --- a/src/main/java/model/InputConverter.java +++ b/src/main/java/model/InputConverter.java @@ -3,6 +3,8 @@ import java.util.ArrayList; import java.util.List; +import static model.ErrorMessage.*; + public class InputConverter { private InputConverter() {} @@ -15,7 +17,7 @@ public static List convertStringToIntegerList(String input) { for (char character : trimmedInput.toCharArray()) { if (!Character.isDigit(character)) { - throw new IllegalArgumentException("[ERROR] 숫자만 입력해주세요"); + throw new IllegalArgumentException(NOT_NUMBER); } int number = Character.getNumericValue(character); diff --git a/src/main/java/model/Referee.java b/src/main/java/model/Referee.java index 079545f3..52fdfe1a 100644 --- a/src/main/java/model/Referee.java +++ b/src/main/java/model/Referee.java @@ -8,7 +8,7 @@ public GameResult judge(BaseballNumber computer, BaseballNumber user) { int balls = 0; int strikes = 0; - for (int i = 0; i < Constant.NUMBERS_SIZE; i++) { + for (int i = 0; i < GameConfig.NUMBERS_SIZE; i++) { int userNumber = user.getNumber(i); // 스트라이크인지 확인 (위치와 숫자가 모두 같음); From 7fa2b0d5a860268db69aeb741f44ac03283c233d Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Wed, 28 Jan 2026 18:25:26 +0900 Subject: [PATCH 18/27] =?UTF-8?q?fix:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/{model => constant}/ErrorMessage.java | 2 +- src/main/java/{model => constant}/GameConfig.java | 2 +- src/main/java/controller/GameController.java | 8 ++++---- src/main/java/{model => domain}/BaseballNumber.java | 6 +++--- .../java/{model => domain}/RandomNumberGenerator.java | 2 +- src/main/java/{model => domain}/Referee.java | 3 ++- src/main/java/{model => utils}/InputConverter.java | 4 ++-- 7 files changed, 14 insertions(+), 13 deletions(-) rename src/main/java/{model => constant}/ErrorMessage.java (96%) rename src/main/java/{model => constant}/GameConfig.java (90%) rename src/main/java/{model => domain}/BaseballNumber.java (95%) rename src/main/java/{model => domain}/RandomNumberGenerator.java (96%) rename src/main/java/{model => domain}/Referee.java (93%) rename src/main/java/{model => utils}/InputConverter.java (92%) diff --git a/src/main/java/model/ErrorMessage.java b/src/main/java/constant/ErrorMessage.java similarity index 96% rename from src/main/java/model/ErrorMessage.java rename to src/main/java/constant/ErrorMessage.java index 30579b6a..92563593 100644 --- a/src/main/java/model/ErrorMessage.java +++ b/src/main/java/constant/ErrorMessage.java @@ -1,4 +1,4 @@ -package model; +package constant; public class ErrorMessage { public static final String ERROR_PREFIX = "[ERROR] "; diff --git a/src/main/java/model/GameConfig.java b/src/main/java/constant/GameConfig.java similarity index 90% rename from src/main/java/model/GameConfig.java rename to src/main/java/constant/GameConfig.java index dce894d7..157ae887 100644 --- a/src/main/java/model/GameConfig.java +++ b/src/main/java/constant/GameConfig.java @@ -1,4 +1,4 @@ -package model; +package constant; public class GameConfig { diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index ae9cd8e3..11e8dbd2 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,10 +1,10 @@ package controller; import dto.GameResult; -import model.BaseballNumber; -import model.InputConverter; -import model.RandomNumberGenerator; -import model.Referee; +import domain.BaseballNumber; +import utils.InputConverter; +import domain.RandomNumberGenerator; +import domain.Referee; import view.InputView; import view.OutputView; diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/domain/BaseballNumber.java similarity index 95% rename from src/main/java/model/BaseballNumber.java rename to src/main/java/domain/BaseballNumber.java index 516ace1b..2e0e9b53 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/domain/BaseballNumber.java @@ -1,12 +1,12 @@ -package model; +package domain; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import static model.ErrorMessage.*; -import static model.GameConfig.*; +import static constant.ErrorMessage.*; +import static constant.GameConfig.*; /** * 숫자 3개를 담고 있는 객체 diff --git a/src/main/java/model/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java similarity index 96% rename from src/main/java/model/RandomNumberGenerator.java rename to src/main/java/domain/RandomNumberGenerator.java index 6678e951..b58535e4 100644 --- a/src/main/java/model/RandomNumberGenerator.java +++ b/src/main/java/domain/RandomNumberGenerator.java @@ -1,4 +1,4 @@ -package model; +package domain; import java.util.*; diff --git a/src/main/java/model/Referee.java b/src/main/java/domain/Referee.java similarity index 93% rename from src/main/java/model/Referee.java rename to src/main/java/domain/Referee.java index 52fdfe1a..b5361a2b 100644 --- a/src/main/java/model/Referee.java +++ b/src/main/java/domain/Referee.java @@ -1,5 +1,6 @@ -package model; +package domain; +import constant.GameConfig; import dto.GameResult; public class Referee { diff --git a/src/main/java/model/InputConverter.java b/src/main/java/utils/InputConverter.java similarity index 92% rename from src/main/java/model/InputConverter.java rename to src/main/java/utils/InputConverter.java index eb2b0c33..13e0f985 100644 --- a/src/main/java/model/InputConverter.java +++ b/src/main/java/utils/InputConverter.java @@ -1,9 +1,9 @@ -package model; +package utils; import java.util.ArrayList; import java.util.List; -import static model.ErrorMessage.*; +import static constant.ErrorMessage.*; public class InputConverter { From 2774dcf44724fbf907b35a4a31ac8625030cd0d4 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Fri, 30 Jan 2026 01:59:07 +0900 Subject: [PATCH 19/27] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/BaseballNumber.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/domain/BaseballNumber.java b/src/main/java/domain/BaseballNumber.java index 2e0e9b53..a2e8cd45 100644 --- a/src/main/java/domain/BaseballNumber.java +++ b/src/main/java/domain/BaseballNumber.java @@ -28,14 +28,12 @@ private void validate(List numbers) { } private void validateSize(List numbers) { - // TODO: 검증로직 작성 if (numbers.size() != NUMBERS_SIZE) { throw new IllegalArgumentException(INVALID_SIZE); } } private void validateRange(List numbers) { - // TODO: 검증로직 작성 for (int number: numbers) { if (number < NUMBER_MIN_RANGE || number > NUMBER_MAX_RANGE) { throw new IllegalArgumentException(OUT_OF_RANGE); @@ -44,7 +42,6 @@ private void validateRange(List numbers) { } private void validateDuplicate(List numbers) { - // TODO: 검증로직 작성 Set nonDuplicateNumbers = new HashSet<>(numbers); if (nonDuplicateNumbers.size() != NUMBERS_SIZE) { throw new IllegalArgumentException(DUPLICATE_NUMBER); @@ -63,7 +60,6 @@ public boolean contain(int number) { return numbers.contains(number); } - // TODO: 불변리스트 반환 메서드 작성 public List getNumbers() { return Collections.unmodifiableList(numbers); } From dde095cab4472b3241d6026854931bbced927859 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Fri, 30 Jan 2026 18:39:07 +0900 Subject: [PATCH 20/27] =?UTF-8?q?feat:=20=EC=88=AB=EC=9E=90=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EA=B2=80=EC=A6=9D=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/utils/InputConverter.java | 37 ++++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/main/java/utils/InputConverter.java b/src/main/java/utils/InputConverter.java index 13e0f985..63d509e0 100644 --- a/src/main/java/utils/InputConverter.java +++ b/src/main/java/utils/InputConverter.java @@ -10,20 +10,43 @@ public class InputConverter { private InputConverter() {} public static List convertStringToIntegerList(String input) { - // 사용자의 공백 입력 실수 방지 + // null 및 공백처리 + if (input == null || input.isBlank()) { + throw new IllegalArgumentException(NOT_NUMBER); + } + String trimmedInput = input.trim(); + // 길이검증 + validateLength(trimmedInput); List numbers = new ArrayList<>(); - for (char character : trimmedInput.toCharArray()) { - if (!Character.isDigit(character)) { - throw new IllegalArgumentException(NOT_NUMBER); - } - + for (char character: trimmedInput.toCharArray()) { + validateDigit(character); int number = Character.getNumericValue(character); + validateRange(number); numbers.add(number); } - return numbers; } + + private static void validateLength(String input) { + if (input.length() != 3) { + throw new IllegalArgumentException(OUT_OF_RANGE); + } + } + + private static void validateDigit(char number) { + if (!Character.isDigit(number)) { + throw new IllegalArgumentException(OUT_OF_RANGE); + } + } + + private static void validateRange(int number) { + if (number == 0) { + throw new IllegalArgumentException(OUT_OF_RANGE); + } + } + + } From db94990d7d05828a7b6c6b598d3e4077343512ab Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Sat, 31 Jan 2026 00:27:33 +0900 Subject: [PATCH 21/27] =?UTF-8?q?test:=20input=5Fconverter=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/utils/InputConverter.java | 4 +- src/test/java/utils/InputConverterTest.java | 91 +++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/test/java/utils/InputConverterTest.java diff --git a/src/main/java/utils/InputConverter.java b/src/main/java/utils/InputConverter.java index 63d509e0..1c109213 100644 --- a/src/main/java/utils/InputConverter.java +++ b/src/main/java/utils/InputConverter.java @@ -1,5 +1,7 @@ package utils; +import constant.ErrorMessage; + import java.util.ArrayList; import java.util.List; @@ -32,7 +34,7 @@ public static List convertStringToIntegerList(String input) { private static void validateLength(String input) { if (input.length() != 3) { - throw new IllegalArgumentException(OUT_OF_RANGE); + throw new IllegalArgumentException(INVALID_SIZE); } } diff --git a/src/test/java/utils/InputConverterTest.java b/src/test/java/utils/InputConverterTest.java new file mode 100644 index 00000000..1f49f146 --- /dev/null +++ b/src/test/java/utils/InputConverterTest.java @@ -0,0 +1,91 @@ +package utils; + +import constant.ErrorMessage; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +class InputConverterTest { + + @Test + @DisplayName("정상 입력: 3자리 숫자가 입력되면 리스트 [1,2,3]을 반환한다.") + void convertSuccess() { + // given + String input = "123"; + + // when + List result = InputConverter.convertStringToIntegerList(input); + + // then + assertThat(result) + .hasSize(3) + .containsExactly(1, 2, 3); + } + + @Test + @DisplayName("예외 발생: 입력값의 길이가 3자리가 아니면(2자리) 예외가 발생한다.") + void convertFailShortLength() { + // given + String input = "12"; + + // when & then + assertThatThrownBy(() -> InputConverter.convertStringToIntegerList(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(ErrorMessage.INVALID_SIZE); + } + + @Test + @DisplayName("예외 발생: 입력값의 길이가 3자리가 아니면(4자리) 예외가 발생한다.") + void convertFailLongLength() { + // given + String input = "1234"; + + // when & then + assertThatThrownBy(() -> InputConverter.convertStringToIntegerList(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(ErrorMessage.INVALID_SIZE); + } + + @Test + @DisplayName("예외 발생: 0이 포함되면 예외가 발생한다.") + void convertFailContainsZero() { + // given + String input = "012"; + + // when & then + assertThatThrownBy(() -> InputConverter.convertStringToIntegerList(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(ErrorMessage.OUT_OF_RANGE); + } + + @Test + @DisplayName("예외 발생: 숫자가 아닌 문자가 포함되면 예외가 발생한다.") + void convertFailNonNumeric() { + // given + String input = "1a3"; + + // when & then + assertThatThrownBy(() -> InputConverter.convertStringToIntegerList(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(ErrorMessage.OUT_OF_RANGE); + + } + + @Test + @DisplayName("엣지 케이스: 앞뒤 공백은 제거하고 숫자만 3개라면 통과한다.") + void convertSuccessWithWhitespace() { + // given + String input = " 123 "; + + // when & then + List result = InputConverter.convertStringToIntegerList(input); + assertThat(result) + .hasSize(3) + .containsExactly(1, 2, 3); + } + +} \ No newline at end of file From e89ed850e2836dd5e87a372521e0310f0b293832 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Sat, 31 Jan 2026 00:50:05 +0900 Subject: [PATCH 22/27] =?UTF-8?q?test:=20referee=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/RefereeTest.java | 57 +++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/test/java/domain/RefereeTest.java diff --git a/src/test/java/domain/RefereeTest.java b/src/test/java/domain/RefereeTest.java new file mode 100644 index 00000000..da0264c5 --- /dev/null +++ b/src/test/java/domain/RefereeTest.java @@ -0,0 +1,57 @@ +package domain; + +import dto.GameResult; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +class RefereeTest { + // judge 메서드를 테스트 해야함. + // 반환되는 GameResult 인스턴스에서 결과를 추출하고, + // 올바른 볼, 스트라이크 결과를 넘겨주는지에 대해 중점적으로 테스트한다. + // 잘못된 입력이 주어지는 예외 케이스에 대해서는 InputConverter에서 검사하기 때문에 따로 테스트하지 않는다. + + // 입력값과 기댓값만 바뀌고 검증로직은 똑같기 때문에 파라미터화 테스트가 적합하다고 생각했습니다. + + @DisplayName("다양한 경기 결과 판정 테스트") + @ParameterizedTest(name = "컴퓨터:{0}, 유저:{1} -> {2}볼 {3}스트라이크") + @CsvSource({ + "123, 123, 0, 3", // 3 스트라이크 + "123, 124, 0, 2", // 2 스트라이크 + "123, 456, 0, 0", // 낫싱 (0볼 0스트라이크) + "123, 312, 3, 0", // 3 볼 + "123, 132, 2, 1", // 2볼 1스트라이크 + "123, 415, 1, 0" // 1볼 + }) + void judge_parameterized_test(String computerStr, String userStr, int expectedBall, int expectedStrike) { + // given + BaseballNumber computer = new BaseballNumber(toList(computerStr)); + BaseballNumber user = new BaseballNumber(toList(userStr)); + Referee referee = new Referee(); + + // when + GameResult result = referee.judge(computer, user); + + // then + // 볼과 스트라이크 개수를 동시에 검증 + assertThat(result) + .extracting("ball", "strike") // 필드명 혹은 getter 이름 + .containsExactly(expectedBall, expectedStrike); + } + + private List toList(String input) { + List numbers = new ArrayList<>(); + for (String s : input.split("")) { + numbers.add(Integer.parseInt(s)); + } + return numbers; + } +} \ No newline at end of file From e8013d46c04ee9c7030dfd3bf1556b0ac486588c Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Sat, 31 Jan 2026 00:59:56 +0900 Subject: [PATCH 23/27] test: random_number_generator test --- .../domain/RandomNumberGeneratorTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/test/java/domain/RandomNumberGeneratorTest.java diff --git a/src/test/java/domain/RandomNumberGeneratorTest.java b/src/test/java/domain/RandomNumberGeneratorTest.java new file mode 100644 index 00000000..c159b003 --- /dev/null +++ b/src/test/java/domain/RandomNumberGeneratorTest.java @@ -0,0 +1,24 @@ +package domain; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +class RandomNumberGeneratorTest { + + // 3개의 숫자가 담긴 BaseballNumber를 반환해주는지만 간단하게 테스트하면 좋을 것 같습니다. + @Test + @DisplayName("랜덤 숫자 3개 생성") + void testGenerateRandomThreeNumber() { + RandomNumberGenerator generator = new RandomNumberGenerator(); + BaseballNumber numbers = generator.generate(); + + assertThat(numbers.getNumbers()) + .isNotNull() + .hasSize(3); + } + +} \ No newline at end of file From 427983d581d7db30da5e1f7c16c33396546fb3f5 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Sat, 31 Jan 2026 01:07:10 +0900 Subject: [PATCH 24/27] test: baseball_number test --- src/test/java/domain/BaseballNumberTest.java | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/test/java/domain/BaseballNumberTest.java diff --git a/src/test/java/domain/BaseballNumberTest.java b/src/test/java/domain/BaseballNumberTest.java new file mode 100644 index 00000000..299b253a --- /dev/null +++ b/src/test/java/domain/BaseballNumberTest.java @@ -0,0 +1,50 @@ +package domain; + +import constant.ErrorMessage; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class BaseballNumberTest { + + // BaseballNumber 객체를 생성하면서 검증로직도 함께 수행되기 때문에 + // 정상적으로 생성되는지, 검증로직이 제대로 동작하는지 테스트하면 좋을 것 같습니다. + + @Test + @DisplayName("성공: 정상적으로 객체를 생성") + void success() { + BaseballNumber numbers = new BaseballNumber(List.of(1, 2, 3)); + assertThat(numbers.getNumbers()) + .hasSize(3) + .containsExactly(1, 2, 3); + } + + // 정상적인 사이즈가 아닌경우, 중복된 원소가 있는경우, 1~9 사이의 범위를 벗어난 경우 예외가 발생합니다. + @Test + @DisplayName("예외: 사이즈가 4개인 야구번호 생성 시도") + void failWrongSize() { + assertThatThrownBy(() -> new BaseballNumber(List.of(1, 2, 3, 4))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(ErrorMessage.INVALID_SIZE); + } + + @Test + @DisplayName("예외: 중복된 원소로 야구번호 생성 시도") + void failDuplicateNumber() { + assertThatThrownBy(() -> new BaseballNumber(List.of(1, 1, 2))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(ErrorMessage.DUPLICATE_NUMBER); + } + + @Test + @DisplayName("예외: 1~9 이외의 범위로 야구번호 생성 시도") + void failWrongRange() { + assertThatThrownBy(() -> new BaseballNumber(List.of(0, 1, 2))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(ErrorMessage.OUT_OF_RANGE); + } + +} \ No newline at end of file From a0ea641f8c02b85ea5869c09d8d603a0705c9296 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Sat, 31 Jan 2026 01:13:16 +0900 Subject: [PATCH 25/27] refactor: gamecontroller di --- src/main/java/Application.java | 11 ++++++++++- src/main/java/controller/GameController.java | 15 ++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 6ff8c393..e031c5ce 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,8 +1,17 @@ import controller.GameController; +import domain.RandomNumberGenerator; +import domain.Referee; +import view.InputView; +import view.OutputView; public class Application { public static void main(String[] args) { - GameController gameController = new GameController(); + GameController gameController = new GameController( + new InputView(), + new OutputView(), + new RandomNumberGenerator(), + new Referee() + ); gameController.run(); } diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 11e8dbd2..1ebd95e3 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -16,11 +16,16 @@ public class GameController { private final RandomNumberGenerator randomNumberGenerator; private final Referee referee; - public GameController() { - this.inputView = new InputView(); - this.outputView = new OutputView(); - this.randomNumberGenerator = new RandomNumberGenerator(); - this.referee = new Referee(); + public GameController( + InputView inputView, + OutputView outputView, + RandomNumberGenerator randomNumberGenerator, + Referee referee + ) { + this.inputView = inputView; + this.outputView = outputView; + this.randomNumberGenerator = randomNumberGenerator; + this.referee = referee; } public void run() { From f00409f48dd9007da696a7e346b6dc9b70768b2a Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Sat, 31 Jan 2026 10:42:58 +0900 Subject: [PATCH 26/27] =?UTF-8?q?refactor:=20=EC=9E=85=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=20=EB=B0=8F=20=EC=88=AB=EC=9E=90=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=20&=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC?= =?UTF-8?q?=EC=9E=85=20=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 8 ++--- src/main/java/controller/GameController.java | 6 ++-- src/main/java/domain/NumberGenerator.java | 6 ++++ .../java/domain/RandomNumberGenerator.java | 2 +- src/main/java/view/ConsoleInputView.java | 26 ++++++++++++++++ src/main/java/view/ConsoleOutputView.java | 31 +++++++++++++++++++ src/main/java/view/InputView.java | 25 ++------------- src/main/java/view/OutputView.java | 29 +++-------------- 8 files changed, 79 insertions(+), 54 deletions(-) create mode 100644 src/main/java/domain/NumberGenerator.java create mode 100644 src/main/java/view/ConsoleInputView.java create mode 100644 src/main/java/view/ConsoleOutputView.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java index e031c5ce..c4fa534e 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,14 +1,14 @@ import controller.GameController; import domain.RandomNumberGenerator; import domain.Referee; -import view.InputView; -import view.OutputView; +import view.ConsoleInputView; +import view.ConsoleOutputView; public class Application { public static void main(String[] args) { GameController gameController = new GameController( - new InputView(), - new OutputView(), + new ConsoleInputView(), + new ConsoleOutputView(), new RandomNumberGenerator(), new Referee() ); diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 1ebd95e3..216b389a 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,10 +1,12 @@ package controller; +import domain.NumberGenerator; import dto.GameResult; import domain.BaseballNumber; import utils.InputConverter; import domain.RandomNumberGenerator; import domain.Referee; +import view.ConsoleOutputView; import view.InputView; import view.OutputView; @@ -13,13 +15,13 @@ public class GameController { private final InputView inputView; private final OutputView outputView; - private final RandomNumberGenerator randomNumberGenerator; + private final NumberGenerator randomNumberGenerator; private final Referee referee; public GameController( InputView inputView, OutputView outputView, - RandomNumberGenerator randomNumberGenerator, + NumberGenerator randomNumberGenerator, Referee referee ) { this.inputView = inputView; diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/domain/NumberGenerator.java new file mode 100644 index 00000000..4d01ebc2 --- /dev/null +++ b/src/main/java/domain/NumberGenerator.java @@ -0,0 +1,6 @@ +package domain; + +public interface NumberGenerator { + + public BaseballNumber generate(); +} diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java index b58535e4..fd15a27b 100644 --- a/src/main/java/domain/RandomNumberGenerator.java +++ b/src/main/java/domain/RandomNumberGenerator.java @@ -2,7 +2,7 @@ import java.util.*; -public class RandomNumberGenerator { +public class RandomNumberGenerator implements NumberGenerator { private final Random random; diff --git a/src/main/java/view/ConsoleInputView.java b/src/main/java/view/ConsoleInputView.java new file mode 100644 index 00000000..83115d6c --- /dev/null +++ b/src/main/java/view/ConsoleInputView.java @@ -0,0 +1,26 @@ +package view; + +import java.util.Scanner; + +public class ConsoleInputView implements InputView { + private static final String INPUT_MESSAGE = "숫자를 입력해 주세요 : "; + private static final String RESTART_MESSAGE = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + private final Scanner scanner; + + public ConsoleInputView() { + this.scanner = new Scanner(System.in); + } + + public String inputNumbers() { + System.out.println(INPUT_MESSAGE); + + String input = scanner.nextLine(); + + return input.trim(); + } + + public String inputRestartCommand() { + System.out.println(RESTART_MESSAGE); // 안내 문구 출력 + return scanner.nextLine().trim(); // 입력받은 값(1 또는 2) 반환 + } +} diff --git a/src/main/java/view/ConsoleOutputView.java b/src/main/java/view/ConsoleOutputView.java new file mode 100644 index 00000000..a2d8e0ea --- /dev/null +++ b/src/main/java/view/ConsoleOutputView.java @@ -0,0 +1,31 @@ +package view; + +import dto.GameResult; + +public class ConsoleOutputView implements OutputView { + + public void printResult(GameResult result) { + if (result.isNothing()) { + System.out.println("낫싱"); + return; + } + + StringBuilder sb = new StringBuilder(); + if (result.getBall() > 0) { + sb.append(result.getBall()).append("볼 "); + } + if (result.getStrike() > 0) { + sb.append(result.getStrike()).append("스트라이크 "); + } + + System.out.println(sb.toString().trim()); + } + + public void printGameSuccess() { + System.out.println("3개의 숫자를 모두 맞추셨습니다! 게임 종료"); + } + + public void printError(String message) { + System.out.println(message); + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 65870abf..0701b59e 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,26 +1,7 @@ package view; -import java.util.Scanner; +public interface InputView { -public class InputView { - private static final String INPUT_MESSAGE = "숫자를 입력해 주세요 : "; - private static final String RESTART_MESSAGE = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; - private final Scanner scanner; - - public InputView() { - this.scanner = new Scanner(System.in); - } - - public String inputNumbers() { - System.out.println(INPUT_MESSAGE); - - String input = scanner.nextLine(); - - return input.trim(); - } - - public String inputRestartCommand() { - System.out.println(RESTART_MESSAGE); // 안내 문구 출력 - return scanner.nextLine().trim(); // 입력받은 값(1 또는 2) 반환 - } + public String inputNumbers(); + public String inputRestartCommand(); } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 052ceaab..27efe756 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -2,30 +2,9 @@ import dto.GameResult; -public class OutputView { +public interface OutputView { - public void printResult(GameResult result) { - if (result.isNothing()) { - System.out.println("낫싱"); - return; - } - - StringBuilder sb = new StringBuilder(); - if (result.getBall() > 0) { - sb.append(result.getBall()).append("볼 "); - } - if (result.getStrike() > 0) { - sb.append(result.getStrike()).append("스트라이크 "); - } - - System.out.println(sb.toString().trim()); - } - - public void printGameSuccess() { - System.out.println("3개의 숫자를 모두 맞추셨습니다! 게임 종료"); - } - - public void printError(String message) { - System.out.println(message); - } + public void printResult(GameResult result); + public void printGameSuccess(); + public void printError(String message); } From 44ab66b609d879f0c5aea9aab8942b2c3cdaf8f8 Mon Sep 17 00:00:00 2001 From: sapiensxxv Date: Sat, 31 Jan 2026 14:43:33 +0900 Subject: [PATCH 27/27] test: gamecontroller test --- src/main/java/controller/GameController.java | 7 +- src/main/java/dto/GameResult.java | 11 ++ src/main/java/view/ConsoleOutputView.java | 2 +- src/main/java/view/OutputView.java | 2 +- .../java/controller/GameControllerTest.java | 157 ++++++++++++++++++ 5 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 src/test/java/controller/GameControllerTest.java diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 216b389a..70752de3 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -4,9 +4,7 @@ import dto.GameResult; import domain.BaseballNumber; import utils.InputConverter; -import domain.RandomNumberGenerator; import domain.Referee; -import view.ConsoleOutputView; import view.InputView; import view.OutputView; @@ -31,8 +29,7 @@ public GameController( } public void run() { - System.out.println("숫자 야구 게임을 시작합니다."); // 게임 시작 문구 (선택 사항) - + outputView.printMessage("숫자 야구 게임을 시작하겠습니다."); // 게임 전체 반복 (재시작 로직) while (true) { // 1. 컴퓨터 숫자 생성 @@ -65,7 +62,7 @@ private void play(BaseballNumber computerNumber) { break; } } catch (IllegalArgumentException e) { - outputView.printError(e.getMessage()); + outputView.printMessage(e.getMessage()); } } } diff --git a/src/main/java/dto/GameResult.java b/src/main/java/dto/GameResult.java index 32135cf8..5f0966fe 100644 --- a/src/main/java/dto/GameResult.java +++ b/src/main/java/dto/GameResult.java @@ -24,4 +24,15 @@ public int getBall() { public int getStrike() { return strike; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (isThreeStrike()) { + sb.append("3스트라이크"); + } else { + sb.append(getBall() + "볼 " + getStrike() + "스트라이크"); + } + return sb.toString(); + } } diff --git a/src/main/java/view/ConsoleOutputView.java b/src/main/java/view/ConsoleOutputView.java index a2d8e0ea..b27bb0cb 100644 --- a/src/main/java/view/ConsoleOutputView.java +++ b/src/main/java/view/ConsoleOutputView.java @@ -25,7 +25,7 @@ public void printGameSuccess() { System.out.println("3개의 숫자를 모두 맞추셨습니다! 게임 종료"); } - public void printError(String message) { + public void printMessage(String message) { System.out.println(message); } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 27efe756..44e246a3 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -6,5 +6,5 @@ public interface OutputView { public void printResult(GameResult result); public void printGameSuccess(); - public void printError(String message); + public void printMessage(String message); } diff --git a/src/test/java/controller/GameControllerTest.java b/src/test/java/controller/GameControllerTest.java new file mode 100644 index 00000000..e08f82b1 --- /dev/null +++ b/src/test/java/controller/GameControllerTest.java @@ -0,0 +1,157 @@ +package controller; + +import domain.BaseballNumber; +import domain.NumberGenerator; +import domain.Referee; +import dto.GameResult; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import view.InputView; +import view.OutputView; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import static org.assertj.core.api.Assertions.*; + +class GameControllerTest { + + @Test + @DisplayName("게임 정상 실행: 오답 -> 정답 -> 종료(2) 시나리오 검증") + void gameRunSuccessScenario() { + // 컴퓨터의 입력을 1,2,3 으로 고정 + FixedNumberGenerator fixedGenerator = new FixedNumberGenerator("123"); + + // 사용자의 입력을 순서대로 설정 + StubInputView stubInputView = new StubInputView(List.of("145", "123"), "2"); + + // 출력을 캡쳐할 뷰 생성 + SpyOutputView spyOutputView = new SpyOutputView(); + + // 실제 심판 객체 + Referee referee = new Referee(); + + // 컨트롤러 조립 + GameController controller = new GameController( + stubInputView, + spyOutputView, + fixedGenerator, + referee + ); + + //when + controller.run(); + + // 출력된 메세지들이 예상대로 포함되어 있는지 + List logs = spyOutputView.getLogs(); + + assertThat(logs) + .containsExactly( + "숫자 야구 게임을 시작하겠습니다.", + "0볼 1스트라이크", // 145 입력 결과 + "3스트라이크", // 123 입력 결과 + "3개의 숫자를 모두 맞히셨습니다! 게임 종료" // 정답 메세지(OutputView 구현에 따라 다를 수 있음 + ); + } + + @Test + @DisplayName("예외 처리: 잘못된 입력 -> 에러 출력 -> 재입력 -> 성공 시나리오") + void gameRunExceptionScenario() { + FixedNumberGenerator fixedGenerator = new FixedNumberGenerator("123"); + // "abc" (에러발생) -> "123" (정답) -> "2" (종료) + StubInputView stubInputView = new StubInputView(List.of("abc", "123"), "2"); + SpyOutputView spyOutputView = new SpyOutputView(); + Referee referee = new Referee(); + + GameController controller = new GameController(stubInputView, spyOutputView, fixedGenerator, referee); + + //when + controller.run(); + + //then + List logs = spyOutputView.getLogs(); + assertThat(logs.toString()).contains("[ERROR]"); // 에러메세지가 출력되었는지 확인 + assertThat(logs).contains("3스트라이크"); // 결국 성공했는지 확인 + } + + /** + * 테스트를 위한 가짜 객체 정의 + * 무조건 정해진 숫자만 반환하는 가짜 생성기 + */ + static class FixedNumberGenerator implements NumberGenerator { + + private final List numbers; + + public FixedNumberGenerator(String input) { + this.numbers = new ArrayList<>(); + for (String s: input.split("")) { + numbers.add(Integer.parseInt(s)); + } + } + + @Override + public BaseballNumber generate() { + return new BaseballNumber(numbers); + } + } + + /** + * 미리 정해진 입력을 반환하는 테스트용 객체 + */ + static class StubInputView implements InputView { + + private final Queue numberInputs; + private final Queue restartInputs; + + public StubInputView( + List numberInputs, + String restartInputs + ) { + this.numberInputs = new LinkedList<>(numberInputs); + this.restartInputs = new LinkedList<>(List.of(restartInputs)); + } + + @Override + public String inputNumbers() { + return numberInputs.poll(); // 큐에서 하나씩 꺼냄 + } + + @Override + public String inputRestartCommand() { + return restartInputs.poll(); + } + } + + /** + * 출력된 내용을 리스트에 저장하는 가짜 객체 + */ + static class SpyOutputView implements OutputView { + + private final List logs = new ArrayList<>(); + + public List getLogs() { + return logs; + } + + @Override + public void printResult(GameResult result) { + // 실제 로직을 흉내내거나, 결과 객체 정보를 로그에 저장 + // 여기서는 간단히 문자로 변환해 저장한다고 가정 + String message = result.toString(); + logs.add(message); + } + + @Override + public void printGameSuccess() { + logs.add("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); + } + + @Override + public void printMessage(String message) { + logs.add(message); + } + } +} \ No newline at end of file