이번 TIL에서는 기존에 작성한 자바 계산기 프로젝트를 리팩토링한 과정을 공유하려 한다. 원래는 하나의 클래스에서 입력 처리와 연산 처리를 모두 담당했지만, 기능을 분리하여 코드를 더 깔끔하고 유지보수하기 쉽게 리팩토링했다.
기존 코드는 입력 처리, 숫자 검증, 연산 처리 등이 모두 App.java
에 모여 있었다. 코드가 길어지다 보니 중복된 코드가 많고, 유지보수가 어려워졌다.
리팩토링을 통해 코드를 기능별로 분리하였다. 구체적으로는 입력 처리와 연산 처리를 분리하고, 검증 로직을 별도의 메서드로 분리하여 재사용성을 높였다.
package Calculator_Lv2;
import java.util.InputMismatchException;
import java.util.Scanner;
public class App {
public static void main(String[] args) {
Calculator calc = new Calculator();
Scanner sc = new Scanner(System.in);
while (true) {
int num1 = 0, num2 = 0;
// 첫 번째 숫자 입력
while (true) {
System.out.print("첫 번째 숫자를 입력하세요(0을 포함한 양의 정수): ");
if (sc.hasNextInt()) {
num1 = sc.nextInt();
if (num1 >= 0) {
break;
} else {
System.out.println("0 또는 양의 정수를 입력해주세요.");
}
} else {
System.out.println("잘못된 입력입니다. 정수를 입력해주세요.");
sc.next(); // 잘못된 입력 처리
}
}
// 두 번째 숫자 입력
while (true) {
System.out.print("두 번째 숫자를 입력하세요(0을 포함한 양의 정수): ");
if (sc.hasNextInt()) {
num2 = sc.nextInt();
if (num2 >= 0) {
break;
} else {
System.out.println("0 또는 양의 정수를 입력해주세요.");
}
} else {
System.out.println("잘못된 입력입니다. 정수를 입력해주세요.");
sc.next(); // 잘못된 입력 처리
}
}
// 연산자 입력 및 검증
char operator;
do {
System.out.print("사칙연산 기호를 입력하세요(+, -, *, /): ");
operator = sc.next().charAt(0);
try {
calc.setResults(num1, num2, operator);
break; // 올바른 연산 기호라면 루프 탈출
} catch (IllegalStateException | ArithmeticException e) {
System.out.println(e.getMessage());
}
} while (true);
try {
// 연산 수행 및 결과 출력
System.out.println("결과: " + calc.getResults());
} catch (ArithmeticException e) {
System.out.println(e.getMessage());
continue;
}
// 종료 여부 확인
System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
String nextLine = sc.next();
if (nextLine.equals("exit")) {
System.out.println("계산기를 종료합니다.");
break;
}
}
sc.close();
}
}
package Calculator_Lv2;
import java.util.Scanner;
public class App {
public static void main(String[] args) {
Calculator calc = new Calculator();
Scanner sc = new Scanner(System.in);
while (true) {
int num1 = getInputNumber(sc, "첫 번째 숫자를 입력하세요(0을 포함한 양의 정수): ");
int num2 = getInputNumber(sc, "두 번째 숫자를 입력하세요(0을 포함한 양의 정수): ");
char operator = getOperator(sc);
try {
calc.setResults(num1, num2, operator);
System.out.println("결과: " + calc.getResults());
} catch (ArithmeticException e) {
System.out.println(e.getMessage());
continue;
}
System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
String nextLine = sc.next();
if (nextLine.equals("exit")) {
System.out.println("계산기를 종료합니다.");
break;
}
}
sc.close();
}
// 숫자 입력 받기
private static int getInputNumber(Scanner sc, String prompt) {
int num;
while (true) {
System.out.print(prompt);
if (sc.hasNextInt()) {
num = sc.nextInt();
if (num >= 0) {
break;
} else {
System.out.println("0 또는 양의 정수를 입력해주세요.");
}
} else {
System.out.println("잘못된 입력입니다. 정수를 입력해주세요.");
sc.next(); // 잘못된 입력 처리
}
}
return num;
}
// 연산자 입력 받기
private static char getOperator(Scanner sc) {
char operator;
while (true) {
System.out.print("사칙연산 기호를 입력하세요(+, -, *, /): ");
operator = sc.next().charAt(0);
if ("+-*/".indexOf(operator) != -1) {
break;
} else {
System.out.println("잘못된 연산 기호입니다. 다시 입력해주세요.");
}
}
return operator;
}
}
리팩토링 후 코드는 기능별로 나누어져 있어 한눈에 각 부분이 무엇을 담당하는지 쉽게 알 수 있다. 각 클래스와 메서드가 하나의 책임만 가지고 있기 때문에, 코드가 간결하고 명확해졌다.
입력과 연산 처리 로직을 분리함으로써 나중에 새로운 기능을 추가하거나 기존 로직을 수정할 때, 한 부분만 수정해도 나머지에 영향을 미치지 않는다.
숫자와 연산자 입력 검증을 별도의 메서드로 분리함으로써, 중복 코드를 제거하고 코드 재사용성이 높아졌다.
이번 리팩토링을 통해 코드를 기능별로 분리하면서 가독성, 유지보수성, 확장성 면에서 많은 개선이 이루어졌다. 이제 코드는 더 깔끔해졌고, 각 클래스와 메서드는 명확한 책임을 가지게 되었다. 이러한 리팩토링을 통해 프로젝트를 유지보수하기도 더 쉬워졌고, 앞으로 새로운 기능을 추가할 때도 훨씬 수월할 것이다.