리팩터링을 언제해야 하는가에 대해 명확하게 정립된 규칙은 없다. 하지만 리팩터링이 절실한 코드들의 특징은 존재한다. 켄트 백은 이를 냄새(악취)
라고 정의한다.
기이한 이름
코드는 단순하고 명료하게 작성되어야 한다. 특히 이름은 보기만 해도 어떤 작업을 하는 코드인지 명확히 알 수 있어야 한다. 이러한 노력은 곧 코드를 이해하기 쉽게 만든다.
만약 마땅한 이름이 떠오르지 않는다면 설계에 근본적인 문제가 있을 수 있다. 이름을 잘 정리하다 보면 코드가 간결해지는 경우도 있다.
중복 코드
똑같은 코드 구조가 반복된다면 하나로 통합하여 더 나은 프로그램을 만들 수 있다.
리팩터링 | 설명 |
---|---|
함수 추출하기 | |
문장 슬라이드하기 | 함수 추출하기 를 더 쉽게 적용 |
메서드 올리기 |
긴 함수
리팩터링 | 설명 |
---|---|
짧은 함수 | |
좋은 이름 | 동작 방식 이 아닌 의도 가 드러나게 지음. |
긴 매개변수 목록
매개변수 또한 길어지면 이해하기 어려워 진다.
리팩터링 | 설명 |
---|---|
매개변수를 질의 함수로 바꾸기 | |
객체 통째로 넘기기 | |
매개변수 객체 만들기 | |
플래그 인수 제거 | |
여러 함수를 클래스로 묶기 |
전역 데이터
전역 데이터는 코드베이스 어디에서든 건드릴 수 있고 값을 누가 바꿨는지 찾아낼 메커니즘이 없다는 게 문제다.
리팩터링 | 설명 |
---|---|
변수 캡슐화하기 |
가변 데이터
데이터를 변경하면 예상치 못한 결과나 골치 아픈 버그로 이어지는 경우가 종종 있다.
리팩터링 | 설명 |
---|---|
변수 캡슐화하기 | |
변수 쪼개기 | |
질의 함수와 변경 함수 분리하기 | |
세터 제거하기 | |
파생 변수를 질의 함수로 바꾸기 | |
여러 함수를 클래스 / 변환 함수로 묶기 | |
참조를 값으로 바꾸기 |
뒤엉킨 변경
소프트웨어의 구조는 변경하기 쉬운 형태로 조직되어야 한다. 그렇지 않다면 뒤엉킨 변경
이나 산탄총 수술
이 풍기기 마련이다.
뒤엉킨 변경은 SRP(단일 책임 원칙, Single Responsibility Principle)
이 만족하지 않을 때 발생한다. 즉, 하나의 모듈이 서로 다른 이유들로 인해 변경되는 일이 많을 때 발생한다.
비즈니스 로직과 데이터베이스 연동은 서로 다른 맥락에서 이뤄지므로 독립된 모듈로 구성해야 마땅하다. 하지만 개발 초기에는 이러한 경계를 나누는 것이 쉽지 않으며, 개발 과정에서 이 경계 또한 끊임없이 변경된다.
리팩터링 | 설명 |
---|---|
단계 쪼개기 | |
함수 옮기기 | |
함수 추출하기 | |
클래스 추출하기 |
산탄총 수술
산탄총 수술은 뒤엉킨 변경과 비슷하면서도 정반대다.
이 냄새는 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때 풍긴다. 변경할 부분이 코드 전반에 퍼져 있다면 찾기도 어렵고 꼭 수정해야 할 곳을 지나치기 쉽다.
리팩터링 | 설명 |
---|---|
함수 옮기기 / 필드 옮기기 | |
여러 함수를 클래스로 묶기 | |
여러 함수를 변환 함수로 묶기 | |
단계 쪼개기 | |
함수 인라인하기 / 클래스 인라인하기 |
기능 편애
어떤 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈과 상호작용할 일이 더 많을 때 풍기는 냄새
리팩터링 | 설명 |
---|---|
함수 옮기기 | |
함수 추출하기 | 함수 옮기기 로 원하는 모듈로 이동 |
어디로 옮길지가 명확하게 드러나지 않는다면 가장 많은 데이터를 포함한 모듈로 이동하자
데이터 뭉치
데이터 항목들이 항상 함께 뭉쳐 다니는 데이터 뭉치는 보금자리를 따로 마련해줘야 마땅하다.
리팩터링 | 설명 |
---|---|
클래스 추출하기 | |
매개변수 객체 만들기 / 객체 통째로 넘기기 |
클래스를 이용하면 좋은 향기를 흩뿌릴 기회가 생긴다.
기본형 집착
프로그래밍 언어에서 제공하는 기본형에 집착하고, 주어진 문제에 맞는 기초 타입을 직접 정의하기 꺼리는 사람이 많다.
- 기본형을 객체로 바꾸기
- 기본 자료형을 감싸기만 한 것처럼 보임
- 필요한 경우 특별한 동작을 더할 수 있음
- 기본형으로 표현된 코드가 조건부 동작을 제어하는 타입 코드로 쓰이는 경우
- 타입 코드를 서브클래스로 바꾸기
- 조건부 로직을 다형성으로 바꾸기
- 위 리팩터링을 순차 적용한다
반복되는 switch문
조건부 로직을 다형성으로 바꾸기를 통해 가능한 한 switch문은 없애주는 편이 좋다. 조건절을 추가할 떄마다 다른 switch문들도 모두 찾아서 함께 수정해줘야 하기 떄문이다.
반복분
지금은 일급 함수(first-class function)
을 지원하는 언어가 많다. 반복문을 파이프라인으로 바꾸기
를 적용하여 시대에 걸맞지 않은 반복문을 제거하자. 필터나 맵 같은 파이프라인 연산을 사용하면 코드에서 각 원소들이 어떻게 처리되는지 쉽게 파악 가능하다.
성의 없는 요소
실질적으로 메서드가 하나뿐인 클래스와 같은 부실한 모듈의 경우 제거 작업을 진행한다. 함수 인라인하기
나 클래스 인라인하기
로 처리한다. 상속을 사용한 경우 계층 합치기
를 사용한다.
추측성 일반화
당장은 필요 없는 모든 종류의 후킹 포인트와 특이 케이스 처리 로직을 작성해둔 코드에서 풍긴다.
- 하는 일이 거의 없는 추상 클래스
- 계층 합치기
- 쓸데없이 위임하는 코드
- 함수 인라인하기나 클래스 인라인하기로 삭제
- 본문에서 사용되지 않는 매개변수
- 함수 선언 바꾸기로 삭제
- 테스트 코드 말고는 사용하는 곳이 없는 함수나 클래스
- 죽은 코드 제거하기
임시 필드
특정 상황에서만 값이 설정되는 필드를 가진 클래스가 간혹 있다. 이러한 경우 사용자는 쓰이지 않는 것처럼 보이는 필드가 존재하는 이유를 파악하느라 머리를 싸매게 된다.
- 덩그러니 떨어져 있는 필드를 발견하면
클래스 추출하기
로 모은다. - 임시 필드들과 관련된 코드를
함수 옮기기
를 이용해 앞에서 만든 클래스에 넣는다. - 임시 필드들이 유효한지 확인한 후 동작하는 조건부 로직은
특이 케이스 추가하기
로 필드들이 유효하지 않을 때를 위한 대안 클래스를 만들어서 제거한다.