4 분 소요

1. 리팩토링 학습 시작

이제 본격적으로 리팩토링에 대해 학습을 시작했다. 리팩토링을 위한 학습은 마틴 파울러의 『리팩터링 2판 : 코드 구조를 체계적으로 개선하여 효율적인 리팩터링 구현하기』란 책을 보면서 진행하기로 결정했다. 앞장의 내용은 리팩토링을 진행하는 대략적인 모습과 리팩터링의 원칙에 대한 내용을 소개해주고 있었다. 리팩토링 과정을 지켜보면서 공감되는 점이 많았고, 내가 가장 신경쓰면서 리팩토링을 해야겠다고 마음먹었던 부분이 사실 리팩토링의 원칙을 위배하는 상황도 볼 수 있었다.

생각했던 것과 가장 달랐던 점으로 나는 리팩토링을 진행할 때는 미래에 일어날 일을 고려해 최대한 확장성 있게 코드를 구성해야한다고 생각했는데 현재 요구사항을 만족할 정도만 진행해도 충분하다는 사실을 깨닫게 되었다. 😮 → 유연성 매커니즘

이번 3장을 학습하면서 본격적으로 리팩토링과 관련된 내용을 정리하고 추후에 관련 작업을 할 때 참고할 수 있도록 정리하기 위해 이번 포스팅을 작성하게 되었다.

2. 코드에서 나는 악취

위의 소제목은 내가 이 책을 구매했던 가장 결정적인 이유이다. (정말 마음에 든 제목이다! 🥰)

실제로도 내가 짠 코드를 보면서 나름 고민하면서 짰다고 생각했지만, 완성되고 나서 돌이켜보면 왜 이렇게 비효율적으로 짰을까라고 생각했던 부분이 굉장히 많았다고 생각한다. 하지만 어떤 부분이 비효율적인가라고 물어보면 정확히 대답을 할 수 없다는 점이 너무 아쉬웠다. 이번 챕터를 읽으면서 비효율적으로 코드를 짰다고 말할 수 있는 항목에 대해 살펴볼 수 있었고 어떻게 하면 이를 해결할 수 있는지에 대한 소개를 읽으면서 이후의 내용에 더 많은 관심을 갖게되었다.

이 챕터에서는 코드의 비효율적인 구성을 24개의 항목을 나누어 설명해주고 있다. 우선 이 포스팅에서는 관련된 내용을 짧막하게 요약하고 이후에 작업을 진행하면서 내가 경험했던 내용과 이를 해결한 방법에 대해 따로 포스팅으로 작성할 예정이다. 해결 기법에 대한 소개는 추후 학습 후 링크로 연결지을 예정이다.

코드의 비효율적 구성 및 해결 기법

  1. 기이한 이름
    • 모호한 이름, 문맥을 파악할 수 있을 정도까지로 수정하는 것이 좋음
    • 해결 기법 : 함수 선언 바꾸기 / 변수 이름 바꾸기 / 필드 이름 바꾸기

  2. 중복 코드
    • 똑같은 코드 구조가 여러 곳에서 반복
    • 해결 기법 : 함수 추출하기 / 문장 슬라이드하기 / 메서드 올리기

  3. 긴 함수
    • 함수를 짧게 구성하는 것을 두려워하지 말 것. 또한 함수 이름은 함수의 의도가 잘 드러나도록 지어주자.
    • 해결 기법 : 함수 추출하기 / 임시 변수를 질의로 바꾸기 / 매개변수 객체 만들기 / 객체 통째로 넘기기 / 함수를 명령으로 바꾸기

  4. 긴 매개변수 목록
    • 매개변수를 활용해 전역 데이터가 늘어나는 것을 막기 위함이란 것은 이해하지만, 전달할 데이터를 쪼개서 주는 것보다 원본 그대로 넘기는게 좋을 수 있음
    • 해결 기법 : 매개변수를 질의 함수로 바꾸기 / 객체 통째로 넘기기 / 매개변수 객체 만들기 / 플래그 인수 제거하기 / 여러 함수를 클래스로 묶기

  5. 전역 데이터
    • 전역 데이터를 쉽게 구성하지 말 것. 특히 조작이 가능한 전역 데이터는 굉장히 조심스럽게 다룬다.
    • 해결 기법 : 변수 캡슐화하기

  6. 가변 데이터
    • 코드의 다른 곳에서는 다른 값을 기대하고, 필요하다면 다른 곳에서 값을 설정하는 데이터
    • 해결 기법
      • 수정·갱신) 변수 캡슐화하기 / 변수 쪼개기 / 문장 슬라이드하기 / 함수 추출하기 / 질의 함수와 변경 함수 분리하기 / 세터 제거하기
      • 값 설정) 파생 변수를 질의 함수로 바꾸기
      • 유효범위) 여러 함수를 클래스로 묶기 / 여러 함수를 변환 함수로 묶기 / 참조를 값으로 바꾸기

  7. 뒤엉킨 변경
    • 하나의 모듈이 서로 다른 이유(상황)로 인해 여러 가지 방식으로 변경되는 모듈. 독립된 모듈로 분리하는 것을 권장
    • 해결 기법 : 단계 쪼개기 / 함수 옮기기 / 함수 추출하기 / 클래스 추출하기

  8. 산탄총 수술
    • 코드를 변경할 때마다 변경된 로직에 맞게 수정할 부분이 많은 상황
    • 해결 기법 : 함수 옮기기 / 필드 옮기기 / 여러 함수를 클래스로 묶기 / 여러 함수를 변환 함수로 묶기 / 단계 쪼개기 / 함수 인라인 하기 / 클래스 인라인하기

  9. 기능 편애
    • 자기가 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터를 더 많이 접근하는 상황
    • 해결 기법 : 함수 옮기기 / 함수 추출하기

  10. 데이터 뭉치
    • 여러 개의 데이터가 항상 뭉쳐다니는 상황. 값 하나를 삭제했을 때 나머지 데이터만으로 의미가 없는 데이터가 되는지 확인 → DB 정규화 느낌
    • 해결 기법 : 클래스 추출하기 / 매개변수 객체 만들기 / 객체 통째로 넘기기

  11. 기본형 집착
    • 기본 타입에 모든 데이터를 담으려고 하는 것. (ex : 전화번호 데이터를 String에 담기)
    • 해결 기법 : 기본형을 객체로 바꾸기 / 타입 코드를 서브클래스로 바꾸기 / 조건부 로직을 다형성으로 바꾸기 / 클래스 추출하기 / 매개변수 만들기

  12. 반복되는 switch문
    • 조건을 하나 추가할 때마다 반복되는 switch 문도 전부 수정해야함
    • 해결 기법 : 조건부 로직을 다형성으로 바꾸기

  13. 반복문
    • for, while 같은 반복문 대신 filter나 map같은 파이프라인 연산을 사용하기
    • 해결 기법 : 반복문을 파이프라인으로 바꾸기

  14. 성의 없는 요소
    • 본문 코드를 그대로 쓰는 것과 큰 차이가 없고 실질적으로 메서드가 하나뿐인 클래스를 수정하기
    • 해결 기법 : 함수 인라인하기 / 클래스 인라인하기 / 계층 합치기

  15. 추측성 일반화
    • 미래의 확장성을 고려하여 만들었지만, 지금 당장 필요하지 않은 코드
    • 해결 기법 : 계층 합치기 / 함수 인라인하기 / 클래스 인라인하기 / 함수 선언 바꾸기 / 죽은 코드 제거하기

  16. 임시 필드
    • 특정 상황에서만 값이 설정되는 필드 → 필드가 언제 채워지는지를 파악하고 있어야해서 이해하기 복잡해짐
    • 해결 기법 : 클래스 추출하기 / 함수 옮기기 / 특이 케이스 추가하기

  17. 메세지 체인
    • 다른 객체를 요청하는 작업이 연쇄적으로 이어져 있는 코드 (ex : aPerson.department.manager.name)
    • 해결 기법 : 위임 숨기기 / 함수 추출하기 / 함수 옮기기

  18. 중개자
    • 단순한 모듈 연계 코드도 중개자를 거쳐서 실행되는 상황
    • 해결 기법 : 중개자 제거하기 / 함수 인라인하기

  19. 내부자 거래
    • 함수 내부에서 보이지 않게 데이터를 주고받아 사적으로 처리하는 상황
    • 해결 기법 : 함수 옮기기 / 필드 옮기기 / 위임 숨기기 / 서브클래스를 위임으로 바꾸기 / 슈퍼클래스를 위임으로 바꾸기

  20. 거대한 클래스
    • 한 클래스가 하는 일이 너무 많은 상황. 또한 하는 일이 유사한 로직이 클래스 내부에 많이 존재
    • 해결 기법 : 클래스 추출하기 / 슈퍼클래스 추출하기 / 타입 코드를 서브클래스로 바꾸기 / 클래스 추출하기 / 슈퍼클래스 추출하기 / 타입 코드를 서브클래스로 바꾸기

  21. 서로 다른 인터페이스의 대안 클래스들
    • 모듈 교체를 원할하게 수행할 수 있도록 하기 위해 인터페이스가 같아질 때까지 필요한 동작들을 클래스 안으로 밀어 넣기
    • 해결 기법 : 함수 선언 바꾸기 / 함수 옮기기 / 슈퍼클래스 추출하기

  22. 데이터 클래스
    • 데이터 필드와 getter/setter 메서드로만 이루어진 클래스 → 단순 데이터 저장 용도로만 사용하고 있는 클래스
    • 해결 기법 : 레코드 캡슐화하기 / 세터 제거하기 / 함수 옮기기 / 함수 추출하기 / 단계 쪼개기

  23. 상속 포기
    • 부모의 메서드와 데이터 중 일부만 물려받고 싶은 상황. 대표적으로 부모의 동작은 필요하지만 인터페이스는 따르고 싶지 않은 경우
    • 해결 기법 : 메서드 내리기 / 필드 내리기 / 서브클래스를 위임으로 바꾸기 / 슈퍼클래스를 위임으로 바꾸기

  24. 주석
    • 적당량의 주석은 좋지만, 특정 코드 블록이 하는 주석을 봐야지만 이해할 수 있는 경우
    • 해결 기법 : 함수 추출하기 / 함수 선언 바꾸기 / 어서션 추가하기