diff --git a/src/main/java/baseball/README.md b/src/main/java/baseball/README.md
new file mode 100644
index 000000000..e81ff95f2
--- /dev/null
+++ b/src/main/java/baseball/README.md
@@ -0,0 +1,37 @@
+# 숫자야구게임
+### 룰
+1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임
+
+같은 수가 같은 자리에 있으면 `스트라이크`
+
+같은 수가 다른 자리에 있으면 `볼`
+
+같은 수가 전혀 없으면 `포볼 또는 낫싱`
+
+여러번 반복해 정보를 얻으며 상대방(컴퓨터)의 수를 맞추면 승리
+
+e.g. 상대방(컴퓨터)의 수가 425일 때 123을 제시한 경우 : `1스트라이크`, 456을 제시한 경우 : `1볼 1스트라이크`, 789를 제시한 경우 : `낫싱`
+
+### 작동 방식
+
+1) 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택
+2) 사용자는 컴퓨터가 생각하고 있는 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한 결과 출력
+3) 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임 종료
+4) 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다
+
+### 기능 목록
+엔티티
+- [x] Ball: 세자리 숫자 저장하는 엔티티 클래스
+- [x] BallStatus: 유저 Ball과 랜덤 Ball 비교한 결과를 저장하는 엔티티 클래스
+- [x] Action: 동작들을 담는 enum 클래스
+
+서비스
+- [x] BaseballService
+ - isBall: 볼 여부 판별
+ - isStrike: 스트라이크 여부 판별
+ - compare: RandomNum, UserNum 비교해 BallStatus 반환
+- [x] RandomNumGenerator: 임의의 서로다른 3자리 수 생성
+
+뷰
+- [x] InputView: 사용자로부터 입력 받는 뷰 담당하는 클래스
+- [x] OutputView: 출력 하는 뷰 담당하는 클래스
\ No newline at end of file
diff --git a/src/main/java/baseball/application.java b/src/main/java/baseball/application.java
new file mode 100644
index 000000000..c40103916
--- /dev/null
+++ b/src/main/java/baseball/application.java
@@ -0,0 +1,31 @@
+package baseball;
+
+import baseball.domain.Ball;
+import baseball.domain.Baseball;
+import baseball.domain.BaseballStatus;
+import baseball.service.BaseballService;
+import baseball.service.RandomBallGenerator;
+import baseball.view.InputView;
+import baseball.view.OutputView;
+
+import java.util.stream.Collectors;
+
+public class application {
+ public static void main(String[] args) throws Exception {
+ InputView inputView = new InputView();
+ OutputView outputView = new OutputView();
+ RandomBallGenerator randomBallGenerator = new RandomBallGenerator();
+ BaseballService baseballService = new BaseballService();
+
+ do {
+ Baseball randomBaseball = new Baseball(randomBallGenerator.makeNum());
+ BaseballStatus baseballStatus;
+ do {
+ Baseball userBaseball = inputView.inputBall();
+ baseballStatus = baseballService.compare(userBaseball, randomBaseball);
+ outputView.printBaseballStatus(baseballStatus);
+ } while (!outputView.exitGame(baseballStatus));
+
+ }while(inputView.resumeGame());
+ }
+}
diff --git a/src/main/java/baseball/domain/Action.java b/src/main/java/baseball/domain/Action.java
new file mode 100644
index 000000000..a138e213d
--- /dev/null
+++ b/src/main/java/baseball/domain/Action.java
@@ -0,0 +1,5 @@
+package baseball.domain;
+
+public enum Action {
+ 볼, 스트라이크, 낫싱
+}
diff --git a/src/main/java/baseball/domain/Ball.java b/src/main/java/baseball/domain/Ball.java
new file mode 100644
index 000000000..6670ec54b
--- /dev/null
+++ b/src/main/java/baseball/domain/Ball.java
@@ -0,0 +1,13 @@
+package baseball.domain;
+
+public class Ball {
+ private final int number;
+
+ public Ball(final int number) {
+ this.number = number;
+ }
+
+ public int getNumber() {
+ return number;
+ }
+}
diff --git a/src/main/java/baseball/domain/Baseball.java b/src/main/java/baseball/domain/Baseball.java
new file mode 100644
index 000000000..82531ee08
--- /dev/null
+++ b/src/main/java/baseball/domain/Baseball.java
@@ -0,0 +1,28 @@
+package baseball.domain;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Baseball {
+ private final List baseballs;
+
+ public Baseball(final List ballList){
+ this.baseballs= new ArrayList<>(ballList);
+ isDistinct();
+ }
+
+ public List getBaseballs() {
+ return Collections.unmodifiableList(baseballs);
+ }
+
+ public void isDistinct(){
+ for(int i=0;i ball.getNumber()>0 && ball.getNumber()<=9)){
+ throw new RuntimeException("범위를 벗어납니다.");
+ }
+ }
+
+ public Baseball inputBall() throws RuntimeException {
+ Scanner scanner = new Scanner(System.in);
+ System.out.print("숫자를 입력해 주세요 : ");
+ String input = scanner.next();
+ List list = Arrays.stream(input.split("")).mapToInt(Integer::parseInt).mapToObj(Ball::new).collect(Collectors.toList());
+ isValidInput(list);
+ return new Baseball(list);
+ }
+
+ public boolean resumeGame() {
+ Scanner scanner = new Scanner(System.in);
+ System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.");
+ int input = scanner.nextInt();
+ return input == 1;
+ }
+
+
+}
diff --git a/src/main/java/baseball/view/OutputView.java b/src/main/java/baseball/view/OutputView.java
new file mode 100644
index 000000000..1ed7c6d1e
--- /dev/null
+++ b/src/main/java/baseball/view/OutputView.java
@@ -0,0 +1,33 @@
+package baseball.view;
+
+import baseball.domain.Action;
+import baseball.domain.BaseballStatus;
+
+public class OutputView {
+ public String outputBaseballStatus(BaseballStatus baseballStatus) {
+ String result = "";
+
+ if (baseballStatus.existsBall()) {
+ result += baseballStatus.getBall() + Action.볼.toString() + " ";
+ }
+ if (baseballStatus.existsStrike()) {
+ result += baseballStatus.getStrike() + Action.스트라이크.toString();
+ }
+ if (baseballStatus.nothing()) {
+ return Action.낫싱.toString();
+ }
+ return result;
+ }
+
+ public void printBaseballStatus(BaseballStatus baseballStatus) {
+ System.out.println(outputBaseballStatus(baseballStatus));
+ }
+
+ public boolean exitGame(BaseballStatus ballStatus) {
+ if (ballStatus.exitGame()) {
+ System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료");
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/test/java/baseball/service/BaseballServiceTest.java b/src/test/java/baseball/service/BaseballServiceTest.java
new file mode 100644
index 000000000..f3ec8422a
--- /dev/null
+++ b/src/test/java/baseball/service/BaseballServiceTest.java
@@ -0,0 +1,48 @@
+package baseball.service;
+
+import baseball.domain.Ball;
+import baseball.domain.Baseball;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BaseballServiceTest {
+ BaseballService baseballService;
+
+ @BeforeEach
+ void setUp(){
+ baseballService= new BaseballService();
+ }
+
+ @Test
+ void isStrikeTest(){
+ Baseball userBall = new Baseball(Arrays.asList(new Ball(1),new Ball(2),new Ball(3)));
+ Baseball randomBall = new Baseball(Arrays.asList(new Ball(1),new Ball(2),new Ball(3)));
+ for(int i=0;i<3;i++){
+ assertThat(baseballService.isStrike(userBall.getBaseballs().get(i), randomBall.getBaseballs().get(i))).isTrue();
+ }
+ }
+
+ @Test
+ void isBallTest(){
+ Baseball userBall = new Baseball(Arrays.asList(new Ball(1),new Ball(3),new Ball(2)));
+ Baseball randomBall = new Baseball(Arrays.asList(new Ball(2),new Ball(1),new Ball(3)));
+ for(int i=0;i<3;i++){
+ assertThat(baseballService.isBall(userBall.getBaseballs().get(i), randomBall)).isTrue();
+ }
+ }
+
+ @ParameterizedTest
+ @CsvSource({"1,2,6,1,9,2,1,1", "3,7,5,7,3,9,2,0", "1,2,3,4,5,6,0,0", "3,4,8,3,4,2,0,2"})
+ void compareTest(int u1,int u2,int u3,int r1,int r2,int r3, int e1, int e2) throws Exception {
+ Baseball userBall = new Baseball(Arrays.asList(new Ball(u1),new Ball(u2),new Ball(u3)));
+ Baseball randomBall = new Baseball(Arrays.asList(new Ball(r1),new Ball(r2),new Ball(r3)));
+
+ assertThat(new int[]{baseballService.compare(userBall, randomBall).getBall(), baseballService.compare(userBall, randomBall).getStrike()}).isEqualTo(new int[]{e1, e2});
+ }
+}
diff --git a/src/test/java/baseball/service/RandomBallTest.java b/src/test/java/baseball/service/RandomBallTest.java
new file mode 100644
index 000000000..27182bc5c
--- /dev/null
+++ b/src/test/java/baseball/service/RandomBallTest.java
@@ -0,0 +1,37 @@
+package baseball.service;
+
+import baseball.domain.Ball;
+import baseball.domain.Baseball;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RandomBallTest {
+ RandomBallGenerator randomBallGenerator;
+
+ @BeforeEach
+ public void setUp() {
+ randomBallGenerator = new RandomBallGenerator();
+ }
+
+ @Test
+ @DisplayName("3개의 서로다른 숫자가 생성되는지 확인하는 테스트입니다.")
+ public void makeNumTest() {
+ List list = randomBallGenerator.makeNum();
+
+ Baseball randomNum = new Baseball(list);
+ assertThat(randomNum.getBaseballs().size()).isEqualTo(3);
+ }
+
+ @Test
+ @DisplayName("1부터 9 사이의 값인지 확인하는 테스트입니다.")
+ public void rangeTest() {
+ List list = randomBallGenerator.makeNum();
+
+ assertThat(list.stream().filter(ball -> ball.getNumber() >= 1 && ball.getNumber() <= 9).count()).isEqualTo(3);
+ }
+}
diff --git a/src/test/java/baseball/view/InputViewTest.java b/src/test/java/baseball/view/InputViewTest.java
new file mode 100644
index 000000000..dc0ad2009
--- /dev/null
+++ b/src/test/java/baseball/view/InputViewTest.java
@@ -0,0 +1,44 @@
+package baseball.view;
+
+import baseball.domain.Baseball;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class InputViewTest {
+ InputView inputView;
+ Baseball baseball;
+
+ @BeforeEach
+ void setUp() {
+ inputView = new InputView();
+ String input = "349";
+ OutputStream out = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(out));
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ }
+
+ @Test
+ void countTest() throws RuntimeException {
+ baseball = inputView.inputBall();
+ assertThat(baseball.getBaseballs().size()).isEqualTo(3);
+ }
+
+ @Test
+ void rangeTest() throws RuntimeException {
+ baseball = inputView.inputBall();
+ assertThat(baseball.getBaseballs().stream().filter(b -> b.getNumber() < 10 && b.getNumber() > 0).count()).isEqualTo(3);
+ }
+
+ @Test
+ void resumeTest() {
+ String data = "1";
+ InputStream in = new ByteArrayInputStream(data.getBytes());
+ System.setIn(in);
+ assertThat(inputView.resumeGame()).isTrue();
+ }
+}
diff --git a/src/test/java/baseball/view/OutputViewTest.java b/src/test/java/baseball/view/OutputViewTest.java
new file mode 100644
index 000000000..788fcbedf
--- /dev/null
+++ b/src/test/java/baseball/view/OutputViewTest.java
@@ -0,0 +1,40 @@
+package baseball.view;
+
+import baseball.domain.BaseballStatus;
+import org.junit.jupiter.api.BeforeEach;
+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 static org.assertj.core.api.Assertions.assertThat;
+
+public class OutputViewTest {
+ OutputView outputView;
+
+ @BeforeEach
+ void setUp() {
+ outputView = new OutputView();
+ }
+
+ @ParameterizedTest
+ @CsvSource({"1,2,1볼 2스트라이크", "0,0,낫싱", "0,2,2스트라이크"})
+ void outputViewTest(int ball, int strike, String expected) throws Exception {
+ BaseballStatus baseballStatus = new BaseballStatus(ball, strike);
+ assertThat(outputView.outputBaseballStatus(baseballStatus)).isEqualTo(expected);
+ }
+
+ @Test
+ @DisplayName("조건에 만족하면 게임을 끝내는 메소드 테스트")
+ void exitGameTest() throws Exception {
+ BaseballStatus baseballStatus = new BaseballStatus(0,3);
+ assertThat(outputView.exitGame(baseballStatus)).isTrue();
+ }
+
+ @Test
+ @DisplayName("조건에 만족하지 않으면 게임 계속하는 메소드 테스트")
+ void exitGameFalseTest() throws Exception {
+ BaseballStatus baseballStatus = new BaseballStatus(1,2);
+ assertThat(outputView.exitGame(baseballStatus)).isFalse();
+ }
+}