From 4ee4ba1b517ee93ca1e4afc52db8ebca081fcff6 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Wed, 30 Jul 2025 22:40:48 +0900 Subject: [PATCH 01/28] =?UTF-8?q?feat.=20=EC=82=AC=EC=B9=99=EC=97=B0?= =?UTF-8?q?=EC=82=B0=20=EA=B3=84=EC=82=B0=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Calculator.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/domain/Calculator.java diff --git a/src/main/java/domain/Calculator.java b/src/main/java/domain/Calculator.java new file mode 100644 index 00000000..c1a92aa5 --- /dev/null +++ b/src/main/java/domain/Calculator.java @@ -0,0 +1,19 @@ +package domain; + +public class Calculator { + int add(int num1, int num2) { + return num1 + num2; + } + + int sub(int num1, int num2) { + return num1 - num2; + } + + int mul(int num1, int num2) { + return num1 * num2; + } + + int div(int num1, int num2) { + return num1 / num2; + } +} From 54afa40a1506797062a018b6e135e073fdefb074 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Wed, 30 Jul 2025 22:41:42 +0900 Subject: [PATCH 02/28] =?UTF-8?q?feat.=200=EC=9C=BC=EB=A1=9C=20=EB=82=98?= =?UTF-8?q?=EB=88=84=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EB=B0=9C=EC=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Calculator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/domain/Calculator.java b/src/main/java/domain/Calculator.java index c1a92aa5..cac2536d 100644 --- a/src/main/java/domain/Calculator.java +++ b/src/main/java/domain/Calculator.java @@ -14,6 +14,9 @@ int mul(int num1, int num2) { } int div(int num1, int num2) { + if (num2 == 0) + throw new RuntimeException("0으로 나눌 수 없습니다."); + return num1 / num2; } } From 06502aafff4194e4976b071b24e231f08be74cf5 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Wed, 30 Jul 2025 22:51:02 +0900 Subject: [PATCH 03/28] =?UTF-8?q?fix.=20=EC=97=B0=EC=82=B0=20=ED=9B=84=20i?= =?UTF-8?q?nt=20=EB=B2=94=EC=9C=84=EB=A5=BC=20=EB=B2=97=EC=96=B4=EB=82=98?= =?UTF-8?q?=EB=A9=B4=20=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Calculator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/Calculator.java b/src/main/java/domain/Calculator.java index cac2536d..23f492b6 100644 --- a/src/main/java/domain/Calculator.java +++ b/src/main/java/domain/Calculator.java @@ -2,15 +2,15 @@ public class Calculator { int add(int num1, int num2) { - return num1 + num2; + return Math.addExact(num1, num2); } int sub(int num1, int num2) { - return num1 - num2; + return Math.subtractExact(num1, num2); } int mul(int num1, int num2) { - return num1 * num2; + return Math.multiplyExact(num1, num2); } int div(int num1, int num2) { From faa2f887a6c9f57e952448cfae27c1da4a59dafe Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Wed, 30 Jul 2025 23:00:48 +0900 Subject: [PATCH 04/28] =?UTF-8?q?feat.=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C?= =?UTF-8?q?=EC=96=B4=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Calculator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/domain/Calculator.java b/src/main/java/domain/Calculator.java index 23f492b6..7aae25fd 100644 --- a/src/main/java/domain/Calculator.java +++ b/src/main/java/domain/Calculator.java @@ -1,19 +1,19 @@ package domain; public class Calculator { - int add(int num1, int num2) { + public int add(int num1, int num2) { return Math.addExact(num1, num2); } - int sub(int num1, int num2) { + public int sub(int num1, int num2) { return Math.subtractExact(num1, num2); } - int mul(int num1, int num2) { + public int mul(int num1, int num2) { return Math.multiplyExact(num1, num2); } - int div(int num1, int num2) { + public int div(int num1, int num2) { if (num2 == 0) throw new RuntimeException("0으로 나눌 수 없습니다."); From 6aaeab58aa846749b0c627306514c1b45449cdac Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 18:18:54 +0900 Subject: [PATCH 05/28] =?UTF-8?q?feat.=20=EC=82=AC=EC=B9=99=EC=97=B0?= =?UTF-8?q?=EC=82=B0=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/CalculatorTest.java | 68 +++++++++++-------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/src/test/java/CalculatorTest.java b/src/test/java/CalculatorTest.java index 7c85e553..640de3ae 100644 --- a/src/test/java/CalculatorTest.java +++ b/src/test/java/CalculatorTest.java @@ -1,66 +1,44 @@ -import org.junit.jupiter.api.DisplayName; +import domain.Calculator; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; -@DisplayName("계산기 기능 테스트 클래스") public class CalculatorTest { - @Test - @DisplayName("더하기 연산 테스트") - public void testAdd() { - int num1 = 1, num2 = 2; - - assertThat(3).isEqualTo(Calculator.add(num1, num2)); - } - - @Test - @DisplayName("빼기 연산 테스트") - public void testSub() { - int num1 = 1, num2 = 2; - - assertThat(-1).isEqualTo(Calculator.sub(num1, num2)); - } + private final Calculator calculator = new Calculator(); @Test - @DisplayName("곱하기 연산 테스트") - public void testMul() { - int num1 = 10, num2 = 2; + public void 덧셈_테스트() { + final int num1 = 1; + final int num2 = 2; + final int actual = calculator.add(num1, num2); - assertThat(20).isEqualTo(Calculator.mul(num1, num2)); + assertEquals(num1 + num2, actual); } @Test - @DisplayName("나누기 연산 테스트") - public void testDiv() { - int num1 = 1, num2 = 2; + public void 뺄셈_테스트() { + final int num1 = 3; + final int num2 = 2; + final int actual = calculator.sub(num1, num2); - assertThat(0).isEqualTo(Calculator.div(num1, num2)); + assertEquals(num1 - num2, actual); } @Test - @DisplayName("나누기 연산에서 나누는 값이 0인지 테스트") - public void testDivideByZero() { - int num1 = 2, num2 = 0; + public void 곱셈_테스트() { + final int num1 = 3; + final int num2 = 2; + final int actual = calculator.mul(num1, num2); - assertThatThrownBy(() -> { - Calculator.div(num1, num2); - }).isInstanceOf(ArithmeticException.class); + assertEquals(num1 * num2, actual); } @Test - @DisplayName("연산 후 int 범위를 벗어나는 지 테스트") - public void testOverflow() { - assertThatThrownBy(() -> { - Calculator.add(Integer.MAX_VALUE, 1); - }).isInstanceOf(ArithmeticException.class); - - assertThatThrownBy(() -> { - Calculator.sub(Integer.MIN_VALUE, 1); - }).isInstanceOf(ArithmeticException.class); + public void 나눗셈_테스트() { + final int num1 = 4; + final int num2 = 2; + final int actual = calculator.div(num1, num2); - assertThatThrownBy(() -> { - Calculator.mul(Integer.MAX_VALUE, 2); - }).isInstanceOf(ArithmeticException.class); + assertEquals(num1 / num2, actual); } } From eae23cda2001c80058f726fecdb79ea17424bf10 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Wed, 30 Jul 2025 23:17:17 +0900 Subject: [PATCH 06/28] =?UTF-8?q?fix.=200=EC=9C=BC=EB=A1=9C=20=EB=82=98?= =?UTF-8?q?=EB=88=8C=20=EA=B2=BD=EC=9A=B0=20=EB=B0=9C=EC=83=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=98=88=EC=99=B8=20=EC=A2=85=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Calculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/domain/Calculator.java b/src/main/java/domain/Calculator.java index 7aae25fd..7078bbf4 100644 --- a/src/main/java/domain/Calculator.java +++ b/src/main/java/domain/Calculator.java @@ -15,7 +15,7 @@ public int mul(int num1, int num2) { public int div(int num1, int num2) { if (num2 == 0) - throw new RuntimeException("0으로 나눌 수 없습니다."); + throw new ArithmeticException("0으로 나눌 수 없습니다."); return num1 / num2; } From 0b0bd61f4df7e15cc63afd638b51585a7eb9309a Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Wed, 30 Jul 2025 23:20:04 +0900 Subject: [PATCH 07/28] =?UTF-8?q?feat.=20=EC=82=AC=EC=B9=99=EC=97=B0?= =?UTF-8?q?=EC=82=B0=20=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/CalculatorTest.java | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/test/java/CalculatorTest.java b/src/test/java/CalculatorTest.java index 640de3ae..d6a9c813 100644 --- a/src/test/java/CalculatorTest.java +++ b/src/test/java/CalculatorTest.java @@ -41,4 +41,36 @@ public class CalculatorTest { assertEquals(num1 / num2, actual); } + + @Test + public void 덧셈_오버플로우_예외_발생() { + final int num1 = Integer.MAX_VALUE; + final int num2 = Integer.MAX_VALUE; + + assertThrows(ArithmeticException.class, () -> calculator.add(num1, num2)); + } + + @Test + public void 뺄셈_오버플로우_예외_발생() { + final int num1 = Integer.MIN_VALUE; + final int num2 = Integer.MAX_VALUE; + + assertThrows(ArithmeticException.class, () -> calculator.sub(num1, num2)); + } + + @Test + public void 곱셈_오버플로우_예외_발생() { + final int num1 = Integer.MAX_VALUE; + final int num2 = Integer.MAX_VALUE; + + assertThrows(ArithmeticException.class, () -> calculator.mul(num1, num2)); + } + + @Test + public void 나눗셈_0으로_나누면_예외_발생() { + final int num1 = Integer.MAX_VALUE; + final int num2 = 0; + + assertThrows(ArithmeticException.class, () -> calculator.div(num1, num2)); + } } From 4a76f60a97939f3fec7922752bebd79052708e5b Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 16:14:32 +0900 Subject: [PATCH 08/28] =?UTF-8?q?feat.=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/main/java/domain/StringCalculator.java diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java new file mode 100644 index 00000000..92841ae3 --- /dev/null +++ b/src/main/java/domain/StringCalculator.java @@ -0,0 +1,68 @@ +package domain; + +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +public class StringCalculator { + private final String delimeterRegex = "[,|:]"; + + public int calculate(String str) { + String[] tokens; + String customDelimeter = findCustomDelimeter(str); + + if (str == null || str.isBlank()) { + throw new RuntimeException("문자열이 비어있습니다."); + } + + if (customDelimeter == null) { + tokens = str.split(delimeterRegex); + } else { + tokens = str.substring(str.indexOf("\n") + 1) + .split(Pattern.quote(customDelimeter)); + } + + List numbers = parseNumber(tokens); + + return add(numbers); + } + + public String findCustomDelimeter(String str) { + int startDelimeterIdx = str.indexOf("//"); + int endDelimeterIdx = str.indexOf("\n"); + + if (startDelimeterIdx == -1 && endDelimeterIdx == -1) { + return null; + } + + if ((startDelimeterIdx == -1) ^ (endDelimeterIdx == -1) || (startDelimeterIdx + 2 == endDelimeterIdx)) { + throw new RuntimeException("커스텀 구분자를 찾을 수 없습니다."); + } + + return str.substring(startDelimeterIdx + 2, endDelimeterIdx); + } + + public List parseNumber(String[] tokens) { + return Arrays.stream(tokens) + .map(token -> { + try { + int number = Integer.parseInt(token.trim()); + + if (number < 0) { + throw new RuntimeException("음수는 처리할 수 없습니다."); + } + + return number; + } catch (NumberFormatException ex) { + throw new RuntimeException("문자열은 처리할 수 없습니다."); + } + }) + .toList(); + } + + public int add(List numbers) { + return numbers.stream() + .mapToInt(Integer::intValue) + .sum(); + } +} From 4b08af298b4bccabad2ea06b24ce87c51c39b7e8 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 18:21:08 +0900 Subject: [PATCH 09/28] =?UTF-8?q?feat.=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=EA=B8=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/StringCalculatorTest.java | 104 ++++++++++++++++++------ 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/src/test/java/StringCalculatorTest.java b/src/test/java/StringCalculatorTest.java index eb350cf3..cfe85649 100644 --- a/src/test/java/StringCalculatorTest.java +++ b/src/test/java/StringCalculatorTest.java @@ -1,49 +1,99 @@ -import org.junit.jupiter.api.DisplayName; +import domain.StringCalculator; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class StringCalculatorTest { + private final StringCalculator stringCalculator = new StringCalculator(); + @ParameterizedTest - @DisplayName("문자열 계산기 기능 테스트") - @ValueSource(strings = {"1:2:3", "1,2,3", "3:2,1", "3,2:1"}) - public void testDefaultDelimeter(String str) { - assertThat(StringCalculator.calculate(str)).isEqualTo(6); + @ValueSource(strings = {"1,2,3:4", "1 , 2: 3,4 "}) + public void 기본_구분자_문자열_계산기_테스트(String str) { + int actual = stringCalculator.calculate(str); + + assertEquals(10, actual); } @ParameterizedTest - @ValueSource(strings = {"", " ", " "}) - @DisplayName("공백을 줬을 경우 테스트") - public void testEmptyValue(String value) { - assertThat(StringCalculator.calculate(value)).isEqualTo(0); + @CsvSource({ + " '//+\n', '+' ", + " '// \n', ' ' ", + " '//||\n', '||' " + }) + public void 커스텀_구분자_파싱_테스트(String str, String expected) { + String customDelimeter = stringCalculator.findCustomDelimeter(str); + + assertEquals(expected, customDelimeter); + } + + @Test + public void 문자열_리스트_정수_리스트로_변환() { + String[] tokens = {"1", "2", "3", "4"}; + + List actual = stringCalculator.parseNumber(tokens); + + assertEquals(List.of(1, 2, 3, 4), actual); + } + + @Test + public void 문자열_리스트_정수_리스트로_변환_음수가_포함되면_예외_발생() { + String[] tokens = {"1", "2", "-3", "4"}; + + assertThrows(RuntimeException.class, () -> stringCalculator.parseNumber(tokens)); + } + + @Test + public void 문자열_리스트_정수_리스트로_변환_문자가_포함되면_예외_발생() { + String[] tokens = {"a1", "b", " ", "4"}; + + assertThrows(RuntimeException.class, () -> stringCalculator.parseNumber(tokens)); } @ParameterizedTest - @ValueSource(strings = {"//;\n1;2;3", "//!!\n1!!2!!3"}) - @DisplayName("커스텀 구분자 테스트") - public void testCustomDelimeter(String value) { - assertThat(StringCalculator.calculate(value)).isEqualTo(6); + @ValueSource(strings = { + "//+\n1+2+3+4", + "// \n1 2 3 4", + "//||\n1||2||3||4" + }) + public void 커스텀_구분자_문자열_계산기_테스트(String str) { + int actual = stringCalculator.calculate(str); + + assertEquals(10, actual); } @ParameterizedTest - @ValueSource(strings = {"/;\n1;2;3", "/;\t1;2;3", "//;;\n1;2;3", "// n1 2 3"}) - @DisplayName("커스텀 구분자 형식에 맞지 않을 경우 예외 발생 테스트") - public void testCustomDelimeterFormatError(String value) { - assertThatThrownBy(() -> StringCalculator.calculate(value)).isInstanceOf(RuntimeException.class); + @ValueSource(strings = { + "//1;2;3;4", + "\n1;2;3;4", + "//\n1;2;3;4" + }) + public void 커스텀_구분자_형식에_맞지_않은_경우_예외_발생(String str) { + assertThrows(RuntimeException.class, () -> stringCalculator.calculate(str)); } - @Test - @DisplayName("커스텀 구분자에 아무것도 주지 않은 상황 테스트") - public void testEmptyCustomDelimeter() { - assertThatThrownBy(() -> StringCalculator.calculate("//\n1;2;3")).isInstanceOf(RuntimeException.class); + @ParameterizedTest + @NullSource + @ValueSource(strings = { + "", + " " + }) + public void 문자열이_비어있는_경우_예외_발생(String str) { + assertThrows(RuntimeException.class, () -> stringCalculator.calculate(str)); } @Test - @DisplayName("계산 결과가 int 범위를 벗어나는 경우 예외 발생 테스트") - public void testReusltOverflow() { - assertThatThrownBy(() -> StringCalculator.calculate("2147483647:2:3")).isInstanceOf(ArithmeticException.class); + public void 구분자_없이_숫자만_입력한_경우() { + String str = "1"; + int actual = stringCalculator.calculate(str); + + assertEquals(1, actual); } -} \ No newline at end of file +} From 11d80e0a5d0052108a545b92776956586ff53eb1 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 18:21:44 +0900 Subject: [PATCH 10/28] =?UTF-8?q?build.=20AssertJ=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index f6efa25b..509ac291 100644 --- a/build.gradle +++ b/build.gradle @@ -10,11 +10,10 @@ repositories { } dependencies { - testImplementation platform('org.junit:junit-bom:5.9.1') - testImplementation('org.junit.jupiter:junit-jupiter') testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation platform('org.assertj:assertj-bom:3.25.1') testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation('org.assertj:assertj-core') } From 682929adb3d371d5fc8781ba11e7ba31a5d2f0a2 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 16:29:28 +0900 Subject: [PATCH 11/28] =?UTF-8?q?refactor.=20=EC=82=AC=EC=B9=99=EC=97=B0?= =?UTF-8?q?=EC=82=B0=20=EA=B3=84=EC=82=B0=EA=B8=B0=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20JUnit=20->=20AssertJ=EB=A1=9C?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/CalculatorTest.java | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/test/java/CalculatorTest.java b/src/test/java/CalculatorTest.java index d6a9c813..1cf9eab4 100644 --- a/src/test/java/CalculatorTest.java +++ b/src/test/java/CalculatorTest.java @@ -1,7 +1,7 @@ import domain.Calculator; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.*; public class CalculatorTest { private final Calculator calculator = new Calculator(); @@ -12,7 +12,7 @@ public class CalculatorTest { final int num2 = 2; final int actual = calculator.add(num1, num2); - assertEquals(num1 + num2, actual); + assertThat(actual).isEqualTo(num1 + num2); } @Test @@ -21,7 +21,7 @@ public class CalculatorTest { final int num2 = 2; final int actual = calculator.sub(num1, num2); - assertEquals(num1 - num2, actual); + assertThat(actual).isEqualTo(num1 - num2); } @Test @@ -30,7 +30,7 @@ public class CalculatorTest { final int num2 = 2; final int actual = calculator.mul(num1, num2); - assertEquals(num1 * num2, actual); + assertThat(actual).isEqualTo(num1 * num2); } @Test @@ -39,7 +39,7 @@ public class CalculatorTest { final int num2 = 2; final int actual = calculator.div(num1, num2); - assertEquals(num1 / num2, actual); + assertThat(actual).isEqualTo(num1 / num2); } @Test @@ -47,7 +47,8 @@ public class CalculatorTest { final int num1 = Integer.MAX_VALUE; final int num2 = Integer.MAX_VALUE; - assertThrows(ArithmeticException.class, () -> calculator.add(num1, num2)); + assertThatThrownBy(() -> calculator.add(num1, num2)) + .isInstanceOf(ArithmeticException.class); } @Test @@ -55,7 +56,8 @@ public class CalculatorTest { final int num1 = Integer.MIN_VALUE; final int num2 = Integer.MAX_VALUE; - assertThrows(ArithmeticException.class, () -> calculator.sub(num1, num2)); + assertThatThrownBy(() -> calculator.sub(num1, num2)) + .isInstanceOf(ArithmeticException.class); } @Test @@ -63,7 +65,8 @@ public class CalculatorTest { final int num1 = Integer.MAX_VALUE; final int num2 = Integer.MAX_VALUE; - assertThrows(ArithmeticException.class, () -> calculator.mul(num1, num2)); + assertThatThrownBy(() -> calculator.mul(num1, num2)) + .isInstanceOf(ArithmeticException.class); } @Test @@ -71,6 +74,8 @@ public class CalculatorTest { final int num1 = Integer.MAX_VALUE; final int num2 = 0; - assertThrows(ArithmeticException.class, () -> calculator.div(num1, num2)); + assertThatThrownBy(() -> calculator.div(num1, num2)) + .isInstanceOf(ArithmeticException.class) + .hasMessageContaining("0으로 나눌 수 없습니다."); } } From 65e7e2cbcd42e2abf534f2c6583b65ff7161d846 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 16:36:56 +0900 Subject: [PATCH 12/28] =?UTF-8?q?fix.=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=EC=9D=B4=20=EB=B9=84=EC=96=B4=EC=9E=88=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=ED=95=98=EA=B8=B0=20=EC=A0=84=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EA=B5=AC=EB=B6=84=EC=9E=90=20=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=EC=9C=BC=EB=A1=9C=20=EB=B0=9C=EC=83=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index 92841ae3..e346406e 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -9,12 +9,14 @@ public class StringCalculator { public int calculate(String str) { String[] tokens; - String customDelimeter = findCustomDelimeter(str); + String customDelimeter; if (str == null || str.isBlank()) { throw new RuntimeException("문자열이 비어있습니다."); } + customDelimeter = findCustomDelimeter(str); + if (customDelimeter == null) { tokens = str.split(delimeterRegex); } else { From 98b0f3f1c40ef76ec49f15ee27f9166fcbe2b36f Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 16:43:52 +0900 Subject: [PATCH 13/28] =?UTF-8?q?refactor.=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=20=EA=B3=84=EC=82=B0=EA=B8=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20JUnit=20->=20AssertJ=EB=A1=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/StringCalculatorTest.java | 41 +++++++++++++++---------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/test/java/StringCalculatorTest.java b/src/test/java/StringCalculatorTest.java index cfe85649..faab1362 100644 --- a/src/test/java/StringCalculatorTest.java +++ b/src/test/java/StringCalculatorTest.java @@ -1,5 +1,4 @@ import domain.StringCalculator; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -8,30 +7,32 @@ import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.assertj.core.api.Assertions.*; public class StringCalculatorTest { private final StringCalculator stringCalculator = new StringCalculator(); @ParameterizedTest - @ValueSource(strings = {"1,2,3:4", "1 , 2: 3,4 "}) + @ValueSource(strings = { + "1,2,3:4", + "1 , 2: 3,4 " + }) public void 기본_구분자_문자열_계산기_테스트(String str) { int actual = stringCalculator.calculate(str); - assertEquals(10, actual); + assertThat(actual).isEqualTo(10); } @ParameterizedTest @CsvSource({ - " '//+\n', '+' ", - " '// \n', ' ' ", - " '//||\n', '||' " + "'//+\n', '+'", + "'// \n', ' '", + "'//||\n', '||'" }) public void 커스텀_구분자_파싱_테스트(String str, String expected) { String customDelimeter = stringCalculator.findCustomDelimeter(str); - assertEquals(expected, customDelimeter); + assertThat(customDelimeter).isEqualTo(expected); } @Test @@ -40,21 +41,25 @@ public class StringCalculatorTest { List actual = stringCalculator.parseNumber(tokens); - assertEquals(List.of(1, 2, 3, 4), actual); + assertThat(actual).containsExactly(1, 2, 3, 4); } @Test public void 문자열_리스트_정수_리스트로_변환_음수가_포함되면_예외_발생() { String[] tokens = {"1", "2", "-3", "4"}; - assertThrows(RuntimeException.class, () -> stringCalculator.parseNumber(tokens)); + assertThatThrownBy(() -> stringCalculator.parseNumber(tokens)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("음수는 처리할 수 없습니다."); } @Test public void 문자열_리스트_정수_리스트로_변환_문자가_포함되면_예외_발생() { String[] tokens = {"a1", "b", " ", "4"}; - assertThrows(RuntimeException.class, () -> stringCalculator.parseNumber(tokens)); + assertThatThrownBy(() -> stringCalculator.parseNumber(tokens)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("문자열은 처리할 수 없습니다."); } @ParameterizedTest @@ -66,7 +71,7 @@ public class StringCalculatorTest { public void 커스텀_구분자_문자열_계산기_테스트(String str) { int actual = stringCalculator.calculate(str); - assertEquals(10, actual); + assertThat(actual).isEqualTo(10); } @ParameterizedTest @@ -76,7 +81,9 @@ public class StringCalculatorTest { "//\n1;2;3;4" }) public void 커스텀_구분자_형식에_맞지_않은_경우_예외_발생(String str) { - assertThrows(RuntimeException.class, () -> stringCalculator.calculate(str)); + assertThatThrownBy(() -> stringCalculator.calculate(str)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("커스텀 구분자를 찾을 수 없습니다."); } @ParameterizedTest @@ -86,7 +93,9 @@ public class StringCalculatorTest { " " }) public void 문자열이_비어있는_경우_예외_발생(String str) { - assertThrows(RuntimeException.class, () -> stringCalculator.calculate(str)); + assertThatThrownBy(() -> stringCalculator.calculate(str)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("문자열이 비어있습니다."); } @Test @@ -94,6 +103,6 @@ public class StringCalculatorTest { String str = "1"; int actual = stringCalculator.calculate(str); - assertEquals(1, actual); + assertThat(actual).isEqualTo(1); } } From 239431939606fadb50c4d1c88f9003eb5ef3f4c9 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 16:57:15 +0900 Subject: [PATCH 14/28] =?UTF-8?q?refactor.=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=EB=A5=BC=20=ED=95=98=EB=93=9C=20?= =?UTF-8?q?=EC=BD=94=EB=94=A9=20=EB=B0=A9=EC=8B=9D=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EB=B3=80=EC=88=98=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Calculator.java | 4 +++- src/main/java/domain/StringCalculator.java | 12 ++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/domain/Calculator.java b/src/main/java/domain/Calculator.java index 7078bbf4..d5397834 100644 --- a/src/main/java/domain/Calculator.java +++ b/src/main/java/domain/Calculator.java @@ -1,6 +1,8 @@ package domain; public class Calculator { + public static final String DIVIDE_BY_ZERO = "0으로 나눌 수 없습니다."; + public int add(int num1, int num2) { return Math.addExact(num1, num2); } @@ -15,7 +17,7 @@ public int mul(int num1, int num2) { public int div(int num1, int num2) { if (num2 == 0) - throw new ArithmeticException("0으로 나눌 수 없습니다."); + throw new ArithmeticException(DIVIDE_BY_ZERO); return num1 / num2; } diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index e346406e..ed849ad1 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -5,6 +5,10 @@ import java.util.regex.Pattern; public class StringCalculator { + public static final String EMPTY_STRING = "문자열이 비어있습니다."; + public static final String CUSTOM_DELIMITER_NOT_FOUND = "커스텀 구분자를 찾을 수 없습니다."; + public static final String NEGATIVE_NUMBER_NOT_ALLOWED = "음수는 처리할 수 없습니다."; + public static final String INVALID_STRING = "문자열은 처리할 수 없습니다."; private final String delimeterRegex = "[,|:]"; public int calculate(String str) { @@ -12,7 +16,7 @@ public int calculate(String str) { String customDelimeter; if (str == null || str.isBlank()) { - throw new RuntimeException("문자열이 비어있습니다."); + throw new RuntimeException(EMPTY_STRING); } customDelimeter = findCustomDelimeter(str); @@ -38,7 +42,7 @@ public String findCustomDelimeter(String str) { } if ((startDelimeterIdx == -1) ^ (endDelimeterIdx == -1) || (startDelimeterIdx + 2 == endDelimeterIdx)) { - throw new RuntimeException("커스텀 구분자를 찾을 수 없습니다."); + throw new RuntimeException(CUSTOM_DELIMITER_NOT_FOUND); } return str.substring(startDelimeterIdx + 2, endDelimeterIdx); @@ -51,12 +55,12 @@ public List parseNumber(String[] tokens) { int number = Integer.parseInt(token.trim()); if (number < 0) { - throw new RuntimeException("음수는 처리할 수 없습니다."); + throw new RuntimeException(NEGATIVE_NUMBER_NOT_ALLOWED); } return number; } catch (NumberFormatException ex) { - throw new RuntimeException("문자열은 처리할 수 없습니다."); + throw new RuntimeException(INVALID_STRING); } }) .toList(); From 6546a24afc7c38b65f6859b6e530293be4af2b4b Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 16:57:31 +0900 Subject: [PATCH 15/28] =?UTF-8?q?refactor.=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=ED=8F=AC=ED=95=A8=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=8B=9C=20=EC=83=81=EC=88=98=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=20=ED=99=9C=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/CalculatorTest.java | 2 +- src/test/java/StringCalculatorTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/CalculatorTest.java b/src/test/java/CalculatorTest.java index 1cf9eab4..ff4fd727 100644 --- a/src/test/java/CalculatorTest.java +++ b/src/test/java/CalculatorTest.java @@ -76,6 +76,6 @@ public class CalculatorTest { assertThatThrownBy(() -> calculator.div(num1, num2)) .isInstanceOf(ArithmeticException.class) - .hasMessageContaining("0으로 나눌 수 없습니다."); + .hasMessageContaining(Calculator.DIVIDE_BY_ZERO); } } diff --git a/src/test/java/StringCalculatorTest.java b/src/test/java/StringCalculatorTest.java index faab1362..3153578e 100644 --- a/src/test/java/StringCalculatorTest.java +++ b/src/test/java/StringCalculatorTest.java @@ -50,7 +50,7 @@ public class StringCalculatorTest { assertThatThrownBy(() -> stringCalculator.parseNumber(tokens)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining("음수는 처리할 수 없습니다."); + .hasMessageContaining(StringCalculator.NEGATIVE_NUMBER_NOT_ALLOWED); } @Test @@ -59,7 +59,7 @@ public class StringCalculatorTest { assertThatThrownBy(() -> stringCalculator.parseNumber(tokens)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining("문자열은 처리할 수 없습니다."); + .hasMessageContaining(StringCalculator.INVALID_STRING); } @ParameterizedTest @@ -83,7 +83,7 @@ public class StringCalculatorTest { public void 커스텀_구분자_형식에_맞지_않은_경우_예외_발생(String str) { assertThatThrownBy(() -> stringCalculator.calculate(str)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining("커스텀 구분자를 찾을 수 없습니다."); + .hasMessageContaining(StringCalculator.CUSTOM_DELIMITER_NOT_FOUND); } @ParameterizedTest @@ -95,7 +95,7 @@ public class StringCalculatorTest { public void 문자열이_비어있는_경우_예외_발생(String str) { assertThatThrownBy(() -> stringCalculator.calculate(str)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining("문자열이 비어있습니다."); + .hasMessageContaining(StringCalculator.EMPTY_STRING); } @Test From 335766688de3e7589109b6caf772e58651ee4351 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 17:15:57 +0900 Subject: [PATCH 16/28] =?UTF-8?q?style.=20=EA=B0=9C=ED=96=89=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=A4=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Calculator.java | 1 + src/main/java/domain/StringCalculator.java | 1 + src/test/java/CalculatorTest.java | 1 + src/test/java/StringCalculatorTest.java | 1 + 4 files changed, 4 insertions(+) diff --git a/src/main/java/domain/Calculator.java b/src/main/java/domain/Calculator.java index d5397834..3429e69b 100644 --- a/src/main/java/domain/Calculator.java +++ b/src/main/java/domain/Calculator.java @@ -1,6 +1,7 @@ package domain; public class Calculator { + public static final String DIVIDE_BY_ZERO = "0으로 나눌 수 없습니다."; public int add(int num1, int num2) { diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index ed849ad1..1894cee2 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -5,6 +5,7 @@ import java.util.regex.Pattern; public class StringCalculator { + public static final String EMPTY_STRING = "문자열이 비어있습니다."; public static final String CUSTOM_DELIMITER_NOT_FOUND = "커스텀 구분자를 찾을 수 없습니다."; public static final String NEGATIVE_NUMBER_NOT_ALLOWED = "음수는 처리할 수 없습니다."; diff --git a/src/test/java/CalculatorTest.java b/src/test/java/CalculatorTest.java index ff4fd727..721177ca 100644 --- a/src/test/java/CalculatorTest.java +++ b/src/test/java/CalculatorTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.*; public class CalculatorTest { + private final Calculator calculator = new Calculator(); @Test diff --git a/src/test/java/StringCalculatorTest.java b/src/test/java/StringCalculatorTest.java index 3153578e..e442b286 100644 --- a/src/test/java/StringCalculatorTest.java +++ b/src/test/java/StringCalculatorTest.java @@ -10,6 +10,7 @@ import static org.assertj.core.api.Assertions.*; public class StringCalculatorTest { + private final StringCalculator stringCalculator = new StringCalculator(); @ParameterizedTest From 6e1cc5422532c6a6fbc8fd6ae359c3766814cc24 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 17:32:21 +0900 Subject: [PATCH 17/28] =?UTF-8?q?refactor.=20=EB=A9=94=EC=86=8C=EB=93=9C?= =?UTF-8?q?=20=EC=B1=85=EC=9E=84=20=EB=B6=84=ED=95=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 59 +++++++++++++--------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index 1894cee2..f30c13fd 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -13,22 +13,13 @@ public class StringCalculator { private final String delimeterRegex = "[,|:]"; public int calculate(String str) { - String[] tokens; - String customDelimeter; - if (str == null || str.isBlank()) { throw new RuntimeException(EMPTY_STRING); } - customDelimeter = findCustomDelimeter(str); - - if (customDelimeter == null) { - tokens = str.split(delimeterRegex); - } else { - tokens = str.substring(str.indexOf("\n") + 1) - .split(Pattern.quote(customDelimeter)); - } - + String customDelimeter = findCustomDelimeter(str); + String strNumbers = extractNumber(str, customDelimeter); + String[] tokens = splitTokens(strNumbers, customDelimeter); List numbers = parseNumber(tokens); return add(numbers); @@ -49,24 +40,42 @@ public String findCustomDelimeter(String str) { return str.substring(startDelimeterIdx + 2, endDelimeterIdx); } + public String extractNumber(String str, String customDelimeter) { + if (customDelimeter != null) { + return str.substring(str.indexOf("\n") + 1); + } + + return str; + } + + public String[] splitTokens(String strNumbers, String customDelimeter) { + if (customDelimeter == null) { + return strNumbers.split(delimeterRegex); + } + + return strNumbers.split(Pattern.quote(customDelimeter)); + } + public List parseNumber(String[] tokens) { return Arrays.stream(tokens) - .map(token -> { - try { - int number = Integer.parseInt(token.trim()); - - if (number < 0) { - throw new RuntimeException(NEGATIVE_NUMBER_NOT_ALLOWED); - } - - return number; - } catch (NumberFormatException ex) { - throw new RuntimeException(INVALID_STRING); - } - }) + .map(this::parseTokenToInt) .toList(); } + public int parseTokenToInt(String token) { + try { + int number = Integer.parseInt(token.trim()); + + if (number < 0) { + throw new RuntimeException(NEGATIVE_NUMBER_NOT_ALLOWED); + } + + return number; + } catch (NumberFormatException ex) { + throw new RuntimeException(INVALID_STRING); + } + } + public int add(List numbers) { return numbers.stream() .mapToInt(Integer::intValue) From 015db6f4f57d61122a08292b02e1aca7154dee66 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 17:34:49 +0900 Subject: [PATCH 18/28] =?UTF-8?q?fix.=20=EB=8B=A8=EC=96=B4=20=EC=B2=A0?= =?UTF-8?q?=EC=9E=90=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 32 +++++++++++----------- src/test/java/StringCalculatorTest.java | 4 +-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index f30c13fd..253267a2 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -10,50 +10,50 @@ public class StringCalculator { public static final String CUSTOM_DELIMITER_NOT_FOUND = "커스텀 구분자를 찾을 수 없습니다."; public static final String NEGATIVE_NUMBER_NOT_ALLOWED = "음수는 처리할 수 없습니다."; public static final String INVALID_STRING = "문자열은 처리할 수 없습니다."; - private final String delimeterRegex = "[,|:]"; + private final String delimiterRegex = "[,|:]"; public int calculate(String str) { if (str == null || str.isBlank()) { throw new RuntimeException(EMPTY_STRING); } - String customDelimeter = findCustomDelimeter(str); - String strNumbers = extractNumber(str, customDelimeter); - String[] tokens = splitTokens(strNumbers, customDelimeter); + String customDelimiter = findCustomDelimiter(str); + String strNumbers = extractNumber(str, customDelimiter); + String[] tokens = splitTokens(strNumbers, customDelimiter); List numbers = parseNumber(tokens); return add(numbers); } - public String findCustomDelimeter(String str) { - int startDelimeterIdx = str.indexOf("//"); - int endDelimeterIdx = str.indexOf("\n"); + public String findCustomDelimiter(String str) { + int startDelimiterIdx = str.indexOf("//"); + int endDelimiterIdx = str.indexOf("\n"); - if (startDelimeterIdx == -1 && endDelimeterIdx == -1) { + if (startDelimiterIdx == -1 && endDelimiterIdx == -1) { return null; } - if ((startDelimeterIdx == -1) ^ (endDelimeterIdx == -1) || (startDelimeterIdx + 2 == endDelimeterIdx)) { + if ((startDelimiterIdx == -1) ^ (endDelimiterIdx == -1) || (startDelimiterIdx + 2 == endDelimiterIdx)) { throw new RuntimeException(CUSTOM_DELIMITER_NOT_FOUND); } - return str.substring(startDelimeterIdx + 2, endDelimeterIdx); + return str.substring(startDelimiterIdx + 2, endDelimiterIdx); } - public String extractNumber(String str, String customDelimeter) { - if (customDelimeter != null) { + public String extractNumber(String str, String customDelimiter) { + if (customDelimiter != null) { return str.substring(str.indexOf("\n") + 1); } return str; } - public String[] splitTokens(String strNumbers, String customDelimeter) { - if (customDelimeter == null) { - return strNumbers.split(delimeterRegex); + public String[] splitTokens(String strNumbers, String customDelimiter) { + if (customDelimiter == null) { + return strNumbers.split(delimiterRegex); } - return strNumbers.split(Pattern.quote(customDelimeter)); + return strNumbers.split(Pattern.quote(customDelimiter)); } public List parseNumber(String[] tokens) { diff --git a/src/test/java/StringCalculatorTest.java b/src/test/java/StringCalculatorTest.java index e442b286..34059f80 100644 --- a/src/test/java/StringCalculatorTest.java +++ b/src/test/java/StringCalculatorTest.java @@ -31,9 +31,9 @@ public class StringCalculatorTest { "'//||\n', '||'" }) public void 커스텀_구분자_파싱_테스트(String str, String expected) { - String customDelimeter = stringCalculator.findCustomDelimeter(str); + String customDelimiter = stringCalculator.findCustomDelimiter(str); - assertThat(customDelimeter).isEqualTo(expected); + assertThat(customDelimiter).isEqualTo(expected); } @Test From 98575d9ace8df70172269164909a4eefb19e8a0e Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Thu, 31 Jul 2025 17:50:04 +0900 Subject: [PATCH 19/28] =?UTF-8?q?fix.=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index 253267a2..9dbe023e 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -22,7 +22,7 @@ public int calculate(String str) { String[] tokens = splitTokens(strNumbers, customDelimiter); List numbers = parseNumber(tokens); - return add(numbers); + return sum(numbers); } public String findCustomDelimiter(String str) { @@ -76,7 +76,7 @@ public int parseTokenToInt(String token) { } } - public int add(List numbers) { + public int sum(List numbers) { return numbers.stream() .mapToInt(Integer::intValue) .sum(); From 32da58deb4d32dff4e0027181a3cb1d303a2cae6 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 09:50:12 +0900 Subject: [PATCH 20/28] =?UTF-8?q?chore.=20=EC=9D=B4=EC=A0=84=EC=97=90=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=ED=95=9C=20=EA=B3=84=EC=82=B0=EA=B8=B0=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Calculator.java | 31 --------------- src/main/java/StringCalculator.java | 58 ----------------------------- 2 files changed, 89 deletions(-) delete mode 100644 src/main/java/Calculator.java delete mode 100644 src/main/java/StringCalculator.java diff --git a/src/main/java/Calculator.java b/src/main/java/Calculator.java deleted file mode 100644 index 6b06ec43..00000000 --- a/src/main/java/Calculator.java +++ /dev/null @@ -1,31 +0,0 @@ -import java.util.function.BiFunction; - -public class Calculator { - public static int calculate(BiFunction func, int num1, int num2) { - try { - return func.apply(num1, num2); - } catch (ArithmeticException e) { - throw new ArithmeticException("연산 결과가 자료형 int의 범위를 벗어났습니다."); - } - } - - public static int add(int num1, int num2) { - return calculate(Math::addExact, num1, num2); - } - - public static int sub(int num1, int num2) { - return calculate(Math::subtractExact, num1, num2); - } - - public static int mul(int num1, int num2) { - return calculate(Math::multiplyExact, num1, num2); - } - - public static int div(int num1, int num2) { - if (num2 == 0) { - throw new ArithmeticException("0으로 나눌 수 없습니다."); - } - - return num1 / num2; - } -} \ No newline at end of file diff --git a/src/main/java/StringCalculator.java b/src/main/java/StringCalculator.java deleted file mode 100644 index 4dfc37b5..00000000 --- a/src/main/java/StringCalculator.java +++ /dev/null @@ -1,58 +0,0 @@ -public class StringCalculator { - public static int calculate(String str) { - String delimeter = "[,|:]"; - String[] tokens; - int res = 0; - - if (str == null || str.isBlank()) { - return 0; - } - - String customDelimter = findCustomDelimeter(str); - - if (customDelimter != null) { - delimeter = customDelimter; - tokens = str.substring(str.indexOf("\n") + 1).split(delimeter); - } - - else { - tokens = str.split(delimeter); - } - - return sum(tokens); - } - - public static String findCustomDelimeter(String str) { - int idx = 0; - int startIdx = str.indexOf("//"); - int endIdx = str.indexOf("\n"); - - if ((startIdx == -1) != (endIdx == -1) || (startIdx + 2 == endIdx)) { // 커스텀 구분자 형식인 // 과 \n 중 하나만 존재하거나 구분자가 없는 경우 - throw new RuntimeException("커스텀 구분자 형식에 맞지 않는 문자열을 전달했습니다."); - } - - else if ((startIdx != -1)) { // 커스텀 구분자 형식인 경우 - return str.substring(startIdx + 2, endIdx); - } - - else { // 디폴트 구분자 형식인 경우 - return null; - } - } - - public static int sum(String[] tokens) { - int res = 0; - - for (String token : tokens) { - try { - res = Math.addExact(res, Integer.parseInt(token)); - } catch (NumberFormatException e) { - throw new RuntimeException("숫자 이외의 값 또는 음수를 전달할 수 없습니다."); - } catch (ArithmeticException e) { - throw new ArithmeticException("연산 결과가 자료형 int의 범위를 벗어났습니다."); - } - } - - return res; - } -} From fa3592251f0ea9b821673b6bccec27d030ce19af Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 10:32:47 +0900 Subject: [PATCH 21/28] =?UTF-8?q?fix.=20=EA=B3=B5=EB=B0=B1=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=EC=9D=B4=20=EB=93=A4=EC=96=B4=EC=98=A8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EA=B3=84=EC=82=B0=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?0=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index 9dbe023e..bd810426 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -6,17 +6,20 @@ public class StringCalculator { - public static final String EMPTY_STRING = "문자열이 비어있습니다."; + public static final String NULL_STRING = "문자열에 null값이 전달되었습니다."; public static final String CUSTOM_DELIMITER_NOT_FOUND = "커스텀 구분자를 찾을 수 없습니다."; public static final String NEGATIVE_NUMBER_NOT_ALLOWED = "음수는 처리할 수 없습니다."; public static final String INVALID_STRING = "문자열은 처리할 수 없습니다."; private final String delimiterRegex = "[,|:]"; public int calculate(String str) { - if (str == null || str.isBlank()) { - throw new RuntimeException(EMPTY_STRING); + if (str == null) { + throw new RuntimeException(NULL_STRING); } + if (str.isBlank()) + return 0; + String customDelimiter = findCustomDelimiter(str); String strNumbers = extractNumber(str, customDelimiter); String[] tokens = splitTokens(strNumbers, customDelimiter); From f7824c7e1b9d430d79d34a8cc9f627581f021ed9 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 10:51:06 +0900 Subject: [PATCH 22/28] =?UTF-8?q?fix.=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=EB=A1=9C=20=EA=B3=B5=EB=B0=B1=EC=9D=B4=20=EB=93=A4=EC=96=B4?= =?UTF-8?q?=EC=98=A8=20=EA=B2=BD=EC=9A=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B6=84=ED=95=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/StringCalculatorTest.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/test/java/StringCalculatorTest.java b/src/test/java/StringCalculatorTest.java index 34059f80..b2eb354c 100644 --- a/src/test/java/StringCalculatorTest.java +++ b/src/test/java/StringCalculatorTest.java @@ -89,14 +89,21 @@ public class StringCalculatorTest { @ParameterizedTest @NullSource - @ValueSource(strings = { - "", - " " - }) - public void 문자열이_비어있는_경우_예외_발생(String str) { + public void 문자열이_널값인_경우_예외_발생(String str) { assertThatThrownBy(() -> stringCalculator.calculate(str)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining(StringCalculator.EMPTY_STRING); + .hasMessageContaining(StringCalculator.NULL_STRING); + } + + @ParameterizedTest + @ValueSource(strings = { + "", + " " + }) + public void 문자열이_비어있는_경우_결과값으로_0리턴(String str) { + int actual = stringCalculator.calculate(str); + + assertThat(actual).isEqualTo(0); } @Test From 640f52e74294fd67651ecd9bc2a3feb4ee0e5a5d Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 12:15:10 +0900 Subject: [PATCH 23/28] =?UTF-8?q?feat.=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=A7=91=ED=95=A9=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/exception/ErrorMessage.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/exception/ErrorMessage.java diff --git a/src/main/java/exception/ErrorMessage.java b/src/main/java/exception/ErrorMessage.java new file mode 100644 index 00000000..d612613d --- /dev/null +++ b/src/main/java/exception/ErrorMessage.java @@ -0,0 +1,14 @@ +package exception; + +public class ErrorMessage { + + public static final String NULL_STRING = "문자열에 null 값이 전달되었습니다."; + public static final String CUSTOM_DELIMITER_NOT_FOUND = "커스텀 구분자를 찾을 수 없습니다."; + public static final String NEGATIVE_NUMBER_NOT_ALLOWED = "음수는 처리할 수 없습니다."; + public static final String INVALID_STRING = "문자열은 처리할 수 없습니다."; + public static final String DIVIDE_BY_ZERO = "0으로 나눌 수 없습니다."; + + private ErrorMessage() { + throw new RuntimeException(); + } +} From e71b3db2636b6572a52ac4751d565be835048133 Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 12:17:34 +0900 Subject: [PATCH 24/28] =?UTF-8?q?refactor.=20ErrorMessage=20=EC=83=81?= =?UTF-8?q?=EC=88=98=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Calculator.java | 6 +++--- src/main/java/domain/StringCalculator.java | 14 ++++++-------- src/test/java/CalculatorTest.java | 3 ++- src/test/java/StringCalculatorTest.java | 9 +++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/domain/Calculator.java b/src/main/java/domain/Calculator.java index 3429e69b..46de3fde 100644 --- a/src/main/java/domain/Calculator.java +++ b/src/main/java/domain/Calculator.java @@ -1,8 +1,8 @@ package domain; -public class Calculator { +import exception.ErrorMessage; - public static final String DIVIDE_BY_ZERO = "0으로 나눌 수 없습니다."; +public class Calculator { public int add(int num1, int num2) { return Math.addExact(num1, num2); @@ -18,7 +18,7 @@ public int mul(int num1, int num2) { public int div(int num1, int num2) { if (num2 == 0) - throw new ArithmeticException(DIVIDE_BY_ZERO); + throw new ArithmeticException(ErrorMessage.DIVIDE_BY_ZERO); return num1 / num2; } diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index bd810426..9ab94633 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -1,20 +1,18 @@ package domain; +import exception.ErrorMessage; + import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; public class StringCalculator { - public static final String NULL_STRING = "문자열에 null값이 전달되었습니다."; - public static final String CUSTOM_DELIMITER_NOT_FOUND = "커스텀 구분자를 찾을 수 없습니다."; - public static final String NEGATIVE_NUMBER_NOT_ALLOWED = "음수는 처리할 수 없습니다."; - public static final String INVALID_STRING = "문자열은 처리할 수 없습니다."; private final String delimiterRegex = "[,|:]"; public int calculate(String str) { if (str == null) { - throw new RuntimeException(NULL_STRING); + throw new RuntimeException(ErrorMessage.INVALID_STRING); } if (str.isBlank()) @@ -37,7 +35,7 @@ public String findCustomDelimiter(String str) { } if ((startDelimiterIdx == -1) ^ (endDelimiterIdx == -1) || (startDelimiterIdx + 2 == endDelimiterIdx)) { - throw new RuntimeException(CUSTOM_DELIMITER_NOT_FOUND); + throw new RuntimeException(ErrorMessage.CUSTOM_DELIMITER_NOT_FOUND); } return str.substring(startDelimiterIdx + 2, endDelimiterIdx); @@ -70,12 +68,12 @@ public int parseTokenToInt(String token) { int number = Integer.parseInt(token.trim()); if (number < 0) { - throw new RuntimeException(NEGATIVE_NUMBER_NOT_ALLOWED); + throw new RuntimeException(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED); } return number; } catch (NumberFormatException ex) { - throw new RuntimeException(INVALID_STRING); + throw new RuntimeException(ErrorMessage.INVALID_STRING); } } diff --git a/src/test/java/CalculatorTest.java b/src/test/java/CalculatorTest.java index 721177ca..199afe17 100644 --- a/src/test/java/CalculatorTest.java +++ b/src/test/java/CalculatorTest.java @@ -1,4 +1,5 @@ import domain.Calculator; +import exception.ErrorMessage; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; @@ -77,6 +78,6 @@ public class CalculatorTest { assertThatThrownBy(() -> calculator.div(num1, num2)) .isInstanceOf(ArithmeticException.class) - .hasMessageContaining(Calculator.DIVIDE_BY_ZERO); + .hasMessageContaining(ErrorMessage.DIVIDE_BY_ZERO); } } diff --git a/src/test/java/StringCalculatorTest.java b/src/test/java/StringCalculatorTest.java index b2eb354c..5a5af800 100644 --- a/src/test/java/StringCalculatorTest.java +++ b/src/test/java/StringCalculatorTest.java @@ -1,4 +1,5 @@ import domain.StringCalculator; +import exception.ErrorMessage; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -51,7 +52,7 @@ public class StringCalculatorTest { assertThatThrownBy(() -> stringCalculator.parseNumber(tokens)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining(StringCalculator.NEGATIVE_NUMBER_NOT_ALLOWED); + .hasMessageContaining(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED); } @Test @@ -60,7 +61,7 @@ public class StringCalculatorTest { assertThatThrownBy(() -> stringCalculator.parseNumber(tokens)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining(StringCalculator.INVALID_STRING); + .hasMessageContaining(ErrorMessage.INVALID_STRING); } @ParameterizedTest @@ -84,7 +85,7 @@ public class StringCalculatorTest { public void 커스텀_구분자_형식에_맞지_않은_경우_예외_발생(String str) { assertThatThrownBy(() -> stringCalculator.calculate(str)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining(StringCalculator.CUSTOM_DELIMITER_NOT_FOUND); + .hasMessageContaining(ErrorMessage.CUSTOM_DELIMITER_NOT_FOUND); } @ParameterizedTest @@ -92,7 +93,7 @@ public class StringCalculatorTest { public void 문자열이_널값인_경우_예외_발생(String str) { assertThatThrownBy(() -> stringCalculator.calculate(str)) .isInstanceOf(RuntimeException.class) - .hasMessageContaining(StringCalculator.NULL_STRING); + .hasMessageContaining(ErrorMessage.NULL_STRING); } @ParameterizedTest From 859680d176f0f3396dadb88ce7598d19e5e4816b Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 13:43:47 +0900 Subject: [PATCH 25/28] =?UTF-8?q?refactor.=20=EA=B5=AC=EB=B6=84=EC=9E=90?= =?UTF-8?q?=20=EC=A0=95=EA=B7=9C=20=ED=91=9C=ED=98=84=EC=8B=9D=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EC=88=98=20->=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=A7=80=EC=97=AD=20=EB=B3=80=EC=88=98=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index 9ab94633..96c34868 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -8,8 +8,6 @@ public class StringCalculator { - private final String delimiterRegex = "[,|:]"; - public int calculate(String str) { if (str == null) { throw new RuntimeException(ErrorMessage.INVALID_STRING); @@ -50,6 +48,8 @@ public String extractNumber(String str, String customDelimiter) { } public String[] splitTokens(String strNumbers, String customDelimiter) { + String delimiterRegex = "[,|:]"; + if (customDelimiter == null) { return strNumbers.split(delimiterRegex); } From b2a779c44de146f7e93ac99aceaf226a94e8c57b Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 16:30:36 +0900 Subject: [PATCH 26/28] =?UTF-8?q?refactor.=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=20=EC=9C=A0=ED=8B=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=ED=95=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 47 ++++------------------ src/main/java/util/DelimiterUtil.java | 21 ++++++++++ src/main/java/util/ParseUtil.java | 29 +++++++++++++ 3 files changed, 57 insertions(+), 40 deletions(-) create mode 100644 src/main/java/util/DelimiterUtil.java create mode 100644 src/main/java/util/ParseUtil.java diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index 96c34868..daaf7777 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -1,6 +1,8 @@ package domain; import exception.ErrorMessage; +import util.DelimiterUtil; +import util.ParseUtil; import java.util.Arrays; import java.util.List; @@ -16,30 +18,15 @@ public int calculate(String str) { if (str.isBlank()) return 0; - String customDelimiter = findCustomDelimiter(str); + String customDelimiter = DelimiterUtil.findCustomDelimiter(str); String strNumbers = extractNumber(str, customDelimiter); String[] tokens = splitTokens(strNumbers, customDelimiter); - List numbers = parseNumber(tokens); + List numbers = ParseUtil.parseNumber(tokens); return sum(numbers); } - public String findCustomDelimiter(String str) { - int startDelimiterIdx = str.indexOf("//"); - int endDelimiterIdx = str.indexOf("\n"); - - if (startDelimiterIdx == -1 && endDelimiterIdx == -1) { - return null; - } - - if ((startDelimiterIdx == -1) ^ (endDelimiterIdx == -1) || (startDelimiterIdx + 2 == endDelimiterIdx)) { - throw new RuntimeException(ErrorMessage.CUSTOM_DELIMITER_NOT_FOUND); - } - - return str.substring(startDelimiterIdx + 2, endDelimiterIdx); - } - - public String extractNumber(String str, String customDelimiter) { + private String extractNumber(String str, String customDelimiter) { if (customDelimiter != null) { return str.substring(str.indexOf("\n") + 1); } @@ -47,7 +34,7 @@ public String extractNumber(String str, String customDelimiter) { return str; } - public String[] splitTokens(String strNumbers, String customDelimiter) { + private String[] splitTokens(String strNumbers, String customDelimiter) { String delimiterRegex = "[,|:]"; if (customDelimiter == null) { @@ -57,27 +44,7 @@ public String[] splitTokens(String strNumbers, String customDelimiter) { return strNumbers.split(Pattern.quote(customDelimiter)); } - public List parseNumber(String[] tokens) { - return Arrays.stream(tokens) - .map(this::parseTokenToInt) - .toList(); - } - - public int parseTokenToInt(String token) { - try { - int number = Integer.parseInt(token.trim()); - - if (number < 0) { - throw new RuntimeException(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED); - } - - return number; - } catch (NumberFormatException ex) { - throw new RuntimeException(ErrorMessage.INVALID_STRING); - } - } - - public int sum(List numbers) { + private int sum(List numbers) { return numbers.stream() .mapToInt(Integer::intValue) .sum(); diff --git a/src/main/java/util/DelimiterUtil.java b/src/main/java/util/DelimiterUtil.java new file mode 100644 index 00000000..8e3f090f --- /dev/null +++ b/src/main/java/util/DelimiterUtil.java @@ -0,0 +1,21 @@ +package util; + +import exception.ErrorMessage; + +public class DelimiterUtil { + + public static String findCustomDelimiter(String str) { + int startDelimiterIdx = str.indexOf("//"); + int endDelimiterIdx = str.indexOf("\n"); + + if (startDelimiterIdx == -1 && endDelimiterIdx == -1) { + return null; + } + + if ((startDelimiterIdx == -1) ^ (endDelimiterIdx == -1) || (startDelimiterIdx + 2 == endDelimiterIdx)) { + throw new RuntimeException(ErrorMessage.CUSTOM_DELIMITER_NOT_FOUND); + } + + return str.substring(startDelimiterIdx + 2, endDelimiterIdx); + } +} diff --git a/src/main/java/util/ParseUtil.java b/src/main/java/util/ParseUtil.java new file mode 100644 index 00000000..cf5086e3 --- /dev/null +++ b/src/main/java/util/ParseUtil.java @@ -0,0 +1,29 @@ +package util; + +import exception.ErrorMessage; + +import java.util.Arrays; +import java.util.List; + +public class ParseUtil { + + public static List parseNumber(String[] tokens) { + return Arrays.stream(tokens) + .map(ParseUtil::parseTokenToInt) + .toList(); + } + + public static int parseTokenToInt(String token) { + try { + int number = Integer.parseInt(token.trim()); + + if (number < 0) { + throw new RuntimeException(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED); + } + + return number; + } catch (NumberFormatException ex) { + throw new RuntimeException(ErrorMessage.INVALID_STRING); + } + } +} From d8d8ecc19ec7521c82499d3b64c50fd06efa647b Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 16:32:30 +0900 Subject: [PATCH 27/28] =?UTF-8?q?refactor.=20=EC=9C=A0=ED=8B=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EB=B6=84=ED=95=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/StringCalculatorTest.java | 41 +------------------ src/test/java/UtilTest.java | 52 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 39 deletions(-) create mode 100644 src/test/java/UtilTest.java diff --git a/src/test/java/StringCalculatorTest.java b/src/test/java/StringCalculatorTest.java index 5a5af800..8cd40401 100644 --- a/src/test/java/StringCalculatorTest.java +++ b/src/test/java/StringCalculatorTest.java @@ -5,6 +5,8 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; +import util.DelimiterUtil; +import util.ParseUtil; import java.util.List; @@ -25,45 +27,6 @@ public class StringCalculatorTest { assertThat(actual).isEqualTo(10); } - @ParameterizedTest - @CsvSource({ - "'//+\n', '+'", - "'// \n', ' '", - "'//||\n', '||'" - }) - public void 커스텀_구분자_파싱_테스트(String str, String expected) { - String customDelimiter = stringCalculator.findCustomDelimiter(str); - - assertThat(customDelimiter).isEqualTo(expected); - } - - @Test - public void 문자열_리스트_정수_리스트로_변환() { - String[] tokens = {"1", "2", "3", "4"}; - - List actual = stringCalculator.parseNumber(tokens); - - assertThat(actual).containsExactly(1, 2, 3, 4); - } - - @Test - public void 문자열_리스트_정수_리스트로_변환_음수가_포함되면_예외_발생() { - String[] tokens = {"1", "2", "-3", "4"}; - - assertThatThrownBy(() -> stringCalculator.parseNumber(tokens)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED); - } - - @Test - public void 문자열_리스트_정수_리스트로_변환_문자가_포함되면_예외_발생() { - String[] tokens = {"a1", "b", " ", "4"}; - - assertThatThrownBy(() -> stringCalculator.parseNumber(tokens)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining(ErrorMessage.INVALID_STRING); - } - @ParameterizedTest @ValueSource(strings = { "//+\n1+2+3+4", diff --git a/src/test/java/UtilTest.java b/src/test/java/UtilTest.java new file mode 100644 index 00000000..3367452d --- /dev/null +++ b/src/test/java/UtilTest.java @@ -0,0 +1,52 @@ +import exception.ErrorMessage; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import util.DelimiterUtil; +import util.ParseUtil; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class UtilTest { + @ParameterizedTest + @CsvSource({ + "'//+\n', '+'", + "'// \n', ' '", + "'//||\n', '||'" + }) + public void 커스텀_구분자_파싱_테스트(String str, String expected) { + String customDelimiter = DelimiterUtil.findCustomDelimiter(str); + + assertThat(customDelimiter).isEqualTo(expected); + } + + @Test + public void 문자열_리스트_정수_리스트로_변환() { + String[] tokens = {"1", "2", "3", "4"}; + + List actual = ParseUtil.parseNumber(tokens); + + assertThat(actual).containsExactly(1, 2, 3, 4); + } + + @Test + public void 문자열_리스트_정수_리스트로_변환_음수가_포함되면_예외_발생() { + String[] tokens = {"1", "2", "-3", "4"}; + + assertThatThrownBy(() -> ParseUtil.parseNumber(tokens)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED); + } + + @Test + public void 문자열_리스트_정수_리스트로_변환_문자가_포함되면_예외_발생() { + String[] tokens = {"a1", "b", " ", "4"}; + + assertThatThrownBy(() -> ParseUtil.parseNumber(tokens)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining(ErrorMessage.INVALID_STRING); + } +} From a5edf2ca1af004fa8459fc784bd836003b24a3be Mon Sep 17 00:00:00 2001 From: Donghun <2dh2@naver.com> Date: Sat, 2 Aug 2025 16:35:54 +0900 Subject: [PATCH 28/28] =?UTF-8?q?fix.=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/StringCalculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/domain/StringCalculator.java b/src/main/java/domain/StringCalculator.java index daaf7777..728e5eb0 100644 --- a/src/main/java/domain/StringCalculator.java +++ b/src/main/java/domain/StringCalculator.java @@ -12,7 +12,7 @@ public class StringCalculator { public int calculate(String str) { if (str == null) { - throw new RuntimeException(ErrorMessage.INVALID_STRING); + throw new RuntimeException(ErrorMessage.NULL_STRING); } if (str.isBlank())