diff --git a/README.md b/README.md index 13420b29..661f02d9 100644 --- a/README.md +++ b/README.md @@ -1 +1,38 @@ -# javascript-calculator-precourse \ No newline at end of file +# javascript-calculator-precourse + +# 구현할 기능 목록 + +## 문자 입력받기 + +- `Console.readLineAsync("문자열을 입력해 주세요")`로 문자열 받기 +- 문자열이 비어있을 시 0 반환 + +
+ +## 숫자 추출을 위한 문자열 분리 + +- `,`와 `:`을 이용하여 문자열을 분리 +- `//`와 `\n` 사이의 문자를 커스텀 구분자로 지정 +- 커스텀 구분자를 이용하여 문자열을 분리 + +
+ +## 덧셈 하기 + +- `reduce`를 이용하여 분리된 숫자들을 모두 더함 + +
+ +## 결과 반환 + +- `Console.print()`로 결과 출력 + +
+ +## ERROR 검출 + +- `[ERROR]`로 시작하는 메시지와 함께 Error 발생 후 애플리케이션 종료 +- 구분자가 아닌 다른 문자가 있는 경우 Error 발생 +- 분리된 문자열의 문자가 숫자가 아닌경우 Error 발생 +- 커스텀 구분자 지정 시 `\n`이 빠진 경우 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", diff --git a/src/App.js b/src/App.js index 091aa0a5..ecb5744d 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,52 @@ +import { Console } from '@woowacourse/mission-utils'; +import { MESSAGES } from './constants.js'; +import { + validateAllowedCharacters, + validateCustomDelimiterSyntax, + validateIsNumber, +} from './validation.js'; + class App { - async run() {} + async run() { + try { + await this.getInput(); + const numbers = this.parseInputToNumbers(); + const sum = this.calculateSum(numbers); + Console.print(`${MESSAGES.OUTPUT.OUTPUT_RESULT}${sum}`); + } catch (error) { + throw error; + } + } + + async getInput() { + this.input = await Console.readLineAsync(MESSAGES.INPUT.INPUT_STRING); + } + + parseInputToNumbers() { + if (!this.input) return []; + + const customDelimiter = validateCustomDelimiterSyntax(this.input); + + if (customDelimiter) { + const numbersPart = this.input.split('\\n')[1]; + validateAllowedCharacters(numbersPart, customDelimiter); + return this.convertToNumberArray(numbersPart.split(customDelimiter)); + } + + validateAllowedCharacters(this.input); + return this.convertToNumberArray(this.input.split(/,|:/)); + } + + convertToNumberArray(values) { + return values.map((value) => { + validateIsNumber(value); + return parseInt(value, 10); + }); + } + + calculateSum(numbers) { + return numbers.reduce((sum, value) => sum + value, 0); + } } export default App; diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 00000000..2278b6b1 --- /dev/null +++ b/src/constants.js @@ -0,0 +1,16 @@ +export const MESSAGES = { + INPUT: { + INPUT_STRING: `덧셈할 문자열을 입력해 주세요.\n`, + }, + + OUTPUT: { + OUTPUT_RESULT: `결과 : `, + }, + + 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 new file mode 100644 index 00000000..bd00d026 --- /dev/null +++ b/src/validation.js @@ -0,0 +1,44 @@ +import { MESSAGES } from './constants.js'; + +export const validateAllowedCharacters = (numbersPart, customDelimiter) => { + const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const pattern = customDelimiter + ? new RegExp(`[^0-9${escapeRegex(customDelimiter)}]`) + : /[^0-9,:]/; + + if (pattern.test(numbersPart)) { + throw new Error(MESSAGES.ERROR.CHARACTER_ERROR_MESSAGE); + } +} + +export const validateIsNumber = (str) => { + const isValidNumber = /^\d+$/.test(str); + if (!isValidNumber) { + throw new Error(MESSAGES.ERROR.NUMBER_ERROR_MESSAGE); + } +} + +export const validateCustomDelimiterSyntax = (input) => { + const hasCustomDelimiter = input.startsWith("//"); + if (!hasCustomDelimiter) return null; + + const hasNewLine = input.includes("\\n"); + if (!hasNewLine) { + throw new Error(MESSAGES.ERROR.CUSTOM_SETTING_ERROR_MESSAGE); + } + + const [delimiterPart] = input.split("\\n"); + const match = /^\/\/(.+)$/.exec(delimiterPart); + + if (!match) { + throw new Error(MESSAGES.ERROR.CUSTOM_SETTING_ERROR_MESSAGE); + } + + const delimiter = match[1]; + + if (/^\d+$/.test(delimiter)) { + throw new Error(MESSAGES.ERROR.CUSTOM_ERROR_MESSAGE); + } + + return delimiter; +} \ No newline at end of file