소프트웨어 개발자는 종종 기존 코드(레거시 코드)를 다룰 때 어려움을 겪습니다. 이러한 코드는 종종 유지보수하기 어렵고, 테스트 가능성이 낮으며, 새로운 요구 사항에 맞게 변경하기도 까다롭습니다. "호출 추출과 재정의(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로 추출한 후에는 테스트 환경에 맞는 데이터를 반환하거나 동작을 커스터마이징하는 것이 간단해졌습니다.
결론
레거시 코드와 씨름할 때, "호출 추출과 재정의"는 더 나은 유지보수성과 확장성을 위한 필수적인 도구가 될 수 있습니다. 이 기법을 통해 코드를 단순화하고, 외부 의존성을 제거하며, 테스트 가능성을 높일 수 있습니다. 또한, 서브클래스를 통해 메서드 동작을 커스터마이징할 수 있어 새로운 요구사항에도 유연하게 대응할 수 있습니다.
여러분의 프로젝트에서도 이 기법을 활용해 코드 품질을 높이고, 개발 생산성을 향상시켜 보세요. 효율적이고 테스트 가능한 코드는 더 나은 소프트웨어를 만드는 첫걸음입니다.
'SW 개발 일반 > 레거시코드와 놀기' 카테고리의 다른 글
레거시 코드와 놀기: Getter 메소드 추출과 재정의 (Extract and Override Getter) (0) | 2025.01.27 |
---|---|
레거시 코드와 놀기 백서: Working Effectively with Legacy Code (0) | 2025.01.25 |
레거시 코드와 놀기: 정적 메소드 드러내기 (Expose Static Method) (0) | 2025.01.22 |
느리지만 그나마 쉬운 테스트 개발하기 (0) | 2025.01.20 |
레거시 코드와 놀기: 전역 참조 캡슐화 (Encapsulate Global Reference) (0) | 2025.01.20 |