본문 바로가기

programming study/Refactoring

Refactoring - 코드에서 나는 악취(2)

본 내용은 마틴 파울러의 Refactoring 2판을 토대로 작성되었습니다.

코드에서 나는 악취

전역 데이터

  • 전역 데이터는 코드베이스 어디에서든 건드릴 수 있고 값을 누가 바꿨는지 찾아낼 매커니즘이 없음
    • 유령 같은 원격 작용(Spooky action at a distance)처럼, 버그는 끊임없이 발생
    • 원인이 되는 코드를 찾아내기가 굉장히 어려움
  • 대표적인 형태는 전역 변수
    • 클래스 변수와 싱글톤(singletone)에서도 같은 문제가 발생
  • 이를 방지하기 위한 대표적인 리팩터링은 변수 캡슐화하기
    • 함수를 감싸는 것만으로도 데이터를 수정하는 부분을 쉽게 찾을 수 있고 접근을 통제
  • 접근자 함수들을 클래스나 모듈에 집어넣고 그 안에서만 사용할 수 있도록 접근 범위를 최소로 줄이는 것이 좋음
  • 전역 데이터가가변(mutable)이라면 특히나 다루기 까다로움
    • 반면, 값이 바뀌지 않는다고 보장할 수 있는 전역 데이터는 그나마 안전한 편
  • 전역 데이터가 조금만 있더라도 캡슐화 하기

 

가변 데이터

  • 데이터를 변경했더니 예상치 못한 결과나 골치 아픈 버그로 이어지는 경우가 종종 있음
  • 다른 곳에서는 다른 값을 기대한다는 사실을 인지하지 못한 채 수정해버리면 프로그램이 오작동
    • 아주 드문 조건에서만 발생한다면 원인을 알아내기가 매우 어려움
    • 이런 이유로, 함수형 프로그래밍에서는 데이터는 절대 변하지 않고, 데이터를 변경하려면 반드시 원래 데이터는 그대로 둔채, 변경하려는 값에 해당하는 복사본을 만들어서 변환한다는 개념을 기본으로 삼고 있음
  • 변수 캡슐화하기
    • 정해놓은 함수를 거쳐야만 값을 수정할 수 있도록 하면 값이 어떻게 수정되는지 감시하거나 코드를 개선
  • 변수 쪼개기
    • 하나의 변수에 용도가 다른 값들을 저장하느라 값을 갱신하는 경우
    • 용도별로 독립 변수에 저장하게 하여 값 갱신이 문제를 일으킬 여지를 없앰
  • 문장 슬라이드하기, 함수 추출하기
    • 갱신 로직은 다른 코드와 떨어뜨려 놓는 것이 좋음
    • 무언가를 갱신하는 코드로부터 부작용이 없는 코드를 분리
  • 질의 함수와 변경 함수 분리하기
    • 부작용이 있는 코드를 호출할 수 없게 함
  • 세터 제거하기
    • 세터를 호출하는 클라이언트를 찾는 것만으로도 변수의 유효범위를 줄이는 데 도움이 됨
  • 파생 변수를 질의 함수로 바꾸기
  • 여러 함수를 클래스로 묶기여러 함수를 변환 함수로 묶기를 활용해서 변수를 갱신하는 코드들의 유효범위를 제한
  • 구조체처럼 내부 필드에 데이터를 담고 잇는 변수라면, 참조를 값으로 바꾸기를 적용하여 구조체를 통째로 교체하는 편이 나음

 

뒤엉킨 변경

  • 소프트웨어는 변경하기 쉬운 형태로 조직됨
    • 코드를 수정할 때는 시스템에서 고쳐야 할 딱 한 군데를 찾아서 그 부분만 수정할 수 있어야 함
    • 이렇게 할 수 없다면, 뒤엉킨 변경과 산탄총 수술 악취 중 하나가 풍김
  • 뒤엉킨 변경
    • 단일 책임 원칙(SRP, Single Responsibility Principle)이 제대로 지켜지지 않을 때 나타남
    • 하나의 모듈이 서로 다른 이유들로 인해 여러가지 방식으로 변경되는 일이 많을 때
  • ex) 지원해야할 데이터베이스가 추가될 때마다 함수 세개를 바꿔야 하고, 금융 상품이 추가될 때마다 또 다른 함수 네 개를 바꿔야 하는 모듈
    • 데이터베이스 연동과 금융 상품 처리는 서로 다른 맥락에서 이뤄지므로 독립된 모듈로 분리해야 프로그래밍이 편함

 

산탄총 수술

  • 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때
  • 함수 옮기기필드 옮기기로 모두 한 모듈에 묶어두면 좋음
  • 비슷한 데이터를 다루는 함수가 많다면 여러 함수를 클래스로 묶기 적용
  • 데이터 구노를 변환하거나 보강하는 함수들에는 여러 함수를 변환 함수로 묶기 적용
  • 묶은 함수들의 출력 결과를 묶어서 다음 단계의 로직으로 전달할 수 있으면 단계 쪼개기를 적용
  • 어설프게 분리된 로직을 함수 인라인 하기클래스 인라인 하기 같은 인라인 리팩터링으로 하나를 합치는 것도 좋은 방법
    • 메서드나 클래스가 더 비대해지지만, 추후 추출하기 리팩터링으로 더 좋은 형태로 분리 가능

 

기능 편애

  • 프로그램을 모듈화할 때는 코드를 여러 영역으로 나눈 뒤 영역 안에서 이뤄지는 상호작용은 최대한 늘릴 것
    • 영역 사이에서 이뤄지는 상호작용은 최소로 줄이는데 주력
  • 기능편애는 어떤 함수가 자기가 속한 모듈의 하수나 데이터보다 다른 모듈의 함수나 데이터와 상호작용 할 일이 더 많을 때 풍기는 냄새
  • 데이터 근처로 옮기기(함수 옮기기)
  • 함수의 일부에서만 기능을 편애하는 경우, 그 부분만 독립 함수로 빼내고(함수 추출하기) 원하는 모듈로 보내기(함수 옮기기)
  • 함수가 사용하는 모듈이 다양하다면, 가장 많은 데이터를 포함한 모듈로 옮긴다(함수 추출하기)
  • 함수 추출하기로 함수를 여러 조각으로 나눈 후 각각을 적합한 모듈로 옮기면 더 쉽게 해결 되는 경우도 많음
  • 디자인 패턴중 전략 패턴(Strategy Pattern)과 방문자 패턴(Visitor Pattern), 자기 위임(Self-Delegation)은 뒤엉킨 변경 냄새를 없앨 때 활용하는 패턴
    • 함께 변경할 대상을 한데 모으는 것
    • 전략 패턴과 방문자 패턴을 적용하면, 오버라이드해야 할 소량하의 동작 코드를 각각의 클래스로 격리해주므로 수정하기가 쉬워짐(대신 간접 호출이 늘어남)

 

데이터 뭉치

  • 몰려다니는 데이터 뭉치는 보금자리를 따로 마련해줘야 함
  • 필드 형태의 데이터 뭉치를 찾아서 클래스 추출하기로 하나의 객체로 묶음
  • 메서드 시그니처에 있는 데이터 뭉치는 매개 변수 객체 만들기, 객체 통째로 넘기기를 적용해서 매개변수 수를 줄임
    • 메서드 호출 코드가 간결해 짐
  • 데이터 뭉치인지 판별하려면?
    • 값 하나를 삭제
    • 나머지 데이터만으로는 의미가 없다면 객체로 바꾸어야 할 데이터 뭉치
  • 새로운 클래스를 만들었다면, 이어서 그 클래스로 옮기며 좋을 동작은 없는지 살펴볼 것
    • 데이터 뭉치가 생산성에 기여하게 될 것