Camp/과제

Camp/과제[TIL] CH2 Java 계산기 과제 Lv.2 (트러블 슈팅)

taeri7204 2026. 6. 16. 21:21

 

TrouBle 1. Calculator 클래스를 분리하라는데, 어디서부터 어떻게 해야 하는지 몰랐다.

Lv.1에서는 main 메서드 하나 안에 입력, 계산, 출력이 전부 들어가 있었다. Lv.2에서 Calculator를 따로 만들라는 요구사항을 보고 머리론 알겠는데도 감이 안 잡혔다. 어떤 코드를 어디로 옮겨야 하는 건지, 두 파일이 어떻게 연결되는 건지가 강의를 그렇게도 많이 봤건만 막상 시작하려니 막막했다.

 

// Lv.1 방식: main이 모든 걸 다 함
public static void main(String[] args) {
    // 입력 받고, 계산하고, 출력까지 전부 여기서
    if (operator == '+') result = num1 + num2;
    System.out.println("결과: " + result);
}

// Lv.2 방식: 역할 분리
// Calculator.java → 계산만 담당
public class Calculator {
    public Integer calculate(int num1, int num2, char operator) {
        // 계산 로직만 여기에
    }
}

// App.java → 흐름 관리만 담당
public class App {
    public static void main(String[] args) {
        Calculator calculator = new Calculator(); // Calculator 인스턴스화
        Integer result = calculator.calculate(num1, num2, operator); // 결과 받기
    }
}

Lv.1은 혼자 주문받고 요리하고 서빙까지 다 하는 구조다. Lv.2는 홀 직원(App)이 주문을 받아서 주방(Calculator)에 넘기고, 주방이 결과를 돌려주는 구조다. App은 "어떻게 계산하는지" 알 필요가 없고, Calculator는 "사용자가 뭘 입력했는지" 알 필요가 없다.

 

TrouBle 2. Calculator 인스턴스를 왜 new로 만들어야 하는지 몰랐다

App.java에서 Calculator의 메서드를 쓰려고 바로 calculator.calculate()를 호출했더니 오류가 났다. 클래스 파일이 있는데 왜 안 된다는 건지 이해가 안 됐다.

클래스는 설계도 일뿐, 실제로 쓰려면 new로 인스턴스(실체)를 먼저 만들어야 한다. 설계도만 있고 실제 객체가 없는 상태에서 메서드를 부르면 오류 발생.

 

public class App {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Calculator calculator = new Calculator(); // 인스턴스화 - 이 줄이 없으면 오류
        // 이제 calculator.calculate(), calculator.getResults() 사용 가능
    }
}

클래스 = 설계도, new 클래스명() = 설계도로 실제 물건 만들기. 물건이 있어야 쓸 수 있다.

 

TrouBle 3. 결과를 어떻게 저장해야 하는지 몰랐다

계산할 때마다 결과를 쌓아야 하는데, 배열(int[])을 쓰려니 크기를 미리 정해야 해서 몇 번 계산할지 모르는 상황에선 안 됐다.

 

배열은 선언할 때 크기가 고정된다. 계산 횟수가 정해지지 않은 상황에선 크기가 자동으로 늘어나는 컬렉션이 필요하다.

 

import java.util.ArrayList;
import java.util.List;

public class Calculator {
    private List<Integer> results = new ArrayList<>();
    // ArrayList는 크기 자동 증가
    // Integer = int의 포장 버전 (ArrayList는 기본형 int를 바로 못 담음)
}

기본 동작:

results.add(result);   // 결과 추가
results.get(0);        // 첫 번째 값 조회
results.remove(0);     // 첫 번째 값 삭제

구분 배열 int[] ArrayList

크기 선언 시 고정 자동 증가
추가 불가 add()
삭제 불가 remove()
이번 과제 적합성 부적합 적합

TrouBle 4. calculate() 메서드에서 return 쓰면 빨간줄이 떴다

 Lv.1에서는 0으로 나누거나 잘못된 연산자를 입력하면 sout으로 메시지 출력하고 return으로 종료했다. Lv.2 Calculator 클래스에서 그걸 그대로 옮겼더니 빨간줄이 뜨면서 컴파일이 안 됐다.

 main 메서드의 반환 타입은 void라서 return으로 그냥 끝낼 수 있다. 하지만 calculate() 메서드의 반환 타입은 Integer다. return만 쓰면 반환값이 없어지는데, Integer를 돌려줘야 하는 메서드가 아무것도 안 돌려주면 컴파일 오류가 난다.

// Lv.1 방식 (void 메서드에서는 가능)
if (num2 == 0) {
    System.out.println("0으로 나눌 수 없습니다.");
    return; // void니까 그냥 끝낼 수 있음
}

// Lv.2 방식 (반환 타입이 있는 메서드에서)
if (num2 == 0) {
    throw new ArithmeticException("0으로 나눌 수 없습니다.");
    // throw는 예외를 던지고 끝내기 - 반환값 없어도 됨
}

throw를 쓰면 "이 상황은 비정상"이라는 신호를 보내고 메서드를 끝낼 수 있다. 반환값이 없어도 된다.

예외 종류 언제 쓰나

ArithmeticException 수학적으로 불가능한 연산 (0으로 나누기)
IllegalArgumentException 허용되지 않는 입력 (지원 안 하는 연산자)

TrouBle 5. 캡슐화를 왜 해야 하는지 몰랐다

상황 results 필드를 public으로 만들었다. App에서 값을 불러오면 되니까 문제없다고 생각했다.

원인 public이면 외부에서 마음대로 수정할 수 있다. calculator.results.add(999)나 calculator.results.clear() 같은 코드가 App 어디서든 실행될 수 있어서 내가 의도한 대로 동작하지 않을 수 있다.

해결

public class Calculator {
    private List<Integer> results = new ArrayList<>(); // private으로 변경

    public List<Integer> getResults() { // getter - 읽기만 허용
        return results;
    }

    public void removeResult() { // 삭제는 이 메서드로만
        if (!results.isEmpty()) {
            results.remove(0);
        }
    }
}

App에서는 직접 접근 대신 getter를 쓴다.

// 직접 접근 (캡슐화 전)
calculator.results.add(999); // 마음대로 수정 가능 - 위험

// getter 사용 (캡슐화 후)
System.out.println(calculator.getResults()); // 읽기만 가능

기억 포인트: private = 외부에서 직접 건드리기 불가. getter = 읽기 전용 창구.


학습 메모

Lv.2에서 제일 당황했던 건 파일이 두 개로 쪼개지면서 어디서부터 읽어야 할지, 또 어디서부터 설계를 해야할지 감이 안 왔다는 거다. App이 Calculator를 부르고, Calculator가 결과를 돌려주고, 그 결과를 App이 출력한다는 흐름을 머릿속에 잡아야 전체 구조가 보인다.

return 대신 throw로 바꿔야 한다는 것도 처음엔 왜 그런지 몰랐는데, 반환 타입이 있는 메서드에서 아무것도 안 돌려주면 컴파일이 안 된다는 점을 알게됐다.

캡슐화는 개념도 약하고 "왜 굳이 막나"가 이해가 안 됐는데, 외부에서 특강 때 들었던 네이버 인턴인 내가 회사 코드를 마음대로 수정하면 벌어지게될 참사를 생각하니 납득이 됐다.

다음 과제 목표: Lv.3 - Enum, 제네릭, 스트림 적용


핵심 요약

개념 내용

클래스 분리 Calculator = 계산 담당 / App = 입출력 + 흐름 담당
인스턴스화 new Calculator()로 실체를 만들어야 메서드 사용 가능
ArrayList 크기 자동 증가 / add(), remove(), get()
throw 반환 타입 있는 메서드에서 예외 상황 처리 (return 대신)
캡슐화 private 필드 + getter로 외부 직접 접근 차단