1. 코드 스멜이란 무엇인가
- 코드 스멜(Code Smell)은 Martin Fowler가 1999년 저서 "Refactoring: Improving the Design of Existing Code"에서 처음 체계화한 개념입니다.
- 코드가 기능적으로는 정상 작동하지만, 설계나 구조상 문제가 있어 향후 유지보수나 확장에 어려움을 줄 수 있는 징후를 의미합니다.
- 코드 스멜은 버그나 오류가 아니라 "개선이 필요한 신호"로, 리팩토링을 통해 해결할 수 있습니다.
정보
코드 스멜은 "냄새"라는 표현을 사용해서 직관적으로 "뭔가 문제가 있다"는 느낌을 주지만, 즉시 수정해야 하는 것은 아닙니다. 상황에 따라 우선순위를 정 해 점진적으로 개선하는 것이 좋습니다.
1.1 코드 스멜의 중요성
- 유지보수성 향상: 문제가 되는 코드 패턴을 조기에 발견하여 수정 비용을 줄입니다.
- 가독성 개선: 이해하기 어려운 코드 구조를 개선하여 팀의 생산성을 높입니다.
- 버그 예방: 복잡하고 결합도가 높은 코드에서 발생할 수 있는 버그를 사전에 방지합니다.
- 확장성 확보: 새로운 요구사항에 유연하게 대응할 수 있는 코드 구조를 만듭니다.
1.2 코드 스멜과 기술 부채
- 코드 스멜은 기술 부채(Technical Debt)의 구체적인 징후라고 볼 수 있습니다.
- 단기적으로는 문제없이 작동하지만, 장기적으로는 개발 속도를 늦추고 유지보수 비용을 증가시킵니다.
- 적절한 리팩토링을 통해 이러한 부채를 줄여나가는 것이 중요합니다.
2. Martin Fowler의 코드 스멜 분류 체계
- Martin Fowler는 코드 스멜을 다음 5가지 주요 카테고리로 분류했습니다
- Bloaters (비대한 코드)
- Object-Orientation Abusers (객체지향 남용자)
- Change Preventers (변경 방해자)
- Dispensables (불필요한 것들)
- Couplers (결합자)
3. Bloaters (비대한 코드)
- 비대한 코드는 크기가 비정상적으로 커서 다루기 어려운 코드를 의미합니다.
- 시간이 지나면서 점진적으로 커져서 관리가 어려워진 코드들이 이 범주에 속합니다.
3.1 Long Method (긴 메서드)
- 레퍼런스
- 징후와 증상
- 메서드가 너무 많은 코드 라인을 포함하고 있습니다.
- 일반적으로 10줄보다 긴 메서드는 문제를 제기하기 시작해야 합니다.
- 문제의 원인
- 메서드에는 항상 무언가가 추가되지만 제거되는 것은 없습니다.
- 기존 메서드에 추가하는 것보다 새로운 메서드를 만드는 것이 더 어려운 경우가 많습니다.
- 이는 또 다른 줄이 추가되고 또 다른 줄이 추가되어 스파게티 코드의 엉킴을 만들어냅니다.
- 성능
- 많은 사람들이 주장하는 것처럼 메서드 수의 증가가 성능에 해를 끼칠까요? 거의 모든 경우에 그 영향은 너무 미미해서 걱정할 가치가 없습니다.
3.1.1 해결 방법
- 경험상, 메서드 내부의 무언가에 주석을 달고 싶다면, 그 코드를 가져와서 새로운 메서드에 넣어야 합니다.
- 설명이 필요하다면 한 줄이라도 별도의 메서드로 분리될 수 있고 분리되어야 합니다.
- 메서드 본문의 길이를 줄이려면 Extract Method를 사용하세요.
- Extract Method: 긴 메서드의 일부분을 별도의 메서드로 분리하는 기법입니다.
- 지역 변수와 매개변수가 메서드 추출을 방해한다면, Replace Temp with Query, Introduce Parameter Object 또는 Preserve Whole Object를 사용하세요.
- Replace Temp with Query: 임시 변수를 제거하고, 해당 값을 반환하는 메서드로 대체하는 기법입니다.
- Introduce Parameter Object: 여러 개의 매개변수를 하나의 객체로 묶어서 전달하는 기법입니다.
- Preserve Whole Object: 객체의 여러 속성을 개별적으로 전달하는 대신 객체 전체를 전달하는 기법입니다.
- 이전의 방법들이 도움이 되지 않는다면, Replace Method with Method Object를 통해 전체 메서드를 별도의 객체로 이동시키는 것을 시도해보세요.
- Replace Method with Method Object: 복잡한 메서드를 별도의 클래스로 추출하여 여러 메서드로 분해하는 기법
- 조건 연산자와 루프는 코드가 별도의 메서드로 이동될 수 있다는 좋은 단서입니다.
- 조건문의 경우 Decompose Conditional을 사용하세요.
- 루프가 방해가 된다면 Extract Method를 시도해보세요.
- Decompose Conditional: 복잡한 조건문을 별도의 메서드로 분해하는 기법
3.1.2 이점
- 모든 종류의 객체지향 코드 중에서 짧은 메서드를 가진 클래스가 가장 오래 살아남습니다.
- 메서드나 함수가 길어질수록 이해하고 유지보수하기가 더 어려워집니다.
- 또한, 긴 메서드는 원치 않는 중복 코드를 숨기기에 완벽한 장소를 제공합니다.
3.2 Large Class (거대한 클래스)
- 특징: 하나의 클래스가 너무 많은 책임을 가지고 있는 경우입니다.
- 문제점: 단일 책임 원칙(SRP)을 위반하며, 클래스의 응집도가 낮아집니다.
- 해결방법: Extract Class나 Extract Subclass를 통해 책임을 분리합니다.
- 실무 예시: UserService가 사용자 관리뿐만 아니라 이메일 발송, 파일 업로드, 알림 등 모든 기능을 처리하는 경우입니다.