From c454c6eacbe8b1f88f500d79e6653f1225aa3b12 Mon Sep 17 00:00:00 2001 From: hyo-lin Date: Mon, 20 Oct 2025 23:24:53 +0900 Subject: [PATCH 1/7] =?UTF-8?q?docs(readme):=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20=EB=B0=8F=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bd90ef0247..90e2f722f5 100644 --- a/README.md +++ b/README.md @@ -1 +1,26 @@ -# java-calculator-precourse \ No newline at end of file +# java-calculator-precourse + +# 구현 기능 목록 +1. 입력 처리 +- camp.nextstep.edu.missionutils.Console.readLine()을 사용해 문자열 입력을 받는다. +- 입력 문구: "덧셈할 문자열을 입력해 주세요." + +2. 빈 문자열 처리 +- 입력값이 빈 문자열("")일 경우 결과는 0을 반환한다. + +3. 기본 구분자 처리 +- 쉼표(,) 또는 콜론(:)을 구분자로 처리한다. + +4. 커스텀 구분자 처리 +- "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 처리한다. + +5. 예외처리 +- 다음과 같은 경우 `IllegalArgumentException`을 발생시키고 프로그램은 종료된다. + - 음수 입력 (`-1,2,3`) + - 숫자가 아닌 입력 (`a,1,2`) + - 잘못된 구분자 형식 (`//;12;3`, `//\n1,2`) + - 빈 숫자 토큰이 존재하는 경우 (`1,,2`) +- 프로그램 종료 시 `System.exit()`을 호출하지 않는다. + +6. 출력 +- 계산된 결과를 "결과 : " 형식으로 출력한다. \ No newline at end of file From 1a053eead79d8858477f199c7b9865f0195733a6 Mon Sep 17 00:00:00 2001 From: hyo-lin Date: Mon, 20 Oct 2025 23:28:40 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat(app):=20Application=20main=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=8B=A4=ED=96=89=20=ED=9D=90=EB=A6=84?= =?UTF-8?q?=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Application.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index 573580fb40..e5efffdbbb 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -1,7 +1,17 @@ package calculator; +import calculator.controller.CalculatorController; +import calculator.service.StringCalculatorService; +import calculator.view.InputView; +import calculator.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + InputView in = new InputView(); + OutputView out = new OutputView(); + StringCalculatorService svc = new StringCalculatorService(); + CalculatorController controller = new CalculatorController(in, out, svc); + + controller.run(); } } From 433d0120d22db891eec843fa588e6b1cc495ba1e Mon Sep 17 00:00:00 2001 From: hyo-lin Date: Mon, 20 Oct 2025 23:29:10 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat(view):=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EB=B7=B0=20=EC=B6=94=EA=B0=80=20(InputView)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/view/InputView.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/calculator/view/InputView.java diff --git a/src/main/java/calculator/view/InputView.java b/src/main/java/calculator/view/InputView.java new file mode 100644 index 0000000000..430825de72 --- /dev/null +++ b/src/main/java/calculator/view/InputView.java @@ -0,0 +1,9 @@ +package calculator.view; + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + public String readLine() { + return Console.readLine(); + } +} From c826b3619dd62a812910487d777d86aaf855ebac Mon Sep 17 00:00:00 2001 From: hyo-lin Date: Mon, 20 Oct 2025 23:29:28 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat(view):=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EB=B7=B0=20=EC=B6=94=EA=B0=80=20(OutputView)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/view/OutputView.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/calculator/view/OutputView.java diff --git a/src/main/java/calculator/view/OutputView.java b/src/main/java/calculator/view/OutputView.java new file mode 100644 index 0000000000..088303ee02 --- /dev/null +++ b/src/main/java/calculator/view/OutputView.java @@ -0,0 +1,11 @@ +package calculator.view; + +public class OutputView { + public void printPrompt() { + System.out.println("덧셈할 문자열을 입력해 주세요."); + } + + public void printResult(int sum) { + System.out.println("결과 : " + sum); + } +} From c3d71e8d69066dda4d7f873cd3cc34c8dd40c07d Mon Sep 17 00:00:00 2001 From: hyo-lin Date: Mon, 20 Oct 2025 23:29:46 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat(controller):=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=E2=86=92=EA=B3=84=EC=82=B0=E2=86=92=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CalculatorController.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/calculator/controller/CalculatorController.java diff --git a/src/main/java/calculator/controller/CalculatorController.java b/src/main/java/calculator/controller/CalculatorController.java new file mode 100644 index 0000000000..9f2236e30e --- /dev/null +++ b/src/main/java/calculator/controller/CalculatorController.java @@ -0,0 +1,25 @@ +package calculator.controller; + +import calculator.service.StringCalculatorService; +import calculator.view.InputView; +import calculator.view.OutputView; + +public class CalculatorController { + private final InputView inputView; + private final OutputView outputView; + private final StringCalculatorService service; + + public CalculatorController(InputView inputView, OutputView outputView, StringCalculatorService service) { + this.inputView = inputView; + this.outputView = outputView; + this.service = service; + } + + public void run() { + outputView.printPrompt(); + String input = inputView.readLine(); + input = input.replace("\\n", "\n"); + int sum = service.add(input); + outputView.printResult(sum); + } +} From c9947f6e35bb42aa0b70ebec47ed993feefef175 Mon Sep 17 00:00:00 2001 From: hyo-lin Date: Mon, 20 Oct 2025 23:30:16 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat(service):=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=20=EB=8D=A7=EC=85=88=20=EA=B3=84=EC=82=B0=EA=B8=B0=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/StringCalculatorService.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/main/java/calculator/service/StringCalculatorService.java diff --git a/src/main/java/calculator/service/StringCalculatorService.java b/src/main/java/calculator/service/StringCalculatorService.java new file mode 100644 index 0000000000..8b8e037064 --- /dev/null +++ b/src/main/java/calculator/service/StringCalculatorService.java @@ -0,0 +1,85 @@ +package calculator.service; + +import java.util.regex.Pattern; + +public class StringCalculatorService { + private static final String DEFAULT_DELIMS = "[,:]"; + + public int add(String input) { + if (isEmpty(input)) { + return 0; + } + + Parsed parsed = parse(input); + String[] tokens = tokenize(parsed.numbers, parsed.delimRegex); + validate(tokens); + return sum(tokens); + } + + private boolean isEmpty(String s) { + return s == null || s.isEmpty(); + } + + private Parsed parse(String input) { + if (!input.startsWith("//")) { + return new Parsed(DEFAULT_DELIMS, input); + } + + int nl = input.indexOf('\n'); + if (nl < 0) { + throw new IllegalArgumentException("잘못된 커스텀 구분자 형식입니다."); + } + + String custom = input.substring(2, nl); + if (custom.isEmpty()) { + throw new IllegalArgumentException("구분자가 비어 있습니다."); + } + + String delimRegex = Pattern.quote(custom); + String numbers = input.substring(nl + 1); + return new Parsed(delimRegex, numbers); + } + + private String[] tokenize(String numbers, String delimRegex) { + if (numbers.isEmpty()) { + return new String[0]; + } + if (DEFAULT_DELIMS.equals(delimRegex)) { + return numbers.split(DEFAULT_DELIMS, -1); + } + return numbers.split(delimRegex, -1); + } + + private void validate(String[] tokens) { + for (String t : tokens) { + if (t == null || t.isEmpty()) { + throw new IllegalArgumentException("빈 숫자 토큰이 포함되어 있습니다."); + } + if (!t.chars().allMatch(Character::isDigit)) { + throw new IllegalArgumentException("숫자가 아닌 값이 포함되었습니다."); + } + int v = Integer.parseInt(t); + if (v < 0) { + throw new IllegalArgumentException("음수는 허용되지 않습니다."); + } + } + } + + private int sum(String[] tokens) { + int total = 0; + for (String t : tokens) { + total += Integer.parseInt(t); + } + return total; + } + + private static final class Parsed { + final String delimRegex; + final String numbers; + + Parsed(String d, String n) { + this.delimRegex = d; + this.numbers = n; + } + } +} From c37bdf165de82ff01b54e2519fdbb2b403a83816 Mon Sep 17 00:00:00 2001 From: hyo-lin Date: Mon, 20 Oct 2025 23:30:43 +0900 Subject: [PATCH 7/7] =?UTF-8?q?test(app):=20=EC=83=81=ED=99=A9=EB=B3=84=20?= =?UTF-8?q?=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/calculator/ApplicationTest.java | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index 93771fb011..b06d7dc6b6 100644 --- a/src/test/java/calculator/ApplicationTest.java +++ b/src/test/java/calculator/ApplicationTest.java @@ -8,10 +8,11 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; class ApplicationTest extends NsTest { + @Test void 커스텀_구분자_사용() { assertSimpleTest(() -> { - run("//;\\n1"); + run("//;\\n1\n"); assertThat(output()).contains("결과 : 1"); }); } @@ -19,13 +20,45 @@ class ApplicationTest extends NsTest { @Test void 예외_테스트() { assertSimpleTest(() -> - assertThatThrownBy(() -> runException("-1,2,3")) - .isInstanceOf(IllegalArgumentException.class) + assertThatThrownBy(() -> runException("-1,2,3\n")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + @Test + void 기본_구분자_쉼표와_콜론_테스트() { + assertSimpleTest(() -> { + run("1,2:3\n"); + assertThat(output()).contains("결과 : 6"); + }); + } + + @Test + void 빈_문자열_입력시_0_반환() { + assertSimpleTest(() -> { + run("\n"); + assertThat(output()).contains("결과 : 0"); + }); + } + + @Test + void 잘못된_커스텀_형식_예외() { + assertSimpleTest(() -> + assertThatThrownBy(() -> runException("//;12;3\n")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + @Test + void 비어있는_커스텀_구분자_예외() { + assertSimpleTest(() -> + assertThatThrownBy(() -> runException("//\n1,2\n")) + .isInstanceOf(IllegalArgumentException.class) ); } @Override public void runMain() { - Application.main(new String[]{}); + Application.main(new String[] {}); } }