언제, 어디에 리팩토링을 해야할까 (When, Where)

When do you refactoring?

3의 법칙

  1. 처음에는 그냥 한다
  2. 비슷한일을 두번째 하게되면 일단 계속 진행한다.
  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)