From cd11d5962dacf4476cfb5f4df387c8493f5657ad Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Fri, 19 Sep 2025 14:34:29 +0900 Subject: [PATCH 01/10] first commit --- README.md | 29 ++++++++- src/main/java/calculator/Application.java | 10 +++- src/main/java/calculator/Calculator.java | 72 +++++++++++++++++++++++ src/main/java/calculator/Delimiter.java | 17 ++++++ 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 src/main/java/calculator/Calculator.java create mode 100644 src/main/java/calculator/Delimiter.java diff --git a/README.md b/README.md index 79ea6bfd9a..d51dfe2a18 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,28 @@ # java-calculator-precourse -![문제 원문1](calculator-description-1.png) -![문제 원문2](calculator-description-2.png) +## 구현 기능 목록 + +### 입력 문자열 처리 + +- 빈 문자열인 경우 + - [x] null 또는 빈 문자열("") 또는 1개 이상의 공백 입력 시 0을 반환 + +- 커스텀 구분자를 지정한 경우 + - [x] 커스텀 구분자가 `//`로 시작해 `\n`가 포함되는지 확인 + - [x] 입력 문자열에서 커스텀 구분자를 추출 + - 1개 이상의 구분자이며, 문자와 숫자 모두 가능하다. + - [x] 추출한 커스텀 구분자를 기본 구분자 목록에 추가 + - [x] 커스텀 구분자 지정 패턴(\n으로 끝나는 문자열) 이후의 문자열 추출 + +- 공통 + - [x] 구분자로 입력받은 문자열을 분리한다. + - 계산기에서 모든 예외를 처리 하므로 일단 구분자로 모든 문자열을 분리한다. + +### 계산기 + +- [x] 분리한 문자열을 양수 값으로 변환해 더해준다. +- [x] 양수 값 이외의 값이 배열에 들어있는 경우 예외 발생 +- [x] 합계가 정수 최댓값을 초과하는 경우 예외 발생 +- [x] 추출한 숫자의 합 반환 + +### 리펙토링 + diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index 573580fb40..eb227b4015 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -1,7 +1,15 @@ package calculator; +import camp.nextstep.edu.missionutils.Console; + public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 + System.out.println("덧셈할 문자열을 입력헤주세요."); + String input = Console.readLine(); + + Calculator calculator = new Calculator(); + int result = calculator.calculate(input); + System.out.println("결과 : " + result); } -} +} \ No newline at end of file diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java new file mode 100644 index 0000000000..afec08cb06 --- /dev/null +++ b/src/main/java/calculator/Calculator.java @@ -0,0 +1,72 @@ +package calculator; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Calculator { + private static Delimiter delimiter; + private String[] numbers; + private int result; + + public int calculate(String input) { + if (input == null || input.isBlank()) { + return 0; + } + + input = CustomController.getNewInputIfCustom(input); + + if (delimiter == null) { + delimiter = new Delimiter(); + } + + numbers = delimiter.split(input); + calculateResult(); + + return result; + } + + private void calculateResult() { + for (String number : numbers) { + // 만약 s가 숫자(양수)가 아니면 IllegalArgumentException 예외 발생 + // 오버플로우 발생 시 예외 발생 + try { + result = safeAdd(result, Integer.parseUnsignedInt(number)); + } catch (RuntimeException e) { + throw new IllegalArgumentException(); + } + } + } + + private int safeAdd(int result, int number) { + if (result > Integer.MAX_VALUE - number) { + throw new ArithmeticException("Overflow"); + } + return result + number; + } + + private abstract static class CustomController { + private static final String VALID_CUSTOM_INPUT = "^//(.*)\\\\n(.*)"; + private static final int CUSTOM_DELIMITER_GROUP = 1; + private static final int CUSTOM_PURE_INPUT_GROUP = 2; + + private static String getNewInputIfCustom(String input) { + Pattern pattern = Pattern.compile(VALID_CUSTOM_INPUT); + Matcher matcher = pattern.matcher(input); + + if (matcher.matches()) { + String customDelimiter = extractCustomDelimiter(matcher); + delimiter = new Delimiter(customDelimiter); + input = extractPureInput(matcher); + } + return input; + } + + private static String extractCustomDelimiter(Matcher matcher) { + return matcher.group(CUSTOM_DELIMITER_GROUP); + } + + private static String extractPureInput(Matcher matcher) { + return matcher.group(CUSTOM_PURE_INPUT_GROUP); + } + } +} \ No newline at end of file diff --git a/src/main/java/calculator/Delimiter.java b/src/main/java/calculator/Delimiter.java new file mode 100644 index 0000000000..a9973bf334 --- /dev/null +++ b/src/main/java/calculator/Delimiter.java @@ -0,0 +1,17 @@ +package calculator; + +public class Delimiter { + private final String delimiter; + + public Delimiter() { + delimiter = ",;"; + } + + public Delimiter(String customDelimiter) { + this.delimiter = ",:" + customDelimiter; + } + + public String[] split(String input) { + return input.split("[" + delimiter + "]"); + } +} \ No newline at end of file From 6c38c9f06f6c33947b978248d13bcacfe341d54c Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Fri, 19 Sep 2025 16:21:51 +0900 Subject: [PATCH 02/10] =?UTF-8?q?Refactor(Calculator):=20CustomController?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A5=BC=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20st?= =?UTF-8?q?atic=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A5=BC=20=EC=9D=B8?= =?UTF-8?q?=EC=8A=A4=ED=84=B4=EC=8A=A4=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index afec08cb06..a205995b23 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -4,7 +4,7 @@ import java.util.regex.Pattern; public class Calculator { - private static Delimiter delimiter; + private Delimiter delimiter; private String[] numbers; private int result; @@ -13,7 +13,8 @@ public int calculate(String input) { return 0; } - input = CustomController.getNewInputIfCustom(input); + CustomController customController = new CustomController(); + input = customController.getNewInputIfCustom(input); if (delimiter == null) { delimiter = new Delimiter(); @@ -44,12 +45,12 @@ private int safeAdd(int result, int number) { return result + number; } - private abstract static class CustomController { + private class CustomController { private static final String VALID_CUSTOM_INPUT = "^//(.*)\\\\n(.*)"; private static final int CUSTOM_DELIMITER_GROUP = 1; private static final int CUSTOM_PURE_INPUT_GROUP = 2; - private static String getNewInputIfCustom(String input) { + private String getNewInputIfCustom(String input) { Pattern pattern = Pattern.compile(VALID_CUSTOM_INPUT); Matcher matcher = pattern.matcher(input); @@ -61,11 +62,11 @@ private static String getNewInputIfCustom(String input) { return input; } - private static String extractCustomDelimiter(Matcher matcher) { + private String extractCustomDelimiter(Matcher matcher) { return matcher.group(CUSTOM_DELIMITER_GROUP); } - private static String extractPureInput(Matcher matcher) { + private String extractPureInput(Matcher matcher) { return matcher.group(CUSTOM_PURE_INPUT_GROUP); } } From 25590f6cea9968aac9fd62bcbbd08f6e184654a5 Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Fri, 19 Sep 2025 16:26:58 +0900 Subject: [PATCH 03/10] =?UTF-8?q?Docs:=20README.md=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=AA=A9=EB=A1=9D(=EB=A6=AC=ED=8E=99?= =?UTF-8?q?=ED=86=A0=EB=A7=81)=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d51dfe2a18..f77595534b 100644 --- a/README.md +++ b/README.md @@ -25,4 +25,4 @@ - [x] 추출한 숫자의 합 반환 ### 리펙토링 - +- [x] CustomController 클래스를 내부클래스로 변경, static 메서드를 인스턴스 메서드로 변경 \ No newline at end of file From 364690744ca97f5c9a2805e730f20ac92881246f Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Fri, 19 Sep 2025 16:36:34 +0900 Subject: [PATCH 04/10] =?UTF-8?q?Refactor(Calculator):=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EB=B0=9C=EC=83=9D=20=EC=8B=9C=20IllegalArgumentExc?= =?UTF-8?q?eption(e)=EB=A5=BC=20=EB=8D=98=EC=A0=B8=20stacktrace=EC=97=90?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- src/main/java/calculator/Calculator.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f77595534b..02c25352c2 100644 --- a/README.md +++ b/README.md @@ -25,4 +25,5 @@ - [x] 추출한 숫자의 합 반환 ### 리펙토링 -- [x] CustomController 클래스를 내부클래스로 변경, static 메서드를 인스턴스 메서드로 변경 \ No newline at end of file +- [x] CustomController 클래스를 내부클래스로 변경, static 메서드를 인스턴스 메서드로 변경 +- [x] 예외 발생 시 IllegalArgumentException(e)를 던져 stacktrace에 에러메시지 출력 \ No newline at end of file diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index a205995b23..205a283c69 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -33,7 +33,7 @@ private void calculateResult() { try { result = safeAdd(result, Integer.parseUnsignedInt(number)); } catch (RuntimeException e) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } } } From 14e620d439ce7ce6153d3a8370a8cf706d672914 Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Fri, 19 Sep 2025 17:13:18 +0900 Subject: [PATCH 05/10] =?UTF-8?q?Refactor(CustomController):=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20VALID=5FC?= =?UTF-8?q?USTOM=5FINPUT=20->=20CUSTOM=5FDELIMITER=5FPATTERN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 205a283c69..445e4a61ed 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -46,12 +46,12 @@ private int safeAdd(int result, int number) { } private class CustomController { - private static final String VALID_CUSTOM_INPUT = "^//(.*)\\\\n(.*)"; + private static final String CUSTOM_DELIMITER_PATTERN = "^//(.*)\\\\n(.*)"; private static final int CUSTOM_DELIMITER_GROUP = 1; private static final int CUSTOM_PURE_INPUT_GROUP = 2; private String getNewInputIfCustom(String input) { - Pattern pattern = Pattern.compile(VALID_CUSTOM_INPUT); + Pattern pattern = Pattern.compile(CUSTOM_DELIMITER_PATTERN); Matcher matcher = pattern.matcher(input); if (matcher.matches()) { From 3d704340b1c39be9396cb0323399de3f2091aedd Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Fri, 19 Sep 2025 17:13:38 +0900 Subject: [PATCH 06/10] =?UTF-8?q?Refactor(CustomController):=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20VALID=5FC?= =?UTF-8?q?USTOM=5FINPUT=20->=20CUSTOM=5FDELIMITER=5FPATTERN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 02c25352c2..d2975eeea5 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,5 @@ ### 리펙토링 - [x] CustomController 클래스를 내부클래스로 변경, static 메서드를 인스턴스 메서드로 변경 -- [x] 예외 발생 시 IllegalArgumentException(e)를 던져 stacktrace에 에러메시지 출력 \ No newline at end of file +- [x] 예외 발생 시 IllegalArgumentException(e)를 던져 stacktrace에 에러메시지 출력 +- [x] 변수 이름 변경 VALID_CUSTOM_INPUT -> CUSTOM_DELIMITER_PATTERN \ No newline at end of file From 0a9a8ba9d0f16ce049435800776cd5b02c6b55ea Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Fri, 19 Sep 2025 17:15:49 +0900 Subject: [PATCH 07/10] =?UTF-8?q?Revert=20"Refactor(CustomController):=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?VALID=5FCUSTOM=5FINPUT=20->=20CUSTOM=5FDELIMITER=5FPATTERN"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3d704340b1c39be9396cb0323399de3f2091aedd. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index d2975eeea5..02c25352c2 100644 --- a/README.md +++ b/README.md @@ -26,5 +26,4 @@ ### 리펙토링 - [x] CustomController 클래스를 내부클래스로 변경, static 메서드를 인스턴스 메서드로 변경 -- [x] 예외 발생 시 IllegalArgumentException(e)를 던져 stacktrace에 에러메시지 출력 -- [x] 변수 이름 변경 VALID_CUSTOM_INPUT -> CUSTOM_DELIMITER_PATTERN \ No newline at end of file +- [x] 예외 발생 시 IllegalArgumentException(e)를 던져 stacktrace에 에러메시지 출력 \ No newline at end of file From ec0df69dedf956a7e781e5ee160677c434bb350c Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Fri, 19 Sep 2025 17:17:49 +0900 Subject: [PATCH 08/10] =?UTF-8?q?Docs:=20README.md=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=AA=A9=EB=A1=9D(=EB=A6=AC=ED=8E=99?= =?UTF-8?q?=ED=86=A0=EB=A7=81)=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 02c25352c2..d2975eeea5 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,5 @@ ### 리펙토링 - [x] CustomController 클래스를 내부클래스로 변경, static 메서드를 인스턴스 메서드로 변경 -- [x] 예외 발생 시 IllegalArgumentException(e)를 던져 stacktrace에 에러메시지 출력 \ No newline at end of file +- [x] 예외 발생 시 IllegalArgumentException(e)를 던져 stacktrace에 에러메시지 출력 +- [x] 변수 이름 변경 VALID_CUSTOM_INPUT -> CUSTOM_DELIMITER_PATTERN \ No newline at end of file From 4e88af5f5b32276b5f46cf8da6fcc5b82f176453 Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Sat, 20 Sep 2025 15:35:54 +0900 Subject: [PATCH 09/10] =?UTF-8?q?Fix(Delimiter):=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=EC=9E=90=20=EC=B4=88=EA=B8=B0=ED=99=94=20?= =?UTF-8?q?=EA=B0=92=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/calculator/Delimiter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/calculator/Delimiter.java b/src/main/java/calculator/Delimiter.java index a9973bf334..f332b0c5a8 100644 --- a/src/main/java/calculator/Delimiter.java +++ b/src/main/java/calculator/Delimiter.java @@ -4,7 +4,7 @@ public class Delimiter { private final String delimiter; public Delimiter() { - delimiter = ",;"; + delimiter = ",:"; } public Delimiter(String customDelimiter) { From d1e8a13287173a08073c00bc5b80492afd134294 Mon Sep 17 00:00:00 2001 From: kyeonghyeon Date: Sat, 20 Sep 2025 15:39:24 +0900 Subject: [PATCH 10/10] =?UTF-8?q?Feat(ApplicationTest):=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EA=B5=AC=EB=B6=84=EC=9E=90=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=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/calculator/ApplicationTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index 93771fb011..f49e6f674f 100644 --- a/src/test/java/calculator/ApplicationTest.java +++ b/src/test/java/calculator/ApplicationTest.java @@ -8,6 +8,14 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; class ApplicationTest extends NsTest { + @Test + void 기본_구분자_사용() { + assertSimpleTest(() -> { + run("1:2,3"); + assertThat(output()).contains("결과 : 6"); + }); + } + @Test void 커스텀_구분자_사용() { assertSimpleTest(() -> {