본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로소프트웨어 공학 › 코드 품질 › 리팩터링

2. Duplicated Code(중복코드)

목차

2. Duplicated Code(중복코드)

  • 중복 코드의 단점
  • 비슷한지, 완전 동일한 코드인지 주의깊게 봐야한다.
  • 코드를 변경할 때, 동일한 모든 곳의 코드를 변경해야 한다.
  • 사용할 수 있는 리팩토링 기술
  • Extract Function(함수 추출하기) :동일한 코드를 여러 메소드에서 사용하는 경우
  • Slide Statements(코드 분리하기) : 코드가 비슷하게 생겼지만 완전히 같지 않은 경우
  • Pull Up Method(메소드 올리기) : 여러 하위 클래스에 동일한 코드가 있을경우

Extract Function(함수 추출하기)

  • “의도”와 “구현”을 분리하기
  • 무슨 일을 하는 코드인지 알아내려고 노력해야 하는 코드라면 해당 코드를 함수로 분리하고 함수 이름으로 무슨 일을 하는지 표현할 수 있다.
  • 한줄 짜리 메소드도 괜찮은가?
  • 거대한 함수 안에 들어있는 주석은 추출한 함수를 찾는데 있어서 좋은 단서가 될 수 있다.

Slide Statements(코드 정리하기)

  • 관련있는 코드끼리 묶여있어야 코드를 더 쉽게 이해할 수 있다.
  • 함수에서 사용할 변수를 상단에 미리 정의하기 보다는, 해당 변수를 사용하는 코드바로 위에 선언
  • 관련있는 코드끼리 묶은 다음, 함수 추출하기(Extract Function)를 사용해서 더 깔끔하게 분리할 수 있다.

Pull Up Method(메소드 올리기)

  • 중복 코드는 당장은 잘 돌아가더라도 미래에 버그를 만들어 낼 빌미를 제공.
    • ex) A에서 코드를 고치고, B에는 반영하지 않은 경우
  • 여러 하위 클래스에 동일한 코드가 있다면, 손쉽게 이 방법을 적용할 수 있다.
  • 비슷하지만 일부 값만 다른 경우 “함수 매개변수화 하기” 리팩토링을 적용한 이후, 이 방법을 사용할 수 있다.
  • 하위 클래스에 있는 코드가 상위 클래스가 아닌 하위 클래스 기능에 의존하고 있다면 “필드 올리기”를 적용한 이후 이 방법을 적용할 수 있다.
  • 두 메소드가 비슷한 절차를 따르고 있다면, “템플릿 메소드 패턴” 적용을 고려한다.

예제: 같은 계산이 두 곳에 있을 때

영수증 화면과 확인 메일에 같은 주문 소계를 표시한다고 가정한다. 둘 다 단가 × 수량을 더해야 하는데, 구현을 복사하면 어느 한쪽만 할인 규칙을 고칠 수 있다.

// 변경 전
type OrderLine = { unitPriceWon: number; quantity: number };

function receiptSubtotal(lines: OrderLine[]): number {
  return lines.reduce((sum, line) => sum + line.unitPriceWon * line.quantity, 0);
}

function emailSubtotal(lines: OrderLine[]): number {
  return lines.reduce((sum, line) => sum + line.unitPriceWon * line.quantity, 0);
}

두 함수의 계산 목적이 완전히 같다면 Extract Function(함수 추출하기)로 한곳에 모은다.

// 변경 후
type OrderLine = { unitPriceWon: number; quantity: number };

function calculateSubtotalWon(lines: OrderLine[]): number {
  return lines.reduce((sum, line) => sum + line.unitPriceWon * line.quantity, 0);
}

const lines = [{ unitPriceWon: 1200, quantity: 2 }];
const receiptValue = calculateSubtotalWon(lines);
const emailValue = calculateSubtotalWon(lines);

두 출력의 값은 모두 2400으로 유지된다. 이때 중요한 질문은 코드 모양이 아니라 정책이 같은가다. 영수증은 세금을 포함하고 메일은 포함하지 않아야 한다면, 비슷한 reduce가 있어도 계산 전체를 억지로 하나로 합치면 안 된다. 공통인 소계 계산만 추출하고 나머지 정책은 각자의 함수에 둔다.

순서를 정리한 다음 추출하기

Slide Statements(문장 옮기기)는 관련된 문장을 가까이 두는 기법이다. 두 함수에 흩어진 가격 읽기 → 수량 곱하기 → 합산 단계를 같은 순서로 정리하면 실제 중복 범위가 눈에 보인다. 함수를 추출하기 전에 문장을 이동할 때에는 실행 순서가 결과를 바꾸지 않는지 확인한다. 예를 들어 로그 기록이나 데이터 변경이 사이에 있다면 위치를 옮기는 것만으로 동작이 달라질 수 있다.

Pull Up Method(메서드 올리기)는 여러 하위 클래스가 같은 메서드를 갖고 있고 상위 클래스에서 표현해도 의미가 같을 때 쓴다. 단순히 메서드 이름이 같다는 이유만으로 올리지 않는다. 하위 클래스별로 다른 필드에 의존한다면 매개변수화하거나 공통 책임을 다시 확인해야 한다.

리팩터링 전에는 빈 목록, 항목 한 개, 여러 항목에서 소계가 같은지 테스트해 두면 좋다. 그런 다음 추출·이동·호출부 변경을 작은 단계로 진행하고 매 단계 결과를 확인한다. 중복을 줄였는데 예전과 다른 금액이 나온다면 구조를 바꾸는 과정에서 정책을 함께 바꾼 것이다.

같은 카테고리의 글