TIL - 계산기 오답노트

Jiyeong Kim·2025년 1월 10일

TIL

목록 보기
7/24
post-thumbnail

과제 제출을 했다..
근데 하고 보니 체크 박스를 놓친게 보이는거다..
양수를 왜 보지 못했던거니..

단순히 양수만 놓친 것도 아니라서 정답 코드와 문제를 보고 오답노트를 해보려고 한다.

Lv 1. 클래스 없이 기본적인 연산을 수행할 수 있는 계산기 만들기

조건1. 양의 정수(0 포함)를 입력받기

  • Scanner를 사용하여 양의 정수 2개(0 포함)를 전달 받을 수 있습니다.
  • 양의 정수는 각각 하나씩 전달 받습니다.
  • 양의 정수는 적합한 타입으로 선언한 변수에 저장합니다.

나의 코드

System.out.println("첫 번째 정수를 입력하세요: ");
int number1 = sc.nextInt();

System.out.println("원하는 사칙연산 기호를 입력하세요(+, -, *, /): ");
String Operation = sc.next();

System.out.println("두 번째 정수를 입력하세요: ");
int number2 = sc.nextInt();

(이미 다음 조건도 틀림)

정답 코드

while (true) {
      System.out.print("첫 번째 숫자를 입력하세요: ");
      int firstNumber = sc.nextInt();
      if (firstNumber < 0) {
      System.out.println("음수는 입력할 수 없습니다. 프로그램을 재시작합니다.");
                continue;
      }

      System.out.print("두 번째 숫자를 입력하세요: ");
      int secondNumber = sc.nextInt();
      if (secondNumber < 0) {
      System.out.println("음수는 입력할 수 없습니다. 프로그램을 재시작합니다.");
      continue;
      }

      System.out.print("사칙연산 기호를 입력하세요 (+, -, *, /): ");
      char operator = sc.next().charAt(0);
  • 만족되지 않은 조건 : Scanner를 사용하여 양의 정수 2개(0 포함)를 전달 받을 수 있습니다.
  • 추가해야 하는 코드 : 각 숫자 입력 코드 마다 음의 정수 입력시 불가능하다는 if문을 추가한다.
if (firstNumber < 0) {
       System.out.println("음수는 입력할 수 없습니다. 프로그램을 재시작합니다.");
       continue;
}




조건2. 사칙연산 기호(➕,➖,✖️,➗)를 입력받기

  • Scanner를 사용하여 사칙연산 기호를 전달 받을 수 있습니다.
  • 사칙연산 기호를 적합한 타입으로 선언한 변수에 저장합니다. (charAt(0))

나의 코드

System.out.println("원하는 사칙연산 기호를 입력하세요(+, -, *, /): ");
String Operation = sc.next();

굳이 변명?하자면? char타입? 쓸줄 모름~ 하고 String을 썼던 것 같다.

튜터님께서 몰라도 쓰면 인텔리제이가 알려주신다고 했다..
문제를 활용하자

정답코드

System.out.print("사칙연산 기호를 입력하세요 (+, -, *, /): ");
char operator = sc.next().charAt(0);

이 경우에 또 바꾸어줘야하는게

switch (Operation) {
      case "+":
           result = number1 + number2;
           break;
      case "-":
           result = number1 - number2;
           break;
      case "*":
           result = number1 * number2;
           break;
      case "/":
           if (number2 != 0) {
                result = number1 / number2;
           } else {
               System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
               break;

각 연산기호에 씌워져 있는 "" 을 ''으로 바꾸어 줘야 오류가 뜨지 않는다.




조건3. 위에서 입력받은 양의 정수 2개와 사칙연산 기호를 사용하여 연산을 진행한 후 결과값을 출력하기

  • 키워드 : if switch
  • 사칙연산 기호에 맞는 연산자를 사용하여 연산을 진행합니다.
  • 입력받은 연산 기호를 구분하기 위해 제어문을 사용합니다. (예를 들면 if, switch)
  • 연산 오류가 발생할 경우 해당 오류에 대한 내용을 정제하여 출력합니다.
    - ex) “나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.“

    예시 코드

    public class App {
      public static void main(String[] args) {
          Scanner sc = new Scanner(System.in);
          ...
          int result = 0;
          /* 제어문을 활용하여 위 요구사항을 만족할 수 있게 구현합니다.*/
          System.out.println("결과: " + result);
      }
    }

나의 코드

  switch (Operation) {
              case '+':
                  result = number1 + number2;
                  break;
              case '-':
                  result = number1 - number2;
                  break;
              case '*':
                  result = number1 * number2;
                  break;
              case '/':
                  if (number2 != 0) {
                      result = number1 / number2;
                  } else {
                      System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
                      break;
                  }
              default:
                  System.out.println("유효하지 않은 연산자입니다.");
                  continue;
      }
      System.out.println("결과: " + result);
      System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
  • 문제점: 나누기 식에서 정수끼리 계산했다 하더라도 결과가 실수로 나타날 경우를 고려하지 않음.

정답 코드

switch (operator) {
                case '+':
                    System.out.println("결과: " + (firstNumber + secondNumber));
                    break;
                case '-':
                    System.out.println("결과: " + (firstNumber - secondNumber));
                    break;
                case '*':
                    System.out.println("결과: " + firstNumber * secondNumber);
                    break;
                case '/':
                    if (secondNumber == 0) {
                        throw new IllegalArgumentException("나눗셈 연산에서 분모(두 번째 정수)가 0일 수 없습니다.");
                    }
                    System.out.println("결과: " + (double) firstNumber / secondNumber);
                    break;
                default:
                    throw new IllegalArgumentException("사칙연산 기호는 +, -, *, / 중 하나여야 합니다.");
            }
  • 개선 방안: 나누기 코드에를 아래와 같이 수정한다.
case '/':
     if (number2 != 0) {
        result = (double) number1 / number2;

다만 이 경우 double에 오류가 뜨는데, 원인은 내가 선언해 놓은 result 값이 int 타입이기 때문이다. 선택지는 두 가지 인데, 정답코드처럼 아예 result라는 값을 만들지 않고 그냥 프린트 하는 방법과, 내가 선언해 놓은 result 타입을 int에서 double로 바꾸어주면 된다. 나는 double로 바꾸어 주었다.

double result = 0;
  • 추가로 까먹지 않았으면 하는 것 : 예외처리 - throw

정답 코드

default:
       throw new IllegalArgumentException("사칙연산 기호는 +, -, *, / 중 하나여야 합니다.");

정답 코드처럼 "throw new 발생시킬예외();"의 형태로 사용할 수 있다.






조건4. 반복문을 사용하되, 반복의 종료를 알려주는 “exit” 문자열을 입력하기 전까지 무한으로 계산을 진행할 수 있도록 소스 코드를 수정하기

  • 키워드 : 무한으로 반복, 수정하기 (처음부터 무한 반복하는 것이 아니라, 위 스텝별로 진행하며 수정)
  • 반복문을 사용합니다. (예를 들어, for, while…)

나의 코드

  • 우선 나는 맨 위에 boolean 타입의 변수(keepRunning)를 선언해서 true, false로 계산을 계속 진행할지를 정하도록 만들어놨다.
boolean keepRunning = true;
  • 이에 while(keepRunning)안에 연산식을 넣어 무한으로 반복하도록 만들었다.
while(keepRunning) {
    System.out.println("첫 번째 정수를 입력하세요: ");
    int number1 = sc.nextInt();
    if (number1 < 0) {
        System.out.println("음수는 입력할 수 없습니다. 프로그램을 재시작합니다.");
        continue;
    }
    System.out.println("원하는 사칙연산 기호를 입력하세요(+, -, *, /): ");
    char Operation = sc.next().charAt(0);

    System.out.println("두 번째 정수를 입력하세요: ");
    int number2 = sc.nextInt();
    if (number2 < 0) {
       System.out.println("음수는 입력할 수 없습니다. 프로그램을 재시작합니다.");
       continue;
    }
.
.
.
.
System.out.println("결과: " + result);
        System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");

        String reply = sc.next();
        if (reply == "exit") {
            keepRunning = false;
        }
        }
        System.out.println("계산기를 종료합니다.");
        sc.close();
  • if 문으로 exit을 받게 되면 keepRunning이 false가 되면서 계산이 종료된다.

정답 코드

  • 해당 문제에서는 굳이 while을 쓰기 위해 변수를 선언하지 않아도 됨을 알 수 있다.
 while (true) {
            System.out.print("첫 번째 숫자를 입력하세요: ");
            int firstNumber = sc.nextInt();
            if (firstNumber < 0) {
                System.out.println("음수는 입력할 수 없습니다. 프로그램을 재시작합니다.");
                continue;
            }

            System.out.print("두 번째 숫자를 입력하세요: ");
            int secondNumber = sc.nextInt();
            if (secondNumber < 0) {
                System.out.println("음수는 입력할 수 없습니다. 프로그램을 재시작합니다.");
                continue;
            }

            System.out.print("사칙연산 기호를 입력하세요 (+, -, *, /): ");
            char operator = sc.next().charAt(0);
.
.
.
.
System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
        String answer = sc.next();
        if ("exit".equals(answer)) {
           break;
  • 강조했던 부분: ".equal"사용 부분
    다들 이렇게 작성하지 않고 (next.equls("exit"))과 같은 형태로 작업을 했을 거라고 하셨는데(해당 문제에서는 저렇게 해도 문제는 없다.) (reply=="exit") 따위로 적은 나로서는 굉장히 찔리지도 못하게 괴로운 설명이었다 :) 나 진짜 어떡하지....
  • 정답코드 처럼 작성해야 하는 이유:
    • Scanner로 입력 받은 문자는 절대로 null이 될 수 없음
    • "exit"이라는 문자열도 null이 될 수 없음
      왜? 아래와 같은 형태이기 때문.
      String answer = sc.next();
      String exit = "exit"; //<-이미 글자를 입력했으니 null이 될 수 없음
      if ("exit".equals(answer))
    • 그리고 이렇게 확실하게 null이 아닌 코드가 앞에 오는 것이 좋은 코드임(안전한secure 코드)
    • 만약 null이 될 수 있는 String이 앞에오고, null값이 되는 순간 NullPointerException 에러가 날 수 있다.

앞으로 이렇게(=NullSafe) 작성하는 버릇을 들여야 한다.

  • 고친 코드 부분
    String reply = sc.next();
                if ("exit".equals(reply)) {
                keepRunning = false;
            }

lv2. 도 이걸 기반으로 했기 때문에 다 뜯어 고쳤다..^.^










Lv2. 클래스를 적용해 기본적인 연산을 수행할 수 있는 계산기 만들기

조건1. 사칙연산을 수행 후, 결과값 반환 메서드 구현 & 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성

  • 사칙연산을 수행한 후, 결과값을 반환하는 메서드 구현
  • 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
  • 1) 양의 정수 2개(0 포함)와 연산 기호를 매개변수로 받아 사칙연산(➕,➖,✖️,➗) 기능을 수행한 후 2) 결과 값을 반환하는 메서드와 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성합니다.

나의 코드 (필드)

public class Calculator2 {
    private int number1;
    private String Operation;
    private int number2;
    private boolean keepRunning;
    private List<Double> records = new ArrayList<>();
.
.
.  
.

정답 코드

public class Calculator2 {

    private final List<Double> resultList = new ArrayList<>();
.
.
.  
.

비교하면 진짜 웃기다 왜 이렇게 길게 적었지

  • 주목할 부분
    1) List 다음과 같이쓰기
    private List<Double> records = new ArrayList<>(); 
    2) final 사용
    • 사용 이유: records 안의 내용이 바뀜과 상관없이 List 주소값의 변화가 없도록 해야 하기 때문
    • 추후 협업을 위해서라도 final의 사용을 중요시 해야함(final 사용 불가 시 주석처리라도 하기)
  • 파이널 삽입 시 :

    이렇게 위에 이미 선언되어있기 때문에 아랫쪽에 records 리스트를 건드리는 코드가 있으면 오류가 뜬다.
records.add(result);
        return result;	 /// 나의 코드
resultList.add(result);
  		return result;   /// 강의 정답 코드 --> 강의 중 수정
addResult(result);   
        return result;   /// 정답 코드(수정 후/아랫쪽에서 설명ㅇ)

사실 더 큰 문제는 따로 있다..
나는 패키지 분리를... 이걸 쓰면서 했다...^.^...

그냥 칼큘레이터.java, 칼큘레이터2.java <- 이게 한 패키지에.. 있었음...;)



조건2. Lv. 1에서 구현한 App 클래스의 main 메서드에 Calculator 클래스가 활용될 수 있도록 수정

  • 연산 수행 역할은 Calculator 클래스가 담당
    • 연산 결과는 Calculator 클래스의 연산 결과를 저장하는 필드에 저장
  • 소스 코드 수정 후에도 수정 전의 기능들이 반드시 똑같이 동작해야합니다.

나의 코드

public class App {
    public static void main(String[] args) {
        Calculator2 calculator = new Calculator2();
        Scanner sc = new Scanner(System.in);
        boolean keepRunning = true;
        Double result = (double) 0;

변치않는 나의 지저분한 코드...^.^

정답 코드

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

        Calculator2 calculator = new Calculator2();
  • Calculator2 클래스 호출문 비교

나의 코드

result = calculator.calculate(number1, Operation, number2);

정답 코드

double result = calculator.calculate(firstNumber, secondNumber, operator);

나는 윗쪽에 따로 result를 Double로 선언해놨는데, 정답코드처럼 입력하는게 더 깔끔한 것 같다.



조건3. App 클래스의 main 메서드에서 Calculator 클래스의 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근하지 못하도록 수정 (캡슐화)==private

  • 간접 접근을 통해 필드에 접근하여 가져올 수 있도록 구현합니다. (Getter 메서드)
  • 간접 접근을 통해 필드에 접근하여 수정할 수 있도록 구현합니다. (Setter 메서드)
  • 위 요구사항을 모두 구현 했다면 App 클래스의 main 메서드에서 위에서 구현한 메서드를 활용 해봅니다.

나의 코드

public List<Double> getRecords() {
        return records;
}

public void setRecords(Double records) {
        this.records.add(records);
        System.out.println("기록이 추가되었습니다.");

정답 코드

public List<Double> getResultList() {
        return resultList;
}

public void addResult(double result) {
        resultList.add(result);
}
  • setter 부분이 꼭 set으로 시작할 필요는 없다. 잘 알아보게 적으면 된다.
  • 그리고 이제 윗쪽에서 수정되었던 코드의 이유를 볼 수 있다.

윗쪽 리스트 저장 코드

records.add(result);
        return result;	 /// 나의 코드
resultList.add(result); // <- 이 부분이 setter 부분과 동일
  		return result;   /// 강의 정답 코드 --> 강의 중 수정
addResult(result);     // 때문에 아래와 같이 수정됨
        return result;   /// 정답 코드(수정 후/아랫쪽에서 설명ㅇ)



  • 메서드 활용 부분

나의 코드

System.out.println("전체 연산 기록 :" + calculator.getRecords());

원하지 않아도 냅다 출력해줘버리기

정답 코드

System.out.println("저장된 연산결과를 조회하시겠습니까? (yes 입력 시 조회)");
            if ("yes".equals(sc.next())) {
                List<Double> resultList = calculator.getResultList();
                System.out.println("저장된 연산결과는 : " + resultList.toString());

toString은.. 아직 나에게 조금의 더 시간이 필요하다.






조건4. Calculator 클래스에 저장된 연산 결과들 중 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현한 후 App 클래스의 main 메서드에 삭제 메서드가 활용될 수 있도록 수정

  • 키워드 : 컬렉션
    • 컬렉션에서 ‘값을 넣고 제거하는 방법을 이해한다.’가 중요합니다!

나의 코드(Calculator class)

public void removeResult(List<Double> records) {
        records.remove(0);
        System.out.println("최종 연산 기록 :");
    }

내가 봐도 뒤죽박죽이다... 내일 알라딘 가서 자바의 정석 사올거다...

정답 코드(Calculator class)

public void removeResult() {
        resultList.remove(0);
    }

나의 코드(App class)

System.out.println("첫 번째 연산 기록을 삭제합니다.");
calculator.removeResult(calculator.getRecords());

시키지도 않았는데 냅다 삭제해버리기..getRecords는 왜 여기있는지.. 눈물만 나온다..

정답 코드(App class)

System.out.println("가장 먼저 저장된 연산 결과를 삭제하시겠습니까? (yes 입력 시 삭제)");
            if ("yes".equals(sc.next())) {
                calculator.removeResult();
            }






후기: 다음주 과제가 너무 무섭다..

그러니까 주말에 공부하기...

할게 쌓여만 간다..

3,4,5주차 강의 들어야하고 도서관 과제도 해야하고, 새 과제도 나오면 해야 한다..! ...어떻게든 하겠지...

profile
해봅시다

0개의 댓글