💡 핵심 인사이트
리팩토링은 마틴 파울러(Martin Fowler)가 집대성한 개념으로, **"소프트웨어가 겉으로 작동하는 결과(외부 동작)는 단 1%도 바꾸지 않은 채, 오직 내부의 냄새나는 소스 코드 구조(내부 설계)만을 뜯어고쳐 깔끔하고 이해하기 쉽게 만드는 코드 대청소 작업"**입니다.
기능 추가나 버그 수정이 절대 아닙니다. 오직 가독성과 유지보수성을 높이기 위한 작업입니다.
Ⅰ. 리팩토링의 목적: 왜 작동하는 코드를 건드리는가?
"돌아가는 코드는 절대 건드리지 마라"라는 개발자들의 오랜 격언이 있습니다.
하지만 시간에 쫓겨 대충 붙여 넣은 코드, 이름이 a1, b2로 지어진 변수, 1,000줄이 넘어가는 거대한 함수들을 그대로 방치하면 이것들이 빚(Technical Debt, 기술 부채)으로 쌓입니다.
결국 1년 뒤 새로운 기능을 추가하려 할 때, 스파게티처럼 꼬인 코드를 분석하느라 개발이 아예 마비되는(소프트웨어 부패) 사태가 옵니다.
리팩토링의 목적:
- 가독성 향상: 남이 봐도 1초 만에 이해할 수 있는 아름다운 코드로 만듭니다.
- 버그 탐지: 코드를 쪼개고 정리하다 보면 숨어있던 논리적 오류가 스스로 모습을 드러냅니다.
- 개발 속도(생산성) 유지: 당장은 정리하느라 시간이 걸리지만, 뼈대가 튼튼해져 향후 새로운 기능을 추가할 때의 속도가 10배 빨라집니다.
Ⅱ. 코드의 악취 (Code Smells)와 리팩토링 기법
마틴 파울러는 리팩토링해야 할 쓰레기 코드의 징후를 **'코드의 악취(Smell)'**라고 불렀습니다.
대표적인 악취와 해결 기법 (리팩토링 카탈로그)
- 중복된 코드 (Duplicated Code)
- 증상: A 클래스에도, B 클래스에도 토씨 하나 안 틀리고 똑같은 로직이 복사 붙여넣기 되어 있습니다.
- 리팩토링: 메서드 추출 (Extract Method). 그 코드를 빼내어 별도의 공통 함수로 만들고 양쪽에서 호출(Call)하게 만듭니다.
- 거대한 함수 (Long Method)
- 증상: 함수 하나가 수백 줄에 달해, 이 함수가 무슨 일을 하는지 스크롤을 한참 내려야 알 수 있습니다.
- 리팩토링: 10~20줄 단위의 짧고 의미 있는 이름을 가진 여러 개의 작은 함수들로 쪼갭니다(Extract).
- 직관적이지 않은 이름
- 증상: 변수명이
int d;라고 되어 있어 무엇을 의미하는지 아무도 모릅니다. - 리팩토링: 이름 변경 (Rename).
int elapsedTimeInDays;로 직관적으로 바꿉니다. (가장 훌륭하고 빈번한 리팩토링)
- 증상: 변수명이
- 거대한 클래스 (Large Class)
- 증상: 클래스 하나가 유저 정보도 관리하고, 장바구니도 결제하고, 메일도 발송하는 등 너무 많은 책임(신의 객체, God Object)을 집어삼켰습니다.
- 리팩토링: 클래스 추출 (Extract Class). 단일 책임 원칙(SRP)에 따라 책임을 분리하여 여러 개의 독립적인 클래스로 쪼갭니다.
Ⅲ. 리팩토링의 절대 원칙 (Two Hats)
개발자는 코딩할 때 **'두 개의 모자'**를 씁니다. 하나는 '기능 추가 모자'이고, 다른 하나는 '리팩토링 모자'입니다. 이 두 모자를 동시에 쓰면 절대 안 됩니다.
- 기능을 추가할 때는 코드 구조가 엉망이 되더라도 일단 돌아가게 기능만 짭니다.
- 기능을 다 짠 뒤, 모자를 바꿔 쓰고 리팩토링할 때는 새로운 기능을 절대 추가하지 않습니다. 오직 코드의 구조만 깎고 다듬습니다.
- 안전망 필수: 앞서 배운 **자동화된 단위 테스트(TDD)**가 없다면, 리팩토링은 "멀쩡한 시스템을 부수어버리는 미친 짓"이 됩니다. 테스트 코드가 백업해 주어야만 과감하게 내부 구조를 갈아엎을 수 있습니다.
📢 섹션 요약 비유: 리팩토링은 식당의 **'주방 대청소 및 동선 재배치'**입니다. 이 청소를 한다고 해서 손님상에 나가는 돈가스(외부 동작 기능)의 맛이 고기반찬으로 바뀌는 것은 아닙니다. 하지만 프라이팬 위치와 조미료 통을 깔끔하게 재배치(내부 구조 개선)해 두면, 내일 치킨을 새로 튀길 때 요리사(개발자)의 속도가 3배는 빨라지게 됩니다.