infinity : 무한한 성장가능성

Chapter 1) 리팩터링: 첫번째 예시 1.1~1.5 본문

📖 book/💚 Refactoring 2판

Chapter 1) 리팩터링: 첫번째 예시 1.1~1.5

인피니 2023. 2. 25. 20:21

예전에 스터디하면서 책을 읽었던 기억은 있는데.. 많이 까먹기도 했고, 

최근 진행한 프로젝트를 리팩터링 하고 싶다는 생각이 들었고, 리팩터링 책을 읽으며 적용해 보는 건 어떨까 싶어 책을 다시 읽으며 정리해보려고 한다! 😄

 

책에서는 간단한 공연료 청구서를 출력하는 코드를 예로 들어준다.

function statement(invoice,plays){

  let totalAmount = 0;
  let volumeCredits = 0;
  let result = `청구내역 ( 고객명: ${invoide.customer})\n`;
  const format = new Intl.NumberFormat("en-Us",{style:"currency",currency:"USD",
    minimumFractionDigits:2}).format;

  for(let perf of invoice.performances) {
    const play = plays[perf.playId];
    let thisAmount = 0;

    Switch(play.type) {

    case "tragedy": //비극
      thisAmount = 40000;
      if(perf.audience > 30){
        thisAmount += 10000 * (perf.audience - 30);
      }
      break;
    case "comedy": //희극
      thisAmount = 30000;
      if(perf.audience > 20){
        thisAmount += 10000 + 500  * (perf.audience - 20);
      }
      thisAmount += 300 * perf.audience;
      break;
    default:
      throw new Error(`알 수 없는 장르: ${play.type}`);
    }
    //포인트를 적립한다.
    volumeCredits += Math.max(perf.audience - 30,0);
    //희극 관객 5명마다 추가 포인트를 제공한다.
    if("comedy" === play.type) volumeCredits += Math.floor(perf.audience/5);

    //청구내역을 출력한다.
    result += `${play.name}: ${format(thisAmount/100)} (${perf.audience}석)\n`;
    totalAmount += thisAmount;
  }
  result += `총액: ${format(totalAmount/100)}\n`;
  result += `적립 포인트: ${volumeCredits}점\n`;
  return result;
}

 

그러면서, 저자는 프로그램이 새로운 기능을 추가하기에 편한 구조가 아니라면, 먼저 기능을 추가하기 쉬운 형태로 리팩터링 하고 원하는 기능을 추가한다고 한다.

 

위 코드에서 사용자의 필요에 맞게 수정할 부분은 

1) 청구내역을 HTML 로 출력하는 기능추가 

-> 해당 기능을 추가할 때 영향을 미치는 부분은 청구 결과에 문자열을 추가하는 문장을 각각 조건문으로 감싸야함 

-> 이때 문제점은 statement 함수의 복잡도가 증가한다. 

 

2) 연극 type 이 다양하게 추가

-> 공연료와 적립 포인트 계산법에 영향을 주게 됨  

 

리팩터링

1) 리팩터링의 첫 단계는 리팩터링 할 코드 영역을 꼼꼼하게 검사해 줄 테스트 코드 작성 (4장에서 테스트 코드에 대해 좀 더 다루는 것 같다)

2) statement() 함수 쪼개기 

 

statement() 함수 쪼개기 

긴 함수를 리팩터링 할 때는 먼저 전체 동작을 각각의 부분으로 나눌 수 있는 지점을 찾는다.

책에서는 switch 에 주목한다.

switch 문이 하는 역할은 한 번의 공연에 대한 요금을 계산하고 있기 때문에 이 부분을 별도의 함수로 추출한다.

amountFor이라고 switch 문이 하는 역할을 함수명으로 적고 추출한다. 

 

별도 함수로 빼냈을 때

유효범위를 벗어나는 변수, 즉 새 함수에서 곧바로 사용할 수 없는 변수가 있는지 확인한다.

(문맥상 유효범위를 벗어나는 변수를 함수를 추출했을 때 추출한 부분에서 선언된 함수가 아니라면 유효범위를 벗어나는 함수라고 이해했다.)

switch 문에서는 perf,play,thisAmount 가 속한다. 

perf & play 는 amountFor 함수에서도 필요하지만 값을 변경하지 않기 때문에 매개변수로 전달하고, thisAmount는 함수 안에서 값이 변한다. 해당 변수는 값을 반환해 주도록 한다.

(*) 코드는 책을 참고하자 (책 30p)

 

함수를 쪼개고 테스트코드를 돌려본다.

간단한 수정이라도 리팩터링 후 항상 테스트하는 습관을 들이자.

 

커밋시점

하나의 리팩터링이 끝나고 나면 커밋 + 해당 커밋들이 쌓여 의미 있는 단위가 된다면 푸시한다.

 

함수 추출하기 후 

추출된 함수 코드를 들여다보면서 지금보다 명확하게 표현 할 수 있는 방법은 없는지 검토한다.

 

변수제거하기

함수를 쪼갤 때 해당 함수 안에 있는 임시변수를 최대한 제거한다

왜냐? 임시 변수들로 인해 로컬 범위에 존재하는 이름이 늘어나, 추출작업이 복잡해지기 때문에 

(임시변수 : 임시적으로 사용하는 변수)

statement 함수에서는 play 가 임시변수에 속한다.

 

이런 임시변수를 제거하는 방법으로는 임시변수를 질의 함수로 바꾸기가 있다,

(질의함수: 연산을 통해 값을 계산하여 반환하는 함수)

임시변수를 사용하는 대신 질의함수로 임시변수 사용하는 곳을 대체한다.

 

리팩터링의 1.1~1.5를 읽으며 느낀 것은 보통 코드를 작성할 때 임시변수를 사용해서 함수 호출하는 부분을 줄이는 것이 좋다고 생각했는데,

임시변수를 줄일수록 함수를 쪼개기 더 쉬워진다는 것을 알게 되었다.

그렇지만, 무조건적으로 임시변수 사용을 줄이고 임시변수대신 함수를 호출하는 것이 좋은 것인가? 에 대한 궁금증은 남은 챕터였다. 

'📖 book > 💚 Refactoring 2판' 카테고리의 다른 글

chapter 02) 리팩터링 : 리팩터링 원칙  (0) 2023.03.26