소프트웨어의 겉보기 동작은 그대로 유지한 채,
코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법
[마틴 파울러, 리팩터링 2판, 한빛미디어]
나쁜 코드에서는 악취가 난다고 하고 이를 Code Smell이라고 부름. 켄트 백과 마틴 파울러가 90년대 사용해서 유명해진 용어.
1. 너무 큰 함수나 클래스
2. 이름이 명확하지 않은 함수나 변수 이름. 지나치게 짧은 것보다는 조금 긴 게 나음. 개인 프로젝트거나 구성원 간 협의만 있으면 차라리 한국어로 작성해도 좋다고 생각. 어차피 번들러와 미니파이어가 다른 이름으로 바꿀 거거든.
3. 중복 코드: 같은 일을 하는 코드가 여기저기 산재해있으면 수정
4. 전역 변수: 가능하면 함수 내 혹은 모듈 내에 변수를 두자
5. 과도한 콜백. 조건문 중첩
6. 과도하게 긴 식별자
리팩터링 하기 전에 테스트 케이스부터 작성하자.
리팩토링하면서는 코드 작성에 집중하느라 테스트를 해피 패스(좋은 사례)만 해보고 말기도 함.
테스트없는 리팩터링은 끔찍하다 -> 반드시 테스트 해보기
function processPosts() {
const posts = getPosts();
...
posts.forEach(post => {
console.log(post);
});
...
}
to
function logPosts(posts) {
posts.forEach(post => {
console.log(post);
});
}
function processPosts() {
const posts = getPosts();
...
logPosts(posts);
...
}
const user = getUser();
if (user.authKey) {
...
}
to
const user = getUser();
const isLoggedIn = Boolean(user.authKey);
if (isLoggedIn) {
...
}
조금 복잡한 계산이 필요하다면 함수로 만드는 방법도 있음
const user = getUser();
const purchases = getPurchaseHistory(user);
// 지금까지 구매 내역이 없는 한국어 사용자에게 이벤트 배너 표시
if (user.authKey && user.locale === 'kr' && purchases.length === 0) {
showEventBanner();
}
to
function isEligibleForEvent(user) {
const purchases = getPurchaseHistory(user);
const isLoggedIn = Boolean(user.authKey);
const isKoreanUser = user.locale === 'kr';
const hasPurchaseHistory = purchases.length > 0;
return ( isLoggedIn && isKoreanUser && ! hasPurchaseHistory );
}
const user = getUser();
if (isEligibleForEvent(user)) {
showEventBanner();
}
이 사용자가 이벤트 배너를 보여주기에 적합한가 명확하게 보여주는 코드로 변경
var가 보이거든 let, const로 변환. let 보다는 const 위주로 사용. let이 보인다면 꼭 필요한 것인지(반드시 변환이 되어야 하는 변수인지) 다시 한 번 고려.
함수의 사이드 이펙트 최소화. 가능하면 순수 함수로 작성하자.
순수함수 : 똑같은 입력값이 주어졌을 때 똑같은 출력을 하는 함수
조건식 통합: 책에서는 '조건식 통합'이랬지만 차라리 분리하고 함수로 나누는 방법을 오히려 추천. 코드가 더 명확함. 그렇지 않다면 변수 이름으로 받아서 작성하는 방법도 있음. 들여쓰기와 괄호 중괄호 조합때문에 오히려 지저분하게 느껴지기도 함
빠른 실패: fast fail. 들여쓰기 단계를 줄여줌
반복문 보다는 파이프: 이해하기 쉬워서.
*파이프라인 코드 : filter, map 등
const names = [];
for (const i of input) {
if (i.job === 'programmer') {
names.push(i.name);
}
}
const names = input.filter(i => i.jpb === 'programmer').map(i => i.name);
// 비슷한 사례
const items = [1, 2, 3];
for (let i = 0; i < items.length; i++) {
let item = items[i];
console.log(item);
}
[1, 2, 3].forEach( item => {
console.log(item);
} );
굳이 따지자면 성능면에서는 손해인데 막 수만건씩 다루는 게 아닌 이상 없다고 봐야 할 정도이므로 걱정말고 쓰기.
조건, 반복문에 중괄호는 꼭 쓰는 편. 한 줄에 표현할 정도로 짧고 명확하면 생략하기도 함.
switch 대신 object literal


//변하지 않은 것으로 간주되는 코드
const blackPink = [ '지수', '제니', '로제', '리사' ];
blackPink.forEach((value, index) => {
blackPink[index] += '❤️';
});
console.log(blackPink);
to
//새로운 변수를 만들어 새롭게 배열을 작성. 기존의 blackPink라는 배열도 그대로 유지되고 blackPinkWithLove라는 새로운 값으로 저장됌
const blackPink = [ '지수', '제니', '로제', '리사' ];
const blackPinkWithLove = blackPink.map((value, index) => {
blackPink[index] += '❤️';
});
console.log(blackPinkWithLove);
함수에 전달된 파라미터도 마찬가지. 앞서 순수 함수처럼 작성하자는 것과도 같은 맥락.
