Troubleshooting: 무엇이 문제였는가?/본캠프 2주 차: 계산기 만들기

2단계: "반복문 안에서 인스턴스화를 했다고요? 갑부시군요!"

writingforever162 2024. 11. 24. 17:41

[문제]

분명 가장 먼저 저장된 결괏값을 삭제하지 않았으니 나누기 연산을 했을 때 [55, 3]이 출력되어야 하는데 [3]이 출력되었다. 처음에는 클래스를 작성할 때 for문 같은 반복문을 쓰지 않아서 발생한 문제인 줄 알았다. 

 

[원인 추측]

package com.project.personal.calculator2;

import java.util.*;

public class CalcLvTwo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("0 이상인 첫 번째 정수를 입력해 주세요: ");
            int numFirst = sc.nextInt();

            System.out.print("사칙연산 기호(+, -, *, /) 중 하나를 입력해 주세요: ");
            String operator = sc.next();

            System.out.print("0 이상인 두 번째 정수를 입력해 주세요: ");
            int numSecond = sc.nextInt();

            Calculator calculation = new Calculator();
            int result = calculation.calculate(numFirst, operator, numSecond);

            if (result == Integer.MIN_VALUE) {
                System.out.println("나눗셈 연산에서 분모(두 번째 정수)에 0은 올 수 없습니다.");
            } else {
                calculation.saveResults(result);
                String outcome = Integer.toString(result);
                System.out.println("연산한 결과는 " + outcome + "입니다.");
            }
            System.out.println("결괏값은 다음과 같이 저장되었습니다: "+calculation.getResults());
            System.out.print("가장 먼저 저장된 결괏값을 삭제하시겠습니까? 삭제를 원하시면 yes를 입력해 주세요: ");
            // 29번 줄을 linebreak로 설정하고 디버깅 실행
            // 저장 관련 코드에 문제가 있다고 판단했기 때문에
            String yes = sc.next();
            if (yes.equals("yes")) {
                System.out.println("예, 알겠습니다. 가장 먼저 저장된 결괏값을 삭제합니다.");
                calculation.removeFirstResult();
                System.out.println("삭제 후 결괏값은 다음과 같이 조회됩니다: "+calculation.getResults());
            }
            System.out.print("프로그램을 종료하시겠습니까? 종료를 원하시면 exit을 입력해 주세요: ");
            String exit = sc.next();
            if (exit.equals("exit")) {
                System.out.println("예, 알겠습니다. 프로그램을 종료합니다.");
                break;
            }
        }
    }
}

원인을 찾고자 메인(main)인 CalcLvTwo에서 디버깅(debugging)을 진행했다. 의심이 가는 부분을 중지점(브레이크 포인트, breakpoint 후에 파란 네모로 표시한 우측 상단의 벌레 버튼을 눌렀다.

디버깅이 이루어지려면 벌레 버튼을 누른 다음 직접 콘솔(console)에 값을 입력해야 했다. 즉, 벌레 버튼을 누르기만 해서는 디버깅이 되지 않았다. 값이 잘 저장되는지 확인해야 했으므로, 파란 네모로 표시한 화살표 버튼을 눌러서 콘솔에 다시 한번 값을 입력했다. 

디버깅 후 값을 입력할 때마다 주솟값이 바뀐다는 점을 찾아냈다. 우선 사칙연산에 해당하는 Calculator 클래스를 작성할 때 무언가를 잘못 쓰지 않았는지 확인하기로 했다. 

import java.util.ArrayList;

public class Calculator {
    private ArrayList<Integer> results = new ArrayList<Integer>();

    public Calculator() {
    }

    public int calculate(int numOne, String operator, int numTwo) {
        if (operator.equals("+")) {
            return numOne + numTwo;
        } else if (operator.equals("-")) {
            return numOne - numTwo;
        } else if (operator.equals("*")) {
            return numOne * numTwo;
        } else {
            if (numTwo == 0) {
                return Integer.MIN_VALUE;
            } else {
                return numOne / numTwo;
            }
        }
    }

    public void saveResults(int eachResult) {
        this.results.add(eachResult);
    }

    public ArrayList<Integer> getResults() {
        return this.results;
    }

    public void setResults(ArrayList<Integer> results) {
        this.results = results;
    }

    public void removeFirstResult() {
        this.results.remove(0);
    }
}

Calculator 클래스를 쭉 확인한 결과, 잘못 작성한 부분은 없었다. 그렇다면 값이 누적되어 저장되지 않는 이유는 생성자나 메서드(method)를 메인(main)에 호출하는 과정에서 무언가를 잘못했다고 추측할 수 있었다.

 

[원인] 

import java.util.Scanner;

public class CalcLvTwo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        while (true) {
            System.out.print("0 이상인 첫 번째 정수를 입력해 주세요: ");
            int numFirst = sc.nextInt();

            System.out.print("사칙연산 기호(+, -, *, /) 중 하나를 입력해 주세요: ");
            String operator = sc.next();

            System.out.print("0 이상인 두 번째 정수를 입력해 주세요: ");
            int numSecond = sc.nextInt();

            Calculator calculation = new Calculator();
            // [오답] while문 안에 위치
            // [정답] while문 바깥에 있어야 함
            
            int result = calculation.calculate(numFirst, operator, numSecond);

            if (result == Integer.MIN_VALUE) {
                System.out.println("나눗셈 연산에서 분모(두 번째 정수)에 0은 올 수 없습니다.");
            } else {
                calculation.saveResults(result);
                String outcome = Integer.toString(result);
                System.out.println("연산한 결과는 " + outcome + "입니다.");
            }
            System.out.println("결괏값은 다음과 같이 저장되었습니다: " + calculation.getResults());
            System.out.print("가장 먼저 저장된 결괏값을 삭제하시겠습니까? 삭제를 원하시면 yes를 입력해 주세요: ");
            String yes = sc.next();

            if (yes.equals("yes")) {
                System.out.println("예, 알겠습니다. 가장 먼저 저장된 결괏값을 삭제합니다.");
                calculation.removeFirstResult();
                System.out.println("삭제 후 결괏값은 다음과 같이 조회됩니다: " + calculation.getResults());
            }
            System.out.print("프로그램을 종료하시겠습니까? 종료를 원하시면 exit을 입력해 주세요: ");
            String exit = sc.next();
            if (exit.equals("exit")) {
                System.out.println("예, 알겠습니다. 프로그램을 종료합니다.");
                break;
            }
        }
    }
}

원인은 인스턴스화(instantiation), 즉 객체를 생성할 때 반복문 안에서 진행한 데에 있었다.

 

튜터님의 비유를 빌리자면, 이건 마치 문방구에 가서 계산기를 구매했는데 한 번 쓰고 버린 다음 새로운 계산기를 사고 버리고 또 새로 사고 버리는 무한반복과 같다고.

 

"갑부시군요!" 

 

이 마지막 한마디에 나도 빵 터지고, 튜터님도 웃음꽃을 피우셨다. for문을 추가로 써서 반복을 돌려야 하나 고민했는데, 원인을 파악하기가 무섭게 어이없어서 절로 웃음이 나왔다.

 

[해결]

import java.util.Scanner;

public class CalcLvTwo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Calculator calculation = new Calculator();
        // [수정] while문 바깥에서 이름이 calculation인 객체를 생성함
        //        == 인스턴스화

        while (true) {
            System.out.print("0 이상인 첫 번째 정수를 입력해 주세요: ");
            int numFirst = sc.nextInt();

            System.out.print("사칙연산 기호(+, -, *, /) 중 하나를 입력해 주세요: ");
            String operator = sc.next();

            System.out.print("0 이상인 두 번째 정수를 입력해 주세요: ");
            int numSecond = sc.nextInt();

            int result = calculation.calculate(numFirst, operator, numSecond);

            if (result == Integer.MIN_VALUE) {
                System.out.println("나눗셈 연산에서 분모(두 번째 정수)에 0은 올 수 없습니다.");
            } else {
                calculation.saveResults(result);
                String outcome = Integer.toString(result);
                System.out.println("연산한 결과는 " + outcome + "입니다.");
            }
            System.out.println("결괏값은 다음과 같이 저장되었습니다: " + calculation.getResults());
            System.out.print("가장 먼저 저장된 결괏값을 삭제하시겠습니까? 삭제를 원하시면 yes를 입력해 주세요: ");
            String yes = sc.next();

            if (yes.equals("yes")) {
                System.out.println("예, 알겠습니다. 가장 먼저 저장된 결괏값을 삭제합니다.");
                calculation.removeFirstResult();
                System.out.println("삭제 후 결괏값은 다음과 같이 조회됩니다: " + calculation.getResults());
            }
            System.out.print("프로그램을 종료하시겠습니까? 종료를 원하시면 exit을 입력해 주세요: ");
            String exit = sc.next();
            if (exit.equals("exit")) {
                System.out.println("예, 알겠습니다. 프로그램을 종료합니다.");
                break;
            }
        }
    }
}

이 트러블슈팅은 아마 절대 잊지 못할 거다. 문법이 아직 익숙하지 않다는 말이 무슨 의미인지 제대로 체감하는 중이다. 갑부로 오해 받은 이 트러블슈팅을 듣고 빵 터지지 않은 사람이 아무도 없었다. 이번 트러블슈팅은 추억으로 삼을 겸 꼼꼼하게 적었다.

 

[수정 전] 배열에 값이 누적되지 않는 오류 1개 발생 

[수정 후] 배열에 값이 누적되지 않는 오류 0개 발생