From fd88d68fded6288721154cece9fbfd2ed967870c Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sat, 18 Oct 2025 09:36:46 +0900 Subject: [PATCH 01/21] =?UTF-8?q?docs:=20README.md=EC=97=90=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=A0=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=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 --- README.md | 35 ++++++++++++++++++++++++++++++++++- package-lock.json | 2 ++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 13420b29..82984eeb 100644 --- a/README.md +++ b/README.md @@ -1 +1,34 @@ -# javascript-calculator-precourse \ No newline at end of file +# javascript-calculator-precourse + +# 구현할 기능 목록 + +## 문자 입력받기 + +- `Console.readLineAsync("문자열을 입력해 주세요")`로 문자열 받기 +- 문자열이 비어있을 시 0 반환 + +
+ +## 숫자 추출을 위한 문자열 분리 + +- `,`와 `:`을 이용하여 문자열을 분리 +- `//`와 `\n` 사이의 문자를 커스텀 구분자로 지정 +- 커스텀 구분자를 이용하여 문자열을 분리 + +
+ +## 덧셈 하기 + +- `reduce`를 이용하여 분리된 숫자들을 모두 더함 + +
+ +## 결과 반환 + +- `Console.print()`로 결과 출력 + +
+ +## ERROR 검출 + +- `[ERROR]`로 시작하는 메시지와 함께 Error 발생 후 애플리케이션 종료 diff --git a/package-lock.json b/package-lock.json index a1211435..bd7e7bd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,6 +52,7 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -2969,6 +2970,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001663", "electron-to-chromium": "^1.5.28", From c7e5709c144f3f391f531194d17376031ddae4fd Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sat, 18 Oct 2025 14:03:26 +0900 Subject: [PATCH 02/21] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EC=9E=85/=EC=B6=9C=EB=A0=A5=20=EB=AC=B8=EA=B5=AC=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/constants.js diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 00000000..12184ae6 --- /dev/null +++ b/src/constants.js @@ -0,0 +1,7 @@ +export const INPUT_MESSAGE = { + INPUT_STRING: "덧셈할 문자열을 입력해 주세요.\n", +}; + +export const OUTPUT_MESSAGE = { + OUTPUT_RESULT: "결과 : ", +}; From 8ea8e007f54a189cc9f7c3c093dc84e9e02a2e1d Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sat, 18 Oct 2025 14:45:46 +0900 Subject: [PATCH 03/21] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=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/App.js | 11 ++++++++++- src/constants.js | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index 091aa0a5..5ec691e7 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,14 @@ +import { Console } from "@woowacourse/mission-utils"; +import { INPUT_MESSAGE } from "./constants.js"; + class App { - async run() {} + async run() { + await this.getInput(); + } + + async getInput() { + this.input = await Console.readLineAsync(INPUT_MESSAGE.INPUT_STRING); + } } export default App; diff --git a/src/constants.js b/src/constants.js index 12184ae6..f23fe158 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,7 +1,7 @@ export const INPUT_MESSAGE = { - INPUT_STRING: "덧셈할 문자열을 입력해 주세요.\n", + INPUT_STRING: '덧셈할 문자열을 입력해 주세요.\n', }; export const OUTPUT_MESSAGE = { - OUTPUT_RESULT: "결과 : ", + OUTPUT_RESULT: '결과 : ', }; From 4f132dabfbc6e49690516a9ec02d55114f0bedb0 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sat, 18 Oct 2025 18:24:08 +0900 Subject: [PATCH 04/21] =?UTF-8?q?feat:=20=EA=B8=B0=EB=B3=B8=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=EC=9E=90=EC=99=80=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=EC=9E=90=EB=A5=BC=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=ED=95=9C=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/App.js b/src/App.js index 5ec691e7..fff9943c 100644 --- a/src/App.js +++ b/src/App.js @@ -9,6 +9,16 @@ class App { async getInput() { this.input = await Console.readLineAsync(INPUT_MESSAGE.INPUT_STRING); } + + splitInput() { + if (this.input.startsWith('//')) { + const customIndex = this.input.indexOf('\\n'); + const customSeparator = this.input.slice(2,customIndex); + const separatedIndex = this.input.slice(customIndex + 2).split(customSeparator); + return separatedIndex + } + return this.input.split(/,|:/); + } } export default App; From affc0b7a68aaccfb75092746e6839fdeee4819c4 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sat, 18 Oct 2025 18:26:05 +0900 Subject: [PATCH 05/21] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=EC=9D=B4=20=EB=B9=84=EC=96=B4=EC=9E=88=EC=9D=84=20=EC=8B=9C=20?= =?UTF-8?q?0=EC=9D=84=20=EC=B6=9C=EB=A0=A5=EC=8B=9C=ED=82=A4=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EB=B9=88=20=EB=B0=B0=EC=97=B4=20=EB=B0=98?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.js b/src/App.js index fff9943c..037f9f75 100644 --- a/src/App.js +++ b/src/App.js @@ -11,6 +11,7 @@ class App { } splitInput() { + if (!this.input) return []; if (this.input.startsWith('//')) { const customIndex = this.input.indexOf('\\n'); const customSeparator = this.input.slice(2,customIndex); From 2205a1025b90125d70f87e4860d68ab1f5c8c5b4 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sat, 18 Oct 2025 18:34:29 +0900 Subject: [PATCH 06/21] =?UTF-8?q?feat:=20'reduce'=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20=EB=B6=84=EB=A6=AC=EB=90=9C=20?= =?UTF-8?q?=EC=88=AB=EC=9E=90=EB=93=A4=EC=9D=84=20=EB=AA=A8=EB=91=90=20?= =?UTF-8?q?=EB=8D=94=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index 037f9f75..9aa6eaf8 100644 --- a/src/App.js +++ b/src/App.js @@ -15,11 +15,17 @@ class App { if (this.input.startsWith('//')) { const customIndex = this.input.indexOf('\\n'); const customSeparator = this.input.slice(2,customIndex); - const separatedIndex = this.input.slice(customIndex + 2).split(customSeparator); - return separatedIndex + const separatedArray = this.input.slice(customIndex + 2).split(customSeparator); + return separatedArray } return this.input.split(/,|:/); } + + calculateSum() { + const separatedArray = this.splitInput(); + const resultSum = separatedArray.reduce((a, b) => parseInt(a) + parseInt(b), 0); + return resultSum; + } } export default App; From 4535006cd7970d082fcbb9a187f3d036d4d98e89 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sat, 18 Oct 2025 18:35:53 +0900 Subject: [PATCH 07/21] =?UTF-8?q?feat:=20'Console.print()'=EB=A1=9C=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=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/App.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.js b/src/App.js index 9aa6eaf8..b6553dd1 100644 --- a/src/App.js +++ b/src/App.js @@ -4,6 +4,7 @@ import { INPUT_MESSAGE } from "./constants.js"; class App { async run() { await this.getInput(); + Console.print(this.calculateSum()); } async getInput() { From b9f39c3521e3ca856f8ba2aff804e3a50a23af32 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sun, 19 Oct 2025 20:13:14 +0900 Subject: [PATCH 08/21] =?UTF-8?q?docs:=20README.md=EC=97=90=20ERROR=20?= =?UTF-8?q?=EA=B2=80=EC=B6=9C=20=EC=83=81=ED=99=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 82984eeb..5f4a348f 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,7 @@ ## ERROR 검출 - `[ERROR]`로 시작하는 메시지와 함께 Error 발생 후 애플리케이션 종료 +- 구분자가 아닌 다른 문자가 있는 경우 Error 발생 +- 분리된 문자열의 문자가 숫자가 아닌경우 Error 발생 +- 커스텀 구분자 지정 시 `//`나 `\n`이 빠진 경우 Error 발생 +- 커스텀 구분자 지정 시 문자가 아닌 경우 Error 발생 From 39ccac2a0850fe8348d5e245fc5416968efb123a Mon Sep 17 00:00:00 2001 From: hj-66 Date: Sun, 19 Oct 2025 20:21:52 +0900 Subject: [PATCH 09/21] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=83=81=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/constants.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/constants.js b/src/constants.js index f23fe158..c4953e96 100644 --- a/src/constants.js +++ b/src/constants.js @@ -5,3 +5,9 @@ export const INPUT_MESSAGE = { export const OUTPUT_MESSAGE = { OUTPUT_RESULT: '결과 : ', }; + +export const ERROR_MESSAGE = { + CUSTOM_ERROR_MESSAGE: '[ERROR] 커스텀 구분자 지정에 실패했습니다.', + CHARACTER_ERROR_MESSAGE: '[ERROR] 구분자가 아닌 다른 문자가 있습니다.', + NUMBER_ERROR_MESSAGE: '[ERROR] 숫자를 입력해 주세요.' +} \ No newline at end of file From 92f12eb8900d678d2d3ee5a0d3fa622859de029c Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 00:03:12 +0900 Subject: [PATCH 10/21] =?UTF-8?q?feat:=20Error=20=EA=B5=AC=EB=B6=84=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 --- README.md | 2 +- src/constants.js | 1 + src/validation.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/validation.js diff --git a/README.md b/README.md index 5f4a348f..661f02d9 100644 --- a/README.md +++ b/README.md @@ -34,5 +34,5 @@ - `[ERROR]`로 시작하는 메시지와 함께 Error 발생 후 애플리케이션 종료 - 구분자가 아닌 다른 문자가 있는 경우 Error 발생 - 분리된 문자열의 문자가 숫자가 아닌경우 Error 발생 -- 커스텀 구분자 지정 시 `//`나 `\n`이 빠진 경우 Error 발생 +- 커스텀 구분자 지정 시 `\n`이 빠진 경우 Error 발생 - 커스텀 구분자 지정 시 문자가 아닌 경우 Error 발생 diff --git a/src/constants.js b/src/constants.js index c4953e96..e968c331 100644 --- a/src/constants.js +++ b/src/constants.js @@ -8,6 +8,7 @@ export const OUTPUT_MESSAGE = { export const ERROR_MESSAGE = { CUSTOM_ERROR_MESSAGE: '[ERROR] 커스텀 구분자 지정에 실패했습니다.', + CUSTOMSETTING_ERROR_MESSAGE: '[ERROR] 커스텀 구분자 지정 형식이 잘못되었습니다.', CHARACTER_ERROR_MESSAGE: '[ERROR] 구분자가 아닌 다른 문자가 있습니다.', NUMBER_ERROR_MESSAGE: '[ERROR] 숫자를 입력해 주세요.' } \ No newline at end of file diff --git a/src/validation.js b/src/validation.js new file mode 100644 index 00000000..bc4c8578 --- /dev/null +++ b/src/validation.js @@ -0,0 +1,42 @@ +import { ERROR_MESSAGE } from "./constants"; + +export function validateAllowedCharacters(input, customDelimiter = null) { + + const allowed = [',', ':', ... (customDelimiter ? [customDelimiter] : [])]; + + for (const ch of input) { + if (!/[0-9]/.test(ch) && !allowed.includes(ch)) { + throw new Error(ERROR_MESSAGE.CUSTOM_ERROR_MESSAGE); + } + } +} + +export function validateIsNumber(str) { + if (!/^\d+$/.test(str)) { + throw new Error(ERROR_MESSAGE.NUMBER_ERROR_MESSAGE); + } +} + +export function validateCustomDelimiterSyntax(input) { + if (input.startsWith('//')) { + if (!input.includes('\n')) { + throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); + } + + const delimiterPart = input.split('\n')[0]; + + const match = /^\/\/(.)$/.exec(delimiterPart); + if (!match) { + throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); + } + + const delimiter = match[1]; + + if (!isNaN(delimiter)) { + throw new Error(ERROR_MESSAGE.CUSTOM_ERROR_MESSAGE); + } + + return delimiter; + } + return null; +} \ No newline at end of file From 7cf9a471015cae48122f1dbf37c99c9f914cf6f7 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 16:40:03 +0900 Subject: [PATCH 11/21] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=ED=95=A8=EC=88=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 26 +++++++++++++++++++------- src/validation.js | 6 +++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/App.js b/src/App.js index b6553dd1..6d120a76 100644 --- a/src/App.js +++ b/src/App.js @@ -1,10 +1,15 @@ import { Console } from "@woowacourse/mission-utils"; -import { INPUT_MESSAGE } from "./constants.js"; +import { INPUT_MESSAGE, OUTPUT_MESSAGE } from "./constants.js"; +import { validateAllowedCharacters, validateCustomDelimiterSyntax, validateIsNumber } from "./validation.js"; class App { async run() { + try { await this.getInput(); - Console.print(this.calculateSum()); + Console.print(OUTPUT_MESSAGE.OUTPUT_RESULT + this.calculateSum()); + } catch (error) { + throw error; + } } async getInput() { @@ -13,17 +18,24 @@ class App { splitInput() { if (!this.input) return []; - if (this.input.startsWith('//')) { - const customIndex = this.input.indexOf('\\n'); - const customSeparator = this.input.slice(2,customIndex); - const separatedArray = this.input.slice(customIndex + 2).split(customSeparator); - return separatedArray + const customDelimiter = validateCustomDelimiterSyntax(this.input); + + + if (customDelimiter) { + const numbersPart = this.input.split('\\n')[1]; + validateAllowedCharacters(numbersPart, customDelimiter); + return numbersPart.split(customDelimiter); } + + validateAllowedCharacters(this.input); return this.input.split(/,|:/); } calculateSum() { const separatedArray = this.splitInput(); + separatedArray.forEach((value) => { + validateIsNumber(value); + }); const resultSum = separatedArray.reduce((a, b) => parseInt(a) + parseInt(b), 0); return resultSum; } diff --git a/src/validation.js b/src/validation.js index bc4c8578..f673756d 100644 --- a/src/validation.js +++ b/src/validation.js @@ -1,4 +1,4 @@ -import { ERROR_MESSAGE } from "./constants"; +import { ERROR_MESSAGE } from "./constants.js"; export function validateAllowedCharacters(input, customDelimiter = null) { @@ -19,11 +19,11 @@ export function validateIsNumber(str) { export function validateCustomDelimiterSyntax(input) { if (input.startsWith('//')) { - if (!input.includes('\n')) { + if (!input.includes('\\n')) { throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); } - const delimiterPart = input.split('\n')[0]; + const delimiterPart = input.split('\\n')[0]; const match = /^\/\/(.)$/.exec(delimiterPart); if (!match) { From 7f54ea13831b717e922ccbf8c2a7631e652173b3 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 19:25:15 +0900 Subject: [PATCH 12/21] =?UTF-8?q?feat:=20=EA=B5=AC=EB=B6=84=EC=9E=90?= =?UTF-8?q?=EB=A5=BC=20=EC=97=AC=EB=9F=AC=EA=B8=80=EC=9E=90=EB=A1=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=ED=95=A8=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/validation.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/validation.js b/src/validation.js index f673756d..0759b2cd 100644 --- a/src/validation.js +++ b/src/validation.js @@ -1,13 +1,16 @@ import { ERROR_MESSAGE } from "./constants.js"; -export function validateAllowedCharacters(input, customDelimiter = null) { - - const allowed = [',', ':', ... (customDelimiter ? [customDelimiter] : [])]; +export function validateAllowedCharacters(numbersPart, customDelimiter) { + let pattern; + if (customDelimiter) { + const escapeRegex = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + pattern = new RegExp(`[^0-9${escapeRegex(customDelimiter)}]`); + } else { + pattern = /[^0-9,:]/; + } - for (const ch of input) { - if (!/[0-9]/.test(ch) && !allowed.includes(ch)) { - throw new Error(ERROR_MESSAGE.CUSTOM_ERROR_MESSAGE); - } + if (pattern.test(numbersPart)) { + throw new Error(ERROR_MESSAGE.CHARACTER_ERROR_MESSAGE); } } @@ -24,19 +27,20 @@ export function validateCustomDelimiterSyntax(input) { } const delimiterPart = input.split('\\n')[0]; + const match = /^\/\/(.+)$/.exec(delimiterPart); - const match = /^\/\/(.)$/.exec(delimiterPart); if (!match) { throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); } const delimiter = match[1]; - if (!isNaN(delimiter)) { + if (/^\d+$/.test(delimiter)) { throw new Error(ERROR_MESSAGE.CUSTOM_ERROR_MESSAGE); } return delimiter; } + return null; } \ No newline at end of file From 3bfebe87f5cc4afb8867d1e07d2e272762baf00c Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 20:40:11 +0900 Subject: [PATCH 13/21] =?UTF-8?q?refactor:=20import=20=EB=B6=80=EB=B6=84?= =?UTF-8?q?=20=EB=94=B0=EC=98=B4=ED=91=9C,=20=EC=A4=84=EB=B0=94=EA=BF=88?= =?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/App.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index 6d120a76..1cc309b5 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,10 @@ -import { Console } from "@woowacourse/mission-utils"; -import { INPUT_MESSAGE, OUTPUT_MESSAGE } from "./constants.js"; -import { validateAllowedCharacters, validateCustomDelimiterSyntax, validateIsNumber } from "./validation.js"; +import { Console } from '@woowacourse/mission-utils'; +import { INPUT_MESSAGE, OUTPUT_MESSAGE } from './constants.js'; +import { + validateAllowedCharacters, + validateCustomDelimiterSyntax, + validateIsNumber, +} from './validation.js'; class App { async run() { From 9b8644244f6006d6f0a293137241685269654dae Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 20:43:59 +0900 Subject: [PATCH 14/21] =?UTF-8?q?refactor:=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EB=B6=80=EB=B6=84=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EB=A6=AC=ED=84=B0=EB=9F=B4=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/App.js b/src/App.js index 1cc309b5..7753d440 100644 --- a/src/App.js +++ b/src/App.js @@ -8,13 +8,13 @@ import { class App { async run() { - try { + try { await this.getInput(); - Console.print(OUTPUT_MESSAGE.OUTPUT_RESULT + this.calculateSum()); - } catch (error) { - throw error; - } + Console.print(`${OUTPUT_MESSAGE.OUTPUT_RESULT}${this.calculateSum()}`); + } catch (error) { + throw error; } +} async getInput() { this.input = await Console.readLineAsync(INPUT_MESSAGE.INPUT_STRING); From 9a41e45071f73bb085403b73bf5866e37ae2e5e5 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 20:47:11 +0900 Subject: [PATCH 15/21] =?UTF-8?q?refactor:=20calculateSum()=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EA=B0=84=EA=B2=B0=ED=99=94=EC=99=80=20=EC=A4=91?= =?UTF-8?q?=EA=B0=84=EB=B3=80=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/App.js b/src/App.js index 7753d440..8f711154 100644 --- a/src/App.js +++ b/src/App.js @@ -37,11 +37,8 @@ class App { calculateSum() { const separatedArray = this.splitInput(); - separatedArray.forEach((value) => { - validateIsNumber(value); - }); - const resultSum = separatedArray.reduce((a, b) => parseInt(a) + parseInt(b), 0); - return resultSum; + separatedArray.forEach((value) => validateIsNumber(value)); + return separatedArray.reduce((sum, value) => sum + parseInt(value, 10), 0); } } From 23c2f886a5b02647a80049ee03639d7e300c2e71 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 21:06:25 +0900 Subject: [PATCH 16/21] =?UTF-8?q?refactor:=20=ED=95=A8=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B3=B5=ED=86=B5=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=EC=9D=84=20=EC=83=88=20=ED=95=A8=EC=88=98=EB=A1=9C=20?= =?UTF-8?q?=EB=AC=B6=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/App.js b/src/App.js index 8f711154..0d356f28 100644 --- a/src/App.js +++ b/src/App.js @@ -8,37 +8,44 @@ import { class App { async run() { - try { - await this.getInput(); - Console.print(`${OUTPUT_MESSAGE.OUTPUT_RESULT}${this.calculateSum()}`); - } catch (error) { - throw error; + try { + await this.getInput(); + const numbers = this.parseInputToNumbers(); + const sum = this.calculateSum(numbers); + Console.print(`${OUTPUT_MESSAGE.OUTPUT_RESULT}${sum}`); + } catch (error) { + throw error; + } } -} async getInput() { this.input = await Console.readLineAsync(INPUT_MESSAGE.INPUT_STRING); } - splitInput() { + parseInputToNumbers() { if (!this.input) return []; - const customDelimiter = validateCustomDelimiterSyntax(this.input); + const customDelimiter = validateCustomDelimiterSyntax(this.input); if (customDelimiter) { const numbersPart = this.input.split('\\n')[1]; validateAllowedCharacters(numbersPart, customDelimiter); - return numbersPart.split(customDelimiter); + return this.convertToNumberArray(numbersPart.split(customDelimiter)); } validateAllowedCharacters(this.input); - return this.input.split(/,|:/); + return this.convertToNumberArray(this.input.split(/,|:/)); + } + + convertToNumberArray(values) { + return values.map((value) => { + validateIsNumber(value); + return parseInt(value, 10); + }); } - calculateSum() { - const separatedArray = this.splitInput(); - separatedArray.forEach((value) => validateIsNumber(value)); - return separatedArray.reduce((sum, value) => sum + parseInt(value, 10), 0); + calculateSum(numbers) { + return numbers.reduce((sum, value) => sum + value, 0); } } From 7410a6d560c481dac6abc69baa303ca5e334d160 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 21:15:25 +0900 Subject: [PATCH 17/21] =?UTF-8?q?refactor:=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=84=A0=EC=96=B8=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/validation.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/validation.js b/src/validation.js index 0759b2cd..d61d3c99 100644 --- a/src/validation.js +++ b/src/validation.js @@ -1,6 +1,6 @@ import { ERROR_MESSAGE } from "./constants.js"; -export function validateAllowedCharacters(numbersPart, customDelimiter) { +export const validateAllowedCharacters = (numbersPart, customDelimiter) => { let pattern; if (customDelimiter) { const escapeRegex = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); @@ -14,13 +14,13 @@ export function validateAllowedCharacters(numbersPart, customDelimiter) { } } -export function validateIsNumber(str) { +export const validateIsNumber = (str) => { if (!/^\d+$/.test(str)) { throw new Error(ERROR_MESSAGE.NUMBER_ERROR_MESSAGE); } } -export function validateCustomDelimiterSyntax(input) { +export const validateCustomDelimiterSyntax = (input) => { if (input.startsWith('//')) { if (!input.includes('\\n')) { throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); From 4493446b0be04040a1265bcbc68bf4ad61b48602 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 21:17:55 +0900 Subject: [PATCH 18/21] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=B3=80=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/validation.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/validation.js b/src/validation.js index d61d3c99..c3d0a9c4 100644 --- a/src/validation.js +++ b/src/validation.js @@ -1,13 +1,10 @@ import { ERROR_MESSAGE } from "./constants.js"; export const validateAllowedCharacters = (numbersPart, customDelimiter) => { - let pattern; - if (customDelimiter) { - const escapeRegex = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - pattern = new RegExp(`[^0-9${escapeRegex(customDelimiter)}]`); - } else { - pattern = /[^0-9,:]/; - } + const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const pattern = customDelimiter + ? new RegExp(`[^0-9${escapeRegex(customDelimiter)}]`) + : /[^0-9,:]/; if (pattern.test(numbersPart)) { throw new Error(ERROR_MESSAGE.CHARACTER_ERROR_MESSAGE); From 8efb93d8248f9df5cdcec4a67cc28886b900c5e3 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 21:21:43 +0900 Subject: [PATCH 19/21] =?UTF-8?q?refactor:=20validateIsNumber=20if?= =?UTF-8?q?=EB=AC=B8=20=EB=B3=80=EC=88=98=EB=A1=9C=20=ED=95=A0=EB=8B=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/validation.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/validation.js b/src/validation.js index c3d0a9c4..c26c2a38 100644 --- a/src/validation.js +++ b/src/validation.js @@ -12,7 +12,8 @@ export const validateAllowedCharacters = (numbersPart, customDelimiter) => { } export const validateIsNumber = (str) => { - if (!/^\d+$/.test(str)) { + const isValidNumber = /^\d+$/.test(str); + if (!isValidNumber) { throw new Error(ERROR_MESSAGE.NUMBER_ERROR_MESSAGE); } } From c55ed7f5c6549860be0a49e718b638298ccd9005 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 21:25:40 +0900 Subject: [PATCH 20/21] =?UTF-8?q?refactor:=20validateCustomDelimiterSyntax?= =?UTF-8?q?=20if=EB=AC=B8=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/validation.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/validation.js b/src/validation.js index c26c2a38..e0b6304d 100644 --- a/src/validation.js +++ b/src/validation.js @@ -19,26 +19,26 @@ export const validateIsNumber = (str) => { } export const validateCustomDelimiterSyntax = (input) => { - if (input.startsWith('//')) { - if (!input.includes('\\n')) { - throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); - } + const hasCustomDelimiter = input.startsWith("//"); + if (!hasCustomDelimiter) return null; - const delimiterPart = input.split('\\n')[0]; - const match = /^\/\/(.+)$/.exec(delimiterPart); + const hasNewLine = input.includes("\\n"); + if (!hasNewLine) { + throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); + } - if (!match) { - throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); - } + const [delimiterPart] = input.split("\\n"); + const match = /^\/\/(.+)$/.exec(delimiterPart); - const delimiter = match[1]; + if (!match) { + throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); + } - if (/^\d+$/.test(delimiter)) { - throw new Error(ERROR_MESSAGE.CUSTOM_ERROR_MESSAGE); - } + const delimiter = match[1]; - return delimiter; + if (/^\d+$/.test(delimiter)) { + throw new Error(ERROR_MESSAGE.CUSTOM_ERROR_MESSAGE); } - return null; + return delimiter; } \ No newline at end of file From b08492ea43385eba57fbe221fd34b488bad68c33 Mon Sep 17 00:00:00 2001 From: hj-66 Date: Mon, 20 Oct 2025 21:45:05 +0900 Subject: [PATCH 21/21] =?UTF-8?q?refactor:=20constants.js=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EB=B3=80=EC=88=98=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=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/App.js | 6 +++--- src/constants.js | 26 ++++++++++++++------------ src/validation.js | 12 ++++++------ 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/App.js b/src/App.js index 0d356f28..ecb5744d 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,5 @@ import { Console } from '@woowacourse/mission-utils'; -import { INPUT_MESSAGE, OUTPUT_MESSAGE } from './constants.js'; +import { MESSAGES } from './constants.js'; import { validateAllowedCharacters, validateCustomDelimiterSyntax, @@ -12,14 +12,14 @@ class App { await this.getInput(); const numbers = this.parseInputToNumbers(); const sum = this.calculateSum(numbers); - Console.print(`${OUTPUT_MESSAGE.OUTPUT_RESULT}${sum}`); + Console.print(`${MESSAGES.OUTPUT.OUTPUT_RESULT}${sum}`); } catch (error) { throw error; } } async getInput() { - this.input = await Console.readLineAsync(INPUT_MESSAGE.INPUT_STRING); + this.input = await Console.readLineAsync(MESSAGES.INPUT.INPUT_STRING); } parseInputToNumbers() { diff --git a/src/constants.js b/src/constants.js index e968c331..2278b6b1 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,14 +1,16 @@ -export const INPUT_MESSAGE = { - INPUT_STRING: '덧셈할 문자열을 입력해 주세요.\n', -}; +export const MESSAGES = { + INPUT: { + INPUT_STRING: `덧셈할 문자열을 입력해 주세요.\n`, + }, -export const OUTPUT_MESSAGE = { - OUTPUT_RESULT: '결과 : ', -}; + OUTPUT: { + OUTPUT_RESULT: `결과 : `, + }, -export const ERROR_MESSAGE = { - CUSTOM_ERROR_MESSAGE: '[ERROR] 커스텀 구분자 지정에 실패했습니다.', - CUSTOMSETTING_ERROR_MESSAGE: '[ERROR] 커스텀 구분자 지정 형식이 잘못되었습니다.', - CHARACTER_ERROR_MESSAGE: '[ERROR] 구분자가 아닌 다른 문자가 있습니다.', - NUMBER_ERROR_MESSAGE: '[ERROR] 숫자를 입력해 주세요.' -} \ No newline at end of file + ERROR: { + CUSTOM_ERROR_MESSAGE: `[ERROR] 커스텀 구분자 지정에 실패했습니다.`, + CUSTOM_SETTING_ERROR_MESSAGE: `[ERROR] 커스텀 구분자 지정 형식이 잘못되었습니다.`, + CHARACTER_ERROR_MESSAGE: `[ERROR] 구분자가 아닌 다른 문자가 있습니다.`, + NUMBER_ERROR_MESSAGE: `[ERROR] 숫자를 입력해 주세요.`, + }, +}; \ No newline at end of file diff --git a/src/validation.js b/src/validation.js index e0b6304d..bd00d026 100644 --- a/src/validation.js +++ b/src/validation.js @@ -1,4 +1,4 @@ -import { ERROR_MESSAGE } from "./constants.js"; +import { MESSAGES } from './constants.js'; export const validateAllowedCharacters = (numbersPart, customDelimiter) => { const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); @@ -7,14 +7,14 @@ export const validateAllowedCharacters = (numbersPart, customDelimiter) => { : /[^0-9,:]/; if (pattern.test(numbersPart)) { - throw new Error(ERROR_MESSAGE.CHARACTER_ERROR_MESSAGE); + throw new Error(MESSAGES.ERROR.CHARACTER_ERROR_MESSAGE); } } export const validateIsNumber = (str) => { const isValidNumber = /^\d+$/.test(str); if (!isValidNumber) { - throw new Error(ERROR_MESSAGE.NUMBER_ERROR_MESSAGE); + throw new Error(MESSAGES.ERROR.NUMBER_ERROR_MESSAGE); } } @@ -24,20 +24,20 @@ export const validateCustomDelimiterSyntax = (input) => { const hasNewLine = input.includes("\\n"); if (!hasNewLine) { - throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); + throw new Error(MESSAGES.ERROR.CUSTOM_SETTING_ERROR_MESSAGE); } const [delimiterPart] = input.split("\\n"); const match = /^\/\/(.+)$/.exec(delimiterPart); if (!match) { - throw new Error(ERROR_MESSAGE.CUSTOMSETTING_ERROR_MESSAGE); + throw new Error(MESSAGES.ERROR.CUSTOM_SETTING_ERROR_MESSAGE); } const delimiter = match[1]; if (/^\d+$/.test(delimiter)) { - throw new Error(ERROR_MESSAGE.CUSTOM_ERROR_MESSAGE); + throw new Error(MESSAGES.ERROR.CUSTOM_ERROR_MESSAGE); } return delimiter;