개요
CH 2 계산기과제를 진행하며 고민했던 부분들을 작성해보겠습니다 ✏️✏️✏️
📌 계산기 과제 개발 프로세스 가이드
### Lv 1. 클래스 없이 기본적인 연산을 수행할 수 있는 계산기 만들기
- **양의 정수(0 포함)를 입력받기**
- Scanner를 사용하여 양의 정수 2개(0 포함)를 전달 받을 수 있습니다.
- 양의 정수는 각각 하나씩 전달 받습니다.
- 양의 정수는 적합한 타입으로 선언한 변수에 저장합니다.
- **사칙연산 기호(➕,➖,✖️,➗)를 입력받기**
- Scanner를 사용하여 사칙연산 기호를 전달 받을 수 있습니다.
- 사칙연산 기호를 적합한 타입으로 선언한 변수에 저장합니다. (`charAt(0)`)
- **위에서 입력받은 양의 정수 2개와 사칙연산 기호를 사용하여 연산을 진행한 후 결과값을 출력하기**
- 키워드 : `if` `switch`
- 사칙연산 기호에 맞는 연산자를 사용하여 연산을 진행합니다.
- 입력받은 연산 기호를 구분하기 위해 제어문을 사용합니다. (예를 들면 if, switch)
- 연산 오류가 발생할 경우 해당 오류에 대한 내용을 정제하여 출력합니다.
- ex) “나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.“
- **반복문을 사용하되, 반복의 종료를 알려주는 “exit” 문자열을 입력하기 전까지 무한으로 계산을 진행할 수 있도록 소스 코드를 수정하기**
- 키워드 : 무한으로 반복, 수정하기 (처음부터 무한 반복하는 것이 아니라, 위 스텝별로 진행하며 수정)
- 반복문을 사용합니다. (예를 들어, for, while…)
### Lv 2. 클래스를 적용해 기본적인 연산을 수행할 수 있는 계산기 만들기
- 사칙연산을 수행 후, 결과값 반환 메서드 구현 & 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
- 사칙연산을 수행한 후, 결과값을 반환하는 메서드 구현
- 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
- 1) 양의 정수 2개(0 포함)와 연산 기호를 매개변수로 받아 사칙연산(➕,➖,✖️,➗) 기능을 수행한 후 2) 결과 값을 반환하는 메서드와 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성합니다.
- **Lv 1에서 구현한 App 클래스의 main 메서드에 Calculator 클래스가 활용될 수 있도록 수정**
- 연산 수행 역할은 Calculator 클래스가 담당
- 연산 결과는 Calculator 클래스의 연산 결과를 저장하는 필드에 저장
- 소스 코드 수정 후에도 수정 전의 기능들이 반드시 똑같이 동작해야합니다.
- **App 클래스의 main 메서드에서 Calculator 클래스의 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근하지 못하도록 수정 (캡슐화)**
- 간접 접근을 통해 필드에 접근하여 **가져올** 수 있도록 구현합니다. (Getter 메서드)
- 간접 접근을 통해 필드에 접근하여 **수정할** 수 있도록 구현합니다. (Setter 메서드)
- 위 요구사항을 모두 구현 했다면 App 클래스의 main 메서드에서 위에서 구현한 메서드를 활용 해봅니다.
- Calculator 클래스에 저장된 연산 결과들 중 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현한 후 App 클래스의 main 메서드에 삭제 메서드가 활용될 수 있도록 수정
- 키워드 : `컬렉션`
- 컬렉션에서 ‘값을 넣고 제거하는 방법을 이해한다.’가 중요합니다!
### Lv 3. Enum, 제네릭, 람다 & 스트림을 이해한 계산기 만들기
- **현재 사칙연산 계산기는 (➕,➖,✖️,➗) 이렇게 총 4가지 연산 타입으로 구성되어 있습니다.**
- Enum 타입을 활용하여 연산자 타입에 대한 정보를 관리하고 이를 사칙연산 계산기 ArithmeticCalculator 클래스에 활용 해봅니다.
- **실수, 즉 double 타입의 값을 전달 받아도 연산이 수행하도록 만들기**
- 키워드 : `제네릭`
- 단순히, 기존의 Int 타입을 double 타입으로 바꾸는 게 아닌 점에 주의하세요!
- 지금까지는 ArithmeticCalculator, 즉 사칙연산 계산기는 양의 정수(0 포함)를 매개변수로 전달받아 연산을 수행
- 피연산자를 여러 타입으로 받을 수 있도록 기능을 확장
- ArithmeticCalculator 클래스의 연산 메서드(`calculate`)
- 위 요구사항을 만족할 수 있도록 ArithmeticCalculator 클래스를 수정합니다. (제네릭)
- 추가적으로 수정이 필요한 다른 클래스나 메서드가 있다면 같이 수정 해주세요.
- **저장된 연산 결과들 중 Scanner로 입력받은 값보다 큰 결과값 들을 출력**
- ArithmeticCalculator 클래스에 위 요구사항을 만족하는 조회 메서드를 구현합니다.
- 단, 해당 메서드를 구현할 때 Lambda & Stream을 활용하여 구현합니다.
- Java 강의에서 람다 & 스트림을 학습 및 복습 하시고 적용 해보세요!
📌 계산기 과제 TIL 모음
2025.01.02 - [TIL (Today I Learned)] - [TIL] Java-객체지향 프로그래밍, 계산기 만들기 (25-01-02)
[TIL] Java-객체지향 프로그래밍, 계산기 만들기 (25-01-02)
🤖 진행 사항 🤖 Java 문법 종합반 3주차Java 계산기 과제Lv 1. 클래스 없이 기본적인 연산을 수행할 수 있는 계산기 만들기Lv 2. 클래스를 적용해 기본적인 연산을 수행할 수 있는 계산기
mannakingdom.tistory.com
[TIL] Java 계산기 만들기, 계산기 예외처리 과제 / Java Error와 Exception, Generic (25-01-03)
🤖 25-01-03 진행 사항🤖3주차 과제 : 계산기 만들기 - 추상화 클래스, 클래스 포함 관계 적용Java 문법 종합반 - 4주차Error / ExceptionGenetic4주차 과제 : 계산기 예외처리계산기 만들기 - 추상화 클래스
mannakingdom.tistory.com
2025.01.06 - [TIL (Today I Learned)] - [TIL] Java Enum과 Generic 활용하여 계산기 만들기 (25-01-06)
[TIL] Java Enum과 Generic 활용하여 계산기 만들기 (25-01-06)
🤖 25-01-06 진행 사항 🤖ch2. 계산기 과제 Lv 3 : Enum, 제네릭, 람다&스트림을 이해한 계산기 만들기EnumGeneric예외처리EnumEnum 타입을 활용하여 연산자 타입에 대한 정보를 관리하고 이를 사칙연산 계
mannakingdom.tistory.com
고민했던 부분
1. 나누기 연산 예외 처리
나누기 연산에서 분모가 0일 경우 반환되는 예외 처리를 해야했다.
lv 1을 진행할 때는 switch
문에서, lv 2에서는 if
문으로 나누기 연산이면서 입력된 두번째 정수가 0이면 안내문이 출력되도록 했다.
- App.java
if ((operator == '/' || operator == '➗') && num2 == 0) {
System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
}
lv 3를 진행하면서 해당 예외 처리에 대한 고뇌가 시작됐다.
lv 3에서 생성한 OperatorType
Enum 클래스에 나누기 연산이 맞는지 확인하는 메서드를 추가하여 다음과 같이 확인했다.
- OperatorType.java
// 나눗셈 연산인지 판별
public static boolean isDivisionOperator(char input) {
if (OperatorType.DIVIDE.operationSymbol == input
|| OperatorType.DIVIDE.operationEmoji == input) {
return true;
}
return false;
}
- App.java
if (OperatorType.isDivisionOperator(operator) && numbers[1].equals(0)) {
System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
}
조건은 맞지만 더 간결하고 보기 좋게 예외 처리를 하고싶어서, 검색을 해보다가 산술 작업에서 오류가 발생한 경우 throw되는 예외인 ArithmeticException을 알게됐다.

그런데 위와 같이 적용을 하니 오류가 throw되지 않고 결과값으로 NaN(Not-a-Number)값과 Infinity가 출력됐다.
- 0 / 0 ➡️ NaN
- 0이 아닌 숫자 / 0 ➡️ Infinify
lv 3을 진행하면서 전달받는 매개변수들에 제네릭을 적용하게 됐고, 사용자에게 숫자를 입력받을 때도 Number
형 배열에 double
값으로 저장을 하게되어서 ArithmeticException
이 발생하지 않는 것이었다.
ArithmeticException
은 정수 연산에서 0으로 나누기와 같은 문제에서 발생하기 때문에 ArithmeticException
를 이용한 예외처리는 포기하게 됐다.
마지막으로 채택하게 된 예외처리는 IllegalArgumentException이다.
✏️ IllegalArgumentException
호출자가 인수로 부적절한 값을 넘길 때 던지는 예외다.
자바 API에서 제공하는 표준 예외를 사용해서 가독성을 높이고 간단하게 표현했다.
- App.java
// 사칙연산 기호(➕,➖,✖️,➗) 입력 받기
while (true) {
System.out.print("사칙연산 기호를 입력하세요: ");
try {
char operator = sc.next().trim().charAt(0);
// 입력받은 사칙연산 기호가 올바른지 판별
if (!OperatorType.isValidOperator(operator)) {
throw new IllegalArgumentException("사칙연산 기호가 적절하지 않습니다.");
}
// 나눗셈 연산이 유효한지 판별
if (!OperatorType.isValidDivision(operator, numbers[1])) {
throw new IllegalArgumentException("나눗셈의 분모(두 번째 숫자)가 0일 수 없습니다.");
}
calculator.setOperator(operator);
break; // 유효한 연산자 입력 시 반복 종료
} catch (Exception e) {
System.out.println(e.getMessage() + " 사칙연산 기호를 다시 입력하세요.");
System.out.println("사용 가능한 사칙연산 기호: +, -, *, /, ➕, ➖, ✖, ➗");
System.out.println("--------------------------------------------------");
}
}
OperatorType
Enum에는 나눗셈 연산이 유효한지 판별해주는 메서드를 다시 만들었다.
매개변수로 연산자와 입력받은 두 번째 숫자를 전달했다.
- OperatorType.java

2. 연산 메서드 캡슐화
ArithmeticCalculator
클래스의 calculate
메서드에서 switch
문의 case '➕', '+'
와 같은 방식으로 직접적으로 연산자를 확인하는 것이 비효율적으로 보여 수정하고 싶었다.

이미 OperatorType
에 연산자들('➕', '+' 등)이 상수마다 필드로 저장됐기 때문에, 이를 활용하는 방법을 찾아보았다.

ArithmeticCalculator
클래스의 operator
필드에 저장된 값이 OperatorType
Enum에서 어떤 상수인지 확인하는 메서드가 필요했다.
다음과 같이 selectOperator
메서드를 생성했다.
- OperatorType.java
// 어떤 연산자를 사용하는지 반환해주는 메서드
public static OperatorType selectOperator(char input) {
for (OperatorType op : OperatorType.values()) {
if (op.operationSymbol == input || op.operationEmoji == input) {
return op;
}
}
throw new IllegalArgumentException("사칙연산 기호가 적절하지 않습니다.");
}
enum의 기본 제공 메서드 values()
를 사용해서 모든 상수에 대해 input값과 같은게 있는지 비교하고, 같은게 없다면 IllegalArgumentException
을 throw하도록 했다.
생성한 selectOperator
메서드를 활용하여, ArithmeticCalculator
클래스의 calculate
메서드를 수정하면 다음과 같다.
- ArithmeticCalculator.java
// 사칙연산을 수행한 후, 결과값을 반환하는 메서드 구현
// 매개변수 타입은 제네릭, 반환은 항상 double
public double calculate(T num1, T num2) {
OperatorType operatorType = OperatorType.selectOperator(this.operator);
return operatorType.operate(num1, num2);
}
switch
문을 사용할 때 보다 훨씬 가독성이 좋아지고 유지보수성이 좋아진 것 같다❗ ❗ ❗
아쉬운 점
1. Try-Catch
try-catch
의 활용 범위(어디까지 묶는지)와 어떤 부분에 예외 처리를 넣어야하는지 명확하지 않았다.
while문 전체에 try-catch로 묶는 것이 나을지, 세부적으로 처리해야 할지 판단이 어려웠다.
앞으로 더 큰 프로젝트를 하고 구현하는 범위도 넓어질텐데, 어떤 기준으로 판단해야할지 잘 모르겠다.........🥲
예외 처리에 대한 공부가 더 필요할 것 같다.
2. <T extends Number> 활용의 모호함
제네릭 타입으로 구현한 ArithmeticCalculator
에서 <T extends Number>를 사용했지만, 실제 연산은 모두 double
형으로 처리하고 반환한다.
double
형으로 처리하게 만든 이유는, 일반적인 산술 결과가 실수라고 판단했기 때문이다.
예를 들어 3 / 2 의 경우 입력값이 정수여도 결과값으로 1.5 라는 실수를 반환해줘야 한다고 생각했다.
- OperatorType.java
// 매개변수는 제네릭 타입(Number 또는 그 하위 클래스들)으로 받고,
// 반환 타입을 항상 double 타입으로 고정: 일반적인 산술 결과가 실수이기 때문에
public abstract <T extends Number> double operate(T num1, T num2);
그래서 사용자에서 숫자를 입력받을 때도 저장할 배열은 Number
형이지만, 저장하는 값은 Double
로 파싱해서 저장했다.
- App.java
// 연산할 숫자 2개 입력 받기
// 입력 받은것이 숫자가 맞는지 확인
String userInput;
Number[] numbers = new Number[2];
for (int i = 0; i < 2; i++) {
System.out.print((i + 1) + " 번째 숫자를 입력하세요: ");
userInput = sc.next().trim();
try {
numbers[i] = Double.parseDouble(userInput);
} catch (NumberFormatException e) {
System.out.println(userInput + "은 숫자가 아닙니다. 숫자를 다시 입력하세요.");
System.out.println("--------------------------------------------------");
--i;
}
}
이런식으로 구현하다보니 결국 모든 값을 그냥 double
로 다루고 있었다...🥲🥲🥲🥲🥲
제네릭 활용을 제한적으로 하게되어서 아쉽고 올바른? 적절한? 활용법이 궁금하다.
그리고 long
타입의 숫자를 입력했을 때, 연산을 double
형으로 하기 때문에 부동소수점 연산/출력이 되는 점을 발견했다.

부동소수점 연산 방식으로 출력을 하게되어 입력과 출력의 일관성이 없어진 점이 아쉽다.
마무리
계산기 과제를 하면서 평소엔 안 해봤던 예외 처리나 리팩토링에 대해서 고민해 볼 수 있어서 좋았다.
특히 Lv 3를 진행하면서 이론적으로 몰랐던 부분을 공부하고 직접 적용해 보면서 더 확실하게 학습이 된 것 같아서 뿌듯하다!
https://github.com/mannaKim/java-calculator
GitHub - mannaKim/java-calculator: 계산기 구현 Java 프로그래밍 과제
계산기 구현 Java 프로그래밍 과제. Contribute to mannaKim/java-calculator development by creating an account on GitHub.
github.com
'TIL (Today I Learned)' 카테고리의 다른 글
[TIL] SQL - WITH RECURSIVE 개념과 사용법 (0) | 2025.02.06 |
---|---|
[TIL] SQL 윈도우 함수 (Window Function) (0) | 2025.01.14 |
[TIL] Java Enum과 Generic 활용하여 계산기 만들기 (25-01-06) (0) | 2025.01.06 |
[TIL] Java 계산기 만들기, 계산기 예외처리 과제 / Java Error와 Exception, Generic (25-01-03) (1) | 2025.01.03 |
[TIL] Java-객체지향 프로그래밍, 계산기 만들기 (25-01-02) (0) | 2025.01.02 |