diff --git a/README.md b/README.md index 13420b29..2a82f8ae 100644 --- a/README.md +++ b/README.md @@ -1 +1,27 @@ -# javascript-calculator-precourse \ No newline at end of file +# javascript-calculator-precourse + +## 기능 목록 + +### 입력받기 + +- [x] 입력받은 문자열에서 구분자를 기준으로 숫자를 추출한다. + +### 구분자 정하기 + +- [x] 기본 구분자: 쉼표(`,`), 콜론(`:`) +- [x] 커스텀 구분자: `//`와 `\\n` 사이에 위치하는 문자 + +### 검증하기 + +- [x] 올바른 커스텀 구분자인지 검증한다(문자 길이 1 확인) +- [x] 올바른 숫자 형식인지 검증한다(숫자만 허용) +- [x] 양수인지 검사한다(음수 불허용) + +### 계산하기 + +- [x] 추출한 숫자들을 모두 더한다. + +### 출력하기 + +- [x] 계산 결과를 `결과 : {숫자}` 형식으로 출력한다. +- [x] 에러 발생 시 `[ERROR] {에러메시지}` 형식으로 출력한다. diff --git a/src/App.js b/src/App.js index 091aa0a5..5d6f3e28 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,31 @@ +import { InputManager } from "./InputManager.js"; +import { Separator } from "./Separator.js"; +import { Validator } from "./Validator.js"; +import { OutputManager } from "./OutputManager.js"; +import { MissionUtils } from "@woowacourse/mission-utils"; + class App { - async run() {} + async run() { + try { + const rawInput = await InputManager.getInput(); + const { separator, numStrs } = Separator.parse(rawInput); + Validator.validateSeparators(separator); + const escapedSeparators = separator.map((sep) => + sep.replace(/[\\^$.|?*+()[\]{}-]/g, "\\$&") + ); // 정규표현식 내에서 특수문자는 이스케이프되어야 함. + const regex = new RegExp(`[${escapedSeparators.join("")}]`); + const numbers = numStrs.split(regex).filter((n) => n.trim() !== ""); + Validator.validateNumbers(numbers); + Validator.validatePositiveNumbers(numbers); + const addResult = numbers + .map((s) => +s.trim()) + .reduce((r, n) => r + n, 0); + OutputManager.print(addResult); + } catch (err) { + OutputManager.errorPrint(err.message); + throw err; + } + } } export default App; diff --git a/src/InputManager.js b/src/InputManager.js new file mode 100644 index 00000000..3c4ddea4 --- /dev/null +++ b/src/InputManager.js @@ -0,0 +1,9 @@ +import { MissionUtils } from "@woowacourse/mission-utils"; + +export class InputManager { + static async getInput() { + return await MissionUtils.Console.readLineAsync( + "덧셈할 문자열을 입력해 주세요.\n" + ); + } +} diff --git a/src/OutputManager.js b/src/OutputManager.js new file mode 100644 index 00000000..5c4c28df --- /dev/null +++ b/src/OutputManager.js @@ -0,0 +1,11 @@ +import { MissionUtils } from "@woowacourse/mission-utils"; + +export class OutputManager { + static print(result) { + MissionUtils.Console.print(`결과 : ${result}`); + } + + static errorPrint(errMsg) { + MissionUtils.Console.print(`${errMsg}`); + } +} diff --git a/src/Separator.js b/src/Separator.js new file mode 100644 index 00000000..9e0f2413 --- /dev/null +++ b/src/Separator.js @@ -0,0 +1,14 @@ +export class Separator { + static parse(rawInput) { + const separator = [",", ":"]; // 기본 구분자 + const customSeparators = [...rawInput.matchAll(/\/\/(.+?)\\n/g)].map( + (m) => m[1] + ); // 커스텀 구분자 + if (customSeparators.length) { + separator.push(...customSeparators); + } + // 숫자 부분 추출 + const numStrs = rawInput.replace(/\/\/(.+?)\\n/, ""); + return { separator, numStrs }; + } +} diff --git a/src/Validator.js b/src/Validator.js new file mode 100644 index 00000000..f6957e5c --- /dev/null +++ b/src/Validator.js @@ -0,0 +1,34 @@ +export class Validator { + // 유효한 구분자인지 검사 (문자인지 확인) + static validateSeparators(separator) { + if (!Array.isArray(separator) || separator.length === 0) { + throw new Error("[ERROR] 구분자는 하나 이상이여야 합니다."); + } + + separator.forEach((sep) => { + if (typeof sep !== "string" || sep.length !== 1) { + throw new Error("[ERROR] 각 구분자는 하나의 문자여야 합니다."); + } + }); + } + + // 유효한 숫자인지 검사 (숫자 형식 검증) + static validateNumbers(numStrs) { + numStrs.forEach((numStr) => { + const trimmed = numStr.trim(); + if (trimmed === "" || isNaN(Number(trimmed))) { + throw new Error("[ERROR] 올바른 숫자 형식이 아닙니다."); + } + }); + } + + // 양수인지 검사 + static validatePositiveNumbers(numStrs) { + numStrs.forEach((numStr) => { + const num = Number(numStr.trim()); + if (num < 0) { + throw new Error("[ERROR] 음수는 허용되지 않습니다."); + } + }); + } +}