diff --git a/README.md b/README.md index 13420b29..8fbb9e01 100644 --- a/README.md +++ b/README.md @@ -1 +1,103 @@ -# javascript-calculator-precourse \ No newline at end of file +# javascript-calculator-precourse + +## ✅ 기능 구현 목록 + +- [x] 문자열 입력 받기 +- [x] 구분자를 기준으로 구분하기 + - [x] `,`, `:` 기준으로 구분하기 + - [x] 커스텀 구분자 기준으로 구분하기 +- [x] 구분한 숫자가 양수인지 확인 + - [x] 잘못된 값일 시 에러 처리 후 종료 +- [x] 구분한 숫자 더하기 +- [x] 결과 출력하기 + +## 💻 구현 내용 + +### 커스텀 구분자 처리 + +``` +getCustomSeperator(input){ + if(!input.startsWith("//")) return null; + const match = input.match(/^\/\/(.*?)\\n/); + return match ? match[1] : null; +} +``` + +- 정규표현식을 사용해 `//`와 `\n` 사이에 있는 문자를 추출. +- match 메서드로 추출한 값이 있는 경우, 두 번째 값이 커스텀 구분자이므로 index 1번 값을 반환. +- match 메서드에 매치되는 결과가 없는 경우에는 null 반환. +- `\n`이 줄바꿈을 의미하는 이스케이프 문자로 인식되어 제대로 인식되지 않는 문제가 있었는데 앞에 `\`을 붙여 `\\n`으로 사용하니 `\n`을 그냥 문자로 사용할 수 있었음. +- `/`도 마찬가지로 인식되지 않는 문제가 있었는데 앞에 `\`을 붙여 사용하니 인식됨. + +### 문자열 구분자로 구분 + +``` +splitNumbers(numbersPart, customSeperator = null) { + let delimiters = ",:"; + + if(customSeperator) { + delimiters += customSeperator; + } + + const regex = new RegExp(`[${delimiters}]`); + return numbersPart.split(regex); +} +``` + +- `,`,`:`, 커스텀 구분자를 사용해 문자열을 분리. +- 배열로 저장된 분리한 문자열을 반환. + +### 입력값 잘못된 경우 에러 처리 후 종료 + +``` +validateNumbers(numbers) { + numbers.forEach((value) => { + const num = Number(value); + if(isNaN(num) || num < 0) { + throw new Error("[ERROR]"); + } + }); +} +``` + +- 입력값이 구분자와 양수 외에 다른 문자로 이루어져 있는지 확인하는 메서드. +- 구분자로 구분한 값이 저장되어 있는 배열 numbers를 forEach로 순환하면서 확인. +- Number 메서드를 사용했을 때 숫자로 변경되지 않는 문자면 NaN이 저장되므로 isNaN 메서드를 사용해서 숫자인지 아닌지 확인. +- 변경된 숫자는 음수인지 아닌지 확인 +- 두 조건 중 하나라도 만족하지 않으면 `throw new Error()`를 사용해 메시지와 함께 에러를 발생시킨 후 애플리케이션을 종료. + +### 추출한 숫자 더하기 + +``` +sum(numbers) { + return numbers.reduce((sum, value) => sum + Number(value), 0); +} +``` + +- reduce 메서드를 사용해 배열을 돌면서 배열의 값들을 모두 더해 반환. +- 배열에 있는 값들이 문자열이기 때문에 Number를 사용해 숫자로 변환. + +## ⭐️ 실행 결과 + +### 옳은 입력 + +- `,`를 구분자로 사용한 경우 + Image + +- `:`를 구분자로 사용한 경우 + Image + +- 커스텀 구분자를 입력한 경우 + Image + +### 잘못된 입력 시 + +- 양수가 아닌 문자를 입력한 경우 + Image + +- 양수가 아닌 음수를 입력한 경우 + Image + +### 테스트 결과 + +Image diff --git a/src/App.js b/src/App.js index 091aa0a5..204c73a1 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,10 @@ +import CalculatorController from "./controller/CalculatorController.js"; + class App { - async run() {} + async run() { + const controller = new CalculatorController(); + await controller.run(); + } } export default App; diff --git a/src/controller/CalculatorController.js b/src/controller/CalculatorController.js new file mode 100644 index 00000000..096738c2 --- /dev/null +++ b/src/controller/CalculatorController.js @@ -0,0 +1,24 @@ +import ConsoleView from "../view/ConsoleView.js"; +import Calculator from "../model/Calculator.js"; + +class CalculatorController { + constructor() { + this.view = new ConsoleView(); + this.calculator = new Calculator(); + } + + async run() { + try{ + const input = await this.view.getInput(); + const numbers = this.calculator.parseInput(input); + this.calculator.validateNumbers(numbers); + const result = this.calculator.sum(numbers); + this.view.printResult(result); + } catch(error) { + this.view.printError(error); + throw error; + } + } +} + +export default CalculatorController; \ No newline at end of file diff --git a/src/model/Calculator.js b/src/model/Calculator.js new file mode 100644 index 00000000..c9d2060f --- /dev/null +++ b/src/model/Calculator.js @@ -0,0 +1,44 @@ +class Calculator { + parseInput(input){ + let numbersPart = input; + const customSeperator = this.getCustomSeperator(input); + + if(customSeperator) { + numbersPart = input.split(`\\n`)[1]; + } + + return this.splitNumbers(numbersPart, customSeperator); + } + + getCustomSeperator(input){ + if(!input.startsWith("//")) return null; + const match = input.match(/^\/\/(.*?)\\n/); + return match ? match[1] : null; + } + + splitNumbers(numbersPart, customSeperator = null) { + let delimiters = ",:"; + + if(customSeperator) { + delimiters += customSeperator; + } + + const regex = new RegExp(`[${delimiters}]`); + return numbersPart.split(regex); + } + + validateNumbers(numbers) { + numbers.forEach((value) => { + const num = Number(value); + if(isNaN(num) || num < 0) { + throw new Error("[ERROR]"); + } + }); + } + + sum(numbers) { + return numbers.reduce((sum, value) => sum + Number(value), 0); + } +} + +export default Calculator; \ No newline at end of file diff --git a/src/view/ConsoleView.js b/src/view/ConsoleView.js new file mode 100644 index 00000000..f88028c7 --- /dev/null +++ b/src/view/ConsoleView.js @@ -0,0 +1,23 @@ +import { Console } from "@woowacourse/mission-utils"; + +class ConsoleView { + async getInput() { + try { + const input = await Console.readLineAsync("덧셈할 문자열을 입력해 주세요\n"); + return input; + } catch { + throw new Error("[ERROR]"); + } + + } + + printResult(result) { + Console.print(`결과 : ${result}`); + } + + printError(error){ + Console.print("[ERROR]" + error.message); + } +} + +export default ConsoleView; \ No newline at end of file