[리팩터링 2판] #1 리팩터링 첫번째 예시

joy_five·2023년 3월 4일
0

1.3 리팩터링의 첫 단계

리팩터링의 첫 단계는 테스트 코드 작성하기

  1. 함수의 결과값의 type에 따라 미리 return값을 활용한 응용 return값을 여러개 준비해두기
  2. 테스트 프레임워크 등을 활용하여 테스트 과정 단축하기 (단축키를 활용한 테스트 코드 실행)
  3. 테스트 결과 확인 시 RED / GREEN 등 성공과 실패를 스스로 판단하는 자가진단 테스트로 만들어둘 것

    리팩터링하기 전에 제대로 된 테스트부터 마련한다. 테스트는 반드시 자가진단하도록 만든다.

1.4 statement()함수 쪼개기

함수 추출하기: 함수의 전체 동작 중 부분으로 나눌 수 있는 지점을 찾아보기

  • 분리할 수 있는 코드 조각을 추출한다.
  • 추출한 함수는 동작을 직관적으로 알 수 있는 이름으로 지어준다.
  • 함수를 분리할 때 유효범위를 벗어나는 변수의 유무를 확인하고, 매개변수를 전달한다.
  • 수정하고 나면, 이전과 동일하게 동작하는지 컴파일-테스트-커밋 실행

한 가지를 수정할 때 마다 테스트하면, 오류가 생기더라도 변경 폭이 적어 문제를 찾고 해결하기 쉽다. 한번에 너무 많이 수정하려다 실수하는 경우 디버깅하기 어려워 결과적으로 작업시간이 늘어난다.

  • 함수를 추출하고 나면 추출된 함수 코드를 보다 더 명확하게 표현할 수 있는 방법이 없는지 검토하고, 변수/함수의 네이밍이 명확한지도 점검한다.

임시 변수를 질의함수로 바꾸기

  • 대입문을 활용하여 우변에 함수를 호출한다.

변수 인라인하기

  • 위 질의함수를 직접적인 변수로 인라인하고, 대입문은 제거한다.
...
for (let perf of invoice.performances) {
const play = playFor(perf); // 인라인된 변수 제거
let thisAmout = amountFor(perf, playFor(perf)); // 변수 인라인
...
}

함수 선언 바꾸기 : play 매개변수를 제거하자.

  • 새롭게 생성한 playFor()함수를 사용하도록 기존 play 변수를 활용하는 코드들을 수정하고, play 매개변수를 제거한다.
  • 코드의 가시범위에 따라 함수의 역할을 명료하게 전달할 수 있는 네이밍 하기
    format -> formatAsUSD -> usd : 44p

반복문 쪼개기

  • 반복문을 한 바퀴 돌 때 마다 값을 누적하는 함수를 리팩터링 할때에는 먼저 반복문 쪼개기로 값이 누적되는 부분을 따로 빼낸다.
    1개의 반복문 -> 2개의 반복문으로 수정

문장 슬라이드 하기 : 코드 위치 수정하기

함수로 추출

  • 지역변수로 선언된 변수를 제거하기 위해, 변수를 인라인하기에 앞서 로직을 별도 함수로 분리하고, 함수를 활용하여 변수를 인라인 하여 지역 변수를 제거할 수 있다.

volumeCredits 변수를 제거한 프로세스

  1. 반복문 쪼개기 : 변수 값을 누적시키는 부분을 분리한다.
  2. 문장 슬라이드하기 : 변수 초기화 문장을 변수 값 누적 코드 앞으로 이동시킨다.
  3. 함수 추출하기 : 적립 포인트 계산 부분을 별도 함수로 추출한다.
  4. 변수 인라인하기 : voulmeCredits 변수는 제거한다.

1.6 계산 단계와 포맷팅 단계 분리하기

단계 쪼개기

  • 첫 단계에서는 statement()에 필요한 데이터 처리 (연산)
  • 두번째 단계에서는 앞선 결과를 텍스트나 HTML로 처리 (포맷팅)

함수 추출하기

  • 합쳐져 있는 코드 중 포맷팅에 해당하는 코드들을 별도의 함수로 분리한다.
  • 첫번째 함수와 두번째 함수 사이에 중단 데이터 구조 역할을 할 객체를 만들어 인수로 전달한다.

함수 옮기기

  • 실제로 데이터를 담기 위해 playfor() 함수를 statement()로 옮기고, 기존 playfor()를 호출하던 부분을 중간 데이터를 사용하도록 수정한다.

반복문을 파이프라인으로 바꾸기

function totalAmont(data){ 
return data.performances
.reduce((total,p)=> total + p.amount, 0); // for문을 파이프라인으로 변경
}
function totalVolumeCredits(data) {
return data.performances
.reduce((total,p) => total + p.volumeCredits,0); // for문을 파이프라인으로 변경
}

1.8 다형성을 활용해 계산 코드 재구성

현재 상태에서 코드를 변경하려면 계산을 수행하는 함수에서 조건문을 수정해야 한다.
amoutFor() 함수는 연극 장르에 따라 계산 방식이 달라진다는 사실을 알 수 있는데, 이런 형태의 조건부 로직은 코드 수정 횟수가 많아질수록 비효율적인 코드로 느껴지기 쉽다.
구조적인 프로그래밍을 위해 객체지향의 핵심 특성인 다형성을 활용하는 방안에 대해 알아보자.

조건부 로직을 다형성으로 바꾸기

  • 조건부 코드 한 덩어리를 다형성을 활용하는 방식으로 변경하기
  • 상속 계층부터 정의한다.
  • 희극 서브 클래스와 비극 서브클래스가 각자의 계산 로직을 정의하도록 슈퍼 클래스 > 서브 클래스의 상속관계를 맺도록 하는 것은 ‘희극이냐 비극이냐’ 하는 1차적인 판단으로부터 연결되어지는 계산 로직이 달라지도록 의도한다.
  • 공연료 계산기 performanceCalculator() 만들기 :
  1. 공연 관련 데이터를 계산하는 함수들로 구성된 계산 로직을 분리하고, 클래스에 객체를 생성한다.
  2. 기존 코드에서 몇가지 동작을 해당 클래스로 이동한다.
  3. 계산기 클래스 생성자에 함수 선언 바꾸기를 적용하여 공연할 연극을 계산기로 전달한다.
  • 함수들을 계산기로 옮기기
    함수 옮기기 리팩터링을 통해 다른 컨텍스트에 있던 계산 로직과 연관된 함수들을 계산기 함수 안으로 이동하여 중첩 함수를 구성한다.
    이 코드가 기존과 동일하게 동작할 수 있도록 aPerformancethis.performance와 같은 모양으로 변경한다.
    함수를 인라인하여 새 함수를 직접 호출하도록 하고, 변수는 제거한다.
  • 공연료 계산기를 다형성 버전으로 만들기
  1. 타입 코드를 서브클래스로 바꾸기 : 서브 클래스를 구성한 뒤, 기존 데이터가 적합한 서브 클래스를 사용하도록 생성자 대신 함수를 호출하는 방식으로 리팩터링 한다.
  2. 생성자를 팩터리 함수로 바꾸기 : 생성자가 서브 클래스의 인스턴스를 반환할 수 있도록 기존의 생성자 대신 팩터리 함수로 변경해준다.
  3. 조건부 로직을 다형성으로 바꾸기

💬 1장의 예시를 살펴본 감상

약 50여 페이지에 걸쳐 간단한 로직과 간결한 코드를 가지고 있던 모양새가 보다 구조적이고 명료한 코드로 변환됨과 동시에 엄청나게 코드의 양이 늘어남을 느낄 수 있었다.
나는 짧고 간결한 코드를 선호하는 편이지만, 좋은 코드란 짧은 코드를 의미하지 않는다는 말을 다시금 확인하게 된 것 같아 정신을 바짝! 차리게 되는것 같다.

현재, 프로젝트에서 각각의 관심사를 분리하고 쪼갤 수 있을 때까지 쪼개어 나가는 방식을 지향(채택이라고 하기에는 내가 너무 못지키고 있다)하고 있어, 특히 리팩터링을 하는 과정에서 점차 함수가 분리되고, 파일이 분리되어 나가는 과정이 낯설게만 느껴지지 않았는데, 지금 당장은 아토믹 디자인 패턴을 구현하는데 자꾸만 게으름을 부리게 되지만, 앞으로는 보다 더 철저하게, 효율적인 코드를 고려할 수 있도록 노력해야겠다.

1장은 개념을 설명하고, 정의하기보다는 예시를 통해 전체적인 흐름과 중간중간 어떤 기법을 사용했는지에 대한 언급 정도로 넘어가는 파트이다보니 생각보다 쉽게 이해할 수 있었는데, 자세한 방법론이나 개념들을 잘 익혀보는 것으로! 😤

파이팅해봅시다!

profile
😤 Started in Sep. 2022 😎 I'm going to further!

0개의 댓글