아래는 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. 설계 및 구현
- Console 버전:
CalculatorConsole
클래스는 콘솔 환경에서 사칙연산을 처리하는 로직으로 구성되어 있습니다. 주로 숫자 입력과 연산 기호를 받아 연산을 수행한 후 결과를 저장하고 출력하는 방식으로 동작합니다.- 주요 메서드:
setCalc(char operation, int num1, int num2)
: 연산 기호와 두 숫자를 입력받아 계산하는 메서드입니다. 내부적으로calculate()
메서드를 호출해 연산을 수행합니다.calculate()
: 입력된 연산 기호에 따라 덧셈, 뺄셈, 곱셈, 나눗셈을 수행합니다. 나눗셈에서0
으로 나눌 때 오류 메시지를 출력하고, 잘못된 연산 기호를 입력하면 오류를 처리하는 기능도 포함되어 있습니다.delete()
: 저장된 연산 결과에서 특정 값을 삭제하는 기능을 제공합니다. 중복된 값을 모두 삭제할지, 단일 값을 삭제할지를 선택할 수 있습니다.record()
: 현재까지 저장된 연산 결과를 반환하는 메서드입니다.
- 구현 로직:
- 사용자 입력을 받아 사칙연산을 수행하고, 결과를 리스트에 저장하여 기록을 유지하는 기능이 있습니다.
- 잘못된 입력 처리: 잘못된 연산 기호 입력 시 사용자에게 알리고 다시 입력받습니다.
- 삭제 기능: 저장된 결과 중 특정 값을 삭제할 수 있으며, 중복된 값에 대해서도 처리할 수 있습니다.
- GUI 버전:
CalculatorGUI
클래스는 GUI를 통해 사칙연산을 처리하며, 사용자 친화적인 인터페이스로 동작합니다.JFrame
을 기반으로 하고, 버튼을 통한 사용자 입력을 처리하는 방식입니다.- 주요 구성 요소:
JFrame
: 계산기 창을 담당하는 메인 프레임입니다.JTextField
: 사용자 입력과 연산 결과를 출력하는 필드입니다.JButton[]
: 숫자와 연산 기호, 그리고 추가적인 기능을 담당하는 버튼들입니다. 각 버튼은ActionListener
를 통해 클릭 이벤트를 처리합니다.CalculatorControllerApp
: 연산 로직을 담당하는 컨트롤러 클래스입니다.
- 주요 메서드:
actionPerformed(ActionEvent e)
: 버튼 클릭 이벤트를 처리하는 메서드로, 숫자 입력, 연산자 처리, 결과 계산, 이전 결과 불러오기 등의 기능을 처리합니다.handleNumberInput(String command)
: 숫자나 소수점 입력 시 처리하는 메서드로, 연산자 클릭 후 숫자를 다시 입력할 때 기존 입력을 초기화합니다.handleOperator(String command)
: 연산자 버튼이 클릭되었을 때의 동작을 처리하며, 연속적인 연산자 입력에도 대응할 수 있습니다.handleEqualOperation()
:=
버튼을 클릭하면 현재 입력된 값들을 바탕으로 연산을 수행하고, 결과를 출력합니다.
- 구현 로직:
- 사용자로부터 숫자와 연산자를 입력받아, 실시간으로 연산 결과를 화면에 출력합니다.
- 큐를 활용한 결과 관리: 연산 결과는
Queue
자료구조에 저장되며, 최대 10개까지만 관리됩니다. 이전 결과를 불러오거나 결과 큐가 초과될 경우, 가장 오래된 결과가 자동으로 삭제됩니다.
4. 결과 및 개선 사항
- Console 버전:
- 콘솔 버전의 경우 사용자가 직접 숫자와 연산자를 입력해야 하므로 GUI에 비해 인터랙티브한 경험은 부족하지만, 기본적인 사칙연산 및 결과 기록 관리 기능을 완벽히 수행합니다.
- 개선사항:
- 현재 프로그램은 두 숫자를 입력받아 연산을 수행하는 단순한 형태로 동작합니다. 이를 개선해 여러 개의 연산을 한 번에 입력받을 수 있도록 기능을 확장할 수 있습니다.
- 오류 처리 부분에서 좀 더 구체적인 에러 메시지와 사용자 피드백을 제공할 수 있습니다.
- GUI 버전:
- GUI 버전은 직관적이며, 사용자 경험을 고려한 디자인과 기능을 제공합니다. 특히 큐를 활용한 결과 기록 관리와
Backspace
,Clear
,Recall
등의 추가 기능을 통해 실용성을 높였습니다. - 개선사항:
- 현재 구현에서는 숫자나 연산자의 잘못된 입력 시 예외 처리를 통한 오류 메시지를 텍스트 필드에 직접 출력하고 있지만, 좀 더 세련된 방식으로 팝업 창을 통해 사용자에게 오류를 알릴 수 있습니다.
Recall
기능을 확장해, 큐에 저장된 결과 중 여러 개의 결과를 확인하고 선택할 수 있는 기능을 추가할 수 있습니다.
- GUI 버전은 직관적이며, 사용자 경험을 고려한 디자인과 기능을 제공합니다. 특히 큐를 활용한 결과 기록 관리와
5. 결론
이번 프로젝트를 통해 CalculatorConsole
과 CalculatorGUI
두 가지 방식으로 계산기를 구현했습니다. 콘솔 버전은 기본적인 사칙연산과 결과 기록을 관리하는 기능을 수행하고, GUI 버전은 사용자 친화적인 인터페이스를 제공하며 추가 기능으로 사용자 경험을 제공했습니다.
'TIL > 스파르타 TIL' 카테고리의 다른 글
[내일배움캠프] 본캠프 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 |