어제에 이어 오늘은 chapter 2, 리팩토링 원칙부터 진행한다. chapter 1의 경우, 간단하게 리팩토링에 대해 실습하고 감을 잡는 개념이라 별도의 정리는 없을 것이다. 2장 리팩토링 원칙에서는 리팩토링 전반에 적용되는 원칙에 대해 정리한다.
2장 리팩토링에 대한 정의로 시작한다. 리팩토링은 특정한 방식에 따라 코드를 정리하는 것만을 지칭한다. 즉 기능이 바뀌여선 안된다! 물론 이 과정에서 인터페이스가 변경될 수 있지만 전체적인 로직은 변경되선 안된다.
책에서는 "리팩터링을 하다가 코드가 깨져서 며칠이나 고생했다"라고 한다면 이는 리팩터링을 한 것이 아니다"라고 표현한다.
예를 들면 1장에서 리팩토링한 코드를 봤을 때, 리팩토링 전 후로 main 로직은 변경이 없다. 왜냐하면 내가 리팩토링한 객체는 statement 코드고, statement의 구현에 직접적인 연관이 없는 main 코드는 변경이 일어나선 안된다!
//================== 리팩토링 전 main ==================
const plays = fs.readFileSync('ch1/data/plays.json', 'utf8');
const invoices = fs.readFileSync('ch1/data/invoices.json', 'utf8');
const playsJson = JSON.parse(plays);
const invoicesJson = JSON.parse(invoices);
for(let invoice of invoicesJson){
const s = new statement(invoice, playsJson)
const result = s.run()
console.log(result)
}
//================== 리팩토링 후 main ==================
const plays = fs.readFileSync('ch1/data/plays.json', 'utf8');
const invoices = fs.readFileSync('ch1/data/invoices.json', 'utf8');
const playsJson = JSON.parse(plays);
const invoicesJson = JSON.parse(invoices);
for(let invoice of invoicesJson){
const s = new statement(invoice, playsJson)
const result = s.run()
console.log(result)
}
// 둘의 차이는 없다!!! <- 이게 리팩토링의 핵심!!!
2장에서 나름? 재미있게 본 부분이 있다. 리팩토링과 성능 최적화를 비교한 부분이다. 리팩토링과 성능 최적화는 둘 다 코드를 바꾼다는 공통점이 있다. 그러나 둘의 목표가 다른데 리팩토링의 경우, 코드를 이해하고 수정하기 쉽게 변경하는 것을 목표로 한다. 반면 성능 최적화의 경우, 오로지 속도 개선에만 신경을 쓴다.
const sort = (inputList) => {
if (inputList.length <= 1)
return inputList;
let pivot = inputList[0];
inputList.shift();
/** 예시 1번, for문 한번으로 해결 O(N logN) */
let left = [];
let right = [];
for(var i = 0; i <= inputList.length-1; i++){
if (inputList[i] <= pivot){
left.push(inputList[i]);
}else {
right.push(inputList[i]);
}
}
/** 예시 2번, filter를 사용해서 inputList를 두번 탐색하여 해결 O(2N logN) */
let left = inputList.filter(x => x <= pivot);
let right = inputList.filter(x => x > pivot);
return sort(left) + pivot + sort(right);
}
// ============== TEST =================
var test = [8,2,4,3,5,6,7,1,9,10,11];
var result = sort(test);
console.log(result);
위 코드는 quick sort를 구현한 코드이다. quick sort는 pivot을 중심으로 작은 것은 왼쪽, 큰 것은 오른쪽으로 정렬시키는 것으로 left, right을 나누는 것이 중요하다.
먼저 첫 번째 구현 코드를 살펴보면 for문 한 번으로 left, right를 나눈다. 따라서 시간 복잡도는 O(N)이다. 반면 그 아래에 filter를 사용한 코드를 보면 left 때 한 번, right 때 한 번, 총 두 번의 탐색을 진행하기 때문에 O(2N)의 시간복잡도를 가진다. 물론 O(N)과 O(2N)은 큰 차이를 보이지 않지만 성능 최적화가 필요한 경우에는 for문 한 번으로 구현해야한다. 그러나 코드 가독성을 생각한다면 밑의 코드가 더 가독성이 높다!
정리해보자면 성능 최적화와 리팩토링은 모두 코드를 수정하는 작업이지만 성능 최적화는 예시 1번을 목표로, 리팩토링은 예시 2번을 목표로 한다는 차이가 있다.
리팩토링은 기존의 동작 방식을 변경하지 않고 코드를 정리하는 작업으로 성능 최적화와는 다르게 코드를 이해하고 수정하기 쉽게 변경하는 것을 목표로 한다!!!