diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..29298219
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,25 @@
+##๐๊ตฌํ ๊ธฐ๋ฅ ๋ชฉ๋ก
+###1. ์์์ ์ซ์ ์์ฑ
+- 1์์ 9๊น์ง ์๋ก ๋ค๋ฅธ ์์์ ์ 3๊ฐ๋ฅผ ์ ํํ๋ค.
+- ์ปดํจํฐ์ ๋๋ค ๊ฐ์ ๋ฐ๋์ JavaScript์ Math.random ๋์ MissionUtils ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ Random.pickNumberInRange๋ฅผ ์ฌ์ฉํด ๊ตฌํ๋ค.
+
+###2. ์ ํจํ ๊ฐ ์
๋ ฅ๋ฐ๊ธฐ
+- 1์์ 9๊น์ง ์๋ก ๋ค๋ฅธ ์ 3๊ฐ
+- ์ฌ์ฉ์๊ฐ ์๋ชป๋ ๊ฐ์ ์
๋ ฅํ ๊ฒฝ์ฐ alert์ผ๋ก ์๋ฌ ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ๋ค์ ์
๋ ฅํ ์ ์๊ฒ ํ๋ค.
+
+###3. ์ ๋ต ๊ฒ์ฌ
+- ์คํธ๋ผ์ดํฌ: ๊ฐ์ ์๊ฐ ๊ฐ์ ์๋ฆฌ์ ์์ ๋
+- ๋ณผ: ๊ฐ์ ์๊ฐ ๋ค๋ฅธ ์๋ฆฌ์ ์์ ๋
+- ๋ซ์ฑ: ๊ฐ์ ์๊ฐ ์ ํ ์์ ๋
+- ์ ๋ต ์ฌ๋ถ ๋ฐํ
+
+###4. ๊ฒ์์ ์ข
๋ฃ
+- ์์์ ์ซ์ 3๊ฐ๋ฅผ ๋ชจ๋ ๋ง์ถ ๊ฒฝ์ฐ ๊ฒ์ ์ข
๋ฃ
+- ๊ฒ์ ์ข
๋ฃ ์ ์ฌ์์ ๋ฒํผ ๋
ธ์ถ
+
+###5. ์ฌ์์
+- ์ฌ์์ ๋ฒํผ ํด๋ฆญ ์ ๊ฒ์ ์ฌ์์
+
+
+###๐ํ
์คํธ ๊ฒฐ๊ณผ
+
\ No newline at end of file
diff --git a/docs/result.png b/docs/result.png
new file mode 100644
index 00000000..ed4000b7
Binary files /dev/null and b/docs/result.png differ
diff --git a/index.html b/index.html
index c85d50be..c370d91d 100644
--- a/index.html
+++ b/index.html
@@ -17,11 +17,11 @@
โพ ์ซ์ ์ผ๊ตฌ ๊ฒ์
๐ ๊ฒฐ๊ณผ
- 1๋ณผ 1์คํธ๋ผ์ดํฌ
-
+
+
diff --git a/src/controller.js b/src/controller.js
new file mode 100644
index 00000000..9794444a
--- /dev/null
+++ b/src/controller.js
@@ -0,0 +1,37 @@
+import BaseballGame from './model.js';
+import InputView from './views/InputView.js'
+import OutputView from './views/OutputView.js';
+
+export default class BaseballController {
+ constructor() {
+ this.model = new BaseballGame();
+ this.inputView = new InputView();
+ this.outputView = new OutputView();
+
+ this.init();
+ }
+
+ init() {
+ this.inputView.bindSubmitEvent(this.onSubmit.bind(this));
+ this.outputView.bindRestartEvent(this.onRestart.bind(this));
+ }
+
+ onSubmit(inputNumber) {
+ try {
+ this.model.checkValidity(inputNumber);
+
+ const score = this.model.play(this.model.randomNumber, inputNumber);
+ this.outputView.showResult(score.ball, score.strike);
+
+ } catch (error) {
+ this.outputView.showError(error.message);
+ this.inputView.clearInput();
+ }
+ }
+
+ onRestart() {
+ this.model.init();
+ this.inputView.clearInput();
+ this.outputView.clearResult();
+ }
+}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 00000000..dbeede74
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,3 @@
+import BaseballController from './controller.js';
+
+new BaseballController();
\ No newline at end of file
diff --git a/src/model.js b/src/model.js
new file mode 100644
index 00000000..7413a11e
--- /dev/null
+++ b/src/model.js
@@ -0,0 +1,49 @@
+import {isDuplicate, checkValidity} from './utils/validation.js';
+
+export default class BaseballGame {
+ constructor() {
+ this.randomNumber = this.getRandomNumber();
+ }
+
+ getRandomNumber() {
+ const tempArr = [];
+
+ while (tempArr.length < 3) {
+ const tempNumber = MissionUtils.Random.pickNumberInRange(1, 9);
+ tempArr.push(tempNumber);
+
+ if(isDuplicate(tempArr)) {
+ tempArr.pop();
+ }
+ }
+
+ console.log("RandomNumber: "+ tempArr)
+ return tempArr;
+ }
+
+ checkValidity(inputNumber) {
+ checkValidity(inputNumber);
+ }
+
+ play(randomNumber, inputNumber) {
+ let ball = 0, strike = 0;
+
+ // ๋ณผ ๊ฐ์ ํ์ธ
+ for (let i = 0; i < randomNumber.length; i++) {
+ for (let j = 0; j < inputNumber.length; j++) {
+ if (randomNumber[i] == inputNumber[j]) {
+ ball++;
+ }
+ }
+ }
+
+ // ์คํธ๋ผ์ดํฌ ๊ฐ์ ํ์ธ
+ for (let i = 0; i < randomNumber.length; i++) {
+ if (randomNumber[i] == inputNumber[i]) {
+ strike++;
+ }
+ }
+
+ return {ball, strike};
+ }
+}
\ No newline at end of file
diff --git a/src/utils/validation.js b/src/utils/validation.js
new file mode 100644
index 00000000..ab3b92ee
--- /dev/null
+++ b/src/utils/validation.js
@@ -0,0 +1,27 @@
+ // ์
๋ ฅํ ์ซ์์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฌ
+export function checkValidity(inputNumber) {
+ // ์ซ์๊ฐ ์๋ ๊ฒฝ์ฐ
+ for (let i = 0; i < inputNumber.length; i++) {
+ if ('0' > inputNumber[i] || inputNumber[i] > '9') {
+ throw new Error('์ซ์๋ฅผ ์
๋ ฅํด ์ฃผ์ธ์.');
+ }
+ }
+
+ // 3์๋ฆฌ๊ฐ ์๋ ๊ฒฝ์ฐ
+ if (inputNumber.length !== 3) {
+ throw new Error('3๊ฐ์ ์ซ์๋ฅผ ์
๋ ฅํ์ธ์.');
+ }
+
+ // ์ค๋ณต๋ ์๊ฐ ์๋ ๊ฒฝ์ฐ
+ if (isDuplicate(inputNumber)) {
+ throw new Error('์ค๋ณต ์์ด ์
๋ ฅํด ์ฃผ์ธ์.');
+ }
+}
+
+export function isDuplicate(arr) {
+ const isDup = arr.some(function(x) {
+ return arr.indexOf(x) !== arr.lastIndexOf(x);
+ });
+
+ return isDup;
+}
\ No newline at end of file
diff --git a/src/views/InputView.js b/src/views/InputView.js
new file mode 100644
index 00000000..bc149ac3
--- /dev/null
+++ b/src/views/InputView.js
@@ -0,0 +1,18 @@
+export default class InputView {
+ constructor() {
+ this.userInput = document.getElementById('user-input');
+ this.submitBtn = document.getElementById('submit');
+ }
+
+ bindSubmitEvent(handler) {
+ this.submitBtn.addEventListener('click', (event) => {
+ event.preventDefault();
+ const inputNumber = this.userInput.value.split("");
+ handler(inputNumber);
+ }
+ );}
+
+ clearInput() {
+ this.userInput.value = "";
+ }
+}
\ No newline at end of file
diff --git a/src/views/OutputView.js b/src/views/OutputView.js
new file mode 100644
index 00000000..06bb7684
--- /dev/null
+++ b/src/views/OutputView.js
@@ -0,0 +1,51 @@
+export default class OutputView {
+ constructor() {
+ this.result = document.getElementById('result');
+ this.restartBtn = document.getElementById('game-restart-button');
+
+ this.restartBtn.style.display = "none";
+ }
+
+ // ์ ๋ต ์ถ๋ ฅ
+ showResult(ball, strike) {
+ if (ball == 3 && strike == 3) {
+ this.restartBtn.style.display = "block";
+ this.result.innerHTML = "๐์ ๋ต์ ๋ง์ถ์
จ์ต๋๋ค๐
๊ฒ์์ ์๋ก ํ์๊ฒ ์ต๋๊น?
";
+ return;
+ }
+
+ if (ball == 0 && strike == 0) {
+ this.result.innerHTML = "๋ซ์ฑ";
+ return;
+ }
+
+ if ((ball == 0 || ball - strike == 0) && strike != 0) {
+ this.result.innerHTML = strike + "์คํธ๋ผ์ดํฌ";
+ return;
+ }
+
+ if (ball != 0 && strike == 0) {
+ this.result.innerHTML = ball + "๋ณผ";
+ return;
+ }
+
+ this.result.innerHTML = ball - strike + "๋ณผ " + strike + "์คํธ๋ผ์ดํฌ";
+ return;
+ }
+
+ showError(errorMessage) {
+ alert(errorMessage);
+ }
+
+ clearResult() {
+ this.result.innerHTML = "";
+ this.restartBtn.style.display = "none";
+ }
+
+ // ์ฌ์์ ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ
+ bindRestartEvent(handler) {
+ this.restartBtn.addEventListener("click", () => {
+ handler();
+ });
+ }
+}
\ No newline at end of file