들어가며
리팩터링을 하는 방법은 다양하지만 대부분 메서드를 구성하는 것에 사용됩니다. 오늘은 메서드를 분리해보면서 간단하면서도 강력한 Composing Methods에 대해 알아보고자 합니다. 코드는 자바로 되어있지만 다른 언어를 사용하시는 분들도 읽는데 무리 없이 최대한 쉽게 코드를 작성해보겠습니다.
Composing Methods
지나치게 긴 메소드는 악의 근원입니다. 논리를 쉽게 파악할 수 없게 만들고 변경하기도 어렵게 만듭니다. Composing methods는 코드 중복을 제거하고 메서드를 간소화하며 향후 개선할 수 있는 기반을 만들어줍니다.
Extract Method
메서드의 라인이 길수록 의도를 파악하기 어려워집니다. 리팩터링의 주된 이유이고 Extract Method(메서드 추출)는 리팩터링 접근 방식의 한 방법입니다. 기능 단위로 분리하고 나면 코드의 오류 가능성을 줄일 수 있습니다. 사용자에게 숫자를 입력받아 숫자만큼 별을 출력해주는 코드를 예시로 들어보겠습니다. 간단한 예제라 코드라인이 짧지만 입력받는 부분과 반복문을 돌며 출력하는 로직이 더 복잡하고 길다고 상상해주시길 바랍니다. (Scanner는 자바에서 입력을 도와주는 객체입니다.)
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int count = scanner.nextInt();
for (int i = 0; i < count; i++) {
System.out.print("*");
}
}
메소드 분리를 통해 우리는 입력을 받는 로직과 출력을 하는 로직을 분리할 수 있습니다. 분리하게 된다면 다음과 같은 코드로 변경할 수 있습니다.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int value = inputCount(scanner); // 입력받고
printStar(value); // 출력한다
}
private static int inputCount(Scanner scanner) {
int value = scanner.nextInt();
return value;
}
private static void printStar(int value) {
for (int i = 0; i < value; i++) {
System.out.print("*");
}
}
우리는 메서드 분리를 통해 InputCount라는 메소드와 printStart라는 이름을 가진 메서드로 분리하게 되었습니다.
Inline Temp
위 예제처럼 읽기만 하고 변하지 않는 지역변수가 있다면 Inline Temp 방법을 통해 가독성을 향상시킬 수 있습니다.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
printStar(inputCount(scanner)); // 스캐너를 통해 수를 입력받고 별을 출력한다
}
private static int inputCount(Scanner scanner) {
return scanner.nextInt();
}
private static void printStar(int value) {
for (int i = 0; i < value; i++) {
System.out.print("*");
}
}
Replace Temp with Query
임시 변수를 메서드로 변경합니다. 근무시간을 가지고 총주급을 계산하는 로직을 살펴보겠습니다. 근무 시간이 15시간이 넘으면 1.2배의 수당을 더 부과합니다.
double calculateWage(int workTime) {
double baseWage = workTime * 9120;
if (workTime > 15) {
return baseWage * 1.2;
}
return baseWage;
}
위 메소드는 totalWage라는 지역변수를 가지고 있습니다. 해당 로직을 메서드로 변경하면 어떻게 리팩터링 할 수 있는지 보겠습니다.
double calculateWage(int workTime) {
if (workTime > 15) {
return multiplyBaseWage(workTime) * 1.2;
}
return multiplyBaseWage(workTime);
}
private int multiplyBaseWage(int workTime) {
return workTime * 9120;
}
지금은 단순한 예제이지만 언제나 더 복잡한 상황이라고 가정해주세요. 메소드를 추출할 때 지역변수가 있다면 메서드로 추출하는 작업이 어려워집니다. 하지만 위와 같은 메서드는 다시 메서드로 추출하는 작업이 편해집니다. 추출한 메서드는 재활용이 가능해지고 적절한 기능을 담당하는 이름을 가지게 됩니다. Extract Method(메서드 추출)을 위한 기본적인 단계를 마련할 수 있습니다.
연습해보기
리팩토링을 하는 방법은 다양합니다. 메서드 추출이 있다면 다시 메서드를 합치는 방법도 있습니다. https://refactoring.guru/refactoring/techniques 해당 사이트에 더 다양한 방법을 제공해줍니다. 개발은 혼자하는 것이 아니라 동료 개발자들과 함께 만들어가기 때문에 읽기 쉽고 유지 보수하기 쉬운 코드를 지향합니다. 메서드를 추출하는 간단하지만 강력한 방법으로 리팩터링을 경험할 수 있습니다. 저는 혼자 코딩을 할 때 스스로 제약을 걸어보기도 합니다. 메서드 라인이 10라인이 넘어가지 않도록 구현해보기. 메서드의 깊이가 1 depth가 넘어가지 않도록 구현하기 (예를 들면 if문 안에 if문이 있다면 2 depth). 처음엔 어려웠지만 꾸준히 연습하다보면 깔끔하게 분리된 코드를 보고 뿌듯한 경험을 했습니다. 리팩터링이 처음이라면 조금씩 제약을 걸어가면서 의식적으로 연습해 보는 방법을 추천드립니다.