코딩/sparta TIL

CH2 계산기 과제

americanoallday 2025. 2. 25. 21:51

Intro

더보기

여러분들은 자바의 기본 문법과 객체 지향 프로그래밍의 개념을 익혀나가고 있습니다.

이제, 이 지식을 활용하여 흥미로운 과제를 진행할 시간입니다.

여러분이 이번 과제에서 만들게 될 프로그램은 ‘계산기’입니다.

하지만 단순한 계산기가 아니라, 클래스를 활용한 계산기와 클래스를 사용하지 않는 계산기 두 가지 모두를 구현해볼 것입니다.

계산기를 만드는 데에는 여러 가지 접근 방식이 있습니다. 이번 과제를 통해 여러분은 자바의 기본 문법을 적용하면서 객체 지향 프로그래밍의 개념을 실습하게 될 것입니다. 자, 이제 우리가 다룰 두 가지 버전의 계산기에서 무엇을 해야 할지 살펴볼까요?

  1. 클래스를 활용한 계산기: 이 버전에서는 자바의 객체 지향 원칙을 적용하여 클래스를 정의하고, 연산을 수행하는 메소드를 작성해야 합니다. 이 과정에서 클래스와 객체의 개념을 이해하고 활용하는 방법을 익힐 수 있습니다.
  2. 클래스를 사용하지 않는 계산기: 이 버전에서는 클래스 없이 자바의 기본 문법만을 사용하여 계산기를 구현합니다. 변수와 메소드를 이용하여 연산을 수행하는 과정에서 자바의 절차적 프로그래밍을 경험할 수 있습니다.
    1. 절차적 프로그래밍? 쉽게 말해 중복이나 관련 없는 기능들은 따로 분리하는 프로그래밍
    2. 분리해낸 단위를 procedure라고 합니다. 쉽게 말해 “함수”죠!

이번 과제의 핵심 목표는 자바 문법의 실습과 객체 지향 개념의 적용입니다.

예시 코드를 기반으로 여러분은 주어진 코드에 기능을 추가하고, 각 버전의 계산기를 완성해보세요.

계산기 구현 외에도, 여러분이 알고 있는 자바 문법과 객체 지향 개념을 활용하여 프로그램의 구조를 고민해보고, 이를 통해 더 나아가 프로그래밍 능력을 발전시킬 수 있는 기회가 될 것입니다.

그럼, 자바를 사용하여 멋진 계산기를 만들어볼 준비가 되셨나요? 시작해봅시다!

개발 프로세스 가이드

더보기

0단계 : 개발환경 세팅

  1. IntelliJ IDEA 설치 및 실행
  2. 새 Java 프로젝트 생성
    • IntelliJ IDEA를 열고, Welcome Screen에서 New Project를 클릭합니다.
      • 기존 프로젝트가 열려 있다면, File 메뉴에서 New -> Project...를 선택합니다.
    • 프로젝트 타입 선택 : 왼쪽 사이드바에서 Java를 선택합니다.
    • JDK 선택: Project SDK 드롭다운에서 사용할 JDK 버전을 선택합니다. JDK가 설치되어 있지 않다면, Add SDK를 클릭하여 설치된 JDK 경로를 지정합니다. 17버전 지정
    • 프로젝트 템플릿 선택: 기본적으로 Create project from template은 선택하지 않고 진행합니다.
    • 프로젝트 이름 및 위치 설정: Project name 필드에 프로젝트 이름을 입력합니다. (예: CalculatorProject)
      • Project location 필드에서 프로젝트를 저장할 위치를 지정합니다.
    • Finish 버튼을 클릭하여 프로젝트를 생성합니다.
  3. 패키지 및 클래스 추가
    • 패키지 생성:
      • 프로젝트가 생성되면, src 폴더를 우클릭하고 New -> Package를 선택합니다.
      • 패키지 이름을 입력합니다. (예: com.example.calculator).
    • 클래스 생성:
      • 생성한 패키지를 우클릭하고, New -> Java Class를 선택합니다.
        • IntelliJ IDEA에서 생성된 Calculator.java 파일을 열고, 기본 코드를 입력합니다.
      • 클래스 이름을 입력합니다. (예: Calculator) 이후, Enter를 눌러 클래스 파일을 생성합니다.
  4. 기본 코드 작성
    • 코드를 작성한 후, 파일을 저장합니다.
  5. 프로젝트 실행
    • 실행 구성 설정:
      • 상단 메뉴에서 Run -> Edit Configurations...를 선택합니다.
      • + 버튼을 클릭하고 Application을 선택하여 새로운 실행 구성을 추가합니다.
      • Main class 필드에 com.example.calculator.Calculator를 입력하거나, ... 버튼을 클릭하여 클래스를 선택합니다.
      • 설정이 완료되면 Apply를 클릭하고 OK를 클릭합니다.
      • 상단의 Run 버튼(녹색 화살표)을 클릭하거나, Shift + F10을 눌러 프로그램을 실행합니다.
      • 콘솔 창에서 "Hello, Calculator!"가 출력되는 것을 확인할 수 있습니다.

이렇게 IntelliJ IDEA를 사용하여 새 Java 프로젝트를 생성하고 기본 클래스를 작성할 수 있습니다. 이후 단계에서는 요구사항 정의, 설계, 구현 등을 진행하면 됩니다.

 

1단계 : 요구사항 정의 및 설계

  • 이미 요구사항이 주어졌다면, 구현을 위해 어떤 요구사항이 있는지 숙지해야합니다.
  1. 요구사항 정의
    • 어떤 계산기 기능이 필요한지 명확히 합니다.
    • 예를 들어, 기본적인 사칙연산(덧셈, 뺄셈, 곱셈, 나눗셈), 괄호 사용, 제곱근, 제곱 등.
    • 사용자 인터페이스 방식 결정 : 콘솔 기반인지, GUI(그래픽 사용자 인터페이스) 기반인지.
    • 예외 처리 : 0으로 나누기와 같은 오류를 어떻게 처리할지 결정합니다.
  2. 설계
    • 클래스 다이어그램: 계산기에 필요한 클래스를 설계합니다. 예를 들어, Calculator, Operation, Parser 등이 될 수 있습니다.
    • 기능 분해: 주요 기능을 메소드로 분리합니다. 예를 들어, add(), subtract(), multiply(), divide(), evaluateExpression() 등.
    • 데이터 흐름 설계: 데이터가 클래스 간에 어떻게 흐를지를 결정합니다. 입력, 처리, 출력의 흐름을 이해합니다.

필수 기능 가이드

더보기

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

  • [ ] 양의 정수(0 포함)를 입력받기
    • [ ] Scanner를 사용하여 양의 정수 2개(0 포함)를 전달 받을 수 있습니다.
    • [ ] 양의 정수는 각각 하나씩 전달 받습니다.
    • [ ] 양의 정수는 적합한 타입으로 선언한 변수에 저장합니다.
    • 예시 코드
    • public class App {
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);

              System.out.print("첫 번째 숫자를 입력하세요: ");
              // Scanner를 사용하여 양의 정수를 입력받고 적합한 타입의 변수에 저장합니다.
              System.out.print("두 번째 숫자를 입력하세요: ");
              // Scanner를 사용하여 양의 정수를 입력받고 적합한 타입의 변수에 저장합니다.
          }
      }
  • [ ] 사칙연산 기호(➕,➖,✖️,➗)를 입력받기
    • [ ] Scanner를 사용하여 사칙연산 기호를 전달 받을 수 있습니다.
    • [ ] 사칙연산 기호를 적합한 타입으로 선언한 변수에 저장합니다. (charAt(0))
    • 예시 코드
    • public class App {
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);
              ...
              System.out.print("사칙연산 기호를 입력하세요: ");
              // 사칙연산 기호를 적합한 타입으로 선언한 변수에 저장합니다. 
          }
      }
  • [ ] 위에서 입력받은 양의 정수 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);
            }
        }
  • [ ] 반복문을 사용하되, 반복의 종료를 알려주는 “exit” 문자열을 입력하기 전까지 무한으로 계산을 진행할 수 있도록 소스 코드를 수정하기
    • [ ] 키워드 : 무한으로 반복, 수정하기 (처음부터 무한 반복하는 것이 아니라, 위 스텝별로 진행하며 수정)
    • [ ] 반복문을 사용합니다. (예를 들어, for, while…)
    • 예시 코드
    • public class App {
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);
              /* 반복문 사용 해서 연산을 반복 */
              ...
              System.out.println("결과: " + result);
              
              System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
              /* exit을 입력 받으면 반복 종료 */
          }
      }

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

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

          public 반환타입 calculate(...매개변수) {
              /* 위 요구사항에 맞게 구현 */
              /* return 연산 결과 */
          }
      }
  • [ ] Lv 1에서 구현한 App 클래스의 main 메서드에 Calculator 클래스가 활용될 수 있도록 수정
    • [ ] 연산 수행 역할은 Calculator 클래스가 담당
      • [ ] 연산 결과는 Calculator 클래스의 연산 결과를 저장하는 필드에 저장
    • [ ] 소스 코드 수정 후에도 수정 전의 기능들이 반드시 똑같이 동작해야합니다.
    • 예시 코드
    • 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 입력 시 종료)");
                  ...
              /* 반복문 종료 */
          }
      }
  • [ ] App 클래스의 main 메서드에서 Calculator 클래스의 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근하지 못하도록 수정 (캡슐화)
    • [ ] 간접 접근을 통해 필드에 접근하여 가져올 수 있도록 구현합니다. (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 입력 시 종료)");
                  ...
              /* 반복문 종료 */
          }
      }
  • [ ] Calculator 클래스에 저장된 연산 결과들 중 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현한 후 App 클래스의 main 메서드에 삭제 메서드가 활용될 수 있도록 수정
    • [ ] 키워드 : 컬렉션
      • [ ] 컬렉션에서 ‘값을 넣고 제거하는 방법을 이해한다.’가 중요합니다!
    • 예시 코드
    • 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 입력 시 종료)");
                  ...
              /* 반복문 종료 */
          }
      }

도전 기능 가이드

더보기

3. Enum, 제네릭, 람다 & 스트림을 이해한 계산기 만들기

  • [ ] 현재 사칙연산 계산기는 (➕,➖,✖️,➗) 이렇게 총 4가지 연산 타입으로 구성되어 있습니다.
    • [ ] Enum 타입을 활용하여 연산자 타입에 대한 정보를 관리하고 이를 사칙연산 계산기 ArithmeticCalculator 클래스에 활용 해봅니다.
    • 예시 코드(기존에 작성했던 양의 정수 계산기를 수정)
    • public enum OperatorType {
          /* 구현 */
      }

      public class ArithmeticCalculator /* Hint */ {
      /* 수정 */
      }
  • [ ] 실수, 즉 double 타입의 값을 전달 받아도 연산이 수행하도록 만들기
    • [ ] 키워드 : 제네릭
      • [ ] 단순히, 기존의 Int 타입을 double 타입으로 바꾸는 게 아닌 점에 주의하세요!
    • [ ] 지금까지는 ArithmeticCalculator, 즉 사칙연산 계산기는 양의 정수(0 포함)를 매개변수로 전달받아 연산을 수행
    • [ ] 피연산자를 여러 타입으로 받을 수 있도록 기능을 확장
      • [ ] ArithmeticCalculator 클래스의 연산 메서드(calculate)
    • [ ] 위 요구사항을 만족할 수 있도록 ArithmeticCalculator 클래스를 수정합니다. (제네릭)
      • [ ] 추가적으로 수정이 필요한 다른 클래스나 메서드가 있다면 같이 수정 해주세요.
    • 예시 코드(기존에 작성했던 양의 정수 계산기를 수정)
    • public class ArithmeticCalculator /* Hint */ {
      /* 수정 */
      }
  • [ ] 저장된 연산 결과들 중 Scanner로 입력받은 값보다 큰 결과값 들을 출력
    • [ ] ArithmeticCalculator 클래스에 위 요구사항을 만족하는 조회 메서드를 구현합니다.
    • [ ] 단, 해당 메서드를 구현할 때 Lambda & Stream을 활용하여 구현합니다.
      • [ ] Java 강의에서 람다 & 스트림을 학습 및 복습 하시고 적용 해보세요!
    • [ ] 추가) 람다 & 스트림 학습을 위해 여러 가지 조회 조건들을 추가하여 구현 해보시면 학습에 많은 도움이 되실 수 있습니다.

Goal : 제출 일정 및 트러블 슈팅하는 법

더보기

최종 제출

  • 기한 : 03/06(목) 14:00 까지
  • 제출해야 할 것
    1. 과제
      1. Github 링크 (Public)
      2. 트러블슈팅을 작성한 TIL
      3. 어디까지 구현했는지
      4. 어떤 부분을 고민했는지, 어려웠던 부분은 어디였는지
        1. 해당 부분을 작성하시면 피드백 간, 튜터님께서 확인 후 피드백 해주실 예정입니다.
    2. 제출 링크

과제 해설 세션

  • 일시 : 03/06(목) 14:00
  • 진행 : 박성규 튜터님

트러블 슈팅하는 법

  • 대기업의 트러블 슈팅 예시를 잠시 살펴보면서 어떤 형식으로 트러블 슈팅을 작성하면 좋을지 알아봅시다.
  • LG CNS - AWS IoT Core Endpoint 신뢰하지 않는 인증서 오류 트러블슈팅
  • 요약
    • LG와 같은 대기업에서도 에러 발생 시 엄청난 테스트와 웹서치로 에러를 해결하는 것을 알 수 있습니다.
    • 트러블 슈팅 형식은 아래와 같은 형식으로 진행된다는 것을 확인할 수 있습니다.
      • 개요
      • 트러블 슈팅
        • 배경 : 어떤 현상을 발견해서
        • 발단 : 이런 장애가 생길 수 있다는 것을 인지했고,
        • 전개 : 장애를 대응, 해결하던 와중에
        • 위기 : 또 다른 장애 발견 또는 간단하게 해결할 수 없다는 것을 알게되어서,
        • 절정 : 근본적인 해결을 위해 이런 방법으로 접근하였다.
        • 결말 : 따라서, 이런이런 방법을 통해 근본적으로 해결 및 앞으로 유지, 보수에 용이하게 개선하게 되었다.
      • 예시
        • 배경 : 개발 환경은 Spring boot로 개발된 Backend Pod가 EKS on EC2로 기동 중이었으며, Backend에서 IoT Core를 통해 IoT Device에 Rule을 내리는 기능이 개발 중이었습니다.
        • 발단 : 이때 다음과 같은 에러가 발생합니다. Backend에서 IoT Core로 API 통신을 위해 Endpoint로 연결을 시도 중, SSL: CERTIFAICATE VERIFY FAILED 가 발생한 것입니다.
        • 전개 : 해당 엔드포인드에 접근해 보니 다음 그림과 같이 신뢰하지 않는 인증서였습니다. 확인해 보니 구글 크롬, 사파리 등에서 더 이상 Symantec에서 발급한 인증서를 신뢰하지 않고 있었습니다.
        • 위기 : 이에 해당 인증서를 별도로 받아 keystore를 통해 신뢰하는 인증서에 추가해 보았으나,여전히 인증서 오류가 발생하였습니다.
        • 절정 : 여러 테스트 및 웹 서핑을 통해 원인을 찾아낼 수 있었습니다. keystore로 인증서를 추가하더라도 2019년 4월 JDK(버전 12, 11, 8 및 7) 패치 이후로 Symantec 인증서를 신뢰하지 않도록 변경 되었던 것이었습니다. 이를 Hedge 할 방법으론, java.secure 파일에서 ‘jdk.security.caDistrustPolicies=SYMANTEC_TLS’ 옵션을 제거(주석) 하는 것입니다.
        • 결말 : 하지만 위의 방법은 굳이 보안에 문제가 있어서 막은 것을 풀고 쓴다는 것이 찝찝했습니다. 그리고 AWS는 Symantec 인증서 비신뢰 이슈가 발생하고 있는데 아무 조치도 하지 않고 있었을까? 하는 의문이 들었고, 역시 다른 경로(엔드포인트)가 존재했습니다. AWS IoT Core의 서비스 엔드포인트는 원래 https://{account-specific-prefix}.iot.{region}.amazonaws.com 으로 Symantec 인증서를 사용하였으나, 위와 같이 많은 기업에서 신뢰하지 않게 되면서 새로운 서비스 엔드포인트https://{account-specific-prefix}-ats.iot.{region}.amazonaws.com 을 생성하였고 이쪽을 사용하도록 권고하고 있었습니다. 하지만 AWS CLI 명령어와 SDK의 Default는 여전히 Symantec 인증서가 사용되는 엔드포인트여서 발생한 문제였습니다. 이에 Backend에서 사용하는 IoT Core 엔드포인트를 ATS로 변경하였고 모든 문제를 해결할 수 있었습니다.
    • 여러분들도 이런 좋은 트러블 슈팅 사례, 형식을 찾아보고 이런 형태로 트러블 슈팅을 작성하고 발표해 주세요! (추천 검색어: aws s3 lg 트러블 슈팅)

 

평가 기준

완성도
필수 필수 기능 - Lv1. 클래스 없이 계산기 구현 - Lv2. 클래스를 활용한 계산기 구현
도전 도전 기능 - Lv3. Enum, 제네릭, 람다, 스트림을 적용한 계산기 구현
이해도
필수 - TIL에 트러블슈팅 과정을 기록해보세요. (과제 제출란에 TIL 링크를 제출해주세요.)
도전 - 디렉토리 및 파일의 적절한 분리 - 변수명 등 코드를 직관적이고 이해하기 쉽게 작성 - 주석을 활용해 코드를 설명
우수성
필수 - ReadMe를 작성해 과제를 소개
도전 - 특수 상황에 대한 예외 처리를 2가지 이상 구현 - 커밋 컨벤션을 지킨 커밋을 10회 이상 시행

피드백은 제출 후, 위 지표를 바탕으로 여러분의 과제 제출 페이지에서 확인할 수 있습니다. (아직은 과제 생성이 되어 있지 않습니다. 제출하실 때 공지로 안내드릴게요!)

과제를 제출한 뒤 위 채점 기준을 바탕으로 튜터님께서 점수를 측정하고 피드백을 남기고, 피드백은 영업일 기준으로 3일 이내로 피드백이 제공될 예정입니다.

여기서 잠깐, 위 기능만 해야하는 건가요? 정답은, “NO”입니다. 위 필수 도전 기능을 모두 구현했다면, 적용해보고 싶었던 기술을 적용해보셔도 됩니다. 요구사항을 만족한다면, 추가 기능은 얼마든지 환영입니다. 그에 맞는 피드백을 제공해드릴게요.

여러분의 과제 수행도는 운영 및 팀편성에 중요한 지표로 활용됩니다. 따라서, 필수 기능 구현을 우선적으로 완성해보고, 완료했다면 도전 기능에도 도전하여 실력을 더욱 강화해보시면 좋겠습니다. 각 기능의 구현 상태에 따라 평가가 이루어지며, 이는 팀 구성과 추후의 팀 프로젝트 성공에 큰 영향을 미칠 수 있습니다. 최선을 다해 모든 기능을 완성해 주시기 바랍니다!

필수 기능이든 도전 기능이든, 어떤 것을 시도했는지와 상관없이 100% 만족스러운 경험은 아닐 수 있습니다. 누구나 성장 과정에서 겪는 일입니다. 중요한 건 여기서 배운 경험을 다음에 어떻게 활용하느냐입니다. 팀 프로젝트에서 더 큰 성장을 이룰 수 있을 거라 믿어요. 앞으로의 발전을 기대하며, 함께 더 나아갈 수 있도록 지원하겠습니다.

 

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

개행문자 이슈 💀 

기존 코드에서 end 입력 받는 부분이 계속 무시됨...ㅁㄴㅇㄴㄹ

// 기존에 입력 받던 순서
int a = scanner.nextInt();
int b = scanner.nextInt();
String op = scanner.next();
String end = scanner.nextLine();

 

찾아보니, scanner.nextInt() 사용 후 개행 문자(\n)입력 버퍼에 남아 있어서 무시 된....거라...고 함 🙂

> scanner.nextLine()을 한 번 더 호출하여 버퍼를 비워서 해결.

 

입력버퍼 자세히 알아보자~~~~~~

1. 자바의 Scanner는 입력을 "입력 버퍼(Input Buffer)"에서 가져옴.

2. 사용자가 엔터를 입력하면 엔터값도 입력 버퍼에 "\n"들어감.

3. nextInt()는 입력버퍼에서 숫자만 가져가고, 개행문자는 남겨둠. 그래서 위와 같은 문제가 발생생생 ☠️

 

next(), nextInt(), nextLine() 자세히 알아보기 ^^

메서드 동작 방식
next() 공백 전까지 단어 하나만 읽고, 개행문자(\n)는 남김
nextInt() 공백 전까지 숫자만 읽고 개항문자는 남김
nextLine() 한 줄 전체(개항문자 포함)를 읽고 버퍼를 비움

 

그래서 기존 코드 스캐너 순서에 따르면 nextInt() 또는 next() 사용 시, 입력 버퍼에 개행문자가 남아있던 것...

> String op = scanner.next(); 작동했던 이유 : 공백 전까지 단어 하나만 읽어 가므로 정상 작동 됨.

 

 

전체 코드(이슈 수정)

더보기
package calculator.v1;

import java.util.Scanner;

public class Calculator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        boolean flag = true;
        while (flag){

            System.out.println("계산할 숫자 2개 입력");
            int a = scanner.nextInt();
            int b = scanner.nextInt();

            System.out.println("사칙연산 기호 하나 입력(+, -, *, /)");
            String op = scanner.next();

            switch (op){
                case "+":
                    System.out.println(a + b);
                    break;
                case "-":
                    System.out.println(a - b);
                    break;
                case "*":
                    System.out.println(a * b);
                    break;
                case "/":
                    System.out.println(a / b);
                    break;
                default:
                    System.out.println("wrong input data");
//                    throw new IllegalArgumentException("잘못된 입력입니다.");
            }

            System.out.println("추가 계산 진행시 아무 문자 입력(exit입력 시 종료)");
            scanner.nextLine();
            String end = scanner.nextLine();

            if(end.equals("exit")){
                flag =false;
            }
        }
    }
}

 

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

 

레벨2에서도 개행문자 문제로 또 시간 걸림.

(이전 문제 이어서 하면 더 빨리 풀었을 수도...)

nextline()이 한 줄 전체를 읽고 버퍼를 비우는 게 핵심인데 그걸 까먹고 계속 버퍼 비운다고 무지성으로 scanner.nextline() 남발해서 오류 발생.

 

전체 코드(해결)

더보기

App.java

package calculator.v2;

import java.util.Scanner;

public class App {
    public static void main(String[] args) {
        /* Calculator 인스턴스 생성 */
        Calculator calculator = new Calculator();

        Scanner scanner = new Scanner(System.in);
        boolean flag = true;
        while (flag){

            System.out.println("계산할 숫자 2개 입력");
            int a = scanner.nextInt();
            int b = scanner.nextInt();

            System.out.println("사칙연산 기호 하나 입력(+, -, *, /)");
            String op = scanner.next();

            int result = calculator.calculate(a, b, op);
            System.out.println("결과 : "+result);

            scanner.nextLine();

            System.out.println("계산 기록을 보시겠습니까? (Y/N(enter))");
            String choice = null;
            while (true) {
                choice = scanner.nextLine();
                if(choice.equals("Y")||choice.equals("y")){
                    System.out.println(calculator.getHistory());
                    break;
                }else if(choice.equals("N")||choice.equals("n")||choice.isEmpty()){
                    break;
                }else{
                    System.out.println("please, y or n(enter)");
                }
            }


            System.out.println("계산 기록을 삭제하시겠습니까? (Y/N(enter))");

            while (true){
                choice = scanner.nextLine();
                if(choice.equals("Y")||choice.equals("y")){
                    calculator.removeResult();
                    System.out.println("삭제 후 리스트 내용 : " + calculator.getHistory());
                    break;
                }else if(choice.equals("N")||choice.equals("n")||choice.isEmpty()){
                    System.out.println("현재 리스트 내용 : " + calculator.getHistory());
                    break;
                }else{
                    System.out.println("please, y or n");
                }
            }


            System.out.println("추가 계산 진행하시겠습니까?(exit입력 시 종료)");
            String end = scanner.nextLine();

            if(end.equals("exit")){
                flag =false;
            }
        }
    }
}

 

Calculator.java

package calculator.v2;

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

public class Calculator {
    private List<Integer> history;

    public Calculator() {
        history = new ArrayList<>();
    }

    public List<Integer> getHistory() {
        return history;
    }

    public void setHistory(List<Integer> history) {
        this.history = history;
    }

    public void removeResult(){
        if(history.isEmpty()){
            System.out.println("Nothing to remove");
        }else {
            history.clear();
        }
    }

    public int calculate(int a, int b,String c){
        int result = 0;
        switch (c){
            case "+":
                result = a + b;
                break;
            case "-":
                result = a - b;
                break;
            case "*":
                result = a * b;
                break;
            case "/":
                if(b==0){
                    System.out.println("0으로 나눌 수 없습니다.");
                    return 0;
                }else{
                    result = a/b;
                    break;
                }
            default:
                System.out.println("wrong input data");
                return 0;
        }

        history.add(result);
        return result;
    }
}

 

3. Enum, 제네릭, 람다 & 스트림을 이해한 계산기 만들기

🐸 이슈 1 : 제너릭 타입으로 매개 변수를 받고 사칙연산 진행이 불가한 현상

🐣 해결 방안 1 : 제너릭을 숫자 타입만 받도록 제한 <T extends Number>

🐣 해결 방안 2 : doubleValue() 함수를 사용하여 값을 변환 후 계산 진행 (doubleValue() : 객체값을 double값으로 변환하는 메소드)

 

> 여전히 람다랑 스트림 잘 모르겠다. 😇

 

전체 코드

더보기
package calculator.v3;

import java.util.Scanner;
import java.util.function.Function;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        ArithmeticCalculator calculator = new ArithmeticCalculator();

        boolean flag = true;

        while (flag) {
            System.out.print("Enter number: ");
            double num1 = scanner.nextDouble();
            double num2 = scanner.nextDouble();

            System.out.print("Enter operator: ");
            String op = scanner.next();

            switch (op) {
                case "+":
                    calculator.calculate(num1,num2,OperatorType.PLUS);
                    break;
                case "-":
                    calculator.calculate(num1,num2,OperatorType.MINUS);
                    break;
                case "*":
                    calculator.calculate(num1,num2,OperatorType.MULTIPLY);
                    break;
                case "/":
                    calculator.calculate(num1,num2,OperatorType.DIVIDE);
                    break;
                default:
                    System.out.println("wrong opreator");
                    break;
            }

            System.out.println("Result List: " + calculator.getResults());

            System.out.println("결과값 중 입력값 보다 높은값 조회해보기");
            double num3 = scanner.nextDouble();
            System.out.println(calculator.getBiggerResults(num3));

            System.out.println("계산종료 : exit 입력");
            scanner.nextLine(); // 버퍼 비우기
            String end = scanner.nextLine();
            if(end.equals("exit")){
                flag =false;
            }
        }

        //저장된 결과들 중 입력받은 값보다 큰 결과값 출력하기 : 람다 & 스트림 활용


    }
}
package calculator.v3;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class ArithmeticCalculator<T extends Number> {
    // 피연산자를 여러 타입으로 받을 수 있도록 기능을 확장하기 위해 Double형으로 변환
    List<Double> results = new ArrayList<>();

    public Double calculate(T a, T b, OperatorType op) {
        double result = 0;
        switch (op) {
            case PLUS:
                result = a.doubleValue() + b.doubleValue();
                break;
            case MINUS:
                result = a.doubleValue() - b.doubleValue();
                break;
            case MULTIPLY:
                result = a.doubleValue() * b.doubleValue();
                break;
            case DIVIDE:
                if(b.doubleValue() == 0){
                    System.out.println("0으로 나눌 수 없습니다.");
                    break;
                }else{
                    result = a.doubleValue() / b.doubleValue();
                    break;
                }
        }
        results.add(result);
        return result;
    }

    public List<Double> getResults() {
        return results;
    }

    public List<Double> getBiggerResults(Double a){
        return results.stream().filter(x -> x.doubleValue() > a).collect(Collectors.toList());
    }

}
package calculator.v3;

public enum OperatorType{
    PLUS,
    MINUS,
    MULTIPLY,
    DIVIDE
}

'코딩 > sparta TIL' 카테고리의 다른 글

TIL 9 : BufferedReader, BufferedWriter  (0) 2025.02.27
TIL 8 : Java 문법 종합반 3주차  (0) 2025.02.26
TIL 7 : Java 문법 종합반 2주차  (0) 2025.02.24
TIL 6 : Java 문법 종합반 1주차  (1) 2025.02.24
TIL 5 : KPT  (0) 2025.02.21