Skip to content
Open
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
# javascript-calculator-precourse
# javascript-calculator-precourse
# 1주차 - 문자열 덧셈 계산기

## ⚙️ 간단한 프로젝트 실행 흐름
1. 안내 문구를 출력한다.
2. 사용자 입력을 수신한다.
3. 입력 문자열을 parser로 넘겨 숫자 배열로 변환한다.
4. 숫자 배열을 validator로 검증한다.
5. 검증 통과 배열을 calculator로 합산한다.
6. 합산 결과를 지정된 포맷으로 출력한다.
7. 프로세스를 종료한다.


## 🔧 구현할 기능 목록

### 1. parser (문자열 -> 숫자 배열 반환)
- [X] 기본 구분자(, :) 분리 및 숫자 배열 변환
- [X] 커스텀 구분자 지정(문자열 앞부분의 "//", "\n")

### 2. validator (숫자 배열 유효성 검사)
- [X] 숫자가 아닌 값이 포함된 경우 검증
- [X] 양수가 아닌 값인 경우 검증

### 3. calculator (합산)
- [X] 빈 배열 합산 시 0 반환
- [X] 그 외 숫자 배열 합산

### 4. App.js (전체 통합 테스트)
- [X] 모든 모듈 연동
- [X] `App.run()` 실행 로직 완성
- [X] `AplicationTest.js` 테스트 확인
20 changes: 19 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
import { MissionUtils } from "@woowacourse/mission-utils";
import { InputParser } from "./InputParser.js";
import { InputValidator } from "./InputValidator.js";
import { Calculator } from "./Calculator.js";

class App {
async run() {}
async run() {
try {
const input = await MissionUtils.Console.readLineAsync("덧셈할 문자열을 입력해 주세요.\n");

const numbers = InputParser.parse(input);
InputValidator.validate(numbers);
const result = Calculator.sum(numbers);

MissionUtils.Console.print(`결과 : ${result}`)
} catch (error) {
MissionUtils.Console.print(error.message);
throw error;
}
}
}

export default App;
12 changes: 12 additions & 0 deletions src/Calculator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class Calculator {
static sum(numbers) {
// 빈 배열인 경우 0 반환
if (!numbers || numbers.length === 0){
return 0;
}

// 그 외 숫자 배열인 경우 모든 숫자 합산
const total = numbers.reduce((acc, cur) => acc + cur, 0);
return total;
}
}
36 changes: 36 additions & 0 deletions src/InputParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export class InputParser {
static parse(input) {
// 입력이 비어있거나 공백인 경우
const raw = (input || "").trim();
if (raw.length === 0) {
return [];
}

// 기본 구분자: 쉼표(,)와 콜론(:)
let delimiters = [",", ":"];
let numbersPart = raw;

// 커스텀 구분자
if (raw.startsWith("//")) {
const match =
raw.match(/^\/\/(.)\n(.*)/s) || // 실제 줄바꿈
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

match를 활용해 커스텀 구분자를 이렇게 더 깔끔하게 파싱할 수 있었군요! 새로운 방법을 알아갑니다😊

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

커스텀 구분자를 한 글자만 인식하도록 되어 있는데, 혹시 여러 글자 구분자도 고려해볼 수 있을까요? 정규식을 확장한다면 어떤 식으로 바꿀 수 있을지 궁금합니다🧐

raw.match(/^\/\/(.)\\n(.*)/s); // 문자열 리터럴 "\n"
if (!match) {
throw new Error("[ERROR] 잘못된 커스텀 구분자 형식입니다.");
}
const [, custom, rest] = match;
delimiters = [",", ":", custom];
numbersPart = rest;
}

const escaped = delimiters.map((d) => d.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'));
Copy link
Copy Markdown

@janghw0126 janghw0126 Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구분자에 특수문자가 포함될 수도 있다는 점을 고려해 정규식을 이렇게 안전하게 생성할 수도 있군요! 덕분에 escape 처리를 활용하는 방법을 배워갑니다 😊

const regex = new RegExp(`(?:${escaped.join("|")})`, "g");

// 숫자 배열로 변환
return numbersPart
.split(regex)
.map((token) => token.trim())
Copy link
Copy Markdown

@janghw0126 janghw0126 Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const raw = (input || "").trim();

입력 전체와 각 토큰에 대해 각각 trim()을 적용하신 게 좋은 것 같아요! 혹시 입력 앞뒤 공백뿐 아니라, 숫자 사이에 실수로 들어간 공백도 잘 처리하려고 trim()을 두 번 쓴 건가요?

.filter((token) => token.length > 0)
.map((token) => Number(token)); // 숫자로 변환
}
}
18 changes: 18 additions & 0 deletions src/InputValidator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export class InputValidator {
static validate(numbers) {
// 숫자가 아닌 값이 포함된 경우
const hasNonNumber = numbers.some((n) =>
typeof n !== "number" || isNaN(n));
if (hasNonNumber) {
throw new Error("[ERROR] 숫자가 아닌 값이 포함되어 있습니다.");
}

// 양수가 아닌 값(0 또는 음수)
const hasNegative = numbers.some((n) => n <= 0);
Copy link
Copy Markdown

@janghw0126 janghw0126 Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

n <= 0 조건으로 0도 제외하신 이유가 궁금합니다! 과제 요구사항 상 음수만 금지인데, 0까지 제한하신 이유가 있으실까요?

if (hasNegative) {
throw new Error("[ERROR] 양수가 아닌 값이 포함되어 있습니다.");
}

return true;
}
}