자바 미니 프로젝트 - 계산기 만들기 Part1

Zyoon·2025년 4월 18일

미니프로젝트

목록 보기
4/35
post-thumbnail

💡자바 콘솔로 간단한 사칙연산 계산기 만들기


설계

  • 입력 값으로 숫자(int) 두개와 연산자(+,-,*,/) 하나를 입력받는다
  • 입력값을 계산하여 결과 값 반환한다
  • 입력한 문자와 연산자, 결과 값은 클래스화 하여 객체로 관리한다
  • 입력한 문자들과 연산 결과들은 모두 stack으로 push
  • 연산 결과 데이터 삭제기능


주요 코드 정리

필드부분

//Getter, Setter, 생성자
public class CalculatorData {
    private int firstNum;
    private int secondNum;
    private char operator;
    private int resultNum;

첫번째 숫자 입력

//스캐너 및 클래스 인스턴스
Scanner sc = new Scanner(System.in);
CalculatorData calData = new CalculatorData();

//첫번째 숫자 입력
int tempNum;
while(true){
    System.out.println("\n첫번째 숫자를 입력하세요");
    System.out.print("숫자 입력 : ");
    try{
        tempNum = sc.nextInt();
        calData.setFirstNum(tempNum);
        break;
    }catch (InputMismatchException e){
        System.out.println("문자나 기호, 너무 긴 숫자는 입력할 수 없어요.");
        sc.nextLine();
    }
}
  • nextInt() 로 숫자만 입력받음
  • 문자열이나 int 값 범위를 벗어나는 값이 입력될 경우 InputMismatchException 발생
  • 이후 try-catch 로 예외처리 후 재 입력
  • 제대로 입력받으면 break 후 반복문 벗어남. 값은 객체로 Set
  • 두번째 입력 방식도 위와 동일하지만 나눗셈 예외 처리 추가
    //연산자가 '/' 이고 두번째 숫자가 0이라면 예외처리
    if(tempChar == '/' && tempNum == 0) {
        System.out.println("0으로 나눌 수 없습니다.");
        continue;
    }

연산자 입력

//set으로 연산자 값 미리 생성
static Set<Character> operatorSet = new HashSet<>(Arrays.asList('*', '+', '-', '/'));

char tempChar;
while(true){
    System.out.println("\n연산자를 입력하세요");
    System.out.print("연산자 입력 : ");
    try{
        String str = sc.next(); //nextLine > next 로 변경
        tempChar = str.charAt(str.length()-1);
        if(operatorSet.contains(tempChar)) {
            calData.setOperator(tempChar);
            break;
        }else {
            System.out.println("+,-,*,/ 만 입력해주세요");
        }
    } catch (Exception e) {
        System.out.println("+,-,*,/ 만 입력해주세요");
        sc.nextLine();
        continue;
    }
}
  • HashSet 으로 연산자 4개 미리 정의해둠
  • next() 로 연산자 입력 받은 후 HashSet에 정의해 둔 연산자와 contains() 로 비교
  • 연산자가 아닌 모든 결과는 다시 입력하라는 출력문 후 반복 입력
  • 제대로된 연산자 입력 시 객체에 Setbreak

연산

//계산 메서드 (계산 Class 부분) -> 오버플로우 방지 throws
public int calculate(CalculatorData calData) throws ArithmeticException{

  int resultNum = 0;
  int firstNum = calData.getFirstNum();
  int secondNum = calData.getSecondNum();
  char operator = calData.getOperator(); // [+,-,*,/]

  switch (operator){
      case '+' : resultNum = Math.addExact(firstNum,secondNum); break;
      case '-' : resultNum = Math.subtractExact(firstNum,secondNum); break;
      case '*' : resultNum = Math.multiplyExact(firstNum,secondNum); break;
      case '/' : resultNum = firstNum / secondNum; break;
  }
  return resultNum;
}

//계산 메서드 - 입력값 범위 초과시 에러 (Main 부분)
try{
    calData.setResultNum(calculator.calculate(calData));
    System.out.println("연산 결과 : " + calData.getResultNum());
}catch (ArithmeticException e) {
    System.out.println("입력값이 범위를 초과하였습니다.");
    System.out.println("다시 입력해 주세요.");
    continue;
}

//결과 저장
calculator.getResultStack().push(calData);

//결과들 출력
System.out.println(calculator); // 전체 결과{[연산 결과{ 5 + 6 = 11 }]}
  • 객체로 넘긴 데이터에서 각각의 데이터 Get
  • switch 문으로 각각의 연산자로 연산
  • input 데이터를 int 값으로 받았기 때문에 연산 후 데이터 오버플로우 시 예외 처리 후 처음부터 다시 입력하도록 설정 (Main class 반복문 처음으로 돌아감)
  • 결과값은 객체로 Set 후 모든 연산값은 stack으로 push
  • 결과값 출력 연산 재 반복

데이터 삭제

public Calculator removeResult(Calculator cal, Scanner sc){
  //데이터 확인
  if(cal.getResultStack().isEmpty()) return cal;
  
  //데이터 삭제
  int scTemp;
  while (true){
       System.out.println("\n데이터를 지우시겠습니까?");
       System.out.println("최근 데이터 삭제 : 1");
       System.out.println("모든 데이터 삭제 : 2");
       System.out.println("삭제 안함 : 0");
       try {
           scTemp = sc.nextInt();

           if(scTemp == 1){
               cal.getResultStack().pop();
               return cal;
           } else if (scTemp == 2) {
               cal.getResultStack().clear();
               return cal;
           } else if(scTemp == 0){
               return cal;
           }
           System.out.println("다시 눌러 주세요.");
       } catch (Exception e) {
           System.out.println("다시 눌러 주세요.");
           sc.nextLine();
       }
   }
}
  • 입력 값 받아서 해당 조건으로 수행
  • 1번 입력 시 : pop() 함수 사용하여 최근 데이터 삭제 후 리턴
  • 2번 입력 시 : clear() 함수 사용하여 모든 데이터 삭제 후 리턴
  • 0번 입력 시 : 그대로 리턴

출력 결과

첫번째 숫자를 입력하세요.
숫자 입력 : 56

연산자를 입력하세요.(+,-,*,/)
연산자 입력 : -

두번째 숫자를 입력하세요.
숫자 입력 : 44

연산 결과 : 12
전체 결과{연산 결과{ 56 - 44 = 12 }]}

이슈 사항

1. nextLine 과 nextInt 의 차이

문제점

  • 입력값을 받을 때 nextInt 로 값을 입력 받은 후 다음에 nextLine() 으로 입력 받으면 입력하기 전에 바로 넘어가버리는 문제 발생했다.
[출력문]
첫번째 숫자를 입력하세요
숫자 입력 : 45

연산자를 입력하세요
연산자 입력 : +,-,*,/ 만 입력해주세요 -> 입력을 하기도 전에 바로 넘어가버림

원인

  • nextInt와 nextLine의 차이는 단순히 int값과 string 값을 받는 차이가 아니다.
  • 가장 중요한 차이는 버퍼에 남아있는 “엔터”를 어떻게 처리하는지의 차이가 있다.
  • 만약 입력값에서 ‘12345’ 입력 후 “엔터”를 누르게 되면 nextInt 의 경우 ‘12345’만 가져오고 뒤의 “엔터”는 버퍼에 남겨둔다.
  • 그리고 다음에 nextLine() 이 호출되면 그 “엔터” 만을 읽어와서 빈 문자열을 리턴해버린다.

해결

  • 가장 좋은 방법은 nextInt() 다음에 바로 nextLine() 을 날려서 엔터를 소비하는 것이 가장 좋다.
    tempNum = sc.nextInt();
    sc.nextLine();
    ------------------------
    String str = sc.nextLine();
  • 두번째 방법은 nextInt() 후에 next() 를 사용하는 건데 이렇게 하면 결국 또 “엔터” 가 남아있기 때문에 추천은 하지 않는다
    tempNum = sc.nextInt();
    ------------------------
    String str = sc.next(); //정상적으로 넘어가기는 하지만 비추천
  • catch 문 안에도 nextLine() 사용하여 처리하였다.
    try{
        tempNum = sc.nextInt();
        sc.nextLine();
        calData.setFirstNum(tempNum);
        break;
    }catch (InputMismatchException e){
        System.out.println("에러가 났어요.");
        sc.nextLine(); // 예외 상황에서도 버퍼를 비울 수 있음
    }

2. Scanner.close() 의 위치

문제점

  • Scanner.close() 을 한 후 다음 메서드에서 new 생성자로 생성한 뒤 입력 값을 받을 때 NoSuchElementException 발생
    	Exception in thread "main" java.util.NoSuchElementException

원인

  • 자바의 Scanner는 한 번 close() 하면 해당 InputStream이 같이 닫혀버린다.(System.in)
  • 그래서 이후에 다시 Scanner를 새로 만들어도 InputStream이 이미 닫힌 상태이다.
  • 그러므로 입력을 받으려 할 때 NoSuchElementException이 발생하게 된다.

해결법

  • Scanner 프로그램 시작 시 한 번 생성 후 ****마지막에만 한 번 닫도록 설정.
  • 보통 System.in 은 프로그램 종료 직전에 close() 하는 게 베스트.

📗미니 프로젝트 - 계산기 만들기 Part2
📓계산기 만들기 Github

profile
기어 올라가는 개발

0개의 댓글