function printOwing(invoice) {
printBanner();
let outstanding = calculateOtustanding();
// 세부사항 출력
console.log(`고객명: ${invoice.customer}`);
console.log(`체무액: ${outstanding}`);
함수 추출하기 진행 후
function printOwing(invoice) {
printBanner();
let outstanding = calculateOtustanding();
funtion printDetails(outstanding) {
console.log(`고객명: ${invoice.customer}`);
console.log(`체무액: ${outstanding}`);
}
}
🧷 배경
코드 조각을 찾아 무슨 일을 하는지 파악한 다음, 독립된 함수로 추출하고 목적에 맞는 이름을 붙인다.
📍 언제 독립된 함수로 묶어야 할까?
길이를 기준
재사용성
목적과 구현을 분리: 코드를 보고 무슨 일을 하는지 파악하는 데 한참이 걸리는 경우
📍 함수가 짧아지면 함수 호출이 많아져서 성능이 느려질까?
함수가 짧으면 캐싱하기가 더 쉽기 때문에 컴파일러가 최적화하는데 유리한 경우가 더 많다. 짧은 함수의 이점은 이름을 잘 지어야 발휘된다. 긴 함수의 코드 덩어리의 주석을 참고하면 도움된다.
캐싱하기 쉬운 경우는 어떤 경우일까?
🧷 절차
함수를 새로 만들고 목적을 잘 드러내는 이름을 붙인다. (어떻게가 아닌 무엇을 하는지가 드러나야 한다.)
추출할 코드를 원본 함수에서 복사하여 새 함수에 붙여넣는다.
추출한 코드 중 원본 함수의 지역 변수를 참조하거나 추출한 함수의 유효범위를 벗어나느 변수는 없는지 검사한다. 있다면 매개변수로 전달한다.
변수를 다 처리했다면 컴파일 한다.
원본 함수에서 추출한 코드 부분을 새로 만든 함수를 호출하는 문장으로 바꾼다. 추출한 함수로 일을 위임한다.
테스트한다.
다른 코드에 방금 추출한 것과 똑같거나 비슷한 코드가 없는지 살핀다. 있다면 방금 추출한 새 함수를 호출하도록 바꿀지 검토한다. 인라인 코드를 함수 호출로 바꾼다.
🧷 리팩터링 전
function printOwing(invoice) {
let outstanding = 0;
console.log("********");
console.log("**고객 채무**");
console.log("********");
//미해결 채무(outstanding) 계산
for(const o of invoice.orders) {
outstanding += 0.amountl
}
//마감일(dueDate)을 기록한다.
const today = Clock.today;
invoice.dueDate = new Date(today.getFullYear(), today.getMonth(),
today.getDate() + 30);
//세부 사항을 출력한다.
console.log(`고객명: ${invoice.customer}`);
console.log(`채무액: ${outstanding}`);
console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`);
}
🧷 예시: 유효범위를 벗어나는 변수가 없을 때
Banner 출력하는 코드 추출하기. 해당 코드를 잘라내서 새 함수에 붙이고, 원래 자리에 새 함수 호출문을 넣는다.
function printOwing(invoice) {
let outstanding = 0;
printBanner(); // 배너 출력 로직을 함수로 추출
//미해결 채무(outstanding) 계산
for(const o of invoice.orders) {
outstanding += 0.amountl
}
//마감일(dueDate)을 기록한다.
const today = Clock.today;
invoice.dueDate = new Date(today.getFullYear(), today.getMonth(),
today.getDate() + 30);
//세부 사항을 출력한다.
console.log(`고객명: ${invoice.customer}`);
console.log(`채무액: ${outstanding}`);
console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`);
function printBanner () {
console.log("********");
console.log("**고객 채무**");
console.log("********");
}
}
마찬가지로, 세부 사항을 출력하는 코드도 간단히 추출한다.
function printOwing(invoice) {
let outstanding = 0;
printBanner(); // 배너 출력 로직을 함수로 추출
//미해결 채무(outstanding) 계산
for(const o of invoice.orders) {
outstanding += 0.amountl
}
//마감일(dueDate)을 기록한다.
const today = Clock.today;
invoice.dueDate = new Date(today.getFullYear(), today.getMonth(),
today.getDate() + 30);
printDetails(); // 세부 사항 출력 로직을 함수로 추출
function printBanner () {
console.log("********");
console.log("**고객 채무**");
console.log("********");
}
function printDetails () {
console.log(`고객명: ${invoice.customer}`);
console.log(`채무액: ${outstanding}`);
console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`);
}
}
🧷 예시: 지역 변수를 사용할 때
지역 변수와 관련하여 가장 간단한 경우는 변수를 사용하지만 다른 값을 다시 대입하지는 않을 때다. 이 경우에는 지역 변수들을 그냥 매개변수로 넘기면 된다.
세부사항을 출력하는 코드를 지역 변수 두 개를 매개변수로 받는 함수로 추출한다.
function printOwing(invoice) {
let outstanding = 0;
printBanner(); // 배너 출력 로직을 함수로 추출
//미해결 채무(outstanding) 계산
for(const o of invoice.orders) {
outstanding += 0.amountl
}
//마감일(dueDate)을 기록한다.
const today = Clock.today;
invoice.dueDate = new Date(today.getFullYear(), today.getMonth(),
today.getDate() + 30);
printDetails(invoice, outstanding); // 지역변수를 매개변수로 전달
function printBanner () {
console.log("********");
console.log("**고객 채무**");
console.log("********");
}
function printDetails (invoice, outstanding) {
console.log(`고객명: ${invoice.customer}`);
console.log(`채무액: ${outstanding}`);
console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`);
}
}
마감일을 설정하는 코드를 추출한다.
function printOwing(invoice) {
let outstanding = 0;
printBanner(); // 배너 출력 로직을 함수로 추출
//미해결 채무(outstanding) 계산
for(const o of invoice.orders) {
outstanding += 0.amountl
}
recordDueDate(invoice); // 마감일 설정 로직을 함수로 추출
printDetails(invoice, outstanding); // 지역변수를 매개변수로 전달
function printBanner () {
console.log("********");
console.log("**고객 채무**");
console.log("********");
}
function printDetails (invoice, outstanding) {
console.log(`고객명: ${invoice.customer}`);
console.log(`채무액: ${outstanding}`);
console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`);
}
function recordDueDate (invoice) {
const today = Clock.today;
invoice.dueDate = new Date(today.getFullYear(), today.getMonth(),
today.getDate() + 30);
}
}
🧷 예시: 지역 변수의 값을 변경 할 때
지역 변수에 값을 대입하면 문제가 복잡해진다. 변수가 추출한 함수 밖에서 사용되는 경우 변수에 대입된 새 값을 반환해야 한다.
function printOwing(invocie) {
let outstanding = 0;
printBanner();
//미해결 채무(outstanding) 계산
for(const o of invoice.orders) {
outstanding += 0.amountl
}
recordDueDate(invoice);
printDetails(invoice, outstanding);
}
선언문을 변수가 사용되는 코드 근처로 슬라이드 한다.
function printOwing(invocie) {
printBanner();
//미해결 채무(outstanding) 계산
let outstanding = 0; // 맨 위에 있던 선언문을 이 위치로 이동
for(const o of invoice.orders) {
outstanding += 0.amountl
}
recordDueDate(invoice);
printDetails(invoice, outstanding);
}
추출할 부분을 새로운 함수로 복사한다.
function printOwing(invocie) {
printBanner();
//미해결 채무(outstanding) 계산
let outstanding = 0; // 맨 위에 있던 선언문을 이 위치로 이동
for(const o of invoice.orders) {
outstanding += 0.amountl
}
recordDueDate(invoice);
printDetails(invoice, outstanding);
}
function calculateOutstanding (invoice) {
let outstanding = 0; // 추출할 코드 복사
for(const o of invoice.orders) {
outstanding += 0.amount;
}
return outstanding; // 수정된 값 반환
}
outstanding의 선언문을 추출할 코드 앖으로 옮겼기 때문에 매개변수로 전달하지 않아도 된다. 추출할 코드에서 값이 변경된 변수는 outstanding뿐이므로 이 값을 반환한다.
function printOwing(invocie) {
printBanner();
const outstanding = calculateOutstanding(invoice); // 함수 추출 완료. 추출한 함수가 반환한 값을 원래 변수에 저장한다.
for(const o of invoice.orders) {
outstanding += 0.amountl
}
recordDueDate(invoice);
printDetails(invoice, outstanding);
}
function calculateOutstanding (invoice) {
let result = 0; // 변수 이름 변경
for(const o of invoice.orders) {
outstanding += 0.amount;
}
return result;
}
원본 변수인 outstanding에 const를 붙여 불변으로 만들었다.
🧷 리팩터링 후
function printOwing(invoice) {
printBanner();
const outstanding = calculateOutstanding(invoice);
recordDueDate(invoice);
printDetails(invoice, outstading);
}
function printBanner () {
console.log("********");
console.log("**고객 채무**");
console.log("********");
}
function calculateOutstanding (invoice) {
let result = 0;
for(const o of invoice.orders) {
outstanding += 0.amount;
}
return result;
}
function recordDueDate (invoice) {
const today = Clock.today;
invoice.dueDate = new Date(today.getFullYear(), today.getMonth(),
today.getDate() + 30);
}
function printDetails (invoice, outstanding) {
console.log(`고객명: ${invoice.customer}`);
console.log(`채무액: ${outstanding}`);
console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`);
}