코드스멜과 리팩토링으로 유지보수 가능한 코드 만들기

코드스멜의 주요 유형과 리팩토링 원칙, 테스트 기반 구조 개선 기법을 정리해 유지보수 가능한 소프트웨어 개발 방법을 살펴본다.

2026-08-14 · 최초 발행 2026-04-17

변경이 어려워지는 코드에는 징후가 남는다

기능은 정상 동작하지만 읽기 어렵고, 작은 요구사항에도 여러 곳을 건드려야 하는 코드가 있다. 이런 구조를 방치하면 중복과 결합이 쌓이고, 변경의 영향 범위를 예측하기 어려워진다. 코드스멜을 찾아내고 행위 보존을 전제로 구조를 다듬는 일은 기술 부채가 유지보수 불능 상태로 번지는 것을 막는 방법이다.

코드스멜(Code Smell)은 소스 코드에 심각한 문제가 있을 가능성을 보여 주는 징후다. 컴파일 오류나 실행 중단을 일으키는 버그는 아니지만, 가독성을 낮추고 중복 로직을 만들며 코드 품질을 해친다.

이런 냄새는 개발자의 인지 부하를 높이고 변경에 따른 사이드 이펙트를 가늠하기 어렵게 한다. 켄트 벡(Kent Beck)과 마틴 파울러(Martin Fowler)에 의해 대중화된 이 개념은 코드에서 무언가 잘못되었다는 직관을 리팩토링 시점의 판단 기준으로 다룬다.

자주 발견되는 코드스멜

중복된 코드 (Duplicated Code)

동일하거나 거의 같은 코드 구조가 두 곳 이상에 존재하는 경우다. 로직을 바꿀 때 중복된 모든 지점을 함께 수정해야 하므로 변경 누락으로 인한 버그가 생기기 쉽다.

긴 메소드 (Long Method)

하나의 메소드가 많은 행을 담거나 여러 책임을 동시에 맡는 상태다. 메소드가 길어질수록 이해와 재사용이 어려워지고 테스트 코드도 복잡해진다.

큰 클래스 (Large Class)

인스턴스 변수나 메소드가 지나치게 많은 클래스는 단일 책임 원칙(SRP)을 위반할 수 있다. 클래스 사이의 결합도는 높아지고 응집도는 낮아져 가독성과 확장성이 떨어진다.

여러 클래스의 동시 수정 (Shotgun Surgery)

하나의 요구사항을 반영하려고 여러 클래스의 작은 부분을 함께 수정해야 하는 상황이다. 로직이 파편화되었다는 신호이며, 변경할 지점을 빠뜨려 시스템 안정성을 해칠 수 있다.

다른 클래스의 지나친 사용 (Feature Envy)

메소드가 자신이 속한 클래스보다 다른 클래스의 데이터나 메소드를 더 많이 사용하는 경우다. 데이터와 로직의 위치가 어긋나 있으며 객체지향의 캡슐화 원칙을 훼손할 수 있다.

많은 파라미터 (Long Parameter List)

메소드 호출에 필요한 인자가 너무 많으면 호출 자체가 혼란스러워진다. 메소드가 지나치게 많은 정보를 요구하거나 여러 책임을 맡고 있다는 신호일 수 있으며, 파라미터 객체로 바꿀 필요를 시사한다.

기능을 바꾸지 않고 구조를 개선하는 작업

리팩토링(Refactoring)은 소프트웨어의 외부 행위(Behavior)를 보전하면서 내부 구조를 개선해 가독성, 유지보수성, 성능 등을 높이는 품질 향상 기법이다.

기능 추가와 구조 개선은 분리해야 한다. 기능을 추가하는 동안에는 리팩토링하지 않고, 구조를 개선할 때는 새 기능을 넣지 않는 두 개의 모자(Two Hats) 원칙이 여기에 해당한다. 또한 리팩토링 전후 테스트가 통과하는지 확인해야 기존 기능이 파손되지 않았음을 보장할 수 있다.

탐지와 검증을 반복하는 개선 흐름

대상 선정정상성공실패코드 품질 분석 시작코드스멜 탐지(중복, 메소드 등)리팩토링 대상 결정리팩토링 기법 적용(Extract, Move 등)품질 유지 상태행위 보존 확인(단위 테스트)내부 구조 개선 완료이전 상태 복구 분석

코드스멜을 탐지한 뒤에는 개선 대상을 정하고 적절한 기법을 적용한다. 이후 단위 테스트로 행위 보존을 확인한다. 검증에 실패했다면 이전 상태로 복구하고 원인을 다시 분석해야 한다.

책임과 인터페이스를 조정하는 리팩토링 기법

메소드 내부의 로직을 정리할 때는 의도를 드러내는 단위로 분리하는 방식이 쓰인다.

  • Extract Method: 긴 메소드의 일부를 별도 메소드로 추출하고 이름을 붙여 의도를 분명히 한다.
  • Replace Temp with Query: 임시 변수를 메소드 호출로 대체해 중복을 줄이고 재사용성을 높인다.

객체 사이의 책임 배분이 맞지 않을 때는 로직의 위치를 옮긴다.

  • Move Method: 메소드가 자신의 클래스보다 다른 클래스를 더 많이 참조한다면 그 메소드를 다른 클래스로 옮긴다.
  • Extract Class: 하나의 클래스가 너무 많은 일을 맡을 때 일부 필드와 메소드를 새 클래스로 분리한다.

호출자가 이해하기 쉬운 인터페이스를 만드는 작업도 필요하다.

  • Rename Method: 역할을 가장 잘 드러내는 이름으로 메소드명을 바꾼다.
  • Remove Parameter: 사용되지 않거나 불필요한 파라미터를 제거한다.

상속 구조에서는 공통 코드를 상위 계층으로 모아 중복을 줄일 수 있다.

  • Pull up Field: 서브 클래스에 공통으로 존재하는 필드를 슈퍼 클래스로 이동한다.
  • Pull up Method: 같은 기능을 수행하는 서브 클래스의 메소드를 슈퍼 클래스로 옮긴다.

다음 기법도 구조를 단순하게 만드는 데 사용된다.

  • Encapsulation Field: 공개 필드를 비공개로 바꾸고 접근자(Getter/Setter)를 통해 데이터에 접근하도록 한다.
  • Decompose Condition: 복잡한 조건문(if-then-else)을 의도가 드러나는 메소드로 추출한다.
  • Substitute Algorithm: 복잡하거나 비효율적인 알고리즘을 더 명확하고 효율적인 코드로 완전히 교체한다.

코드 품질 관리는 한 번의 정비로 끝나지 않는다. 코드스멜을 발견했을 때 테스트를 기반으로 작은 구조 개선을 반복하면, 읽기 쉽고 수정하기 쉬운 코드 베이스를 유지하는 데 도움이 된다.

Sources

  • Martin Fowler, "Refactoring: Improving the Design of Existing Code", Addison-Wesley.
  • Kent Beck, "Implementation Patterns", Addison-Wesley.
  • Joshua Kerievsky, "Refactoring to Patterns", Addison-Wesley Professional.
  • SourceMaking, "Refactoring and Design Patterns", https://sourcemaking.com/refactoring
코드스멜리팩토링소프트웨어 품질기술 부채객체지향