본문 바로가기

내일배움캠프

계산기 프로젝트

현재 Java를 본격적으로 공부하고 있는데 지금껏 배운 Java를 활용해서 계산기를 만드는 간단한 프로젝트를 할 예정이다.

일단 계산기 프로젝트는 두 단계에 나눠서 진행할 예정이다.

1단계는

  • 변수 & 타입
  • 연산자
  • 제어문 & 반복문
  • 배열 & 컬렉션

를 활용해서 만들고

2단계는 

  • 클래스 & 메서드 이해하기
  • 생성자 & 접근 제어자 이해하기
  • static & final 이해하기
  • 상속(&포함) & 다형성 이해하기
  • Exception & 예외처리 이해하기

를 활용해서 만들것이다.

오늘은 2단계 분량까지 진도를 나가지 못했기 때문에 1단계만 가지고 작업을 할 것이다.

 

1. 사칙연산 4가지 구현하기

우선 계산을 하기 위한 변수 두 개와 어떤 사칙연산을 할 지 구분하기 위해 사칙연산 기호 하나, 총 3개의 변수가 필요하다.

그리고 값을 입력받기 위한 Scanner 객체도 만들어야 한다.

Scanner sc = new Scanner(System.in);

System.out.print("첫 번째 숫자를 입력하세요: ");
// Scanner로 양의 정수를 입력받고 적합한 타입의 변수에 저장
int var1 = sc.nextInt();
System.out.print("두 번째 숫자를 입력하세요: ");
// Scanner로 양의 정수를 입력받고 적합한 타입의 변수에 저장
int var2 = sc.nextInt();
System.out.print("사칙연산 기호를 입력하세요: ");
// 사칙연산 기호를 적합한 타입의 변수에 저장
char operator = sc.next().charAt(0);
// next()메소드는 문자열로 입력 받아서 문자로 변환하는 charAt()메소드를 추가로 사용했다.

덧셈, 뺄셈, 곱셈, 나눗셈을 operator변수의 값에 따라 나눠서 해야하므로 switch문으로 분기를 만들고 연산이 완료된 결과를 저장해야 하기에 result 변수를 선언했는데 result의 자료형은 나눗셈 결과가 실수로 나오는 것 까지 감안해서 double형으로 선언했다.

double result = 0; // 사칙연산 결과
switch (operator) { // 사칙연산 기호에 맞는 연산 하기
    case '+':
       // 덧셈 연산
        break;
    case '-':
       // 뺄셈 연산
        break;
    case '*':
        // 곱셈 연산
        break;
    case '/':
      	// 나눗셈 연산
        break;
}

그리고 각 case별로 사칙연산을 구현한다.

switch (operator) { // 사칙연산 기호에 맞는 연산 하기
    case '+':
        result = var1 + var2;
        System.out.println("결과: " + result);
        break;
    case '-':
        result = var1 - var2;
        System.out.println("결과: " + result);
        break;
    case '*':
        result = var1 * var2;
        System.out.println("결과: " + result);
        break;
    case '/':
        result = (double) var1 / var2;
        System.out.println("결과: " + result);
        break;
}

그리고 이 계산기는 계산이 완료된 후에 exit을 입력해서 프로그램을 종료할 건지 물어보게 하고 exit을 받기 전까진 무한 반복하게 할 것이다.

따라서 방금까지 작성한 코드에 while(true) 구문으로 감쌀 것이고 exit을 입력받으면 while문을 빠져나오게 할 것이다.

while (true) {
    System.out.print("첫 번째 숫자를 입력하세요: ");
    // Scanner로 양의 정수를 입력받고 적합한 타입의 변수에 저장
    int var1 = sc.nextInt();
    System.out.print("두 번째 숫자를 입력하세요: ");
    // Scanner로 양의 정수를 입력받고 적합한 타입의 변수에 저장
    int var2 = sc.nextInt();
    System.out.print("사칙연산 기호를 입력하세요: ");
    // 사칙연산 기호를 적합한 타입의 변수에 저장
    char operator = sc.next().charAt(0);

    double result = 0; // 사칙연산 결과
    switch (operator) { // 사칙연산 기호에 맞는 연산 하기
        case '+':
            result = var1 + var2;
            System.out.println("결과: " + result);
            break;
        case '-':
            result = var1 - var2;
            System.out.println("결과: " + result);
            break;
        case '*':
            result = var1 * var2;
            System.out.println("결과: " + result);
            break;
        case '/':
            result = (double) var1 / var2;
            System.out.println("결과: " + result);
            break;
    }
    sc.nextLine(); // exit을 입력받는 기능이 스킵되는 것을 방지
    System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
    String exit = sc.nextLine();
    if (exit.equals("exit")) {
        break;
    }
}

여기까지 계산기의 핵심 기능은 구현했다.

 

2. 0으로 나누기 방지와 결과값 10개 저장하는 배열 구현하기

0으로 나누기를 방지하고 싶으면 case '/'구문에 var2가 0인지 검사하는 if문을 넣고 참이면 continue를 실행해서 맨 처음부터 다시 값을 입력받게 하면 된다.

case '/':
    if (var2 == 0) {
        System.out.println("0으로 나눌 수 없습니다."); // 0으로 나눗셈을 시도하면 처음으로 이동함
        continue;
    }
    result = (double) var1 / var2;
    System.out.println("결과: " + result);
    break;

이제 최근 계산한 내용을 배열로 저장해놓는 기능을 구현할 것이다.

배열에는 최대 10개까지 저장해놓게 할 것이고 10개를 넘어서면 가장 오래된 값을 지우고 최근 계산한 값을 저장할 것이다.

우선 배열을 선언하고 몇개가 저장되었는지 카운트하기 위해 정수형 변수 index를 추가로 선언할 것이다. 그리고 이 값들은 while문을 돌 때마다 초기화되면 안되기 때문에 while문 밖에 선언할 것이다.

double[] resultArray = new double[10];
int index = 0;
while(true) {
	//사칙연산 switch문...
    
    if (index >= resultArray.length) {
        for (int i = 0; i < resultArray.length - 1; i++) {
            resultArray[i] = resultArray[i+1];
        }
        index = 9;
    }
    resultArray[index++] = result;
    for (double num : resultArray) {
        System.out.print("결과 배열: " + num + " ");
    }
...

배열이 resultArray[1]...[10]까지 있다고 하면 resultArray[0] = 결과, resultArray[1] = 결과 ... resultArray[9] = 결과 처럼 순차적으로 결과가 저장되게 하기 위해 index번째 배열에 값을 저장하게 하고 저장할 때 마다 index를 1씩 늘어나게 했다.

그리고 배열의 꽉 차면 가장 오래된 결과를 지우기 위해 resultArray[0] = resultArray[1], resultArray[1] = resultArray[2], ..., resultArray[8] = resultArray[9]까지 앞번호 값에 뒷번호 값을 덮어씌우는 식으로 구현했다. 이러면 0번째 값만 날아가고 모든 값들이 한 칸 앞으로 당겨질 것이다. 그래서 for문의 매개변수 i는 8까지 오게끔 하기위해 for문의 조건식은 i < resultArray.length - 1로 했다.

마지막으로 저장된 배열을 출력하는 기능을 구현했다.

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int index = 0;
        double[] resultArray = new double[10];
        while (true) {
            System.out.print("첫 번째 숫자를 입력하세요: ");
            // Scanner로 양의 정수를 입력받고 적합한 타입의 변수에 저장
            int var1 = sc.nextInt();
            System.out.print("두 번째 숫자를 입력하세요: ");
            // Scanner로 양의 정수를 입력받고 적합한 타입의 변수에 저장
            int var2 = sc.nextInt();
            System.out.print("사칙연산 기호를 입력하세요: ");
            // 사칙연산 기호를 적합한 타입의 변수에 저장
            char operator = sc.next().charAt(0);

            double result = 0; // 사칙연산 결과
            switch (operator) { // 사칙연산 기호에 맞는 연산 하기
                case '+':
                    result = var1 + var2;
                    System.out.println("결과: " + result);
                    break;
                case '-':
                    result = var1 - var2;
                    System.out.println("결과: " + result);
                    break;
                case '*':
                    result = var1 * var2;
                    System.out.println("결과: " + result);
                    break;
                case '/':
                    if (var2 == 0) {
                        System.out.println("0으로 나눌 수 없습니다."); // 0으로 나눗셈을 시도하면 처음으로 이동함
                        continue;
                    }
                    result = (double) var1 / var2;
                    System.out.println("결과: " + result);
                    break;
            }
           	if (index >= resultArray.length) {
                for (int i = 0; i < resultArray.length - 1; i++) {
                    resultArray[i] = resultArray[i+1];
                }
                index = 9;
            }
            resultArray[index++] = result;
            for (double num : resultArray) {
                System.out.print("결과 배열: " + num + " ");
            }
            sc.nextLine(); // String형 exit에 입력이 스킵되는 현상 방지
            System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
            String exit = sc.nextLine();
            if (exit.equals("exit")) {
                break;
            }
        }
    }
}

/* 사실 이 기능을 구현 할 때 index를 while문 안에 넣는 실수를 하는 바람에 계속 1번째 요소에만 값이 저장됐다. 
필자는 index가 초기화되는 원인을 찾느라 15~20분 헤멨다. 
그리고 값을 덮어씌우고 index를 9로 초기화 해야 하는데 
그걸 잊어먹어서 프로그램이 계속 뻗었다. 필자는 이 원인을 찾는데에도 15~20분 헤맸다. */

지금까지 구현한 기능들의 코드 전문이다. 

 

3. 배열을 큐로 교체하기, 오래된 값 삭제와 큐 전체 출력 물어보기

Java에는 배열보다 더 고급진 자료구조 시스템을 기본적으로 제공한다. ArrayList, Stack, Queue, LinkedList등이 그 예시인데 이를 통틀어서 Collection이라고 부른다. 이번엔 복잡한 배열 대신 Collection으로 결과값 들을 저장할 것이다.

resultArray는 가장 오래된 값이 앞에서부터 지워지고 새로운 값은 가장 뒤에서부터 추가된다. 이건 Queue의 특징하고 완전히 일치해서 Queue를 가지고 구현해 볼 것이다.

우선 double형 배열 대신 double형 큐를 선언할 것이다. 그리고 큐는 무조건 앞에서만 삭제되고 뒤에서만 추가되기 때문에 몇 번째 저장되는지 카운트할 index는 필요없다.

import java.util.LinkedList;
import java.util.Queue;

Scanner sc = new Scanner(System.in);
Queue<Double> resultQueue = new LinkedList<>();
while(true) {
	//사칙연산 코드...
    
    resultQueue.add(result); // 결과 큐에 값 저장
...

오래된 값을 삭제할 지 물어보는 if문을 쓰고 remove를 입력하면 삭제하는 기능을 구현할 것이다.

sc.nextLine(); // String형 remove에 입력이 스킵되는 현상 방지
System.out.println("가장 먼저 저장된 연산 결과를 삭제하시겠습니까? (remove 입력 시 삭제)");
String remove = sc.nextLine();
if(remove.equals("remove")) {
    resultQueue.poll(); // 큐의 맨 앞에있는 값 삭제
}

이렇게 삽입과 삭제는 메소드 하나면 해결되서 배열보다 더 간단하게 구현할 수 있다.

문제는 큐에 저장된 모든 값을 출력할 때이다.

큐에 1,2,3,...,10까지 저장되어 있다고 치면 1만 출력된다. 2,3을 출력하고 싶으면 1을 삭제하고 다음은 2, 3 순서대로 삭제해야한다.

그래서 필자는 1을 출력하고 10뒤로 새로 1을 삽입하고, 2를 출력하고 다시 새로 1뒤에 2삽입하고 이걸 큐의 사이즈만큼 반복할 것이다. 그러면 결과적으론 처음과 똑같은 큐가 보존되기 때문이다.

System.out.println("저장된 연산결과를 조회하시겠습니까? (inquiry 입력 시 조회)");
String inquiry = sc.nextLine();
if (inquiry.equals("inquiry")) {
    System.out.print("저장된 결과: ");
    for (int i = 0; i < resultQueue.size(); i++) {
        double temp = resultQueue.poll();
        System.out.print(temp + " ");
        resultQueue.add(temp);
    } // 큐의 사이즈만큼 삭제하고 삭제한 값을 다시 입력하는거 반복(결과적으로 같은 내용물의 큐가 됨)
    System.out.println();
}

이러면 1단계 과정은 마무리 된 것 같다.

지금까지 구현한 코드 전문이다.

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //int index = 0;
        //double[] resultArray = new double[10];
        Queue<Double> resultQueue = new LinkedList<>();
        while (true) {
            System.out.print("첫 번째 숫자를 입력하세요: ");
            // Scanner로 양의 정수를 입력받고 적합한 타입의 변수에 저장
            int var1 = sc.nextInt();
            System.out.print("두 번째 숫자를 입력하세요: ");
            // Scanner로 양의 정수를 입력받고 적합한 타입의 변수에 저장
            int var2 = sc.nextInt();
            System.out.print("사칙연산 기호를 입력하세요: ");
            // 사칙연산 기호를 적합한 타입의 변수에 저장
            char operator = sc.next().charAt(0);

            double result = 0; // 사칙연산 결과
            switch (operator) { // 사칙연산 기호에 맞는 연산 하기
                case '+':
                    result = var1 + var2;
                    System.out.println("결과: " + result);
                    break;
                case '-':
                    result = var1 - var2;
                    System.out.println("결과: " + result);
                    break;
                case '*':
                    result = var1 * var2;
                    System.out.println("결과: " + result);
                    break;
                case '/':
                    if (var2 == 0) {
                        System.out.println("0으로 나눌 수 없습니다."); // 0으로 나눗셈을 시도하면 처음으로 이동함
                        continue;
                    }
                    result = (double) var1 / var2;
                    System.out.println("결과: " + result);
                    break;
            }
            resultQueue.add(result); // 결과 큐에 값 저장
//            if (index >= resultArray.length) {
//                for (int i = 0; i < resultArray.length - 1; i++) {
//                    resultArray[i] = resultArray[i+1];
//                }
//                index = 9;
//            }
//            resultArray[index++] = result;
//            for (double num : resultArray) {
//                System.out.print("결과 배열: " + num + " ");
//            }
            sc.nextLine(); // String형 remove에 입력이 스킵되는 현상 방지
            System.out.println("가장 먼저 저장된 연산 결과를 삭제하시겠습니까? (remove 입력 시 삭제)");
            String remove = sc.nextLine();
            if(remove.equals("remove")) {
                resultQueue.poll(); // 큐의 맨 앞에있는 값 삭제
            }
            System.out.println("저장된 연산결과를 조회하시겠습니까? (inquiry 입력 시 조회)");
            String inquiry = sc.nextLine();
            if (inquiry.equals("inquiry")) {
                System.out.print("저장된 결과: ");
                for (int i = 0; i < resultQueue.size(); i++) {
                    double temp = resultQueue.poll(); // 맨 앞의 큐의 값을 리턴하고 삭제한다.
                    System.out.print(temp + " ");
                    resultQueue.add(temp);
                } // 큐의 사이즈만큼 삭제하고 삭제한 값을 다시 입력하는거 반복(결과적으로 같은 내용물의 큐가 됨)
                System.out.println();
            }
            System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
            String exit = sc.nextLine();
            if (exit.equals("exit")) {
                break;
            }
        }
    }
}

만약 ArrayList같은 걸 썼다면 저장된 결과를 조회하는 기능을 더 간단하게 구현했을 지도 모른다. 정말로 그런지는 시간이 남으면 시도해보겠다.

 

1단계 개발과정은 여기까지 마무리하고 진도를 충분히 빼면 그 때 2단계에 도전해보겠다.

 

 

 

 

 

 

 

 

'내일배움캠프' 카테고리의 다른 글

Java문법 종합반 3주차 Part2  (1) 2024.04.26
Java문법 종합반 3주차 Part1  (0) 2024.04.25
Java문법 종합반 2주차  (0) 2024.04.23
Java 문법 종합반 1주차  (0) 2024.04.22
사전캠프 미니미니프로젝트 회고  (0) 2024.04.19