When do you refactoring?
3의 법칙
- 처음에는 그냥 한다
- 비슷한일을 두번째 하게되면 일단 계속 진행한다.
- 비슷한일을 세번째 하게되면 리팩터링 한다.
언제 리팩터링을 해야하는가?
에 대한 답은, 언제나 리팩터링을 해야한다
입니다.
리팩터링은 수시로 진행을 해야하며, 작업흐름에 자연스럽게 녹아들 수 있어야 합니다. 아래와 같은 상황에서 리팩터링을 하면 좋습니다.
1. 기능을 새로 추가하기 직전
- 기능을 추가하기 쉽게만드는 것이 리팩터링의 핵심
- 구조를 살짝 바꾸면 다른 작업하기 쉬워질 만한 부분을 찾는다.
- 기능을 추가하면서 중복코드가 생길만한 부분을 함수화 시킨다.
2. 코드를 이해하기 어려울때
- 코드 수정시 코드를 이해하기 어렵다면 이해를 위한 리팩터링을 진행한다.
- 코드만 보더라도 이해를 쉽게 할 수 있도록 변수와 함수의 이름을 변경한다.
- 코드를 이해하기 쉽게 만드는것은 협업하기도 좋고 코드를 오래 보존 할 수 있게된다.
3. 불필요한 코드를 발견했을때
- 코드가 비효율적으로 수행되는 것을 발견했을대 리팩터링을 진행한다.
- 로직 혹은 코드가 쓸데없이 복잡하거나 불필요한 코드를 발견했다면 보이스카웃 규칙을 떠올리자.
- 원래 하려던 작업시간을 뺏길 수 있으니 간단한 일이라면 바로 처리하고, 시간이 좀 걸릴 것 같으면
TODO
를 남겨두자.
4. 계획된 리팩터링
- 수시로 진행하는 리팩터링 외에도 따로 시간을 내서 리팩터링을 진행 할 수 있다.
- 미리 새기능을 추가 할 수 있도록 코드를 개선해둔다.
- 코드가 이미 깔끔하다면 리팩터링을 하기에도 더 쉽다.
5. 코드 리뷰
- 팀원들과의 코드리뷰를 할때 리팩터링을 진행한다.
- 다른 사람들이 찾아준 리팩토링 포인트를 개선한다.
- 리팩토링 포인트에 대한 코드리뷰시에는 코드에 대한 이해 후 몇가지 개선방향을 제시한다.
- 작성자와 리뷰자가 함께 리팩터링을 진행하면서 자연스러운 페어프로그래밍을 진행하면 좋다.
위에서 리팩터링을 해야할 때를 정리해 보았다면, 리팩터링을 하지 말아야 할때도 있습니다.
6. 굳이 지금, 수정할 필요가 없을때
- 단순히 호출해서 사용하고 있는 코드의 경우 그냥 둔다.
- 실제 내부 동작을 이해해야 할 시점에 리팩터링 하는것이 효과가 좋다.
7.새로 작성하는것이 쉬울때
- 리팩터링보다 새로 코드를 작성하는 쉬운 코드의 경우 그냥 둔다.
- 어떤 코드가 리팩터링보다 새로 만드는게 쉬운가에 대한 판단은 많은 경험이 뒷받침 되어야한다.
Where do you refactoring?
리팩터링 포인트를 찾는 일은 결국 코드내의 악취 (Code-smell
)을 찾는 일 입니다.
아래와 같은 상황들은 일반적으로 찾을 수 있는 리팩터링 포인트들 입니다.
1. 기이한 이름 (Mysterious Name)
- 함수, 모듈, 변수, 클래스 등 코드에서 사용되는 모든 이름은 명료해야한다.
- 마땅한 이름이 떠오르지 않는다면 설계에 근본적인 원인이 있을수도 있다.
- 단순히 혼란스러운 이름을 정리하기 위해 리팩터링을 진행 하고나면, 코드가 훨씬 간결해질때가 많다.
2. 중복 코드 (Dupplicated Code)
- 똑같은 코드 구조가 반복될때 하나로 통합하여 더 나은 프로그램을 만들 수 있다.
3. 긴 함수 (Long Function)
- 코드를 이해하고, 공유하고, 선택하기 쉬워진다는 장점은 짧은 코드에서 나온다.
- 끝없이 위임하고 함수이름을 잘 지어야 한다.
- 주석을 달만한 부분은 함수로 만들어 의도가 드러나게 만든다.
4. 긴 매개변수 목록 (Long Parameter List)
- 매개변수 목록이 길어지면 그자체로 이해하기 어려울때가 많다.
5. 전역 데이터 (Global Data)
- 전역 데이터는 누가 어디서 바궜는지 찾아 낼 수 없어 버그가 발생 할 수 있다.
- 변수 캡슐화를 통해 접근 범위를 최소로 줄이는것이 좋다.
6. 가변 데이터 (Mutable Data)
- 가변 데이터의 유효범위가 넓어질수록 문제를 일으킬 가능성이 높아진다.
- 가변 데이터 사용시 유효범위를 제한 시켜주어야 한다.
7. 뒤엉킨 변경 (Divergent Change)
- 하나의 코드가 여러가지 이유들에 의해 변경되는 경우 문제가 있다.
- 하나의 모듈이 여러가지 기능을 담당하는, 즉 단일책임원칙(SRP)이 제대로 지켜지지 않아서 발생한다.
8. 산탄총 수술 (Shotgun Surgery)
- 코드를 수정할때마다 다른 여러가지 코드들을 같이 수정해야하는 경우 문제가 있다.
- 여러개의 모듈이 한가지 기능을 담당하는, 즉 단일책임원칙(SRP)이 제대로 지켜지지 않아서 발생한다.
9. 기능 편애 (Feature Envy)
- 다른 모듈의 함수나 데이터와 상호작용을 더 많이 하고 있다면 문제가 있다.
- 해당 모듈의 위치를 변경해주는것을 고려해야 한다.
10. 데이터 뭉치 (Data Clumps)
- 자주 같이 사용되는 데이터들은 별도의 의미가 있을 수 있다.
- 하나의 오브젝트로 묶어주는것이 좋다.
11. 기본형 집착 (Primitive Obsession)
- 객체를 만드는것이 귀찮아 기본형만 사용하는것을 피해야한다.
12. 반복되는 스위치문 (Repeated Swwitches)
- 중복된 스위치문을 사용하는경우 조건문 추가시 다른 케이스에서도 조건을 모두 추가해주어야 한다.
13. 반복문 (Loops)
- 가능하다면 파이프라인으로 변경하자.
14. 성의 없는 요소 (Lazy Element)
- 쓸모없는 요소들 (클래스, 함수, 변수 등)은 제거해주는것이 좋다.
15. 추측성 일반화 (Speculative Generality)
- 나중의 작업을 위해 작업해둔, 현재는 사용되지 않는코드들은 제거해주는것이 좋다.
16. 임시 필드 (Temporary Field)
- 특정상황에서만 값이 설정되는 필드가 있다면 해당 필드의 존재이유를 파악하기 쉽지 않다.
- 클래스를 분리하던가, 해당 필드를 함수에 넣어는 등의 방법으로 필드의 위치를 변경해 주는것이 좋다.
17. 메시지 체인 (Message Chains)
- 객체요청이 꼬리를 물고 이어지는 코드는 객체네비게이션에 종속되었음을 의미한다.
- 함수화를 통해 객체 체이닝을 숨기는것이 좋다.
18. 중재자 (Middle Man)
- 단순히 중재자의 역할만 하고 있는 함수가 많다면 쓸모없는 코드들이 많은것 일 수 있다.
- 불필요한 중재자 코드를 제거해주는것이 좋다.
19. 내부자 거래 (Insider Trading)
- 모듈간 데이터 조회가 많으면 결합도가 높다는 의미.
- 최소로 줄이고 투명하게 처리해야한다.
20. 거대한 클래스 (Large Class)
- 클래스에 필드가 많으면 중복코드가 생기기 쉽다.
- 너무많은 클래스가 생기는것도 혼동을 일으키기 쉽다.
- 최대한 중복을 줄이고, 클래스를 나누자.
21. 서로 다른 인터페이스의 대안 클래스들 (Alternative Classes with Different Interfaces)
- 클래스의 장점은 필요할때 교체가 가능한것
- 교체가 가능하도록 인터페이스를 잘 선언해줘야한다.
22. 데이터 클래스 (Data Class)
- 데이터 클래스(필드와 getter/setter로 구성된 클래스)는 캡슐화를 통해 필드를 잘 숨겨야한다.
- Immutable한 VO의 경우 필드를 직접 공개해도 된다.
23. 상속 포기 (Refused Bequest)
- 부모클래스의 메서드 혹은 데이터가 필요없을 수 있다.
- 다만, 구현을 따르지 않을 수 있지만, 인터페이스를 따르지 않는것은 문제가 있을 수 있다.
24. 주석 (Comments)
- 장황한 주석은 코드가 잘못되었기 때문일 확률이 높다.
- 주석대신 함수로 코드를 명확하게 표현한다.
Reference
리팩터링 2판
(https://front.wemakeprice.com/product/822375110?search_keyword=%25EB%25A6%25AC%25ED%258C%25A9%25ED%2586%25A0%25EB%25A7%2581&_service=5&_no=1)