리팩터링 | 마틴 파울러 | 한빛미디어- 교보ebook

코드 구조를 체계적으로 개선하여 효율적인 리팩터링 구현하기, 20여 년 만에 다시 돌아온 마틴 파울러의 리팩터링 2판 리팩터링 1판은 1999년 출간되었으며, 한국어판은 2002년 한국에 소개되었다

ebook-product.kyobobook.co.kr

 

이 책은 개발자를 대상으로 쓰여진 책이기 때문에 흥미를 위해 첫 장부터 리팩터링을 수행하는 방식으로 진행한다.

첫 장부터 리팩터링이 나오니 그 과정을 전부 파악할 순 없다. 그냥 진행되는 과정에 감을 잡는 정도면 충분하다.

 

예제

https://github.com/rkwhr0010/refactoring/tree/main/refactoring/chap01

 

GitHub - rkwhr0010/refactoring: 리팩터링책 내용

리팩터링책 내용. Contribute to rkwhr0010/refactoring development by creating an account on GitHub.

github.com

리팩터링 과정마다 기존 코드를 커밋&푸시해 갱신하는 방식이 아닌 Ex01 ==> Ex02 이렇게 갱신하는 방식으로 따라했다.

 

폴더 구조는 해당 목차에 리팩터링이 없다면, 건너뛴다.

1.1 자, 시작해보자!

	
	plays = {
	    "hamlet":{"name" : "Hamlet", "type" : "tragedy"},
	    "as-like" : {"name":"As You Like It", "type" : "comedy"},
	    "othello":{"name":"Othello", "type":"tragedy"}
	}
	
	invoices = 
	    {
	        "customer" : "BigCo" ,
	        "performances" : [
	            {
	                "playID":"hamlet",
	                "audience" : 55
	            },
	            {
	                "playID" :"as-like",
	                "audience": 35
	            },
	            {
	                "playID":"othello",
	                "audience" :40
	                
	            }
	        ]
	    }
	
	
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta http-equiv="X-UA-Compatible" content="IE=edge">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title>Document</title>
	</head>
	<body>
	    <script src="plays.json" type="text/javascript"></script>
	    <script src="invoices.json" type="text/javascript"></script>
	<script>
	    //기본 함수
	    function statement(invoice, plays) {
	        let totalAmount = 0;
	        let volumeCredits = 0;
	        let result = `청구 내역 (고객명 : ${invoice.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 += 1000 * (perf.audience - 30);
	                }
	                break;
	                case "comedy" : //희극
	                thisAmount = 30000;
	                if(perf.audience > 30){
	                    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;
	    }
	    console.log(statement(invoices,plays))
	</script>
	</body>
</html>

청구 내역 (고객명 : BigCo)
Hamlet : $650.00 (55석)
As You Like It : $580.00 (35석)
Othello : $500.00 (40석)
총액: $1,730.00
적립 포인트: 47점

공연료 관련 프로그램, 예제 코드 출력 시 위와 같은 결과 출력

 

1.2 예시 프로그램을 본소감

프로그램이 작동하는 상황에서 그저 코드가 "지저분하다" 이유로 불평하는 것은 프로그램 구조를 너무 미적인 기준으로만 판단하는 걸까?

컴파일러는 코드가 이쁘건 말건 상관없다. 하지만 그 코드를 수정하는 사람은 미적인 상태에 민감하다

설계가 나쁘면 수정이 어렵다. 수정할 부분을 특정하기도 힘들고 그과정에서 실수를 하기 쉽상이다.

 

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

HTML 출력하는 기능을 추가해야한다고 가정하자

HTML 태그를 전부 넣어야되니 함수 복잡도가 증가하기에 기존 함수를 복사해서  HTML버전을 만드는 것이 일반적일 것이다. 이러면 중복코드도 발생할 것이고, statement()함수 수정이 생기면 HTML버전도 함께 수정해야 한다.

 

리팩터링이 필요한 이유는 이러한 변경 때문이다.

리팩터링은 지금 코드가 절대 변경될 없고, 동작한다면 굳이 필요 없다.

 

 

1.3 리팩터링의 첫 단계

리팩터링 단계는 항상 같다. 리팩터링할 코드 영역을 꼼꼼하게 검사해줄 테스트 코드부터 마련한다.

이유는 리팩터링 기법들의 버그 발생 여지를 최소화하기 위함이다.

리팩터링 또한 코드를 변경하는 행위로 버그가 충분히 스며들 가능성이 있다,

변경 때마다 테스트 코드를 돌린다

테스트 코드는 작성하는데 시간이 걸리지만, 나중에 디버깅 시간이 줄어 결과적으로 전체 작업 시간이 감소된다.

 

 

1.4 statement()함수 쪼개기

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

 

switch(play.type){
    case "tragedy" : //비극
    thisAmount = 40000;
    if(perf.audience > 30){
        thisAmount += 1000 * (perf.audience - 30);
    }
    break;
    case "comedy" : //희극
    thisAmount = 30000;
    if(perf.audience > 30){
        thisAmount += 10000 + 500 * (perf.audience - 20);
    }
    thisAmount += 300 * perf.audience;
    break;
    default: throw new Error(`알수 없는 장르: ${play.type}`);
}

switch문을 보면, 공연에 대한 요금을 계산하고 있다.

이러한 사실은 코드를 분석해서 얻은 정보다. 이런 식으로 파악한 정보는 휘발성이 강해 다음에 코드를 다시 본다면 까먹는 경우가 많다.

 

코드를 별도 함수로 추출해 명확한 이름을 붙이자. amountFor()

추가로 함수 내부에서 쓰이는 변수, 파라미터 이름도 명확하게 변경한다.

컴퓨터가 이해하는 코드는 바보도 작성할 수 있다. 사람이 이해하도록 작성하는 프로그래머가 진정한 실력자다.

좋은 코드라면 의도가 명확히 표현돼야 한다. 요즘 IDE 이름변경이 매우 쉬우므로 좋은 이름으로 바꾸길 주저하지 말자

//기본 함수
function statement(invoice, plays) {
    let totalAmount = 0;
    let volumeCredits = 0;
    let result = `청구 내역 (고객명 : ${invoice.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 = amountFor(perf, play);//함수로 추출했다.

        //포인트를 적립한다.
        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;

    function amountFor(aPerformance, play) {//명확한 이름으로 변경
        let result = 0;

        switch(play.type){
            case "tragedy" : //비극
            result = 40000;
            if(aPerformance.audience > 30){
                result += 1000 * (aPerformance.audience - 30);
            }
            break;
            case "comedy" : //희극
            result = 30000;
            if(aPerformance.audience > 30){
                result += 10000 + 500 * (aPerformance.audience - 20);
            }
            result += 300 * aPerformance.audience;
            break;
            default: throw new Error(`알수 없는 장르: ${play.type}`);
        }
        return result; 
    }
}

 

play 변수 제거하기

aPerformance 루프 변수에서 오기에 순회마다 값이 변경된다.

반면에 play 개별공연(aPerformance)에서 얻기 때문에 매개변수로 전달할 필요가 없다.

amountFor() 안에서 다시 계산하면 된다.

이런 식으로 함수를 쪼갤 때마다 불필요한 매개변수를 최대한 제거한다.

이러한 임시 변수들 때문에 로컬 범위에 존재하는 이름이 많아져 추출 작업이 복잡해진다.

 for(let perf of invoice.performances){
        const play = playFor(perf); // 우변을 함수로 추출
... 

function playFor(aPerformance){
    return plays[aPerformance.playID];
}

정상동작 테스트함 다음 과정 변수 인라인하기

 

for(let perf of invoice.performances){
    // const play = playFor(perf); //변수 인라인하기
    let thisAmount = amountFor(perf, playFor(perf));
    //포인트를 적립한다.
    volumeCredits += Math.max(perf.audience -30,0);
    //희극 관객 5명 마다 추가 포인트를 제공한다.
    if("comedy" === playFor(perf).type) volumeCredits += Math.floor(perf.audience/5);
    //청구 내역을 출력한다.
    result += `${playFor(perf).name} : ${format(thisAmount/100)} (${perf.audience}석)\n`;
    totalAmount += thisAmount;
}

amountFor()함수에서 playFor()함수로 인해 매개변수를 하나 줄일 있게 됐다.

    function amountFor(aPerformance/*, play*/) {//필요없어진 매개변수제거
        let result = 0;
    
        switch(playFor(aPerformance).type){
            case "tragedy" : //비극
            result = 40000;
            if(aPerformance.audience > 30){
                result += 1000 * (aPerformance.audience - 30);
            }
            break;
            case "comedy" : //희극
            result = 30000;
            if(aPerformance.audience > 30){
                result += 10000 + 500 * (aPerformance.audience - 20);
            }
            result += 300 * aPerformance.audience;
            break;
            default: throw new Error(`알수 없는 장르: ${playFor(aPerformance).type}`);
        }
        return result; 
    }

지역 변수를 제거해서 함수 추출하기가 훨씬 쉬워졌다.

이유는 유효범위를 신경써야할 대상이 줄었기 때문이다.

추출 작업 전에 거의 항상 지역 변수부터 제거하도록 하자

 

amountFor()함수 파라미터는 정리했으니 statement() 보자

임시 변수인 thisAmount 선언되고 값이 변하지 않는다.

따라서 "변수 인라인하기" 적용한다.

 

	statement()속…
	
	    for(let perf of invoice.performances){
        	//let thisAmount = amountFor(perf); //thisAmount변수 인라인
	        //포인트를 적립한다.
	        volumeCredits += Math.max(perf.audience -30,0);
	        //희극 관객 5명 마다 추가 포인트를 제공한다.
	        if("comedy" === playFor(perf).type) volumeCredits += Math.floor(perf.audience/5);
	        //청구 내역을 출력한다.
	        result += `${playFor(perf).name} : ${format(amountFor(perf)/100)} (${perf.audience}석)\n`;
	        totalAmount += amountFor(perf);//thisAmount변수 인라인
	    }

 

적립 포인트 계산 코드 추출하기

play 변수를 제거한 결과 로컬 유효범위의 변수가 하나 줄어서 적립 포인트 계산 부분을 추출하기가 쉬워졌다.

 

	function statement(invoice, plays) {
	    let totalAmount = 0;
	    let volumeCredits = 0;
	    let result = `청구 내역 (고객명 : ${invoice.customer})\n`;
	    const format = new Intl.NumberFormat("en-US", {
	        style: "currency",
	        currency: "USD",
	        minimumFractionDigits: 2
	    }).format;
	    for(let perf of invoice.performances){
	        //포인트를 적립한다.
	        volumeCredits += Math.max(perf.audience -30,0);
	        //희극 관객 5명 마다 추가 포인트를 제공한다.
	        if("comedy" === playFor(perf).type) volumeCredits += Math.floor(perf.audience/5);
	        //청구 내역을 출력한다.
	        result += `${playFor(perf).name} : ${format(amountFor(perf)/100)} (${perf.audience}석)\n`;
	        totalAmount += amountFor(perf);//thisAmount변수 인라인
	    }
	    result += `총액: ${format(totalAmount/100)}\n`;
	    result += `적립 포인트: ${volumeCredits}점\n`;
	    
	    return result;
}

처리해야 변수 perf, volumeCredits

volumeCredits처리
//함수 추출
function volumeCreditsFor(perf){
    let volumeCredits = 0;
    volumeCredits += Math.max(perf.audience -30,0);
    if("comedy" === playFor(perf).type) 
        volumeCredits += Math.floor(perf.audience/5);
    return volumeCredits;
}

 ...............

    for(let perf of invoice.performances){
        volumeCredits += volumeCreditsFor(perf);//추출한 함수를 이용해 값을 누적
        result += `${playFor(perf).name} : ${format(amountFor(perf)/100)} (${perf.audience}석)\n`;
        totalAmount += amountFor(perf);
    }

테스트 동작 확인 이제 적절한 이름으로
...............
function volumeCreditsFor(aPerformance){
    let volumeCredits = 0;
    volumeCredits += Math.max(aPerformance.audience -30,0);
    if("comedy" === playFor(aPerformance).type) 
        volumeCredits += Math.floor(aPerformance.audience/5);
    return volumeCredits;
}

 

format 변수 제거하기

임시 변수는 나중에 문제를 일으킬 있다.

임시 변수는 자신이 속한 루틴에서만 의미가 있어서 루틴이 길고 복잡해지기 쉽다.

 

임시 변수를 제거하는 것이 다음 리팩터링

 

리팩토링 대상코드

const format = new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    minimumFractionDigits: 2
}).format;

format 임시 변수에 함수를 대입한 형태다.(함수 포인터처럼)

직접 함수를 선언해 사용하도록 바꾼다.

function statement(invoice, plays) {
    let totalAmount = 0;
    let volumeCredits = 0;
    let result = `청구 내역 (고객명 : ${invoice.customer})\n`;
    for(let perf of invoice.performances){
        volumeCredits += volumeCreditsFor(perf);
        result += `${playFor(perf).name} : ${format(amountFor(perf)/100)} (${perf.audience}석)\n`;
        totalAmount += amountFor(perf);
    }
    result += `총액: ${format(totalAmount/100)}\n`;//임시 변수였던 format을 함수 호출로 대체
    result += `적립 포인트: ${volumeCredits}점\n`;

    return result;
}
function format(aNumber){
    return new Intl.NumberFormat("en-US", {
        style: "currency",
        currency: "USD",
        minimumFractionDigits: 2
    }).format(aNumber);
}

format만으로 함수가 하는 일을 충분히 설명하지 못한다. 함수에 핵심 기능에 걸맞는 이름을 짓는다.(화폐 단위 맞추기)

    for(let perf of invoice.performances){
        volumeCredits += volumeCreditsFor(perf);
        result += `${playFor(perf).name} : ${usd(amountFor(perf))} (${perf.audience}석)\n`;
        totalAmount += amountFor(perf);
    }
    result += `총액: ${usd(totalAmount)}\n`;
    result += `적립 포인트: ${volumeCredits}점\n`;

    return result;
}
function usd(aNumber){
    return new Intl.NumberFormat("en-US", {
        style: "currency",
        currency: "USD",
        minimumFractionDigits: 2
    }).format(aNumber/100); // 단위 변환 로직도 이 함수로 안으로 이동
}

함수를 작게 쪼개는 리팩터링은 이름을 잘지어야만 효과가 있다.

이름이 좋으면 함수 본문을 읽지 않고도 무슨 일을 하는지 있다.

 

이름 바꾸기는 쉬우므로 이름 짓기를 주저하지 말고 짓자. 좋은 이름이 떠올르면 다시 변경하면 그만이다.

 

 

volumeCredits 변수 제거하기

변수는 반복문을 바퀴 때마다 값을 누적하기 때문에 리팩터링하기가 까다롭다.

function statement(invoice, plays) {
    let totalAmount = 0;
    let volumeCredits = 0;
    let result = `청구 내역 (고객명 : ${invoice.customer})\n`;
    for(let perf of invoice.performances){
        volumeCredits += volumeCreditsFor(perf);
        result += `${playFor(perf).name} : ${usd(amountFor(perf))} (${perf.audience}석)\n`;
        totalAmount += amountFor(perf);
    }
    result += `총액: ${usd(totalAmount)}\n`;
    result += `적립 포인트: ${volumeCredits}점\n`;

    return result;
}

먼저 반복문 쪼개기로 volumeCredits 값이 누적되는 부분을 따로 빼낸다.

위를 아래처럼

function statement(invoice, plays) {
    let totalAmount = 0;
    let result = `청구 내역 (고객명 : ${invoice.customer})\n`;

    for(let perf of invoice.performances){
        //청구 내역을 출력한다.
        result += `${playFor(perf).name} : ${usd(amountFor(perf))} (${perf.audience}석)\n`;
        totalAmount += amountFor(perf);
    }
    let volumeCredits = 0;// 변수 선언을 반복문 앞으로 옮김
    for(let perf of invoice.performances){ // 값 누적 로직을 별도  for문으로 분리
        volumeCredits += volumeCreditsFor(perf);
    }
    result += `총액: ${usd(totalAmount)}\n`;
    result += `적립 포인트: ${volumeCredits}점\n`;

    return result;
}

이렇게 분리하면 임시 변수를 질의 함수로 바꾸기가 수월해진다.

함수로 추출한 것으로 변수 인라인을 한다.

    for(let perf of invoice.performances){
        //청구 내역을 출력한다.
        result += `${playFor(perf).name} : ${usd(amountFor(perf))} (${perf.audience}석)\n`;
        totalAmount += amountFor(perf);
    }
    result += `총액: ${usd(totalAmount)}\n`;
    result += `적립 포인트: ${totalVolumeCredits()}점\n`;
return result;

반복문이 중복되는 것을 꺼리는 이들이 많다.

이정도 중복은 성능에 미치는 영향이 미미할 때가 많다.

 

실제로 리펙터링한 결과를 비교해도 실행 시간 차이가 거의 없다.

 

경험 많은 프로그래머조차 코드의 실제 성능을 정확히 예측하지 못한다.

이유는 똑똑한 컴파일러들은 최신 캐싱 기법 등으로 무장하고 있어서 우리의 직관을 초월하는 결과를 내어주기 때문이다.

또한 소프트웨어 성능은 대체로 코드의 몇몇 작은 부분에 의해 결정되므로 외의 부분은 수정한다고 해도 성능 차이를 체감할 없다.

 

때로는 리팩터링이 성능에 상당한 영향을 주기도 한다. 그런 경우라도 개의치 않고 리팩터링한다.

다음어진 코드라야 성능 개선 작업도 훨씬 수월하기 때문이다.

추가로 시간내어 작업을 한다면 결과적으로 깔끔하고, 빠른 코드를 얻게 된다.

 

따라서 리팩터링으로 인한 성능 문제에 대한 저자의 조언은

"특별한 경우가 아니라면 일단 무시하라"

리팩터링 성능이 떨어진다면, 그때 성능 개선하자

 

리팩터링 중간에 테스트가 실패하고 원인을 바로 찾기 못하면 가장 최근 커밋으로 돌아가서 리팩터링의 단계를 작게 나눠서 다시 시도할 있다.

 

코드가 복잡할 수록 단계를 작게 나누면 작업 속도가 빨라지기 때문이다.

 

totalAmount 앞에 과정과 동일하게 제거한다.

반복문 쪼개고, 변수 초기화 문장을 옮기고, 함수를 추출,

	    for(let perf of invoice.performances){
	        //청구 내역을 출력한다.
	        result += `${playFor(perf).name} : ${usd(amountFor(perf))} (${perf.audience}석)\n`;
	    }
	    let totalAmount = tmpFunction();
	    function tmpFunction(){ //함수 추출/ 이름은 임시로
	        let totalAmount = 0;
	        for(let perf of invoice.performances){
	            totalAmount += amountFor(perf);
	        }
	        return totalAmount;
	    }
	
	…..
	
	
	    function totalAount(){ 
	        let result = 0; // 함수 안 변수이름도 자기 스타일에 맞게 변경
	        for(let perf of invoice.performances){
	            result += amountFor(perf);
	        }
	        return result;
	    }
	    result += `총액: ${usd(totalAount())}\n`; //함수 인라인 후 의미있는 이름으로 변경하기
	    result += `적립 포인트: ${totalVolumeCredits()}점\n`;

 

1.5 중간 점검: 난무하는 중첩 함수

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
<script src="plays.json" type="text/javascript"></script>
<script src="invoices.json" type="text/javascript"></script>
<script>

function statement(invoice, plays) {
    let result = `청구 내역 (고객명 : ${invoice.customer})\n`;
    for(let perf of invoice.performances){
        result += `${playFor(perf).name} : ${usd(amountFor(perf))} (${perf.audience}석)\n`;
    }
    result += `총액: ${usd(totalAount())}\n`; 
    result += `적립 포인트: ${totalVolumeCredits()}점\n`;
    return result;

    //중첩 함수 시작
    function totalAount(){ 
        let result = 0;
        for(let perf of invoice.performances){
            result += amountFor(perf);
        }
        return result;
    }

    function totalVolumeCredits(){
        let volumeCredits = 0;
        for(let perf of invoice.performances){ 
            volumeCredits += volumeCreditsFor(perf);
        }
        return volumeCredits;
    }
    function usd(aNumber){
        return new Intl.NumberFormat("en-US", {
            style: "currency",
            currency: "USD",
            minimumFractionDigits: 2
        }).format(aNumber/100); 
    }
    function volumeCreditsFor(aPerformance){
        let volumeCredits = 0;
        volumeCredits += Math.max(aPerformance.audience -30,0);
        if("comedy" === playFor(aPerformance).type) 
            volumeCredits += Math.floor(aPerformance.audience/5);
        return volumeCredits;
    }
    function playFor(aPerformance){
        return plays[aPerformance.playID];
    }
    function amountFor(aPerformance) {
        let result = 0;
        switch(playFor(aPerformance).type){
            case "tragedy" : 
            result = 40000;
            if(aPerformance.audience > 30){
                result += 1000 * (aPerformance.audience - 30);
            }
            break;
            case "comedy" : 
            result = 30000;
            if(aPerformance.audience > 30){
                result += 10000 + 500 * (aPerformance.audience - 20);
            }
            result += 300 * aPerformance.audience;
            break;
            default: throw new Error(`알수 없는 장르: ${playFor(aPerformance).type}`);
        }
        return result; 
    }
    
}

console.log(statement(invoices,plays));
</script>

</body>

</html>

최상위 statement()함수는 이제 일곱줄 뿐이며, 출력할 문장 생성 역할만 한다.

 

계산 로직은 여러 보조 함수로 빼냈다.

전체적인 흐름을 이해하기가 훨씬 쉬워졌다.

 

 

 

 

 

 

 
 

<!DOCTYPE html>
<html lang="ko">

<헤드>
<메타 문자셋="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>문서</title>
</헤드>

<몸>
<script src="plays.json" type="text/javascript"></script>
<script src="invoices.json" type="text/javascript"></script>
<스크립트>

기능문(인보이스, 재생) {
let result = ` 청구 내역 (고객명 : ${invoice.customer})\n`;
for(invoice.performances의 let perf){
result += `${playFor(perf).name} : ${usd(amountFor(perf))} (${perf.audience}석)\n`;
}
result += `전체: ${usd(totalAount())}\n`;
result += `적립 포인트: ${totalVolumeCredits()}점\n`;
반환 결과;

//중첩 맛있게 시작
함수 totalAount(){
let 결과 = 0;
for(invoice.performances의 let perf){
결과 += amountFor(perf);
}
반환 결과;
}

함수 totalVolumeCredits(){
let volumeCredits = 0;
for(invoice.performances의 let perf){
volumeCredits += volumeCreditsFor(perf);
}
반환 volumeCredits;
}
함수 usd(숫자){
새로운 Intl.NumberFormat("en-US", {를 반환합니다.
스타일: "통화",
통화: "USD",
minimumFractionDigits: 2
}).format(숫자/100);
}
기능 volumeCreditsFor(aPerformance){
let volumeCredits = 0;
volumeCredits += Math.max(aPerformance.audience -30,0);
if("코미디" === playFor(aPerformance).type)
volumeCredits += Math.floor(aPerformance.audience/5);
반환 volumeCredits;
}
함수 playFor(aPerformance){
반환 재생[aPerformance.playID];
}
함수 amountFor(aPerformance) {
let 결과 = 0;
switch(playFor(aPerformance).type){
사례 "비극":
결과 = 40000;
if(aPerformance.audience > 30){
결과 += 1000 * (aPerformance.audience - 30);
}
부서지다;
케이스 "코미디":
결과 = 30000;
if(aPerformance.audience > 30){
결과 += 10000 + 500 * (aPerformance.audience - 20);
}
결과 += 300 * aPerformance.audience;
부서지다;
default: throw new Error(`알수없는 장르: ${playFor(aPerformance).type}`);
}
반환 결과;
}

}

console.log(statement(invoices,plays));
</스크립트>

</body>

</html>

 

'IT책, 강의 > 리팩터링' 카테고리의 다른 글

03 - 코드에서 나는 악취  (0) 2023.07.10
02 - 리팩터링 원칙 - 02  (0) 2023.07.08
02 - 리팩터링 원칙 - 01  (0) 2023.07.06
01 - 리팩터링: 첫 번째 예시 - 02  (0) 2023.07.04
00 - 들어가며  (0) 2023.07.01

+ Recent posts