diff --git a/README.md b/README.md index 13420b29f..816dc09a9 100644 --- a/README.md +++ b/README.md @@ -1 +1,35 @@ -# javascript-calculator-precourse \ No newline at end of file + +## **문자열 덧셈 계산기** + +### **📘기능 요구 사항** + +입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다. + +- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다. + ◦ 예: "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6 +- 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다. + - 예를 들어 "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다. +- 사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지와 함께 `Error`를 발생시킨 후 애플리케이션은 종료되어야 한다. + +### **입출력 요구 사항** + +**입력** + +- 구분자와 양수로 구성된 문자열 + +**출력** + +- 덧셈 결과 + + ``` + 결과 : 6 + ``` + +## 💡생각해보기 +1. 문자열을 입력받는다. +2. ,과 :을 기본 구분자로 넣는다. +3. 받은 문자열을 검사한다. + 3-1. 커스텀 문자열이라면 //와\n사이에 위치한 문자를 커스텀 구분자로 사용한다. + 3-2. 아니라면 기존 구분자를 사용해서 문자열을 계산한다. + 3-3. 잘못된 문자열이 입력되면 "[ERROR]"로 시작하는 메시지와 함께 `Error`를 발생 시키고 종료한다. +4. 문자열 수들의 합을 구해서 "결과: 결과 값" 형식으로 출력한다. \ No newline at end of file diff --git a/__tests__/ApplicationTest.js b/__tests__/ApplicationTest.js index 7c6962dd1..24cd04003 100644 --- a/__tests__/ApplicationTest.js +++ b/__tests__/ApplicationTest.js @@ -1,6 +1,7 @@ import App from "../src/App.js"; import { MissionUtils } from "@woowacourse/mission-utils"; + const mockQuestions = (inputs) => { MissionUtils.Console.readLineAsync = jest.fn(); diff --git a/package-lock.json b/package-lock.json index 2edacf7e3..6c5e8e06a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,14 @@ "name": "javascript-calculator", "version": "0.0.0", "dependencies": { - "@woowacourse/mission-utils": "^2.2.0" + "prompt": "^1.3.0" }, "devDependencies": { "@babel/core": "^7.25.8", "@babel/preset-env": "^7.25.8", + "@woowacourse/mission-utils": "^2.2.0", "babel-jest": "^29.6.0", - "jest": "^29.6.0" + "jest": "^29.7.0" }, "engines": { "node": ">=20.17.0", @@ -1790,6 +1791,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2719,6 +2729,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@woowacourse/mission-utils/-/mission-utils-2.2.0.tgz", "integrity": "sha512-yuG68cd42EsvI43c/7T6eQOKgobRkMGqvpN9gp4P134L1LQ/BksA+4hfDt8Qc71TzQEf3cb17y2L7BWwXudu8Q==", + "dev": true, "engines": { "node": ">=20.17.0", "npm": ">=10.8.2" @@ -2787,6 +2798,12 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/async": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "license": "MIT" + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -3224,6 +3241,15 @@ "dev": true, "license": "MIT" }, + "node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3365,6 +3391,14 @@ "node": ">= 8" } }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -3559,6 +3593,14 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3879,6 +3921,12 @@ "dev": true, "license": "ISC" }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -5737,6 +5785,12 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -5844,6 +5898,12 @@ "dev": true, "license": "MIT" }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6096,6 +6156,22 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prompt": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz", + "integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.5.0", + "async": "3.2.3", + "read": "1.0.x", + "revalidator": "0.1.x", + "winston": "2.x" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6134,6 +6210,18 @@ "dev": true, "license": "MIT" }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -6270,6 +6358,15 @@ "node": ">=10" } }, + "node_modules/revalidator": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "integrity": "sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==", + "license": "Apache 2.0", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -6355,6 +6452,15 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -6670,6 +6776,32 @@ "node": ">= 8" } }, + "node_modules/winston": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.7.tgz", + "integrity": "sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg==", + "license": "MIT", + "dependencies": { + "async": "^2.6.4", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/winston/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 122233751..2bef5b587 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,9 @@ "devDependencies": { "@babel/core": "^7.25.8", "@babel/preset-env": "^7.25.8", + "@woowacourse/mission-utils": "^2.2.0", "babel-jest": "^29.6.0", - "jest": "^29.6.0" - }, - "dependencies": { - "@woowacourse/mission-utils": "^2.2.0" + "jest": "^29.7.0" }, "jest": { "transform": { @@ -29,5 +27,8 @@ "engines": { "npm": ">=10.8.2", "node": ">=20.17.0" + }, + "dependencies": { + "prompt": "^1.3.0" } } diff --git a/src/App.js b/src/App.js index 091aa0a5d..e31c292a4 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,42 @@ +import { Console } from "@woowacourse/mission-utils"; + class App { - async run() {} -} + async run() { + try { + + // 문자열을 입력받는다. + const input = await Console.readLineAsync("계산할 문자열을 입력해 주세요: "); + + const result = this.calculate(input); + Console.print(`결과: ${result}`); + } catch (error) { + Console.print(error.message); + throw error; + } + } + calculate(input) { + //빈 문자열인지 검사 + if (input === "") return 0; -export default App; + //구분자 배열 생성 + let delimiterArray = [',', ':']; + // 커스텀 구분자 + const customDelimiter = input.match(/^\/\/(.)\\n/); + let numString = input; + + if (customDelimiter) { + //커스텀 구분자 배열에 추가 + delimiterArray.push(customDelimiter[1]); + numString = input.split('\\n')[1]; + } + + const nums = numString.split(new RegExp(`[${delimiterArray.join('')}]`)).map(Number); + + //에러처리 => 음수랑 잘못된 문자열 + if (nums.some((num) => num < 0 || isNaN(num))) { + throw new Error("[ERROR] 잘못된 입력 값 입니다."); + } + return nums.reduce((sum, num) => sum + num, 0); + } +} +export default App; \ No newline at end of file