리팩토링이란?
리팩토링 : 외부에서 보는 프로그램 동작은 바꾸지 않고 프로그램의 내부 구조를 개선하는 것.
- 리팩토링을 해도 외부에서 보는 프로그램 동작은 변하지 않는다.
- 리팩토링을 하면 프로그램의 내부 구조가 개선된다.
🛠 버그 수정은 리팩토링이 아니다.
: 버그를 수정하면 프로그램의 외부 동작이 바뀐다.
🛠 기능 추가는 리팩토링이 아니다.
: 기능을 추가하면 외부에서 보는 프로그램 동작이 변한다.
🛠 리팩토링과 유닛테스트
: 리팩토링을 한다면 적어도 유닛 테스트 정도는 반드시 해야한다.
리팩토링할 부분을 테스트하고, 리팩토링을 한 뒤에도 테스트 결과가 같은 지 확인한다. 그러므로 리팩토링 단계마다 유닛테스트를 해야한다.
리팩토링의 목적
- 버그를 발견하기 쉽게 만든다.
- 기능을 추가하기 쉽게 만든다.
- 코드 리뷰하기 쉽게 만든다.
리팩토링의 한계
- 프로그램이 아직 동작하지 않을 때는 리팩토링을 할 수 없다.
- 시간이 촉박할 때는 리팩토링을 할 수 없다. -> 리팩토링은 시간이 지나면서 효과를 본다.
리팩토링과 악취
프로그램에서 개선이 필요한 부분을 '악취'가 난다고 한다.
아래와 같은 부분이 악취가 나는 부분이다.
- 이해하기 어려운 부분
- 수정하기 어려운 부분
- 확장하기 어려운 부분
중복 코드 | 같은 코드가 곳곳에 중복되어 있다. |
너무 긴 메서드 | 메서드가 너무 길다. |
방대한 클래스 | 클래스의 필드나 메서드가 너무 많다. |
과다한 매개변수 | 메서드가 받는 매개변수 개수가 너무 많다. |
변경 발산 | 사양 변경이 있을 때 수정 내용이 곳곳에 흩어져 있다. |
변경 분산 | 어떤 클래스를 수정하면 다른 클래스도 수정해야 한다. |
속성, 조작 끼어들기 | 언제나 다른 클래스 내용을 수정하는 클래스가 있다. |
데이터 뭉치 | 합쳐서 다뤄야 할 데이터가 한 클래스에 모여 있지 않다. |
기본 타입 집착 | 클래스를 만들지 않고 int같은 기본 타입만 사용한다. |
스위치 문 | switch, if같은 분기문으로 동작을 나눈다. |
평행 상속 | 하위 클래스를 만들면 클래스 계층의 다른 곳에도 하위 클래스를 만들어야 한다. |
게으른 클래스 | 클래스가 별로 하는게 없다. |
의심스러운 일반화 | '언젠자 확장을 하겠지'라고 너무 일반화한다. |
임시 속성 | 임시로만 쓰는 필드가 있다. |
메시지 연쇄 | 메서드 호출 연쇄가 너무 많다. |
중개자 | 맡기기만 하고 자신은 일을 안하는 클래스가 있다. |
부적절한 관계 | 그럴 필요가 없는데도 양방향 링크를 걸거나 IS-A 관계가 없는데 상속을 사용한다. |
클래스 인터페이스 불일치 | 클래스 인터페이스(API)가 적절하지 않다. |
불완전한 라이브러리 클래스 | 기본 라이브러리 클래스를 사용하기 어렵다. |
데이터 클래스 | 필드와 getter, setter 메서드 뿐인 클래스가 있다. |
상속 거부 | 상속한 메서드인데 호출하면 문제가 발생한다. |
주석 | 코드의 모자란 점을 설명하기 위해 자세한 주석이 붙어있다. |
위에 악취를 나타내는 말이 너무 많은데.. 줄이면 여섯가지로 줄일 수 있다.
1. 겹친다.(중복 코드)
➡️ 메서드 추출, 클래스 추출, 널객체 도입으로 해결
2. 너무 길다.(너무 긴 메서드)
➡️ 메서드 추출, 클래스 추출로 해결
3. 너무 많다.(방대한 클래스)
➡️ 클래스 추출, 중개자 제거, 클래스 인라인화, 메서드 인라인화로 해결
4. 이름이 안맞는다.(적절하지 않은 클래스,메서드,변수 명)
➡️ 메서드 추출, 메서드명 변경, 설명용 변수 조입, 임시 변수 분리로 해결
5. 너무 공개적이다.(지나친 public 남발)
➡️ 필드 캡술화, 생성자를 팩토리 메서드로 치환으로 해결(정보 은닉)
6. 객체 지향답지 않다. 1. switch if문 2.instanceOf 3.int만 쓰고 전용 클래스를 사용하지 않음
➡️ 분류 코드를 클래스로 치환, 분류코드를 하위 클래스로 치환으로 해결
리팩토링 에센스 (스텝 바이 스텝)
리팩토링은 단계별로! 스텝 바이 스텝으로 하자!
1. 두 가지 수정을 한꺼번에 하지 않기
ex) A1 -> B1 -> A2 -> B2 가 아닌 A1 -> A2 -> 작업A 확인, B1 -> B2 -> 작업B 확인
2. 되돌리기 쉽게 하기
수정을 되돌려야할 수 있다. 이때 스텝 바이 스텝으로 리팩토링을 했다면 되돌리기 쉬워진다.
3. 단계마다 확인
특정 단계의 리팩토링을 마치면 테스트를 하여 확인한다. 이렇게 테스트를 하면 어느정도까지가 리팩토링의 범위였는지도 확인할 수 있다.
4. 오래된 것을 새로운 것으로 바꿈
'모든 것을 부수고 새로 만든다'가 아니라 '동작하는 상태를 유지하면서 새로운 코드를 추가해서 오래된 것이 모두 새로워지면 오래된 것을 제거한다.'