코드 중복도 (Code Duplication)
코드 중복도는 소프트웨어 내에서 동일하거나 유사한 코드가 반복되는 정도를 나타낸다.
중복된 코드는 유지보수를 어렵게 만들고, 버그 수정 시 여러 곳을 동시에 수정해야 하는 문제를 야기한다.
이는 일반적으로 바람직하지 않은 프로그래밍 관행으로 간주되며, 소프트웨어의 유지보수성과 확장성을 저해할 수 있다.
특징과 특성
- 유지보수 어려움: 중복된 코드는 변경 시 여러 곳을 동시에 수정해야 하므로 유지보수가 어려워진다.
- 버그 발생 가능성 증가: 한 곳의 수정을 다른 곳에 반영하지 않을 경우, 일관성이 깨지고 버그가 발생할 수 있다.
- 코드량 증가: 중복 코드는 전체 코드의 양을 증가시켜 가독성을 떨어뜨리고 디버깅을 어렵게 만든다.
- OCP(Open-Closed Principle) 위배: 중복 코드는 수정에 닫혀 있어야 한다는 SOLID 원칙에 위배된다.
중복 코드 감지 방법
- 수동 코드 리뷰: 개발팀이 협력하여 코드를 검토하고 중복을 식별한다.
- 코드 분석 도구 사용: SonarQube, PMD, ESLint 등의 도구를 활용하여 자동으로 중복 코드를 탐지한다.
- 버전 관리 시스템 활용: Git 등의 버전 관리 시스템을 통해 코드 변경 사항을 비교하여 중복을 식별한다.
- IDE 기능 활용: 대부분의 현대적인 IDE는 코드 중복을 찾는 기능을 내장하고 있다.
코드 중복의 유형
완전 중복(Exact Duplication):
동일한 코드가 그대로 복사되어 사용되는 경우.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
// 완전 중복의 예 class OrderCalculator { public double calculateTotal(List<Item> items) { double total = 0; for (Item item : items) { total += item.getPrice() * (1 - item.getDiscount()); } return total * (1 + TAX_RATE); } } class InvoiceCalculator { public double calculateTotal(List<Item> items) { double total = 0; for (Item item : items) { total += item.getPrice() * (1 - item.getDiscount()); } return total * (1 + TAX_RATE); } } // 개선된 버전 class PriceCalculator { public double calculateTotal(List<Item> items) { double subtotal = calculateSubtotal(items); return applyTax(subtotal); } private double calculateSubtotal(List<Item> items) { return items.stream() .mapToDouble(item -> item.getPrice() * (1 - item.getDiscount())) .sum(); } private double applyTax(double amount) { return amount * (1 + TAX_RATE); } }
구조적 중복(Structural Duplication):
코드의 구조는 같지만 변수명이나 일부 값만 다른 경우.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// 구조적 중복의 예 public void processCustomerOrder(Order order) { if (order.isValid()) { if (order.getTotal() > 1000) { applyDiscount(order, 0.1); } sendConfirmation(order); } } public void processBusinessOrder(Order order) { if (order.isValid()) { if (order.getTotal() > 5000) { applyDiscount(order, 0.15); } sendConfirmation(order); } } // 개선된 버전 public void processOrder(Order order, double threshold, double discountRate) { if (order.isValid()) { if (order.getTotal() > threshold) { applyDiscount(order, discountRate); } sendConfirmation(order); } }
중복 코드 검출 방법
토큰 기반 검출:
코드를 토큰으로 변환하여 유사한 토큰 시퀀스를 찾는다.추상 구문 트리(AST) 기반 검출:
코드의 구조적 유사성을 분석한다.메트릭 기반 검출:
코드의 복잡도, 길이 등의 메트릭을 비교한다.
중복 제거 전략
추출 메서드(Extract Method) 패턴:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// 중복된 코드를 메서드로 추출 public class ReportGenerator { private void addHeader(Document doc) { doc.addTitle(); doc.addDate(); doc.addAuthor(); } public void generateReport(Document doc) { addHeader(doc); // 보고서 특화 로직 } public void generateSummary(Document doc) { addHeader(doc); // 요약 특화 로직 } }
템플릿 메서드 패턴:
전략 패턴:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
interface PricingStrategy { double calculatePrice(Order order); } class RegularPricing implements PricingStrategy { public double calculatePrice(Order order) { // 일반 가격 계산 } } class DiscountPricing implements PricingStrategy { public double calculatePrice(Order order) { // 할인 가격 계산 } }
상속 활용: 여러 클래스에서 공통으로 사용되는 코드를 상위 클래스로 이동시킨다.
매개변수화: 유사하지만 일부 값만 다른 코드를 매개변수를 받는 함수로 통합한다.
도구
- SonarQube: 다양한 프로그래밍 언어를 지원하는 정적 코드 분석 도구로, 코드 중복을 포함한 다양한 코드 품질 이슈를 탐지한다.
- PMD: 자바, JavaScript 등 여러 언어를 지원하며, 코드 중복 감지 규칙을 제공한다.
- IntelliJ IDEA: 내장된 코드 분석 기능을 통해 중복 코드를 식별하고 리팩토링 제안을 제공한다.
주의사항
- 과도한 추상화 주의: 중복 제거를 위해 과도하게 추상화하면 코드의 복잡성이 증가할 수 있다.
- 컨텍스트 고려: 겉보기에 유사한 코드라도 다른 맥락에서 사용될 수 있으므로 신중히 판단해야 한다.
- 성능 고려: 중복 제거로 인한 함수 호출 증가가 성능에 미치는 영향을 고려해야 한다.
권장사항
- DRY 원칙 준수: “Don’t Repeat Yourself” 원칙을 따라 코드의 중복을 최소화한다.
- 모듈화 강조: 코드를 작은 단위의 모듈로 분리하여 재사용성을 높인다.
- 지속적인 리팩토링: 코드 베이스를 주기적으로 검토하고 리팩토링하여 중복을 제거한다.
- 코드 리뷰 문화 정착: 팀 내에서 정기적인 코드 리뷰를 통해 중복 코드를 조기에 발견하고 개선한다.