Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
# javascript-calculator-precourse
# 1주차 미션: 문자열 덧셈 계산기

### 기능 요구 사항

---

입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.

- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다.

- 예: "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6

- 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다.

- 예를 들어 "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.

- 사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지와 함께 `Error`를 발생시킨 후 애플리케이션은 종료되어야 한다.

---

### 기능 구현 목록

---

- [ ] 입력 후 정규 표현식을 통해 커스텀 구분자를 확인하는 로직
- [ ] 구분자와 숫자를 구분하여 합산 구하는 로직

- [ ] 입력 전후 요구 사항에 맞게 결과 출력하는 로직
- [ ] 에러 발생 시 에러 메시지 출력하는 로직

---
90 changes: 89 additions & 1 deletion __tests__/ApplicationTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const getLogSpy = () => {
};

describe("문자열 계산기", () => {
// 정답 케이스
test("커스텀 구분자 사용", async () => {
const inputs = ["//;\\n1"];
mockQuestions(inputs);
Expand All @@ -32,12 +33,99 @@ describe("문자열 계산기", () => {
});
});

test("예외 테스트", async () => {
test("다중 커스텀 구분자", async () => {
const inputs = ["//@#\\n1@2#3"];
mockQuestions(inputs);

const logSpy = getLogSpy();
const outputs = ["결과 : 6"];

const app = new App();
await app.run();

outputs.forEach((output) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(output));
});
});

test("16진수 덧셈", async () => {
const inputs = ["0x01,2,3"];
mockQuestions(inputs);

const logSpy = getLogSpy();
const outputs = ["결과 : 6"];

const app = new App();
await app.run();

outputs.forEach((output) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(output));
});
});

test("커스텀 구분자 x와 16진수", async () => {
const inputs = ["//x\\n0x01,2,3"];
mockQuestions(inputs);

const logSpy = getLogSpy();
const outputs = ["결과 : 6"];

const app = new App();
await app.run();

outputs.forEach((output) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(output));
});
});

test("마이너스 커스텀 구분자", async () => {
const inputs = ["//-\\n1,-2,3"];
mockQuestions(inputs);

const logSpy = getLogSpy();
const outputs = ["결과 : 6"];

const app = new App();
await app.run();

outputs.forEach((output) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(output));
});
});

// 예외 테스트
test("음수 입력", async () => {
const inputs = ["-1,2,3"];
mockQuestions(inputs);

const app = new App();

await expect(app.run()).rejects.toThrow("[ERROR]");
});

test("구분자 마무리", async () => {
const inputs = ["1,2,3,"];
mockQuestions(inputs);

const app = new App();

await expect(app.run()).rejects.toThrow("[ERROR]");
});

test("숫자 커스텀 구분자", async () => {
const inputs = ["//1\\n1,2,3"];
mockQuestions(inputs);

const app = new App();

await expect(app.run()).rejects.toThrow("[ERROR]");
});
test("커스텀 구분자 선언 문제", async () => {
const inputs = ["//#\n1#2#3"];
mockQuestions(inputs);

const app = new App();

await expect(app.run()).rejects.toThrow("[ERROR]");
});
});
17 changes: 16 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { Console } from "@woowacourse/mission-utils";
import {

Choose a reason for hiding this comment

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

모듈을 나누어 import하는 구조로 코드를 작성할 수 있군요!
저는 기능 구현에 집중하느라 가독성 있게 구조를 나누는 방법까지는 생각하지 못했는데, 이 코드를 보고 배웠습니다 :)

getCustomSeparator,
getExpression,
executeExpression,
} from "./function.js";

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

const separators = getCustomSeparator(input);
const expressions = getExpression(input);
const result = executeExpression(expressions, separators);

Console.print(`결과 : ${result}`);
}
}

export default App;
61 changes: 61 additions & 0 deletions src/function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const pattern = /\/\/(.*)\\n/;

Choose a reason for hiding this comment

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

[제안]
상수로 보여서 SNAKE_CASE로 표기하면 어떨까요?
이름으로 구분자 패턴임을 명확히 나타내면 좋을 것 같아요. 이름에 separator를 붙이면 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

앗 그러네요! 다음부터는 저런 상수는 표기법 바꿔서 해야겠네요. 이름만으로 파악하기 어려워서 SEPARATOR_PATTERN 같은 이름으로 바꾸는게 좋아보이네요!!


function hasCustomSeparator(input) {
// 커스텀 구분자를 선언하는 데 이용되는 패턴 선언
return pattern.test(input);
}

export function getCustomSeparator(input) {
let separators = [":", ","];
if (hasCustomSeparator(input)) {
const customSeparators = input.match(pattern)[1];
isLegelSeparator(customSeparators);

Choose a reason for hiding this comment

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

isLegalSeparator()로 수정하면 더 명확할 것 같습니다 :)

Copy link
Author

Choose a reason for hiding this comment

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

앗 오타가 있었네요 집어주셔서 감사합니다!!

for (const separator of customSeparators) {
separators.push(separator);
}
}
return separators;
}

export function getExpression(input) {
if (hasCustomSeparator(input)) {
return input.replace(pattern, "");
} else {
return input;
}
}
Comment on lines +20 to +26

Choose a reason for hiding this comment

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

[제안]
첨에 expression이 뭐지 싶어서 살짝 헷갈렸어요.
inputWithoutSeparatorPattern 같은.. 조금 더 명시적인 이름으로 하면 어떨까요?
아니면 expression 이름 그대로 가져가고 주석을 달아줘도 좋을 것 같네요.

Copy link
Author

Choose a reason for hiding this comment

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

사실 저도 expression이라는 이름이 살짝 애매하다고 생각했는데, inputWithoutSeparatorPattern라고 쓰면 좀 길어지더라도 훨씬 명확해지네요! 이번 주차부터 JSDoc에 대해서 조금 공부해서 그거와 함께 주석을 추가하는 것도 좋은 방법일거 같습니다


export function executeExpression(expressions, separators) {
let stack = "";
let result = 0;

for (const char of expressions) {

Choose a reason for hiding this comment

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

문자열을 한 글자씩 순회하면서 직접 숫자를 만들어가는 구조가 흥미로웠어요! 평소엔 split()으로 나누는 방식만 생각했는데, 이런 식으로 스택처럼 직접 쌓았다가 처리하는 방법도 가능하다는 걸 배웠습니다.

if (separators.includes(char)) {
number = Number(stack);

Choose a reason for hiding this comment

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

let number = Number(stack);처럼 명시적으로 선언해주면 안전할 것 같습니다 !
또는 함수 내부에서 const currentNumber = Number(stack);처럼 이름을 명확히 해주면 더 좋을 것 같아요 ☺️
혹시 선언을 안한 이유가 따로 있으실까요?

Copy link
Author

Choose a reason for hiding this comment

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

엇 저부분 무슨 생각을 하고 작성했는지 기억이 안나네요 다음부터는 조금더 깊게 생각하고 작성해봐야 할거 같습니다!

isLegalNumber(number);
result += number;
stack = "";
} else stack += char;
}

if (stack === "") throw new Error("[ERROR] 구분자로 마무리될 수 없어요.");

number = Number(stack);
isLegalNumber(number);
result += Number(stack);

return result;
}

function isLegalNumber(number) {
if (number < 0) throw new Error("[ERROR] 양수를 입력해주세요.");
if (Number.isNaN(number))
throw new Error("[ERROR] 올바른 숫자를 입력해주세요.");
}

function isLegelSeparator(separators) {
if (separators.length < 1)
throw new Error("[ERROR] 구분자를 올바르게 입력해주세요.");
if (/.*\d.*/.test(separators))
throw new Error("[ERROR] 숫자를 구분자로 이용할 수 없어요.");
}
Comment on lines +50 to +61

Choose a reason for hiding this comment

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

[제안]
함수 이름 앞에 is가 붙어서 boolean을 반환할 거라고 예상했는데 그게 아니어서 살짝 헷갈렸어요.
에러를 던지는 거면 validate같은 이름을 붙이면 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

앗 저도 is라고 적은게 맞는 표현인지 몰랐는데 validate라는 좋은 단어가 있네요!! 2주차부터 바로 반영해보겠습니다!

Copy link
Author

Choose a reason for hiding this comment

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

좋은 제안 많이 해주셔서 감사합니다 😊 큰 도움되었습니다!!