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

레거시 코드와 놀기 백서: Working Effectively with Legacy Code

growdai1y 2025. 1. 25. 11:42

Working Effectively with Legacy Code는 레거시 코드와 마주했을 때 작업을 안전하고 점진적으로 진행할 수 있는 구체적인 전략과 마음가짐을 제시하는 책입니다. 레거시 코드 속에서 오랜 시간 고통받아 왔던 제게, 이 책은 큰 깨달음을 주었습니다. 테스트는 이제 선택이 아니라 필수라는 마음가짐을 갖게 되었습니다.

 

비록 주변에서는 여전히 테스트에 대한 관심이 크지 않아, 개발자 수준의 테스트가 필수적인 활동으로 여겨지지 않더라도, 저는 꾸준히 테스트를 놓지 않고 이어나갈 결심을 하게 되었습니다. 퇴사하기 전까지, 적어도 제가 있는 동안만이라도 계속 노력할 생각입니다.

 

어떤 책에서는 더 나은 환경의 회사로 이직하는 것도 방법이라고 조언하지만, 오랜 시간을 버텨온 이곳을 떠나는 게 참 어렵습니다. 그런 점에서 이 책은 제게 단순한 기술적 지침을 넘어, 현재의 환경에서도 희망을 잃지 않게 하는 의지가 되어 주었습니다.

레거시 코드와 놀기 백서
레거시 코드와 놀기 백서


1. 레거시 코드란 무엇인가?

마이클 페더스는 레거시 코드를 단순히 오래된 코드로 정의하지 않습니다. 책에서 가장 강조하는 레거시 코드의 정의는 테스트가 없는 코드로, 테스트가 없는 코드는 안전하게 변경할 수 없다고 합니다. 따라서 레거시 코드와의 작업은 곧 테스트를 추가하여 코드를 안전하게 변경 가능하게 만드는 과정을 의미합니다.


2. 레거시 코드와 작업하기 위한 철학: "코드와의 대화"

테스트 추가의 중요성

책의 주요 메시지는 레거시 코드에 테스트를 추가하고 리팩토링을 수행하며, 코드를 점진적으로 이해하고 제어하는 과정에 대한 철학을 강조합니다. 테스트가 없는 코드에는 반드시 테스트를 추가해야 하며, 이는 안전한 작업을 위한 기본 전제 조건입니다.

리팩토링과 테스트의 상호작용

리팩토링은 반드시 테스트가 뒷받침되어야만 안전하게 수행할 수 있으며, 테스트는 코드의 의도를 명확히 하고 리팩토링 후에도 기존 기능이 유지됨을 보장합니다.

의존성 문제 해결의 중요성

레거시 코드는 의존성 문제로 인해 테스트가 어려운 경우가 많습니다. 외부 시스템, 복잡한 구조, 정적 참조 등 다양한 의존성을 제어할 방법을 찾아야 하며, 이 과정은 모든 문제를 한 번에 해결하기보다는 한 번에 하나씩 해결하며 점진적으로 진행해야 합니다. 작은 변경을 자주 테스트하는 방식으로 작업하는 것이 핵심입니다.


3. 테스트 추가를 위한 전략적 접근법

현재 동작 이해와 방어

레거시 코드에 테스트를 추가하는 과정은 단계를 나눠 체계적으로 접근해야 합니다. 첫 번째 단계는 코드의 현재 동작을 이해하고, 변경으로 인해 시스템이 깨지지 않도록 방어하는 데 초점을 맞추는 것입니다. 이 과정에서는 기존 코드를 바로 이해하려 하기보다는 작은 단위에서 동작을 확인하는 탐색적 테스트로 시작하는 것이 효과적입니다.

의존성 제어의 방법

두 번째 단계에서는 의존성을 제어해야 합니다. 의존성이 테스트를 방해할 때 이를 제어하거나 제거하기 위한 다양한 기법을 사용할 수 있습니다. 의존성 삽입(Dependency Injection)을 통해 외부 객체를 주입받는 방식으로 코드의 유연성을 높이거나, 의존성 격리(Seams)를 통해 테스트 환경에서 코드를 제어할 수 있는 구조를 만듭니다. 또한, Mock과 Stub을 활용해 의존성을 대체함으로써 테스트를 독립적으로 수행할 수 있습니다.

안정성 확보 후 리팩토링

세 번째 단계에서는 안정성을 확보한 후 리팩토링을 진행해야 합니다. 코드를 직접적으로 변경하기 전에 주요 경로를 테스트로 감싸 안정지대를 확보하고, 리팩토링은 작은 단위로 나누어 기존 테스트가 통과하는지를 지속적으로 확인하며 진행해야 합니다.


4. 책에서 소개된 구체적인 기법들

클래스 수준의 작업

책에서는 레거시 코드를 다룰 때 유용한 다양한 기법들을 제시합니다. 클래스 수준에서 작업할 때는 테스트하기 어려운 클래스를 독립적으로 실행할 수 있도록 격리하거나, 상속과 인터페이스를 활용해 특정 동작을 대체할 수 있게 만드는 방법이 있습니다.

함수 수준의 작업

함수 수준에서는 복잡한 함수 내에서 테스트 가능한 부분을 분리하여 별도의 함수로 추출하거나, 코드의 흐름을 테스트가 용이하도록 재정렬하는 방식이 유용합니다.

의존성 제거를 위한 방법

의존성을 제거하기 위한 기법으로는 전역 변수를 제거하거나 이를 감싸는 래퍼를 추가하는 방법, 정적 메서드를 인스턴스 메서드로 변환하거나 이를 감싸는 클래스를 추가하는 방법이 있습니다. 또한, 파일 시스템과의 분리를 통해 파일 읽기/쓰기와 같은 동작을 추상화하거나, 외부 API와의 상호작용을 Mocking하여 테스트 환경에서 제어할 수 있습니다.


5. 실무 적용을 위한 팁

점진적 접근의 중요성

레거시 코드를 다룰 때는 작은 성공부터 시작하는 것이 중요합니다. 처음부터 모든 문제를 해결하려 하기보다는 작은 단위에서 성공적인 테스트를 작성하고, 이를 점진적으로 확장해 나가는 방식이 효과적입니다.

팀원과의 협력

팀원들과 협력하여 변경 사항을 공유하고, 합의된 코드 스타일과 테스트 원칙을 설정하는 것도 필수적입니다.

도구 활용

정적 분석 도구나 리팩토링 지원 도구를 활용하면 의존성 파악과 코드 정리에 큰 도움을 받을 수 있습니다.


결론: 레거시 코드를 다룰 때의 마인드셋

레거시 코드를 다룰 때 가장 중요한 것은 점진적인 변화와 테스트 기반의 안정성을 유지하는 것입니다. Working Effectively with Legacy Code는 이를 실현하기 위한 구체적인 전략과 기법을 제공합니다. 이 책에서 강조하는 메시지는 단순히 코드 변경 기술에 그치지 않고, 레거시 코드와의 "대화"를 통해 시스템을 안정적으로 발전시키는 과정을 담고 있습니다.

 

레거시 코드는 부담이 아닌, 대화하며 개선해 나갈 대상임을 기억해야 합니다. 작은 변화의 걸음을 내딛고 해결의 순간을 거듭할수록 자신만의 역량과 자신감이 쌓이는 것을 느낄 수 있을 것입니다.

 

 

 

 

반응형