-
Notifications
You must be signed in to change notification settings - Fork 1k
[문자열 덧셈 계산기] 한임경 미션 제출합니다. #1103
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 3 commits
5aee2d2
7fc586f
c420bfe
25f1471
cd7edea
34f86c9
8efb09d
dce87e5
ee97410
74117da
0af0764
3d26714
36bdbf8
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,122 @@ | ||
| # java-calculator-precourse | ||
| # java-calculator-precourse | ||
| # 문자열 덧셈 계산기 | ||
|
|
||
| 입력된 문자열을 구분자를 기준으로 분리하여 합계를 계산하는 프로그램입니다. | ||
| 절차지향적으로 기능을 완성한 뒤, 객체지향 원칙에 따라 리팩토링하며 구조를 개선했습니다. | ||
|
|
||
| --- | ||
|
|
||
| ## 미션 개요 | ||
|
|
||
| - 우아한테크코스 프리코스 1주차 과제 | ||
| - 문자열 입력을 받아 구분자 기준으로 숫자를 분리하고, 합을 계산하는 프로그램 | ||
| - 기본 구분자( `,` , `:` )와 커스텀 구분자(`//;\n`) 모두 지원 | ||
| - 잘못된 입력(음수, 문자 등)에 대해 예외 발생 | ||
|
|
||
| --- | ||
|
|
||
| ## 기능 요구사항 | ||
|
|
||
| - 입력된 문자열에서 숫자를 추출하여 덧셈 결과를 반환한다. | ||
| - 기본 구분자(쉼표 `,` , 콜론 `:`)를 사용한다. | ||
| - 커스텀 구분자를 지정할 수 있다. (예: `//;\n1;2;3`) | ||
| - 잘못된 입력(문자, 음수, 공백 등)은 `IllegalArgumentException`을 발생시킨다. | ||
|
|
||
| --- | ||
|
|
||
| ## 구현 단계별 정리 | ||
|
|
||
| 이 프로젝트는 **절차지향적으로 구현한 뒤**, | ||
| **리팩토링을 통해 객체지향적으로 개선한 과정**을 담고 있습니다. | ||
| 핵심 로직을 세분화하고, 역할에 따라 클래스를 분리하며 점진적으로 구조를 발전시켰습니다. | ||
|
|
||
| --- | ||
|
|
||
| ### 1단계: 핵심 로직 구현 (절차지향 → 기능 분리) | ||
|
|
||
| - 문자열을 입력받아 구분자 기준으로 분리하고, 숫자를 더하는 기본 기능 구현 | ||
| - 커스텀 구분자(`//;\n`) 처리 및 음수 검증 등 비즈니스 규칙을 절차적으로 구현 | ||
| - 기능 단위로 메서드를 분리하여 테스트 용이성 확보 | ||
|
|
||
| --- | ||
|
|
||
| ### 2단계: 구조화 및 객체지향 리팩토링 | ||
|
|
||
| - **DelimiterInfo**: 구분자 정규식과 숫자 문자열을 담는 DTO | ||
| - **DelimiterParser / DefaultDelimiterParser**: 입력 문자열 분석 및 `DelimiterInfo` 반환 | ||
| - **NumberParser**: 문자열을 정수로 변환하며 예외 처리 | ||
| - **Validator**: 음수 값 검증 후 `IllegalArgumentException` 발생 | ||
| - **StringCalculator** | ||
| - 구성 요소를 주입받아 순차적으로 호출 | ||
| - 문자열 분리 → 숫자 변환 → 검증 → 합산 로직 수행 | ||
| - **Calculator 인터페이스** | ||
| - `calculate(String input)` 메서드로 계산 행위의 규약 정의 | ||
|
|
||
| --- | ||
|
|
||
| ### 3단계: 입출력 연결 및 실행 환경 구성 | ||
|
|
||
| - **InputHandler** | ||
| - `Console.readLine()`으로 사용자 입력 처리 및 자원 정리 | ||
| - **Application (main)** | ||
| - 입력 수집 → `StringCalculator` 실행 → 결과 출력 | ||
| - `try-catch-finally` 구문을 통해 예외 처리 및 리소스 정리 | ||
|
|
||
| --- | ||
|
|
||
| ## 예외 상황 | ||
|
|
||
| - 문자가 포함된 입력값 (예: `"a,2,3"`, `"1b:4"`) | ||
| - 음수 입력값 (예: `"-1,2,3"`) | ||
| - 구분자가 연속된 경우 (예: `"1,,2"`) | ||
| - 소수 입력 (예: `"1.5,2"`) | ||
| - 정수 범위 초과 입력 | ||
| - 빈 문자열 또는 공백 입력 (예: `""`, `" "`) | ||
| - `null` 또는 입력이 존재하지 않는 경우 | ||
| - 숫자만 단독 입력된 경우 | ||
|
|
||
| --- | ||
|
|
||
| ## 미션 진행 방향 | ||
|
|
||
| 이 미션은 **절차지향적으로 작동하는 코드를 먼저 작성한 뒤**, | ||
| 객체지향 원칙(OOP)에 따라 **역할과 책임 중심으로 구조를 개선**하는 것을 목표로 합니다. | ||
|
|
||
| 1. 절차지향적으로 기능을 완성한다. | ||
| 2. 역할과 책임을 분리하여 구조를 개선한다. | ||
| 3. 테스트 가능한 구조로 리팩토링한다. | ||
|
|
||
| --- | ||
|
|
||
| ## 커밋 컨벤션 | ||
|
|
||
| AngularJS Commit Message 규칙을 참고했습니다. | ||
| 커밋은 **기능 단위**로 나누어 작성합니다. (예: 기본 구분자 처리, 커스텀 구분자 추가, 음수 예외 처리 등) | ||
|
|
||
| **Allowed `<type>`** | ||
|
|
||
| - feat: 새로운 기능 추가 | ||
| - fix: 버그 수정 | ||
| - docs: 문서 수정 | ||
| - style: 코드 포맷팅 | ||
| - refactor: 코드 리팩토링 | ||
| - test: 테스트 코드 추가 | ||
| - chore: 빌드, 설정 등 유지보수 작업 | ||
|
|
||
| > 콜론(`:`) 뒤에는 반드시 공백 한 칸을 둡니다. | ||
|
|
||
| --- | ||
|
|
||
| ## 어떤 점에 집중했는가 | ||
|
|
||
| - 절차지향적 설계로 기본 동작 완성 능력 향상 | ||
| - 예외 처리와 입력 검증을 체계적으로 구현 | ||
| - 객체지향적 리팩토링을 통한 책임 분리 연습 | ||
| - 클린 코드 원칙을 적용하고 유지보수성을 고려한 설계 | ||
|
|
||
| --- | ||
|
|
||
| ## 요약 | ||
|
|
||
| 이 프로젝트는 문자열 계산기를 절차지향적으로 구현한 뒤, | ||
| 객체지향 설계 원칙에 따라 구조를 개선하며 **깨끗하고 확장 가능한 코드**로 발전시키는 과정을 담고 있습니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,81 @@ | ||
| package calculator; | ||
|
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. 감사합니다 2주차때 적용해보도록 하겠습니다! |
||
|
|
||
| import java.util.regex.Pattern; | ||
| import camp.nextstep.edu.missionutils.Console; | ||
|
|
||
| public class Application { | ||
|
|
||
| public static void main(String[] args) { | ||
| // TODO: 프로그램 구현 | ||
| System.out.println("===덧셈할 문자열을 입력해주세요==="); | ||
| String input = Console.readLine(); // Console.readLine() 사용 | ||
|
|
||
| try { | ||
| if (input == null || input.trim().isEmpty()) { | ||
| System.out.println("결과: 0"); | ||
| return; | ||
| } | ||
|
|
||
| String basicDelimiterRegex = "[,:]"; | ||
| String finalDelimiterRegex = basicDelimiterRegex; | ||
| String numbersToSplit = input; | ||
|
|
||
| // 2. 커스텀 구분자 | ||
| if (input.startsWith("//")) { | ||
| int delimiterEndIndex = input.indexOf("\n"); | ||
|
|
||
| if (delimiterEndIndex == -1) { | ||
| throw new IllegalArgumentException("커스텀 구분자 선언 후 반드시 줄 바꿈(\\n)을 해야합니다."); | ||
| } | ||
| String customDelimiter = input.substring(2, delimiterEndIndex); | ||
|
|
||
| finalDelimiterRegex += "|" + Pattern.quote(customDelimiter); | ||
|
|
||
| numbersToSplit = input.substring(delimiterEndIndex + 1); | ||
| } | ||
|
|
||
|
|
||
| String[] parts = numbersToSplit.split(finalDelimiterRegex); | ||
|
|
||
| int sum = 0; | ||
| StringBuilder negativeNumbers = new StringBuilder(); | ||
|
|
||
| for (String part : parts) { | ||
| String trimmedS = part.trim(); | ||
|
|
||
| if (trimmedS.isEmpty()) { | ||
| continue; | ||
| } | ||
|
|
||
| if (!trimmedS.matches("^-?\\d+$")) { | ||
| throw new IllegalArgumentException("잘못된 입력 형식입니다. 숫자만 입력이 가능합니다: " + trimmedS); | ||
| } | ||
|
|
||
| int num = Integer.parseInt(trimmedS); | ||
|
|
||
| if (num < 0) { | ||
| if(negativeNumbers.length() > 0) { | ||
| negativeNumbers.append(", "); | ||
| } | ||
| negativeNumbers.append(num); | ||
| continue; | ||
| } | ||
|
|
||
| sum += num; | ||
| } | ||
|
|
||
| if (negativeNumbers.length() > 0) { | ||
| throw new IllegalArgumentException("음수는 입력이 불가능합니다: " + negativeNumbers.toString()); | ||
| } | ||
|
|
||
| System.out.println("결과: " + sum); | ||
|
|
||
| } catch (IllegalArgumentException e) { | ||
| System.out.println("오류: " + e.getMessage()); | ||
| } catch (Exception e) { | ||
| System.out.println("예상치 못한 오류가 발생했습니다."); | ||
| // e.printStackTrace(); | ||
| } finally { | ||
| Console.close(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package calculator; | ||
|
|
||
| import java.util.regex.Pattern; | ||
|
|
||
| public class DefaultDelimiterParser implements DelimiterParser { | ||
|
|
||
| private static final String BASIC_DELIMITER_REGEX = "[,:]"; | ||
|
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. 리스트로 만들면 확실히 구분자가 추가 되거나 변경되어도 리스트만 수정하면 좋겠네요 감사합니다! 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. 변수명에 대해 몇 가지 궁금한 점이 있습니다!
|
||
|
|
||
| @Override | ||
| public DelimiterInfo parse(String input) { | ||
| String finalDelimiterRegex = BASIC_DELIMITER_REGEX; | ||
| String numbersToSplit = input; | ||
|
|
||
| if(input.startsWith("//")) { | ||
| int delimiterEndIndex = input.indexOf("\n"); | ||
|
|
||
| if (delimiterEndIndex == -1) { | ||
| throw new IllegalArgumentException("커스텀 구분자 선언 후 반드시 줄 바꿈(\\n)을 해야합니다."); | ||
| } | ||
|
|
||
| String customDelimiter = input.substring(2, delimiterEndIndex); | ||
| finalDelimiterRegex += "|" + Pattern.quote(customDelimiter); | ||
|
|
||
| numbersToSplit = input.substring(delimiterEndIndex + 1); | ||
| } | ||
|
|
||
| return new DelimiterInfo(finalDelimiterRegex, numbersToSplit); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package calculator; | ||
|
|
||
|
|
||
| public class DelimiterInfo { | ||
|
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. 구분자에 대한 내용을 패키지로 한번 더 묶으면 가독성이 좋아질 것 같아요. 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. Info같은 경우 전부 객체를 생성할 필요없이 클래스 래벨에서 처리할 수 있을 것 같아요
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. 좋은 피드백 감사합니다! |
||
|
|
||
| final private String delimiterRegex; | ||
| final private String numbersString; | ||
|
|
||
| public DelimiterInfo(String delimiterRegex, String numbersString) { | ||
| this.delimiterRegex = delimiterRegex; | ||
| this.numbersString = numbersString; | ||
| } | ||
|
|
||
| public String getDelimiterRegex() { | ||
| return delimiterRegex; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package calculator; | ||
|
|
||
| public interface DelimiterParser { | ||
|
|
||
| DelimiterInfo parse(String input); | ||
|
|
||
| } | ||
|
|
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.
파일 한 곳에 여러 기능들이 몰려있는 느낌입니다..! 클래스의 인스턴스 생성, Caclulator의 동작 메서드 등 다양하게 역할을 분배해도 좋을 것 같습니다.
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.
감사합니다! 2주차때 적용해보도록 하겠습니다 ㅎㅎ