Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
04a0c88
docs: 구현 기능 목록 작성
dd-jiyun Mar 24, 2025
39e1c1c
test: 덧셈 계산 기능 테스트
dd-jiyun Mar 25, 2025
629533e
feat: 덧셈 기능 구현
dd-jiyun Mar 25, 2025
aa5a17c
test: 문자열 -> 숫자 변환 기능 테스트
dd-jiyun Mar 25, 2025
0c77f38
feat: 문자열 -> 숫자 변환 기능 구현
dd-jiyun Mar 25, 2025
4b9d17d
test: 구분자 추출 기능 테스트
dd-jiyun Mar 25, 2025
34a8b9f
feat: 기본 구분자 추출 기능 구현
dd-jiyun Mar 25, 2025
2c9b9fd
test: 변환 기능 예외 처리 테스트
dd-jiyun Mar 26, 2025
59f68c4
test: 구분자 테스트
dd-jiyun Mar 27, 2025
cbe76f4
feat: 구분자 판별 구현
dd-jiyun Mar 27, 2025
e3fc33f
test: 구분자 추출 기능 테스트
dd-jiyun Mar 27, 2025
6bf86de
refactor: 구분자 추출 기능 수정
dd-jiyun Mar 27, 2025
c4b4a6d
refactor: 변환 과정 음수 입력 예외 처리
dd-jiyun Mar 28, 2025
2ebbda8
test: 문자열 구분하는 기능 테스트
dd-jiyun Mar 28, 2025
b8a4054
feat: 문자열 구분하는 기능 구현
dd-jiyun Mar 28, 2025
de3c373
refactor: 커스텀 구분자 추가 테스트
dd-jiyun Mar 29, 2025
e6b1ba7
refactor: 커스텀 구분자 검증 기능 구현
dd-jiyun Mar 29, 2025
f23ecb8
refactor: 커스텀 구분자 추출 기능 추가
dd-jiyun Mar 29, 2025
5514eca
refactor: 문자열 파싱하는 기능 수정
dd-jiyun Mar 29, 2025
69c6344
fix: 덧셈 메서드 이름 변경
dd-jiyun Mar 29, 2025
9e503af
refactor: 패키지 구조 변경
dd-jiyun Mar 30, 2025
05971be
feat: View 추가
dd-jiyun Mar 30, 2025
4852356
refactor: 실행 흐름 관리 controller 추가
dd-jiyun Mar 31, 2025
d599266
refactor: 기본 구분자 관련 로직 변경
dd-jiyun Mar 31, 2025
417abf7
refactor: 커스텀 구분자 형식 검증 로직 구현
dd-jiyun Apr 3, 2025
a1fbb56
docs: 기능 요구 사항 README 수정
dd-jiyun Apr 4, 2025
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
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
# java-calculator-precourse
# java-calculator-precourse

Comment on lines +1 to +2

Choose a reason for hiding this comment

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

image

커밋 메시지에 관한 지윤님만의 기준(단위 및 시제, 어조 포함)이 있으면 좋을 것 같아요. 😄

커밋은 작성 당시의 변경 사항을 설명하는 게 아니라 왜와 무엇이 바뀌었는지 기록하는 것이다. 라는 기준이 있다고 가정하면,
단순한 서술보다 의도를 명확히 하는 데 중점을 둘 수 있을 것 같아요. (중요한건 일관성!)

커밋 메시지 본문에 판별한다., 판별합니다., 추가하였습니다.
시제와 어조가 혼재되어 있어 통일하면 더 좋을 것 같아 리뷰 남깁니다! 👍

Choose a reason for hiding this comment

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

image
  • 사용 안내가 더 위에 출력되면 좋을 것 같아요. 덧셈할 문자열을 아래로!
  • 커스텀 구분자를 어떻게 사용할 수 있을까요? {}는 무엇을 의미하나요?
    • 문서를 읽지 않고 바로 실행하더라도 사용하기 쉬우면 더 좋을 것 같아요.

Choose a reason for hiding this comment

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

image image

어떤 차이가 있을까요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

커밋 메시지에 관한 지윤님만의 기준(단위 및 시제, 어조 포함)이 있으면 좋을 것 같아요. 😄
커밋은 작성 당시의 변경 사항을 설명하는 게 아니라 왜와 무엇이 바뀌었는지 기록하는 것이다. 라는 기준이 있다고 가정하면,
단순한 서술보다 의도를 명확히 하는 데 중점을 둘 수 있을 것 같아요. (중요한건 일관성!)
커밋 메시지 본문에 판별한다., 판별합니다., 추가하였습니다.
시제와 어조가 혼재되어 있어 통일하면 더 좋을 것 같아 리뷰 남깁니다! 👍

커밋 메세지를 일관적이게 작성했다고 생각했는데 놓친 부분이 있었군요 😅 조금 더 신경써서 작성하겠습니다. 감사합니다 :)

사용 안내가 더 위에 출력되면 좋을 것 같아요. 덧셈할 문자열을 아래로!
커스텀 구분자를 어떻게 사용할 수 있을까요? {}는 무엇을 의미하나요?
문서를 읽지 않고 바로 실행하더라도 사용하기 쉬우면 더 좋을 것 같아요.

헉 저 {구분자}는 {}를 제외한 구분자만 입력하는 걸 표현한 것이었는데 혼동이 있을 수 있겠네요! 수정하겠습니다.

어떤 차이가 있을까요?

기본 구분자 사이의 공백을 제대로 처리하지 못하고 있는 것 같아요 🥺 수정하겠습니다!

## 미션 목표

- 🔺 OOP(Object Oriented Programming)
- ✅ TDD 실습

## 미션 요구사항

입력한 문자열에서 숫자를 추출해 더하는 계산기를 만든다.

- 기본 구분자인 쉼표`,`나 콜론`:`을 구분자로 가지는 문자열을 받으면 구분자를 기준으로 숫자를 분리한 후 숫자를 더한 값을 반환한다.
- 기본 구분자 외에 커스텀 구분자를 `//`와 `\n`에 정의하여 사용할 수 있다.
- 잘못된 값을 입력할 경우 `IlLegaLArgumentException`을 발생시킨 후 종료된다.

Choose a reason for hiding this comment

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

Suggested change
- 잘못된 값을 입력할 경우 `IlLegaLArgumentException`을 발생시킨 후 종료된다.
- 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시킨 후 종료된다.

Copy link
Owner Author

Choose a reason for hiding this comment

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

오타가 있었군요! 감사합니다 👍🏻


## 구현 기능 목록

### 입력

- [x] 구분자와 양수로 구성된 문자열을 입력받는다.

### 구분자 확인

- [x] 기본 구분자인지 커스텀 구분자인지 확인한다.

### 문자열 분리

- [x] 구분자를 기준으로 숫자를 분리한다.

### 문자열 계산

- [x] 추출된 문자열을 숫자로 변환한 후 더한다.

### 예외 상황

- [x] 기본 구분자도 커스텀 구분자도 아닌 경우
- [x] `,`와 `:` 외의 구분자를 사용한 경우
- [x] `//`와 `\n`의 형식을 지키지 않은 경우
- 음수를 입력한 경우
- 사용할 수 없는 구분자를 사용한 경우 ex) 숫자, 커스텀 구분자 형식 (`// \n`), 공백

## 예제

| 입력 | 출력 |
|--------------|-----|
| `""` | `0` |
| `1` | `1` |
| `1,2,3` | `6` |
| `1,2:3` | `6` |
| `1:2:3` | `6` |
| `//;\n1;2;3` | `6` |

## 예외 예제

| 입력 | 예외 (`IllegalArgumentException`) |
|---------------|---------------------------------|
| `1,2,-3` | `양수를 입력해주세요` |
| `1,2;3` | `기본 구분자(,나 :)를 사용해주세요.` |
| `1,a,3` | `숫자만 입력해주세요.` |
| `//;1;2;3` | `커스텀 구분자 형식에 맞게 입력해주세요.` |
| `//\n123` | `커스텀 구분자를 입력해주세요.` |
| `//1\n112131` | `사용 가능한 커스텀 구분자를 입력해주세요.` |
Comment on lines +53 to +62

Choose a reason for hiding this comment

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

지윤님은 어떤 장점을 위해 예외 상황을 문서로 관리하는 것을 선택하셨나요?
예외를 문서로 작성하고 관리하게 되면 어떤 장점과 단점이 있을까요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

README를 보고 제가 생각한 예외 상황에 대해서 명시하기 위해서 작성했습니다!
하지만 작성한 예외 상황 외에도 더 많은 상황들이 발견될 때마다 README에 업데이트 해줘야 하는 단점이 있을 것 같아요,,

그렇다면 예혁님은 테스트 코드로만 예외 상황을 표현하시나요?
README에 '이건 제외하는 게 좋다'하는 기준이 있으시면 알려주실 수 있을까요?

Choose a reason for hiding this comment

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

README를 보고 제가 생각한 예외 상황에 대해서 명시하기 위해서 작성했습니다!

요구사항을 보고 생각했던 예외를 말씀 하신 걸까요? 그리고 어떤 장점이 있었기에 명시하려고 하셨나요?

그렇다면 예혁님은 테스트 코드로만 예외 상황을 표현하시나요?
README에 '이건 제외하는 게 좋다'하는 기준이 있으시면 알려주실 수 있을까요?

README는 결국 필요한 내용을 담는 것이 가장 큰 기준이 되지 않을까 싶어요.
예를 들어, 애플리케이션을 처음 실행하는 사람을 위해 환경 설정이나 실행 방법을 작성할 수도 있고,
처음 보는 사람도 어떤 프로그램인지 빠르게 이해할 수 있도록 실행 화면을 캡처해 첨부할 수도 있겠죠.

저는 이런 건 넣고, 저런 건 빼야 한다는 고정된 기준보다는
README를 읽는 사람의 입장에서 어떤 것을 도울 수 있을까?를 생각해보면서
목적를 중심으로 구성하면 더 좋다고 생각해요! 😄

12 changes: 11 additions & 1 deletion src/main/java/calculator/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
package calculator;

import calculator.controller.CalculatorController;
import calculator.model.Calculator;
import calculator.model.DelimiterExtractor;
import calculator.model.StringSplitter;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
CalculatorController calculatorController = new CalculatorController(
new DelimiterExtractor(),
new StringSplitter(),
new Calculator()
);
calculatorController.run();
}
}
49 changes: 49 additions & 0 deletions src/main/java/calculator/controller/CalculatorController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package calculator.controller;

import calculator.model.Calculator;
import calculator.model.DelimiterExtractor;
import calculator.model.Parser;
import calculator.model.StringSplitter;
import calculator.view.InputView;
import calculator.view.OutputView;
import camp.nextstep.edu.missionutils.Console;
import java.util.List;

public class CalculatorController {

private final DelimiterExtractor delimiterExtractor;
private final StringSplitter stringSplitter;
private final Calculator calculator;

public CalculatorController(final DelimiterExtractor delimiterExtractor,
final StringSplitter stringSplitter,
final Calculator calculator) {
this.delimiterExtractor = delimiterExtractor;
this.stringSplitter = stringSplitter;
this.calculator = calculator;
}
Comment on lines +12 to +24

Choose a reason for hiding this comment

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

해당 객체들을 Controller의 상태(필드)로 가져야 하는 이유가 있나요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

흐름을 제어하기 위해 필요하다고 판단해서 객체를 주입하는 방식으로 선택했습니다.
만약 상태를 가지게 하지 않기 위해서는 저 객체들이 상호작용할 수 있도록 Service 클래스가 생길 수 있을 것 같은데 그 부분을 고려해서 수정 하는게 더 나은 설계인가요?

Choose a reason for hiding this comment

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

흐름을 제어하기 위해 필요하다

이 부분이 조금 추상적으로 느껴졌어요. 혹시 어떤 흐름을 말씀하신 건가요?

그 객체(DelimiterExtractor, StringSplitter 등)가 필요했다면,
run() 메서드 안에서 객체를 생성해도 되지 않나요?
꼭 필드로 객체를 들고 있어야 하는가?에 대한 것이 궁금해서 드린 질문이었습니다.


public void run() {
String input = InputView.getInput();
if (isCheckedEmptyOrNull(input)) {
return;
}

List<String> delimiters = delimiterExtractor.extract(input);
List<String> numbers = stringSplitter.split(input, delimiters);
List<Integer> parsedNumbers = Parser.parse(numbers);

int result = calculator.add(parsedNumbers);
OutputView.printResult(result);
Console.close();
}

private boolean isCheckedEmptyOrNull(final String input) {
if (input == null || input.isBlank()) {
OutputView.printResult(0);
return true;
}

return false;
}
Comment on lines +41 to +48

Choose a reason for hiding this comment

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

입력값이 빈문자열인 경우 아무 연산을 수행하지 않고, 바로 0을 반환하도록 구성한 것은 효율적인 방법일 수 있어요. 👍 👍
그러나 유지보수하기 어려울 수도 있겠다는 생각이 들었어요.

예를 들어, 숫자를 1만 입력하면 그대로 1을 출력하도록 하는 것도 Controller에서 처리해준다면 여러 계산이나 구분자 처리 없이 가능하지 않을까요? 0으로 반환 또는 특수한 입력을 처리하는 로직이 늘어난다면 어떻게 될까요?

저는 오히려 예상하지 못한 곳에 코드가 존재하게될 확률이 높아질 것 같아요.
아무것도 입력하지 않으면 어떻게 처리되지? 계산기 코드를 봐야겠다 -> 찾을 수 없음

Controller가 아닌 모델이 책임질 수 있도록 설계하면 어떨까요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

저도 이 의견에 동의하는 것 같아요. 아무것도 입력하지 않았을 때 최대한 적은 접근으로 반환하려 하다보니 Controller에 두게 됐네요!
예혁님이 말씀해주신 부분 참고해서 수정해보겠습니다. 감사합니다 🙇🏻‍♀️

}
15 changes: 15 additions & 0 deletions src/main/java/calculator/model/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package calculator.model;

import java.util.List;

public class Calculator {

public int add(List<Integer> values) {
Comment on lines +5 to +7

Choose a reason for hiding this comment

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

문자열 계산기 프로젝트에서 계산기 객체가 중요할 것 같다고 판단했는데,
숫자를 모두 더하는 기능만 있어서 의외였던 것 같아요.
오히려 Controller가 문자열 계산기 프로젝트 내에서 가장 비즈니스 로직을 많이 수행하는 것 같은데
이에 대해서는 어떻게 생각하시나요? 😄

Copy link
Owner Author

@dd-jiyun dd-jiyun Apr 23, 2025

Choose a reason for hiding this comment

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

저는 각 클래스가 책임에 따라 역할을 명확히 나누는 데 중점을 두었고, Controller는 그 흐름을 제어하는 조립자라고 생각했어요!

하지만, 예혁님 피드백을 보고 Controller에서 핵심 기능인 덧셈이 어떻게 이루어지는 지에 대한 부가적인 기능들을 다 알고 있어도 되는지에 대한 고민이 생겼어요.
그렇다면 Calculator가 비지니스 로직을 수행하는 것이 가장 이상적인 설계일까요?

리뷰를 제가 잘 이해하고 대답한 건지 모르겠네요 .. 😅

Choose a reason for hiding this comment

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

저는 각 클래스가 책임에 따라 역할을 명확히 나누는 데 중점을 두었고, Controller는 그 흐름을 제어하는 조립자라고 생각했어요!

각 클래스가 책임에 따라 역할을 명확히 나누는 데 중점을 두셨고,
Controller를 흐름을 제어하는 조립자로 보셨다고 하셨어요.

지금 구조를 보면 Controller가 여러 객체들을 직접 조립하고 호출하는 방식이에요.
그런데 이 방식이 정말 객체지향적인 협력이라고 볼 수 있을까? 하는 생각이 들었어요.
Controller는 단순히 조립자 이상의 책임을 갖고 있고,
계산의 핵심인 숫자 분리와 덧셈까지 모두 관여하고 있는 구조니까요.

Calculator는 단순히 List를 더하는 역할만 수행하고 있어서,
과연 이게 비즈니스 로직을 담당하는 객체인가?라는 의문도 들었고요.

조금 더 객체들 간에 역할을 분산시키고, 서로 알아서 일할 수 있는 구조로 바꿔보는 것은 어떨까요?
고민을 더 해보시면 좋을 것 같습니다! 👍

int result = 0;
for (int value : values) {
result += value;
}

return result;
}
Comment on lines +7 to +14

Choose a reason for hiding this comment

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

현재 코드에서 이 메서드의 역할은 리스트의 값을 모두 더해 최종 합계를 반환하는 것 같아요! 😄

add는 일반적으로 값을 하나씩 추가하거나 덧셈의 동작을 명확히 나타내는 데 사용되는 것으로 알고 있어요.
예를 들어, 리스트에 값을 추가한다 (list.add(5))거나 단일 덧셈 작업 등이 있어요.

반면, sum은 현재 메서드처럼 컬렉션의 모든 값을 합산하여
최종 결과를 계산하는 작업을 표현할 때 주로 사용돼요.
예시: int total = numbers.stream().mapToInt(Integer::intValue).sum();

이 메서드는 결국 최종 합계의 개념을 전달하므로,
결과 값 중심의 맥락을 유지하기 위해 sum이라는 이름이 더 자연스러울 것 같은데 어떻게 생각하시나요? 😄

// total을 만드는 과정이 sum
for (int value : values) {
    total += value; // add
}

Copy link
Owner Author

Choose a reason for hiding this comment

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

덧셈이니 add라고 생각했는데 예혁님이 말씀해주신 대로 sum이 더 나을 것 같아요! 코드로 설명해주시니 더 이해가 쉽게 되네요 👍🏻 감사합니다.

}
48 changes: 48 additions & 0 deletions src/main/java/calculator/model/Delimiter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package calculator.model;

import static calculator.model.DelimiterConstants.DEFAULT_DELIMITER_REGEX;

import java.util.List;
import java.util.stream.Stream;

public enum Delimiter {
COMMA(",", true),
COLON(":", true),
DOUBLE_SLASH("//", false),
NEW_LINE("\\n", false),
NUMBER("[0-9]+", false),
SPACE(" ", false);


private final String delimiter;
private final boolean isDefault;
Comment on lines +8 to +18

Choose a reason for hiding this comment

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

COMMA, COLON과 안되는 것을 구분하기 위해, isDefault 필드를 가지게 되었고, 더 복잡해진 것 같아요.
각 요소를 살펴보면 문자열이다. 말고는 공통된 요소가 없는 것 같은데 Enum으로 관리하는 이유가 있나요?

  • COMMA, COLON : 기분 구분자 성격
  • DOUBLE_SLASH, NEW_LINE : 커스텀 구분자를 자르기 위해 필요한 커맨드
  • NUMBER : 안되는 것
  • SPACE : 코드에서 사용되지 않음 (필요하지 않다면 삭제해 주세요!)

기본 구분자 열거형, 커스텀 구분자로 사용할 수 없는 구분자 열거형 이런 식으로 각 책임에 맞게 열거형을 선언하는 것은 어떨까요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Delimiter를 통합적으로 사용한 것은 커스텀 구분자에 사용할 수 없는 구분자로 정의하기 위함이 우선적이었습니다.
그러다 보니 기본 구분자를 사용한 입력에서 기본 구분자를 판별하는데에서 isDefault로 구분을 해주게 되었습니다.
그래도 구분하는게 더 나을까요?

Choose a reason for hiding this comment

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

생각의 변화 유무의 상관 없이 근거를 가지고 이런 결정을 했다. 라고 제시해주시는 편이 더 좋을 것 같아요. 😄


Delimiter(final String delimiter, final boolean isDefault) {
this.delimiter = delimiter;
this.isDefault = isDefault;
}

public static List<String> getDefaultDelimiter() {
return Stream.of(values())
.filter(Delimiter::isDefault)
.map(Delimiter::getDelimiter)
.toList();
}

public static boolean isValidDelimiter(String input) {
return DEFAULT_DELIMITER_REGEX.matcher(input).matches();
}

public static boolean isValidCustomDelimiter(String delimiter) {
return Stream.of(values())
.noneMatch(d -> d.getDelimiter().equals(delimiter)) && !delimiter.matches(NUMBER.getDelimiter());
}

private String getDelimiter() {
return delimiter;
}

private boolean isDefault() {
return isDefault;
}
}
9 changes: 9 additions & 0 deletions src/main/java/calculator/model/DelimiterConstants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package calculator.model;

import java.util.regex.Pattern;

public class DelimiterConstants {
public static final Pattern DEFAULT_DELIMITER_REGEX = Pattern.compile("^[0-9,:]+$");
public static final String CUSTOM_DELIMITER_PREFIX = "//";
public static final String CUSTOM_DELIMITER_SUFFIX = "\\n";
}
Comment on lines +5 to +9

Choose a reason for hiding this comment

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

// 1. 이벤트용으로 사용할 상수를 만들 때 상속
class EventDelimiterConstants extends DelimiterConstants {
    public static final String EVENT_DELIMITER = "event";
}

// 2. 인스턴스화
DelimiterConstants ds = new DelimiterConstants; // 가능

DelimiterConstants를 상속하여 새로운 상수를 추가(EventDelimiterConstants)하거나
상수 정의를 확장할 수 있어요. 이것은 의도된 구조인가요?

DelimiterConstants는 기본 생성자를 통해 인스턴스화가 가능하네요.(new DelimiterConstants()).
의도가 상수 저장용 유틸리티 클래스라면, 인스턴스화를 방지하는 것이 좋을 것 같은데 어떻게 생각하시나요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

DelimiterConstants를 상속하여 새로운 상수를 추가(EventDelimiterConstants)하거나
상수 정의를 확장할 수 있어요. 이것은 의도된 구조인가요?

의도하지 않았습니다,,😢 예혁님 말대로 상수 저장을 위한 클래스이기 때문에 인스턴스화 방지를 하는게 맞다고 생각합니다!

65 changes: 65 additions & 0 deletions src/main/java/calculator/model/DelimiterExtractor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package calculator.model;

import static calculator.model.DelimiterConstants.CUSTOM_DELIMITER_PREFIX;
import static calculator.model.DelimiterConstants.CUSTOM_DELIMITER_SUFFIX;

import java.util.List;

public class DelimiterExtractor {

public List<String> extract(final String input) {
if (isCheckCustomDelimiterFormat(input)) {
validateCustomDelimiterFormat(input);
return extractCustomDelimiters(input);
}

validateDefaultDelimiter(input);
return Delimiter.getDefaultDelimiter();
}
Comment on lines +10 to +18

Choose a reason for hiding this comment

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

메서드 내에서 줄바꿈을 사용하는 지윤님의 기준을 설명해주실 수 있나요?

  • 이 메서드는 if 내의 return 문 위에는 줄바꿈이 없어요.
    • if 아래에 줄바꿈이 있어요. (DelimiterExtractor#validateCustomDelimiterFormat 메서드에는 if 아래에 줄바꿈이 없어요.)
  • 이 메서드 끝 return 문 위에 코드 한줄이 있어요.
    • DelimiterExtractor#extractCustomDelimiters 메서드에서는 끝나는 return문 위에 바로 줄바꿈이 있어요.

Copy link
Owner Author

Choose a reason for hiding this comment

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

문맥상 연관이 있다고 생각한 부분을 줄바꿈 없이 작성한 것 같아요.
근데 다른 코드에서는 return 문 위에 줄바꿈을 한 것이 있고 안한 것이 있네요 !
컨벤션을 의식적으로 신경쓰면서 일관적이게 코드 수정해보겠습니다. 감사합니다 ☺️


private boolean isCheckCustomDelimiterFormat(final String input) {
return input.startsWith(CUSTOM_DELIMITER_PREFIX) || input.contains(CUSTOM_DELIMITER_SUFFIX);
}

private void validateCustomDelimiterFormat(final String input) {
if (!input.startsWith(CUSTOM_DELIMITER_PREFIX) || !input.contains(CUSTOM_DELIMITER_SUFFIX)) {
throw new IllegalArgumentException("커스텀 구분자 형식에 맞게 입력해주세요.");
}
validateEmptyCustomDelimiter(input);
}

private void validateEmptyCustomDelimiter(final String input) {
int start = CUSTOM_DELIMITER_PREFIX.length();
int end = input.indexOf(CUSTOM_DELIMITER_SUFFIX);

if (end <= start) {
throw new IllegalArgumentException("커스텀 구분자를 입력해주세요.");
}
}

private List<String> extractCustomDelimiters(String input) {
List<String> delimiters = List.of(
input.substring(CUSTOM_DELIMITER_PREFIX.length(), input.indexOf(CUSTOM_DELIMITER_SUFFIX)));
validateAllCustomDelimiter(delimiters);

return delimiters;
}

private void validateAllCustomDelimiter(List<String> delimiters) {
for (String delimiter : delimiters) {
validateCustomDelimiter(delimiter);
}
}

private void validateCustomDelimiter(final String delimiter) {
if (!Delimiter.isValidCustomDelimiter(delimiter)) {
throw new IllegalArgumentException("사용 가능한 커스텀 구분자를 입력해주세요.");
}
}

private void validateDefaultDelimiter(final String input) {
if (!Delimiter.isValidDelimiter(input)) {
throw new IllegalArgumentException("기본 구분자(,나 :)를 사용해주세요.");
}
}
Comment on lines +60 to +64

Choose a reason for hiding this comment

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

기본 구분자 정책이 :(콜론) 대신에 -(대시)를 사용하는 것으로 변경된다면
예외 메시지도 수정되어야 할 것 같은데, 어떻게 생각하시나요?

Copy link
Owner Author

@dd-jiyun dd-jiyun Apr 22, 2025

Choose a reason for hiding this comment

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

사용자가 예외 메세지를 보고 문제를 파악하는데에 편리해야 한다고 생각해 자세하게 작성해주는 것이 맞다고 생각했어요!

하지만, 예혁님이 말씀해주신 것처럼 변경되었을 때 수정해야한다는 불편함이 있을 수 있겠네요! 고려해서 수정해보겠습니다.

}
32 changes: 32 additions & 0 deletions src/main/java/calculator/model/Parser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package calculator.model;

import java.util.List;

public class Parser {

public static List<Integer> parse(final List<String> input) {
Comment on lines +5 to +7

Choose a reason for hiding this comment

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

Parser#parse 라는 이름만 봤을 때 무슨 일을 수행하는지
예측하기 어려울 것 같은데 어떻게 생각하시나요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

제가 생각해도 어떤 일을 하는지 명확하게 표현하지 못한 것 같아요!
그렇다면 숫자로 변환하는 것을 표현하는 parseInt는 어떤가요?
실제 변환할 때 사용하는 메서드 명과 동일해서 좀 헷갈릴 수 있을 것 같기도 하네요..🥲

try {
List<Integer> numbers = getNumbers(input);
for (int number : numbers) {
validatePositiveNumber(number);
}

return numbers;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("숫자만 입력해주세요.");
}
}

private static List<Integer> getNumbers(final List<String> input) {
return input.stream()
.mapToInt(Integer::parseInt)
.boxed()
.toList();
}
Comment on lines +20 to +25

Choose a reason for hiding this comment

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

getNumbers 메서드는 input 리스트를 받아서

  • 각 문자열 요소를 정수(Integer)로 변환
  • 이를 List<Integer>로 수집

제 개인적인 기준으로는 두 가지 책임(변환과 수집)이 포함된 것으로 보이는데,
이 정도는 허용 가능한 복잡성인지, 더 분리하는 게 좋을지
고민해볼 필요가 있을 것 같아요. 😄

각 문자열 요소를 정수(Integer)로 변환 이 일을 Integer#parseInt가 대신 해준다고도 볼 수 있지만,
그에 관한 예외 처리(NumberFormatException)를 밖에서 해줘야 한다면,
여전히 지윤님이 의도한 정수 변환 처리를 완벽히 해내지 못한다고 볼 수 있을 것 같아요.

지윤님의 기준으로 이 메서드는 몇 가지의 책임을 수행하고 있나요?
아니라면 이를 분리해야 할까요? 더 나아가 어떻게 해볼 수 있을까요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

제 개인적인 기준으로는 두 가지 책임(변환과 수집)이 포함된 것으로 보이는데,
이 정도는 허용 가능한 복잡성인지, 더 분리하는 게 좋을지
고민해볼 필요가 있을 것 같아요. 😄

저는 getNumbers에서 문자열을 숫자로 변환하는 책임을 지는 것을 의도했기 때문에 변환하고 다시 리스트로 모아 반환하는 것을 한 가지의 책임으로 볼 수 있다고 생각해요.

각 문자열 요소를 정수(Integer)로 변환 이 일을 Integer#parseInt가 대신 해준다고도 볼 수 있지만,
그에 관한 예외 처리(NumberFormatException)를 밖에서 해줘야 한다면,
여전히 지윤님이 의도한 정수 변환 처리를 완벽히 해내지 못한다고 볼 수 있을 것 같아요.

말씀하신 대로 변환하는 일을 위임했지만 거기서 발생할 수 있는 예외 상황을 parse에서 하고 있네요.. 😂
정수 변환 과정에서 발생할 수 있는 예외이기 때문에 getNumbers 메서드에서 처리하는 것이 더 나을 것이라고 생각합니다!

Choose a reason for hiding this comment

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

getNumbers에서 문자열을 숫자로 변환하는 책임을 지는 것을 의도했기 때문에

말씀하신 의도가 메서드의 시그니처로 충분히 설명될 것 같나요? 지윤님의 생각은 어떠신가요?


private static void validatePositiveNumber(final int number) {
if (number < 0) {
throw new IllegalArgumentException("양수를 입력해주세요.");
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/calculator/model/StringSplitter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package calculator.model;

import static calculator.model.DelimiterConstants.CUSTOM_DELIMITER_SUFFIX;

import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class StringSplitter {

public List<String> split(String input, final List<String> delimiters) {
input = preprocessingCustomDelimiter(input);

String regex = getRegex(delimiters);
String[] splitResult = input.split(regex);

return List.of(splitResult);
}

private String preprocessingCustomDelimiter(String input) {
if (input.contains(CUSTOM_DELIMITER_SUFFIX)) {
input = input.substring(input.indexOf(CUSTOM_DELIMITER_SUFFIX) + 2);
}

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.

2는 무엇을 의미할까요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

substring을 할 때 SUFFIX의 값인 \\n이 이스케이프 문자열을 포함하고 있어 \n 자체의 길이인 2로 더해주어야 제가 원하는 커스텀 구분자 형식 부분을 제외한 뒤의 숫자만 반환하게 됩니다.

예를 들어, 입력값이 //;\n1;2;3을 하였을 때 +2를 하지 않으면 1;2;3이 아닌 \\n가 시작하는 3이 indexOf가 되어 \n1;2;3이 반환되게 됩니다. 그래서 +2를 해주었습니다.

하지만 SUFFIX가 바뀐다면 그에 대한 길이도 조정해야 한다는 점이 불편할 수 있을 것 같다고 생각이 드네요.
그래서 제가 생각한 방식은 상수로 하드코딩을 하는 것이 아니라 SUFFIX.length()를 사용해 수정할 수 있을 것 같은데 괜찮을까요?


private String getRegex(final List<String> delimiters) {
return delimiters.stream()
.map(Pattern::quote)
.collect(Collectors.joining("|"));
}
Comment on lines +28 to +32

Choose a reason for hiding this comment

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

"|"는 무엇을 의미할까요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

정규표현식에서 OR을 의미합니다!
여러 구분자를 정규식으로 만들어서 문자열을 구분하기 위해 Pattern.quote()로 감싸고 이어붙였습니다.

Choose a reason for hiding this comment

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

처음보는 개발자가 이해하기 어려울 것 같아요. 상수화를 해보는 것은 어떨까요?
문자열로 쓰이는 |인지,
말씀 하신 정규표현식의 OR인지 이해하기 힘들었습니다!

}
22 changes: 22 additions & 0 deletions src/main/java/calculator/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package calculator.view;

import camp.nextstep.edu.missionutils.Console;

public class InputView {

public static final String START_MESSAGE = "덧셈할 문자열을 입력해주세요.";
public static final String USAGE_GUIDE = """
[사용 안내]
기본 구분자 => ,(쉼표)와 :(콜론)만 사용 가능합니다.
커스텀 구분자 => //{구분자}\\n 형식을 사용해 선언할 수 있습니다.
""";
Comment on lines +7 to +12

Choose a reason for hiding this comment

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

Suggested change
public static final String START_MESSAGE = "덧셈할 문자열을 입력해주세요.";
public static final String USAGE_GUIDE = """
[사용 안내]
기본 구분자 => ,(쉼표) :(콜론) 사용 가능합니다.
커스텀 구분자 => //{구분자}\\n 형식을 사용해 선언할 수 있습니다.
""";
private static final String START_MESSAGE = "덧셈할 문자열을 입력해주세요.";
private static final String USAGE_GUIDE = """
[사용 안내]
기본 구분자 => ,(쉼표) :(콜론) 사용 가능합니다.
커스텀 구분자 => //{구분자}\\n 형식을 사용해 선언할 수 있습니다.
""";
  • 한번만 사용되는 것 같아요. 이를 상수로 관리하면 어떤 이점이 있었나요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

앞서 가이드 관련 리뷰와 같이 수정이 될 경우 더 빠르게 찾을 수 있다고 생각했습니다.

물론, 안내 문구를 보고 사용 방법을 안내하기 위한 의도인 것을 알 수 있지만, 코드 내에 작성하는 것보다 상수로 관리하여 해당 메세지가 어떤 것을 의미하는 지 더 명확하고 가독성 있을 것이라고 판단하여 상수로 두었습니다!


private InputView() {
}

public static String getInput() {
System.out.println(START_MESSAGE);
System.out.println(USAGE_GUIDE);
return Console.readLine();
}
}
11 changes: 11 additions & 0 deletions src/main/java/calculator/view/OutputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package calculator.view;

public class OutputView {

private OutputView() {
}
Comment on lines +3 to +6

Choose a reason for hiding this comment

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

View 부분이 간단해 보이는 것 같은데,
특별히 InputViewOutputView로 나누신 이유가 있나요?

Copy link
Owner Author

Choose a reason for hiding this comment

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

두 뷰로 나눈 이유는 Input은 입력, Output은 출력을 담당해야 한다고 생각했기 때문입니다.

그렇다면 간단한 출력일 경우에는 그냥 View를 담당하는 클래스 하나에 모아두는 것이 더 나을까요?

Choose a reason for hiding this comment

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

입력과 출력을 나눈다고 하셨는데 지윤님이 생각하시는 입력과 출력의 정의는 무엇인가요?

// InputView 클래스
public static String getInput() {
    System.out.println(START_MESSAGE); // 이것도 출력 아닌가요?
    System.out.println(USAGE_GUIDE);
    return Console.readLine();
}


public static void printResult(final int result) {
System.out.println("결과 : " + result);
}
}
Loading