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