Repository: next-step/java-racingcar
Pull request #5935: 2단계 - 문자열 덧셈 계산기
Author: login: gan-ta
Labels: name: cc-java-20
Created: 2025-03-14T00:45:28Z | Merged: 2025-03-14T04:31:01Z

리뷰 잘 부탁 드립니다.

다음과 같은 내용을 생각하면서 구현했습니다.
- TDD 개발하기 위해 테스트 코드를 먼저 구현하고 로직 구현 했습니다., commit을 단계별로 나눴습니다.
- 클래스명을 DelimitedStringCalculator 라고 지어본 이유는 보통 계산기를 하면 사칙연산을 구현하는 식으로 확장해 나가는데 계산기 클래스 안에서 덧셈을 구현하고 추후 확장성을 고려했을 때는 클래스명을 이렇게 짓는 것이 좋을 것 같다고 생각했습니다. (클래스를 하나씩 생성하는 방법도 있지만 계산기 클래스 안에 사칙연산 메서드가 있는 것이 조금 더 깔끔하지 않을까 생각들어서 구현했습니다.)
- 클래스가 상태를 가지고 있지 않고 유틸리티성 클래스라 판단해서 static 함수로 구현했습니다.
- 메소드에 많은 논리가 들어가지 않도록 구분하려고 고민해봤습니다.


Diff:
diff --git a/src/main/java/calculator/DelimitedStringCalculator.java b/src/main/java/calculator/DelimitedStringCalculator.java
new file mode 100644
index 00000000000..ae4a84fbb55
--- /dev/null
+++ b/src/main/java/calculator/DelimitedStringCalculator.java
@@ -0,0 +1,130 @@
+package calculator;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class DelimitedStringCalculator {
+
+  private static final String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";
+  private static final String DEFAULT_DELIMITER_REGEX = "^[0-9,:]+$";
+
+  private DelimitedStringCalculator() {
+
+  }
+
+  public static int sum(String input) {
+    if (isNotValidString(input)) {
+      return 0;
+    }
+
+    if (hasCustomDelimiter(input)) {
+      return sumNonNegativeNumberWithCustomDelimiter(input);
+    }
+
+    if (hasDefaultDelimiter(input)) {
+      return sumNonNegativeNumberWithDefaultDelimiter(input);
+    }
+
+    return convertStringToNumber(input);
+  }
+
+  private static boolean isNotValidString(String input) {
+    return input == null || input.isEmpty();
+  }
+
+  private static boolean hasCustomDelimiter(String input) {
+    Matcher matcher = Pattern.compile(CUSTOM_DELIMITER_REGEX).matcher(input);
+
+    return  matcher.matches();
+  }
+
+  private static int sumNonNegativeNumberWithCustomDelimiter(String input) {
+    Matcher m = Pattern.compile(CUSTOM_DELIMITER_REGEX).matcher(input);
+
+    if (m.matches()) {
+      String customDelimiter = m.group(1);
+
+      int[] numbers = convertStringsToNumbers(m.group(2).split(Pattern.quote(customDelimiter)));
+      if (hasNegativeNumber(numbers)) {
+        throw new IllegalArgumentException(String.format(
+            "음수는 연산할 수 없습니다. - %s",
+            input
+        ));
+      }
+
+      return sumNumbers(numbers);
+    }
+
+    throw new IllegalArgumentException(String.format(
+            "커스텀 구분자가 들어가있는 문자열 형태가 아닙니다. - %s",
+            input
+        ));
+  }
+
+  private static boolean hasDefaultDelimiter(String input) {
+    Matcher m = Pattern.compile(DEFAULT_DELIMITER_REGEX).matcher(input);
+
+    return m.find();
+  }
+
+  private static int sumNonNegativeNumberWithDefaultDelimiter(String input) {
+    Matcher m = Pattern.compile(DEFAULT_DELIMITER_REGEX).matcher(input);
+
+    if (!m.matches()) {
+      throw new IllegalArgumentException(String.format(
+          "커스텀 구분자 입력을 받지 않으면 구분자는 \",\" 혹은 \":\" 구분자만 사용 가능합니다. - %s",
+          input
+      ));
+    }
+
+    int[] numbers = convertStringsToNumbers(input.split("[,:]"));
+    if (hasNegativeNumber(numbers)) {
+      throw new IllegalArgumentException(String.format(
+          "음수는 연산할 수 없습니다. - %s",
+          input
+      ));
+    }
+
+    return sumNumbers(numbers);
+  }
+
+  private static boolean hasNegativeNumber(int[] numbers) {
+    for (int number : numbers) {
+      if (number < 0) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private static int[] convertStringsToNumbers(String[] values) {
+    int[] numbers = new int[values.length];
+
+    for (int i = 0; i < values.length; i++) {
+      numbers[i] = convertStringToNumber(values[i]);
+    }
+
+    return numbers;
+  }
+
+  private static int convertStringToNumber(String value) {
+    try {
+       return Integer.parseInt(value);
+    } catch (NumberFormatException e) {
+      throw new IllegalArgumentException(String.format(
+          "유효하지 않는 숫자 형식의 문자가 문자열에 있습니다. - %s ",
+          value
+      ), e);
+    }
+  }
+
+  private static int sumNumbers(int[] numbers) {
+    int sum = 0;
+
+    for (int number : numbers) {
+      sum += number;
+    }
+
+    return sum;
+  }
+}
diff --git a/src/test/java/calculator/DelimitedStringCalculatorTest.java b/src/test/java/calculator/DelimitedStringCalculatorTest.java
new file mode 100644
index 00000000000..71454377a22
--- /dev/null
+++ b/src/test/java/calculator/DelimitedStringCalculatorTest.java
@@ -0,0 +1,107 @@
+package calculator;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DelimitedStringCalculatorTest {
+
+  @DisplayName("빈 문자열이 주어졌을 때 sum()을 호출하면 0을 반환한다.")
+  @Test
+  void sum_emptyString_returnsZero() {
+    String input = "";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(0);
+  }
+
+  @DisplayName("null이 주어졌을 때 sum()을 호출하면 0을 반환한다.")
+  @Test
+  void sum_null_returnZero(){
+    String input = null;
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(0);
+  }
+
+  @DisplayName("구분자가 없이 숫자 하나만 있는 문자열이 주어졌을 때 sum()을 호출하면 입력된 수 그대로 반환한다.")
+  @Test
+  void sum_singleNumberString_returnsSameNumber() {
+    String input = "1";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(1);
+  }
+
+  @DisplayName("\",\"로 구분되는 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_commaSeparatedString_returnsSumOfNumbers() {
+    String input = "1,2,3";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(6);
+  }
+
+  @DisplayName("\":\"로 구분되는 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_colonSeparatedString_returnsSumOfNumbers() {
+    String input = "1:2:3";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(6);
+  }
+
+  @DisplayName("커스컴 구분자를 가지는 형식의 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_customDelimiterString_returnsSumOfNumbers() {
+    String input1 = "//;\n1;2;3";
+    String input2 = "//$\n1$2$4";
+
+    int result1 = DelimitedStringCalculator.sum(input1);
+    int result2 = DelimitedStringCalculator.sum(input2);
+
+    assertThat(result1).isEqualTo(6);
+    assertThat(result2).isEqualTo(7);
+  }
+
+
+  @DisplayName("커스텀 구분자를 입력받은 형태가 아닌데 \",\" 혹은 \":\" 구분자가 아닌 문자열이 주어졌을 떄 sum()을 호출하면 RuntimeException을 던진다.")
+  @Test
+  void sum_invalidDelimiter_throwsRuntimeException() {
+    String input = "1$2";
+
+    Assertions.assertThrows(
+        RuntimeException.class,
+        () -> DelimitedStringCalculator.sum(input)
+    );
+  }
+
+  @DisplayName("음수를 가지는 형식의 문자열이 주어졌을 때 sum()을 호출하면 RuntimeException을 던진다.")
+  @Test
+  void sum_withNegativeValue_throwsRuntimeException() {
+    String input = "-1:2";
+
+    Assertions.assertThrows(
+        RuntimeException.class,
+        () -> DelimitedStringCalculator.sum(input)
+    );
+  }
+
+  @DisplayName("커스텀 구분자를 입력받았을 때 커스텀 구분자랑 숫자 이외 문자열이 주어졌을 때 sum()을 호출하면 RuntimeException을 던진다.")
+  @Test
+  void sum_invalidCharacterWithoutDelimiter_throwsRuntimeException() {
+    String input = "//;\n1;2;3|4";
+
+    Assertions.assertThrows(
+        RuntimeException.class,
+        () -> DelimitedStringCalculator.sum(input)
+    );
+  }
+}
diff --git a/todo/step2 b/todo/step2
new file mode 100644
index 00000000000..d0f795e951d
--- /dev/null
+++ b/todo/step2
@@ -0,0 +1,11 @@
+DONE
+- 빈 문자열이 입력되면 0을 반환한다.
+- null이 입력되면 0을 반환한다.
+- 구분자가 없이 숫자 하나이면 그대로 반환한다.
+- "," 로 구분자로 가지는 문자열을 split해서 수를 더한다.
+- ":" 로 구분자로 가지는 문자열을 split해서 수를 더한다.
+- 커스컴 구분자를 입력받아 구분해서 수를 더한다. (ex. //;\n1;2;3” 형식)
+- 커스텀 구분자를 입력받은 형태가 아닌데 "," 혹은 ":" 구분자가 아닌 경우는  RuntimeException을 던진다.
+- 음수를 전달하면 RuntimeException을 던진다.
+- 커스컴 구분자를 입력받아 나눴는데 나눈 결과에 숫자 이외 문자가 들어 있으면  RuntimeException을 던진다.
+


Reviews:
user: login: neojjc2
body: 안녕하세요 희섭님 🙇 

2단계 구현 잘 해주셨습니다 👍 
`method` 분리에 신경을 많이 써주신 부분이 인상적이었습니다. 
소소한 의견 드렸는데, 한번 참고 정도만 해주시면 좋을 것 같네요 😄 

본격 미션이 기대되는데요?? 
그럼 본격 자동차 미션 시작해보시죠

`merge` 하겠습니다 🙇 
state: APPROVED
author_association: NONE
submitted_at: 2025-03-14T04:24:58Z

Review comments:
diff_hunk: @@ -0,0 +1,130 @@
+package calculator;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class DelimitedStringCalculator {
+
+  private static final String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";
+  private static final String DEFAULT_DELIMITER_REGEX = "^[0-9,:]+$";
path: src/main/java/calculator/DelimitedStringCalculator.java
user: login: neojjc2
body: 정적 상수 추출 깔끔하게 잘 해주셨습니다 💯 
created_at: 2025-03-14T04:05:03Z
author_association: NONE
diff_hunk: @@ -0,0 +1,130 @@
+package calculator;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class DelimitedStringCalculator {
+
+  private static final String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";
+  private static final String DEFAULT_DELIMITER_REGEX = "^[0-9,:]+$";
+
+  private DelimitedStringCalculator() {
path: src/main/java/calculator/DelimitedStringCalculator.java
user: login: neojjc2
body: 인스턴스 방지 코드도 좋네요 👍 
아주 소소한데 익셉션 같은거 하나 던져두는 것도 나쁘지 않을 것 같습니다 😄 
사용하는 다른 도메인클래스나, 서비스에게 좀 친절한 것도 나쁘지 않을 것 같아요 🙏 
created_at: 2025-03-14T04:05:51Z
author_association: NONE
diff_hunk: @@ -0,0 +1,130 @@
+package calculator;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class DelimitedStringCalculator {
+
+  private static final String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";
+  private static final String DEFAULT_DELIMITER_REGEX = "^[0-9,:]+$";
+
+  private DelimitedStringCalculator() {
+
+  }
+
+  public static int sum(String input) {
+    if (isNotValidString(input)) {
+      return 0;
+    }
+
+    if (hasCustomDelimiter(input)) {
+      return sumNonNegativeNumberWithCustomDelimiter(input);
+    }
+
+    if (hasDefaultDelimiter(input)) {
+      return sumNonNegativeNumberWithDefaultDelimiter(input);
+    }
+
+    return convertStringToNumber(input);
+  }
+
+  private static boolean isNotValidString(String input) {
+    return input == null || input.isEmpty();
+  }
path: src/main/java/calculator/DelimitedStringCalculator.java
user: login: neojjc2
body: 검증 코드 좋습니다 👍 
created_at: 2025-03-14T04:06:51Z
author_association: NONE
diff_hunk: @@ -0,0 +1,130 @@
+package calculator;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class DelimitedStringCalculator {
+
+  private static final String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";
+  private static final String DEFAULT_DELIMITER_REGEX = "^[0-9,:]+$";
+
+  private DelimitedStringCalculator() {
+
+  }
+
+  public static int sum(String input) {
+    if (isNotValidString(input)) {
+      return 0;
+    }
+
+    if (hasCustomDelimiter(input)) {
+      return sumNonNegativeNumberWithCustomDelimiter(input);
+    }
+
+    if (hasDefaultDelimiter(input)) {
+      return sumNonNegativeNumberWithDefaultDelimiter(input);
+    }
+
+    return convertStringToNumber(input);
+  }
+
+  private static boolean isNotValidString(String input) {
+    return input == null || input.isEmpty();
+  }
+
+  private static boolean hasCustomDelimiter(String input) {
+    Matcher matcher = Pattern.compile(CUSTOM_DELIMITER_REGEX).matcher(input);
+
+    return  matcher.matches();
path: src/main/java/calculator/DelimitedStringCalculator.java
user: login: neojjc2
body: `Pattern.compile(CUSTOM_DELIMITER_REGEX)`를 아예 정적상수로 추출하시는건 어떨까요??
어짜피 `CUSTOM_DELIMITER_REGEX` 값이 변하는 것도 아닐테니 
그리고 `hasCustomDelimiter`가 단순 `matches`의 래핑이라면 살짝 과도한 `method` 추출 같기도 합니다 😄 

요건 개선이라기 보다는 검토 정도 해주시면 될 것 같습니다 🙇 

created_at: 2025-03-14T04:13:54Z
author_association: NONE
diff_hunk: @@ -0,0 +1,130 @@
+package calculator;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class DelimitedStringCalculator {
+
+  private static final String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";
+  private static final String DEFAULT_DELIMITER_REGEX = "^[0-9,:]+$";
+
+  private DelimitedStringCalculator() {
+
+  }
+
+  public static int sum(String input) {
+    if (isNotValidString(input)) {
+      return 0;
+    }
+
+    if (hasCustomDelimiter(input)) {
+      return sumNonNegativeNumberWithCustomDelimiter(input);
+    }
+
+    if (hasDefaultDelimiter(input)) {
+      return sumNonNegativeNumberWithDefaultDelimiter(input);
+    }
+
+    return convertStringToNumber(input);
+  }
+
+  private static boolean isNotValidString(String input) {
+    return input == null || input.isEmpty();
+  }
+
+  private static boolean hasCustomDelimiter(String input) {
+    Matcher matcher = Pattern.compile(CUSTOM_DELIMITER_REGEX).matcher(input);
+
+    return  matcher.matches();
+  }
+
+  private static int sumNonNegativeNumberWithCustomDelimiter(String input) {
+    Matcher m = Pattern.compile(CUSTOM_DELIMITER_REGEX).matcher(input);
+
+    if (m.matches()) {
+      String customDelimiter = m.group(1);
+
+      int[] numbers = convertStringsToNumbers(m.group(2).split(Pattern.quote(customDelimiter)));
+      if (hasNegativeNumber(numbers)) {
+        throw new IllegalArgumentException(String.format(
+            "음수는 연산할 수 없습니다. - %s",
+            input
+        ));
+      }
+
+      return sumNumbers(numbers);
+    }
+
+    throw new IllegalArgumentException(String.format(
+            "커스텀 구분자가 들어가있는 문자열 형태가 아닙니다. - %s",
+            input
+        ));
+  }
+
+  private static boolean hasDefaultDelimiter(String input) {
+    Matcher m = Pattern.compile(DEFAULT_DELIMITER_REGEX).matcher(input);
+
+    return m.find();
+  }
+
+  private static int sumNonNegativeNumberWithDefaultDelimiter(String input) {
+    Matcher m = Pattern.compile(DEFAULT_DELIMITER_REGEX).matcher(input);
+
+    if (!m.matches()) {
+      throw new IllegalArgumentException(String.format(
+          "커스텀 구분자 입력을 받지 않으면 구분자는 \",\" 혹은 \":\" 구분자만 사용 가능합니다. - %s",
+          input
+      ));
+    }
+
+    int[] numbers = convertStringsToNumbers(input.split("[,:]"));
+    if (hasNegativeNumber(numbers)) {
+      throw new IllegalArgumentException(String.format(
+          "음수는 연산할 수 없습니다. - %s",
+          input
+      ));
+    }
+
+    return sumNumbers(numbers);
+  }
+
+  private static boolean hasNegativeNumber(int[] numbers) {
+    for (int number : numbers) {
+      if (number < 0) {
+        return true;
+      }
+    }
+    return false;
+  }
path: src/main/java/calculator/DelimitedStringCalculator.java
user: login: neojjc2
body: 전체적으로 `method`에 단일책임만 부여할 수 있도록 연습하신 것 같네요 😄 
좋습니다 👍 

다만 너무 과도한 `method` 추출은 오히려 가독성을 해치게 될 수도 있습니다. 
이번 과정 진행하시면서 적절한 희섭님만의 수준을 찾아가는것도 
같이 연습해보시면 좋을 것 같네요 😄 

관련해서 저도 계속 의견 나눌 수 있도록 해보겠습니다 🙏 
created_at: 2025-03-14T04:18:19Z
author_association: NONE
diff_hunk: @@ -0,0 +1,107 @@
+package calculator;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DelimitedStringCalculatorTest {
+
+  @DisplayName("빈 문자열이 주어졌을 때 sum()을 호출하면 0을 반환한다.")
+  @Test
+  void sum_emptyString_returnsZero() {
+    String input = "";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(0);
+  }
+
+  @DisplayName("null이 주어졌을 때 sum()을 호출하면 0을 반환한다.")
+  @Test
+  void sum_null_returnZero(){
+    String input = null;
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(0);
+  }
+
+  @DisplayName("구분자가 없이 숫자 하나만 있는 문자열이 주어졌을 때 sum()을 호출하면 입력된 수 그대로 반환한다.")
+  @Test
+  void sum_singleNumberString_returnsSameNumber() {
+    String input = "1";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(1);
+  }
+
+  @DisplayName("\",\"로 구분되는 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_commaSeparatedString_returnsSumOfNumbers() {
+    String input = "1,2,3";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(6);
+  }
+
+  @DisplayName("\":\"로 구분되는 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_colonSeparatedString_returnsSumOfNumbers() {
+    String input = "1:2:3";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(6);
+  }
+
+  @DisplayName("커스컴 구분자를 가지는 형식의 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_customDelimiterString_returnsSumOfNumbers() {
+    String input1 = "//;\n1;2;3";
+    String input2 = "//$\n1$2$4";
+
+    int result1 = DelimitedStringCalculator.sum(input1);
+    int result2 = DelimitedStringCalculator.sum(input2);
+
+    assertThat(result1).isEqualTo(6);
+    assertThat(result2).isEqualTo(7);
+  }
+
+
+  @DisplayName("커스텀 구분자를 입력받은 형태가 아닌데 \",\" 혹은 \":\" 구분자가 아닌 문자열이 주어졌을 떄 sum()을 호출하면 RuntimeException을 던진다.")
+  @Test
+  void sum_invalidDelimiter_throwsRuntimeException() {
+    String input = "1$2";
+
+    Assertions.assertThrows(
+        RuntimeException.class,
+        () -> DelimitedStringCalculator.sum(input)
+    );
+  }
+
+  @DisplayName("음수를 가지는 형식의 문자열이 주어졌을 때 sum()을 호출하면 RuntimeException을 던진다.")
+  @Test
+  void sum_withNegativeValue_throwsRuntimeException() {
+    String input = "-1:2";
+
+    Assertions.assertThrows(
+        RuntimeException.class,
+        () -> DelimitedStringCalculator.sum(input)
+    );
+  }
+
+  @DisplayName("커스텀 구분자를 입력받았을 때 커스텀 구분자랑 숫자 이외 문자열이 주어졌을 때 sum()을 호출하면 RuntimeException을 던진다.")
+  @Test
+  void sum_invalidCharacterWithoutDelimiter_throwsRuntimeException() {
+    String input = "//;\n1;2;3|4";
+
+    Assertions.assertThrows(
+        RuntimeException.class,
+        () -> DelimitedStringCalculator.sum(input)
+    );
+  }
+}
path: src/test/java/calculator/DelimitedStringCalculatorTest.java
user: login: neojjc2
body: 전체적으로 테스트 작성도 잘 해주셨습니다 👍 
created_at: 2025-03-14T04:18:47Z
author_association: NONE
diff_hunk: @@ -0,0 +1,107 @@
+package calculator;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DelimitedStringCalculatorTest {
+
+  @DisplayName("빈 문자열이 주어졌을 때 sum()을 호출하면 0을 반환한다.")
+  @Test
+  void sum_emptyString_returnsZero() {
+    String input = "";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(0);
+  }
+
+  @DisplayName("null이 주어졌을 때 sum()을 호출하면 0을 반환한다.")
+  @Test
+  void sum_null_returnZero(){
+    String input = null;
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(0);
+  }
+
+  @DisplayName("구분자가 없이 숫자 하나만 있는 문자열이 주어졌을 때 sum()을 호출하면 입력된 수 그대로 반환한다.")
+  @Test
+  void sum_singleNumberString_returnsSameNumber() {
+    String input = "1";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(1);
+  }
+
+  @DisplayName("\",\"로 구분되는 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_commaSeparatedString_returnsSumOfNumbers() {
+    String input = "1,2,3";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(6);
+  }
+
+  @DisplayName("\":\"로 구분되는 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_colonSeparatedString_returnsSumOfNumbers() {
+    String input = "1:2:3";
+
+    int result = DelimitedStringCalculator.sum(input);
+
+    assertThat(result).isEqualTo(6);
+  }
+
+  @DisplayName("커스컴 구분자를 가지는 형식의 문자열이 주어졌을 때 sum()을 호출하면 수의 합을 반환한다.")
+  @Test
+  void sum_customDelimiterString_returnsSumOfNumbers() {
+    String input1 = "//;\n1;2;3";
+    String input2 = "//$\n1$2$4";
+
+    int result1 = DelimitedStringCalculator.sum(input1);
+    int result2 = DelimitedStringCalculator.sum(input2);
+
+    assertThat(result1).isEqualTo(6);
+    assertThat(result2).isEqualTo(7);
path: src/test/java/calculator/DelimitedStringCalculatorTest.java
user: login: neojjc2
body: `assertAll`도 같이 챙겨가시면 좋을 것 같습니다 🙇 
> https://zzang9ha.tistory.com/418
created_at: 2025-03-14T04:19:56Z
author_association: NONE
diff_hunk: @@ -0,0 +1,11 @@
+DONE
+- 빈 문자열이 입력되면 0을 반환한다.
+- null이 입력되면 0을 반환한다.
+- 구분자가 없이 숫자 하나이면 그대로 반환한다.
+- "," 로 구분자로 가지는 문자열을 split해서 수를 더한다.
+- ":" 로 구분자로 가지는 문자열을 split해서 수를 더한다.
+- 커스컴 구분자를 입력받아 구분해서 수를 더한다. (ex. //;\n1;2;3” 형식)
+- 커스텀 구분자를 입력받은 형태가 아닌데 "," 혹은 ":" 구분자가 아닌 경우는  RuntimeException을 던진다.
+- 음수를 전달하면 RuntimeException을 던진다.
+- 커스컴 구분자를 입력받아 나눴는데 나눈 결과에 숫자 이외 문자가 들어 있으면  RuntimeException을 던진다.
+
path: todo/step2
user: login: neojjc2
body: 요구사항 정리 👍 
created_at: 2025-03-14T04:20:34Z
author_association: NONE

PR comments:
user: login: neojjc2
created_at: 2025-03-14T04:00:39Z
body: #tag @neojjc2 
author_association: NONE
user: login: neojjc2
created_at: 2025-03-14T04:30:47Z
body: > 리뷰 잘 부탁 드립니다.
> 
> 다음과 같은 내용을 생각하면서 구현했습니다.
> 
> * TDD 개발하기 위해 테스트 코드를 먼저 구현하고 로직 구현 했습니다., commit을 단계별로 나눴습니다.

커밋단위 너무 좋았습니다. 이미 잘 알고 계실 수도 있을 것 같은데, 커밋단위가 적절해야 되돌리기도 `cherry pick` 하기도 좋죠 😄 
다음단계에는 git 관련 요구사항도 있는데요, 함께 연습해보시면 좋을 것 같습니다 🙇 

> * 클래스명을 DelimitedStringCalculator 라고 지어본 이유는 보통 계산기를 하면 사칙연산을 구현하는 식으로 확장해 나가는데 계산기 클래스 안에서 덧셈을 구현하고 추후 확장성을 고려했을 때는 클래스명을 이렇게 짓는 것이 좋을 것 같다고 생각했습니다. (클래스를 하나씩 생성하는 방법도 있지만 계산기 클래스 안에 사칙연산 메서드가 있는 것이 조금 더 깔끔하지 않을까 생각들어서 구현했습니다.)

네, 코드에도 리뷰 드렸지만 그렇게 접근하신 것 같았습니다. 
적절한 혹은 재사용 가능한 것들끼리 묶어서 클래스로 분리하는 방법도 나쁘지 않을 것 같은데, 
구조에 대한 관점은 정답은 없는 부분이어서 같이 미션 진행하시면서 많이 얘기 나눠보는 것도 나쁘지 않을 것 같습니다 🙏 

> * 클래스가 상태를 가지고 있지 않고 유틸리티성 클래스라 판단해서 static 함수로 구현했습니다.

네 좋습니다. 설계의 방향에 적절한 근거가 있다면 존중합니다 💯 

> * 메소드에 많은 논리가 들어가지 않도록 구분하려고 고민해봤습니다.

네 이 부분도 코드에서 소소한 의견드렸는데요, 단일 메소드가 단일 책임을 갖도록 하는 건 너무나도 좋은 구현 습관입니다.
다만 너무 과도한 메소드 추출일 경우 오히려 수정이나, 재사용성을 어렵게 해서 확장성을 떨어뜨리기도 하는데요
요건 같이 연습하시면서 적절한 선을 찾아 나가면 될 것 같습니다 🙇 


author_association: NONE
user: login: gan-ta
created_at: 2025-03-14T05:17:47Z
body: @neojjc2 
꼼꼼하게 리뷰 주셔서 감사합니다  🙇
도움이 많이 되었고 다음 미션도 잘 부탁드립니다~!
author_association: NONE