🌐 github 주소
https://github.com/JoeMinKyung/java_Calculator
사칙연산을 수행한 후, 결과값을 반환하는 메서드 구현
연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
1) 양의 정수 2개(0 포함)와 연산 기호를 매개변수로 받아 사칙연산(➕,➖,✖️,➗) 기능을 수행한 후 2) 결과 값을 반환하는 메서드와 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성합니다.
예시 코드
public class Calculator {
/* 연산 결과를 저장하는 컬렉션 타입 필드 선언 및 생성 */
public 반환타입 calculate(...매개변수) {
/* 위 요구사항에 맞게 구현 */
/* return 연산 결과 */
}
}
간접 접근을 통해 필드에 접근하여 가져올 수 있도록 구현합니다. (Getter 메서드)
간접 접근을 통해 필드에 접근하여 수정할 수 있도록 구현합니다. (Setter 메서드)
위 요구사항을 모두 구현 했다면 App 클래스의 main 메서드에서 위에서 구현한 메서드를 활용 해봅니다.
예시 코드
public class Calculator {
/* 연산 결과를 저장하는 컬렉션 타입 필드를 외부에서 직접 접근 하지 못하도록 수정*/
public 반환타입 calculate(...매개변수) {
...
}
/* Getter 메서드 구현 */
/* Setter 메서드 구현 */
}
public class App {
public static void main(String[] args) {
/* Calculator 인스턴스 생성 */
Scanner sc = new Scanner(System.in);
/* 반복문 시작 */
System.out.print("첫 번째 숫자를 입력하세요:");
int num1 = sc.nextInt();
System.out.print("두 번째 숫자를 입력하세요:");
int num2 = sc.nextInt();
System.out.print("사칙연산 기호를 입력하세요: ");
char operator = sc.next().charAt(0);
/* 위 요구사항에 맞게 소스 코드 수정 */
System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
...
/* 반복문 종료 */
}
}
키워드 : 컬렉션
예시 코드
public class Calculator {
/* 연산 결과를 저장하는 컬렉션 타입 필드를 외부에서 직접 접근 하지 못하도록 수정*/
public 반환타입 calculate(...매개변수) {
...
}
...
public void removeResult() {
/* 구현 */
}
}
public class App {
public static void main(String[] args) {
/* Calculator 인스턴스 생성 */
Scanner sc = new Scanner(System.in);
/* 반복문 시작 */
System.out.print("첫 번째 숫자를 입력하세요:");
int num1 = sc.nextInt();
System.out.print("두 번째 숫자를 입력하세요:");
int num2 = sc.nextInt();
System.out.print("사칙연산 기호를 입력하세요: ");
char operator = sc.next().charAt(0);
/* 위 요구사항에 맞게 소스 코드 수정 */
System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
...
/* 반복문 종료 */
}
}
💻 정답 코드
Calculator2.java
package com.example.calculator2;
import java.util.List;
import java.util.ArrayList;
public class Calculator2 {
// 연산 결과를 저장할 컬렉션 (private으로 캡슐화)
private final List<Integer> results = new ArrayList<>();
// 연산 수행
public int calculate(int a, int b, char c) {
int result = 0; // calculate 수행 결과 값 저장
// 연산 수행하기
switch (c) {
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
if (b == 0) {
throw new ArithmeticException("나눗셈 연산에서 분모가 0입니다.");
}
result = a / b;
break;
default:
throw new IllegalArgumentException("유효하지 않은 연산자입니다: " + c);
}
// 연산 결과를 저장
addResult(result);
return result;
}
// 연산 결과를 추가 (Setter)
private void addResult(int result) {
results.add(result);
}
// 연산 결과를 가져오기 (Getter)
public List<Integer> getResults() {
return new ArrayList<>(results); // 캡슐화를 위해 복사본 반환
}
// 연산 결과 기록 출력하기
public void printResults() {
System.out.println("=== 연산 기록 ===");
for (int i = 0; i < results.size(); i++) {
System.out.println((i + 1) + ". " + results.get(i));
}
}
// 가장 먼저 저장된 연산 결과 삭제
public void removeResult() {
if (!results.isEmpty()) {
results.remove(0);
} else {
System.out.println("삭제할 연산 결과가 없습니다.");
}
}
}
App.java
package com.example.calculator2;
import java.util.Scanner;
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Calculator2 calc = new Calculator2();
while (true) {
int firstNum, secondNum;
char operator;
// 첫번째 숫자 입력받기
firstNum = getPositiveInteger(sc, "첫 번째 숫자를 입력하세요: ");
// 두번째 숫자 입력받기
secondNum = getPositiveInteger(sc, "두 번째 숫자를 입력하세요: ");
// 사칙연산 기호 입력받기
operator = getOperator(sc);
// 연산 수행 및 출력하기
try {
int result = calc.calculate(firstNum, secondNum, operator);
System.out.println("결과: " + result);
} catch (ArithmeticException | IllegalArgumentException e) {
System.out.println("오류: " + e.getMessage());
}
// 반복 여부 확인하기 (메뉴 출력)
while (true) {
System.out.println("연산 결과 조회: 1, 연산 결과 삭제: 2, 더 계산하려면 아무 키나 입력해주세요. (exit 입력 시 종료)");
String continueText = sc.next();
if (continueText.equals("exit")) {
return; // 메서드 종료
} else if (continueText.equals("1")) {
// 연산 결과 조회
calc.printResults();
} else if (continueText.equals("2")) {
// 연산 결과 삭제
while(true){
System.out.println("가장 먼저 저장된 데이터를 삭제합니다. 삭제하시겠습니까? (y, n)");
String input = sc.next();
if (input.equals("y")) {
calc.removeResult();
break; // 다시 메뉴로 돌아가기
}
if (input.equals("n")) {
break; // 다시 메뉴로 돌아가기
}
System.out.println("y 또는 n을 눌러주세요.");
}
} else {
break;
}
}
}
}
// 양의 정수 입력받는 메서드
private static int getPositiveInteger(Scanner sc, String prompt) {
int number;
while (true) {
System.out.print(prompt);
String input = sc.next();
// 정수 입력 확인
try {
number = Integer.parseInt(input); // 문자열을 정수로 변환
// 양의 정수 입력 확인
if (number >= 0) {
return number; // 양의 정수이면 반복 종료
} else {
System.out.println("0 이상의 정수를 입력해주세요.");
}
} catch (NumberFormatException e) {
System.out.println("유효한 정수를 입력해주세요.");
}
}
}
// 연산자 입력받는 메서드
private static char getOperator(Scanner sc) {
char operator;
while (true){
System.out.print("사칙연산 기호를 입력하세요: ");
operator = sc.next().charAt(0);
if(operator == '+' || operator == '-' || operator == '*' || operator == '/'){
return operator;
}
else{
System.out.println("잘못된 사칙연산 기호입니다. 다시 입력해주세요.");
}
}
}
}
💡 코드 해설
App.java
1. main 메서드
프로그램의 메인 로직. 사용자 입력, 계산 실행, 반복 여부 확인 등을 처리.
입력 검증, 계산 실행, 결과 출력, 연산 기록 조회/삭제 기능 구현.
2. getPositiveInteger 메서드
양의 정수(0 포함)를 입력받는 메서드.
입력값이 정수인지 확인 후, 음수인 경우 다시 입력받도록 반복.
3. getOperator 메서드
사칙연산(+, -, *, /) 기호를 입력받아 검증.
유효하지 않은 기호는 다시 입력받음.
4. 예외 처리
calc.calculate() 호출 시 ArithmeticException(나눗셈의 분모가 0)이나 IllegalArgumentException(유효하지 않은 연산자) 발생 가능.
예외 메시지를 출력하여 사용자에게 명확히 알림.
5. 메뉴 루프
연산 결과를 조회, 삭제하거나 계산을 계속 진행.
exit 입력 시 프로그램 종료.
💥 트러블 슈팅
1. 반복 로직 중 코드 중복
문제: 첫 번째와 두 번째 숫자 입력 로직이 중복.
해결: 숫자를 입력받는 메서드(getPositiveInteger)로 분리하여 코드 중복 제거.
2. 사용자 입력 메서드 구조 통일성 부족
문제: 숫자 입력과 연산자 입력이 구조적으로 비슷한데, getPositiveInteger 메서드는 있지만 연산자 입력받는 메서드는 없음.
해결:
getPositiveInteger와 구조적으로 유사한 방식으로 getOperator 메서드를 구현.
사용자 입력을 반복적으로 검증하고 명확한 오류 메시지를 출력하는 방식으로 통일감 부여.
3. 분모가 0인 경우 나눗셈 오류
문제: 0으로 나누려 하면 ArithmeticException이 발생. 따라서 기존에는 분모가 0일 때 -1을 반환했으나, 이는 오류와 정상적인 계산 결과를 구분하기 어려운 방식.
해결: 분모가 0인 경우 ArithmeticException 예외를 발생시키고 main에서 이를 처리하여 메시지 출력.
4. 잘못된 연산 기호 처리
문제: 유효하지 않은 연산자 입력 시 예상치 못한 동작
해결: IllegalArgumentException을 발생시켜 잘못된 연산자를 명확히 인지시킴.
5. 결과 삭제 시 잘못된 입력 반복
문제: 결과 삭제 메뉴에서 y 또는 n 이외의 입력 시 반복적으로 잘못된 입력 메시지를 출력.
해결:
y 또는 n 입력만 허용하도록 반복 로직을 개선.
else를 제거하고 if 조건으로 명확히 구분하여 코드 가독성 향상.
6. 불필요한 버퍼 정리 코드
문제: sc.nextLine()로 버퍼를 정리하던 방식이 불필요함.
해결:
sc.nextLine() 대신 sc.next()로 입력 처리를 변경.
불필요한 버퍼 정리 코드 삭제
7. ArrayList에서 List로 변경
문제: 구체적인 구현체인 ArrayList를 사용하여 코드가 특정 구현에 종속적임.
해결:
변수 타입을 List로 변경하여 다형성을 활용.
추후 다른 리스트 구현체로 변경 시 유연성을 확보.
내부에서는 여전히 ArrayList를 사용하지만, 외부에서는 List로 다루도록 수정.
✍ 고민 및 어려웠던 부분
😮 과제를 하며 코드가 돌아가게 하는 것이 가장 우선 순위였지만, 그래도 나름대로 클린 코드를 위해 노력했다고 생각했는데 피드백을 받으며 내가 정말 더럽게 코드를 짰구나를 느꼈다...
주석도 열심히 달고 엔터도 맞춰서 하고 커밋도 꼬박꼬박 했는데 막상 제일 중요한 대괄호 부분에서 컨벤션에 맞지 않는 코드를 짠다던가 말이다. 변명을 하자면 인텔리제이가 너무 잘 써줘서 당연히 이것도 맞춰줬을 줄 알았는데 하하 코드 정렬을 습관화하자.. 커밋하기 전에 필수..
💥 코드 정렬 단축키:ctrl+Alt+Shift+L
피드백 받은 내용이 대부분 코드 리펙토링이었다. 대괄호 정렬, 코드 정렬, 조건문 통일 ...
그 외에 중요한 피드백이List의 자식들이라 해야 되나 arrayList나 LinkedList 같은 것들 선언할 때 인터페이스를 활용하기.. 즉 List = arrayList 이렇게 말이다. 인터페이스 = 구체적인 클래스
또public int calculate(int a, int b, char c)에서 분모가 0인 계산을 할 때 return -1을 했는데 아주 안 좋은 방식이라는 거~~~ 어떻게 할까 고민하다가 throw로 오류를 던지는 것으로 해결했다!