[TIL] 스파르타코딩 2차 팀편성, 7일차

2024. 9. 11. 07:55·TIL/TIL(Today I Learned)

아래는 TIL(Today I Learned) 형식으로 리팩토링한 내용입니다.


📝 [TIL] Calculator Console & GUI Application 개발

📅 Date: 2024-09-13


🧐 오늘 배운 것

오늘은 콘솔과 GUI를 이용해 계산기 애플리케이션을 구현하는 과정에서 사용한 다양한 기술과 패턴을 배웠습니다. 이번 TIL에서는 각각의 버전(콘솔, GUI)을 분석하고 리팩토링한 내용을 다룹니다.


👨‍💻 주요 내용

1. Console 버전

  • 목표: 기본적인 사칙연산과 결과 저장 및 삭제 기능을 제공하는 콘솔 애플리케이션을 구현하는 것이 목표였습니다.
  • 핵심 기능
    • +, -, *, / 사칙연산 처리
    • 연산 결과 저장 및 삭제 기능 제공
    • 예외 처리: 잘못된 연산 기호, 0으로 나누기 처리
    • 결과 기록 관리: 연산 결과는 ArrayList<Double>에 저장

🛠️ 리팩토링한 코드 (Console 버전)

package testSparta01;

import java.util.*;

public class CalculatorConsole {
    private final ArrayList<Double> result = new ArrayList<>();
    private char operation;
    private int num1;
    private int num2;
    private int error;

    // 결과 반환
    public double getResult() {
        return result.isEmpty() || error == 1 ? 0 : result.get(result.size() - 1);
    }

    // 계산 요청 메서드
    public void setCalc(char operation, int num1, int num2) {
        this.operation = operation;
        this.num1 = num1;
        this.num2 = num2;
        calculate();
    }

    // 결과 삭제
    public void delete(int set, double num) {
        if (!result.contains(num)) {
            System.out.println("해당 값은 존재하지 않습니다.");
            return;
        }
        boolean success = (set == 1) ? result.removeAll(Collections.singleton(num)) : result.remove(Double.valueOf(num));
        if (success) System.out.println("정상적으로 삭제되었습니다. 현재 저장된 값: " + record());
    }

    // 결과 기록 반환
    public ArrayList<Double> record() {
        return result;
    }

    // 실제 연산 수행
    private void calculate() {
        switch (operation) {
            case '+': result.add((double) num1 + num2); break;
            case '-': result.add((double) num1 - num2); break;
            case '*': result.add((double) num1 * num2); break;
            case '/':
                if (num2 == 0) {
                    System.out.println("0으로 나눌 수 없습니다.");
                    error = 1;
                } else {
                    result.add((double) num1 / num2);
                }
                break;
            default:
                System.out.println("잘못된 연산 기호입니다.");
                error = 1;
        }
    }

    // 메인 실행 로직
    public static void main(String[] args) {
        CalculatorConsole calc = new CalculatorConsole();
        Scanner sc = new Scanner(System.in);
        boolean checking = true;

        while (checking) {
            int num1 = getInputValue(sc, "첫 번째 값을 입력해주세요: ");
            if (num1 < 0) continue;

            System.out.print("연산기호를 입력해주세요 (+, -, *, /): ");
            String operationStr = sc.nextLine().trim();
            if (operationStr.length() != 1 || !"+-*/".contains(operationStr)) {
                System.out.println("잘못된 연산 기호입니다. 다시 입력해주세요.");
                continue;
            }
            char operation = operationStr.charAt(0);
            int num2 = getInputValue(sc, "두 번째 값을 입력해주세요: ");
            if (num2 < 0) continue;

            calc.setCalc(operation, num1, num2);
            System.out.println("결과: " + calc.getResult());

            System.out.print("계속 진행(1) / 종료(2) / 삭제(3) 번호 입력: ");
            String choice = sc.nextLine().trim();
            switch (choice) {
                case "1": break;
                case "2": checking = false; break;
                case "3":
                    System.out.println("현재 저장된 값: " + calc.record());
                    System.out.print("삭제할 값을 입력하세요: ");
                    double delnum = Double.parseDouble(sc.nextLine().trim());
                    System.out.print("중복값 포함(1) / 미포함(2) 번호 입력: ");
                    int setOption = Integer.parseInt(sc.nextLine().trim());
                    calc.delete(setOption, delnum);
                    break;
                default:
                    System.out.println("잘못된 입력입니다. 처음으로 돌아갑니다.");
            }
        }
        sc.close();
    }

    // 입력값 유효성 검사
    private static int getInputValue(Scanner sc, String prompt) {
        System.out.print(prompt);
        try {
            int value = Integer.parseInt(sc.nextLine().trim());
            if (value < 0) {
                System.out.println("0 미만의 수는 입력할 수 없습니다.");
                return -1;
            }
            return value;
        } catch (NumberFormatException e) {
            System.out.println("유효한 숫자를 입력해주세요.");
            return -1;
        }
    }
}

2. GUI 버전

  • 목표: GUI 기반의 계산기를 구현하여 사용자에게 더욱 직관적인 인터페이스 제공.
  • 핵심 기능
    • 사칙연산 (+, -, *, /) 수행
    • 결과 저장 및 기록 관리 기능 (최대 10개 결과 저장)
    • 예외 처리: 0으로 나누기, 잘못된 입력 처리
    • 연산 기록 관리: 계산 결과 큐로 저장

🛠️ 리팩토링한 코드 (GUI 버전)

package testSparta02;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class CalculatorGUI implements ActionListener {

    // JFrame과 관련된 GUI 구성 요소 선언
    private JFrame frame;
    private JTextField textField;
    private JPanel panel;
    private String[] buttons = {
            "7", "8", "9", "/",
            "4", "5", "6", "*",
            "1", "2", "3", "-",
            "0", ".", "=", "+",
            " ", "R", "←", "C"
    };
    private JButton[] button = new JButton[buttons.length];
    private String operator = "";
    private double num1 = 0, num2 = 0;
    private boolean isOperatorClicked = false;
    private CalculatorControllerApp calculator = new CalculatorControllerApp(); // 연산 처리용 컨트롤러

    // GUI 창 설정 및 컴포넌트 초기화
    public CalculatorGUI() {
        setupFrame();
        setupTextField();
        setupPanel();
        frame.setVisible(true);
    }

    /**
     * JFrame 초기 설정
     */
    private void setupFrame() {
        frame = new JFrame("계산기");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 400);
    }

    /**
     * JTextField 초기 설정 (결과 출력)
     */
    private void setupTextField() {
        textField = new JTextField();
        textField.setPreferredSize(new Dimension(230, 50));
        textField.setEditable(false);
        textField.setFont(new Font("Arial", Font.PLAIN, 24));
        textField.setHorizontalAlignment(JTextField.RIGHT);
        frame.add(textField, BorderLayout.NORTH);
    }

    /**
     * JPanel에 버튼들을 배치
     */
    private void setupPanel() {
        panel = new JPanel(new GridLayout(5, 4, 10, 10));

        for (int i = 0; i < buttons.length; i++) {
            if (!buttons[i].trim().isEmpty()) {
                button[i] = new JButton(buttons[i]);
                button[i].setFont(new Font("Arial", Font.BOLD, 20));
                button[i].addActionListener(this);
                panel.add(button[i]);
            } else {
                panel.add(new JLabel());
            }
        }
        frame.add(panel, BorderLayout.CENTER);
    }

    /**
     * 버튼 클릭에 따른 동작 처리
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        String command = e.getActionCommand();

        if (isNumberOrDot(command)) {
            handleNumberInput(command);
        } else if (command.equals("C")) {
            clearAll();
        } else if (command.equals("←")) {
            handleBackspace();
        } else if (command.equals("=")) {
            handleEqualOperation();
        } else if (command.equals("R")) {
            handleRecallResult();
        } else {
            handleOperator(command);
        }
    }

    /**
     * 숫자나 소수점 버튼 클릭 여부 확인
     */
    private boolean isNumberOrDot(String command) {
        return command.charAt(0) >= '0' && command.charAt(0) <= '9' || command.equals(".");
    }

    /**
     * 숫자 입력 처리
     */
    private void handleNumberInput(String command) {
        if (isOperatorClicked) {
            textField.setText("");
            isOperatorClicked = false;
        }
        textField.setText(textField.getText() + command);
    }

    /**
     *  이전 결과 표출 버튼
     */

    private void handleRecallResult() {
        double lastResult = calculator.getLastResult();
        if (!Double.isNaN(lastResult)) {
            textField.setText(String.valueOf(lastResult));
        } else {
            textField.setText("No result");
        }
    }

    /**
     * 모든 입력 초기화
     */
    private void clearAll() {
        textField.setText("");
        num1 = 0;
        num2 = 0;
        operator = "";
    }

    /**
     * 마지막 입력 삭제 (Backspace)
     */
    private void handleBackspace() {
        String currentText = textField.getText();
        if (!currentText.isEmpty()) {
            textField.setText(currentText.substring(0, currentText.length() - 1));
        }
    }

    /**
     * 연산자 클릭 시 처리
     */
    /**
     * 연산자 클릭 시 처리 (연산자도 텍스트 필드에 표시)
     */
    private void handleOperator(String command) {
        try {
            if (textField.getText().isEmpty()) {
                textField.setText("NumberEmpty Error");
                return;
            }

            if (!operator.isEmpty() && !isOperatorClicked) {
                // 연산자 연속 입력 시 이전 연산 수행
                num2 = Double.parseDouble(textField.getText().replaceAll("[^0-9.]", "")); // 숫자만 추출
                num1 = calculator.calculate(num1, num2, operator);
                textField.setText(String.valueOf(num1));
            } else if (isOperatorClicked) {
                // 연산자 연속 클릭 시
                operator = command;
                return;
            } else {
                num1 = Double.parseDouble(textField.getText());
            }

            operator = command;
            textField.setText(textField.getText() + " " + operator + " "); // 연산자 표시
            isOperatorClicked = true;
        } catch (NumberFormatException ex) {
            textField.setText("NumberFormatException");
        }
    }


    /**
     * "=" 버튼 클릭 시 연산 수행
     */
    private void handleEqualOperation() {
        if (operator.isEmpty() || textField.getText().isEmpty()) {
            textField.setText("Error");
            return;
        }

        try {
            num2 = Double.parseDouble(textField.getText());

            double result = calculator.calculate(num1, num2, operator);

            if (!Double.isNaN(result)) {
                textField.setText(String.valueOf(result));
                operator = "";
                num2 = 0;
                isOperatorClicked = false;
            } else {
                textField.setText("Error");
            }
        } catch (NumberFormatException ex) {
            textField.setText("NumberFormatException");
        }
    }

    public static void main(String[] args) {
        new CalculatorGUI();
    }
}
package testSparta02;

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

public class CalculatorControllerApp {
    // 계산 결과를 저장하는 큐 (FIFO 방식 사용)
    private Queue<Double> results = new LinkedList<>();
    private static final int MAX_RESULTS = 10; // 저장할 최대 결과 수

    public double calculate(double num1, double num2, String operator) {
        double result = 0;
        switch (operator) {
            case "+":
                result = num1 + num2;
                break;
            case "-":
                result = num1 - num2;
                break;
            case "*":
                result = num1 * num2;
                break;
            case "/":
                if (num2 != 0) {
                    result = num1 / num2;
                } else {
                    System.err.println("오류: 0으로 나눌 수 없습니다.");
                    return Double.NaN;
                }
                break;
            default:
                System.err.println("오류: 잘못된 연산자입니다.");
                return Double.NaN;
        }

        addResult(result);
        return result;
    }

    private void addResult(double result) {
        if (results.size() >= MAX_RESULTS) {
            results.poll();
        }
        results.add(result);
    }

    public double getLastResult() {
        return results.isEmpty() ? Double.NaN : results.peek();
    }
}

계속해서 작성하겠습니다.


3. 설계 및 구현

  1. Console 버전:
    • CalculatorConsole 클래스는 콘솔 환경에서 사칙연산을 처리하는 로직으로 구성되어 있습니다. 주로 숫자 입력과 연산 기호를 받아 연산을 수행한 후 결과를 저장하고 출력하는 방식으로 동작합니다.
    • 주요 메서드:
      • setCalc(char operation, int num1, int num2): 연산 기호와 두 숫자를 입력받아 계산하는 메서드입니다. 내부적으로 calculate() 메서드를 호출해 연산을 수행합니다.
      • calculate(): 입력된 연산 기호에 따라 덧셈, 뺄셈, 곱셈, 나눗셈을 수행합니다. 나눗셈에서 0으로 나눌 때 오류 메시지를 출력하고, 잘못된 연산 기호를 입력하면 오류를 처리하는 기능도 포함되어 있습니다.
      • delete(): 저장된 연산 결과에서 특정 값을 삭제하는 기능을 제공합니다. 중복된 값을 모두 삭제할지, 단일 값을 삭제할지를 선택할 수 있습니다.
      • record(): 현재까지 저장된 연산 결과를 반환하는 메서드입니다.
    • 구현 로직:
      • 사용자 입력을 받아 사칙연산을 수행하고, 결과를 리스트에 저장하여 기록을 유지하는 기능이 있습니다.
      • 잘못된 입력 처리: 잘못된 연산 기호 입력 시 사용자에게 알리고 다시 입력받습니다.
      • 삭제 기능: 저장된 결과 중 특정 값을 삭제할 수 있으며, 중복된 값에 대해서도 처리할 수 있습니다.
  2. GUI 버전:
    • CalculatorGUI 클래스는 GUI를 통해 사칙연산을 처리하며, 사용자 친화적인 인터페이스로 동작합니다. JFrame을 기반으로 하고, 버튼을 통한 사용자 입력을 처리하는 방식입니다.
    • 주요 구성 요소:
      • JFrame: 계산기 창을 담당하는 메인 프레임입니다.
      • JTextField: 사용자 입력과 연산 결과를 출력하는 필드입니다.
      • JButton[]: 숫자와 연산 기호, 그리고 추가적인 기능을 담당하는 버튼들입니다. 각 버튼은 ActionListener를 통해 클릭 이벤트를 처리합니다.
      • CalculatorControllerApp: 연산 로직을 담당하는 컨트롤러 클래스입니다.
    • 주요 메서드:
      • actionPerformed(ActionEvent e): 버튼 클릭 이벤트를 처리하는 메서드로, 숫자 입력, 연산자 처리, 결과 계산, 이전 결과 불러오기 등의 기능을 처리합니다.
      • handleNumberInput(String command): 숫자나 소수점 입력 시 처리하는 메서드로, 연산자 클릭 후 숫자를 다시 입력할 때 기존 입력을 초기화합니다.
      • handleOperator(String command): 연산자 버튼이 클릭되었을 때의 동작을 처리하며, 연속적인 연산자 입력에도 대응할 수 있습니다.
      • handleEqualOperation(): = 버튼을 클릭하면 현재 입력된 값들을 바탕으로 연산을 수행하고, 결과를 출력합니다.
    • 구현 로직:
      • 사용자로부터 숫자와 연산자를 입력받아, 실시간으로 연산 결과를 화면에 출력합니다.
      • 큐를 활용한 결과 관리: 연산 결과는 Queue 자료구조에 저장되며, 최대 10개까지만 관리됩니다. 이전 결과를 불러오거나 결과 큐가 초과될 경우, 가장 오래된 결과가 자동으로 삭제됩니다.

4. 결과 및 개선 사항

  1. Console 버전:
    • 콘솔 버전의 경우 사용자가 직접 숫자와 연산자를 입력해야 하므로 GUI에 비해 인터랙티브한 경험은 부족하지만, 기본적인 사칙연산 및 결과 기록 관리 기능을 완벽히 수행합니다.
    • 개선사항:
      • 현재 프로그램은 두 숫자를 입력받아 연산을 수행하는 단순한 형태로 동작합니다. 이를 개선해 여러 개의 연산을 한 번에 입력받을 수 있도록 기능을 확장할 수 있습니다.
      • 오류 처리 부분에서 좀 더 구체적인 에러 메시지와 사용자 피드백을 제공할 수 있습니다.
  2. GUI 버전:
    • GUI 버전은 직관적이며, 사용자 경험을 고려한 디자인과 기능을 제공합니다. 특히 큐를 활용한 결과 기록 관리와 Backspace, Clear, Recall 등의 추가 기능을 통해 실용성을 높였습니다.
    • 개선사항:
      • 현재 구현에서는 숫자나 연산자의 잘못된 입력 시 예외 처리를 통한 오류 메시지를 텍스트 필드에 직접 출력하고 있지만, 좀 더 세련된 방식으로 팝업 창을 통해 사용자에게 오류를 알릴 수 있습니다.
      • Recall 기능을 확장해, 큐에 저장된 결과 중 여러 개의 결과를 확인하고 선택할 수 있는 기능을 추가할 수 있습니다.

5. 결론

이번 프로젝트를 통해 CalculatorConsole과 CalculatorGUI 두 가지 방식으로 계산기를 구현했습니다. 콘솔 버전은 기본적인 사칙연산과 결과 기록을 관리하는 기능을 수행하고, GUI 버전은 사용자 친화적인 인터페이스를 제공하며 추가 기능으로 사용자 경험을 제공했습니다.

'TIL > TIL(Today I Learned)' 카테고리의 다른 글

[내일배움캠프] 본캠프 TIL Spring 입문(2)  (0) 2024.09.28
[내일배움캠프] 본캠프 TIL Spring 입문(1)  (0) 2024.09.27
[TIL] 스파르타코딩 2차 팀편성, 6일차  (0) 2024.09.09
[TIL] 스파르타코딩 2차 팀편성, 주말공부(1)  (0) 2024.09.08
[TIL] 스파르타코딩 2차 팀편성, 5일차  (0) 2024.09.06
'TIL/TIL(Today I Learned)' 카테고리의 다른 글
  • [내일배움캠프] 본캠프 TIL Spring 입문(2)
  • [내일배움캠프] 본캠프 TIL Spring 입문(1)
  • [TIL] 스파르타코딩 2차 팀편성, 6일차
  • [TIL] 스파르타코딩 2차 팀편성, 주말공부(1)
통촏하여주시옵소서
통촏하여주시옵소서
솔방울님의 블로그 입니다.
  • 통촏하여주시옵소서
    솔방울의 IT
    GuestBook Guest
    GitHub GitHub
    Notion Notion
    글쓰기 관리
    • 분류 전체보기 (120)
      • Java (16)
      • Spring (13)
      • 자료구조 (3)
      • 보안 (6)
        • 네트워크보안 (2)
        • 백신 프로그램 (4)
      • 네트워크 (10)
        • 네트워크 관련지식 (7)
        • TCP IP (3)
      • Windows (9)
      • TIL (54)
        • TIL(Today I Learned) (19)
        • 코딩테스트 연습문제 (29)
        • 내일배움캠프 숙제 (6)
      • 프로젝트 (4)
        • 백신데스크톱 (1)
        • 스파르타코딩 (3)
      • 자격증 (5)
        • 사무자동화산업기사 (0)
        • 정보처리산업기사 (5)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 공지사항

    • 방명록을 적어주시면
  • 전체
    오늘
    어제
  • 06-22 04:13
  • 태그

    스파르타코딩
    검은조직
    IT
    백신프로그램
    비밀집단
    정보보안
    epp
  • 인기 글

  • whlsls3377.dev@gmail.com
통촏하여주시옵소서
[TIL] 스파르타코딩 2차 팀편성, 7일차
상단으로

티스토리툴바