점진적 리팩터
점진적 리팩터
한 번에 큰 리팩터로 모든 문제를 해결하려는 시도는 자주 실패합니다. 작은 단위로 꾸준히 정리하는 패턴이 더 안정적인 결과를 낸다는 평이 흔합니다. 이 글은 Boy Scout Rule 과 Strangler Fig Pattern.
1. Boy Scout Rule
Robert C. Martin ("Uncle Bob") 의 책 Clean Code (2008) 14 장 즈음에 인용한 표어. 원문은 미국 보이스카우트의 캠프장 규칙에서 따 온 것:
"Always leave the campground cleaner than you found it."
코드에 적용하면 "자신이 만지는 코드는 들어왔을 때보다 조금이라도 깨끗한 상태로 남기라". 핵심 둘:
- 작은 정리 — 변수 이름 하나, 죽은 import 한 줄, 누락 주석 한 줄. 큰 리팩터를 별도 작업으로 나누지 않고 일상 작업에 녹임.
- 자기가 만지는 곳 — 무관한 파일을 청소하지 않음. 변경의 범위가 PR 의 의도를 흐리지 않도록.
이 규칙의 실용적 가치:
- 큰 리팩터 일정 없이도 코드 품질이 시간이 갈수록 평균적으로 올라감.
- 매번의 변경이 작아 리뷰가 빠르고 회귀 위험이 작음.
- 큰 변경의 정당성을 한꺼번에 설득해야 하는 정치적 부담이 없음.
자주 인용되는 한계:
- 모든 변경이 정리이면 PR 의 의도가 흐려짐. 본 작업과 정리는 가능한 한 별도 커밋으로 구분.
- "내가 만진다" 의 경계가 흐리면 무관한 변경이 PR 에 섞임.
2. Strangler Fig Pattern
Martin Fowler 가 자신의 블로그에 정리한 패턴. Fowler 는 2001 년 호주 퀸즐랜드의 우림에서 본 교살 무화과나무에서 비유를 얻었다고 적었고, 글의 가장 최근 갱신은 2024 년 8월 22일자.
비유 — 교살 무화과는 숙주 나무 위에서 발아해 자라며, 점점 영양분을 흡수하다가 결국 자립. 원래 나무는 사라짐. 이 점진적 교체가 레거시 시스템 현대화와 닮음.
적용:
- 새 시스템 (또는 새 모듈) 을 레거시 옆에 둠.
- 호출 진입점에서 분기해 일부 트래픽만 새 시스템으로 보냄 (facade · proxy · API gateway).
- 새 시스템이 한 기능씩 인수.
- 모든 기능이 옮겨지면 레거시를 폐기.
이 패턴이 풀어 주는 문제:
- "Big rewrite" (Joel Spolsky 가 2000 년 글 Things You Should Never Do, Part I 에서 비판) 의 위험을 줄임. 한 번에 다시 쓰는 시도는 자주 일정을 초과하고 사용자 가치 전달이 멈춤.
- 레거시가 동작하는 동안 새 시스템도 운영되며, 양쪽이 같은 트래픽 패턴에 노출되어 문제를 일찍 발견.
- 롤백이 점진적. 한 기능에 문제가 있으면 그 기능만 레거시로 되돌림.
3. 작은 단위 정리
코드 단위:
- 의미 없는 약어 변수명을 의미 있는 이름으로 (
d→daysInMonth). - 큰 함수에서 한 책임을 추출.
- 죽은 코드 (쓰이지 않는 export · 도달 불가 분기) 제거.
- 매직 넘버를 이름 있는 상수로.
- 중첩 if 를 early return 으로.
시스템 단위 (Strangler 적용):
- 한 엔드포인트만 새 서비스로 라우팅.
- 한 테이블만 새 스키마로 더블 라이트 (dual-write) 한 뒤 검증, 그 후 읽기 트래픽 전환.
- 한 모듈만 새 빌드 시스템으로 옮기고, 점차 확대.
4. 다른 길
전면 재작성 (Big Rewrite) 이 항상 잘못은 아닙니다. 의존이 너무 얕거나 시스템이 작거나, 레거시가 더 이상 운영 가능하지 않은 경우에는 재작성이 합리적일 수 있음. Strangler 도 결국 레거시와 신규를 동시에 운영하는 비용을 부름.
선택은 환경에 따라:
- 시스템이 운영 중이고 사용자가 있음 → Strangler 가 안전.
- 사용자가 없고 코드도 작음 → 재작성이 더 빠를 수 있음.
- 변경 영향 범위가 극히 좁음 → Boy Scout Rule 만으로 충분한 경우가 많음.
5. 자주 쓰는 모양
코드 리뷰에서 권장되는 PR 패턴:
PR #123: feat(auth): add password reset
- 본 작업: 비밀번호 재설정 엔드포인트 추가
- 부수: 같은 파일의 미사용 import 제거 (boy scout)
부수 변경은 본 작업의 같은 파일 · 인접 코드로 한정. 무관한 디렉터리는 별도 PR.
Strangler 의 facade 한 예 (아주 단순화):
// 기존 모놀리스 라우트
app.use("/api/users", legacyUsersRouter);
app.use("/api/orders", legacyOrdersRouter);
// orders 만 새 서비스로 라우팅
app.use("/api/orders", proxy("https://orders-v2.internal"));
6. 자주 걸리는 자리
Boy Scout Rule 을 명분으로 PR 에 무관한 정리가 대량 섞이는 사고 — 리뷰 시간이 늘고 회귀 원인 찾기가 어려워짐. "본 작업 1 개 + 부수 정리 소량" 으로 자제.
Strangler 의 일시적 양립 기간 (레거시 + 신규) 이 영원이 되는 경우 — 마이그레이션 종료 시점 · 기능 인수 KPI 를 처음부터 정해 두는 편이 안전.
더블 라이트 (dual-write) 가 두 시스템의 상태를 어긋나게 만드는 사고 — 한쪽이 권위 (SSOT) 임을 명확히 하고, 사고 시 어느 쪽으로 회복하는지 합의가 필요.
큰 리팩터의 유혹 — "한 번만 깔끔하게 다시 쓰면 된다" 는 자주 일정 초과. Mythical Man-Month (Brooks, 1975) 의 "second-system effect" 가 같은 함정.
하고픈 말
점진적 리팩터는 일관된 품질 향상의 가장 안전한 길. Boy Scout Rule 의 작은 정리 + Strangler 의 점진 교체 두 패턴이 함께 있을 때, 큰 리팩터의 위험 없이 코드와 시스템이 꾸준히 좋아집니다. "한 번에 다시 쓰자" 는 유혹은 거의 항상 비싸게 끝남.
Next
- docs-for-agent-and-human
- korean-first
Martin Fowler Strangler Fig Application · Robert C. Martin Clean Code · Joel Spolsky Things You Should Never Do, Part I · Microsoft Cloud Adoption Strangler Fig · Sam Newman Monolith to Microservices 를 참고합니다.