-
Notifications
You must be signed in to change notification settings - Fork 0
[문자열 덧셈 계산기] 양화영 미션 제출합니다 #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6a7e6ba
b8f6aa9
4ea1128
31bfeaa
b7fd63b
cd9efd1
8990b2b
dc226b9
5fbdeae
51a252c
b6bb974
44725d5
f588916
abb6742
ae63548
765c712
8ffde3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,76 @@ | ||
| # java-calculator-precourse | ||
| ## 1주차 미션 - 문자열 덧셈 계산기 | ||
| ### 🔍 진행 방식 | ||
| - 미션은 **기능 요구사항** , **프로그래밍 요구사항**, **과제 진행 요구사항** 세가지로 구성되어 있다. | ||
| - 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다 | ||
| - 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다. | ||
| - 매주 진행할 미션은 화요일 오후 3시부터 확인할 수 있으며, 다음 주 월요일까지 구현을 완료하여 제출해야 한다. 제출은 일요일 오후 3시부터 가능하다. | ||
| - 정해진 시간을 지키지 않을 경우 미션을 제출하지 않은 것으로 간주한다. | ||
| - 종료 일시 이후에는 추가 푸시를 허용하지 않는다 | ||
|
|
||
| ### 💡 미션 제출 방식 | ||
| - 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다. | ||
| - GitHub을 활용한 제출 방법은 [프리코스 과제 제출](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 문서를 참고해 | ||
| 제출한다. | ||
| - GitHub에 미션을 제출한 후 [우아한테크코스 지원](https://apply.techcourse.co.kr) 사이트에 접속하여 프리코스 과제를 제출한다. | ||
| - 자세한 방법은 [제출 가이드](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse#제출-가이드) 참고 | ||
| - **Pull Request만 보내고 지원 플랫폼에서 과제를 제출하지 않으면 최종 제출하지 않은 것으로 처리되니 주의한다.** | ||
|
|
||
| ### 🚨 과제 제출 전 체크 리스트 | ||
|
|
||
| - 기능 구현을 모두 정상적으로 했더라도 **요구 사항에 명시된 출력값 형식을 지키지 않을 경우 0점으로 처리**한다. | ||
| - 기능 구현을 완료한 뒤 아래 가이드에 따라 테스트를 실행했을 때 모든 테스트가 성공적으로 실행되는지 확인한다. | ||
| - **테스트가 실패할 경우 0점으로 처리**되므로, 반드시 확인 후 제출한다. | ||
|
|
||
| ### ✏️ 테스트 실행 가이드 | ||
| - 터미널에서 `java -version` 을 실행하여 Java 버전이 21인지 확인한다. Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 21로 실행되는지 확인한다. | ||
| - 터미널에서 Mac 또는 Linux 사용자의 경우 ` ./gradlew clean test ` 명령을 실행하여 모든 테스트가 아래와 같이 통과하는지 확인한다. | ||
| ``` | ||
| BUILD SUCCESSFUL in 0s | ||
| ``` | ||
|
|
||
| ### 🚀 기능 요구사항 | ||
| - 입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다 | ||
| - 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다. | ||
| - (예) "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6 | ||
| - 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다 | ||
| - (예) 예를 들어 "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다 | ||
| - 사용자가 잘못된 값을 입력한 경우 `IllegalArgumentException` 을 발생시킨 후 애플리케이션은 종료 되어야 한다. | ||
|
|
||
| ### 🛠️ 구현 기능 목록 | ||
| - [x] 사용자로부터 문자열을 입력받는다 | ||
| - 문자열이 구분자와 양수로 구성되어 있는지 확인한다. | ||
| - 양수가 아닌 음수가 입력된 경우 -> `NEGATIVE_NUMBER_NOT_ALLOWED` 예외 발생 | ||
| - 아무것도 입력하지않은 경우에는 -> 0 을 반환한다. | ||
| - 공백만 입력하거나 공백을 문자에 포함한 경우의 -> `NEGATIVE_NUMBER_NOT_ALLOWED`예외 발생 | ||
| - 숫자가 아닌 값을 입력할 경우 ->`ENTERED_NON_NUMERIC_VALUE`예외 발생 | ||
| - 문자열이 잘못된 값일 경우 예외를 발생 시킨 후 애플리케이션을 종료한다. | ||
| - [x] 커스텀 구분자를 지정한다 | ||
| - 커스텀 구분자는 문자열 앞부분의 `//` 와 `\n` 사이에 위치하는 문자를 커스텀 구분자로 사용한다. | ||
| - (예) `//;\n1;2;3` 과 같이 값을 입력할 경우 커스텀 구분자는 `;` 이며 결과값은 6이 반환되어야 한다. | ||
| - 커스텀 구분자는 1글자 이상일 수 있다. | ||
| - (예) `//;.\n1;.2;.3 ` 일때 커스텀 구분자는 `;. ` | ||
| - 빈 문자열(1글자 이하)인 경우 -> `DELIMITER_MISSING`예외 발생 | ||
| - 기본 구분자 `,` `;` | ||
| - [x] 문자열을 구분자를 기준으로 분리한다. | ||
| - [x] 구분자를 기준으로 분리한 각 숫자를 합친다. | ||
| - [x] 각 숫자를 합한 결과를 출력한다. | ||
|
|
||
| **실행 결과 예시** | ||
| ``` | ||
| 덧셈할 문자열을 입력해 주세요 | ||
| //;\n1;2;3 | ||
| 결과 : 6 | ||
| ``` | ||
|
|
||
| ### 🎯 프로그래밍 요구 사항 | ||
| - JDK 21 버전에서 실행 가능해야 한다. | ||
| - 프로그램 실행의 시작점은 Application의 `main()`이다. | ||
| - build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다. | ||
| - 프로그램 종료 시 System.exit()를 호출하지 않는다. | ||
| - 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. | ||
| - 자바 코드 컨벤션을 지키면서 프로그래밍한다. | ||
| - 기본적으로 [Java Style Guide](https://github.com/woowacourse/woowacourse-docs/tree/main/styleguide/java)를 원칙으로 한다. | ||
|
|
||
| ### 📚 라이브러리 | ||
| - `camp.nextstep.edu.missionutils `에서 제공하는 Console API를 사용하여 구현해야 한다. | ||
| - 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 readLine()을 활용한다 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| package calculator; | ||
|
|
||
| import calculator.controller.CalculatorController; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| // TODO: 프로그램 구현 | ||
| CalculatorController calculatorController = new CalculatorController(); | ||
| calculatorController.run(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package calculator.controller; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import calculator.domain.Separator; | ||
| import calculator.view.Input; | ||
| import calculator.view.Output; | ||
|
|
||
| public class CalculatorController { | ||
| public void run(){ | ||
| Input input = new Input(); | ||
| String value = input.readInput(); | ||
|
|
||
| //Separator | ||
| Separator separator = new Separator(value); | ||
| List<Integer> numbers = separator.getNumbers(); | ||
|
|
||
| Output output = new Output(); | ||
| output.output(numbers); | ||
|
|
||
|
|
||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package calculator.domain; | ||
|
|
||
| import java.util.regex.Matcher; | ||
| import java.util.regex.Pattern; | ||
|
|
||
| import calculator.message.ErrorMessage; | ||
|
|
||
| public class Delimiter { | ||
| private static final Pattern CUSTOM_PATTERN = Pattern.compile("//(.*?)\\\\?n(.*)", Pattern.DOTALL); | ||
|
|
||
| private final String regex; | ||
|
|
||
| private Delimiter (String regex) { | ||
| this.regex = regex; | ||
| } | ||
| public static Delimiter of(String value) { | ||
| Matcher matcher = CUSTOM_PATTERN.matcher(value); | ||
|
|
||
| if (matcher.matches()) { | ||
| String custom = matcher.group(1); | ||
| if(custom.isEmpty()) { //커스텀 구분자가 빈 문자열이면 예외처리 | ||
| throw new IllegalArgumentException(ErrorMessage.DELIMITER_MISSING); | ||
| } | ||
| return new Delimiter(Pattern.quote(custom)); | ||
| } | ||
| return new Delimiter("[,\n]"); | ||
| } | ||
| public String extractNumbers(String value) { | ||
| Matcher matcher = CUSTOM_PATTERN.matcher(value); | ||
| return matcher.matches() ? matcher.group(2) : value; | ||
| } | ||
| public String getRegex() { | ||
| return regex; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package calculator.domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| import calculator.message.ErrorMessage; | ||
|
|
||
| public class Separator { | ||
|
|
||
| private final Delimiter delimiter; | ||
| private final List<Integer> numbers; | ||
|
|
||
| public Separator(String value) { | ||
| this.delimiter = Delimiter.of(value);//구분자 객체 생성 | ||
| this.numbers = parseNumbers(delimiter.extractNumbers(value)); //숫자 변환 | ||
| } | ||
|
|
||
| private List<Integer> parseNumbers(String nums) { | ||
| List<Integer> result = new ArrayList<>(); | ||
| for (String token: nums.split(delimiter.getRegex())){ | ||
| if(!token.isEmpty()) { | ||
| int num; | ||
| try{ | ||
| num = Integer.parseInt(token); | ||
| }catch (NumberFormatException e){ //숫자가 아닌 경우 예외처리 | ||
| throw new IllegalArgumentException(ErrorMessage.ENTERED_NON_NUMERIC_VALUE); | ||
| } | ||
| //음수인경우 예외처리 | ||
| if (num < 0) { | ||
| throw new IllegalArgumentException(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED); | ||
| } | ||
| result.add(num); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| public List<Integer> getNumbers(){ | ||
| return numbers; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package calculator.message; | ||
|
|
||
| public abstract class ErrorMessage { | ||
|
|
||
| //입출력 관련 | ||
| public final static String TRIM_ERROR = "문자열에 공백이 포함될 수 없습니다"; | ||
| public final static String FORMAT_INCORRECT = "입력 형식이 잘못되었습니다"; | ||
| //구분자 , 입력값 검증 관련 | ||
| public final static String ENTERED_NON_NUMERIC_VALUE = "숫자가 아닌 값이 포함되어 있습니다"; | ||
| public final static String NEGATIVE_NUMBER_NOT_ALLOWED = "음수는 입력할 수 없습니다"; | ||
| public final static String DELIMITER_MISSING = "커스텀 구분자는 한자리 이상 포함되어 있어야 합니다"; | ||
|
|
||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package calculator.message; | ||
|
|
||
| public abstract class SuccessMessage { | ||
|
|
||
|
|
||
| //입출력 관련 | ||
| public final static String INPUT_MESSAGE = "덧셈할 문자열을 입력해 주세요"; | ||
| public final static String OUTPUT_MESSAGE = "결과 :"; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package calculator.view; | ||
|
|
||
| import calculator.message.ErrorMessage; | ||
| import calculator.message.SuccessMessage; | ||
| import camp.nextstep.edu.missionutils.Console; | ||
|
|
||
| public class Input { | ||
| public String readInput() { | ||
| System.out.println(SuccessMessage.INPUT_MESSAGE); | ||
| String input = Console.readLine(); | ||
|
|
||
| //입력값이 null인 경우 0을 반환 | ||
| if (input == null || input.isEmpty()) { | ||
| return "0"; | ||
| } | ||
| //공백만 입력하거나 공백을 문자에 포함한 경우의 예외 처리 | ||
| if (input.trim().isEmpty() || input.contains(" ") ) { | ||
| throw new IllegalArgumentException(ErrorMessage.TRIM_ERROR); | ||
| } | ||
|
Comment on lines
+12
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 입력값 검증을 구체적으로 세분화해주셨군요👍 만약 그렇다면 제 생각에는
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. trim().isEmpty()는 " "만 잡고, 빈 문자열 ""을 못잡기에 input.isEmpty() 가 없으면 NPE 가 발생할 수도 있을 것 같아서 둘다 남겨뒀습니다 ! |
||
|
|
||
| return input; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package calculator.view; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import calculator.message.SuccessMessage; | ||
|
|
||
| public class Output { | ||
| public void output(List<Integer> numbers) { | ||
| int sum = 0; | ||
| for (Integer number : numbers) { | ||
| sum += number; | ||
| } | ||
| System.out.println(SuccessMessage.OUTPUT_MESSAGE + " " + sum); } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 출력 메시지를 enum 처리해준 부분 정말 좋은거 같아요! |
||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package calculator; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.*; | ||
|
|
||
| import java.util.regex.Pattern; | ||
|
|
||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.ValueSource; | ||
|
|
||
| import calculator.domain.Delimiter; | ||
| import calculator.message.ErrorMessage; | ||
|
|
||
| class DelimiterTest { | ||
|
|
||
| @DisplayName("기본 구분자 사용") | ||
| @Test | ||
| void defaultDelimiterTest() { | ||
| Delimiter delimiter = Delimiter.of("1,2,3"); | ||
| assertEquals("[,\n]", delimiter.getRegex()); | ||
|
|
||
| String numbers = delimiter.extractNumbers("1,2,3"); | ||
| assertEquals("1,2,3", numbers); | ||
| } | ||
|
|
||
| @DisplayName("커스텀 구분자 적용") | ||
| @ParameterizedTest | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 테스트 코드를 구조화하신 부분이 정말 좋은 것 같아요 👍👍 |
||
| @ValueSource(strings = {"//;\\n1;2;3", "//*\\n4*5*6"}) | ||
| void customDelimiterTest(String input) { | ||
| Delimiter delimiter = Delimiter.of(input); | ||
|
|
||
| String numbers = delimiter.extractNumbers(input); | ||
| assertNotNull(numbers); | ||
| assertTrue(Pattern.matches("\\d+(;|\\*)\\d+(;|\\*)\\d+", numbers)); | ||
| } | ||
|
|
||
| @DisplayName("커스텀 구분자가 비어있으면 예외") | ||
| @ParameterizedTest | ||
| @ValueSource(strings = {"//\\n1,2,3", "//\\n"}) | ||
| void emptyCustomDelimiterTest(String input) { | ||
| IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, | ||
| () -> Delimiter.of(input)); | ||
| assertEquals(ErrorMessage.DELIMITER_MISSING, exception.getMessage()); | ||
| } | ||
|
|
||
| @DisplayName("잘못된 포맷 입력 시 기본 처리") | ||
| @Test | ||
| void invalidFormatTest() { | ||
| Delimiter delimiter = Delimiter.of("1#2#3"); | ||
| assertEquals("[,\n]", delimiter.getRegex()); | ||
| assertEquals("1#2#3", delimiter.extractNumbers("1#2#3")); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| package calculator; | ||
|
|
||
|
|
||
| import static org.assertj.core.api.Assertions.*; | ||
| import static org.junit.jupiter.api.Assertions.*; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.ValueSource; | ||
|
|
||
| import calculator.domain.Separator; | ||
| import calculator.message.ErrorMessage; | ||
|
|
||
| class SeparatorTest { | ||
|
|
||
| @DisplayName("기본 구분자 - 쉼표") | ||
| @ParameterizedTest | ||
| @ValueSource(strings = {"1,2,3", ",1,2,3"}) | ||
| void basic_separator_comma(String inputs) { | ||
| Separator separator = new Separator(inputs); | ||
| List<Integer> numbers = separator.getNumbers(); | ||
|
|
||
| assertThat(numbers).isEqualTo(List.of(1, 2, 3)); | ||
| } | ||
|
|
||
| @DisplayName("기본 구분자 - 개행") | ||
| @ParameterizedTest | ||
| @ValueSource(strings = {"1\n2\n3","\n1\n2\n3"}) | ||
| void basic_separator_newline(String inputs) { | ||
| //given | ||
|
|
||
| //when | ||
| Separator separator = new Separator(inputs); | ||
| List<Integer> numbers = separator.getNumbers(); | ||
|
|
||
| //then | ||
| assertThat(numbers).isEqualTo(List.of(1, 2, 3)); | ||
| } | ||
|
|
||
| @DisplayName("기본 구분자 - 세미클론") | ||
| @Test | ||
| void basic_seperator_semicolon() { | ||
| Separator separator = new Separator("//;\\n1;2;3"); | ||
| List<Integer> numbers = separator.getNumbers(); | ||
| assertEquals(List.of(1, 2, 3), numbers); | ||
| } | ||
|
|
||
| @DisplayName("음수값 예외") | ||
| @ParameterizedTest | ||
| @ValueSource(strings = {"1,-2,3","-1,1,1","-1,-2,3"}) | ||
| void exception_negative_number(String inputs) { | ||
| //given | ||
|
|
||
| //when, then | ||
| assertThatThrownBy(() -> new Separator(inputs)) | ||
| .isInstanceOf(IllegalArgumentException.class) | ||
| .hasMessage(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED); | ||
|
|
||
| // IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, | ||
| // () -> new Separator("1,-2,3")); | ||
| // assertEquals(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED, exception.getMessage()); | ||
| } | ||
|
|
||
| @DisplayName("숫자가 아닌 값이 들어왔을때 예외 처리") | ||
| @ParameterizedTest | ||
| @ValueSource(strings = {"//;\\na;2;3","a,2:3","@,2:3" }) | ||
| void exception_entered_non_numeric_value(String inputs) { | ||
| //given | ||
|
|
||
| //when, then | ||
| assertThatThrownBy(() -> new Separator(inputs)) | ||
| .isInstanceOf(IllegalArgumentException.class) | ||
| .hasMessage(ErrorMessage.ENTERED_NON_NUMERIC_VALUE); | ||
|
|
||
| // IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, | ||
| // () -> new Separator("1,a,3")); | ||
| // assertEquals(ErrorMessage.ENTERED_NON_NUMERIC_VALUE, exception.getMessage()); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
커스텀 구분자 처리를 정규식으로 구현해주셨는데요
"\\\\?n"패턴은"\n"이 아닌 문자'\' + 'n'의 조합을 인식하게 될거 같아요.즉,
"//;\\n1;2;3"같은 입력에는 잘 동작하지만 실제 콘솔 입력에서는 \n이 개행으로 처리되기 때문에 이 부분이 커버되지 않을 수 있을 것 같아요따라서
"//(.*?)\n(.*)"로 실제 개행 문자를 인식하도록 수정하면 어떨까 싶습니다There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분은 수정 하도록 하겠습니다 . 감사합니다 :)