Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[문자열 덧셈 계산기] 이나영 미션 제출합니다. #572

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6f0da34
docs: README 문구 추가
Bewheneverwhatiwant Oct 17, 2024
f2f9c42
docs: REACME 내용 보완 - branch 전략 추가
Bewheneverwhatiwant Oct 17, 2024
d2b9264
docs: README 브랜치 전략 수정
Bewheneverwhatiwant Oct 18, 2024
fe7259c
feat: 빈 문자열 입력 시 0 반환 기능 추가
Bewheneverwhatiwant Oct 20, 2024
249df5a
docs: README 내용 추가 - 이슈 자동닫힘->수동닫음으로 전략 변경
Bewheneverwhatiwant Oct 20, 2024
316cc8f
feat: 쉼표구분자 문자열 입력 시 연산 기능 추가
Bewheneverwhatiwant Oct 20, 2024
e1020e0
docs: README 일부 내용 삭제
Bewheneverwhatiwant Oct 20, 2024
7dabd65
docs: README 개행 수정
Bewheneverwhatiwant Oct 20, 2024
7abe52f
feat: 쉼표 또는 콜론 구분자의 문자열 입력 시 연산 기능 추가
Bewheneverwhatiwant Oct 20, 2024
4ed98a0
feat: 커스텀 구분자 지원 기능 추가
Bewheneverwhatiwant Oct 20, 2024
f526e61
feat: 음수, 숫자가 아닌 값, 잘못된입력 처리 기능 추가
Bewheneverwhatiwant Oct 20, 2024
c2100f7
docs: README에 사용자 입력 겨우의 수 내용 추가
Bewheneverwhatiwant Oct 20, 2024
a36fc48
docs: README 개행 분리
Bewheneverwhatiwant Oct 20, 2024
614215e
refactor: run() 내부 try catch로 수정, 컨벤션에 따른 수정
Bewheneverwhatiwant Oct 20, 2024
08968bb
feat: 커스텀 구분자와 기본 구분자를 혼용해도 연산 가능하도록 기능 추가
Bewheneverwhatiwant Oct 20, 2024
3b9b3f9
docs: README 기능 및 사용자 경우의 수 추가
Bewheneverwhatiwant Oct 20, 2024
a87d260
chore(ApplicationTest): 테스트케이스(쉼표 구분자)추가
Bewheneverwhatiwant Oct 20, 2024
8068e41
chore(ApplicationTest): 테스트케이스(zhffhs rnqnswk) 추가
Bewheneverwhatiwant Oct 20, 2024
ca88274
chore(ApplicationTest): 테스트케이스(콜론 구분자) 추가
Bewheneverwhatiwant Oct 20, 2024
41788bd
Merge branch 'bewheneverwhatiwant' of https://github.com/Bewheneverwh…
Bewheneverwhatiwant Oct 20, 2024
bc4ca74
chore(ApplicationTest): 테스트케이스(커스텀 구분자만 입력하는 경우) 추가
Bewheneverwhatiwant Oct 20, 2024
865940c
chore(ApplicationTest): 테스트케이스(커스텀 구분자가 /,\,\n인 경우) 추가
Bewheneverwhatiwant Oct 20, 2024
1560c40
style: AirBnB 가이드에 맞춰 큰따옴표를 작은따옴표로 변경
Bewheneverwhatiwant Oct 20, 2024
62aec6b
style(App): AirBnB 가이드에 맞춰 큰따옴표를 작은따옴표로 변경
Bewheneverwhatiwant Oct 20, 2024
ed58de9
Merge branch 'bewheneverwhatiwant' of https://github.com/Bewheneverwh…
Bewheneverwhatiwant Oct 20, 2024
ebb788a
refactor(App): 3개의 함수들을 별도 파일로 분리
Bewheneverwhatiwant Oct 20, 2024
acf275e
refactor(all): 파일 구조를 폴더로 정리
Bewheneverwhatiwant Oct 20, 2024
6975470
refactor(add): 2개 함수를 별도 파일로 분리
Bewheneverwhatiwant Oct 20, 2024
5578dc8
docs(README): 프로그램 흐름을 정리한 내용 추가
Bewheneverwhatiwant Oct 20, 2024
a32249e
docs(README): 오타 및 개행 수정
Bewheneverwhatiwant Oct 20, 2024
fc27c21
docs(README): 디렉토리 구조 설명 추가
Bewheneverwhatiwant Oct 20, 2024
7127a59
docs(README): 개행 수정
Bewheneverwhatiwant Oct 20, 2024
8db452d
docs(README): 기능들의 상위 기능 정의
Bewheneverwhatiwant Oct 20, 2024
540ce83
docs(README): 개행 수정
Bewheneverwhatiwant Oct 20, 2024
593ae60
docs(README): 개행 수정
Bewheneverwhatiwant Oct 20, 2024
a651bc2
refactor(all): eslint.config.js 파일에 따른 eslint적용
Bewheneverwhatiwant Oct 21, 2024
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
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,69 @@
# javascript-calculator-precourse
# javascript-calculator-precourse
---------------------------------

## 프로그램 흐름 정리
### 1. 입력받기
>'덧셈할 문자열을 출력해주세요.' 안내 출력 후, 사용자의 입력을 기다려야 한다.

### 2. 입력분석하기
[연산이 가능하도록 해야 하는 경우]
>ex: 아무런 문자열도 입력하지 않는 경우("") -> 0을 반환한다.<br />
>ex: 1:::2:4 와 같이 구분자가 중복되는 경우 -> 연산이 이루어지도록 한다.<br />
>ex: ,,,,1,,,3,,,5,, 와 같이 문자열의 앞뒤로 구분자가 있는 경우 -> 연산이 이루어지도록 한다.<br />
>ex: //%^\n와 같이 사용자가 커스텀 구분자를 여러개로 설정하는 경우 -> 하나로 묶여 구분자가 되도록 한다.<br />
>ex: 사용자가 ///\n, //\n\n와 같이 / 또는 \n를 구분자로 설정하는 경우 -> 구분자가 되도록 한다.<br />
>ex: 사용자가 커스텀 구분자와 기본 구분자를 혼용하는 경우 -> 연산이 이루어지도록 한다.<br />

[ERROR를 반환해야 하는 경우]
>ex: 사용자가 입력한 숫자에 음수가 있는 경우 -> [ERROR] 양수로만 이루어져야 합니다.<br />
>ex: //&\n와 같이 사용자가 커스텀 구분자만 입력하고 숫자부분을 입력하지 않는 경우 -> [ERROR] 잘못된 입력 형식입니다.<br />
>ex: 사용자가 커스텀 구분자 또는 기본 구분자 외 알 수 없는 구분자를 사용하는 경우 -> [ERROR] 숫자가 아닌 값이 포함되었습니다.<br />

### 3. 결과 출력
[연산이 가능한 경우] <br />
결과 : ${결과} <br />
[ERROR를 반환해야 하는 경우] <br />
[ERROR] ${error message} <br />


## 구현할 기능 목록 정리
>빈 문자열 입력 처리 기능 추가<br />
>(1) 쉼표 구분자로 숫자 분리 및 합산 기능 추가<br />
>(2) 콜론 구분자로 숫자 분리 및 합산 기능 추가<br />
>(3) 커스텀 구분자 지원 기능 추가<br />
>(4) 커스텀 구분자와 기본 구분자를 혼용할 수 있도록 기능 추가<br />
>**1, 2, 3, 4는 사용자 입력 파싱 기능에 속함** <br />
>(5) 음수 입력 시 처리 추가<br />
>(6) 숫자가 아닌 값 입력 시 처리 추가 <br />
>**5, 6은 입력값 검증 기능에 속함** <br />

### 브랜치 전략
>0. bewheneverwhatiwant 브랜치와 dev 브랜치를 사용하여 프로젝트를 관리할 것이다.<br />
>1. 구현할 기능의 목록을 정리한다.<br />
>2. 기능 단위로 Github 이슈를 발행한다.<br />
>3. 기능 단위로 Github 브랜치를 생성한다.<br />
>4. 기능의 구현이 완료된 브랜치는 이슈를 닫는다.<br />
>5. 기능의 구현이 완료된 브랜치는 dev에 merge 후 삭제한다.<br />
>6. 오류가 발생하면, 오류에 대한 이슈를 먼저 발행한다.<br />
>7. 오류에 대한 브랜치를 생성한다.<br />
>8. 오류를 해결 후 이슈를 닫는다.<br />
>9. 해결된 오류의 브랜치는 dev에 merge 후 삭제한다.<br />
>10. 최종적으로 dev가 bewheneverwhatiwant 브랜치에 합쳐지도록 한다.<br />
>11. pr을 보내어 1주차 프리코스를 마무리한다.<br />

>[수정]<br />
>커밋 시 이슈 자동 닫힘은 main 브랜치에 PR 또는 병합 시에만 가능하다<br />
>프리코스 규칙에 따라 main 브랜치가 아닌 bewheneverwhatiwant 브랜치를 사용한다<br />

### 디렉토리 구조

>src<br />
>ㄴApp.js<br />
>ㄴfuntions // App에서 쓰이는 3개의 함수 분리<br />
> ㄴgetInput.js<br />
> ㄴprintResult.js<br />
> ㄴaddFunctions<br />
> ㄴparseInput.js // 입력값 파싱 함수 분리<br />
> ㄴvaludateNumber.js // 숫자 검증 함수 분리<br />

![디렉토리 구조](image.png)
69 changes: 69 additions & 0 deletions __tests__/ApplicationTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,73 @@ describe("문자열 계산기", () => {

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

test("쉼표 구분자 사용", async () => {
const inputs = [",,1,,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 = ["3:5:::6"];
mockQuestions(inputs);

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

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

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

Choose a reason for hiding this comment

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

제가 이번 과제에서 미처 고려하지 못했던 부분이 바로 테스트 케이스를 작성하는 부분이었습니다
많은 분들이 제출해주신 코드를 보면서 테스트코드를 엄청 열심히 작성해주신 부분을 보고 동기부여를 받고갑니다!!


test("커스텀 구분자와 기본 구분자 혼용 사용", async () => {
const inputs = ["//^\\n1,2:3^4"];
mockQuestions(inputs);

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

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

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

test("커스텀 구분자만 입력하는 경우", async () => {
const inputs = ["//&\n"];
mockQuestions(inputs);

const app = new App();

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

test("커스텀 구분자가 /, \, \n인 경우", async () => {
const inputs = ["//\\\\n2\\3\\4"];
mockQuestions(inputs);

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

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

outputs.forEach((output) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(output));
});
});
});
18 changes: 18 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default [
{
files: ["**/*.js"],
languageOptions: {
ecmaVersion: 2021,
sourceType: "module"
},
rules: {
"quotes": ["error", "single"],
"object-curly-spacing": ["error", "always"],
"indent": ["error", 2],
"max-len": ["error", { "code": 100 }],
"wrap-iife": ["error", "outside"],
"newline-before-return": "error",
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"]
}
}
];
Binary file added image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 15 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import { getInput } from './functions/getInput.js';
import { add } from './functions/add.js';
import { printResult } from './functions/printResult.js';
import { Console } from '@woowacourse/mission-utils';

class App {
async run() {}
async run() {

Choose a reason for hiding this comment

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

저는 try-catch로 에러 핸들링하는 것을 생각 못했는데 보고 배워갑니다 ㅎㅎ

Choose a reason for hiding this comment

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

제가 매 코드마다 try-catch를 재탕(?)하는 버릇이 있어서...ㅎㅎ 이 구조가 가장 보기좋다는 인식이 있어서 바로 적용할 수 있었어요 !! 칭찬해주셔서 너무 감사드립니다 🍀

Choose a reason for hiding this comment

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

jaeyoung-kwon님 혹시 woowa 저장소 풀리퀘 주소 남겨주시면 제가 거기서 코드리뷰 해드릴 수 있을 것 같아요 !! 이 답글 보신다면 PR 번호나 주소 남겨주시면 맞리뷰 해드리구 싶습니다 😄

try {
const input = await getInput();
const result = add(input);
printResult(result);
} catch (error) {
Console.print(`${error.message}`);
throw error; // 예외를 다시 던져서 테스트에서 잡히도록 처리
}

Choose a reason for hiding this comment

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

간결한 run 함수 너무 깔끔하네요!
저는 어차피 에러를 처리하지 못하고 throw 하므로 try, catch는 굳이 사용하지 않아도 된다고 생각했어요
에러 객체를 throw 하기때문에 에러 메시지도 콘솔에 출력하지 않아도 될 것 같아요

Choose a reason for hiding this comment

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

아...! 제가 생각치 못했던 부분이네요 ! 깔끔함과 익숙함에 취해서 '이유'를 생각하지 않은 코딩이었던 것 같아요 ㅠㅠ 2주차 미션에서는 더 고민해서 App.js 구조를 작성해보겠습니다 !!

}
}

export default App;
13 changes: 13 additions & 0 deletions src/functions/add.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { parseInput } from './addFuntions/parseInput.js';
import { validateNumber } from './addFuntions/validateNumber.js';

export function add(input) {

Choose a reason for hiding this comment

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

add라는 함수에 input을 parse하고 그것들을 split해서 더하는 것도 진행되고 있는 것 같은데, 한 함수에 add만 하는 것이 아니라 다른 기능도 여러 개 들어간 것 같습니다. 단일 책임 원칙으로 함수를 더 쪼개서 함수에 대한 직관성을 높이면 어떨까 생각이 듭니다!

Choose a reason for hiding this comment

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

너무 좋은 피드백이네요 ! 제가 아직 초보라 입력/계산/출력 정도로만 나눠도 가독성이 좋지 않을까? 라는 막연한 생각을 가졌었는데, 단일 책임의 원칙에 대해서 공부하고 2주차 미션에서는 신경써야겠어요. 조언 너무 감사드립니다 !!

if (input === '') return 0;
const { numbers, delimiter } = parseInput(input); // 입력 파싱
const parsedNumbers = numbers
.split(delimiter)
.filter(num => num !== '') // 중복된 구분자 처리

Choose a reason for hiding this comment

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

여러개의 구분자가 있다는 케이스는 생각을 못했었는데 배워갑니다 ㅎㅎ

.map(validateNumber); // 숫자 유효성 검증 및 변환

Choose a reason for hiding this comment

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

로직 깔끔해용~
하지만 저도 함수를 조금 더 분리하면 좋을 것 같다는 생각입니다!

Choose a reason for hiding this comment

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

add 함수에 너무 많은 역할이 있는 것 말씀이시죠 ? 다음 미션에서는 단일 책임의원칙에 대해 더 공부하고 코딩하려구요 !! 좋은 지적 너무 감사드려요 🍀


return parsedNumbers.reduce((sum, num) => sum + num, 0); // 합산
}

Choose a reason for hiding this comment

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

저도 마찬가지로 add라는 함수안에 있는 작업들을 분리하면 후에 유지 보수할때 더 좋을 것 같아요!

Choose a reason for hiding this comment

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

commit rebase로 합치지 못한 것이 아쉽다고 생각하고 있었는데 !! 좋은 지적해주세서 감사해요 🍀 스쿼시는 사용 경험이 없지만 다음 미션에서 도전해보겠습니다 !

18 changes: 18 additions & 0 deletions src/functions/addFuntions/parseInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 입력 파싱 함수: 커스텀 구분자와 숫자 부분을 분리
export function parseInput(input) {
let delimiter = /[,|:]/; // 기본 구분자

if (input.startsWith('//')) {
const parts = input.split('\\n');

if (parts.length < 2 || parts[1].trim() === '') {

Choose a reason for hiding this comment

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

저는 정규표현식을 사용하지 않고 문제 해결하기 까다롭다고 생각했는데, 이런 식으로 접근할 수도 있군요! 많이 배워갑니다.

Choose a reason for hiding this comment

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

윗분께서 말씀주신대로 조건문이 좀 중첩되는 느낌이 있어서, 다음 미션에서는 더 깔끔하게 해보려구요 !! 칭찬 감사드립니다 👍

throw new Error('[ERROR] 잘못된 입력 형식입니다.');
}

Choose a reason for hiding this comment

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

구분자를 판별하는 과정에서 정규표현식을 사용해보면 어떨까요?
if 조건문을 중첩해서 사용하는 것보다 훨씬 간단하게 처리할 수 있다고 생각합니다

Choose a reason for hiding this comment

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

맞네요 그럼 저 부분이 좀 더 깔끔해질 것 같아요 !! 조언 감사드려요 ! 👍

const customDelimiter = parts[0].slice(2).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
delimiter = new RegExp(`[${customDelimiter},:]`); // 기본 구분자와 혼용 가능
input = parts[1];
}

return { numbers: input, delimiter }; // 파싱된 값 반환

Choose a reason for hiding this comment

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

  1. delimiter와 같은 상수는 외부에 정의해 두는게 관리하기 좋을 것 같습니다
  2. 파라미터를 바꾸는 것은 위험할 수 있다고 생각합니다! 새로운 변수에 할당하는게 안전해보여요
  3. 커스텀 구분자가 여러 글자여도 괜찮은가 보네요? 저랑은 다르게 해석하셔서 재밌게 봤습니다!

Choose a reason for hiding this comment

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

좋은 지적 너무 감사드립니다 ! 2번은 제가 '수정해야지 ~~' 생각만 해두고 수정을 못해버렸네요 ㅠㅠ 그리구 커스텀 구분자가 여러 글자여도 괜찮게 한 것이 맞아욤 !! 2개 이상의 특수문자를 붙일 경우 하나로 묶여서 구분자로 사용 가능하도록 예외를 대처했습니다 !

Choose a reason for hiding this comment

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

  1. parts[0] parts[1] 대신
    const [numbers, delimeter] = parts
    혹은
    const [numbers, delimeter] = put.split('\\n');와 같이 명시적인 네이밍을 사용한다면 가독성이 더 좋을 것 같아요!
  2. 모든 커스텀 문자열을 \$&으로 replace 하셨는데, \$&이 어떤 의미인지 궁금합니다! \으로 replace 하신걸까요?

Choose a reason for hiding this comment

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

  1. 맞네요 ! 좀 더 명확한 네이밍을 하는 걸 습관들여야겠습니다 !
  2. . $&는 JS의 string.prototype.replace()에서 사용하는 특수치환인데, "매칭된 문자열 자체를 반환" 하는 기능을 해요 !
    정규식이 구분자 문자열에서 특수문자를 찾으면, replace() 메서드가 특수문자 앞에 \를 붙여요.
    이 과정에서 $&가 매칭된 특수문자 자체를 의미하기 때문에, 문자가 무엇이든간에 그대로 유지하고 앞에 \를 붙일 수 있어요 !
    저는 예외 케이스에서 커스텀 구분자가 , /, \n, 두개인 경우 등 다양한 경우를 고려했기때문에,
    정확하게 특수문자를 파싱해오는게 중요하다고 생각했고
    $&는 커스텀 구분자가 정규식에서 제대로 처리되는 것을 보장하기 위해 사용했습니다 ㅎㅎ

}
12 changes: 12 additions & 0 deletions src/functions/addFuntions/validateNumber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 숫자 유효성 검증 및 변환 함수
export function validateNumber(num) {
const parsedNum = Number(num);

if (isNaN(parsedNum)) {
throw new Error('[ERROR] 숫자가 아닌 값이 포함되었습니다.');
} else if (parsedNum < 0) {

Choose a reason for hiding this comment

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

첫 if에서 숫자가 아닐 경우 error로 던져지기 때문에 else를 알써도 되지 않을까 생각합니다!

Choose a reason for hiding this comment

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

헉 맞네요 ...! 다음부터는 필요없는 코드는 아닐지 한번 더 숙고하고 코드를 짜야겠어요 !

throw new Error('[ERROR] 양수로만 이루어져야 합니다.');
}

return parsedNum;

Choose a reason for hiding this comment

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

구체적으로 잘 해결한 좋은 코드라고 생각합니다!
다만 변수나 함수 이름이 조금 더 명시적이고 구체적이면 더욱 가독성 있는 멋진 코드가 나올 것 같습니다!

Choose a reason for hiding this comment

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

피드백 감사합니다 !! 변수명과 함수명에서 의도를 바로 알 수 있도록 네이밍하는 습관을 들이도록 할게요 !! 👍

}
7 changes: 7 additions & 0 deletions src/functions/getInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Console } from '@woowacourse/mission-utils';

export async function getInput() {
const input = await Console.readLineAsync('덧셈할 문자열을 입력해 주세요.\n');

return input;
}
5 changes: 5 additions & 0 deletions src/functions/printResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Console } from '@woowacourse/mission-utils';

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