-
Notifications
You must be signed in to change notification settings - Fork 232
[문자열 덧셈 계산기] 주유나 미션 제출합니다. #228
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
base: main
Are you sure you want to change the base?
Changes from all commits
58d17dc
ab6d630
b7354bc
d60a9c7
568594d
f030549
db773d9
f80da93
0a1790d
ba191bd
a51252a
0bdb072
8bc9ca5
ee70eb3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,70 @@ | ||
| # javascript-calculator-precourse | ||
| # javascript-calculator-precourse | ||
|
|
||
| # 🧮 문자열 덧셈 계산기 | ||
|
|
||
| ## 구현 기능 목록 | ||
|
|
||
| > 각 기능 구현이 완료될 때마다 AngularJS 커밋 컨벤션을 사용하여 커밋. | ||
| > 커밋 단위는 “한 기능 = 한 커밋” | ||
|
|
||
| --- | ||
|
|
||
| ### 1. 입력 안내 출력 | ||
|
|
||
| - [x] `Console.print('덧셈할 문자열을 입력해 주세요.')` 출력 | ||
|
|
||
| --- | ||
|
|
||
| ### 2. 문자열 입력 받기 | ||
|
|
||
| - [x] `Console.readLineAsync()`로 사용자로부터 문자열 한 줄을 입력받음 | ||
|
|
||
| --- | ||
|
|
||
| ### 3. 빈 문자열 처리 | ||
|
|
||
| - [x] 빈 문자열(`""` 또는 공백만 포함) 입력 시 결과 `0` 반환 | ||
|
|
||
| --- | ||
|
|
||
| ### 4. 커스텀 구분자 파싱 | ||
|
|
||
| - [x] 입력 문자열이 "//"로 시작하고 "\n"을 포함하는 경우, "//"와 "\n" 사이에 있는 문자를 커스텀 구분자로 사용 | ||
| - [x] 그렇지 않으면 기본 구분자 `,`와 `:` 사용 | ||
|
|
||
| --- | ||
|
|
||
| ### 5. 숫자 분리 로직 | ||
|
|
||
| - [x] 구분자(기본/커스텀)로 문자열을 나누어 각 숫자를 얻기 | ||
| - [x] 각 숫자 앞뒤 공백(`trim()`) 제거 | ||
|
|
||
| --- | ||
|
|
||
| ### 6. 유효성 검증 | ||
|
|
||
| - [x] 분리된 모든 값이 정수 형태의 문자열인지 확인 | ||
| - [x] 양의 정수만 허용 (0 및 음수 금지) | ||
| - [x] 연속 구분자(`"1,,2"`, `"1:"`) 등 잘못된 형식의 입력을 확인 | ||
| - [x] 오류 발생 시 `[ERROR]`로 시작하는 메시지와 함께 `Error` 발생시킨 후 애플리케이션은 종료 | ||
|
|
||
| --- | ||
|
|
||
| ### 7. 합계 계산 | ||
|
|
||
| - [x] 검증 통과한 정수들을 합산하여 `Number`로 반환 | ||
|
|
||
| --- | ||
|
|
||
| ### 8. 출력 형식 준수 | ||
|
|
||
| - [x] 결과 출력 시 형식을 정확히 맞춤 | ||
| - [x] 공백 및 콜론(`:`) 위치 정확히 유지 | ||
|
|
||
| --- | ||
|
|
||
| ### 9. 예외 처리 및 종료 흐름 | ||
|
|
||
| - [x] `run()` 내부에서 오류를 `try/catch`로 처리 | ||
| - [x] `[ERROR]` 메시지를 `Console.print()`로 출력 | ||
| - [x] `process.exit()` 호출 없이 자연스럽게 종료 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,20 @@ | ||
| import { Console } from "@woowacourse/mission-utils"; | ||
| import { add } from "./StringCalculator.js"; | ||
| class App { | ||
| async run() {} | ||
| async run() { | ||
| Console.print("덧셈할 문자열을 입력해 주세요."); | ||
| try { | ||
| const input = await Console.readLineAsync(""); | ||
| const result = add(input); | ||
| Console.print(`결과 : ${result}`); | ||
| } catch (error) { | ||
| const message = | ||
| error?.message ?? "[ERROR] 알 수 없는 오류가 발생했습니다."; | ||
| Console.print(message); | ||
|
|
||
| throw error; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export default App; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| const DEFAULT_DELIMS = [",", ":"]; | ||
|
|
||
| function escapeRegexChar(ch) { | ||
| return ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이스케이프 처리를 별도 함수로 분리한 점이 좋네요!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 감사합니다 😊 말씀하신것과 같이 테스트 코드 작성 시에도 따로 검증할 수 있어서 유지 보수성에 좋을거라 생각했습니다. 리뷰 감사드립니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗 저는 특수문자 처리까지는 생각하지 못했었는데 하나 배워갑니다..!!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
피드백 감사합니다! |
||
|
|
||
| function parseDelimiterAndBody(input) { | ||
| if (input.startsWith("//")) { | ||
| const realNewlineIdx = input.indexOf("\n"); | ||
| const literalNewlineIdx = input.indexOf("\\n"); | ||
|
|
||
| let idx = -1; | ||
| let useLiteral = false; | ||
|
|
||
| if (realNewlineIdx !== -1) { | ||
| idx = realNewlineIdx; | ||
|
Comment on lines
+15
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 이 부분이 실제 개행으로도 커스텀 구분자를 사용할 수 있게끔 하기 위한 부분이 맞을까요..? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 만약 맞다면 사용자 입장에서 사용하기 더 좋은 것 같아요! ㅎㅎ
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
네 맞습니다 👍🏻 |
||
| } else if (literalNewlineIdx !== -1) { | ||
| idx = literalNewlineIdx; | ||
| useLiteral = true; | ||
| } | ||
|
|
||
| if (idx === -1) { | ||
| throw new Error("[ERROR] 커스텀 구분자 뒤에 줄바꿈(\\n)이 필요합니다."); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 애러는 어떤 처리를 해주는 애러인가요??
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 에러는 커스텀 구분자 선언 뒤에 줄바꿈(\n)이 누락된 경우를 처리하기 위한 작성한 예외였습니다! 문제 요구사항에
Comment on lines
+12
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 총 4곳에서
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
그렇네요! 상수로 분리하는 편이 훨씬 더 명확할 것 같습니다. |
||
| const delim = input.slice(2, idx); | ||
| if (!delim || delim.length !== 1) { | ||
| throw new Error( | ||
| "[ERROR] 커스텀 구분자는 '//'와 '\\n' 사이의 문자 1개만 사용할 수 있습니다." | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 메세지 같은 경우, 언제나 유동적으로 바뀔 수 있는 부분이라고 생각해요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 특히 오타와 같은 휴먼애러에도 유용할것 같고요 !
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이 부분에 대해서 리뷰가 많아 다음 미션에는 꼭 적용해도록 해보겠습니다! 좋은 의견 감사합니다 😊
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
에러 메세지 상수화 적용에 대해서 코드를 작성할때 생각하지 못했는데, 다른분들은 이 부분까지 미리 고려하신 것 같습니다!
Comment on lines
+26
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 말씀해주신 것처럼 parseDelimiterAndBody가 파싱과 검증을 함께 처리하고 있어서, 역할 분리를 통해 구조를 더 명확히 하는게 좋을 것 같습니다. 좋은 피드백 감사합니다! 🙇🏻♀️ |
||
| ); | ||
| } | ||
|
|
||
| const body = useLiteral ? input.slice(idx + 2) : input.slice(idx + 1); | ||
|
|
||
| // 커스텀 구분자가 존재할 때 | ||
| return { delims: [delim], body }; | ||
| } | ||
| // 커스텀 구분자가 없을 때 | ||
| return { delims: DEFAULT_DELIMS, body: input }; | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 말씀해주신 대로 현재 하나의 함수가 여러 기능을 담당하고 있어서, 앞으로 함수의 역할 분리를 더욱 신경 쓰며 코드를 작성해야겠다는 생각이 듭니다! 좋은 피드백 감사합니다 😁 |
||
|
|
||
| function splitTokens(body, delims) { | ||
| if (body === "") return []; | ||
| const escaped = delims.map(escapeRegexChar).join(""); | ||
| const re = new RegExp(`[${escaped}]`, "g"); | ||
| const raw = body.split(re).map((t) => t.trim()); | ||
|
|
||
| // split으로 나눈 결과가 유효한지 검사 | ||
| if (raw.some((t) => t === "")) { | ||
| throw new Error("[ERROR] 구분자 사이에 숫자가 비어있습니다."); | ||
| } | ||
|
Comment on lines
+48
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 의도하신건지는 모르겠습니다만
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 피드백 감사합니다! 👍🏻
Comment on lines
+45
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 변수명이 t, raw, re처럼 줄여 쓰여 있어서 처음 읽는 사람 입장에선 의미를 파악하기 어려울 수 있을 것 같아요!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
피드백 감사합니다. 저도 그부분 동의합니다! |
||
| return raw; | ||
| } | ||
|
|
||
| function toPositiveIntegers(tokens) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 함수명이 다소 불명확하다고 느낄 수도 있을 것 같아요..! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. token이 의미하는것이 아마 number겠죠 ?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
함수 내부에서 다양한 기능을 처리하다 보니
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return tokens.map((t) => { | ||
| // 정규식 검사 | ||
| if (!/^\d+$/.test(t)) { | ||
| throw new Error("[ERROR] 숫자 이외의 값이 포함되어 있습니다."); | ||
| } | ||
| // 숫자 변환 및 음수/0 검사 | ||
| const num = Number(t); | ||
| if (num <= 0) { | ||
| throw new Error("[ERROR] 0 또는 음수는 허용되지 않습니다."); | ||
| } | ||
| return num; | ||
| }); | ||
| } | ||
|
|
||
| export function add(input) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 프리코스 피드백 |
||
| const trimmed = (input ?? "").trim(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 예상치 못한 null이나 undefined 입력에서도 안전하게 동작하도록 방어 코드를 추가했습니다. 작은 부분이지만 알아봐주셔서 감사합니다 😊 |
||
| if (trimmed === "") return 0; | ||
|
|
||
| const { delims, body } = parseDelimiterAndBody(trimmed); | ||
|
|
||
| const tokens = splitTokens(body, delims); | ||
|
|
||
| const numbers = toPositiveIntegers(tokens); | ||
|
|
||
| // 합산 | ||
| return numbers.reduce((acc, n) => acc + n, 0); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 함수명을 수정해보는것은 어떨까요 ? 함수명이 add라서 어 덧셈을 해주는 함수구나 함수명을 수정하거나 기능을 쪼개도 좋을것 같긴합니다!!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 공통적으로 받은 피드백에서 다음 코드에서는 보다 구체적인 함수명과 기능 분리를 명확히하여 코드를 개선해봐야겠습니다. 피드백 감사합니다 👍🏻 |
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오오 저도 타입스크립트의 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| declare module "@woowacourse/mission-utils" { | ||
| export const Console: { | ||
| readLineAsync(prompt?: string): Promise<string>; | ||
| print(message: string): void; | ||
| }; | ||
| } | ||
|
Comment on lines
+1
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. library를 따로 분리해서 타입스크립트로 처리한 것 굉장히 인상적입니다!! 많이 배워갑니다 ㅎㅎ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. declare module 을 통해 타입스크립트화 하는 것은 처음 배웠습니다 d.ts로 전역 타입으로 선언함으로서 vscode가 자동완성되게 하신 의도 같아보이는데 맞는지 궁금하네요
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 맞습니다! 😊 이를 해결하기 위한 방법을 찾다가 declare module 을 통해 타입을 직접 정의하는 방식을 배웠습니다. 전역 타입(.d..ts) 으로 선언하여서 IDE 자동완성과 타입 추론이 가능하도록 설정했고, 경고 메세지가 사라졌습니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
리뷰 감사합니다 😁
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 타입스크립트가 지원되지 않는 라이브러리일때 타입추론 하게 하는 방법을 배웠네요 .. 😁 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 저도 타입 추론하는 법 하나 배워갑니다..! 신기하네요...:) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 타입추론에 대해 배워가요. 새롭네요. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 저도 마지막에 한 실수긴 하지만
const input = await Console.readLineAsync("덧셈할 문자열을 입력해 주세요.\n");같이 하셨어도 깔끔했을것 같아요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헉.. 그때는 생각해보지 못했는데 훨씬 가독성도 좋아보이고 흐름이 좋네요 🙇🏻♀️ 리뷰 정말 감사합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
나중에
덧셈할 문자열을 입력해 주세요.같은 텍스트도 상수로 분리하는것도 고려해보시면 좋을것 같습니다!!
저 같은경우 오타를 많이내는 편이라 분리했을때 이점도 있더라고요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다음 미션에서 그부분도 고려해보겠습니다! 좋은 의견 감사드립니다 👩🏻💻