우아한 테크코스 프리코스 - Week1

박지원·2024년 10월 25일

덧셈 계산기

java-calculator-precourse

구현할 기능 목록

  1. 기본 구분자로 덧셈 연산 (',', ':')
    • 사용자가 입력한 문자열을 쉼표(,)와 콜론(:)을 기준으로 분리한다.
    • 분리된 문자열을 숫자로 변환하고, 이를 모두 더한 결과를 반환한다.
    • 빈 문자열이 입력되면 0을 반환한다.
    • 숫자가 아닌 문자가 입력되거나 기본 구분자가 아닐 경우 에러 메시지 출력하고 종료한다.
    • 양수가 아닌 수가 포함되어있을 때, 에러 메시지 출력하고 종료한다.
package calculator;

import camp.nextstep.edu.missionutils.Console;

public class Application {
    public static void main(String[] args) {

        // 프로그램 시작 메시지
        System.out.println("덧셈할 문자열을 입력해 주세요");
        String input = Console.readLine();
        boolean isValidInput = true;
        boolean isNegative = false;

        // 덧셈 결과를 담을 변수
        int sum = 0;
        StringBuilder numberBuilder = new StringBuilder(); // 리스트 대신에 사용하는 StringBuilder

        // 빈 문자일 경우
        if (input.isEmpty()) {
            System.out.println("결과 : 0");
            return;
        }


        for (int i = 0; i < input.length(); i++) {
            char ch = input.charAt(i);

            if (ch == '-') {
                // 음수 부호가 나오면 isNegative = true 설정
                isNegative = true;
            } else if (Character.isDigit(ch)) {
                // 숫자인 경우 숫자 빌더에 추가
                numberBuilder.append(ch);
            } else if (ch == ':' || ch == ',') {
                // 기본 구분자를 만나면 그 전까지의 숫자를 합산
                if (!numberBuilder.isEmpty()) {
                    int number = Integer.parseInt(numberBuilder.toString());

                    // 음수 값인지 확인
                    if (isNegative) {
                        number = -number; // 음수로 변환
                    }

                    if (number <= 0) {
                        System.out.println("양수만 입력 가능합니다: " + number);
                        isValidInput = false;
                        break;
                    } else {
                        sum += number; // 변환한 숫자 합계에 더하기
                    }

                    numberBuilder.setLength(0); // 빌더 초기화
                    isNegative = false; // 음수 플래그 초기화
                }
            } else {
                // 잘못된 입력일 경우
                System.out.println("잘못된 입력입니다.");
                isValidInput = false;
                break;
            }
        }
        // 유효한 입력인 경우 결과 출력
        if (isValidInput) {
            if (!numberBuilder.isEmpty()) {
                int number = Integer.parseInt(numberBuilder.toString());

                // 마지막으로 남아 있는 숫자가 음수인지 확인
                if (isNegative) {
                    number = -number; // 음수로 변환
                }

                if (number < 0) {
                    System.out.println("양수만 입력 가능합니다: " + number);
                    return;
                } else {
                    sum += number; // 마지막 숫자 합산
                }
            }
            // 결과 출력
            System.out.println("결과 : " + sum);
        }
    }
}
  1. 커스텀 구분자로 덧셈 연산
    • 입력문 중에 "//"와 "\n" 사이에 정의한 구분자를 추출한다.
    • 커스텀 구분자가 여러 문자인 경우를 고려해, 구분자를 정확하게 분리한다.
    • 커스텀 구분자가 존재하는 경우, 기본 구분자인 쉼표(,)와 콜론(:)과 함께 해당 구분자를 사용해 숫자를 분리한다.
    • 커스텀 구분자가 없는 경우, 기본 구분자만을 사용한다.
package calculator;

import camp.nextstep.edu.missionutils.Console;

public class Application {
    public static void main(String[] args) {

        System.out.println("덧셈할 문자열을 입력해 주세요");
        String input = Console.readLine();

        boolean isValidInput = true;
        boolean isNegative = false;
        int sum = 0;
        StringBuilder numberBuilder = new StringBuilder(); // 리스트 대신에 사용하는 StringBuilder

        // 빈 문자일 경우
        if (input.isEmpty()) {
            System.out.println("결과 : 0");
            return;
        }

        // 커스텀 구분자 설정
        String customDelimiter = null;

        // 커스텀 구분자 추출
        if (input.startsWith("//")) {
            // Escape 문자와 헷갈리는 문제 발생 - > "\\n"을 실제 개행 문자로 변환
            input = input.replace("\\n", "\n");

            if (input.startsWith("//")) {
                // 커스텀 구분자가 여러 글자일 경우를 위한 start, end 인덱스
                int delimiterStartIndex = input.indexOf("//") + 2;
                int delimiterEndIndex = input.indexOf("\n");

                if (delimiterEndIndex > delimiterStartIndex) {
                    customDelimiter = input.substring(delimiterStartIndex, delimiterEndIndex); // 커스텀 구분자 추출
                    input = input.substring(delimiterEndIndex + 1); // 구분자 이후의 입력값만 남김
                }
            }
        }

        for (int i = 0; i < input.length(); i++) {
            char ch = input.charAt(i);

            if (ch == '-') {
                // 음수 부호가 나오면 isNegative = true 설정
                isNegative = true;
            } else if (Character.isDigit(ch)) {
                // 숫자인 경우 숫자 빌더에 추가
                numberBuilder.append(ch);
            } else if (ch == ':' || ch == ',' || (customDelimiter != null && input.substring(i).startsWith(customDelimiter))) {
                // 기본 구분자 또는 커스텀 구분자를 만나면 그 전까지의 숫자를 합산
                if (!numberBuilder.isEmpty()) {
                    int number = Integer.parseInt(numberBuilder.toString());

                    // 음수 값인지 확인
                    if (isNegative) {
                        number = -number; // 음수로 변환
                    }

                    // 양수가 아닌 수 처리
                    if (number <= 0) {
                        System.out.println("양수만 입력 가능합니다: " + number);
                        isValidInput = false;
                        break;
                    } else {
                        sum += number; // 변환한 숫자 합계에 더하기
                    }

                    numberBuilder.setLength(0); // 빌더 초기화
                    isNegative = false; // 음수 플래그 초기화
                }

                // 커스텀 구분자일 경우 구분자의 길이만큼 건너뜀
                if (customDelimiter != null && input.substring(i).startsWith(customDelimiter)) {
                    i += customDelimiter.length() - 1;
                }
            } else {
                // 잘못된 입력일 경우
                System.out.println("잘못된 입력입니다.");
                isValidInput = false;
                break;
            }
        }

        // 유효한 입력인 경우 결과 출력
        if (isValidInput) {
            if (!numberBuilder.isEmpty()) {
                int number = Integer.parseInt(numberBuilder.toString());

                // 마지막으로 남아 있는 숫자가 음수인지 확인
                if (isNegative) {
                    number = -number; // 음수로 변환
                }

                if (number <= 0) {
                    System.out.println("양수만 입력 가능합니다: " + number);
                    return;
                } else {
                    sum += number; // 마지막 숫자 합산
                }
            }
            // 결과 출력
            System.out.println("결과 : " + sum);
        }
    }
}
  1. 예외 처리
    • 입력된 문자열에 잘못된 형식이나 구분자가 포함된 경우, IllegalArgumentException을 발생시켜 프로그램을 종료한다.
    • 양수가 아닌 수가 포함되어있을 때, 에러 메시지 대신 IllegalArgumentException을 발생시켜 프로그램을 종료한다.
package calculator;

import camp.nextstep.edu.missionutils.Console;

public class Application {
    public static void main(String[] args) {

        System.out.println("덧셈할 문자열을 입력해 주세요");
        String input = Console.readLine();

        boolean isNegative = false;
        int sum = 0;
        StringBuilder numberBuilder = new StringBuilder(); // 리스트 대신에 사용하는 StringBuilder

        // 빈 문자일 경우
        if (input.isEmpty()) {
            System.out.println("결과 : 0");
            return;
        }

        // 커스텀 구분자 설정
        String customDelimiter = null;

        // 커스텀 구분자 추출
        if (input.startsWith("//")) {
            // Escape 문자와 헷갈리는 문제 발생 - > "\\n"을 실제 개행 문자로 변환
            input = input.replace("\\n", "\n");

            if (input.startsWith("//")) {
                // 커스텀 구분자가 여러 글자일 경우를 위한 start, end 인덱스
                int delimiterStartIndex = input.indexOf("//") + 2;
                int delimiterEndIndex = input.indexOf("\n");

                if (delimiterEndIndex > delimiterStartIndex) {
                    customDelimiter = input.substring(delimiterStartIndex, delimiterEndIndex);  // 커스텀 구분자 추출
                    input = input.substring(delimiterEndIndex + 1);  // 구분자 이후의 입력값만 남김
                }
            }

            for (int i = 0; i < input.length(); i++) {
                char ch = input.charAt(i);

                if (ch == '-') {
                    // 음수 부호가 나오면 isNegative = true 설정
                    isNegative = true;
                } else if (Character.isDigit(ch)) {
                    // 숫자인 경우 숫자 빌더에 추가
                    numberBuilder.append(ch);
                } else if (ch == ':' || ch == ',' || (customDelimiter != null && input.substring(i).startsWith(customDelimiter))) {
                    // 기본 구분자 또는 커스텀 구분자를 만나면 그 전까지의 숫자를 합산
                    if (!numberBuilder.isEmpty()) {
                        int number = Integer.parseInt(numberBuilder.toString());

                        // 음수 값인지 확인
                        if (isNegative) {
                            number = -number; // 음수로 변환
                        }

                        // 양수가 아닌 수 처리
                        if (number <= 0) {
                            throw new IllegalArgumentException("양수만 입력 가능합니다: " + number);
                        }

                        sum += number;
                        numberBuilder.setLength(0);
                        isNegative = false;
                    }

                    // 커스텀 구분자일 경우 구분자의 길이만큼 건너뜀
                    if (customDelimiter != null && input.substring(i).startsWith(customDelimiter)) {
                        i += customDelimiter.length() - 1;
                    }
                } else {
                    throw new IllegalArgumentException("잘못된 입력입니다: " + ch);
                }
            }

            // 마지막 숫자 처리
            if (!numberBuilder.isEmpty()) {
                int number = Integer.parseInt(numberBuilder.toString());

                if (isNegative) {
                    number = -number;
                }

                if (number <= 0) {
                    throw new IllegalArgumentException("양수만 입력 가능합니다: " + number);
                }

                sum += number;
            }

            System.out.println("결과 : " + sum);
        }
    }
}

이렇게까지 했는데 에러 발생!!
잘 보시면, 위에 중복 코드가 있어요... ㅎㅎ 이 커밋 컨벤션과 룰이 익숙하지 않아서 프로젝트를 새로 파서 옮기느라 중복코드가 발생한 것 같습니다. 하지만 옮기고나서 다시 테스트 안하고 올린 제 잘못이죠...

fix(calculator): 커스텀 구분자 및 기본 구분자 동시 처리 에러 해결

- 커스텀 구분자와 기본 구분자(쉼표, 콜론) 모두 사용하여 숫자를 분리하는 기능 추가
- 커스텀 구분자와 기본 구분자 적용 방식을 개선하여 유연한 입력 처리 가능
- Test 실행해 프로그램 확인 진행
package calculator;

import camp.nextstep.edu.missionutils.Console;

public class Application {
    public static void main(String[] args) {

        System.out.println("덧셈할 문자열을 입력해 주세요");
        String input = Console.readLine();

        boolean isNegative = false;
        int sum = 0;
        StringBuilder numberBuilder = new StringBuilder(); // 리스트 대신에 사용하는 StringBuilder

        if (input.isEmpty()) {
            System.out.println("결과 : 0");
            return;
        }

        // 커스텀 구분자 설정
        String customDelimiter = null;

        // 커스텀 구분자 추출
        if (input.startsWith("//")) {
            input = input.replace("\\n", "\n");  // 개행 문자 처리

            // 커스텀 구분자가 여러 글자일 경우를 위한 처리
            int delimiterStartIndex = input.indexOf("//") + 2;
            int delimiterEndIndex = input.indexOf("\n");

            if (delimiterEndIndex > delimiterStartIndex) {
                customDelimiter = input.substring(delimiterStartIndex, delimiterEndIndex);  // 커스텀 구분자 추출
                input = input.substring(delimiterEndIndex + 1);  // 구분자 이후의 입력값만 남김
            }
        }

        // for 루프는 항상 실행되도록 수정 (커스텀 구분자 여부 상관없이)
        for (int i = 0; i < input.length(); i++) {
            char ch = input.charAt(i);

            if (ch == '-') {
                // 음수 부호가 나오면 isNegative = true 설정
                isNegative = true;
            } else if (Character.isDigit(ch)) {
                // 숫자인 경우 숫자 빌더에 추가
                numberBuilder.append(ch);
            } else if (ch == ':' || ch == ',' || (customDelimiter != null && input.substring(i).startsWith(customDelimiter))) {
                // 기본 구분자 또는 커스텀 구분자를 만나면 그 전까지의 숫자를 합산
                if (!numberBuilder.isEmpty()) {
                    int number = Integer.parseInt(numberBuilder.toString());

                    // 음수 값인지 확인
                    if (isNegative) {
                        number = -number; // 음수로 변환
                    }

                    // 양수가 아닌 수 처리
                    if (number <= 0) {
                        throw new IllegalArgumentException("양수만 입력 가능합니다: " + number);
                    }

                    sum += number;
                    numberBuilder.setLength(0); // 빌더 초기화
                    isNegative = false; // 음수 플래그 초기화
                }

                // 커스텀 구분자일 경우 구분자의 길이만큼 건너뜀
                if (customDelimiter != null && input.substring(i).startsWith(customDelimiter)) {
                    i += customDelimiter.length() - 1;
                }
            } else {
                // 잘못된 입력일 경우 예외 발생
                throw new IllegalArgumentException("잘못된 입력입니다: " + ch);
            }
        }

        // 마지막 숫자 처리
        if (!numberBuilder.isEmpty()) {
            int number = Integer.parseInt(numberBuilder.toString());

            if (isNegative) {
                number = -number;
            }

            if (number <= 0) {
                throw new IllegalArgumentException("양수만 입력 가능합니다: " + number);
            }

            sum += number;
        }

        // 결과 출력
        System.out.println("결과 : " + sum);
    }
}

그래서 이게 최종입니다!

저의 회고를 정리하자면...

Readme 를 작성할때 첫 커밋에 어떤 기능을 먼저 넣을지를 고민하게 되었습니다. 처음에는 깜빡하고 입력문에 대한 양수 검증하는 부분을 만들지 않았습니다. 그래서 레포지토리를 삭제하고 파일을 다시 클론 받고 진행을 기존 코드를 옮기다 보니, 중복해서 코드를 넣는 일이 발생했습니다.

이 경험을 통해, 요구상황에 맞게 세부적으로 기능을 나누고 ReadMe 를 작성 해야겠다는 생각을 하게 되었고, 테스트의 중요성을 알게되었습니다.

처음이라 그런지 많이 그 컨벤션과 룰을 맞추려고 하다보니, 오히려 더 실수를 하고 꼼꼼하게 테스트 하지 못했습니다. 앞으로 진행할 미션에 있어서는 너무 완벽하게 하려고 하지말고, 조금 더 유연하게 과제를 진행 해야겠다는 생각을 하게되었습니다. 또한 늘 테스트 코드를 진행하고 PR 을 올려야겠다는 큰 깨달음을 얻었습니다.

이리고 나서 공통 피드백을 받았습니다

공통 피드백

요구 사항을 정확하게 준수한다

  • 요구 사항을 준ㅅ하지 않아서 새로 파일을 만들어 문제를 해결했습니다. 물론 다른 방법도 있었겠지만, 이미 PR 을 올렸고 제 성격상 완벽을 추구해서 다시 하느라 더 시간이 오래걸렸습니다. 이번 과제에서는 이를 좀 더 세분화하고, 더 잦은 커밋을 해야할 것 같습니다.

기본적인 Git 명령어 숙지

  • Git 에 대한 간단한 세미나를 최근에 들어서 Git 은 특히 어렵진 않은데요. 시간이나면 테코톡을 한 번 보면 좋을 것 같습니다

Git 관리할 자원을 고려한다

  • IntelliJ IDEA의 .idea 폴더와 Eclipse의 .metadata 폴더도 IDE에서 자동으로 생성하는 폴더이므로 Git으로 관리할 필요가 없다. Git에 코드를 추가할 때는 Git을 통해 형상 관리해야 하는 코드인지 고려하는 것이 좋다. 또한 .gitignore에 대해서도 알아본다.
  • .idea 폴더와 다른 설정 파일들을 .gitignore 추가하고 진행해야겠습니다. 또한 .gitignore 에 들어가야하는 파일 알아봐야겠습니다

커밋 메시지를 의미 있게 작성한다

https://meetup.nhncloud.com/posts/106

  • 제목과 본문을 한 줄 띄워 분리하기
  • 제목은 영문 기준 50자 이내로
  • 제목 첫글자를 대문자로
  • 제목 끝에 . 금지
  • 제목은 명령조로
  • 본문은 영문 기준 72자마다 줄 바꾸기
  • 본문은 어떻게보다 무엇을, 왜에 맞춰 작성하기

커밋 메시지에 이슈 또는 풀 리퀘스트 번호를 포함하지 않는다

  • 원본 저장소와 관련 없는 이슈 또는 PR 을 미칠 수 있다. 따라서 이슈, PR 번호를 포함하지 않는다

오류를 찾을 때 출력 함수 대신 디버거를 사용한다

이름을 통해 의도를 드러낸다

  • 이름을 통해 변수의 역할, 함수의 역할, 클래스의 역할에 대한 의도를 드러내기 위해 노력하자

축약하지 않는다

  • 클래스와 메서드 이름을 한 두 단어로 유지하려고 노력하고 문맥을 중복하는 이름을 자제하자.

공백도 코딩 컨벤션이다

공백 라인을 의미 있게 사용한다

스페이스와 탭을 혼동하지 않는다

의미 없는 주석을 달지 않는다

코드 포매팅을 사용한다

IntelliJ IDEA: ⌥⌘L, Ctrl+Alt+L
Eclipse: ⇧⌘F, Ctrl+Shift+F
Visual Studio Code: ⇧⌥F, Shift+Alt+F

Java에서 제공하는 API를 적극 활용한다

  • 함수(메서드)를 직접 구현하기 전에 API에서 해당 함수를 제공하는지 확인한다.

배열 대신 컬렉션을 이용한다

  • 컬렉션(List, Set, Map 등)을 사용하면 다양한 API를 사용하여 데이터를 조작할 수 있다.

피드백 영상이 따로 있는데.. 이건 나중에 보도록 해야겠다

0개의 댓글