리팩터링 필요 대상
리팩터링의 대상이 되는 여러가지 유형에 대해 소개한다. 리팩터링 기법에 대해서는 추후 다룰 장에서 자세히 설명할 예정으로 여기서는 리팩터링이 필요한 코드 유형에 대한 이해만 하고 넘어가도록 한다.
1. 기이한 이름(Mysterious Name)
- 프로그래밍에서는 함수, 모듈, 변수, 클래스가 이름만 보고도 각각 무슨 일을 하고 어떻게 사용해야 하는지 명확히 알 수 있도록 해야한다.
- 중요도가 높은 만큼 제일 많이 사용하는 리팩터링에는 함수 선언 바꾸기, 변수 이름 바꾸기, 필드 이름 바꾸기 같은 것들이 있다.
2. 중복 코드(Duplicated Code)
- 코드가 중복되면, 하나로 통합하여 더 나은 프로그램을 만들 수 있다. 주의할 점은, 중복 코드를 발견했을 때 서로 차이점은 없는지 주의 깊게 살펴봐야 한다는 점이다.
- 중복 코드의 예로, 한 클래스에 두 메서드가 똑같은 표현식을 사용하는 경우를 들 수 있다. 이를 위한 리팩터링 기법으로 함수 추출하기, 문장 슬라이드하기, 메서드 올리기 등이 있다.
3. 긴 함수(Long Function)
- 저자들의 의견에 따르면 오랜 기간 잘 활용되는 프로그램들은 하나같이 짧은 함수로 구성된다고 한다. 간접 호출의 효과로 코드를 이해하고, 공유하고, 선택하기 쉬워진다는 장점은 함수를 짧게 구성할 때 나온다.
- 짧은 함수의 서브루틴을 사용해도 코드 읽는 사람 입장에서는 왔다 갔다 해야 하는 부담이 여전히 있지만 좋은 이름으로 함수를 지어두면 본문 코드를 볼 이유가 사라진다.
- 의도를 담아야 하는 코드를 잘게 쪼개서 함수로 뽑고, 이름에 코드의 목적을 드러낸다. 코드가 무엇을 하는지를 잘 설명해주지 못할수록 함수로 만드는 것이 유리하다.
- 대표적인 리팩터링 기법으로 함수 추출하기, 임시 변수를 질의함수로 바꾸기, 매개변수 객체 만들기, 객체 통째로 넘기기 등이 있다.
4. 긴 매개변수 목록(Long Parameter List)
- 매개변수 목록이 길어지면 자체로 이해하기 어려울 때가 많다.
- 대표적인 기법으로 매개변수를 질의 함수로 바꾸기, 객체 통째로 넘기기 등이 있다.
5. 전역 데이터(Global Data)
- 전역 데이터는 코드베이스 어디에서든 건드릴 수 있고 값을 누가 바꿨는지 찾아낼 메커니즘이 없어 버그의 온상이 된다.
- 대표적인 리팩터링 기법은 변수 캡슐화하기가 있다.
6. 가변 데이터(Mutable Data)
- 무분별한 데이터 수정은 따른 예상치 못한 결과, 골치 아픈 버그들로 이어질 수 있다.
- 이를 방지하기 위해 변수 캡슐화하기, 변수 쪼개기, 문장 슬라이드하기, 함수 추출하기 등을 이용할 수 있다.
7. 뒤엉킨 변경(Divergent Change)
- 뒤엉킨 변경은 단일 책임 원칙(SRP, Single Responsibility Principle)이 제대로 지켜지지 않을 때 나타난다. 즉, 하나의 모듈이 서로 다른 이유들로 인해 여러 가지 방식으로 변경되는 일이 많을 때 발생한다.
- 뒤엉킨 변경을 방지하기 위해서는 다른 기능은 독립된 모듈로 분리해야 하지만, 개발 초기에는 맥락 사이의 경계를 명확히 나누기 어렵고 시스템 기능이 변경되면서 경계도 바뀌기 때문에 쉽지 않다.
- 이를 위한 리팩터링 기법으로 단계 쪼개기, 함수 옮기기, 함수추출하기, 클래스 추출하기 등이 있다.
8. 산탄총 수술(Shotgun Surgery)
- 뒤엉킨 변경과 같은 원인이지만, 차이점은 변경해야할 부분이 코드 전반에 퍼져 있다는 점이 다르다.
- 리팩터링 기법으로는 함수 옮기기, 필드 옮기기, 여러 함수를 클래스로 묶기, 여러 함수를 변환 함수로 묶기 등이 있다.
9. 기능 편애(Feature Envy)
- 기능 편애는 흔히 어떤 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 상호작용 할 일이 더 많은 경우를 의미한다.
- 리팩터링 기법으로는 함수 옮기기, 함수 추출하기, 함수 옮기기 등이 있다.
10. 데이터 뭉치(Data Clumps)
- 데이터 항목은 여러 곳에서 함께 뭉쳐 다니는 모습을 흔히 볼 수 있는데, 필요한 필드만 뽑아서 분리해줘야 할 필요가 있다.
- 리팩터링 기법으로는 클래스 추출하기, 매개변수 객체 만들기, 객체 통째로 넘기기 등이 있다.
11. 기본형 집착(Primitive Obsession)
- 프로그래밍 언어에서 제공하는 다양한 데이터의 기본형을 가지지만 문제에 주어진 기초 타입을 직접 정의하기 꺼리는 사람들이 많다. 특히 문자열이 그렇다.
- 이를 위한 리팩터링 기법으로 기본형을 객체로 바꾸기, 타입 코드를 서브클래스로 바꾸기, 조건부 로직을 다형성으로 바꾸기 등이 있다.
12. 반복되는 Switch문
- 중복 Switch문은 조건절을 하나 추가할 때마다 다른 switch 문들도 모두 찾아서 수정해야 하는 문제점이 있어 조건부 로직을 다형성으로 바꾸기 기법을 사용해 수정해야 할 필요가 있다.
13. 반복문(Loops)
- 과거와 달리 지금은 일급 함수를 지원하는 언어가 많아졌기 때문에 반복문을 파이프라인으로 바꾸기 기법을 적용해 걸맞지 않은 반복문을 제거할 수 있게 되었다.
14. 성의 없는 요소(Lazy Element)
- 많은 사람들이 코드의 구조를 잡을 때 프로그램 요소(프로그래밍 언어가 제공하는 빌트인 함수, 클래스 등)를 이용하는 것을 좋아하지만, 꼭 필요하지 않은 경우에는 굳이 프로그램 요소를 사용할 필요는 없다.
- 이를 제거하기 위해 함수 인라인하기, 클래스 인라인하기, 계층 합치기 기법 등을 활용할 수 있다.
15. 추측성 일반화(Speculative Generality)
- 추측성 일반화란, ‘나중에 필요할 거야’라는 생각으로 당장은 필요 없는 모든 종류의 후킹(hooking) 포인트와 케이스 처리 로직을 작성해둔 코드를 말한다. 그러나 이런 코드는 실제 사용하면 다행이지만, 그렇지 않는다면 쓸데없는 낭비이기에 눈앞에서 치워버리는 것이 옳다.
- 하는 일이 거의 없는 추상 클래스는 계층 합치기로, 쓸데없는 위임 코드는 함수 인라인 하기 또는 클래스 인라인 하기로, 본문에서 사용되지 않는 매개변수는 함수 선언 바꾸기로 없앤다.
16. 임시 필드(Temporary Field)
- 특정 상황에서만 값이 설정되는 필드를 가진 클래스도 있지만, 이러한 임시 필드는 코드를 이해하기 어렵게 한다.
- 이를 위한 리팩터링 기법으로는 클래스 추출하기, 함수 옮기기, 특이 케이스 추가하기 등이 있다.
17. 메시지 체인(Message Chains)
- 메시지 체인은 클라이언트가 한 객체를 통해 다른 객체를 얻은 뒤 방금 얻은 객체에 다른 객체를 요청하는 식으로, 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 코드를 말한다. 이런 코드는 클라이언트가 객체 내비게이션 구조에 종속됐음을 의미하기 때문에 내비게이션 중간 단계를 수정하면 클라이언트 코드도 수정해야 한다.
- 이 문제는 위임 숨기기, 함수 추출하기, 함수 옮기기 등 기법으로 해결할 수 있다 .
18. 중개자(Middle Man)
- 객체의 대표적 기능 하나로, 외부로부터 세부사항을 숨겨주는 캡슐화가 있는데, 이것이 지나치는 경우가 문제가 된다.
- 이런 경우, 중개자 제거하기 기법을 통해 실제로 일을 하는 객체와 직접 소통하게 할 필요가 있다.
19. 내부자 거래(Insider Trading)
- 모듈 사이의 데이터 거래가 많으면 결합도가 높아지는 문제점이 있다. 거래는 피할 수는 없지만, 그 양을 최소로 줄이고 모두 투명하게 처리해야 한다.
- 함수 옮기기, 필드 옮기기, 위임 숨기기 등 기법을 통해 사적 처리부분을 줄이거나, 같은 관심사를 공유하도록 만들어준다.
20. 거대한 클래스(Large Class)
- 한 클래스가 너무 많은 일을 하면 필드 수가 늘어나며 중복 코드가 생기기 쉽다.
- 가장 간단한 해법은 클래스 안에서 자체적으로 중복을 제거하는 방법이다.
- 리팩토링 기법으로는 클래스 추출하기를 통해 필드 일부를 따로 묶고, 상속 관계가 필요하다면 슈퍼클래스 추출하기, 타입 코드를 서브클래스로 바꾸기 기법 등을 활용할 수도 있다.
21. 서로 다른 인터페이스의 대안 클래스들
- 클래스 사용시 가장 큰 장점은 필요에 따라 언제든 다른 클래스로 교체할 수 있다는 점인데, 교체하려면 인터페이스가 같아야 한다.
- 함수 선언 바꾸기, 함수 옮기기, 슈퍼클래스 추출하기 등 기법을 통해 인터페이스를 동일하게 만들 수 있다.
22. 데이터 클래스(Data Class)
- 데이터 클래스란, 데이터 필드와 게터/세터 메서드로만 구성된 클래스를 말하며, 데이터 클래스가 주로 저장 용도로만 쓰이다보니, 다른 클래스에서 수정 변경할 수 있거나, 엉뚱한 곳에 정의 되는 등 문제가 발생할 수 있다.
- 이를 해결하기 위해 레코드 캡슐화하기, 세터 제거하기, 함수 옮기기, 함수 추출하기 등 기법을 사용할 수 있다.