wisePocket

[Algorithm✅️] 4칙연산을 해주는 계산기 - 혼자 만들기+ChatGPT의 피드백 적용 본문

Java & Algorithm/Algorithm Practice

[Algorithm✅️] 4칙연산을 해주는 계산기 - 혼자 만들기+ChatGPT의 피드백 적용

ohnyong 2023. 8. 1. 16:14

몇시간 전 ChatGPT로부터 지역, 전역 변수를 활용한 반복문 탈출을 배웠다.

이를 통해서 완벽하게 내가 원하던 구성들을 갖춘 계산기를 구현 할 수 있었다. 이전에 CLI 프로젝트를 만들었을 때도 3중 메뉴형태였는데 지역변수, 전역변수에 대한 이해도가 부족했었나보다. 응용으로 지역에서 전역 변수로 특정값을 보내고, 파라미터를 활용한 메서드 이용도 이번 연습하면서 많은것을 얻어서 뿌듯했다.

완성된 계산기는 다음과 같다.


1. Main.java

아무것도 없다.

이것이 내가 바라던 형태로 객체 생성, 호출 이외에 드러나는 것이 하나도 없도록 구현해 볼 수 있었다.
package Algorithm;

public class Main {
    public static void main(String[] args) {
        //계산기 객체 생성
        Solution solution = new Solution();
        //프로그램 이니시 메서드 호출
        solution.runCalculator();
        //끝
    }
}

 

2. Solution.java

계산기의 설계도

Solution 클래스는 계산기의 모든 기능을 가지고있다. 그렇기 때문에 Main.java에서 1개의 객체로 해당 기능을 모두 사용 할 수 있다.

설계도는 크게 3개로 구분되어있다.
이 안에서 메서드 부분은 다시 3개로 구분된다.
전체 구조는 다음과 같다.

1. 전역 변수 선언 부분

2. 기본 생성자 부분
3. 메서드 부분
     - 프로그램 메인 메서드

     - VIEW, 재시작, 종료 등 기능적 로직 구현 메서드 모음
     - 핵심 연산 기능 구현 메서드 모음

ChatGPT의 조언 이후 개선한 계산기 개발 내용
1. for(;;) 무한루프의 가시성이 좋지 않아 while을 통한 무한루프로 수정했다. -> 확실히 코드를 작성하는 나도 직관적으로 구분하기 편했다.
2. 전역 변수였던 selectNum을 지역 변수(대상 반복문 내)로 삽입시키면서 continue를 통한 반복문 재진입 시 해당 변수를 초기화 하여 반복문 구동에 지장이 없도록 적용시켰다. -> 이 부분이 해결되면서 프로그램을 만드는 재미가 생겼다.
3. 전역 변수와 지역 변수의 차이를 이해하면서부터 오히려 전역 변수를 활용하여 유효성 검사 부분에 응용 할 수 있었다. num1, num2를 입력 받을 때 유효성 검사가 확인되어 통과된 계산이 준비된 숫자는 전역 변수로 전달하여 메서드 내에서 혼용되지 않도록 재설계 했다. 이 부분에서 this.num1 = num1; 처럼 전역 변수에 접근하는 방법도 예전에 배웠던 것을 응용하여 적용했다.
4. 프로그램의 전반적인 틀을 갖추기 위해서 CLI지만 사용자의 입장에서 가시적인 UI를 구성했다.
5. 숫자 범위 잘못 입력한 경우, 계산 후, 잘못된 값 입력 후 와 같은 특정 잘못된 접근이나 이용에 불편한 포인트들이 있다. 해당 부분마다 프로그램의 사용 여부를 체크하는 tryAgain() 메서드를 호출하게 하여 사용성에도 신경을 썼다.
6. View 부분을 구성하는 System의 print메서드는 자칫 기능 메서드에서 섞여있어 기능 개발에 거슬리기도 한다. 따라서 View페이지도 모두 별도의 메서드를 구성 showMenu(), showOutput(), tryAgain(), exit() 하여 기능 메서드 내에서는 print호출을 최소화시키고 메서드를 호출하는 쪽으로 개선했다.
7. printf 포맷을 활용하여 전달받은 파라미터를 쉽게 출력 할 수 있도록 간결하게 출력문을 개선했다.
package Algorithm;

import java.util.Scanner;

public class Solution {
    //note* 전역(Global) 변수로 선언되어 있어서 메뉴 변경이 어려웠던 부분
    //note* 메뉴 변경과 연결 된 selectNum을 해당 메서드 내부에 지역 변수로 반복문에서 초기화를 시키도록 재 배치 함.
    //int selectNum;

    //note* 필요없는 중복 변수인 calNum 제거, selectNum이 조건문을 혼자 컨트롤 할 수 있음
    //int calNum;

    //연산하고자하는 두 수의 유효성검사 -50000~50000 범위를 체크한 후, 계산할 준비가 된 숫자를 담을 *전역 변수*
    int num1; //입력 1
    int num2; //입력 2
    int result; //결과
    Scanner sc = new Scanner(System.in); //입력용 스캐너

    //Constructor 생성
    public Solution() {
        System.out.println("Calculator is ready to use");
    }

    //--START----------[프로그램 핵심 비지니스 로직 부분]-------------------
    //계산기 메인 실행 메서드 runCalculator()
    void runCalculator() {
        while (true) {
            // 메뉴 호출
            showMenu();

            //selectNum을 지역변수로 만들어서 continue로 반복 되어 돌아 올 때, 해당 값을 다시 초기화 할 수 있도록
            int selectNum = sc.nextInt();

            //Menu 선택하는 부분
            if (selectNum >= 1 || selectNum <= 5) {
                if (selectNum == 1) {
                    System.out.println("더하기 연산을 시작합니다.");
                    inputNum(selectNum);
                    continue;
                } else if (selectNum == 2) {
                    System.out.println("빼기 연산을 시작합니다.");
                    inputNum(selectNum);
                    continue;
                } else if (selectNum == 3) {
                    System.out.println("곱하기 연산을 시작합니다.");
                    inputNum(selectNum);
                    continue;
                } else if (selectNum == 4) {
                    System.out.println("나누기 몫 연산을 시작합니다.");
                    inputNum(selectNum);
                    continue;
                } else if (selectNum == 5) {
                    System.out.println("나누기 나머지 연산을 시작합니다.");
                    inputNum(selectNum);
                    continue;
                } else if (selectNum == 0) {
                    exit();
                    break;
                } else {
                    System.out.println("잘못된 값을 입력하셨습니다.");
                    tryAagin();
                }
            }
        }
    }

    //계산 할 2개의 값을 입력 받는 메서드 inputNum()
    void inputNum(int selectNum) {
        //note* 기존 중복되는 의머없는 변수인 calNum을 모두 제거함
        //note* calculate(calNum, ...) 메서드의 매개 변수도 모두 calculate(selectNum, ...)으로 변경하여 간결하게 작성함.
        //int calNum = selectNum

        //----Num1 입력 및 유효성 검사 부분----
        while (true) {
            //note* 전역 변수였던 num1을 반복문 내 지역 변수로 만들어서 유효성검사의 초기화를 진행
            int num1 = 0;
            //Num1 입력 및 입력 가능 범위 유효성검사
            System.out.print("num1을 입력해주세요. ==> ");
            num1 = sc.nextInt();
            if (num1 <= -50000 || num1 >= 50000) {
                System.out.println("입력 값은 -50000~50000 범위로만 입력해주세요.");
                continue;
            }
            //note* 지역변수인 num1(반복문내)의 유효성 검사가 통과하면, 전역 변수인 num1에 할당하면서 계산기에 전달 준비
            this.num1 = num1;
            break;
        }

        //----Num2 입력 및 유효성 검사 부분----
        while (true) {
            //note* 전역 변수였던 num2을 반복문 내 지역 변수로 만들어서 유효성검사의 초기화를 진행
            int num2 = 0;
            //Num2 입력 및 입력 가능 범위 유효성검사
            System.out.print("num2를 입력해주세요. ==> ");
            num2 = sc.nextInt();
            if (num2 <= -50000 || num2 >= 50000) {
                System.out.println("입력 값은 -50000~50000 범위로만 입력해주세요.");
                continue;
            }
            //note* 지역변수인 num2(반복문내)의 유효성 검사가 통과하면, 전역 변수인 num2에 할당하면서 계산기에 전달 준비
            this.num2 = num2;
            break;
        }
        //note* 유효성 검사가 완료된 Global 스코프의 준비된 계산 숫자 num1, num2을 파라미터에 담아 계산 메서드에 전달
        calculate(selectNum, num1, num2);
    }

    //메뉴에 따라 선택된 연산 메서드에 연결 및 반환 된 결과를 출력 하는 메서드 calculate()
    //note* inputNum() 메서드에서 유효성검사를 통과하여 전역변수에 담긴 num1, num2는 이곳으로 옵니다.
    int calculate(int selectNum, int num1, int num2) {
        if (selectNum == 1) {
            result = summary(num1, num2);
            showOutput(selectNum, num1, num2, result);
        } else if (selectNum == 2) {
            result = subtract(num1, num2);
            showOutput(selectNum, num1, num2, result);
        } else if (selectNum == 3) {
            result = multiplication(num1, num2);
            showOutput(selectNum, num1, num2, result);
        } else if (selectNum == 4) {// int == 4
            result = divisionQuiotient(num1, num2);
            showOutput(selectNum, num1, num2, result);
        } else if (selectNum == 5) {// int == 4
            result = divisionRemainder(num1, num2);
            showOutput(selectNum, num1, num2, result);
        } else {
            exit();
        }
        return result;
    }
    //--END------------[프로그램 핵심 비지니스 로직 부분]-------------------


    //--START----------[VIEW, OUTPUT PRINT, EXIT 기능적 부분]-------------------
    // Menu판 View 구성 메서드 showMenu()
    void showMenu() {
        System.out.println("==========================");
        System.out.println("||\t\t김인용의 계산기\t\t||");
        System.out.println("|| 원하시는 연산을 선택해주세요 ||");
        System.out.println("==========================");
        System.out.println("||\t[1] 더하기\t\t\t||");
        System.out.println("||\t[2] 빼기\t\t\t\t||");
        System.out.println("||\t[3] 곱하기\t\t\t||");
        System.out.println("||\t[4] 나눈몫\t\t\t||");
        System.out.println("||\t[5] 나눈나머지\t\t\t||");
        System.out.println("==========================");
        System.out.println("||\t[0] 프로그램 종료\t\t||");
        System.out.println("==========================");
        System.out.print("원하는 기능 번호를 기입해주세요. ==> ");
    }

    // 결과 출력 View 구성 메서드 showOutput()
    void showOutput(int selectNum, int num1, int num2, int result) {
        switch (selectNum) {
            case 1:
                System.out.printf("result : %d + %d = %d%n", num1, num2, result);
                tryAagin();
                break;
            case 2:
                System.out.printf("result : %d - %d = %d%n", num1, num2, result);
                tryAagin();
                break;
            case 3:
                System.out.printf("result : %d * %d = %d%n", num1, num2, result);
                tryAagin();
                break;
            case 4:
                System.out.printf("result : %d / %d = %d%n", num1, num2, result);
                tryAagin();
                break;
            case 5:
                System.out.printf("result : %d % %d = %d%n", num1, num2, result);
                tryAagin();
                break;
            default:
                break;
        }
    }

    void tryAagin() {
        while (true) {
            System.out.println("계산기를 계속 이용하시겠습니까?\n[1]예 [2]아니오");
            int tryNum = sc.nextInt();
            if (tryNum == 1) {
                break;
            } else {
                exit();
                break;
            }
        }
    }

    // 프로그램 종료 선택 시 Run을 종료하는 메서드 exit()
    void exit() {
        System.out.println("프로그램을 종료합니다. 좋은하루되세요!");
        System.exit(0);
    }
    //--END------------[VIEW, OUTPUT PRINT, EXIT 기능적 부분]-------------------


    //--START----------[핵심 4칙 연산 메서드 부분]-------------------
    //두 수의 합를 구하는 메서드 summary()
    int summary(int num1, int num2) {
        int result = num1 + num2;
        return result;
    }

    //두 수의 차를 구하는 메서드 subtract()
    int subtract(int num1, int num2) {
        result = num1 - num2;
        return result;
    }

    //두 수의 곱을 구하는 메서드 multiplication()
    int multiplication(int num1, int num2) {
        result = num1 * num2;
        return result;
    }

    //두 수의 나눈 몫을 구하는 메서드 divisionQuiotient()
    int divisionQuiotient(int num1, int num2) {
        result = num1 / num2;
        return result;
    }

    //두 수의 나눈 나머지를 구하는 메서드 divisionQuiotient()
    int divisionRemainder(int num1, int num2) {
        result = num1 % num2;
        return result;
    }
}
//--END------------[핵심 4칙 연산 메서드 부분]-------------------

 

3. 프로그램 시연

실행 화면
실행 시 메뉴가 나타나며, 아래 안내에 따라 "원하는 기능 번호를 기입"하는 쪽에 커서가 위치한다.
선택한 계산기의 기능(3)에 따라서 어떤 연산을 수행하는지 안내 문구가 나타나고,
첫번째 대상 숫자인 num1의 입력 안내가 나타난다. 

입력 제한 범위 외 숫자를 기입하게 되면 입력 범위를 안내하고 다시 num1의 입력을 요청한다.
num1은 정상적인 범위 내의 값을 입력하면 전역 변수 num1로 값이 저장되고
num2에 대한 입력을 요청한다,
마찬가지로 num2의 범위가 입력 제한 범위 외 숫자가 기입되면 안내와 함께 num2의 입력을 요청한다.
num2을 정상적인 범위 내 값으로 입력하면 또한 전역 변수 num2로 값이 저장되고
두 값에 대한 선택한 연산을 결과로 보여준다.
바로 계산기를 계속 이용하겠냐는 안내가 나타나고
1 입력 시 처음 메뉴로 돌아가며
 2 입력 시 프로그램을 종료 한다.
최초 메뉴에서도 1~5 계산 또는 프로그램 종료인 0 이외의 값을 입력하면
잘못된 값을 입력 한 것에 대한 안내와 프로그램의 이용에 대한 안내가 나타난다.
프로그램을 계속 이용하겠다고 1을 선택하면 메뉴가 재 등장하게 되며 다시 메뉴를 선택 할 수 있다.
프로그램 종료인 0이나, 선택 안내 문구에서 종료인 2번을 선택하면 다음과 같이 인사와 프로그램이 종료 된다.

 

4. 느낀점

우선 해당 연습은 복사 붙여넣기 하나도 없이, 예전 내용을 하나도 복사, 컨닝하지 않고 혼자 구현하고 ChatGPT의 조언을 참고하고 또 혼자 풀어내서 뿌듯하다. 전부터 해결하지 못하고 그냥 지나쳤던 반복문 탈출과 이어하기, 지역 변수와 전역 변수를 제대로 실습해보면서 자신감이 생겼다. 사실 이 기능은 아래처럼 그냥 3줄이면 끝날 정도로 간단한 알고리즘 첫 문제이다.

하지만 나는 예시 코드에서 문제에서 입력값의 제한사항이 적용되지 않았다는 점을 무시 할 수 없었고,

또한 이후 연속으로 연속 빼기, 곱하기, 나누기 문제가 나오는데 1개의 문제라고 풀었다고 신나있어야 할까? 이것을 그냥 복사 붙여넣기 하면 나에게 발전이 있을까? 생각했다. 이미 객체 지향형 프로그래밍을 배웠고 나는 그것을 활용해보고 싶었다. "웹 프로젝트를 진행할때 하면 되지" 와 같은 생각은 이미 이전 교육과정에서 경험했고, 결과는 처참하게 나에게 남은 지식이 없었다. 가장 기본기에서부터 해보면서 곧 들이닥칠 많은 정보들을 소화할 준비를 해야한다.

이제 추가적으로 접근제어자를 통한 은닉화, 부모자식 상속 구현, 오버로드, 오버라이딩 정도를 진행해봐야겠다.

추상화, 캡슐화, 은닉화, 다형성까지 이 프로그램으로 응용해보려한다.