From ef743b35cd4423435da1599fd1689f2f25361b52 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Tue, 2 Jul 2024 23:37:15 +0900 Subject: [PATCH 01/87] =?UTF-8?q?chore:=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20=EB=A6=AC=EB=93=9C?= =?UTF-8?q?=EB=AF=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- package.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bab3552..d33becb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 자동차 경주 미션 +# 로또 미션 ## 참고링크 및 저장소 diff --git a/package.json b/package.json index 1fe342c..224bbf7 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,12 @@ "version": "1.0.0", "description": "로또 미션을 통해서 학습하는 클린코드", "main": "./src/main.js", - "type": "module", + "type": "module", "scripts": { "start": "node src/main.js", "start:watch": "node --watch src/main.js", - "test": "vitest" + "test": "vitest run", + "test:watch": "vitest" }, "keywords": [], "author": "", From 9f9556191a233212f55e614982e1b77799974921 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Tue, 2 Jul 2024 23:37:51 +0900 Subject: [PATCH 02/87] =?UTF-8?q?feat:=20=ED=8F=B4=EB=8D=94=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/index.js | 0 src/service/index.js | 0 src/utils/index.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/domain/index.js create mode 100644 src/service/index.js create mode 100644 src/utils/index.js diff --git a/src/domain/index.js b/src/domain/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/service/index.js b/src/service/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..e69de29 From de30d4e4347518d1d9b6bed4c9bf29464bfbb0fa Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Tue, 2 Jul 2024 23:38:15 +0900 Subject: [PATCH 03/87] =?UTF-8?q?test:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=8F=20tdd=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 17 +++++++++++++++++ src/__tests__/sum.test.js | 11 ----------- 2 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 src/__tests__/Lotto.test.js delete mode 100644 src/__tests__/sum.test.js diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js new file mode 100644 index 0000000..e8fca62 --- /dev/null +++ b/src/__tests__/Lotto.test.js @@ -0,0 +1,17 @@ +import { describe, test, expect } from "vitest"; + +class Lotto { + constructor(price) { + if (typeof price !== "number") { + throw new Error("로또 구입 금액으로 숫자를 입력해야 합니다."); + } + } +} + +describe("Lotto 클래스 테스트.", () => { + test("로또를 구입할 때 숫자가 아닌 값을 받으면 오류가 발생한다.", () => { + expect(() => new Lotto("1")).toThrowError( + "로또 구입 금액으로 숫자를 입력해야 합니다." + ); + }); +}); diff --git a/src/__tests__/sum.test.js b/src/__tests__/sum.test.js deleted file mode 100644 index efc011c..0000000 --- a/src/__tests__/sum.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { describe, test, expect } from "vitest"; - -function sum(...args) { - return args.reduce((a, b) => a+ b); -} - -describe('예제 테스트입니다.', () => { - test('sum > ', () => { - expect(sum(1,2,3,4,5)).toBe(15); - }) -}) From d579f9bf38eac63a81e3ab868e8acc6302cce4bb Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 16:22:48 +0900 Subject: [PATCH 04/87] =?UTF-8?q?test:=20=EB=A1=9C=EB=98=90=20=EA=B5=AC?= =?UTF-8?q?=EC=9E=85=20=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20Lotto=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EC=97=90=20=EA=B5=AC=EC=9E=85=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js index e8fca62..5f3831f 100644 --- a/src/__tests__/Lotto.test.js +++ b/src/__tests__/Lotto.test.js @@ -1,10 +1,28 @@ import { describe, test, expect } from "vitest"; class Lotto { + static #PRICE_PER_ONE = 1000; + + #count; + constructor(price) { if (typeof price !== "number") { throw new Error("로또 구입 금액으로 숫자를 입력해야 합니다."); } + + if (price < Lotto.#PRICE_PER_ONE) { + throw new Error("로또 구입 금액은 1000원이상이어야 합니다."); + } + + this.#count = Lotto.#countLotto(price); + } + + get count() { + return this.#count; + } + + static #countLotto(price) { + return Math.floor(price / Lotto.#PRICE_PER_ONE); } } @@ -14,4 +32,24 @@ describe("Lotto 클래스 테스트.", () => { "로또 구입 금액으로 숫자를 입력해야 합니다." ); }); + + test("로또를 구입할 때 1000원미만의 값을 받으면 오류가 발생한다.", () => { + expect(() => new Lotto(999)).toThrowError( + "로또 구입 금액은 1000원이상이어야 합니다." + ); + }); + + test.each([ + { price: 1001, count: 1 }, + { price: 2500, count: 2 }, + { price: 3999, count: 3 }, + { price: 10999, count: 10 }, + ])( + "로또를 $price원만큼 구매하면 $count장을 발행한다.", + ({ price, count }) => { + const lotto = new Lotto(price); + + expect(lotto.count).toBe(count); + } + ); }); From e855bbf1df2cdd48abebabbb818250198f88ef43 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 16:25:52 +0900 Subject: [PATCH 05/87] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=EC=97=90=EC=84=9C=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=ED=95=9C=20Lotto=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 27 +-------------------------- src/domain/Lotto.js | 27 +++++++++++++++++++++++++++ src/domain/index.js | 1 + 3 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 src/domain/Lotto.js diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js index 5f3831f..e8e7ec9 100644 --- a/src/__tests__/Lotto.test.js +++ b/src/__tests__/Lotto.test.js @@ -1,30 +1,5 @@ import { describe, test, expect } from "vitest"; - -class Lotto { - static #PRICE_PER_ONE = 1000; - - #count; - - constructor(price) { - if (typeof price !== "number") { - throw new Error("로또 구입 금액으로 숫자를 입력해야 합니다."); - } - - if (price < Lotto.#PRICE_PER_ONE) { - throw new Error("로또 구입 금액은 1000원이상이어야 합니다."); - } - - this.#count = Lotto.#countLotto(price); - } - - get count() { - return this.#count; - } - - static #countLotto(price) { - return Math.floor(price / Lotto.#PRICE_PER_ONE); - } -} +import { Lotto } from "../domain/index.js"; describe("Lotto 클래스 테스트.", () => { test("로또를 구입할 때 숫자가 아닌 값을 받으면 오류가 발생한다.", () => { diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js new file mode 100644 index 0000000..2d6b136 --- /dev/null +++ b/src/domain/Lotto.js @@ -0,0 +1,27 @@ +class Lotto { + static #PRICE_PER_ONE = 1000; + + #count; + + constructor(price) { + if (typeof price !== "number") { + throw new Error("로또 구입 금액으로 숫자를 입력해야 합니다."); + } + + if (price < Lotto.#PRICE_PER_ONE) { + throw new Error("로또 구입 금액은 1000원이상이어야 합니다."); + } + + this.#count = Lotto.#countLotto(price); + } + + get count() { + return this.#count; + } + + static #countLotto(price) { + return Math.floor(price / Lotto.#PRICE_PER_ONE); + } +} + +export default Lotto; diff --git a/src/domain/index.js b/src/domain/index.js index e69de29..82190cd 100644 --- a/src/domain/index.js +++ b/src/domain/index.js @@ -0,0 +1 @@ +export { default as Lotto } from "./Lotto.js"; From e3e8ecccdf4ab4f055fe775a15c8d223b17cb0b4 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 21:47:30 +0900 Subject: [PATCH 06/87] =?UTF-8?q?feat:=20=EC=9D=B4=EC=A0=84=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C=20=ED=9B=84=20Lotto=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=88=EB=A1=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/Lotto.js | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index 2d6b136..9340a04 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -1,26 +1,18 @@ class Lotto { - static #PRICE_PER_ONE = 1000; + #lottoNumbers; - #count; + constructor(lottoNumbers) { + Lotto.#validateLottoNumbers(lottoNumbers); - constructor(price) { - if (typeof price !== "number") { - throw new Error("로또 구입 금액으로 숫자를 입력해야 합니다."); - } - - if (price < Lotto.#PRICE_PER_ONE) { - throw new Error("로또 구입 금액은 1000원이상이어야 합니다."); - } - - this.#count = Lotto.#countLotto(price); + this.#lottoNumbers = [...lottoNumbers]; } - get count() { - return this.#count; - } + static #validateLottoNumbers(lottoNumbers) { + const set = new Set(lottoNumbers); - static #countLotto(price) { - return Math.floor(price / Lotto.#PRICE_PER_ONE); + if (set.size !== lottoNumbers.length) { + throw new Error("중복되는 번호가 있습니다."); + } } } From f33d640daa91bd963be0523481881e9703b958e2 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 21:48:13 +0900 Subject: [PATCH 07/87] =?UTF-8?q?test:=20Lotto=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js index e8e7ec9..325eb3b 100644 --- a/src/__tests__/Lotto.test.js +++ b/src/__tests__/Lotto.test.js @@ -2,29 +2,9 @@ import { describe, test, expect } from "vitest"; import { Lotto } from "../domain/index.js"; describe("Lotto 클래스 테스트.", () => { - test("로또를 구입할 때 숫자가 아닌 값을 받으면 오류가 발생한다.", () => { - expect(() => new Lotto("1")).toThrowError( - "로또 구입 금액으로 숫자를 입력해야 합니다." + test("로또 번호 중에 중복되는 번호가 있으면 오류가 발생한다.", () => { + expect(() => new Lotto([1, 2, 3, 4, 5, 5])).toThrowError( + "중복되는 번호가 있습니다." ); }); - - test("로또를 구입할 때 1000원미만의 값을 받으면 오류가 발생한다.", () => { - expect(() => new Lotto(999)).toThrowError( - "로또 구입 금액은 1000원이상이어야 합니다." - ); - }); - - test.each([ - { price: 1001, count: 1 }, - { price: 2500, count: 2 }, - { price: 3999, count: 3 }, - { price: 10999, count: 10 }, - ])( - "로또를 $price원만큼 구매하면 $count장을 발행한다.", - ({ price, count }) => { - const lotto = new Lotto(price); - - expect(lotto.count).toBe(count); - } - ); }); From aced74a8449d410fa96f8a802f771607a4925e12 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 21:52:35 +0900 Subject: [PATCH 08/87] =?UTF-8?q?refactor:=20isDuplicated=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/Lotto.js | 6 +++--- src/utils/index.js | 1 + src/utils/isDuplicated.js | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 src/utils/isDuplicated.js diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index 9340a04..d26e91e 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -1,3 +1,5 @@ +import { isDuplicated } from "../utils/index.js"; + class Lotto { #lottoNumbers; @@ -8,9 +10,7 @@ class Lotto { } static #validateLottoNumbers(lottoNumbers) { - const set = new Set(lottoNumbers); - - if (set.size !== lottoNumbers.length) { + if (isDuplicated(lottoNumbers)) { throw new Error("중복되는 번호가 있습니다."); } } diff --git a/src/utils/index.js b/src/utils/index.js index e69de29..36ac996 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -0,0 +1 @@ +export { default as isDuplicated } from "./isDuplicated.js"; diff --git a/src/utils/isDuplicated.js b/src/utils/isDuplicated.js new file mode 100644 index 0000000..9c01dea --- /dev/null +++ b/src/utils/isDuplicated.js @@ -0,0 +1,7 @@ +function isDuplicated(array) { + const set = new Set(array); + + return set.size !== array.length; +} + +export default isDuplicated; From 5a7c226a02b148838c35bea6f5b0751fee79dd2e Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 22:15:06 +0900 Subject: [PATCH 09/87] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/Lotto.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index d26e91e..b611191 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -10,8 +10,20 @@ class Lotto { } static #validateLottoNumbers(lottoNumbers) { + if (!Array.isArray(lottoNumbers)) { + throw new Error("로또 번호로 적합하지 않은 값입니다."); + } + + if (lottoNumbers.length !== 6) { + throw new Error("로또 번호는 6개여야 합니다."); + } + + if (lottoNumbers.some((number) => typeof number !== "number")) { + throw new Error("로또 번호 중에 적합하지 않은 값이 있습니다."); + } + if (isDuplicated(lottoNumbers)) { - throw new Error("중복되는 번호가 있습니다."); + throw new Error("중복되는 로또 번호가 있습니다."); } } } From 56d244fcc76ad257144bd622135298aefcc2218f Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 22:15:23 +0900 Subject: [PATCH 10/87] =?UTF-8?q?test:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js index 325eb3b..375e1f2 100644 --- a/src/__tests__/Lotto.test.js +++ b/src/__tests__/Lotto.test.js @@ -2,9 +2,43 @@ import { describe, test, expect } from "vitest"; import { Lotto } from "../domain/index.js"; describe("Lotto 클래스 테스트.", () => { + test.each([ + { value: 1 }, + { value: "1" }, + { value: true }, + { value: null }, + { value: undefined }, + { value: {} }, + ])( + "로또 번호로 적합하지 않은 값($value)을 받으면 오류가 발생한다.", + ({ value }) => { + expect(() => new Lotto(value)).toThrowError( + "로또 번호로 적합하지 않은 값입니다." + ); + } + ); + + test("로또 번호가 6개 초과이면 오류가 발생한다.", () => { + expect(() => new Lotto([1, 2, 3, 4, 5, 6, 7])).toThrowError( + "로또 번호는 6개여야 합니다." + ); + }); + + test("로또 번호가 6개 미만이면 오류가 발생한다.", () => { + expect(() => new Lotto([1, 2, 3, 4, 5])).toThrowError( + "로또 번호는 6개여야 합니다." + ); + }); + + test("로또 번호 중에 숫자가 아닌 값이 있으면 오류가 발생한다.", () => { + expect(() => new Lotto(["a", 1, 2, 3, 4, 5])).toThrowError( + "로또 번호 중에 적합하지 않은 값이 있습니다." + ); + }); + test("로또 번호 중에 중복되는 번호가 있으면 오류가 발생한다.", () => { expect(() => new Lotto([1, 2, 3, 4, 5, 5])).toThrowError( - "중복되는 번호가 있습니다." + "중복되는 로또 번호가 있습니다." ); }); }); From 4afa7f00d5d6303fc616a367c385b9e49f8f04d8 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 22:25:15 +0900 Subject: [PATCH 11/87] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/Lotto.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index b611191..1d77f36 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -9,6 +9,10 @@ class Lotto { this.#lottoNumbers = [...lottoNumbers]; } + get numbers() { + return [...this.#lottoNumbers]; + } + static #validateLottoNumbers(lottoNumbers) { if (!Array.isArray(lottoNumbers)) { throw new Error("로또 번호로 적합하지 않은 값입니다."); @@ -22,6 +26,14 @@ class Lotto { throw new Error("로또 번호 중에 적합하지 않은 값이 있습니다."); } + if (lottoNumbers.some((number) => number < 1)) { + throw new Error("로또 번호 중에 1보다 작은 번호가 있습니다."); + } + + if (lottoNumbers.some((number) => 45 < number)) { + throw new Error("로또 번호 중에 45보다 큰 번호가 있습니다."); + } + if (isDuplicated(lottoNumbers)) { throw new Error("중복되는 로또 번호가 있습니다."); } From fd17d31d60e23b1b33cda578ae732987d73fe2fc Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 22:25:28 +0900 Subject: [PATCH 12/87] =?UTF-8?q?test:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js index 375e1f2..2e750a5 100644 --- a/src/__tests__/Lotto.test.js +++ b/src/__tests__/Lotto.test.js @@ -36,9 +36,27 @@ describe("Lotto 클래스 테스트.", () => { ); }); + test("로또 번호 중에 1보다 작은 번호가 있으면 오류가 발생한다.", () => { + expect(() => new Lotto([0, 2, 3, 4, 5, 45])).toThrowError( + "로또 번호 중에 1보다 작은 번호가 있습니다." + ); + }); + + test("로또 번호 중에 45보다 큰 번호가 있으면 오류가 발생한다.", () => { + expect(() => new Lotto([1, 2, 3, 4, 5, 46])).toThrowError( + "로또 번호 중에 45보다 큰 번호가 있습니다." + ); + }); + test("로또 번호 중에 중복되는 번호가 있으면 오류가 발생한다.", () => { expect(() => new Lotto([1, 2, 3, 4, 5, 5])).toThrowError( "중복되는 로또 번호가 있습니다." ); }); + + test("로또 번호를 가져옵니다.", () => { + const lotto = new Lotto([1, 2, 3, 4, 5, 6]); + + expect(lotto.numbers).toEqual([1, 2, 3, 4, 5, 6]); + }); }); From 86dd7f47c83ee3f0272fb71ec4eb3f2e5e8b9679 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 23:23:10 +0900 Subject: [PATCH 13/87] =?UTF-8?q?feat:=20LottoMachine=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoMachine.js | 56 ++++++++++++++++++++++++++++++++++++++ src/domain/index.js | 1 + 2 files changed, 57 insertions(+) create mode 100644 src/domain/LottoMachine.js diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js new file mode 100644 index 0000000..f86a410 --- /dev/null +++ b/src/domain/LottoMachine.js @@ -0,0 +1,56 @@ +import { isDuplicated } from "../utils/index.js"; +import Lotto from "./Lotto.js"; + +class LottoMachine { + static #PRICE_PER_ONE = 1000; + #count; + #lottos; + + constructor(price) { + LottoMachine.#validatePrice(price); + + this.#count = LottoMachine.#countLotto(price); + + this.#lottos = Array.from({ length: this.#count }).map(() => + LottoMachine.#createLotto() + ); + } + + get count() { + return this.#count; + } + + get lottos() { + return this.#lottos; + } + + static #createLotto() { + const lottoNumbers = []; + + while (lottoNumbers.length < 6) { + const num = Math.floor(Math.random() * 45) + 1; + + if (!isDuplicated(lottoNumbers.concat(num))) { + lottoNumbers.push(num); + } + } + + return new Lotto(lottoNumbers); + } + + static #countLotto(price) { + return Math.floor(price / LottoMachine.#PRICE_PER_ONE); + } + + static #validatePrice(price) { + if (typeof price !== "number") { + throw new Error("로또 구입 금액으로 숫자를 입력해야 합니다."); + } + + if (price < LottoMachine.#PRICE_PER_ONE) { + throw new Error("로또 구입 금액은 1000원이상이어야 합니다."); + } + } +} + +export default LottoMachine; diff --git a/src/domain/index.js b/src/domain/index.js index 82190cd..cfa0132 100644 --- a/src/domain/index.js +++ b/src/domain/index.js @@ -1 +1,2 @@ export { default as Lotto } from "./Lotto.js"; +export { default as LottoMachine } from "./LottoMachine.js"; From e776555ed2ba3ca464cc1707237c7f0bf3c11121 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Wed, 3 Jul 2024 23:23:31 +0900 Subject: [PATCH 14/87] =?UTF-8?q?test:=20LottoMachine=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoMachine.test.js | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/__tests__/LottoMachine.test.js diff --git a/src/__tests__/LottoMachine.test.js b/src/__tests__/LottoMachine.test.js new file mode 100644 index 0000000..c1ddedc --- /dev/null +++ b/src/__tests__/LottoMachine.test.js @@ -0,0 +1,38 @@ +import { describe, test, expect } from "vitest"; +import { LottoMachine } from "../domain/index.js"; + +describe("LottoMachine 클래스 테스트", () => { + test("로또를 구입할 때 숫자가 아닌 값을 받으면 오류가 발생한다.", () => { + expect(() => new LottoMachine("1")).toThrowError( + "로또 구입 금액으로 숫자를 입력해야 합니다." + ); + }); + + test("로또를 구입할 때 1000원미만의 값을 받으면 오류가 발생한다.", () => { + expect(() => new LottoMachine(999)).toThrowError( + "로또 구입 금액은 1000원이상이어야 합니다." + ); + }); + + test.each([ + { price: 1001, count: 1 }, + { price: 2500, count: 2 }, + { price: 3999, count: 3 }, + { price: 10999, count: 10 }, + ])( + "로또를 $price원만큼 구매하면 $count장을 발행한다.", + ({ price, count }) => { + const lottoMachine = new LottoMachine(price); + + expect(lottoMachine.count).toBe(count); + } + ); + + test("무작위 번호 6개가 적힌 로또를 받는다.", () => { + const lottoMachine = new LottoMachine(1000); + + const lotto = lottoMachine.lottos[0]; + + expect(lotto.numbers).toHaveLength(6); + }); +}); From cda3a775971750a06ccc92eb7a7a2091b9841803 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 15:20:32 +0900 Subject: [PATCH 15/87] =?UTF-8?q?refactor:=20getRandomNumber=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoMachine.js | 22 ++++++++++++---------- src/utils/getRandomNumber.js | 5 +++++ src/utils/index.js | 1 + 3 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 src/utils/getRandomNumber.js diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index f86a410..5b9ecab 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -1,4 +1,4 @@ -import { isDuplicated } from "../utils/index.js"; +import { getRandomNumber, isDuplicated } from "../utils/index.js"; import Lotto from "./Lotto.js"; class LottoMachine { @@ -8,12 +8,8 @@ class LottoMachine { constructor(price) { LottoMachine.#validatePrice(price); - this.#count = LottoMachine.#countLotto(price); - - this.#lottos = Array.from({ length: this.#count }).map(() => - LottoMachine.#createLotto() - ); + this.#lottos = LottoMachine.#createLottos(this.#count); } get count() { @@ -24,15 +20,21 @@ class LottoMachine { return this.#lottos; } + static #createLottos(count) { + return Array.from({ length: count }).map(() => LottoMachine.#createLotto()); + } + static #createLotto() { const lottoNumbers = []; + const addLottoNumber = (number) => { + const nextLottoNumbers = lottoNumbers.concat(number); + !isDuplicated(nextLottoNumbers) && lottoNumbers.push(number); + }; while (lottoNumbers.length < 6) { - const num = Math.floor(Math.random() * 45) + 1; + const num = getRandomNumber(1, 45); - if (!isDuplicated(lottoNumbers.concat(num))) { - lottoNumbers.push(num); - } + addLottoNumber(num); } return new Lotto(lottoNumbers); diff --git a/src/utils/getRandomNumber.js b/src/utils/getRandomNumber.js new file mode 100644 index 0000000..dca72e9 --- /dev/null +++ b/src/utils/getRandomNumber.js @@ -0,0 +1,5 @@ +function getRandomNumber(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +export default getRandomNumber; diff --git a/src/utils/index.js b/src/utils/index.js index 36ac996..3122d0b 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1 +1,2 @@ export { default as isDuplicated } from "./isDuplicated.js"; +export { default as getRandomNumber } from "./getRandomNumber.js"; From 6f8ddc9026862694fcccd9fabed8392252ddce55 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 15:27:52 +0900 Subject: [PATCH 16/87] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=88=AB=EC=9E=90=20=EC=83=81=EC=88=98?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/index.js | 1 + src/constants/lotto.js | 3 +++ src/domain/Lotto.js | 21 +++++++++++++++------ src/domain/LottoMachine.js | 13 ++++++++++--- 4 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 src/constants/index.js create mode 100644 src/constants/lotto.js diff --git a/src/constants/index.js b/src/constants/index.js new file mode 100644 index 0000000..d24cf05 --- /dev/null +++ b/src/constants/index.js @@ -0,0 +1 @@ +export * from "./lotto.js"; diff --git a/src/constants/lotto.js b/src/constants/lotto.js new file mode 100644 index 0000000..021207e --- /dev/null +++ b/src/constants/lotto.js @@ -0,0 +1,3 @@ +export const MIN_LOTTO_NUMBER = 1; +export const MAX_LOTTO_NUMBER = 45; +export const LOTTO_NUMBERS_SIZE = 6; diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index 1d77f36..f11a12e 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -1,4 +1,9 @@ import { isDuplicated } from "../utils/index.js"; +import { + MIN_LOTTO_NUMBER, + MAX_LOTTO_NUMBER, + LOTTO_NUMBERS_SIZE, +} from "../constants/index.js"; class Lotto { #lottoNumbers; @@ -18,20 +23,24 @@ class Lotto { throw new Error("로또 번호로 적합하지 않은 값입니다."); } - if (lottoNumbers.length !== 6) { - throw new Error("로또 번호는 6개여야 합니다."); + if (lottoNumbers.length !== LOTTO_NUMBERS_SIZE) { + throw new Error(`로또 번호는 ${LOTTO_NUMBERS_SIZE}개여야 합니다.`); } if (lottoNumbers.some((number) => typeof number !== "number")) { throw new Error("로또 번호 중에 적합하지 않은 값이 있습니다."); } - if (lottoNumbers.some((number) => number < 1)) { - throw new Error("로또 번호 중에 1보다 작은 번호가 있습니다."); + if (lottoNumbers.some((number) => number < MIN_LOTTO_NUMBER)) { + throw new Error( + `로또 번호 중에 ${MIN_LOTTO_NUMBER}보다 작은 번호가 있습니다.` + ); } - if (lottoNumbers.some((number) => 45 < number)) { - throw new Error("로또 번호 중에 45보다 큰 번호가 있습니다."); + if (lottoNumbers.some((number) => MAX_LOTTO_NUMBER < number)) { + throw new Error( + `로또 번호 중에 ${MAX_LOTTO_NUMBER}보다 큰 번호가 있습니다.` + ); } if (isDuplicated(lottoNumbers)) { diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index 5b9ecab..34fbd09 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -1,4 +1,9 @@ import { getRandomNumber, isDuplicated } from "../utils/index.js"; +import { + MIN_LOTTO_NUMBER, + MAX_LOTTO_NUMBER, + LOTTO_NUMBERS_SIZE, +} from "../constants/index.js"; import Lotto from "./Lotto.js"; class LottoMachine { @@ -31,8 +36,8 @@ class LottoMachine { !isDuplicated(nextLottoNumbers) && lottoNumbers.push(number); }; - while (lottoNumbers.length < 6) { - const num = getRandomNumber(1, 45); + while (lottoNumbers.length < LOTTO_NUMBERS_SIZE) { + const num = getRandomNumber(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER); addLottoNumber(num); } @@ -50,7 +55,9 @@ class LottoMachine { } if (price < LottoMachine.#PRICE_PER_ONE) { - throw new Error("로또 구입 금액은 1000원이상이어야 합니다."); + throw new Error( + `로또 구입 금액은 ${LottoMachine.#PRICE_PER_ONE}원이상이어야 합니다.` + ); } } } From eca33499c05e941d58d003bfa7aaa1293432f87b Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 15:48:01 +0900 Subject: [PATCH 17/87] =?UTF-8?q?refactor:=20=EB=B6=88=EB=B3=80=EC=84=B1?= =?UTF-8?q?=20=EC=9C=A0=EC=A7=80=EB=A5=BC=20=EC=9C=84=ED=95=B4=20deepCopy?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoMachine.js | 16 +++++++++++----- src/utils/deepCopy.js | 27 +++++++++++++++++++++++++++ src/utils/index.js | 1 + 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 src/utils/deepCopy.js diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index 34fbd09..0a2596b 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -1,4 +1,4 @@ -import { getRandomNumber, isDuplicated } from "../utils/index.js"; +import { getRandomNumber, isDuplicated, deepCopy } from "../utils/index.js"; import { MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER, @@ -22,11 +22,17 @@ class LottoMachine { } get lottos() { - return this.#lottos; + const copiedLottos = deepCopy(this.#lottos); + + return copiedLottos; } static #createLottos(count) { - return Array.from({ length: count }).map(() => LottoMachine.#createLotto()); + return Array.from({ length: count }).map(() => { + const lotto = LottoMachine.#createLotto(); + + return lotto.numbers; + }); } static #createLotto() { @@ -37,9 +43,9 @@ class LottoMachine { }; while (lottoNumbers.length < LOTTO_NUMBERS_SIZE) { - const num = getRandomNumber(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER); + const randomNumber = getRandomNumber(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER); - addLottoNumber(num); + addLottoNumber(randomNumber); } return new Lotto(lottoNumbers); diff --git a/src/utils/deepCopy.js b/src/utils/deepCopy.js new file mode 100644 index 0000000..23d550a --- /dev/null +++ b/src/utils/deepCopy.js @@ -0,0 +1,27 @@ +function deepCopy(value) { + if (value instanceof Map) { + throw new Error("The value must not be Map"); + } + + if (value instanceof Set) { + throw new Error("The value must not be Set"); + } + + if (Array.isArray(value)) { + return value.map((v) => deepCopy(v)); + } else if (value === null) { + return null; + } else if (typeof value === "object") { + const obj = {}; + + for (const key of Object.keys(value)) { + obj[key] = deepCopy(value[key]); + } + + return obj; + } else { + return value; + } +} + +export default deepCopy; diff --git a/src/utils/index.js b/src/utils/index.js index 3122d0b..1ffb0d0 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,2 +1,3 @@ export { default as isDuplicated } from "./isDuplicated.js"; export { default as getRandomNumber } from "./getRandomNumber.js"; +export { default as deepCopy } from "./deepCopy.js"; From 5f550f1cd3e4cda4cbd361d3759f2e9d46914183 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 15:48:44 +0900 Subject: [PATCH 18/87] =?UTF-8?q?test:=20LottoMachine=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoMachine.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/LottoMachine.test.js b/src/__tests__/LottoMachine.test.js index c1ddedc..ffe828d 100644 --- a/src/__tests__/LottoMachine.test.js +++ b/src/__tests__/LottoMachine.test.js @@ -31,8 +31,8 @@ describe("LottoMachine 클래스 테스트", () => { test("무작위 번호 6개가 적힌 로또를 받는다.", () => { const lottoMachine = new LottoMachine(1000); - const lotto = lottoMachine.lottos[0]; + const lottoNumbers = lottoMachine.lottos[0]; - expect(lotto.numbers).toHaveLength(6); + expect(lottoNumbers).toHaveLength(6); }); }); From 10e8c4dd49ae1d6e5ee7a913cb96f9d1205af019 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 16:17:06 +0900 Subject: [PATCH 19/87] =?UTF-8?q?refactor:=20=EB=B3=B4=EB=84=88=EC=8A=A4?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=EC=97=90=20=EC=82=AC=EC=9A=A9=ED=95=A0=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/Lotto.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index f11a12e..eeaa15c 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -18,6 +18,14 @@ class Lotto { return [...this.#lottoNumbers]; } + static isLessThanMinLottoNumber(number) { + return number < MIN_LOTTO_NUMBER; + } + + static isGreaterThanMaxLottoNumber(number) { + return MAX_LOTTO_NUMBER < number; + } + static #validateLottoNumbers(lottoNumbers) { if (!Array.isArray(lottoNumbers)) { throw new Error("로또 번호로 적합하지 않은 값입니다."); @@ -31,13 +39,13 @@ class Lotto { throw new Error("로또 번호 중에 적합하지 않은 값이 있습니다."); } - if (lottoNumbers.some((number) => number < MIN_LOTTO_NUMBER)) { + if (lottoNumbers.some(Lotto.isLessThanMinLottoNumber)) { throw new Error( `로또 번호 중에 ${MIN_LOTTO_NUMBER}보다 작은 번호가 있습니다.` ); } - if (lottoNumbers.some((number) => MAX_LOTTO_NUMBER < number)) { + if (lottoNumbers.some(Lotto.isGreaterThanMaxLottoNumber)) { throw new Error( `로또 번호 중에 ${MAX_LOTTO_NUMBER}보다 큰 번호가 있습니다.` ); From 40f1f10033c132a7582a487096f3534b52993e02 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 17:15:28 +0900 Subject: [PATCH 20/87] =?UTF-8?q?feat:=20WinningLotto=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/WinningLotto.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/domain/WinningLotto.js diff --git a/src/domain/WinningLotto.js b/src/domain/WinningLotto.js new file mode 100644 index 0000000..4f50159 --- /dev/null +++ b/src/domain/WinningLotto.js @@ -0,0 +1,33 @@ +import { isDuplicated } from "../utils/index.js"; +import Lotto from "./Lotto.js"; + +class WinningLotto extends Lotto { + #bonusNumber; + + constructor(lottoNumbers, bonusNumber) { + super(lottoNumbers); + WinningLotto.#validateBonusNumber(this.numbers, bonusNumber); + + this.#bonusNumber = bonusNumber; + } + + get bonusNumber() { + return this.#bonusNumber; + } + + static #validateBonusNumber(winningNumbers, bonusNumber) { + if (Lotto.isLessThanMinLottoNumber(bonusNumber)) { + throw new Error("보너스 번호는 1이상이어야 합니다."); + } + + if (Lotto.isGreaterThanMaxLottoNumber(bonusNumber)) { + throw new Error("보너스 번호는 45이하여야 합니다."); + } + + if (isDuplicated(winningNumbers.concat(bonusNumber))) { + throw new Error("당첨 번호 중에 보너스 번호와 중복되는 번호가 있습니다."); + } + } +} + +export default WinningLotto; From ce829804952100e2db16049c136b76871bd7c112 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 17:15:44 +0900 Subject: [PATCH 21/87] =?UTF-8?q?test:=20WinningLotto=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/WinningLotto.test.js | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/__tests__/WinningLotto.test.js diff --git a/src/__tests__/WinningLotto.test.js b/src/__tests__/WinningLotto.test.js new file mode 100644 index 0000000..f62e72b --- /dev/null +++ b/src/__tests__/WinningLotto.test.js @@ -0,0 +1,40 @@ +import { describe, expect, test } from "vitest"; +import WinningLotto from "../domain/WinningLotto.js"; + +describe("WinningLotto 클래스 테스트", () => { + test("보너스 번호가 1보다 작으면 오류가 발생한다.", () => { + const lottoNumbers = [1, 2, 3, 4, 5, 6]; + const bonusNumber = 0; + + expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrow( + "보너스 번호는 1이상이어야 합니다." + ); + }); + + test("보너스 번호가 45보다 크면 오류가 발생한다.", () => { + const lottoNumbers = [1, 2, 3, 4, 5, 6]; + const bonusNumber = 46; + + expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrow( + "보너스 번호는 45이하여야 합니다." + ); + }); + + test("당첨 번호 중에 보너스 번호와 중복되는 번호가 있으면 오류가 발생한다.", () => { + const lottoNumbers = [1, 2, 3, 4, 5, 6]; + const bonusNumber = 6; + + expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrow( + "당첨 번호 중에 보너스 번호와 중복되는 번호가 있습니다." + ); + }); + + test("보너스 번호를 가져온다.", () => { + const lottoNumbers = [1, 2, 3, 4, 5, 6]; + const bonusNumber = 45; + + const winningLotto = new WinningLotto(lottoNumbers, bonusNumber); + + expect(winningLotto.bonusNumber).toBe(45); + }); +}); From c4d6d2b0f2a5e3611814d08df1d10015f818f3d2 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 17:24:01 +0900 Subject: [PATCH 22/87] =?UTF-8?q?refactor:=20=EC=83=81=EC=88=98=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20Lotto=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=95=98=EA=B3=A0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/index.js | 1 - src/constants/lotto.js | 3 --- src/domain/Lotto.js | 21 ++++++++++----------- src/domain/LottoMachine.js | 9 ++------- src/domain/WinningLotto.js | 4 ++-- 5 files changed, 14 insertions(+), 24 deletions(-) delete mode 100644 src/constants/index.js delete mode 100644 src/constants/lotto.js diff --git a/src/constants/index.js b/src/constants/index.js deleted file mode 100644 index d24cf05..0000000 --- a/src/constants/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from "./lotto.js"; diff --git a/src/constants/lotto.js b/src/constants/lotto.js deleted file mode 100644 index 021207e..0000000 --- a/src/constants/lotto.js +++ /dev/null @@ -1,3 +0,0 @@ -export const MIN_LOTTO_NUMBER = 1; -export const MAX_LOTTO_NUMBER = 45; -export const LOTTO_NUMBERS_SIZE = 6; diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index eeaa15c..923a34d 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -1,11 +1,10 @@ import { isDuplicated } from "../utils/index.js"; -import { - MIN_LOTTO_NUMBER, - MAX_LOTTO_NUMBER, - LOTTO_NUMBERS_SIZE, -} from "../constants/index.js"; class Lotto { + static MIN_NUMBER = 1; + static MAX_NUMBER = 45; + static NUMBERS_SIZE = 6; + #lottoNumbers; constructor(lottoNumbers) { @@ -19,11 +18,11 @@ class Lotto { } static isLessThanMinLottoNumber(number) { - return number < MIN_LOTTO_NUMBER; + return number < Lotto.MIN_NUMBER; } static isGreaterThanMaxLottoNumber(number) { - return MAX_LOTTO_NUMBER < number; + return Lotto.MAX_NUMBER < number; } static #validateLottoNumbers(lottoNumbers) { @@ -31,8 +30,8 @@ class Lotto { throw new Error("로또 번호로 적합하지 않은 값입니다."); } - if (lottoNumbers.length !== LOTTO_NUMBERS_SIZE) { - throw new Error(`로또 번호는 ${LOTTO_NUMBERS_SIZE}개여야 합니다.`); + if (lottoNumbers.length !== Lotto.NUMBERS_SIZE) { + throw new Error(`로또 번호는 ${Lotto.NUMBERS_SIZE}개여야 합니다.`); } if (lottoNumbers.some((number) => typeof number !== "number")) { @@ -41,13 +40,13 @@ class Lotto { if (lottoNumbers.some(Lotto.isLessThanMinLottoNumber)) { throw new Error( - `로또 번호 중에 ${MIN_LOTTO_NUMBER}보다 작은 번호가 있습니다.` + `로또 번호 중에 ${Lotto.MIN_NUMBER}보다 작은 번호가 있습니다.` ); } if (lottoNumbers.some(Lotto.isGreaterThanMaxLottoNumber)) { throw new Error( - `로또 번호 중에 ${MAX_LOTTO_NUMBER}보다 큰 번호가 있습니다.` + `로또 번호 중에 ${Lotto.MAX_NUMBER}보다 큰 번호가 있습니다.` ); } diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index 0a2596b..837dfd8 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -1,9 +1,4 @@ import { getRandomNumber, isDuplicated, deepCopy } from "../utils/index.js"; -import { - MIN_LOTTO_NUMBER, - MAX_LOTTO_NUMBER, - LOTTO_NUMBERS_SIZE, -} from "../constants/index.js"; import Lotto from "./Lotto.js"; class LottoMachine { @@ -42,8 +37,8 @@ class LottoMachine { !isDuplicated(nextLottoNumbers) && lottoNumbers.push(number); }; - while (lottoNumbers.length < LOTTO_NUMBERS_SIZE) { - const randomNumber = getRandomNumber(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER); + while (lottoNumbers.length < Lotto.NUMBERS_SIZE) { + const randomNumber = getRandomNumber(Lotto.MIN_NUMBER, Lotto.MAX_NUMBER); addLottoNumber(randomNumber); } diff --git a/src/domain/WinningLotto.js b/src/domain/WinningLotto.js index 4f50159..f26d072 100644 --- a/src/domain/WinningLotto.js +++ b/src/domain/WinningLotto.js @@ -17,11 +17,11 @@ class WinningLotto extends Lotto { static #validateBonusNumber(winningNumbers, bonusNumber) { if (Lotto.isLessThanMinLottoNumber(bonusNumber)) { - throw new Error("보너스 번호는 1이상이어야 합니다."); + throw new Error(`보너스 번호는 ${Lotto.MIN_NUMBER}이상이어야 합니다.`); } if (Lotto.isGreaterThanMaxLottoNumber(bonusNumber)) { - throw new Error("보너스 번호는 45이하여야 합니다."); + throw new Error(`보너스 번호는 ${Lotto.MAX_NUMBER}이하여야 합니다.`); } if (isDuplicated(winningNumbers.concat(bonusNumber))) { From eb7368de006eeb75a270ff470b787e90bcd6e6d6 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 21:21:35 +0900 Subject: [PATCH 23/87] =?UTF-8?q?refactor:=20lottos=EB=B0=B0=EC=97=B4?= =?UTF-8?q?=EC=9D=98=20=EC=9B=90=EC=86=8C=EB=A5=BC=20Lotto=20=EC=9D=B8?= =?UTF-8?q?=EC=8A=A4=ED=84=B4=EC=8A=A4=EA=B0=92=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoMachine.test.js | 4 ++-- src/domain/LottoMachine.js | 12 +++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/__tests__/LottoMachine.test.js b/src/__tests__/LottoMachine.test.js index ffe828d..c1ddedc 100644 --- a/src/__tests__/LottoMachine.test.js +++ b/src/__tests__/LottoMachine.test.js @@ -31,8 +31,8 @@ describe("LottoMachine 클래스 테스트", () => { test("무작위 번호 6개가 적힌 로또를 받는다.", () => { const lottoMachine = new LottoMachine(1000); - const lottoNumbers = lottoMachine.lottos[0]; + const lotto = lottoMachine.lottos[0]; - expect(lottoNumbers).toHaveLength(6); + expect(lotto.numbers).toHaveLength(6); }); }); diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index 837dfd8..d75759d 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -1,4 +1,4 @@ -import { getRandomNumber, isDuplicated, deepCopy } from "../utils/index.js"; +import { getRandomNumber, isDuplicated } from "../utils/index.js"; import Lotto from "./Lotto.js"; class LottoMachine { @@ -17,17 +17,11 @@ class LottoMachine { } get lottos() { - const copiedLottos = deepCopy(this.#lottos); - - return copiedLottos; + return [...this.#lottos]; } static #createLottos(count) { - return Array.from({ length: count }).map(() => { - const lotto = LottoMachine.#createLotto(); - - return lotto.numbers; - }); + return Array.from({ length: count }).map(() => LottoMachine.#createLotto()); } static #createLotto() { From bc07e246626a0091419ec455cd7efac728f9c9b8 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 22:52:20 +0900 Subject: [PATCH 24/87] =?UTF-8?q?fix:=20WinningLotto=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20export=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/WinningLotto.test.js | 2 +- src/domain/index.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/__tests__/WinningLotto.test.js b/src/__tests__/WinningLotto.test.js index f62e72b..6f42087 100644 --- a/src/__tests__/WinningLotto.test.js +++ b/src/__tests__/WinningLotto.test.js @@ -1,5 +1,5 @@ import { describe, expect, test } from "vitest"; -import WinningLotto from "../domain/WinningLotto.js"; +import { WinningLotto } from "../domain/index.js"; describe("WinningLotto 클래스 테스트", () => { test("보너스 번호가 1보다 작으면 오류가 발생한다.", () => { diff --git a/src/domain/index.js b/src/domain/index.js index cfa0132..6089f80 100644 --- a/src/domain/index.js +++ b/src/domain/index.js @@ -1,2 +1,3 @@ export { default as Lotto } from "./Lotto.js"; export { default as LottoMachine } from "./LottoMachine.js"; +export { default as WinningLotto } from "./WinningLotto.js"; From 82d0a018349d564b64e63aea09e92ecc9dcb074f Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 22:52:42 +0900 Subject: [PATCH 25/87] =?UTF-8?q?feat:=20LottoCalculator=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoCalculator.js | 44 +++++++++++++++++++++++++++++++++++ src/domain/index.js | 1 + 2 files changed, 45 insertions(+) create mode 100644 src/domain/LottoCalculator.js diff --git a/src/domain/LottoCalculator.js b/src/domain/LottoCalculator.js new file mode 100644 index 0000000..55029b7 --- /dev/null +++ b/src/domain/LottoCalculator.js @@ -0,0 +1,44 @@ +class LottoCalculator { + #winningCounts; + + constructor(lottos, winningLotto) { + this.#winningCounts = { + 1: 0, + 2: 0, + 3: 0, + 4: 0, + 5: 0, + }; + + this.#updateWinningCounts(lottos, winningLotto); + } + + get winningCounts() { + return { ...this.#winningCounts }; + } + + #updateWinningCounts(lottos, winningLotto) { + const result = lottos.map((lotto) => ({ + matchedCount: lotto.numbers.filter((number) => + winningLotto.numbers.includes(number) + ).length, + isMatchedBonus: lotto.numbers.includes(winningLotto.bonusNumber), + })); + + result.forEach(({ matchedCount, isMatchedBonus }) => { + if (matchedCount === 6) { + this.#winningCounts[1] += 1; + } else if (matchedCount === 5 && isMatchedBonus) { + this.#winningCounts[2] += 1; + } else if (matchedCount === 5) { + this.#winningCounts[3] += 1; + } else if (matchedCount === 4) { + this.#winningCounts[4] += 1; + } else if (matchedCount === 3) { + this.#winningCounts[5] += 1; + } + }); + } +} + +export default LottoCalculator; diff --git a/src/domain/index.js b/src/domain/index.js index 6089f80..257e1b2 100644 --- a/src/domain/index.js +++ b/src/domain/index.js @@ -1,3 +1,4 @@ export { default as Lotto } from "./Lotto.js"; export { default as LottoMachine } from "./LottoMachine.js"; export { default as WinningLotto } from "./WinningLotto.js"; +export { default as LottoCalculator } from "./LottoCalculator.js"; From a85d0264713b2b5b6fdbebe44cdd69fdd9ee2f2a Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Thu, 4 Jul 2024 22:53:07 +0900 Subject: [PATCH 26/87] =?UTF-8?q?test:=20LottoCalculator=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoCalculator.test.js | 96 +++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/__tests__/LottoCalculator.test.js diff --git a/src/__tests__/LottoCalculator.test.js b/src/__tests__/LottoCalculator.test.js new file mode 100644 index 0000000..8793ed7 --- /dev/null +++ b/src/__tests__/LottoCalculator.test.js @@ -0,0 +1,96 @@ +import { describe, expect, test, vi } from "vitest"; +import { + LottoMachine, + WinningLotto, + LottoCalculator, + Lotto, +} from "../domain/index.js"; + +describe("LottoCalculator 클래스 테스트", () => { + test("1등부터 5등까지 당첨된 수를 가져온다.", () => { + const lottoMachine = new LottoMachine(6000); + const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); + + const { winningCounts } = new LottoCalculator( + lottoMachine.lottos, + winningLotto + ); + + expect(winningCounts).toHaveProperty("1"); + expect(winningCounts).toHaveProperty("2"); + expect(winningCounts).toHaveProperty("3"); + expect(winningCounts).toHaveProperty("4"); + expect(winningCounts).toHaveProperty("5"); + + expect(typeof winningCounts[1]).toBe("number"); + expect(typeof winningCounts[2]).toBe("number"); + expect(typeof winningCounts[3]).toBe("number"); + expect(typeof winningCounts[4]).toBe("number"); + expect(typeof winningCounts[5]).toBe("number"); + }); + + test("로또 번호 중에 1등 당첨이 하나있다.", () => { + const lotto = new Lotto([1, 2, 3, 4, 5, 6]); + const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); + + const { winningCounts } = new LottoCalculator( + lottoMachineMock.lottos, + winningLotto + ); + + expect(winningCounts).toHaveProperty("1", 1); + }); + + test("로또 번호 중에 2등 당첨이 하나있다.", () => { + const lotto = new Lotto([1, 2, 3, 4, 5, 7]); + const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); + + const { winningCounts } = new LottoCalculator( + lottoMachineMock.lottos, + winningLotto + ); + + expect(winningCounts).toHaveProperty("2", 1); + }); + + test("로또 번호 중에 3등 당첨이 하나있다.", () => { + const lotto = new Lotto([1, 2, 3, 4, 5, 8]); + const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); + + const { winningCounts } = new LottoCalculator( + lottoMachineMock.lottos, + winningLotto + ); + + expect(winningCounts).toHaveProperty("3", 1); + }); + + test("로또 번호 중에 4등 당첨이 하나있다.", () => { + const lotto = new Lotto([1, 2, 3, 4, 8, 9]); + const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); + + const { winningCounts } = new LottoCalculator( + lottoMachineMock.lottos, + winningLotto + ); + + expect(winningCounts).toHaveProperty("4", 1); + }); + + test("로또 번호 중에 5등 당첨이 하나있다.", () => { + const lotto = new Lotto([1, 2, 3, 8, 9, 10]); + const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); + + const { winningCounts } = new LottoCalculator( + lottoMachineMock.lottos, + winningLotto + ); + + expect(winningCounts).toHaveProperty("5", 1); + }); +}); From d66eca8df0ee758955dd83822ebe8b1999be93f0 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 11:21:46 +0900 Subject: [PATCH 27/87] =?UTF-8?q?feat:=20=EA=B8=88=EC=95=A1=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoCalculator.js | 4 +++- src/domain/LottoMachine.js | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/domain/LottoCalculator.js b/src/domain/LottoCalculator.js index 55029b7..8556580 100644 --- a/src/domain/LottoCalculator.js +++ b/src/domain/LottoCalculator.js @@ -1,7 +1,7 @@ class LottoCalculator { #winningCounts; - constructor(lottos, winningLotto) { + constructor({ price, lottos, winningLotto }) { this.#winningCounts = { 1: 0, 2: 0, @@ -39,6 +39,8 @@ class LottoCalculator { } }); } + + #calcRateOfReturn() {} } export default LottoCalculator; diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index d75759d..97e6c73 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -3,15 +3,21 @@ import Lotto from "./Lotto.js"; class LottoMachine { static #PRICE_PER_ONE = 1000; + #price; #count; #lottos; constructor(price) { LottoMachine.#validatePrice(price); + this.#price = price; this.#count = LottoMachine.#countLotto(price); this.#lottos = LottoMachine.#createLottos(this.#count); } + get price() { + return this.#price; + } + get count() { return this.#count; } From d4e99f2e3aaf02545b48e6d635c5aa39d1a4b406 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 11:22:27 +0900 Subject: [PATCH 28/87] =?UTF-8?q?test:=20=EA=B8=88=EC=95=A1=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=B6=80=EB=B6=84=20=EB=B0=8F=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoCalculator.test.js | 79 +++++++++++++++------------ 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/src/__tests__/LottoCalculator.test.js b/src/__tests__/LottoCalculator.test.js index 8793ed7..68e330e 100644 --- a/src/__tests__/LottoCalculator.test.js +++ b/src/__tests__/LottoCalculator.test.js @@ -8,13 +8,14 @@ import { describe("LottoCalculator 클래스 테스트", () => { test("1등부터 5등까지 당첨된 수를 가져온다.", () => { - const lottoMachine = new LottoMachine(6000); + const { price, lottos } = new LottoMachine(6000); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator( - lottoMachine.lottos, - winningLotto - ); + const { winningCounts } = new LottoCalculator({ + price, + lottos, + winningLotto, + }); expect(winningCounts).toHaveProperty("1"); expect(winningCounts).toHaveProperty("2"); @@ -30,67 +31,75 @@ describe("LottoCalculator 클래스 테스트", () => { }); test("로또 번호 중에 1등 당첨이 하나있다.", () => { - const lotto = new Lotto([1, 2, 3, 4, 5, 6]); - const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const { price, lottos } = createLottoMachineMock([1, 2, 3, 4, 5, 6]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator( - lottoMachineMock.lottos, - winningLotto - ); + const { winningCounts } = new LottoCalculator({ + price, + lottos, + winningLotto, + }); expect(winningCounts).toHaveProperty("1", 1); }); test("로또 번호 중에 2등 당첨이 하나있다.", () => { - const lotto = new Lotto([1, 2, 3, 4, 5, 7]); - const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const { price, lottos } = createLottoMachineMock([1, 2, 3, 4, 5, 7]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator( - lottoMachineMock.lottos, - winningLotto - ); + const { winningCounts } = new LottoCalculator({ + price, + lottos, + winningLotto, + }); expect(winningCounts).toHaveProperty("2", 1); }); test("로또 번호 중에 3등 당첨이 하나있다.", () => { - const lotto = new Lotto([1, 2, 3, 4, 5, 8]); - const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const { price, lottos } = createLottoMachineMock([1, 2, 3, 4, 5, 8]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator( - lottoMachineMock.lottos, - winningLotto - ); + const { winningCounts } = new LottoCalculator({ + price, + lottos, + winningLotto, + }); expect(winningCounts).toHaveProperty("3", 1); }); test("로또 번호 중에 4등 당첨이 하나있다.", () => { - const lotto = new Lotto([1, 2, 3, 4, 8, 9]); - const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const { price, lottos } = createLottoMachineMock([1, 2, 3, 4, 8, 9]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator( - lottoMachineMock.lottos, - winningLotto - ); + const { winningCounts } = new LottoCalculator({ + price, + lottos, + winningLotto, + }); expect(winningCounts).toHaveProperty("4", 1); }); test("로또 번호 중에 5등 당첨이 하나있다.", () => { - const lotto = new Lotto([1, 2, 3, 8, 9, 10]); - const lottoMachineMock = vi.fn().mockReturnValueOnce({ lottos: [lotto] })(); + const { price, lottos } = createLottoMachineMock([1, 2, 3, 8, 9, 10]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator( - lottoMachineMock.lottos, - winningLotto - ); + const { winningCounts } = new LottoCalculator({ + price, + lottos, + winningLotto, + }); expect(winningCounts).toHaveProperty("5", 1); }); }); + +function createLottoMachineMock(lottoNumbers) { + const lotto = new Lotto(lottoNumbers); + const lottoMachineMock = vi.fn(); + lottoMachineMock.mockReturnValueOnce({ price: 1000, lottos: [lotto] }); + + return lottoMachineMock(); +} From 89b8eaf3eb70da13ae69a3aa9842035b8d5c83a8 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 13:17:19 +0900 Subject: [PATCH 29/87] =?UTF-8?q?feat:=20=EC=88=98=EC=9D=B5=EB=A5=A0=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoCalculator.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/domain/LottoCalculator.js b/src/domain/LottoCalculator.js index 8556580..b2acd27 100644 --- a/src/domain/LottoCalculator.js +++ b/src/domain/LottoCalculator.js @@ -1,5 +1,14 @@ class LottoCalculator { + static #LOTTO_PRIZES = { + 1: 2_000_000_000, + 2: 30_000_000, + 3: 1_500_000, + 4: 50000, + 5: 5000, + }; + #winningCounts; + #rateOfReturn; constructor({ price, lottos, winningLotto }) { this.#winningCounts = { @@ -11,12 +20,17 @@ class LottoCalculator { }; this.#updateWinningCounts(lottos, winningLotto); + this.#calcRateOfReturn(price); } get winningCounts() { return { ...this.#winningCounts }; } + get rateOfReturn() { + return this.#rateOfReturn; + } + #updateWinningCounts(lottos, winningLotto) { const result = lottos.map((lotto) => ({ matchedCount: lotto.numbers.filter((number) => @@ -40,7 +54,13 @@ class LottoCalculator { }); } - #calcRateOfReturn() {} + #calcRateOfReturn(price) { + const sumOfPrize = Object.entries(this.#winningCounts) + .map(([ranking, count]) => LottoCalculator.#LOTTO_PRIZES[ranking] * count) + .reduce((acc, cur) => acc + cur, 0); + + this.#rateOfReturn = (sumOfPrize / price) * 100; + } } export default LottoCalculator; From e9f317e026b0ac61f87eba69983298b4c01c4ac2 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 13:17:32 +0900 Subject: [PATCH 30/87] =?UTF-8?q?test:=20=EC=88=98=EC=9D=B5=EB=A5=A0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoCalculator.test.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/__tests__/LottoCalculator.test.js b/src/__tests__/LottoCalculator.test.js index 68e330e..e2dcc78 100644 --- a/src/__tests__/LottoCalculator.test.js +++ b/src/__tests__/LottoCalculator.test.js @@ -94,12 +94,28 @@ describe("LottoCalculator 클래스 테스트", () => { expect(winningCounts).toHaveProperty("5", 1); }); + + test("수익률을 가져온다.", () => { + const { price, lottos } = createLottoMachineMock( + [1, 2, 3, 8, 9, 10], + 10000 + ); + const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); + + const { rateOfReturn } = new LottoCalculator({ + price, + lottos, + winningLotto, + }); + + expect(rateOfReturn).toBe(50); + }); }); -function createLottoMachineMock(lottoNumbers) { +function createLottoMachineMock(lottoNumbers, price = 1000) { const lotto = new Lotto(lottoNumbers); const lottoMachineMock = vi.fn(); - lottoMachineMock.mockReturnValueOnce({ price: 1000, lottos: [lotto] }); + lottoMachineMock.mockReturnValueOnce({ price, lottos: [lotto] }); return lottoMachineMock(); } From fbf2d4fcc1da16b2d9b5c97c00d92e9a12aaba30 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 14:23:26 +0900 Subject: [PATCH 31/87] =?UTF-8?q?refactor:=20=EC=88=9C=EC=88=98=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoCalculator.js | 73 +++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/src/domain/LottoCalculator.js b/src/domain/LottoCalculator.js index b2acd27..2972510 100644 --- a/src/domain/LottoCalculator.js +++ b/src/domain/LottoCalculator.js @@ -19,8 +19,15 @@ class LottoCalculator { 5: 0, }; - this.#updateWinningCounts(lottos, winningLotto); - this.#calcRateOfReturn(price); + this.#winningCounts = LottoCalculator.#updateWinningCounts({ + winningCounts: this.#winningCounts, + lottos, + winningLotto, + }); + this.#rateOfReturn = LottoCalculator.#calcRateOfReturn( + this.#winningCounts, + price + ); } get winningCounts() { @@ -31,35 +38,53 @@ class LottoCalculator { return this.#rateOfReturn; } - #updateWinningCounts(lottos, winningLotto) { - const result = lottos.map((lotto) => ({ - matchedCount: lotto.numbers.filter((number) => - winningLotto.numbers.includes(number) - ).length, - isMatchedBonus: lotto.numbers.includes(winningLotto.bonusNumber), + static #updateWinningCounts({ winningCounts, lottos, winningLotto }) { + const copiedWinningCounts = { ...winningCounts }; + const lottoMatchResults = lottos.map((lotto) => ({ + matchedCount: LottoCalculator.#matchLottoNumbers( + lotto.numbers, + winningLotto.numbers + ), + isMatchedBonusNumber: lotto.numbers.includes(winningLotto.bonusNumber), })); - result.forEach(({ matchedCount, isMatchedBonus }) => { - if (matchedCount === 6) { - this.#winningCounts[1] += 1; - } else if (matchedCount === 5 && isMatchedBonus) { - this.#winningCounts[2] += 1; - } else if (matchedCount === 5) { - this.#winningCounts[3] += 1; - } else if (matchedCount === 4) { - this.#winningCounts[4] += 1; - } else if (matchedCount === 3) { - this.#winningCounts[5] += 1; - } - }); + return LottoCalculator.#countLottoWins( + lottoMatchResults, + copiedWinningCounts + ); + } + + static #matchLottoNumbers(lottoNumber, winningLottoNumber) { + return lottoNumber.filter((number) => winningLottoNumber.includes(number)) + .length; + } + + static #countLottoWins(lottoMatchResults, winningCounts) { + return lottoMatchResults.reduce( + (counts, { matchedCount, isMatchedBonusNumber }) => { + const prizeMap = { + 6: 1, + 5: isMatchedBonusNumber ? 2 : 3, + 4: 4, + 3: 5, + }; + + if (prizeMap[matchedCount]) { + counts[prizeMap[matchedCount]] += 1; + } + + return counts; + }, + winningCounts + ); } - #calcRateOfReturn(price) { - const sumOfPrize = Object.entries(this.#winningCounts) + static #calcRateOfReturn(winningCounts, price) { + const sumOfPrize = Object.entries({ ...winningCounts }) .map(([ranking, count]) => LottoCalculator.#LOTTO_PRIZES[ranking] * count) .reduce((acc, cur) => acc + cur, 0); - this.#rateOfReturn = (sumOfPrize / price) * 100; + return (sumOfPrize / price) * 100; } } From a9471c440473686f28203009a1ca6f0ef6d86106 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 14:38:43 +0900 Subject: [PATCH 32/87] =?UTF-8?q?feat:=20readLineAsynce=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/index.js | 1 + src/utils/readLineAsync.js | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/utils/readLineAsync.js diff --git a/src/utils/index.js b/src/utils/index.js index 1ffb0d0..386508c 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,3 +1,4 @@ export { default as isDuplicated } from "./isDuplicated.js"; export { default as getRandomNumber } from "./getRandomNumber.js"; export { default as deepCopy } from "./deepCopy.js"; +export { default as readLineAsync } from "./readLineAsync.js"; diff --git a/src/utils/readLineAsync.js b/src/utils/readLineAsync.js new file mode 100644 index 0000000..be8424b --- /dev/null +++ b/src/utils/readLineAsync.js @@ -0,0 +1,25 @@ +import readline from "readline"; + +function readLineAsync(query) { + return new Promise((resolve, reject) => { + if (arguments.length !== 1) { + reject(new Error("arguments must be 1")); + } + + if (typeof query !== "string") { + reject(new Error("query must be string")); + } + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + rl.question(query, (input) => { + rl.close(); + resolve(input); + }); + }); +} + +export default readLineAsync; From 7ebaa965754c3523c86f8706cb966d0f8c248b72 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 14:39:53 +0900 Subject: [PATCH 33/87] =?UTF-8?q?feat:=20InputManager=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/service/index.js | 1 + src/service/inputManager.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/service/inputManager.js diff --git a/src/service/index.js b/src/service/index.js index e69de29..20478d0 100644 --- a/src/service/index.js +++ b/src/service/index.js @@ -0,0 +1 @@ +export { default as inputManager } from "./inputManager.js"; diff --git a/src/service/inputManager.js b/src/service/inputManager.js new file mode 100644 index 0000000..72bfc22 --- /dev/null +++ b/src/service/inputManager.js @@ -0,0 +1,32 @@ +import { readLineAsync } from "../utils/index.js"; + +class InputManager { + #inputFn; + + constructor(inputFn) { + this.#inputFn = inputFn; + } + + async scan(query) { + const inputValue = await this.#inputFn(query); + + return inputValue.trim(); + } + + async retryScan(query, processFn) { + try { + const inputValue = await this.scan(query); + + return processFn ? processFn(inputValue) : inputValue; + } catch (error) { + return await this.retryScan( + `${error.message} 다시 입력해주세요.\n`, + processFn + ); + } + } +} + +const inputManager = new InputManager(readLineAsync); + +export default inputManager; From 56a45981eb2a8994481b7c1e64393afb3a970874 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 14:41:04 +0900 Subject: [PATCH 34/87] =?UTF-8?q?feat:=20OutputManager=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/service/index.js | 1 + src/service/outputManager.js | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/service/outputManager.js diff --git a/src/service/index.js b/src/service/index.js index 20478d0..c09c67f 100644 --- a/src/service/index.js +++ b/src/service/index.js @@ -1 +1,2 @@ export { default as inputManager } from "./inputManager.js"; +export { default as outputManager } from "./outputManager.js"; diff --git a/src/service/outputManager.js b/src/service/outputManager.js new file mode 100644 index 0000000..475dfab --- /dev/null +++ b/src/service/outputManager.js @@ -0,0 +1,27 @@ +class OutputManager { + #outputFn; + + constructor(outputFn) { + this.#outputFn = outputFn; + } + + print(value) { + this.#outputFn(`${value}`); + } + + printAll(values, fn) { + values.forEach((value) => { + const resultToPrint = fn(value); + + this.#outputFn(resultToPrint); + }); + } + + linebreak() { + this.#outputFn(""); + } +} + +const outputManager = new OutputManager(console.log); + +export default outputManager; From c41a1fc4f6984fdcc13bfc3fb545e9b33798a4e3 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 14:52:41 +0900 Subject: [PATCH 35/87] =?UTF-8?q?fix:=20=EC=A0=95=EC=88=98=EC=9D=B8?= =?UTF-8?q?=EC=A7=80=20=EA=B2=80=EC=82=AC=ED=95=98=EB=8A=94=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoMachine.test.js | 4 ++-- src/domain/LottoMachine.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/__tests__/LottoMachine.test.js b/src/__tests__/LottoMachine.test.js index c1ddedc..4454b4b 100644 --- a/src/__tests__/LottoMachine.test.js +++ b/src/__tests__/LottoMachine.test.js @@ -2,9 +2,9 @@ import { describe, test, expect } from "vitest"; import { LottoMachine } from "../domain/index.js"; describe("LottoMachine 클래스 테스트", () => { - test("로또를 구입할 때 숫자가 아닌 값을 받으면 오류가 발생한다.", () => { + test("로또를 구입할 때 정수가 아닌 값을 받으면 오류가 발생한다.", () => { expect(() => new LottoMachine("1")).toThrowError( - "로또 구입 금액으로 숫자를 입력해야 합니다." + "로또 구입 금액으로 정수를 입력해야 합니다." ); }); diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index 97e6c73..ca7eecb 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -51,8 +51,8 @@ class LottoMachine { } static #validatePrice(price) { - if (typeof price !== "number") { - throw new Error("로또 구입 금액으로 숫자를 입력해야 합니다."); + if (typeof price !== "number" || !Number.isInteger(price)) { + throw new Error("로또 구입 금액으로 정수를 입력해야 합니다."); } if (price < LottoMachine.#PRICE_PER_ONE) { From 4f786728a81a45a1b7fd95fe1bfb8591064b2af2 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 15:11:41 +0900 Subject: [PATCH 36/87] =?UTF-8?q?feat:=20=EA=B5=AC=EC=9E=85=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=20=EC=9E=85=EB=A0=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main.js b/src/main.js index 96bab59..afb8caf 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,18 @@ -function main() { - console.log('main의 내용을 채워주세요'); +import { inputManager, outputManager } from "./service/index.js"; +import { LottoMachine } from "./domain/index.js"; + +async function main() { + const lottoMachine = await inputManager.retryScan( + "> 구입 금액을 입력해 주세요. ", + (inputValue) => { + const numValue = Number(inputValue); + + return new LottoMachine(numValue); + } + ); + + outputManager.print(`${lottoMachine.count}개 구매했습니다.`); + outputManager.printAll(lottoMachine.lottos, (lottos) => lottos.numbers); } main(); From df5b826d67bb497b0173f837e0e7328f531717f0 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Fri, 5 Jul 2024 15:28:36 +0900 Subject: [PATCH 37/87] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8,=20=EB=B3=B4=EB=84=88=EC=8A=A4=20=EB=B2=88=ED=98=B8?= =?UTF-8?q?=20=EC=9E=85=EB=A0=A5=20=EB=B0=8F=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=ED=86=B5=EA=B3=84=20=EC=B6=9C=EB=A0=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/main.js b/src/main.js index afb8caf..ce7a4a9 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,5 @@ import { inputManager, outputManager } from "./service/index.js"; -import { LottoMachine } from "./domain/index.js"; +import { LottoCalculator, LottoMachine, WinningLotto } from "./domain/index.js"; async function main() { const lottoMachine = await inputManager.retryScan( @@ -13,6 +13,49 @@ async function main() { outputManager.print(`${lottoMachine.count}개 구매했습니다.`); outputManager.printAll(lottoMachine.lottos, (lottos) => lottos.numbers); + + const winningNumbers = await inputManager.retryScan( + "> 당첨 번호를 입력해 주세요. ", + (inputValue) => { + const numbers = inputValue + .split(",") + .map((value) => Number(value.trim())); + + return numbers; + } + ); + + const bonusNumber = await inputManager.retryScan( + "> 보너스 번호를 입력해 주세요. ", + (inputValue) => Number(inputValue) + ); + + const winningLotto = new WinningLotto(winningNumbers, bonusNumber); + + const lottoCalculator = new LottoCalculator({ + price: lottoMachine.price, + lottos: lottoMachine.lottos, + winningLotto, + }); + + outputManager.print("당첨 통계"); + outputManager.print("--------------------"); + outputManager.print( + `3개 일치 (5,000원) - ${lottoCalculator.winningCounts[5]}개` + ); + outputManager.print( + `4개 일치 (50,000원) - ${lottoCalculator.winningCounts[4]}개` + ); + outputManager.print( + `5개 일치 (1,500,000원) - ${lottoCalculator.winningCounts[3]}개` + ); + outputManager.print( + `5개 일치, 보너스 볼 일치 (30,000,000원) - ${lottoCalculator.winningCounts[2]}개` + ); + outputManager.print( + `6개 일치 (2,000,000,000원) - ${lottoCalculator.winningCounts[1]}개` + ); + outputManager.print(`총 수익률은 ${lottoCalculator.rateOfReturn}%입니다.`); } main(); From a5c07c1ea627446963a37a426040979f9a088424 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sat, 6 Jul 2024 15:36:05 +0900 Subject: [PATCH 38/87] =?UTF-8?q?refactor:=20main=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/main.js b/src/main.js index ce7a4a9..348083a 100644 --- a/src/main.js +++ b/src/main.js @@ -2,7 +2,7 @@ import { inputManager, outputManager } from "./service/index.js"; import { LottoCalculator, LottoMachine, WinningLotto } from "./domain/index.js"; async function main() { - const lottoMachine = await inputManager.retryScan( + const { price, count, lottos } = await inputManager.retryScan( "> 구입 금액을 입력해 주세요. ", (inputValue) => { const numValue = Number(inputValue); @@ -11,8 +11,8 @@ async function main() { } ); - outputManager.print(`${lottoMachine.count}개 구매했습니다.`); - outputManager.printAll(lottoMachine.lottos, (lottos) => lottos.numbers); + outputManager.print(`${count}개 구매했습니다.`); + outputManager.printAll(lottos, (lottos) => lottos.numbers); const winningNumbers = await inputManager.retryScan( "> 당첨 번호를 입력해 주세요. ", @@ -24,38 +24,31 @@ async function main() { return numbers; } ); + outputManager.linebreak(); const bonusNumber = await inputManager.retryScan( "> 보너스 번호를 입력해 주세요. ", (inputValue) => Number(inputValue) ); + outputManager.linebreak(); const winningLotto = new WinningLotto(winningNumbers, bonusNumber); - - const lottoCalculator = new LottoCalculator({ - price: lottoMachine.price, - lottos: lottoMachine.lottos, + const { winningCounts, rateOfReturn } = new LottoCalculator({ + price, + lottos, winningLotto, }); outputManager.print("당첨 통계"); outputManager.print("--------------------"); + outputManager.print(`3개 일치 (5,000원) - ${winningCounts[5]}개`); + outputManager.print(`4개 일치 (50,000원) - ${winningCounts[4]}개`); + outputManager.print(`5개 일치 (1,500,000원) - ${winningCounts[3]}개`); outputManager.print( - `3개 일치 (5,000원) - ${lottoCalculator.winningCounts[5]}개` - ); - outputManager.print( - `4개 일치 (50,000원) - ${lottoCalculator.winningCounts[4]}개` - ); - outputManager.print( - `5개 일치 (1,500,000원) - ${lottoCalculator.winningCounts[3]}개` - ); - outputManager.print( - `5개 일치, 보너스 볼 일치 (30,000,000원) - ${lottoCalculator.winningCounts[2]}개` - ); - outputManager.print( - `6개 일치 (2,000,000,000원) - ${lottoCalculator.winningCounts[1]}개` + `5개 일치, 보너스 볼 일치 (30,000,000원) - ${winningCounts[2]}개` ); - outputManager.print(`총 수익률은 ${lottoCalculator.rateOfReturn}%입니다.`); + outputManager.print(`6개 일치 (2,000,000,000원) - ${winningCounts[1]}개`); + outputManager.print(`총 수익률은 ${rateOfReturn}%입니다.`); } main(); From 0bc5f159f66a11e966f9fad42860b0031af34b7e Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sat, 6 Jul 2024 16:08:18 +0900 Subject: [PATCH 39/87] =?UTF-8?q?refactor:=20#count=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=8D=BC=ED=8B=B0=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoMachine.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index ca7eecb..4009f4d 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -4,14 +4,13 @@ import Lotto from "./Lotto.js"; class LottoMachine { static #PRICE_PER_ONE = 1000; #price; - #count; #lottos; constructor(price) { LottoMachine.#validatePrice(price); this.#price = price; - this.#count = LottoMachine.#countLotto(price); - this.#lottos = LottoMachine.#createLottos(this.#count); + const count = LottoMachine.#countLotto(this.#price); + this.#lottos = LottoMachine.#createLottos(count); } get price() { @@ -19,7 +18,7 @@ class LottoMachine { } get count() { - return this.#count; + return this.#lottos.length; } get lottos() { From 3ccfbbcdde2c977dece1daabc57314e1d970f012 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 01:13:33 +0900 Subject: [PATCH 40/87] =?UTF-8?q?refactor:=20=EC=A0=95=EC=88=98=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=20=EC=A1=B0=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/Lotto.js | 2 +- src/domain/LottoMachine.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index 923a34d..fc4642e 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -34,7 +34,7 @@ class Lotto { throw new Error(`로또 번호는 ${Lotto.NUMBERS_SIZE}개여야 합니다.`); } - if (lottoNumbers.some((number) => typeof number !== "number")) { + if (lottoNumbers.some((number) => !Number.isInteger(number))) { throw new Error("로또 번호 중에 적합하지 않은 값이 있습니다."); } diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index 4009f4d..902349b 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -50,7 +50,7 @@ class LottoMachine { } static #validatePrice(price) { - if (typeof price !== "number" || !Number.isInteger(price)) { + if (!Number.isInteger(price)) { throw new Error("로또 구입 금액으로 정수를 입력해야 합니다."); } From 88004ac26b1a18fa2a507226d0e2b0ad6ec2f31e Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 16:13:24 +0900 Subject: [PATCH 41/87] =?UTF-8?q?feat:=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20?= =?UTF-8?q?=EA=B0=90=EC=82=AC=20=EC=9C=A0=ED=8B=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/index.js | 1 + src/utils/validate.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/utils/validate.js diff --git a/src/utils/index.js b/src/utils/index.js index 386508c..38e5284 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -2,3 +2,4 @@ export { default as isDuplicated } from "./isDuplicated.js"; export { default as getRandomNumber } from "./getRandomNumber.js"; export { default as deepCopy } from "./deepCopy.js"; export { default as readLineAsync } from "./readLineAsync.js"; +export * from "./validate.js"; diff --git a/src/utils/validate.js b/src/utils/validate.js new file mode 100644 index 0000000..83a1e91 --- /dev/null +++ b/src/utils/validate.js @@ -0,0 +1,17 @@ +export const throwErrorWithCondition = (condition, errorMessage) => { + if (condition) { + throw new Error(errorMessage); + } +}; + +export const validate = { + type(value, typeValue, errorMessage) { + throwErrorWithCondition(typeof value !== typeValue, errorMessage); + }, + integer(value, errorMessage) { + throwErrorWithCondition(!Number.isInteger(value), errorMessage); + }, + array(value, errorMessage) { + throwErrorWithCondition(!Array.isArray(value), errorMessage); + }, +}; From 502416e322c69ec87ccdf86f9cd85fe82c3b0412 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 16:13:50 +0900 Subject: [PATCH 42/87] =?UTF-8?q?refactor:=20=EC=9C=A0=ED=9A=A8=EC=84=B1?= =?UTF-8?q?=20=EA=B2=80=EC=82=AC=20=EC=9C=A0=ED=8B=B8=EC=9D=84=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/Lotto.js | 59 ++++++++++++++++++++------------------ src/domain/LottoMachine.js | 20 +++++++------ src/domain/WinningLotto.js | 27 +++++++++-------- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/src/domain/Lotto.js b/src/domain/Lotto.js index fc4642e..02d4e3f 100644 --- a/src/domain/Lotto.js +++ b/src/domain/Lotto.js @@ -1,4 +1,8 @@ -import { isDuplicated } from "../utils/index.js"; +import { + isDuplicated, + throwErrorWithCondition, + validate, +} from "../utils/index.js"; class Lotto { static MIN_NUMBER = 1; @@ -26,33 +30,32 @@ class Lotto { } static #validateLottoNumbers(lottoNumbers) { - if (!Array.isArray(lottoNumbers)) { - throw new Error("로또 번호로 적합하지 않은 값입니다."); - } - - if (lottoNumbers.length !== Lotto.NUMBERS_SIZE) { - throw new Error(`로또 번호는 ${Lotto.NUMBERS_SIZE}개여야 합니다.`); - } - - if (lottoNumbers.some((number) => !Number.isInteger(number))) { - throw new Error("로또 번호 중에 적합하지 않은 값이 있습니다."); - } - - if (lottoNumbers.some(Lotto.isLessThanMinLottoNumber)) { - throw new Error( - `로또 번호 중에 ${Lotto.MIN_NUMBER}보다 작은 번호가 있습니다.` - ); - } - - if (lottoNumbers.some(Lotto.isGreaterThanMaxLottoNumber)) { - throw new Error( - `로또 번호 중에 ${Lotto.MAX_NUMBER}보다 큰 번호가 있습니다.` - ); - } - - if (isDuplicated(lottoNumbers)) { - throw new Error("중복되는 로또 번호가 있습니다."); - } + validate.array(lottoNumbers, "로또 번호로 적합하지 않은 값입니다."); + + throwErrorWithCondition( + lottoNumbers.length !== Lotto.NUMBERS_SIZE, + `로또 번호는 ${Lotto.NUMBERS_SIZE}개여야 합니다.` + ); + + throwErrorWithCondition( + lottoNumbers.some((number) => !Number.isInteger(number)), + "로또 번호 중에 적합하지 않은 값이 있습니다." + ); + + throwErrorWithCondition( + lottoNumbers.some(Lotto.isLessThanMinLottoNumber), + `로또 번호 중에 ${Lotto.MIN_NUMBER}보다 작은 번호가 있습니다.` + ); + + throwErrorWithCondition( + lottoNumbers.some(Lotto.isGreaterThanMaxLottoNumber), + `로또 번호 중에 ${Lotto.MAX_NUMBER}보다 큰 번호가 있습니다.` + ); + + throwErrorWithCondition( + isDuplicated(lottoNumbers), + "중복되는 로또 번호가 있습니다." + ); } } diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index 902349b..274cc79 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -1,4 +1,9 @@ -import { getRandomNumber, isDuplicated } from "../utils/index.js"; +import { + getRandomNumber, + isDuplicated, + throwErrorWithCondition, + validate, +} from "../utils/index.js"; import Lotto from "./Lotto.js"; class LottoMachine { @@ -50,15 +55,12 @@ class LottoMachine { } static #validatePrice(price) { - if (!Number.isInteger(price)) { - throw new Error("로또 구입 금액으로 정수를 입력해야 합니다."); - } + validate.integer(price, "로또 구입 금액으로 정수를 입력해야 합니다."); - if (price < LottoMachine.#PRICE_PER_ONE) { - throw new Error( - `로또 구입 금액은 ${LottoMachine.#PRICE_PER_ONE}원이상이어야 합니다.` - ); - } + throwErrorWithCondition( + price < LottoMachine.#PRICE_PER_ONE, + `로또 구입 금액은 ${LottoMachine.#PRICE_PER_ONE}원이상이어야 합니다.` + ); } } diff --git a/src/domain/WinningLotto.js b/src/domain/WinningLotto.js index f26d072..daedef8 100644 --- a/src/domain/WinningLotto.js +++ b/src/domain/WinningLotto.js @@ -1,4 +1,4 @@ -import { isDuplicated } from "../utils/index.js"; +import { isDuplicated, throwErrorWithCondition } from "../utils/index.js"; import Lotto from "./Lotto.js"; class WinningLotto extends Lotto { @@ -16,17 +16,20 @@ class WinningLotto extends Lotto { } static #validateBonusNumber(winningNumbers, bonusNumber) { - if (Lotto.isLessThanMinLottoNumber(bonusNumber)) { - throw new Error(`보너스 번호는 ${Lotto.MIN_NUMBER}이상이어야 합니다.`); - } - - if (Lotto.isGreaterThanMaxLottoNumber(bonusNumber)) { - throw new Error(`보너스 번호는 ${Lotto.MAX_NUMBER}이하여야 합니다.`); - } - - if (isDuplicated(winningNumbers.concat(bonusNumber))) { - throw new Error("당첨 번호 중에 보너스 번호와 중복되는 번호가 있습니다."); - } + throwErrorWithCondition( + Lotto.isLessThanMinLottoNumber(bonusNumber), + `보너스 번호는 ${Lotto.MIN_NUMBER}이상이어야 합니다.` + ); + + throwErrorWithCondition( + Lotto.isGreaterThanMaxLottoNumber(bonusNumber), + `보너스 번호는 ${Lotto.MAX_NUMBER}이하여야 합니다.` + ); + + throwErrorWithCondition( + isDuplicated(winningNumbers.concat(bonusNumber)), + "당첨 번호 중에 보너스 번호와 중복되는 번호가 있습니다." + ); } } From 783e1365cc94a7f9e8d3d1f6e6cc74b156ca7fbd Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 16:24:43 +0900 Subject: [PATCH 43/87] =?UTF-8?q?feat:=20=EC=9E=85=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EB=A7=A4=EB=8B=88=EC=A0=80=EC=97=90=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/service/inputManager.js | 6 ++++-- src/service/outputManager.js | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/service/inputManager.js b/src/service/inputManager.js index 72bfc22..0ba15f2 100644 --- a/src/service/inputManager.js +++ b/src/service/inputManager.js @@ -13,11 +13,13 @@ class InputManager { return inputValue.trim(); } - async retryScan(query, processFn) { + async retryScan(query, processFn = null) { try { const inputValue = await this.scan(query); - return processFn ? processFn(inputValue) : inputValue; + return typeof processFn === "function" + ? processFn(inputValue) + : inputValue; } catch (error) { return await this.retryScan( `${error.message} 다시 입력해주세요.\n`, diff --git a/src/service/outputManager.js b/src/service/outputManager.js index 475dfab..23f866a 100644 --- a/src/service/outputManager.js +++ b/src/service/outputManager.js @@ -9,9 +9,10 @@ class OutputManager { this.#outputFn(`${value}`); } - printAll(values, fn) { + printAll(values, processFn) { values.forEach((value) => { - const resultToPrint = fn(value); + const resultToPrint = + typeof processFn === "function" ? processFn(value) : value; this.#outputFn(resultToPrint); }); From 2ec79a3ed37c02da264badb2e64d4e3b74cf9060 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 16:32:26 +0900 Subject: [PATCH 44/87] =?UTF-8?q?refactor:=20=ED=99=94=EC=82=B4=ED=91=9C?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/getRandomNumber.js | 4 ++-- src/utils/isDuplicated.js | 4 ++-- src/utils/readLineAsync.js | 8 ++------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/utils/getRandomNumber.js b/src/utils/getRandomNumber.js index dca72e9..4ffa325 100644 --- a/src/utils/getRandomNumber.js +++ b/src/utils/getRandomNumber.js @@ -1,5 +1,5 @@ -function getRandomNumber(min, max) { +const getRandomNumber = (min, max) => { return Math.floor(Math.random() * (max - min + 1)) + min; -} +}; export default getRandomNumber; diff --git a/src/utils/isDuplicated.js b/src/utils/isDuplicated.js index 9c01dea..c80df74 100644 --- a/src/utils/isDuplicated.js +++ b/src/utils/isDuplicated.js @@ -1,7 +1,7 @@ -function isDuplicated(array) { +const isDuplicated = (array) => { const set = new Set(array); return set.size !== array.length; -} +}; export default isDuplicated; diff --git a/src/utils/readLineAsync.js b/src/utils/readLineAsync.js index be8424b..d06c644 100644 --- a/src/utils/readLineAsync.js +++ b/src/utils/readLineAsync.js @@ -1,11 +1,7 @@ import readline from "readline"; -function readLineAsync(query) { +const readLineAsync = (query) => { return new Promise((resolve, reject) => { - if (arguments.length !== 1) { - reject(new Error("arguments must be 1")); - } - if (typeof query !== "string") { reject(new Error("query must be string")); } @@ -20,6 +16,6 @@ function readLineAsync(query) { resolve(input); }); }); -} +}; export default readLineAsync; From b7c3d9396dd073919b51d44380771e081607416a Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 16:33:06 +0900 Subject: [PATCH 45/87] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/deepCopy.js | 27 --------------------------- src/utils/index.js | 1 - 2 files changed, 28 deletions(-) delete mode 100644 src/utils/deepCopy.js diff --git a/src/utils/deepCopy.js b/src/utils/deepCopy.js deleted file mode 100644 index 23d550a..0000000 --- a/src/utils/deepCopy.js +++ /dev/null @@ -1,27 +0,0 @@ -function deepCopy(value) { - if (value instanceof Map) { - throw new Error("The value must not be Map"); - } - - if (value instanceof Set) { - throw new Error("The value must not be Set"); - } - - if (Array.isArray(value)) { - return value.map((v) => deepCopy(v)); - } else if (value === null) { - return null; - } else if (typeof value === "object") { - const obj = {}; - - for (const key of Object.keys(value)) { - obj[key] = deepCopy(value[key]); - } - - return obj; - } else { - return value; - } -} - -export default deepCopy; diff --git a/src/utils/index.js b/src/utils/index.js index 38e5284..c563255 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,5 +1,4 @@ export { default as isDuplicated } from "./isDuplicated.js"; export { default as getRandomNumber } from "./getRandomNumber.js"; -export { default as deepCopy } from "./deepCopy.js"; export { default as readLineAsync } from "./readLineAsync.js"; export * from "./validate.js"; From 0e4cadee201b6d5bfaa3afe4b7421f6d616d818d Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 21:01:25 +0900 Subject: [PATCH 46/87] =?UTF-8?q?feat:=20#createLotto=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=A1=9C=EB=98=90=20=EB=B2=88=ED=98=B8=20=EC=A0=95=EB=A0=AC?= =?UTF-8?q?=EB=90=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/LottoMachine.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/domain/LottoMachine.js b/src/domain/LottoMachine.js index 274cc79..1c964b4 100644 --- a/src/domain/LottoMachine.js +++ b/src/domain/LottoMachine.js @@ -35,6 +35,14 @@ class LottoMachine { } static #createLotto() { + const sortedlottoNumbers = LottoMachine.#getLottoNumbers().toSorted( + (a, b) => a - b + ); + + return new Lotto(sortedlottoNumbers); + } + + static #getLottoNumbers() { const lottoNumbers = []; const addLottoNumber = (number) => { const nextLottoNumbers = lottoNumbers.concat(number); @@ -42,12 +50,16 @@ class LottoMachine { }; while (lottoNumbers.length < Lotto.NUMBERS_SIZE) { - const randomNumber = getRandomNumber(Lotto.MIN_NUMBER, Lotto.MAX_NUMBER); + const lottoNumber = LottoMachine.#getLottoNumber(); - addLottoNumber(randomNumber); + addLottoNumber(lottoNumber); } - return new Lotto(lottoNumbers); + return lottoNumbers; + } + + static #getLottoNumber() { + return getRandomNumber(Lotto.MIN_NUMBER, Lotto.MAX_NUMBER); } static #countLotto(price) { From 48d0c42604bc791bf6c64db3f9a0fa88da7d06f9 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 21:29:24 +0900 Subject: [PATCH 47/87] =?UTF-8?q?refactor:=20domain=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=AA=85=EC=9D=84=20model=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 2 +- src/__tests__/LottoCalculator.test.js | 2 +- src/__tests__/LottoMachine.test.js | 2 +- src/__tests__/WinningLotto.test.js | 2 +- src/{domain => model}/Lotto.js | 0 src/{domain => model}/LottoCalculator.js | 0 src/{domain => model}/LottoMachine.js | 0 src/{domain => model}/WinningLotto.js | 0 src/{domain => model}/index.js | 0 9 files changed, 4 insertions(+), 4 deletions(-) rename src/{domain => model}/Lotto.js (100%) rename src/{domain => model}/LottoCalculator.js (100%) rename src/{domain => model}/LottoMachine.js (100%) rename src/{domain => model}/WinningLotto.js (100%) rename src/{domain => model}/index.js (100%) diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js index 2e750a5..ae52eee 100644 --- a/src/__tests__/Lotto.test.js +++ b/src/__tests__/Lotto.test.js @@ -1,5 +1,5 @@ import { describe, test, expect } from "vitest"; -import { Lotto } from "../domain/index.js"; +import { Lotto } from "../model/index.js"; describe("Lotto 클래스 테스트.", () => { test.each([ diff --git a/src/__tests__/LottoCalculator.test.js b/src/__tests__/LottoCalculator.test.js index e2dcc78..bd68aef 100644 --- a/src/__tests__/LottoCalculator.test.js +++ b/src/__tests__/LottoCalculator.test.js @@ -4,7 +4,7 @@ import { WinningLotto, LottoCalculator, Lotto, -} from "../domain/index.js"; +} from "../model/index.js"; describe("LottoCalculator 클래스 테스트", () => { test("1등부터 5등까지 당첨된 수를 가져온다.", () => { diff --git a/src/__tests__/LottoMachine.test.js b/src/__tests__/LottoMachine.test.js index 4454b4b..2070f9c 100644 --- a/src/__tests__/LottoMachine.test.js +++ b/src/__tests__/LottoMachine.test.js @@ -1,5 +1,5 @@ import { describe, test, expect } from "vitest"; -import { LottoMachine } from "../domain/index.js"; +import { LottoMachine } from "../model/index.js"; describe("LottoMachine 클래스 테스트", () => { test("로또를 구입할 때 정수가 아닌 값을 받으면 오류가 발생한다.", () => { diff --git a/src/__tests__/WinningLotto.test.js b/src/__tests__/WinningLotto.test.js index 6f42087..071f894 100644 --- a/src/__tests__/WinningLotto.test.js +++ b/src/__tests__/WinningLotto.test.js @@ -1,5 +1,5 @@ import { describe, expect, test } from "vitest"; -import { WinningLotto } from "../domain/index.js"; +import { WinningLotto } from "../model/index.js"; describe("WinningLotto 클래스 테스트", () => { test("보너스 번호가 1보다 작으면 오류가 발생한다.", () => { diff --git a/src/domain/Lotto.js b/src/model/Lotto.js similarity index 100% rename from src/domain/Lotto.js rename to src/model/Lotto.js diff --git a/src/domain/LottoCalculator.js b/src/model/LottoCalculator.js similarity index 100% rename from src/domain/LottoCalculator.js rename to src/model/LottoCalculator.js diff --git a/src/domain/LottoMachine.js b/src/model/LottoMachine.js similarity index 100% rename from src/domain/LottoMachine.js rename to src/model/LottoMachine.js diff --git a/src/domain/WinningLotto.js b/src/model/WinningLotto.js similarity index 100% rename from src/domain/WinningLotto.js rename to src/model/WinningLotto.js diff --git a/src/domain/index.js b/src/model/index.js similarity index 100% rename from src/domain/index.js rename to src/model/index.js From 4bd886c1d3af3820c66089c01c5d5a23c2fbf087 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 22:07:08 +0900 Subject: [PATCH 48/87] =?UTF-8?q?fix:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EC=A0=95=EC=88=98=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20createWithBonusNum?= =?UTF-8?q?berLater=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/WinningLotto.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/model/WinningLotto.js b/src/model/WinningLotto.js index daedef8..f41c578 100644 --- a/src/model/WinningLotto.js +++ b/src/model/WinningLotto.js @@ -1,4 +1,8 @@ -import { isDuplicated, throwErrorWithCondition } from "../utils/index.js"; +import { + isDuplicated, + throwErrorWithCondition, + validate, +} from "../utils/index.js"; import Lotto from "./Lotto.js"; class WinningLotto extends Lotto { @@ -15,7 +19,13 @@ class WinningLotto extends Lotto { return this.#bonusNumber; } + static createWithBonusLater(lottoNumbers) { + return (bonusNumber) => new WinningLotto(lottoNumbers, bonusNumber); + } + static #validateBonusNumber(winningNumbers, bonusNumber) { + validate.integer(bonusNumber, "보너스 번호는 정수여야 합니다."); + throwErrorWithCondition( Lotto.isLessThanMinLottoNumber(bonusNumber), `보너스 번호는 ${Lotto.MIN_NUMBER}이상이어야 합니다.` From 9b86217187a0da77498a26bafeb8a3f9ae658455 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 22:07:35 +0900 Subject: [PATCH 49/87] =?UTF-8?q?test:=20=EC=A0=95=EC=88=98=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/WinningLotto.test.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/__tests__/WinningLotto.test.js b/src/__tests__/WinningLotto.test.js index 071f894..1efc7c0 100644 --- a/src/__tests__/WinningLotto.test.js +++ b/src/__tests__/WinningLotto.test.js @@ -2,11 +2,20 @@ import { describe, expect, test } from "vitest"; import { WinningLotto } from "../model/index.js"; describe("WinningLotto 클래스 테스트", () => { + test("보너스 번호가 정수가 아니면 오류가 발생한다.", () => { + const lottoNumbers = [1, 2, 3, 4, 5, 6]; + const bonusNumber = "a"; + + expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrowError( + "보너스 번호는 정수여야 합니다." + ); + }); + test("보너스 번호가 1보다 작으면 오류가 발생한다.", () => { const lottoNumbers = [1, 2, 3, 4, 5, 6]; const bonusNumber = 0; - expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrow( + expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrowError( "보너스 번호는 1이상이어야 합니다." ); }); @@ -15,7 +24,7 @@ describe("WinningLotto 클래스 테스트", () => { const lottoNumbers = [1, 2, 3, 4, 5, 6]; const bonusNumber = 46; - expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrow( + expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrowError( "보너스 번호는 45이하여야 합니다." ); }); @@ -24,7 +33,7 @@ describe("WinningLotto 클래스 테스트", () => { const lottoNumbers = [1, 2, 3, 4, 5, 6]; const bonusNumber = 6; - expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrow( + expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrowError( "당첨 번호 중에 보너스 번호와 중복되는 번호가 있습니다." ); }); From b0a1390db96d97b851c9c034d24927fffb937430 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 22:41:25 +0900 Subject: [PATCH 50/87] =?UTF-8?q?feat:=20=EB=B3=B4=EB=84=88=EC=8A=A4=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=EB=A5=BC=20=EB=82=98=EC=A4=91=EC=97=90=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/WinningLotto.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/model/WinningLotto.js b/src/model/WinningLotto.js index f41c578..d3df08e 100644 --- a/src/model/WinningLotto.js +++ b/src/model/WinningLotto.js @@ -10,6 +10,11 @@ class WinningLotto extends Lotto { constructor(lottoNumbers, bonusNumber) { super(lottoNumbers); + + if (bonusNumber === undefined) { + return (bonusNumber) => new WinningLotto(lottoNumbers, bonusNumber); + } + WinningLotto.#validateBonusNumber(this.numbers, bonusNumber); this.#bonusNumber = bonusNumber; @@ -19,10 +24,6 @@ class WinningLotto extends Lotto { return this.#bonusNumber; } - static createWithBonusLater(lottoNumbers) { - return (bonusNumber) => new WinningLotto(lottoNumbers, bonusNumber); - } - static #validateBonusNumber(winningNumbers, bonusNumber) { validate.integer(bonusNumber, "보너스 번호는 정수여야 합니다."); From e4e4f4f2cca6cd1e4c2da1bc3b49d8b8872088cd Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 22:51:52 +0900 Subject: [PATCH 51/87] =?UTF-8?q?feat:=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=A0=88=EC=9D=B4=EC=96=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/calculateWinningResults.js | 13 +++++++++++ src/controller/createWinningLotto.js | 28 +++++++++++++++++++++++ src/controller/index.js | 3 +++ src/controller/purchaseLottos.js | 17 ++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 src/controller/calculateWinningResults.js create mode 100644 src/controller/createWinningLotto.js create mode 100644 src/controller/index.js create mode 100644 src/controller/purchaseLottos.js diff --git a/src/controller/calculateWinningResults.js b/src/controller/calculateWinningResults.js new file mode 100644 index 0000000..1aa4023 --- /dev/null +++ b/src/controller/calculateWinningResults.js @@ -0,0 +1,13 @@ +import { LottoCalculator } from "../model/index.js"; + +const calculateWinningResults = ({ price, lottos, winningLotto }) => { + const { winningCounts, rateOfReturn } = new LottoCalculator({ + price, + lottos, + winningLotto, + }); + + return { winningCounts, rateOfReturn }; +}; + +export default calculateWinningResults; diff --git a/src/controller/createWinningLotto.js b/src/controller/createWinningLotto.js new file mode 100644 index 0000000..fad37e6 --- /dev/null +++ b/src/controller/createWinningLotto.js @@ -0,0 +1,28 @@ +import { inputManager } from "../service/index.js"; +import { WinningLotto } from "../model/index.js"; + +const createWinningLotto = async () => { + const createWinningLotto = await inputManager.retryScan( + "> 당첨 번호를 입력해 주세요. ", + (inputValue) => { + const numbers = inputValue + .split(",") + .map((value) => Number(value.trim())); + + return new WinningLotto(numbers); + } + ); + + const winningLotto = await inputManager.retryScan( + "> 보너스 번호를 입력해 주세요. ", + (inputValue) => { + const bonusNumber = Number(inputValue); + + return createWinningLotto(bonusNumber); + } + ); + + return winningLotto; +}; + +export default createWinningLotto; diff --git a/src/controller/index.js b/src/controller/index.js new file mode 100644 index 0000000..7bdd09f --- /dev/null +++ b/src/controller/index.js @@ -0,0 +1,3 @@ +export { default as purchaseLottos } from "./purchaseLottos.js"; +export { default as createWinningLotto } from "./createWinningLotto.js"; +export { default as calculateWinningResults } from "./calculateWinningResults.js"; diff --git a/src/controller/purchaseLottos.js b/src/controller/purchaseLottos.js new file mode 100644 index 0000000..13f0f77 --- /dev/null +++ b/src/controller/purchaseLottos.js @@ -0,0 +1,17 @@ +import { LottoMachine } from "../model/index.js"; +import { inputManager } from "../service/index.js"; + +const purchaseLottos = async () => { + const { price, count, lottos } = await inputManager.retryScan( + "> 구입 금액을 입력해 주세요. ", + (inputValue) => { + const numValue = Number(inputValue); + + return new LottoMachine(numValue); + } + ); + + return { price, count, lottos }; +}; + +export default purchaseLottos; From ee9f63583a8bae7075c02197b731309edad45709 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 22:52:08 +0900 Subject: [PATCH 52/87] =?UTF-8?q?feat:=20=EB=B7=B0=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view/index.js | 2 ++ src/view/showPurchasedLottos.js | 8 ++++++++ src/view/showWinningResults.js | 16 ++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 src/view/index.js create mode 100644 src/view/showPurchasedLottos.js create mode 100644 src/view/showWinningResults.js diff --git a/src/view/index.js b/src/view/index.js new file mode 100644 index 0000000..d61e2b4 --- /dev/null +++ b/src/view/index.js @@ -0,0 +1,2 @@ +export { default as showPurchasedLottos } from "./showPurchasedLottos.js"; +export { default as showWinningResults } from "./showWinningResults.js"; diff --git a/src/view/showPurchasedLottos.js b/src/view/showPurchasedLottos.js new file mode 100644 index 0000000..ac40460 --- /dev/null +++ b/src/view/showPurchasedLottos.js @@ -0,0 +1,8 @@ +import { outputManager } from "../service/index.js"; + +const showPurchasedLottos = (count, lottos) => { + outputManager.print(`${count}개 구매했습니다.`); + outputManager.printAll(lottos, (lottos) => lottos.numbers); +}; + +export default showPurchasedLottos; diff --git a/src/view/showWinningResults.js b/src/view/showWinningResults.js new file mode 100644 index 0000000..10ba5c0 --- /dev/null +++ b/src/view/showWinningResults.js @@ -0,0 +1,16 @@ +import { outputManager } from "../service/index.js"; + +const showWinningResults = (winningCounts, rateOfReturn) => { + outputManager.print("당첨 통계"); + outputManager.print("--------------------"); + outputManager.print(`3개 일치 (5,000원) - ${winningCounts[5]}개`); + outputManager.print(`4개 일치 (50,000원) - ${winningCounts[4]}개`); + outputManager.print(`5개 일치 (1,500,000원) - ${winningCounts[3]}개`); + outputManager.print( + `5개 일치, 보너스 볼 일치 (30,000,000원) - ${winningCounts[2]}개` + ); + outputManager.print(`6개 일치 (2,000,000,000원) - ${winningCounts[1]}개`); + outputManager.print(`총 수익률은 ${rateOfReturn}%입니다.`); +}; + +export default showWinningResults; From 43c8791f51ced0f0a52fa75c6283d6ce5dea17d3 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 22:52:44 +0900 Subject: [PATCH 53/87] =?UTF-8?q?feat:=20MVC=20=ED=8C=A8=ED=84=B4=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 57 ++++++++++++----------------------------------------- 1 file changed, 13 insertions(+), 44 deletions(-) diff --git a/src/main.js b/src/main.js index 348083a..a9d508b 100644 --- a/src/main.js +++ b/src/main.js @@ -1,54 +1,23 @@ -import { inputManager, outputManager } from "./service/index.js"; -import { LottoCalculator, LottoMachine, WinningLotto } from "./domain/index.js"; +import { + purchaseLottos, + createWinningLotto, + calculateWinningResults, +} from "./controller/index.js"; +import { showPurchasedLottos, showWinningResults } from "./view/index.js"; -async function main() { - const { price, count, lottos } = await inputManager.retryScan( - "> 구입 금액을 입력해 주세요. ", - (inputValue) => { - const numValue = Number(inputValue); +const main = async () => { + const { price, count, lottos } = await purchaseLottos(); - return new LottoMachine(numValue); - } - ); + showPurchasedLottos(count, lottos); - outputManager.print(`${count}개 구매했습니다.`); - outputManager.printAll(lottos, (lottos) => lottos.numbers); - - const winningNumbers = await inputManager.retryScan( - "> 당첨 번호를 입력해 주세요. ", - (inputValue) => { - const numbers = inputValue - .split(",") - .map((value) => Number(value.trim())); - - return numbers; - } - ); - outputManager.linebreak(); - - const bonusNumber = await inputManager.retryScan( - "> 보너스 번호를 입력해 주세요. ", - (inputValue) => Number(inputValue) - ); - outputManager.linebreak(); - - const winningLotto = new WinningLotto(winningNumbers, bonusNumber); - const { winningCounts, rateOfReturn } = new LottoCalculator({ + const winningLotto = await createWinningLotto(); + const { winningCounts, rateOfReturn } = calculateWinningResults({ price, lottos, winningLotto, }); - outputManager.print("당첨 통계"); - outputManager.print("--------------------"); - outputManager.print(`3개 일치 (5,000원) - ${winningCounts[5]}개`); - outputManager.print(`4개 일치 (50,000원) - ${winningCounts[4]}개`); - outputManager.print(`5개 일치 (1,500,000원) - ${winningCounts[3]}개`); - outputManager.print( - `5개 일치, 보너스 볼 일치 (30,000,000원) - ${winningCounts[2]}개` - ); - outputManager.print(`6개 일치 (2,000,000,000원) - ${winningCounts[1]}개`); - outputManager.print(`총 수익률은 ${rateOfReturn}%입니다.`); -} + showWinningResults(winningCounts, rateOfReturn); +}; main(); From ddaf6455d332cfede802b03dd654fdff292a3f95 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 7 Jul 2024 23:52:45 +0900 Subject: [PATCH 54/87] =?UTF-8?q?fix:=20=EB=8F=99=EC=9D=BC=ED=95=9C=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=AA=85=EC=9D=B4=20=EC=9E=88=EC=96=B4?= =?UTF-8?q?=EC=84=9C=20attachBonusNumber=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/createWinningLotto.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controller/createWinningLotto.js b/src/controller/createWinningLotto.js index fad37e6..422c8b1 100644 --- a/src/controller/createWinningLotto.js +++ b/src/controller/createWinningLotto.js @@ -2,7 +2,7 @@ import { inputManager } from "../service/index.js"; import { WinningLotto } from "../model/index.js"; const createWinningLotto = async () => { - const createWinningLotto = await inputManager.retryScan( + const attachBonusNumber = await inputManager.retryScan( "> 당첨 번호를 입력해 주세요. ", (inputValue) => { const numbers = inputValue @@ -18,7 +18,7 @@ const createWinningLotto = async () => { (inputValue) => { const bonusNumber = Number(inputValue); - return createWinningLotto(bonusNumber); + return attachBonusNumber(bonusNumber); } ); From 2b4897aa392c3843e076165e7a9551276476cd6f Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 8 Jul 2024 00:23:34 +0900 Subject: [PATCH 55/87] =?UTF-8?q?fix:=20ci=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=8B=A4=ED=8C=A8=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/LottoMachine.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/model/LottoMachine.js b/src/model/LottoMachine.js index 1c964b4..d8fd9c0 100644 --- a/src/model/LottoMachine.js +++ b/src/model/LottoMachine.js @@ -35,9 +35,8 @@ class LottoMachine { } static #createLotto() { - const sortedlottoNumbers = LottoMachine.#getLottoNumbers().toSorted( - (a, b) => a - b - ); + const lottoNumbers = LottoMachine.#getLottoNumbers(); + const sortedlottoNumbers = [...lottoNumbers].sort((a, b) => a - b); return new Lotto(sortedlottoNumbers); } From 046974b94b1e9ecc0af34a050022695cd8b48d35 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sat, 13 Jul 2024 11:39:42 +0900 Subject: [PATCH 56/87] =?UTF-8?q?feat:=20LottoNumber=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/LottoNumber.js | 34 ++++++++++++++++++++++++++++++++++ src/model/index.js | 1 + 2 files changed, 35 insertions(+) create mode 100644 src/model/LottoNumber.js diff --git a/src/model/LottoNumber.js b/src/model/LottoNumber.js new file mode 100644 index 0000000..0440fa9 --- /dev/null +++ b/src/model/LottoNumber.js @@ -0,0 +1,34 @@ +import { throwErrorWithCondition, validate } from "../utils/index.js"; + +class LottoNumber { + static #MIN_NUMBER = 1; + static #MAX_NUMBER = 45; + + #number; + + constructor(number) { + LottoNumber.#validateNumber(number); + + this.#number = number; + } + + get value() { + return this.#number; + } + + static #validateNumber(number) { + validate.integer(number, "로또 번호로 적합하지 않은 값입니다."); + + throwErrorWithCondition( + number < LottoNumber.#MIN_NUMBER, + `로또 번호는 ${LottoNumber.#MIN_NUMBER}보다 커야 합니다.` + ); + + throwErrorWithCondition( + LottoNumber.#MAX_NUMBER < number, + `로또 번호는 ${LottoNumber.#MAX_NUMBER}보다 작아야 합니다.` + ); + } +} + +export default LottoNumber; diff --git a/src/model/index.js b/src/model/index.js index 257e1b2..1498c5a 100644 --- a/src/model/index.js +++ b/src/model/index.js @@ -2,3 +2,4 @@ export { default as Lotto } from "./Lotto.js"; export { default as LottoMachine } from "./LottoMachine.js"; export { default as WinningLotto } from "./WinningLotto.js"; export { default as LottoCalculator } from "./LottoCalculator.js"; +export { default as LottoNumber } from "./LottoNumber.js"; From 49641e82e0dfd21e30b02de036784551f29eb1c5 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sat, 13 Jul 2024 11:59:42 +0900 Subject: [PATCH 57/87] =?UTF-8?q?test:=20LottoNumber=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoNumber.test.js | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/__tests__/LottoNumber.test.js diff --git a/src/__tests__/LottoNumber.test.js b/src/__tests__/LottoNumber.test.js new file mode 100644 index 0000000..691481d --- /dev/null +++ b/src/__tests__/LottoNumber.test.js @@ -0,0 +1,41 @@ +import { describe, expect, test } from "vitest"; +import { LottoNumber } from "../model/index.js"; + +describe("LottoNumber 클래스 테스트", () => { + test.each([ + { value: "a" }, + { value: true }, + { value: false }, + { value: undefined }, + { value: null }, + { value: [] }, + { value: {} }, + ])( + "로또 번호로 적합하지 않은 값($value)을 할당하면 오류가 발생한다.", + ({ value }) => { + expect(() => new LottoNumber(value)).toThrowError( + "로또 번호로 적합하지 않은 값입니다." + ); + } + ); + + test("로또 번호가 1보다 작으면 오류가 발생한다.", () => { + expect(() => new LottoNumber(0)).toThrowError( + "로또 번호는 1보다 커야 합니다." + ); + }); + + test("로또 번호가 45보다 크면 오류가 발생한다.", () => { + expect(() => new LottoNumber(46)).toThrowError( + "로또 번호는 45보다 작아야 합니다." + ); + }); + + test("로또 번호를 정한다.", () => { + const number = 1; + + const lottoNumber = new LottoNumber(number); + + expect(lottoNumber.value).toBe(1); + }); +}); From af050c12fa76906426ef7b1605bcdcbcd4830f83 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sat, 13 Jul 2024 13:04:37 +0900 Subject: [PATCH 58/87] =?UTF-8?q?refactor:=20LottoNumber=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Lotto.js | 42 ++++++++++----------------------------- src/model/LottoMachine.js | 3 ++- src/model/LottoNumber.js | 8 ++++++++ src/model/WinningLotto.js | 29 +++++++-------------------- 4 files changed, 28 insertions(+), 54 deletions(-) diff --git a/src/model/Lotto.js b/src/model/Lotto.js index 02d4e3f..7bced35 100644 --- a/src/model/Lotto.js +++ b/src/model/Lotto.js @@ -3,53 +3,33 @@ import { throwErrorWithCondition, validate, } from "../utils/index.js"; +import LottoNumber from "./LottoNumber.js"; class Lotto { - static MIN_NUMBER = 1; - static MAX_NUMBER = 45; - static NUMBERS_SIZE = 6; + static #NUMBERS_SIZE = 6; #lottoNumbers; - constructor(lottoNumbers) { - Lotto.#validateLottoNumbers(lottoNumbers); + constructor(numbers) { + Lotto.#validateNumbers(numbers); - this.#lottoNumbers = [...lottoNumbers]; + this.#lottoNumbers = numbers.map((number) => new LottoNumber(number)); } get numbers() { - return [...this.#lottoNumbers]; + return this.#lottoNumbers.map((lottoNumber) => lottoNumber.value); } - static isLessThanMinLottoNumber(number) { - return number < Lotto.MIN_NUMBER; + static get NUMBERS_SIZE() { + return Lotto.#NUMBERS_SIZE; } - static isGreaterThanMaxLottoNumber(number) { - return Lotto.MAX_NUMBER < number; - } - - static #validateLottoNumbers(lottoNumbers) { + static #validateNumbers(lottoNumbers) { validate.array(lottoNumbers, "로또 번호로 적합하지 않은 값입니다."); throwErrorWithCondition( - lottoNumbers.length !== Lotto.NUMBERS_SIZE, - `로또 번호는 ${Lotto.NUMBERS_SIZE}개여야 합니다.` - ); - - throwErrorWithCondition( - lottoNumbers.some((number) => !Number.isInteger(number)), - "로또 번호 중에 적합하지 않은 값이 있습니다." - ); - - throwErrorWithCondition( - lottoNumbers.some(Lotto.isLessThanMinLottoNumber), - `로또 번호 중에 ${Lotto.MIN_NUMBER}보다 작은 번호가 있습니다.` - ); - - throwErrorWithCondition( - lottoNumbers.some(Lotto.isGreaterThanMaxLottoNumber), - `로또 번호 중에 ${Lotto.MAX_NUMBER}보다 큰 번호가 있습니다.` + lottoNumbers.length !== Lotto.#NUMBERS_SIZE, + `로또 번호는 ${Lotto.#NUMBERS_SIZE}개여야 합니다.` ); throwErrorWithCondition( diff --git a/src/model/LottoMachine.js b/src/model/LottoMachine.js index d8fd9c0..142951a 100644 --- a/src/model/LottoMachine.js +++ b/src/model/LottoMachine.js @@ -5,6 +5,7 @@ import { validate, } from "../utils/index.js"; import Lotto from "./Lotto.js"; +import LottoNumber from "./LottoNumber.js"; class LottoMachine { static #PRICE_PER_ONE = 1000; @@ -58,7 +59,7 @@ class LottoMachine { } static #getLottoNumber() { - return getRandomNumber(Lotto.MIN_NUMBER, Lotto.MAX_NUMBER); + return getRandomNumber(LottoNumber.MIN_NUMBER, LottoNumber.MAX_NUMBER); } static #countLotto(price) { diff --git a/src/model/LottoNumber.js b/src/model/LottoNumber.js index 0440fa9..81a30f0 100644 --- a/src/model/LottoNumber.js +++ b/src/model/LottoNumber.js @@ -16,6 +16,14 @@ class LottoNumber { return this.#number; } + static get MIN_NUMBER() { + return LottoNumber.#MIN_NUMBER; + } + + static get MAX_NUMBER() { + return LottoNumber.#MAX_NUMBER; + } + static #validateNumber(number) { validate.integer(number, "로또 번호로 적합하지 않은 값입니다."); diff --git a/src/model/WinningLotto.js b/src/model/WinningLotto.js index d3df08e..2e6f83a 100644 --- a/src/model/WinningLotto.js +++ b/src/model/WinningLotto.js @@ -1,42 +1,27 @@ -import { - isDuplicated, - throwErrorWithCondition, - validate, -} from "../utils/index.js"; +import { isDuplicated, throwErrorWithCondition } from "../utils/index.js"; import Lotto from "./Lotto.js"; +import LottoNumber from "./LottoNumber.js"; class WinningLotto extends Lotto { #bonusNumber; - constructor(lottoNumbers, bonusNumber) { - super(lottoNumbers); + constructor(numbers, bonusNumber) { + super(numbers); if (bonusNumber === undefined) { - return (bonusNumber) => new WinningLotto(lottoNumbers, bonusNumber); + return (bonusNumber) => new WinningLotto(numbers, bonusNumber); } WinningLotto.#validateBonusNumber(this.numbers, bonusNumber); - this.#bonusNumber = bonusNumber; + this.#bonusNumber = new LottoNumber(bonusNumber); } get bonusNumber() { - return this.#bonusNumber; + return this.#bonusNumber.value; } static #validateBonusNumber(winningNumbers, bonusNumber) { - validate.integer(bonusNumber, "보너스 번호는 정수여야 합니다."); - - throwErrorWithCondition( - Lotto.isLessThanMinLottoNumber(bonusNumber), - `보너스 번호는 ${Lotto.MIN_NUMBER}이상이어야 합니다.` - ); - - throwErrorWithCondition( - Lotto.isGreaterThanMaxLottoNumber(bonusNumber), - `보너스 번호는 ${Lotto.MAX_NUMBER}이하여야 합니다.` - ); - throwErrorWithCondition( isDuplicated(winningNumbers.concat(bonusNumber)), "당첨 번호 중에 보너스 번호와 중복되는 번호가 있습니다." From 85b4a72d1178bf4e7fd05b12c449d2499f7b2dc3 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sat, 13 Jul 2024 13:05:11 +0900 Subject: [PATCH 59/87] =?UTF-8?q?test:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 24 ------------------------ src/__tests__/WinningLotto.test.js | 27 --------------------------- 2 files changed, 51 deletions(-) diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js index ae52eee..eac3fe7 100644 --- a/src/__tests__/Lotto.test.js +++ b/src/__tests__/Lotto.test.js @@ -30,30 +30,6 @@ describe("Lotto 클래스 테스트.", () => { ); }); - test("로또 번호 중에 숫자가 아닌 값이 있으면 오류가 발생한다.", () => { - expect(() => new Lotto(["a", 1, 2, 3, 4, 5])).toThrowError( - "로또 번호 중에 적합하지 않은 값이 있습니다." - ); - }); - - test("로또 번호 중에 1보다 작은 번호가 있으면 오류가 발생한다.", () => { - expect(() => new Lotto([0, 2, 3, 4, 5, 45])).toThrowError( - "로또 번호 중에 1보다 작은 번호가 있습니다." - ); - }); - - test("로또 번호 중에 45보다 큰 번호가 있으면 오류가 발생한다.", () => { - expect(() => new Lotto([1, 2, 3, 4, 5, 46])).toThrowError( - "로또 번호 중에 45보다 큰 번호가 있습니다." - ); - }); - - test("로또 번호 중에 중복되는 번호가 있으면 오류가 발생한다.", () => { - expect(() => new Lotto([1, 2, 3, 4, 5, 5])).toThrowError( - "중복되는 로또 번호가 있습니다." - ); - }); - test("로또 번호를 가져옵니다.", () => { const lotto = new Lotto([1, 2, 3, 4, 5, 6]); diff --git a/src/__tests__/WinningLotto.test.js b/src/__tests__/WinningLotto.test.js index 1efc7c0..4aac413 100644 --- a/src/__tests__/WinningLotto.test.js +++ b/src/__tests__/WinningLotto.test.js @@ -2,33 +2,6 @@ import { describe, expect, test } from "vitest"; import { WinningLotto } from "../model/index.js"; describe("WinningLotto 클래스 테스트", () => { - test("보너스 번호가 정수가 아니면 오류가 발생한다.", () => { - const lottoNumbers = [1, 2, 3, 4, 5, 6]; - const bonusNumber = "a"; - - expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrowError( - "보너스 번호는 정수여야 합니다." - ); - }); - - test("보너스 번호가 1보다 작으면 오류가 발생한다.", () => { - const lottoNumbers = [1, 2, 3, 4, 5, 6]; - const bonusNumber = 0; - - expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrowError( - "보너스 번호는 1이상이어야 합니다." - ); - }); - - test("보너스 번호가 45보다 크면 오류가 발생한다.", () => { - const lottoNumbers = [1, 2, 3, 4, 5, 6]; - const bonusNumber = 46; - - expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrowError( - "보너스 번호는 45이하여야 합니다." - ); - }); - test("당첨 번호 중에 보너스 번호와 중복되는 번호가 있으면 오류가 발생한다.", () => { const lottoNumbers = [1, 2, 3, 4, 5, 6]; const bonusNumber = 6; From 6e5ac02c7acf1cd8a15ddd35483a9b1cc215119e Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sat, 13 Jul 2024 17:29:30 +0900 Subject: [PATCH 60/87] =?UTF-8?q?feat:=20retryOnFailureAsync=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/index.js | 1 + src/utils/retryOnFailureAsync.js | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 src/utils/retryOnFailureAsync.js diff --git a/src/utils/index.js b/src/utils/index.js index c563255..75b335b 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -2,3 +2,4 @@ export { default as isDuplicated } from "./isDuplicated.js"; export { default as getRandomNumber } from "./getRandomNumber.js"; export { default as readLineAsync } from "./readLineAsync.js"; export * from "./validate.js"; +export { default as retryOnFailureAsync } from "./retryOnFailureAsync.js"; diff --git a/src/utils/retryOnFailureAsync.js b/src/utils/retryOnFailureAsync.js new file mode 100644 index 0000000..5cc66ca --- /dev/null +++ b/src/utils/retryOnFailureAsync.js @@ -0,0 +1,11 @@ +const retryOnFailureAsync = async (asyncFn, errorFn) => { + try { + return await asyncFn(); + } catch (error) { + errorFn(error); + + return await retryOnFailureAsync(asyncFn, errorFn); + } +}; + +export default retryOnFailureAsync; From bc8c1a7ed9c1cf3a8c4a14d7d751e06474dd71df Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sat, 13 Jul 2024 17:30:37 +0900 Subject: [PATCH 61/87] =?UTF-8?q?refactor:=20=EC=9E=85=EB=A0=A5=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/createWinningLotto.js | 52 +++++++++++++++++++++------- src/controller/purchaseLottos.js | 22 ++++++++---- src/model/WinningLotto.js | 9 ++--- src/service/inputManager.js | 19 ++-------- 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/controller/createWinningLotto.js b/src/controller/createWinningLotto.js index 422c8b1..8aa2d33 100644 --- a/src/controller/createWinningLotto.js +++ b/src/controller/createWinningLotto.js @@ -1,27 +1,53 @@ -import { inputManager } from "../service/index.js"; -import { WinningLotto } from "../model/index.js"; +import { inputManager, outputManager } from "../service/index.js"; +import { Lotto, LottoNumber, WinningLotto } from "../model/index.js"; +import { retryOnFailureAsync } from "../utils/index.js"; -const createWinningLotto = async () => { - const attachBonusNumber = await inputManager.retryScan( +const createWinningNumbers = async () => { + const inputNumbers = await inputManager.scan( "> 당첨 번호를 입력해 주세요. ", - (inputValue) => { - const numbers = inputValue + (inputValue) => + inputValue + .trim() .split(",") - .map((value) => Number(value.trim())); - - return new WinningLotto(numbers); - } + .map((value) => value.trim()) + .map((value) => Number(value)) ); - const winningLotto = await inputManager.retryScan( + const winningNumbers = new Lotto(inputNumbers); + + return winningNumbers.numbers; +}; + +const createBonusNumber = async () => { + const inputNumber = await inputManager.scan( "> 보너스 번호를 입력해 주세요. ", (inputValue) => { - const bonusNumber = Number(inputValue); + const trimedInputValue = inputValue.trim(); - return attachBonusNumber(bonusNumber); + return Number(trimedInputValue); } ); + const bonusNumber = new LottoNumber(inputNumber); + + return bonusNumber.value; +}; + +const createWinningLotto = async () => { + const winningNumbers = await retryOnFailureAsync( + createWinningNumbers, + (error) => outputManager.print(error.message) + ); + + const winningLotto = await retryOnFailureAsync( + async () => { + const bonusNumber = await createBonusNumber(); + + return new WinningLotto(winningNumbers, bonusNumber); + }, + (error) => outputManager.print(error.message) + ); + return winningLotto; }; diff --git a/src/controller/purchaseLottos.js b/src/controller/purchaseLottos.js index 13f0f77..72b6b7c 100644 --- a/src/controller/purchaseLottos.js +++ b/src/controller/purchaseLottos.js @@ -1,14 +1,22 @@ import { LottoMachine } from "../model/index.js"; -import { inputManager } from "../service/index.js"; +import { inputManager, outputManager } from "../service/index.js"; +import { retryOnFailureAsync } from "../utils/index.js"; const purchaseLottos = async () => { - const { price, count, lottos } = await inputManager.retryScan( - "> 구입 금액을 입력해 주세요. ", - (inputValue) => { - const numValue = Number(inputValue); + const { price, count, lottos } = await retryOnFailureAsync( + async () => { + const priceValue = await inputManager.scan( + "> 구입 금액을 입력해 주세요. ", + (inputValue) => { + const trimedInputValue = inputValue.trim(); - return new LottoMachine(numValue); - } + return Number(trimedInputValue); + } + ); + + return new LottoMachine(priceValue); + }, + (error) => outputManager.print(error.message) ); return { price, count, lottos }; diff --git a/src/model/WinningLotto.js b/src/model/WinningLotto.js index 2e6f83a..7e4d1d0 100644 --- a/src/model/WinningLotto.js +++ b/src/model/WinningLotto.js @@ -1,6 +1,5 @@ import { isDuplicated, throwErrorWithCondition } from "../utils/index.js"; import Lotto from "./Lotto.js"; -import LottoNumber from "./LottoNumber.js"; class WinningLotto extends Lotto { #bonusNumber; @@ -8,17 +7,13 @@ class WinningLotto extends Lotto { constructor(numbers, bonusNumber) { super(numbers); - if (bonusNumber === undefined) { - return (bonusNumber) => new WinningLotto(numbers, bonusNumber); - } - WinningLotto.#validateBonusNumber(this.numbers, bonusNumber); - this.#bonusNumber = new LottoNumber(bonusNumber); + this.#bonusNumber = bonusNumber; } get bonusNumber() { - return this.#bonusNumber.value; + return this.#bonusNumber; } static #validateBonusNumber(winningNumbers, bonusNumber) { diff --git a/src/service/inputManager.js b/src/service/inputManager.js index 0ba15f2..d7584c9 100644 --- a/src/service/inputManager.js +++ b/src/service/inputManager.js @@ -7,25 +7,10 @@ class InputManager { this.#inputFn = inputFn; } - async scan(query) { + async scan(query, processFn) { const inputValue = await this.#inputFn(query); - return inputValue.trim(); - } - - async retryScan(query, processFn = null) { - try { - const inputValue = await this.scan(query); - - return typeof processFn === "function" - ? processFn(inputValue) - : inputValue; - } catch (error) { - return await this.retryScan( - `${error.message} 다시 입력해주세요.\n`, - processFn - ); - } + return typeof processFn === "function" ? processFn(inputValue) : inputValue; } } From 60d21541ab37a3c6dfa26adf23c69bffd5d58d77 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 14 Jul 2024 16:32:56 +0900 Subject: [PATCH 62/87] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=EC=97=90?= =?UTF-8?q?=20=EA=B4=80=ED=95=9C=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=EB=82=B4=EB=B6=80=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/LottoCalculator.js | 77 ++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/src/model/LottoCalculator.js b/src/model/LottoCalculator.js index 2972510..e83740b 100644 --- a/src/model/LottoCalculator.js +++ b/src/model/LottoCalculator.js @@ -1,11 +1,32 @@ class LottoCalculator { - static #LOTTO_PRIZES = { - 1: 2_000_000_000, - 2: 30_000_000, - 3: 1_500_000, - 4: 50000, - 5: 5000, - }; + static #LOTTO_RANKING_INFO = [ + { + ranking: 1, + matchingCount: 6, + prizeMoney: 2_000_000_000, + }, + { + ranking: 2, + matchingCount: 5, + isBonusMatch: true, + prizeMoney: 30_000_000, + }, + { + ranking: 3, + matchingCount: 5, + prizeMoney: 1_500_000, + }, + { + ranking: 4, + matchingCount: 4, + prizeMoney: 50_000, + }, + { + ranking: 5, + matchingCount: 3, + prizeMoney: 5_000, + }, + ]; #winningCounts; #rateOfReturn; @@ -41,11 +62,11 @@ class LottoCalculator { static #updateWinningCounts({ winningCounts, lottos, winningLotto }) { const copiedWinningCounts = { ...winningCounts }; const lottoMatchResults = lottos.map((lotto) => ({ - matchedCount: LottoCalculator.#matchLottoNumbers( + matchingCount: LottoCalculator.#matchLottoNumbers( lotto.numbers, winningLotto.numbers ), - isMatchedBonusNumber: lotto.numbers.includes(winningLotto.bonusNumber), + isBonusMatch: lotto.numbers.includes(winningLotto.bonusNumber), })); return LottoCalculator.#countLottoWins( @@ -61,16 +82,14 @@ class LottoCalculator { static #countLottoWins(lottoMatchResults, winningCounts) { return lottoMatchResults.reduce( - (counts, { matchedCount, isMatchedBonusNumber }) => { - const prizeMap = { - 6: 1, - 5: isMatchedBonusNumber ? 2 : 3, - 4: 4, - 3: 5, - }; - - if (prizeMap[matchedCount]) { - counts[prizeMap[matchedCount]] += 1; + (counts, { matchingCount, isBonusMatch }) => { + const ranking = LottoCalculator.#getRanking( + matchingCount, + isBonusMatch + ); + + if (ranking) { + counts[ranking] += 1; } return counts; @@ -81,11 +100,29 @@ class LottoCalculator { static #calcRateOfReturn(winningCounts, price) { const sumOfPrize = Object.entries({ ...winningCounts }) - .map(([ranking, count]) => LottoCalculator.#LOTTO_PRIZES[ranking] * count) + .map( + ([ranking, count]) => LottoCalculator.#getPrizeMoney(ranking) * count + ) .reduce((acc, cur) => acc + cur, 0); return (sumOfPrize / price) * 100; } + + static #getPrizeMoney(ranking) { + return LottoCalculator.#LOTTO_RANKING_INFO.find( + (info) => info.ranking === Number(ranking) + ).prizeMoney; + } + + static #getRanking(matchingCount, isBonusMatch) { + const currentInfo = LottoCalculator.#LOTTO_RANKING_INFO.find( + (info) => + info.matchingCount === matchingCount && + Boolean(info.isBonusMatch) === isBonusMatch + ); + + return currentInfo && currentInfo.ranking; + } } export default LottoCalculator; From 6dcaa4f9a820f7c2ff32cd4eb9d5f55c25fcde6d Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 14 Jul 2024 17:02:49 +0900 Subject: [PATCH 63/87] =?UTF-8?q?refactor:=20constants=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EC=83=9D=EC=84=B1=ED=95=98=EA=B3=A0=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EC=88=9C=EC=9C=84=20=EC=A0=95=EB=B3=B4=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/index.js | 1 + src/constants/lottoRankingInfo.js | 30 ++++++++++++++++++++++++ src/model/LottoCalculator.js | 38 ++++--------------------------- 3 files changed, 36 insertions(+), 33 deletions(-) create mode 100644 src/constants/index.js create mode 100644 src/constants/lottoRankingInfo.js diff --git a/src/constants/index.js b/src/constants/index.js new file mode 100644 index 0000000..cc05873 --- /dev/null +++ b/src/constants/index.js @@ -0,0 +1 @@ +export { default as LOTTO_RANKING_INFO } from "./lottoRankingInfo.js"; diff --git a/src/constants/lottoRankingInfo.js b/src/constants/lottoRankingInfo.js new file mode 100644 index 0000000..b0a0d9d --- /dev/null +++ b/src/constants/lottoRankingInfo.js @@ -0,0 +1,30 @@ +const LOTTO_RANKING_INFO = [ + { + ranking: 1, + matchingCount: 6, + prizeMoney: 2_000_000_000, + }, + { + ranking: 2, + matchingCount: 5, + isBonusMatch: true, + prizeMoney: 30_000_000, + }, + { + ranking: 3, + matchingCount: 5, + prizeMoney: 1_500_000, + }, + { + ranking: 4, + matchingCount: 4, + prizeMoney: 50_000, + }, + { + ranking: 5, + matchingCount: 3, + prizeMoney: 5_000, + }, +]; + +export default LOTTO_RANKING_INFO; diff --git a/src/model/LottoCalculator.js b/src/model/LottoCalculator.js index e83740b..a036257 100644 --- a/src/model/LottoCalculator.js +++ b/src/model/LottoCalculator.js @@ -1,33 +1,6 @@ -class LottoCalculator { - static #LOTTO_RANKING_INFO = [ - { - ranking: 1, - matchingCount: 6, - prizeMoney: 2_000_000_000, - }, - { - ranking: 2, - matchingCount: 5, - isBonusMatch: true, - prizeMoney: 30_000_000, - }, - { - ranking: 3, - matchingCount: 5, - prizeMoney: 1_500_000, - }, - { - ranking: 4, - matchingCount: 4, - prizeMoney: 50_000, - }, - { - ranking: 5, - matchingCount: 3, - prizeMoney: 5_000, - }, - ]; +import { LOTTO_RANKING_INFO } from "../constants/index.js"; +class LottoCalculator { #winningCounts; #rateOfReturn; @@ -109,13 +82,12 @@ class LottoCalculator { } static #getPrizeMoney(ranking) { - return LottoCalculator.#LOTTO_RANKING_INFO.find( - (info) => info.ranking === Number(ranking) - ).prizeMoney; + return LOTTO_RANKING_INFO.find((info) => info.ranking === Number(ranking)) + .prizeMoney; } static #getRanking(matchingCount, isBonusMatch) { - const currentInfo = LottoCalculator.#LOTTO_RANKING_INFO.find( + const currentInfo = LOTTO_RANKING_INFO.find( (info) => info.matchingCount === matchingCount && Boolean(info.isBonusMatch) === isBonusMatch From 26037c24edd2acc35fa32c37b968a89978fafd2d Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 14 Jul 2024 17:28:04 +0900 Subject: [PATCH 64/87] =?UTF-8?q?feat:=20formatKoreanCurrency=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/formatKoreanCurrency.js | 5 +++++ src/utils/index.js | 1 + 2 files changed, 6 insertions(+) create mode 100644 src/utils/formatKoreanCurrency.js diff --git a/src/utils/formatKoreanCurrency.js b/src/utils/formatKoreanCurrency.js new file mode 100644 index 0000000..a5041e5 --- /dev/null +++ b/src/utils/formatKoreanCurrency.js @@ -0,0 +1,5 @@ +const formatKoreanCurrency = (number) => { + return new Intl.NumberFormat("ko-KR").format(number); +}; + +export default formatKoreanCurrency; diff --git a/src/utils/index.js b/src/utils/index.js index 75b335b..d2191d7 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -3,3 +3,4 @@ export { default as getRandomNumber } from "./getRandomNumber.js"; export { default as readLineAsync } from "./readLineAsync.js"; export * from "./validate.js"; export { default as retryOnFailureAsync } from "./retryOnFailureAsync.js"; +export { default as formatKoreanCurrency } from "./formatKoreanCurrency.js"; From 8fb164fb36e8241bfc39885122a7f1ecec4ef52e Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 14 Jul 2024 17:28:42 +0900 Subject: [PATCH 65/87] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EC=88=9C=EC=9C=84=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20=EB=B0=94?= =?UTF-8?q?=ED=83=95=EC=9C=BC=EB=A1=9C=20=EA=B2=B0=EA=B3=BC=EB=A5=BC=20?= =?UTF-8?q?=EB=B3=B4=EC=97=AC=EC=A3=BC=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view/showWinningResults.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/view/showWinningResults.js b/src/view/showWinningResults.js index 10ba5c0..efdcccf 100644 --- a/src/view/showWinningResults.js +++ b/src/view/showWinningResults.js @@ -1,15 +1,22 @@ +import { LOTTO_RANKING_INFO } from "../constants/index.js"; import { outputManager } from "../service/index.js"; +import { formatKoreanCurrency } from "../utils/index.js"; const showWinningResults = (winningCounts, rateOfReturn) => { outputManager.print("당첨 통계"); outputManager.print("--------------------"); - outputManager.print(`3개 일치 (5,000원) - ${winningCounts[5]}개`); - outputManager.print(`4개 일치 (50,000원) - ${winningCounts[4]}개`); - outputManager.print(`5개 일치 (1,500,000원) - ${winningCounts[3]}개`); - outputManager.print( - `5개 일치, 보너스 볼 일치 (30,000,000원) - ${winningCounts[2]}개` + + LOTTO_RANKING_INFO.reverse().forEach( + ({ ranking, matchingCount, isBonusMatch, prizeMoney }) => { + const bonusMatchMessage = isBonusMatch ? `, 보너스 볼 일치` : ""; + const formattedPrizeMoney = formatKoreanCurrency(prizeMoney); + + outputManager.print( + `${matchingCount}개 일치${bonusMatchMessage} (${formattedPrizeMoney}원) - ${winningCounts[ranking]}개` + ); + } ); - outputManager.print(`6개 일치 (2,000,000,000원) - ${winningCounts[1]}개`); + outputManager.print(`총 수익률은 ${rateOfReturn}%입니다.`); }; From 0fd967b3c47998bfb4e249cd44b7fc381ca25903 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 14 Jul 2024 23:06:12 +0900 Subject: [PATCH 66/87] =?UTF-8?q?refactor:=20=EA=B0=80=EA=B2=A9=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/index.js | 31 ++++++++++++++++++++++- src/constants/lottoRankingInfo.js | 30 ---------------------- src/controller/calculateWinningResults.js | 3 +-- src/controller/purchaseLottos.js | 4 +-- src/main.js | 3 +-- src/model/LottoCalculator.js | 7 +++-- src/model/LottoMachine.js | 17 +++++-------- 7 files changed, 45 insertions(+), 50 deletions(-) delete mode 100644 src/constants/lottoRankingInfo.js diff --git a/src/constants/index.js b/src/constants/index.js index cc05873..7d742c9 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -1 +1,30 @@ -export { default as LOTTO_RANKING_INFO } from "./lottoRankingInfo.js"; +export const LOTTO_RANKING_INFO = [ + { + ranking: 1, + matchingCount: 6, + prizeMoney: 2_000_000_000, + }, + { + ranking: 2, + matchingCount: 5, + isBonusMatch: true, + prizeMoney: 30_000_000, + }, + { + ranking: 3, + matchingCount: 5, + prizeMoney: 1_500_000, + }, + { + ranking: 4, + matchingCount: 4, + prizeMoney: 50_000, + }, + { + ranking: 5, + matchingCount: 3, + prizeMoney: 5_000, + }, +]; + +export const LOTTO_PRICE = 1000; diff --git a/src/constants/lottoRankingInfo.js b/src/constants/lottoRankingInfo.js deleted file mode 100644 index b0a0d9d..0000000 --- a/src/constants/lottoRankingInfo.js +++ /dev/null @@ -1,30 +0,0 @@ -const LOTTO_RANKING_INFO = [ - { - ranking: 1, - matchingCount: 6, - prizeMoney: 2_000_000_000, - }, - { - ranking: 2, - matchingCount: 5, - isBonusMatch: true, - prizeMoney: 30_000_000, - }, - { - ranking: 3, - matchingCount: 5, - prizeMoney: 1_500_000, - }, - { - ranking: 4, - matchingCount: 4, - prizeMoney: 50_000, - }, - { - ranking: 5, - matchingCount: 3, - prizeMoney: 5_000, - }, -]; - -export default LOTTO_RANKING_INFO; diff --git a/src/controller/calculateWinningResults.js b/src/controller/calculateWinningResults.js index 1aa4023..c46498e 100644 --- a/src/controller/calculateWinningResults.js +++ b/src/controller/calculateWinningResults.js @@ -1,8 +1,7 @@ import { LottoCalculator } from "../model/index.js"; -const calculateWinningResults = ({ price, lottos, winningLotto }) => { +const calculateWinningResults = ({ lottos, winningLotto }) => { const { winningCounts, rateOfReturn } = new LottoCalculator({ - price, lottos, winningLotto, }); diff --git a/src/controller/purchaseLottos.js b/src/controller/purchaseLottos.js index 72b6b7c..f1ba05b 100644 --- a/src/controller/purchaseLottos.js +++ b/src/controller/purchaseLottos.js @@ -3,7 +3,7 @@ import { inputManager, outputManager } from "../service/index.js"; import { retryOnFailureAsync } from "../utils/index.js"; const purchaseLottos = async () => { - const { price, count, lottos } = await retryOnFailureAsync( + const { count, lottos } = await retryOnFailureAsync( async () => { const priceValue = await inputManager.scan( "> 구입 금액을 입력해 주세요. ", @@ -19,7 +19,7 @@ const purchaseLottos = async () => { (error) => outputManager.print(error.message) ); - return { price, count, lottos }; + return { count, lottos }; }; export default purchaseLottos; diff --git a/src/main.js b/src/main.js index a9d508b..8a9dbde 100644 --- a/src/main.js +++ b/src/main.js @@ -6,13 +6,12 @@ import { import { showPurchasedLottos, showWinningResults } from "./view/index.js"; const main = async () => { - const { price, count, lottos } = await purchaseLottos(); + const { count, lottos } = await purchaseLottos(); showPurchasedLottos(count, lottos); const winningLotto = await createWinningLotto(); const { winningCounts, rateOfReturn } = calculateWinningResults({ - price, lottos, winningLotto, }); diff --git a/src/model/LottoCalculator.js b/src/model/LottoCalculator.js index a036257..7ea3703 100644 --- a/src/model/LottoCalculator.js +++ b/src/model/LottoCalculator.js @@ -1,10 +1,10 @@ -import { LOTTO_RANKING_INFO } from "../constants/index.js"; +import { LOTTO_PRICE, LOTTO_RANKING_INFO } from "../constants/index.js"; class LottoCalculator { #winningCounts; #rateOfReturn; - constructor({ price, lottos, winningLotto }) { + constructor({ lottos, winningLotto }) { this.#winningCounts = { 1: 0, 2: 0, @@ -18,6 +18,9 @@ class LottoCalculator { lottos, winningLotto, }); + + const price = lottos.length * LOTTO_PRICE; + this.#rateOfReturn = LottoCalculator.#calcRateOfReturn( this.#winningCounts, price diff --git a/src/model/LottoMachine.js b/src/model/LottoMachine.js index 142951a..74c6cf0 100644 --- a/src/model/LottoMachine.js +++ b/src/model/LottoMachine.js @@ -1,3 +1,4 @@ +import { LOTTO_PRICE } from "../constants/index.js"; import { getRandomNumber, isDuplicated, @@ -8,19 +9,13 @@ import Lotto from "./Lotto.js"; import LottoNumber from "./LottoNumber.js"; class LottoMachine { - static #PRICE_PER_ONE = 1000; - #price; #lottos; constructor(price) { LottoMachine.#validatePrice(price); - this.#price = price; - const count = LottoMachine.#countLotto(this.#price); - this.#lottos = LottoMachine.#createLottos(count); - } - get price() { - return this.#price; + const count = LottoMachine.#countLotto(price); + this.#lottos = LottoMachine.#createLottos(count); } get count() { @@ -63,15 +58,15 @@ class LottoMachine { } static #countLotto(price) { - return Math.floor(price / LottoMachine.#PRICE_PER_ONE); + return Math.floor(price / LOTTO_PRICE); } static #validatePrice(price) { validate.integer(price, "로또 구입 금액으로 정수를 입력해야 합니다."); throwErrorWithCondition( - price < LottoMachine.#PRICE_PER_ONE, - `로또 구입 금액은 ${LottoMachine.#PRICE_PER_ONE}원이상이어야 합니다.` + price < LOTTO_PRICE, + `로또 구입 금액은 ${LOTTO_PRICE}원이상이어야 합니다.` ); } } From f787d18917d894adf4d6166eda995643cbdc4d6f Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Sun, 14 Jul 2024 23:06:38 +0900 Subject: [PATCH 67/87] =?UTF-8?q?test:=20=EC=83=81=EA=B8=88=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/LottoCalculator.test.js | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/__tests__/LottoCalculator.test.js b/src/__tests__/LottoCalculator.test.js index bd68aef..be7e32b 100644 --- a/src/__tests__/LottoCalculator.test.js +++ b/src/__tests__/LottoCalculator.test.js @@ -5,14 +5,14 @@ import { LottoCalculator, Lotto, } from "../model/index.js"; +import { LOTTO_PRICE } from "../constants/index.js"; describe("LottoCalculator 클래스 테스트", () => { test("1등부터 5등까지 당첨된 수를 가져온다.", () => { - const { price, lottos } = new LottoMachine(6000); + const { lottos } = new LottoMachine(6000); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); const { winningCounts } = new LottoCalculator({ - price, lottos, winningLotto, }); @@ -31,11 +31,10 @@ describe("LottoCalculator 클래스 테스트", () => { }); test("로또 번호 중에 1등 당첨이 하나있다.", () => { - const { price, lottos } = createLottoMachineMock([1, 2, 3, 4, 5, 6]); + const { lottos } = createLottoMachineMock([[1, 2, 3, 4, 5, 6]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); const { winningCounts } = new LottoCalculator({ - price, lottos, winningLotto, }); @@ -44,11 +43,10 @@ describe("LottoCalculator 클래스 테스트", () => { }); test("로또 번호 중에 2등 당첨이 하나있다.", () => { - const { price, lottos } = createLottoMachineMock([1, 2, 3, 4, 5, 7]); + const { lottos } = createLottoMachineMock([[1, 2, 3, 4, 5, 7]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); const { winningCounts } = new LottoCalculator({ - price, lottos, winningLotto, }); @@ -57,11 +55,10 @@ describe("LottoCalculator 클래스 테스트", () => { }); test("로또 번호 중에 3등 당첨이 하나있다.", () => { - const { price, lottos } = createLottoMachineMock([1, 2, 3, 4, 5, 8]); + const { lottos } = createLottoMachineMock([[1, 2, 3, 4, 5, 8]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); const { winningCounts } = new LottoCalculator({ - price, lottos, winningLotto, }); @@ -70,11 +67,10 @@ describe("LottoCalculator 클래스 테스트", () => { }); test("로또 번호 중에 4등 당첨이 하나있다.", () => { - const { price, lottos } = createLottoMachineMock([1, 2, 3, 4, 8, 9]); + const { lottos } = createLottoMachineMock([[1, 2, 3, 4, 8, 9]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); const { winningCounts } = new LottoCalculator({ - price, lottos, winningLotto, }); @@ -83,11 +79,10 @@ describe("LottoCalculator 클래스 테스트", () => { }); test("로또 번호 중에 5등 당첨이 하나있다.", () => { - const { price, lottos } = createLottoMachineMock([1, 2, 3, 8, 9, 10]); + const { lottos } = createLottoMachineMock([[1, 2, 3, 8, 9, 10]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); const { winningCounts } = new LottoCalculator({ - price, lottos, winningLotto, }); @@ -96,26 +91,31 @@ describe("LottoCalculator 클래스 테스트", () => { }); test("수익률을 가져온다.", () => { - const { price, lottos } = createLottoMachineMock( - [1, 2, 3, 8, 9, 10], - 10000 + const { lottos } = createLottoMachineMock( + [ + [1, 2, 3, 8, 9, 10], + [8, 9, 10, 11, 12, 13], + ], + 2000 ); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); const { rateOfReturn } = new LottoCalculator({ - price, lottos, winningLotto, }); - expect(rateOfReturn).toBe(50); + expect(rateOfReturn).toBe(250); }); }); -function createLottoMachineMock(lottoNumbers, price = 1000) { - const lotto = new Lotto(lottoNumbers); +function createLottoMachineMock(numbersArray) { + const lottos = numbersArray.map((numbers) => new Lotto(numbers)); const lottoMachineMock = vi.fn(); - lottoMachineMock.mockReturnValueOnce({ price, lottos: [lotto] }); + lottoMachineMock.mockReturnValueOnce({ + price: lottos.length * LOTTO_PRICE, + lottos, + }); return lottoMachineMock(); } From 92a4f4f7086411b6bf65bedb4e8ec07806d5e7df Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 11:19:43 +0900 Subject: [PATCH 68/87] =?UTF-8?q?refactor:=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/createWinningLotto.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/controller/createWinningLotto.js b/src/controller/createWinningLotto.js index 8aa2d33..a4adee7 100644 --- a/src/controller/createWinningLotto.js +++ b/src/controller/createWinningLotto.js @@ -18,6 +18,15 @@ const createWinningNumbers = async () => { return winningNumbers.numbers; }; +const retryCreateWinningNumbers = async () => { + const winningNumbers = await retryOnFailureAsync( + createWinningNumbers, + (error) => outputManager.print(error.message) + ); + + return winningNumbers; +}; + const createBonusNumber = async () => { const inputNumber = await inputManager.scan( "> 보너스 번호를 입력해 주세요. ", @@ -33,15 +42,19 @@ const createBonusNumber = async () => { return bonusNumber.value; }; -const createWinningLotto = async () => { - const winningNumbers = await retryOnFailureAsync( - createWinningNumbers, - (error) => outputManager.print(error.message) +const retryCreateBonusNumber = async () => { + const bonusNumber = await retryOnFailureAsync(createBonusNumber, (error) => + outputManager.print(error.message) ); + return bonusNumber; +}; + +const createWinningLotto = async () => { const winningLotto = await retryOnFailureAsync( async () => { - const bonusNumber = await createBonusNumber(); + const winningNumbers = await retryCreateWinningNumbers(); + const bonusNumber = await retryCreateBonusNumber(); return new WinningLotto(winningNumbers, bonusNumber); }, From 58fd431babf3a1a7226e5252ad701f259bb207e1 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 14:41:55 +0900 Subject: [PATCH 69/87] =?UTF-8?q?feat:=20=EB=B0=B0=EC=97=B4=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/arrayUtils.js | 23 +++++++++++++++++++++++ src/utils/index.js | 1 + 2 files changed, 24 insertions(+) create mode 100644 src/utils/arrayUtils.js diff --git a/src/utils/arrayUtils.js b/src/utils/arrayUtils.js new file mode 100644 index 0000000..2402546 --- /dev/null +++ b/src/utils/arrayUtils.js @@ -0,0 +1,23 @@ +export const range = (start, end) => { + if (end === undefined) { + end = start; + start = 0; + } + + const length = end - start; + const result = Array(length); + + for (let i = 0; i < length; i++) { + result[i] = start + i; + } + + return result; +}; + +export const shuffle = (array) => { + const result = [...array]; + + result.sort(() => 0.5 - Math.random()); + + return result; +}; diff --git a/src/utils/index.js b/src/utils/index.js index d2191d7..11456e2 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -4,3 +4,4 @@ export { default as readLineAsync } from "./readLineAsync.js"; export * from "./validate.js"; export { default as retryOnFailureAsync } from "./retryOnFailureAsync.js"; export { default as formatKoreanCurrency } from "./formatKoreanCurrency.js"; +export * from "./arrayUtils.js"; From 074b130d72cdb698ce56574bee72acc390a72dab Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 14:45:21 +0900 Subject: [PATCH 70/87] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/LottoMachine.js | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/model/LottoMachine.js b/src/model/LottoMachine.js index 74c6cf0..851ca86 100644 --- a/src/model/LottoMachine.js +++ b/src/model/LottoMachine.js @@ -1,9 +1,9 @@ import { LOTTO_PRICE } from "../constants/index.js"; import { - getRandomNumber, - isDuplicated, + range, throwErrorWithCondition, validate, + shuffle, } from "../utils/index.js"; import Lotto from "./Lotto.js"; import LottoNumber from "./LottoNumber.js"; @@ -27,7 +27,7 @@ class LottoMachine { } static #createLottos(count) { - return Array.from({ length: count }).map(() => LottoMachine.#createLotto()); + return Array.from({ length: count }).map(LottoMachine.#createLotto); } static #createLotto() { @@ -38,23 +38,12 @@ class LottoMachine { } static #getLottoNumbers() { - const lottoNumbers = []; - const addLottoNumber = (number) => { - const nextLottoNumbers = lottoNumbers.concat(number); - !isDuplicated(nextLottoNumbers) && lottoNumbers.push(number); - }; - - while (lottoNumbers.length < Lotto.NUMBERS_SIZE) { - const lottoNumber = LottoMachine.#getLottoNumber(); - - addLottoNumber(lottoNumber); - } - - return lottoNumbers; - } + const lottoNumbers = range( + LottoNumber.MIN_NUMBER, + LottoNumber.MAX_NUMBER + 1 + ); - static #getLottoNumber() { - return getRandomNumber(LottoNumber.MIN_NUMBER, LottoNumber.MAX_NUMBER); + return shuffle(lottoNumbers).slice(0, Lotto.NUMBERS_SIZE); } static #countLotto(price) { From d5721d7e61e6b73c31223d59bd171c071ae65d98 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 14:51:34 +0900 Subject: [PATCH 71/87] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=83=81=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/index.js | 64 +++++++++++++++++++----------------- src/model/Lotto.js | 11 ++----- src/model/LottoCalculator.js | 8 ++--- src/model/LottoMachine.js | 16 ++++----- src/model/LottoNumber.js | 20 +++-------- 5 files changed, 52 insertions(+), 67 deletions(-) diff --git a/src/constants/index.js b/src/constants/index.js index 7d742c9..8b9766f 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -1,30 +1,34 @@ -export const LOTTO_RANKING_INFO = [ - { - ranking: 1, - matchingCount: 6, - prizeMoney: 2_000_000_000, - }, - { - ranking: 2, - matchingCount: 5, - isBonusMatch: true, - prizeMoney: 30_000_000, - }, - { - ranking: 3, - matchingCount: 5, - prizeMoney: 1_500_000, - }, - { - ranking: 4, - matchingCount: 4, - prizeMoney: 50_000, - }, - { - ranking: 5, - matchingCount: 3, - prizeMoney: 5_000, - }, -]; - -export const LOTTO_PRICE = 1000; +export const LOTTO = { + NUMBERS_SIZE: 6, + MIN_NUMBER: 1, + MAX_NUMBER: 45, + PRICE: 1000, + RANKING_INFO: [ + { + ranking: 1, + matchingCount: 6, + prizeMoney: 2_000_000_000, + }, + { + ranking: 2, + matchingCount: 5, + isBonusMatch: true, + prizeMoney: 30_000_000, + }, + { + ranking: 3, + matchingCount: 5, + prizeMoney: 1_500_000, + }, + { + ranking: 4, + matchingCount: 4, + prizeMoney: 50_000, + }, + { + ranking: 5, + matchingCount: 3, + prizeMoney: 5_000, + }, + ], +}; diff --git a/src/model/Lotto.js b/src/model/Lotto.js index 7bced35..188d0d2 100644 --- a/src/model/Lotto.js +++ b/src/model/Lotto.js @@ -1,3 +1,4 @@ +import { LOTTO } from "../constants/index.js"; import { isDuplicated, throwErrorWithCondition, @@ -6,8 +7,6 @@ import { import LottoNumber from "./LottoNumber.js"; class Lotto { - static #NUMBERS_SIZE = 6; - #lottoNumbers; constructor(numbers) { @@ -20,16 +19,12 @@ class Lotto { return this.#lottoNumbers.map((lottoNumber) => lottoNumber.value); } - static get NUMBERS_SIZE() { - return Lotto.#NUMBERS_SIZE; - } - static #validateNumbers(lottoNumbers) { validate.array(lottoNumbers, "로또 번호로 적합하지 않은 값입니다."); throwErrorWithCondition( - lottoNumbers.length !== Lotto.#NUMBERS_SIZE, - `로또 번호는 ${Lotto.#NUMBERS_SIZE}개여야 합니다.` + lottoNumbers.length !== LOTTO.NUMBERS_SIZE, + `로또 번호는 ${LOTTO.NUMBERS_SIZE}개여야 합니다.` ); throwErrorWithCondition( diff --git a/src/model/LottoCalculator.js b/src/model/LottoCalculator.js index 7ea3703..733a376 100644 --- a/src/model/LottoCalculator.js +++ b/src/model/LottoCalculator.js @@ -1,4 +1,4 @@ -import { LOTTO_PRICE, LOTTO_RANKING_INFO } from "../constants/index.js"; +import { LOTTO } from "../constants/index.js"; class LottoCalculator { #winningCounts; @@ -19,7 +19,7 @@ class LottoCalculator { winningLotto, }); - const price = lottos.length * LOTTO_PRICE; + const price = lottos.length * LOTTO.PRICE; this.#rateOfReturn = LottoCalculator.#calcRateOfReturn( this.#winningCounts, @@ -85,12 +85,12 @@ class LottoCalculator { } static #getPrizeMoney(ranking) { - return LOTTO_RANKING_INFO.find((info) => info.ranking === Number(ranking)) + return LOTTO.RANKING_INFO.find((info) => info.ranking === Number(ranking)) .prizeMoney; } static #getRanking(matchingCount, isBonusMatch) { - const currentInfo = LOTTO_RANKING_INFO.find( + const currentInfo = LOTTO.RANKING_INFO.find( (info) => info.matchingCount === matchingCount && Boolean(info.isBonusMatch) === isBonusMatch diff --git a/src/model/LottoMachine.js b/src/model/LottoMachine.js index 851ca86..c053361 100644 --- a/src/model/LottoMachine.js +++ b/src/model/LottoMachine.js @@ -1,4 +1,4 @@ -import { LOTTO_PRICE } from "../constants/index.js"; +import { LOTTO } from "../constants/index.js"; import { range, throwErrorWithCondition, @@ -6,7 +6,6 @@ import { shuffle, } from "../utils/index.js"; import Lotto from "./Lotto.js"; -import LottoNumber from "./LottoNumber.js"; class LottoMachine { #lottos; @@ -38,24 +37,21 @@ class LottoMachine { } static #getLottoNumbers() { - const lottoNumbers = range( - LottoNumber.MIN_NUMBER, - LottoNumber.MAX_NUMBER + 1 - ); + const lottoNumbers = range(LOTTO.MIN_NUMBER, LOTTO.MAX_NUMBER + 1); - return shuffle(lottoNumbers).slice(0, Lotto.NUMBERS_SIZE); + return shuffle(lottoNumbers).slice(0, LOTTO.NUMBERS_SIZE); } static #countLotto(price) { - return Math.floor(price / LOTTO_PRICE); + return Math.floor(price / LOTTO.PRICE); } static #validatePrice(price) { validate.integer(price, "로또 구입 금액으로 정수를 입력해야 합니다."); throwErrorWithCondition( - price < LOTTO_PRICE, - `로또 구입 금액은 ${LOTTO_PRICE}원이상이어야 합니다.` + price < LOTTO.PRICE, + `로또 구입 금액은 ${LOTTO.PRICE}원이상이어야 합니다.` ); } } diff --git a/src/model/LottoNumber.js b/src/model/LottoNumber.js index 81a30f0..d45cd84 100644 --- a/src/model/LottoNumber.js +++ b/src/model/LottoNumber.js @@ -1,9 +1,7 @@ import { throwErrorWithCondition, validate } from "../utils/index.js"; +import { LOTTO } from "../constants/index.js"; class LottoNumber { - static #MIN_NUMBER = 1; - static #MAX_NUMBER = 45; - #number; constructor(number) { @@ -16,25 +14,17 @@ class LottoNumber { return this.#number; } - static get MIN_NUMBER() { - return LottoNumber.#MIN_NUMBER; - } - - static get MAX_NUMBER() { - return LottoNumber.#MAX_NUMBER; - } - static #validateNumber(number) { validate.integer(number, "로또 번호로 적합하지 않은 값입니다."); throwErrorWithCondition( - number < LottoNumber.#MIN_NUMBER, - `로또 번호는 ${LottoNumber.#MIN_NUMBER}보다 커야 합니다.` + number < LOTTO.MIN_NUMBER, + `로또 번호는 ${LOTTO.MIN_NUMBER}보다 커야 합니다.` ); throwErrorWithCondition( - LottoNumber.#MAX_NUMBER < number, - `로또 번호는 ${LottoNumber.#MAX_NUMBER}보다 작아야 합니다.` + LOTTO.MAX_NUMBER < number, + `로또 번호는 ${LOTTO.MAX_NUMBER}보다 작아야 합니다.` ); } } From 258249e1491fd8a4c77a231f7024cc4de333c060 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 14:52:25 +0900 Subject: [PATCH 72/87] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/getRandomNumber.js | 5 ----- src/utils/index.js | 1 - 2 files changed, 6 deletions(-) delete mode 100644 src/utils/getRandomNumber.js diff --git a/src/utils/getRandomNumber.js b/src/utils/getRandomNumber.js deleted file mode 100644 index 4ffa325..0000000 --- a/src/utils/getRandomNumber.js +++ /dev/null @@ -1,5 +0,0 @@ -const getRandomNumber = (min, max) => { - return Math.floor(Math.random() * (max - min + 1)) + min; -}; - -export default getRandomNumber; diff --git a/src/utils/index.js b/src/utils/index.js index 11456e2..859268e 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,5 +1,4 @@ export { default as isDuplicated } from "./isDuplicated.js"; -export { default as getRandomNumber } from "./getRandomNumber.js"; export { default as readLineAsync } from "./readLineAsync.js"; export * from "./validate.js"; export { default as retryOnFailureAsync } from "./retryOnFailureAsync.js"; From f3c451b2fb18344c0d99d607984470e64cc6bb7a Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 16:10:30 +0900 Subject: [PATCH 73/87] =?UTF-8?q?refactor:=20LottoCalculator=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EB=B6=84=EB=A6=AC=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/calculateWinningResults.js | 12 +-- src/model/LottoCalculator.js | 103 ---------------------- src/model/LottoMatcher.js | 67 ++++++++++++++ src/model/LottoResult.js | 28 ++++++ src/model/index.js | 3 +- 5 files changed, 104 insertions(+), 109 deletions(-) delete mode 100644 src/model/LottoCalculator.js create mode 100644 src/model/LottoMatcher.js create mode 100644 src/model/LottoResult.js diff --git a/src/controller/calculateWinningResults.js b/src/controller/calculateWinningResults.js index c46498e..d1a52b7 100644 --- a/src/controller/calculateWinningResults.js +++ b/src/controller/calculateWinningResults.js @@ -1,10 +1,12 @@ -import { LottoCalculator } from "../model/index.js"; +import { LOTTO } from "../constants/index.js"; +import { LottoMatcher, LottoResult } from "../model/index.js"; const calculateWinningResults = ({ lottos, winningLotto }) => { - const { winningCounts, rateOfReturn } = new LottoCalculator({ - lottos, - winningLotto, - }); + const { winningCounts } = new LottoMatcher(lottos, winningLotto); + + const price = lottos.length * LOTTO.PRICE; + + const { rateOfReturn } = new LottoResult(winningCounts, price); return { winningCounts, rateOfReturn }; }; diff --git a/src/model/LottoCalculator.js b/src/model/LottoCalculator.js deleted file mode 100644 index 733a376..0000000 --- a/src/model/LottoCalculator.js +++ /dev/null @@ -1,103 +0,0 @@ -import { LOTTO } from "../constants/index.js"; - -class LottoCalculator { - #winningCounts; - #rateOfReturn; - - constructor({ lottos, winningLotto }) { - this.#winningCounts = { - 1: 0, - 2: 0, - 3: 0, - 4: 0, - 5: 0, - }; - - this.#winningCounts = LottoCalculator.#updateWinningCounts({ - winningCounts: this.#winningCounts, - lottos, - winningLotto, - }); - - const price = lottos.length * LOTTO.PRICE; - - this.#rateOfReturn = LottoCalculator.#calcRateOfReturn( - this.#winningCounts, - price - ); - } - - get winningCounts() { - return { ...this.#winningCounts }; - } - - get rateOfReturn() { - return this.#rateOfReturn; - } - - static #updateWinningCounts({ winningCounts, lottos, winningLotto }) { - const copiedWinningCounts = { ...winningCounts }; - const lottoMatchResults = lottos.map((lotto) => ({ - matchingCount: LottoCalculator.#matchLottoNumbers( - lotto.numbers, - winningLotto.numbers - ), - isBonusMatch: lotto.numbers.includes(winningLotto.bonusNumber), - })); - - return LottoCalculator.#countLottoWins( - lottoMatchResults, - copiedWinningCounts - ); - } - - static #matchLottoNumbers(lottoNumber, winningLottoNumber) { - return lottoNumber.filter((number) => winningLottoNumber.includes(number)) - .length; - } - - static #countLottoWins(lottoMatchResults, winningCounts) { - return lottoMatchResults.reduce( - (counts, { matchingCount, isBonusMatch }) => { - const ranking = LottoCalculator.#getRanking( - matchingCount, - isBonusMatch - ); - - if (ranking) { - counts[ranking] += 1; - } - - return counts; - }, - winningCounts - ); - } - - static #calcRateOfReturn(winningCounts, price) { - const sumOfPrize = Object.entries({ ...winningCounts }) - .map( - ([ranking, count]) => LottoCalculator.#getPrizeMoney(ranking) * count - ) - .reduce((acc, cur) => acc + cur, 0); - - return (sumOfPrize / price) * 100; - } - - static #getPrizeMoney(ranking) { - return LOTTO.RANKING_INFO.find((info) => info.ranking === Number(ranking)) - .prizeMoney; - } - - static #getRanking(matchingCount, isBonusMatch) { - const currentInfo = LOTTO.RANKING_INFO.find( - (info) => - info.matchingCount === matchingCount && - Boolean(info.isBonusMatch) === isBonusMatch - ); - - return currentInfo && currentInfo.ranking; - } -} - -export default LottoCalculator; diff --git a/src/model/LottoMatcher.js b/src/model/LottoMatcher.js new file mode 100644 index 0000000..12bd522 --- /dev/null +++ b/src/model/LottoMatcher.js @@ -0,0 +1,67 @@ +import { LOTTO } from "../constants/index.js"; + +class LottoMatcher { + #winningCounts; + + constructor(lottos, winningLotto) { + this.#winningCounts = LottoMatcher.#calculateWinningCounts( + lottos, + winningLotto + ); + } + + get winningCounts() { + return { ...this.#winningCounts }; + } + + static #calculateWinningCounts(lottos, winningLotto) { + const initialCounts = { + 1: 0, + 2: 0, + 3: 0, + 4: 0, + 5: 0, + }; + + const lottoMatchResults = lottos.map((lotto) => ({ + matchingCount: LottoMatcher.#getMatchingCount( + lotto.numbers, + winningLotto.numbers + ), + isBonusMatch: lotto.numbers.includes(winningLotto.bonusNumber), + })); + + return LottoMatcher.#updateWinningCounts(lottoMatchResults, initialCounts); + } + + static #getMatchingCount(lottoNumber, winningLottoNumber) { + return lottoNumber.filter((number) => winningLottoNumber.includes(number)) + .length; + } + + static #updateWinningCounts(lottoMatchResults, winningCounts) { + return lottoMatchResults.reduce(LottoMatcher.#updateCounts, winningCounts); + } + + static #updateCounts(counts, { matchingCount, isBonusMatch }) { + const ranking = LottoMatcher.#getRanking(matchingCount, isBonusMatch); + + if (ranking) { + counts[ranking] += 1; + } + + return counts; + } + + static #getRanking(matchingCount, isBonusMatch) { + const currentInfo = LOTTO.RANKING_INFO.find( + (info) => + info.matchingCount === matchingCount && + Boolean(info.isBonusMatch) === isBonusMatch + ); + + return currentInfo && currentInfo.ranking; + } +} + +export default LottoMatcher; diff --git a/src/model/LottoResult.js b/src/model/LottoResult.js new file mode 100644 index 0000000..29664d0 --- /dev/null +++ b/src/model/LottoResult.js @@ -0,0 +1,28 @@ +import { LOTTO } from "../constants/index.js"; + +class LottoResult { + #rateOfReturn; + + constructor(winningCounts, price) { + this.#rateOfReturn = LottoResult.#calcRateOfReturn(winningCounts, price); + } + + get rateOfReturn() { + return this.#rateOfReturn; + } + + static #calcRateOfReturn(winningCounts, price) { + const sumOfPrize = Object.entries({ ...winningCounts }) + .map(([ranking, count]) => LottoResult.#getPrizeMoney(ranking) * count) + .reduce((acc, cur) => acc + cur, 0); + + return (sumOfPrize / price) * 100; + } + + static #getPrizeMoney(ranking) { + return LOTTO.RANKING_INFO.find((info) => info.ranking === Number(ranking)) + .prizeMoney; + } +} + +export default LottoResult; diff --git a/src/model/index.js b/src/model/index.js index 1498c5a..7f8ceee 100644 --- a/src/model/index.js +++ b/src/model/index.js @@ -1,5 +1,6 @@ export { default as Lotto } from "./Lotto.js"; export { default as LottoMachine } from "./LottoMachine.js"; export { default as WinningLotto } from "./WinningLotto.js"; -export { default as LottoCalculator } from "./LottoCalculator.js"; export { default as LottoNumber } from "./LottoNumber.js"; +export { default as LottoMatcher } from "./LottoMatcher.js"; +export { default as LottoResult } from "./LottoResult.js"; From b41cc97d2d8320b86e7363cd1839decc81b770bd Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 16:11:44 +0900 Subject: [PATCH 74/87] =?UTF-8?q?test:=20LottoCalculator=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=82=AD=EC=A0=9C=ED=95=98=EA=B3=A0=20Lot?= =?UTF-8?q?toMatcher,=20LottoResult=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...alculator.test.js => LottoMatcher.test.js} | 56 ++++--------------- src/__tests__/LottoResult.test.js | 19 +++++++ 2 files changed, 29 insertions(+), 46 deletions(-) rename src/__tests__/{LottoCalculator.test.js => LottoMatcher.test.js} (67%) create mode 100644 src/__tests__/LottoResult.test.js diff --git a/src/__tests__/LottoCalculator.test.js b/src/__tests__/LottoMatcher.test.js similarity index 67% rename from src/__tests__/LottoCalculator.test.js rename to src/__tests__/LottoMatcher.test.js index be7e32b..cdb23b7 100644 --- a/src/__tests__/LottoCalculator.test.js +++ b/src/__tests__/LottoMatcher.test.js @@ -2,20 +2,17 @@ import { describe, expect, test, vi } from "vitest"; import { LottoMachine, WinningLotto, - LottoCalculator, + LottoMatcher, Lotto, } from "../model/index.js"; -import { LOTTO_PRICE } from "../constants/index.js"; +import { LOTTO } from "../constants/index.js"; -describe("LottoCalculator 클래스 테스트", () => { +describe("LottoMatcher 클래스 테스트", () => { test("1등부터 5등까지 당첨된 수를 가져온다.", () => { const { lottos } = new LottoMachine(6000); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator({ - lottos, - winningLotto, - }); + const { winningCounts } = new LottoMatcher(lottos, winningLotto); expect(winningCounts).toHaveProperty("1"); expect(winningCounts).toHaveProperty("2"); @@ -34,10 +31,7 @@ describe("LottoCalculator 클래스 테스트", () => { const { lottos } = createLottoMachineMock([[1, 2, 3, 4, 5, 6]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator({ - lottos, - winningLotto, - }); + const { winningCounts } = new LottoMatcher(lottos, winningLotto); expect(winningCounts).toHaveProperty("1", 1); }); @@ -46,10 +40,7 @@ describe("LottoCalculator 클래스 테스트", () => { const { lottos } = createLottoMachineMock([[1, 2, 3, 4, 5, 7]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator({ - lottos, - winningLotto, - }); + const { winningCounts } = new LottoMatcher(lottos, winningLotto); expect(winningCounts).toHaveProperty("2", 1); }); @@ -58,10 +49,7 @@ describe("LottoCalculator 클래스 테스트", () => { const { lottos } = createLottoMachineMock([[1, 2, 3, 4, 5, 8]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator({ - lottos, - winningLotto, - }); + const { winningCounts } = new LottoMatcher(lottos, winningLotto); expect(winningCounts).toHaveProperty("3", 1); }); @@ -70,10 +58,7 @@ describe("LottoCalculator 클래스 테스트", () => { const { lottos } = createLottoMachineMock([[1, 2, 3, 4, 8, 9]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator({ - lottos, - winningLotto, - }); + const { winningCounts } = new LottoMatcher(lottos, winningLotto); expect(winningCounts).toHaveProperty("4", 1); }); @@ -82,38 +67,17 @@ describe("LottoCalculator 클래스 테스트", () => { const { lottos } = createLottoMachineMock([[1, 2, 3, 8, 9, 10]]); const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - const { winningCounts } = new LottoCalculator({ - lottos, - winningLotto, - }); + const { winningCounts } = new LottoMatcher(lottos, winningLotto); expect(winningCounts).toHaveProperty("5", 1); }); - - test("수익률을 가져온다.", () => { - const { lottos } = createLottoMachineMock( - [ - [1, 2, 3, 8, 9, 10], - [8, 9, 10, 11, 12, 13], - ], - 2000 - ); - const winningLotto = new WinningLotto([1, 2, 3, 4, 5, 6], 7); - - const { rateOfReturn } = new LottoCalculator({ - lottos, - winningLotto, - }); - - expect(rateOfReturn).toBe(250); - }); }); function createLottoMachineMock(numbersArray) { const lottos = numbersArray.map((numbers) => new Lotto(numbers)); const lottoMachineMock = vi.fn(); lottoMachineMock.mockReturnValueOnce({ - price: lottos.length * LOTTO_PRICE, + price: lottos.length * LOTTO.PRICE, lottos, }); diff --git a/src/__tests__/LottoResult.test.js b/src/__tests__/LottoResult.test.js new file mode 100644 index 0000000..9221d46 --- /dev/null +++ b/src/__tests__/LottoResult.test.js @@ -0,0 +1,19 @@ +import { describe, expect, test } from "vitest"; +import { LottoResult } from "../model/index.js"; + +describe("LottoResult 클래스 테스트", () => { + test("수익률을 가져온다.", () => { + const winningCounts = { + 1: 0, + 2: 0, + 3: 0, + 4: 0, + 5: 1, + }; + const price = 2000; + + const { rateOfReturn } = new LottoResult(winningCounts, price); + + expect(rateOfReturn).toBe(250); + }); +}); From 15933cb8946d0d907369b7ee64eed49e37fe5688 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 16:12:13 +0900 Subject: [PATCH 75/87] =?UTF-8?q?fix:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20impo?= =?UTF-8?q?rt=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view/showWinningResults.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/view/showWinningResults.js b/src/view/showWinningResults.js index efdcccf..d985c7d 100644 --- a/src/view/showWinningResults.js +++ b/src/view/showWinningResults.js @@ -1,4 +1,4 @@ -import { LOTTO_RANKING_INFO } from "../constants/index.js"; +import { LOTTO } from "../constants/index.js"; import { outputManager } from "../service/index.js"; import { formatKoreanCurrency } from "../utils/index.js"; @@ -6,7 +6,7 @@ const showWinningResults = (winningCounts, rateOfReturn) => { outputManager.print("당첨 통계"); outputManager.print("--------------------"); - LOTTO_RANKING_INFO.reverse().forEach( + LOTTO.RANKING_INFO.reverse().forEach( ({ ranking, matchingCount, isBonusMatch, prizeMoney }) => { const bonusMatchMessage = isBonusMatch ? `, 보너스 볼 일치` : ""; const formattedPrizeMoney = formatKoreanCurrency(prizeMoney); From daf3e249b6fab88cb5f240d78464c1a44ab20a66 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 16:23:26 +0900 Subject: [PATCH 76/87] =?UTF-8?q?refactor:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...esult.test.js => LottoReturnCalculator.test.js} | 6 +++--- src/controller/calculateWinningResults.js | 4 ++-- .../{LottoResult.js => LottoReturnCalculator.js} | 14 ++++++++++---- src/model/index.js | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) rename src/__tests__/{LottoResult.test.js => LottoReturnCalculator.test.js} (56%) rename src/model/{LottoResult.js => LottoReturnCalculator.js} (63%) diff --git a/src/__tests__/LottoResult.test.js b/src/__tests__/LottoReturnCalculator.test.js similarity index 56% rename from src/__tests__/LottoResult.test.js rename to src/__tests__/LottoReturnCalculator.test.js index 9221d46..fa34d29 100644 --- a/src/__tests__/LottoResult.test.js +++ b/src/__tests__/LottoReturnCalculator.test.js @@ -1,7 +1,7 @@ import { describe, expect, test } from "vitest"; -import { LottoResult } from "../model/index.js"; +import { LottoReturnCalculator } from "../model/index.js"; -describe("LottoResult 클래스 테스트", () => { +describe("LottoReturnCalculator 클래스 테스트", () => { test("수익률을 가져온다.", () => { const winningCounts = { 1: 0, @@ -12,7 +12,7 @@ describe("LottoResult 클래스 테스트", () => { }; const price = 2000; - const { rateOfReturn } = new LottoResult(winningCounts, price); + const { rateOfReturn } = new LottoReturnCalculator(winningCounts, price); expect(rateOfReturn).toBe(250); }); diff --git a/src/controller/calculateWinningResults.js b/src/controller/calculateWinningResults.js index d1a52b7..0fdc8ea 100644 --- a/src/controller/calculateWinningResults.js +++ b/src/controller/calculateWinningResults.js @@ -1,12 +1,12 @@ import { LOTTO } from "../constants/index.js"; -import { LottoMatcher, LottoResult } from "../model/index.js"; +import { LottoMatcher, LottoReturnCalculator } from "../model/index.js"; const calculateWinningResults = ({ lottos, winningLotto }) => { const { winningCounts } = new LottoMatcher(lottos, winningLotto); const price = lottos.length * LOTTO.PRICE; - const { rateOfReturn } = new LottoResult(winningCounts, price); + const { rateOfReturn } = new LottoReturnCalculator(winningCounts, price); return { winningCounts, rateOfReturn }; }; diff --git a/src/model/LottoResult.js b/src/model/LottoReturnCalculator.js similarity index 63% rename from src/model/LottoResult.js rename to src/model/LottoReturnCalculator.js index 29664d0..636baa6 100644 --- a/src/model/LottoResult.js +++ b/src/model/LottoReturnCalculator.js @@ -1,10 +1,13 @@ import { LOTTO } from "../constants/index.js"; -class LottoResult { +class LottoReturnCalculator { #rateOfReturn; constructor(winningCounts, price) { - this.#rateOfReturn = LottoResult.#calcRateOfReturn(winningCounts, price); + this.#rateOfReturn = LottoReturnCalculator.#calcRateOfReturn( + winningCounts, + price + ); } get rateOfReturn() { @@ -13,7 +16,10 @@ class LottoResult { static #calcRateOfReturn(winningCounts, price) { const sumOfPrize = Object.entries({ ...winningCounts }) - .map(([ranking, count]) => LottoResult.#getPrizeMoney(ranking) * count) + .map( + ([ranking, count]) => + LottoReturnCalculator.#getPrizeMoney(ranking) * count + ) .reduce((acc, cur) => acc + cur, 0); return (sumOfPrize / price) * 100; @@ -25,4 +31,4 @@ class LottoResult { } } -export default LottoResult; +export default LottoReturnCalculator; diff --git a/src/model/index.js b/src/model/index.js index 7f8ceee..de53d0a 100644 --- a/src/model/index.js +++ b/src/model/index.js @@ -3,4 +3,4 @@ export { default as LottoMachine } from "./LottoMachine.js"; export { default as WinningLotto } from "./WinningLotto.js"; export { default as LottoNumber } from "./LottoNumber.js"; export { default as LottoMatcher } from "./LottoMatcher.js"; -export { default as LottoResult } from "./LottoResult.js"; +export { default as LottoReturnCalculator } from "./LottoReturnCalculator.js"; From bbf664964756474756d60092bec5291866ac5f35 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 20:48:24 +0900 Subject: [PATCH 77/87] =?UTF-8?q?feat:=20=EC=9E=AC=EC=8B=9C=EC=9E=91=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/index.js | 1 + src/controller/shouldRestartGame.js | 33 +++++++++++++++++++++++++++++ src/main.js | 19 ++++++++++------- 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 src/controller/shouldRestartGame.js diff --git a/src/controller/index.js b/src/controller/index.js index 7bdd09f..27b0aa6 100644 --- a/src/controller/index.js +++ b/src/controller/index.js @@ -1,3 +1,4 @@ export { default as purchaseLottos } from "./purchaseLottos.js"; export { default as createWinningLotto } from "./createWinningLotto.js"; export { default as calculateWinningResults } from "./calculateWinningResults.js"; +export { default as shouldRestartGame } from "./shouldRestartGame.js"; diff --git a/src/controller/shouldRestartGame.js b/src/controller/shouldRestartGame.js new file mode 100644 index 0000000..a9f700d --- /dev/null +++ b/src/controller/shouldRestartGame.js @@ -0,0 +1,33 @@ +import { inputManager, outputManager } from "../service/index.js"; +import { + retryOnFailureAsync, + throwErrorWithCondition, +} from "../utils/index.js"; + +const shouldRestartGame = async () => { + const restart = await retryOnFailureAsync(confirmRestart, (error) => + outputManager.print(error.message) + ); + + return restart === "y"; +}; + +export default shouldRestartGame; + +const confirmRestart = async () => { + const inputValue = await inputManager.scan( + "다시 시작하시겠습니까? (y/n) ", + (inputValue) => inputValue.trim().toLowerCase() + ); + + validateRestartInput(inputValue); + + return inputValue; +}; + +const validateRestartInput = (inputValue) => { + throwErrorWithCondition( + inputValue !== "n" && inputValue !== "y", + "잘못된 입력입니다." + ); +}; diff --git a/src/main.js b/src/main.js index 8a9dbde..490f69a 100644 --- a/src/main.js +++ b/src/main.js @@ -2,21 +2,24 @@ import { purchaseLottos, createWinningLotto, calculateWinningResults, + shouldRestartGame, } from "./controller/index.js"; import { showPurchasedLottos, showWinningResults } from "./view/index.js"; const main = async () => { - const { count, lottos } = await purchaseLottos(); + do { + const { count, lottos } = await purchaseLottos(); - showPurchasedLottos(count, lottos); + showPurchasedLottos(count, lottos); - const winningLotto = await createWinningLotto(); - const { winningCounts, rateOfReturn } = calculateWinningResults({ - lottos, - winningLotto, - }); + const winningLotto = await createWinningLotto(); + const { winningCounts, rateOfReturn } = calculateWinningResults({ + lottos, + winningLotto, + }); - showWinningResults(winningCounts, rateOfReturn); + showWinningResults(winningCounts, rateOfReturn); + } while (await shouldRestartGame()); }; main(); From 59db060e3738ee23353039c0a85e6c78fa6f7d4b Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 20:51:49 +0900 Subject: [PATCH 78/87] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/createWinningLotto.js | 58 ++++++++++++++-------------- src/model/LottoMachine.js | 26 ++++++------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/controller/createWinningLotto.js b/src/controller/createWinningLotto.js index a4adee7..a39bebb 100644 --- a/src/controller/createWinningLotto.js +++ b/src/controller/createWinningLotto.js @@ -2,6 +2,31 @@ import { inputManager, outputManager } from "../service/index.js"; import { Lotto, LottoNumber, WinningLotto } from "../model/index.js"; import { retryOnFailureAsync } from "../utils/index.js"; +const createWinningLotto = async () => { + const winningLotto = await retryOnFailureAsync( + async () => { + const winningNumbers = await retryCreateWinningNumbers(); + const bonusNumber = await retryCreateBonusNumber(); + + return new WinningLotto(winningNumbers, bonusNumber); + }, + (error) => outputManager.print(error.message) + ); + + return winningLotto; +}; + +export default createWinningLotto; + +const retryCreateWinningNumbers = async () => { + const winningNumbers = await retryOnFailureAsync( + createWinningNumbers, + (error) => outputManager.print(error.message) + ); + + return winningNumbers; +}; + const createWinningNumbers = async () => { const inputNumbers = await inputManager.scan( "> 당첨 번호를 입력해 주세요. ", @@ -18,13 +43,12 @@ const createWinningNumbers = async () => { return winningNumbers.numbers; }; -const retryCreateWinningNumbers = async () => { - const winningNumbers = await retryOnFailureAsync( - createWinningNumbers, - (error) => outputManager.print(error.message) +const retryCreateBonusNumber = async () => { + const bonusNumber = await retryOnFailureAsync(createBonusNumber, (error) => + outputManager.print(error.message) ); - return winningNumbers; + return bonusNumber; }; const createBonusNumber = async () => { @@ -41,27 +65,3 @@ const createBonusNumber = async () => { return bonusNumber.value; }; - -const retryCreateBonusNumber = async () => { - const bonusNumber = await retryOnFailureAsync(createBonusNumber, (error) => - outputManager.print(error.message) - ); - - return bonusNumber; -}; - -const createWinningLotto = async () => { - const winningLotto = await retryOnFailureAsync( - async () => { - const winningNumbers = await retryCreateWinningNumbers(); - const bonusNumber = await retryCreateBonusNumber(); - - return new WinningLotto(winningNumbers, bonusNumber); - }, - (error) => outputManager.print(error.message) - ); - - return winningLotto; -}; - -export default createWinningLotto; diff --git a/src/model/LottoMachine.js b/src/model/LottoMachine.js index c053361..6a64996 100644 --- a/src/model/LottoMachine.js +++ b/src/model/LottoMachine.js @@ -25,6 +25,19 @@ class LottoMachine { return [...this.#lottos]; } + static #validatePrice(price) { + validate.integer(price, "로또 구입 금액으로 정수를 입력해야 합니다."); + + throwErrorWithCondition( + price < LOTTO.PRICE, + `로또 구입 금액은 ${LOTTO.PRICE}원이상이어야 합니다.` + ); + } + + static #countLotto(price) { + return Math.floor(price / LOTTO.PRICE); + } + static #createLottos(count) { return Array.from({ length: count }).map(LottoMachine.#createLotto); } @@ -41,19 +54,6 @@ class LottoMachine { return shuffle(lottoNumbers).slice(0, LOTTO.NUMBERS_SIZE); } - - static #countLotto(price) { - return Math.floor(price / LOTTO.PRICE); - } - - static #validatePrice(price) { - validate.integer(price, "로또 구입 금액으로 정수를 입력해야 합니다."); - - throwErrorWithCondition( - price < LOTTO.PRICE, - `로또 구입 금액은 ${LOTTO.PRICE}원이상이어야 합니다.` - ); - } } export default LottoMachine; From ff3fb404e076f069b0a278d2c00a5fe4ebb8db69 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 22:59:46 +0900 Subject: [PATCH 79/87] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EA=B4=80=EB=A0=A8=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/errorMessage.js | 8 ++++++++ src/constants/index.js | 36 ++--------------------------------- 2 files changed, 10 insertions(+), 34 deletions(-) create mode 100644 src/constants/errorMessage.js diff --git a/src/constants/errorMessage.js b/src/constants/errorMessage.js new file mode 100644 index 0000000..3f3c946 --- /dev/null +++ b/src/constants/errorMessage.js @@ -0,0 +1,8 @@ +export const USER_FRIENDLY_ERROR_MESSAGES = { + ERR_001: + "입력하신 값은 로또 번호로 사용할 수 없습니다. 1부터 45 사이의 숫자를 입력해 주세요.", + ERR_002: "로또 번호는 중복 없이 6개의 서로 다른 숫자를 입력해 주세요.", + ERR_003: + "보너스 번호가 당첨 번호와 중복되었습니다. 당첨 번호 6개와 중복되지 않는 보너스 번호 1개를 포함하여 총 7개의 번호를 다시 입력해 주세요.", + ERR_004: "로또 구매 금액은 1,000원 단위로 입력해 주세요.", +}; diff --git a/src/constants/index.js b/src/constants/index.js index 8b9766f..d85d01d 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -1,34 +1,2 @@ -export const LOTTO = { - NUMBERS_SIZE: 6, - MIN_NUMBER: 1, - MAX_NUMBER: 45, - PRICE: 1000, - RANKING_INFO: [ - { - ranking: 1, - matchingCount: 6, - prizeMoney: 2_000_000_000, - }, - { - ranking: 2, - matchingCount: 5, - isBonusMatch: true, - prizeMoney: 30_000_000, - }, - { - ranking: 3, - matchingCount: 5, - prizeMoney: 1_500_000, - }, - { - ranking: 4, - matchingCount: 4, - prizeMoney: 50_000, - }, - { - ranking: 5, - matchingCount: 3, - prizeMoney: 5_000, - }, - ], -}; +export * from "./lotto.js"; +export * from "./errorMessage.js"; From 42f46b7a7ab96b757c1cd746d5e92341e42eea59 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 23:00:18 +0900 Subject: [PATCH 80/87] =?UTF-8?q?refactor:=20=ED=8C=8C=EC=9D=BC=EB=AA=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/lotto.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/constants/lotto.js diff --git a/src/constants/lotto.js b/src/constants/lotto.js new file mode 100644 index 0000000..8b9766f --- /dev/null +++ b/src/constants/lotto.js @@ -0,0 +1,34 @@ +export const LOTTO = { + NUMBERS_SIZE: 6, + MIN_NUMBER: 1, + MAX_NUMBER: 45, + PRICE: 1000, + RANKING_INFO: [ + { + ranking: 1, + matchingCount: 6, + prizeMoney: 2_000_000_000, + }, + { + ranking: 2, + matchingCount: 5, + isBonusMatch: true, + prizeMoney: 30_000_000, + }, + { + ranking: 3, + matchingCount: 5, + prizeMoney: 1_500_000, + }, + { + ranking: 4, + matchingCount: 4, + prizeMoney: 50_000, + }, + { + ranking: 5, + matchingCount: 3, + prizeMoney: 5_000, + }, + ], +}; From 7006658a678021542b077240a4e12ee6d2aa4f85 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 23:00:57 +0900 Subject: [PATCH 81/87] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EA=B4=80=EB=A0=A8=20=EC=9C=A0=ED=8B=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/getUserFriendlyErrorMessage.js | 8 ++++++++ src/utils/index.js | 1 + 2 files changed, 9 insertions(+) create mode 100644 src/utils/getUserFriendlyErrorMessage.js diff --git a/src/utils/getUserFriendlyErrorMessage.js b/src/utils/getUserFriendlyErrorMessage.js new file mode 100644 index 0000000..c103beb --- /dev/null +++ b/src/utils/getUserFriendlyErrorMessage.js @@ -0,0 +1,8 @@ +const getUserFriendlyErrorMessage = (errorMessage, userFriendlyMessages) => { + const match = errorMessage.match(/^\[(ERR_\d{3})\]/); + const errorCode = match && match[1]; + + return (errorCode && userFriendlyMessages[errorCode]) || errorMessage; +}; + +export default getUserFriendlyErrorMessage; diff --git a/src/utils/index.js b/src/utils/index.js index 859268e..b94c3cb 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -4,3 +4,4 @@ export * from "./validate.js"; export { default as retryOnFailureAsync } from "./retryOnFailureAsync.js"; export { default as formatKoreanCurrency } from "./formatKoreanCurrency.js"; export * from "./arrayUtils.js"; +export { default as getUserFriendlyErrorMessage } from "./getUserFriendlyErrorMessage.js"; From e0cd64f882342c4777d24f1f511f49045165d06e Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 23:01:38 +0900 Subject: [PATCH 82/87] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EA=B4=80=EB=A0=A8=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/handleErrorAndPrint.js | 14 ++++++++++++++ src/controller/index.js | 1 + 2 files changed, 15 insertions(+) create mode 100644 src/controller/handleErrorAndPrint.js diff --git a/src/controller/handleErrorAndPrint.js b/src/controller/handleErrorAndPrint.js new file mode 100644 index 0000000..f190631 --- /dev/null +++ b/src/controller/handleErrorAndPrint.js @@ -0,0 +1,14 @@ +import { getUserFriendlyErrorMessage } from "../utils/index.js"; +import { USER_FRIENDLY_ERROR_MESSAGES } from "../constants/index.js"; +import { outputManager } from "../service/index.js"; + +const handleErrorAndPrint = (error) => { + const errorMessage = getUserFriendlyErrorMessage( + error.message, + USER_FRIENDLY_ERROR_MESSAGES + ); + + outputManager.print(errorMessage); +}; + +export default handleErrorAndPrint; diff --git a/src/controller/index.js b/src/controller/index.js index 27b0aa6..0544edf 100644 --- a/src/controller/index.js +++ b/src/controller/index.js @@ -2,3 +2,4 @@ export { default as purchaseLottos } from "./purchaseLottos.js"; export { default as createWinningLotto } from "./createWinningLotto.js"; export { default as calculateWinningResults } from "./calculateWinningResults.js"; export { default as shouldRestartGame } from "./shouldRestartGame.js"; +export { default as handleErrorAndPrint } from "./handleErrorAndPrint.js"; From de23f6865e1dba8416ccd635ec8a21cd390c699c Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 23:02:32 +0900 Subject: [PATCH 83/87] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Lotto.js | 9 ++++++--- src/model/LottoMachine.js | 7 +++++-- src/model/LottoNumber.js | 9 ++++++--- src/model/WinningLotto.js | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/model/Lotto.js b/src/model/Lotto.js index 188d0d2..0737106 100644 --- a/src/model/Lotto.js +++ b/src/model/Lotto.js @@ -20,16 +20,19 @@ class Lotto { } static #validateNumbers(lottoNumbers) { - validate.array(lottoNumbers, "로또 번호로 적합하지 않은 값입니다."); + validate.array( + lottoNumbers, + "[ERR_002] Lotto 클래스의 생성자 인수는 배열이어야 합니다." + ); throwErrorWithCondition( lottoNumbers.length !== LOTTO.NUMBERS_SIZE, - `로또 번호는 ${LOTTO.NUMBERS_SIZE}개여야 합니다.` + `[ERR_002] Lotto 클래스의 생성자 인수의 길이는 ${LOTTO.NUMBERS_SIZE}이어야 합니다.` ); throwErrorWithCondition( isDuplicated(lottoNumbers), - "중복되는 로또 번호가 있습니다." + "[ERR_002] Lotto 클래스의 생성자 인수인 배열에 중복되는 값이 있습니다." ); } } diff --git a/src/model/LottoMachine.js b/src/model/LottoMachine.js index 6a64996..fabd681 100644 --- a/src/model/LottoMachine.js +++ b/src/model/LottoMachine.js @@ -26,11 +26,14 @@ class LottoMachine { } static #validatePrice(price) { - validate.integer(price, "로또 구입 금액으로 정수를 입력해야 합니다."); + validate.integer( + price, + "[ERR_004] LottoMachine 클래스의 생성자 인수는 정수여야 합니다." + ); throwErrorWithCondition( price < LOTTO.PRICE, - `로또 구입 금액은 ${LOTTO.PRICE}원이상이어야 합니다.` + `[ERR_004] LottoMachine 클래스의 생성자 인수는 ${LOTTO.PRICE}이상이어야 합니다.` ); } diff --git a/src/model/LottoNumber.js b/src/model/LottoNumber.js index d45cd84..220776f 100644 --- a/src/model/LottoNumber.js +++ b/src/model/LottoNumber.js @@ -15,16 +15,19 @@ class LottoNumber { } static #validateNumber(number) { - validate.integer(number, "로또 번호로 적합하지 않은 값입니다."); + validate.integer( + number, + "[ERR_001] LottoNumber 클래스의 생성자 인수는 정수여야 합니다." + ); throwErrorWithCondition( number < LOTTO.MIN_NUMBER, - `로또 번호는 ${LOTTO.MIN_NUMBER}보다 커야 합니다.` + `[ERR_001] LottoNumber 클래스의 생성자 인수는 ${LOTTO.MIN_NUMBER}이상이어야 합니다.` ); throwErrorWithCondition( LOTTO.MAX_NUMBER < number, - `로또 번호는 ${LOTTO.MAX_NUMBER}보다 작아야 합니다.` + `[ERR_001] LottoNumber 클래스의 생성자 인수는 ${LOTTO.MAX_NUMBER}이하여야 합니다.` ); } } diff --git a/src/model/WinningLotto.js b/src/model/WinningLotto.js index 7e4d1d0..f5028f8 100644 --- a/src/model/WinningLotto.js +++ b/src/model/WinningLotto.js @@ -19,7 +19,7 @@ class WinningLotto extends Lotto { static #validateBonusNumber(winningNumbers, bonusNumber) { throwErrorWithCondition( isDuplicated(winningNumbers.concat(bonusNumber)), - "당첨 번호 중에 보너스 번호와 중복되는 번호가 있습니다." + "[ERR_003] WinningLotto 클래스의 생성자 인수인 winningNumbers 중에 bonusNumber와 중복됩니다." ); } } From 4477059b9ae717909017fcbe5c5d93b22036b207 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 23:02:50 +0900 Subject: [PATCH 84/87] =?UTF-8?q?test:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=88=98=EC=A0=95=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/Lotto.test.js | 6 +++--- src/__tests__/LottoMachine.test.js | 4 ++-- src/__tests__/LottoNumber.test.js | 6 +++--- src/__tests__/WinningLotto.test.js | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/__tests__/Lotto.test.js b/src/__tests__/Lotto.test.js index eac3fe7..6318151 100644 --- a/src/__tests__/Lotto.test.js +++ b/src/__tests__/Lotto.test.js @@ -13,20 +13,20 @@ describe("Lotto 클래스 테스트.", () => { "로또 번호로 적합하지 않은 값($value)을 받으면 오류가 발생한다.", ({ value }) => { expect(() => new Lotto(value)).toThrowError( - "로또 번호로 적합하지 않은 값입니다." + "[ERR_002] Lotto 클래스의 생성자 인수는 배열이어야 합니다." ); } ); test("로또 번호가 6개 초과이면 오류가 발생한다.", () => { expect(() => new Lotto([1, 2, 3, 4, 5, 6, 7])).toThrowError( - "로또 번호는 6개여야 합니다." + "[ERR_002] Lotto 클래스의 생성자 인수의 길이는 6이어야 합니다." ); }); test("로또 번호가 6개 미만이면 오류가 발생한다.", () => { expect(() => new Lotto([1, 2, 3, 4, 5])).toThrowError( - "로또 번호는 6개여야 합니다." + "[ERR_002] Lotto 클래스의 생성자 인수의 길이는 6이어야 합니다." ); }); diff --git a/src/__tests__/LottoMachine.test.js b/src/__tests__/LottoMachine.test.js index 2070f9c..0f62f03 100644 --- a/src/__tests__/LottoMachine.test.js +++ b/src/__tests__/LottoMachine.test.js @@ -4,13 +4,13 @@ import { LottoMachine } from "../model/index.js"; describe("LottoMachine 클래스 테스트", () => { test("로또를 구입할 때 정수가 아닌 값을 받으면 오류가 발생한다.", () => { expect(() => new LottoMachine("1")).toThrowError( - "로또 구입 금액으로 정수를 입력해야 합니다." + "[ERR_004] LottoMachine 클래스의 생성자 인수는 정수여야 합니다." ); }); test("로또를 구입할 때 1000원미만의 값을 받으면 오류가 발생한다.", () => { expect(() => new LottoMachine(999)).toThrowError( - "로또 구입 금액은 1000원이상이어야 합니다." + "[ERR_004] LottoMachine 클래스의 생성자 인수는 1000이상이어야 합니다." ); }); diff --git a/src/__tests__/LottoNumber.test.js b/src/__tests__/LottoNumber.test.js index 691481d..c89bdd0 100644 --- a/src/__tests__/LottoNumber.test.js +++ b/src/__tests__/LottoNumber.test.js @@ -14,20 +14,20 @@ describe("LottoNumber 클래스 테스트", () => { "로또 번호로 적합하지 않은 값($value)을 할당하면 오류가 발생한다.", ({ value }) => { expect(() => new LottoNumber(value)).toThrowError( - "로또 번호로 적합하지 않은 값입니다." + "[ERR_001] LottoNumber 클래스의 생성자 인수는 정수여야 합니다." ); } ); test("로또 번호가 1보다 작으면 오류가 발생한다.", () => { expect(() => new LottoNumber(0)).toThrowError( - "로또 번호는 1보다 커야 합니다." + "[ERR_001] LottoNumber 클래스의 생성자 인수는 1이상이어야 합니다." ); }); test("로또 번호가 45보다 크면 오류가 발생한다.", () => { expect(() => new LottoNumber(46)).toThrowError( - "로또 번호는 45보다 작아야 합니다." + "[ERR_001] LottoNumber 클래스의 생성자 인수는 45이하여야 합니다." ); }); diff --git a/src/__tests__/WinningLotto.test.js b/src/__tests__/WinningLotto.test.js index 4aac413..542ecc1 100644 --- a/src/__tests__/WinningLotto.test.js +++ b/src/__tests__/WinningLotto.test.js @@ -7,7 +7,7 @@ describe("WinningLotto 클래스 테스트", () => { const bonusNumber = 6; expect(() => new WinningLotto(lottoNumbers, bonusNumber)).toThrowError( - "당첨 번호 중에 보너스 번호와 중복되는 번호가 있습니다." + "[ERR_003] WinningLotto 클래스의 생성자 인수인 winningNumbers 중에 bonusNumber와 중복됩니다." ); }); From d38145f83a5a18cca8d01f52cdb8c9a7e4fa19d7 Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 23:03:11 +0900 Subject: [PATCH 85/87] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EA=B4=80=EB=A0=A8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/createWinningLotto.js | 23 +++++++++++------------ src/controller/purchaseLottos.js | 26 ++++++++++++-------------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/controller/createWinningLotto.js b/src/controller/createWinningLotto.js index a39bebb..90fcb21 100644 --- a/src/controller/createWinningLotto.js +++ b/src/controller/createWinningLotto.js @@ -1,17 +1,15 @@ -import { inputManager, outputManager } from "../service/index.js"; +import { inputManager } from "../service/index.js"; import { Lotto, LottoNumber, WinningLotto } from "../model/index.js"; import { retryOnFailureAsync } from "../utils/index.js"; +import handleErrorAndPrint from "./handleErrorAndPrint.js"; const createWinningLotto = async () => { - const winningLotto = await retryOnFailureAsync( - async () => { - const winningNumbers = await retryCreateWinningNumbers(); - const bonusNumber = await retryCreateBonusNumber(); + const winningLotto = await retryOnFailureAsync(async () => { + const winningNumbers = await retryCreateWinningNumbers(); + const bonusNumber = await retryCreateBonusNumber(); - return new WinningLotto(winningNumbers, bonusNumber); - }, - (error) => outputManager.print(error.message) - ); + return new WinningLotto(winningNumbers, bonusNumber); + }, handleErrorAndPrint); return winningLotto; }; @@ -21,7 +19,7 @@ export default createWinningLotto; const retryCreateWinningNumbers = async () => { const winningNumbers = await retryOnFailureAsync( createWinningNumbers, - (error) => outputManager.print(error.message) + handleErrorAndPrint ); return winningNumbers; @@ -44,8 +42,9 @@ const createWinningNumbers = async () => { }; const retryCreateBonusNumber = async () => { - const bonusNumber = await retryOnFailureAsync(createBonusNumber, (error) => - outputManager.print(error.message) + const bonusNumber = await retryOnFailureAsync( + createBonusNumber, + handleErrorAndPrint ); return bonusNumber; diff --git a/src/controller/purchaseLottos.js b/src/controller/purchaseLottos.js index f1ba05b..c981f41 100644 --- a/src/controller/purchaseLottos.js +++ b/src/controller/purchaseLottos.js @@ -1,23 +1,21 @@ import { LottoMachine } from "../model/index.js"; -import { inputManager, outputManager } from "../service/index.js"; +import { inputManager } from "../service/index.js"; import { retryOnFailureAsync } from "../utils/index.js"; +import handleErrorAndPrint from "./handleErrorAndPrint.js"; const purchaseLottos = async () => { - const { count, lottos } = await retryOnFailureAsync( - async () => { - const priceValue = await inputManager.scan( - "> 구입 금액을 입력해 주세요. ", - (inputValue) => { - const trimedInputValue = inputValue.trim(); + const { count, lottos } = await retryOnFailureAsync(async () => { + const priceValue = await inputManager.scan( + "> 구입 금액을 입력해 주세요. ", + (inputValue) => { + const trimedInputValue = inputValue.trim(); - return Number(trimedInputValue); - } - ); + return Number(trimedInputValue); + } + ); - return new LottoMachine(priceValue); - }, - (error) => outputManager.print(error.message) - ); + return new LottoMachine(priceValue); + }, handleErrorAndPrint); return { count, lottos }; }; From 19569650d29829de1230a1437e6a463d9813d2aa Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 23:38:10 +0900 Subject: [PATCH 86/87] =?UTF-8?q?feat:=20=EC=88=98=EC=9D=B5=EB=A5=A0?= =?UTF-8?q?=EC=9D=B4=20=EC=A0=95=EC=88=98=EC=9D=B8=EC=A7=80=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=EC=A7=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/LottoReturnCalculator.js | 3 ++- src/utils/formatNumber.js | 5 +++++ src/utils/index.js | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/utils/formatNumber.js diff --git a/src/model/LottoReturnCalculator.js b/src/model/LottoReturnCalculator.js index 636baa6..613276c 100644 --- a/src/model/LottoReturnCalculator.js +++ b/src/model/LottoReturnCalculator.js @@ -1,4 +1,5 @@ import { LOTTO } from "../constants/index.js"; +import { formatNumber } from "../utils/index.js"; class LottoReturnCalculator { #rateOfReturn; @@ -11,7 +12,7 @@ class LottoReturnCalculator { } get rateOfReturn() { - return this.#rateOfReturn; + return formatNumber(this.#rateOfReturn); } static #calcRateOfReturn(winningCounts, price) { diff --git a/src/utils/formatNumber.js b/src/utils/formatNumber.js new file mode 100644 index 0000000..c51c8d6 --- /dev/null +++ b/src/utils/formatNumber.js @@ -0,0 +1,5 @@ +const formatNumber = (number, digits = 2) => { + return Number.isInteger(number) ? number : number.toFixed(digits); +}; + +export default formatNumber; diff --git a/src/utils/index.js b/src/utils/index.js index b94c3cb..13c0f93 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -5,3 +5,4 @@ export { default as retryOnFailureAsync } from "./retryOnFailureAsync.js"; export { default as formatKoreanCurrency } from "./formatKoreanCurrency.js"; export * from "./arrayUtils.js"; export { default as getUserFriendlyErrorMessage } from "./getUserFriendlyErrorMessage.js"; +export { default as formatNumber } from "./formatNumber.js"; From 19ab847898236df07842de9652544aa862e7b42f Mon Sep 17 00:00:00 2001 From: seeyoujeong Date: Mon, 15 Jul 2024 23:41:48 +0900 Subject: [PATCH 87/87] =?UTF-8?q?feat:=20=EB=8B=A4=EC=8B=9C=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91=20=EC=B7=A8=EC=86=8C=ED=95=A0=EB=95=8C=20=EC=95=84?= =?UTF-8?q?=EB=AC=B4=ED=82=A4=20=EC=9E=85=EB=A0=A5=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/shouldRestartGame.js | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/controller/shouldRestartGame.js b/src/controller/shouldRestartGame.js index a9f700d..1fc09d1 100644 --- a/src/controller/shouldRestartGame.js +++ b/src/controller/shouldRestartGame.js @@ -1,13 +1,7 @@ -import { inputManager, outputManager } from "../service/index.js"; -import { - retryOnFailureAsync, - throwErrorWithCondition, -} from "../utils/index.js"; +import { inputManager } from "../service/index.js"; const shouldRestartGame = async () => { - const restart = await retryOnFailureAsync(confirmRestart, (error) => - outputManager.print(error.message) - ); + const restart = await confirmRestart(); return restart === "y"; }; @@ -16,18 +10,9 @@ export default shouldRestartGame; const confirmRestart = async () => { const inputValue = await inputManager.scan( - "다시 시작하시겠습니까? (y/n) ", + "다시 시작하시겠습니까? (Yes: y, No: 아무키) ", (inputValue) => inputValue.trim().toLowerCase() ); - validateRestartInput(inputValue); - return inputValue; }; - -const validateRestartInput = (inputValue) => { - throwErrorWithCondition( - inputValue !== "n" && inputValue !== "y", - "잘못된 입력입니다." - ); -};