SW 개발 일반/레거시코드와 놀기

레거시 코드와 놀기: 호출 추출과 재정의 (Extract and Override Call)

growdai1y 2025. 1. 24. 08:20

소프트웨어 개발자는 종종 기존 코드(레거시 코드)를 다룰 때 어려움을 겪습니다. 이러한 코드는 종종 유지보수하기 어렵고, 테스트 가능성이 낮으며, 새로운 요구 사항에 맞게 변경하기도 까다롭습니다. "호출 추출과 재정의(Extract and Override Call)"는 이러한 문제를 해결하기 위한 의존성 제거 기법입니다. 이 글에서는 호출 추출과 재정의의 개념, 적용 방법, 코드 예제, 그리고 테스트 작성법을 살펴보겠습니다.


호출 추출과 재정의란 무엇인가요?

호출 추출과 재정의 (Extract and Override Call)
호출 추출과 재정의 (Extract and Override Call)

호출 추출과 재정의는 클래스 내에서 특정 호출을 별도의 보호(protected) 메서드로 분리하고, 이 메서드를 서브클래스에서 재정의할 수 있게 만드는 기법입니다. 이 리팩토링 방법은 테스트 가능성을 크게 높이고, 외부 의존성을 관리하며, 코드의 유연성을 강화합니다.

주요 장점

  • 테스트 가능성 향상: 외부 의존성을 Mock 객체나 대체 구현으로 쉽게 교체할 수 있습니다.
  • 유연성 증가: 서브클래스를 통해 특정 메서드의 동작을 커스터마이징할 수 있습니다.
  • 코드 가독성 향상: 반복 호출을 별도의 메서드로 추출하여 코드 구조를 단순화합니다.

적용 예제

기존 코드

다음은 레거시 코드에서 흔히 볼 수 있는 예입니다. 이 코드는 외부 메서드 StyleMaster.formStyles를 호출하며, 이를 직접 테스트하거나 대체하기 어렵습니다.

public class PageLayout {
    private int id = 0;
    private List styles;
    private StyleTemplate template;
    ...
    protected void rebindStyles() {
        styles = StyleMaster.formStyles(template, id);
        ...
    }
    ...
}

변경된 코드

기존 코드를 리팩토링하여 StyleMaster.formStyles 호출을 fromStyles라는 보호 메서드로 추출했습니다. 이로써 서브클래스에서 해당 메서드를 재정의할 수 있게 되었습니다.

public class PageLayout {
    private int id = 0;
    private List styles;
    private StyleTemplate template;
    ...
    protected void rebindStyles() {
        styles = fromStyles(template, id);
        ...
    }

    protected List fromStyles(StyleTemplate template, int id) {
        return StyleMaster.formStyles(template, id);
    }    
}

이렇게 변경함으로써 PageLayout 클래스의 서브클래스는 fromStyles 메서드를 재정의하여 외부 의존성을 대체하거나, 테스트 환경에 맞는 Mock 데이터를 쉽게 제공할 수 있습니다.


테스트 코드 작성하기

테스트를 위해 PageLayout 클래스를 확장한 TestingPageLayout을 작성합니다. 이 서브클래스는 fromStyles 메서드를 재정의하여 외부 의존성을 제거하고 Mock 데이터를 반환합니다.

테스트용 서브클래스

public class TestingPageLayout extends PageLayout {
    @Override
    protected List fromStyles(StyleTemplate template, int id) {
        return new ArrayList(); // Mock 데이터를 반환
    }
    ...
}

JUnit 테스트 코드

JUnit을 사용하여 rebindStyles 메서드의 동작을 검증하는 테스트 코드는 다음과 같습니다:

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.util.*;

class PageLayoutTest {

    @Test
    void rebindStyles_ShouldUseMockedFromStyles() {
        // Arrange
        TestingPageLayout pageLayout = new TestingPageLayout();
        
        // Act
        pageLayout.rebindStyles();

        // Assert
        assertNotNull(pageLayout.getStyles()); // styles가 초기화되었는지 확인
        assertTrue(pageLayout.getStyles().isEmpty()); // Mock 데이터가 비어 있는지 확인
    }
}

이 테스트를 통해 rebindStyles 메서드가 예상대로 동작하며, Mock 데이터를 활용하여 외부 의존성을 제거했음을 확인할 수 있습니다.


왜 호출 추출과 재정의를 선택해야 할까요?

호출 추출과 재정의는 특히 레거시 코드에서 외부 의존성을 관리하고, 테스트 가능성을 높이는 데 탁월한 효과를 발휘합니다. 메서드 호출을 보호 메서드로 추출하면, 테스트 환경에서 쉽게 대체할 수 있고, 새로운 요구사항에 따라 동작을 수정하거나 확장하기도 훨씬 쉬워집니다.

예를 들어, 위의 사례에서는 StyleMaster.formStyles 호출을 직접 대체할 수 없었지만, 이를 fromStyles로 추출한 후에는 테스트 환경에 맞는 데이터를 반환하거나 동작을 커스터마이징하는 것이 간단해졌습니다.


결론

레거시 코드와 씨름할 때, "호출 추출과 재정의"는 더 나은 유지보수성과 확장성을 위한 필수적인 도구가 될 수 있습니다. 이 기법을 통해 코드를 단순화하고, 외부 의존성을 제거하며, 테스트 가능성을 높일 수 있습니다. 또한, 서브클래스를 통해 메서드 동작을 커스터마이징할 수 있어 새로운 요구사항에도 유연하게 대응할 수 있습니다.

여러분의 프로젝트에서도 이 기법을 활용해 코드 품질을 높이고, 개발 생산성을 향상시켜 보세요. 효율적이고 테스트 가능한 코드는 더 나은 소프트웨어를 만드는 첫걸음입니다.

반응형