기술 부채의 해부학: 리팩토링을 설계의 언어로 다시 쓰기
1 views
# 기술 부채의 해부학: 리팩토링을 설계의 언어로 다시 쓰기
오래된 코드베이스를 처음 마주하는 감각은 고고학자의 그것과 닮았다. 한 줄 한 줄에 어떤 시대의 결정과 어떤 마감 앞에서 타협한 흔적, 그리고 어떤 신입의 첫 커밋이 층층이 박혀 있다. 우리는 그 지층을 흔히 한 단어로 부른다. 기술 부채.
그러나 이 단어는 너무 많은 것을 가린다.
## 부채라는 은유의 오해
워드 커닝햄이 1992년 OOPSLA 경험 보고서에서 'technical debt' 개념을 처음 제시했을 때, 그가 말한 것은 '나쁜 코드'가 아니었다. 그는 자신이 이해한 만큼만 코드에 반영하고, 이해가 깊어지면 코드를 다시 쓴다는 사실을 금융 부채에 빗대었다. 즉 **부채란 미완의 이해를 잠시 빌려 쓰는 행위**였지, 부주의의 결과물이 아니었다. 빌렸으면 갚아야 하고, 갚지 않으면 이자가 불어난다. 핵심은 처음부터 갚을 의도가 있어야 한다는 점이다.
현장에서 이 은유는 자주 오용된다. 누군가는 모든 레거시를 부채라 부르고, 누군가는 자신의 게으름을 부채라는 이름으로 정당화한다. 마틴 파울러는 이를 정리하기 위해 '기술 부채 사분면'을 제안했다. 의도적인가 우발적인가, 신중한가 경솔한가. 같은 추한 코드라도 "지금은 이 트레이드오프를 안다, 다음 분기에 갚는다"라고 적힌 코드와 "왜 이렇게 됐는지 아무도 모른다"라고 적힌 코드는 전혀 다른 종(種)이다.
부채의 종류를 식별하지 못한 채 시작하는 리팩토링은 가계부 없이 가난을 한탄하는 일과 같다.
## 부채는 어디에 쌓이는가
부채가 가장 깊게 고이는 곳은 코드 그 자체가 아니다. 코드와 코드 사이, 모듈과 모듈 사이의 **경계**다. 단일 함수가 길고 추한 것은 견딜 만하다. 그 함수가 무엇을 책임지는지 명확하기만 하다면. 정말로 위험한 부채는 '이 모듈이 무엇인지' 아무도 한 문장으로 말하지 못할 때 생긴다.

세 가지 신호로 정리할 수 있다.
첫째, **명명의 붕괴**다. `UserService`가 결제 영수증을 발행하고 `OrderManager`가 이메일을 보낸다. 이름과 행위가 어긋난 코드는 읽는 사람의 모델과 실행되는 모델 사이에 끊임없는 시차를 만든다. 시차는 버그가 자라는 토양이다.
둘째, **변경의 비대칭성**이다. 한 가지 비즈니스 변경을 위해 일곱 개의 파일을 동시에 수정해야 한다면, 그 일곱 개는 사실상 하나의 모듈이다. 다만 우리가 그 사실을 인정하지 않고 있을 뿐이다. 데이비드 파나스가 1972년 "모듈은 변경의 비밀을 감추는 단위"라고 말한 이래, 이 진술의 무게는 한 번도 가벼워진 적이 없다.
셋째, **공포의 분포**다. 어떤 디렉터리에 들어가면 모두가 손을 떤다. 테스트가 없거나, 있어도 신뢰하지 못하거나, 마지막으로 그곳을 이해했던 사람이 퇴사했기 때문이다. 공포는 측정 가능한 지표가 아니지만, 어떤 정적 분석 도구보다 정확하게 부채의 위치를 가리킨다.
## 리팩토링은 무엇이 아닌가
리팩토링이라는 단어는 자주 두 가지와 혼동된다. 재작성, 그리고 청소.
재작성은 매혹적이다. 처음부터 다시 짜면 모든 것이 깨끗할 것 같다. 조엘 스폴스키가 2000년 넷스케이프 사례를 두고 "절대 해서는 안 될 단 하나의 일"이라 단언했던 것이 바로 이 유혹이다. 재작성된 코드는 새로 짠 코드가 아니라, 옛 코드가 지난 5년 동안 처리해 온 수천 개의 엣지 케이스를 모두 잃어버린 코드다. 깨끗한 것이 아니라 비어 있는 것이다.
청소도 다르다. 변수명을 다듬고, 들여쓰기를 정리하고, 죽은 코드를 지우는 일은 위생이지 설계가 아니다. 마틴 파울러의 정의를 빌리면, 리팩토링은 **외부 동작을 보존한 채 내부 구조를 바꾸는 행위**다. 핵심은 두 가지다. 동작이 같다는 것을 어떻게 보증할 것인가, 그리고 무엇을 향해 구조를 바꿀 것인가.
전자의 답이 테스트이고, 후자의 답이 설계 의도다. 테스트 없이 시작하는 리팩토링은 도박이고, 의도 없이 시작하는 리팩토링은 산책이다.
## 점진적 회복의 기술
큰 시스템에서 리팩토링은 결코 한 번의 사건이 아니다. 일상의 결에 스며드는 습관이며, 코드를 만질 때마다 직전보다 조금 더 나은 상태로 두고 떠나는 규율이다. 이를 '보이스카우트 규칙'이라 부른다. 캠프장을 떠날 때는 도착했을 때보다 깨끗하게.
실무에서 이 규율은 몇 가지 구체적 패턴으로 굳어졌다.
**스트랭글러 무화과(Strangler Fig)**. 마틴 파울러가 호주 열대우림의 무화과나무에서 빌려온 이름이다. 그 나무는 숙주를 한 번에 쓰러뜨리지 않는다. 줄기를 타고 천천히 감싸 오르다가, 숙주가 시들 즈음에는 이미 스스로 설 수 있다. 레거시 시스템을 대체할 때도 마찬가지다. 새 시스템을 만들고, 트래픽을 한 엔드포인트씩 옮기고, 마지막 호출이 사라질 때 옛 시스템을 끈다. 빅뱅의 야망을 버린 자리에서 안전이 자란다.
**경계 추출(Extract Boundary)**. 곧장 클래스를 쪼개거나 마이크로서비스로 분리하기 전에, 먼저 인터페이스를 추출한다. 호출부와 구현부 사이에 얇은 막을 세우는 일이다. 그 막이 생긴 순간부터 양쪽은 독립적으로 진화할 수 있다. 분리는 그다음 문제다.
**핫스팟 분석(Hotspot Analysis)**. 아담 톤힐이 저서 *Your Code as a Crime Scene*에서 정리한 방법으로, 변경 빈도와 복잡도를 교차해 부채의 우선순위를 정한다. 변경되지 않는 추한 코드는 두어도 좋다. 매주 손이 가는 추한 코드가 진짜 비용이다. 부채에도 이자율이 다르다.
**기능 토글(Feature Toggle)**. 새 구조와 옛 구조를 동시에 운영하며 런타임에 트래픽을 갈아끼운다. 롤백이 한 줄의 설정으로 가능해지는 순간, 리팩토링은 도박에서 실험으로 격이 바뀐다.
## 부채를 갚지 않을 권리
여기서 흔히 빠뜨리는 진실이 있다. 모든 부채를 갚을 필요는 없다.
3년 뒤에 폐기될 모듈에 깔끔한 추상화를 새로 입히는 일은 떠날 집을 리모델링하는 것과 같다. 다 옳지만 다 낭비다. 리팩토링의 가치는 코드의 미학에서 오지 않는다. 그 코드에 앞으로 가해질 변경의 총량에서 온다. 변경되지 않을 코드에 들이는 리팩토링은 자기만족이고, 매일 변경되는 코드에 미루는 리팩토링은 복리로 불어나는 빚이다.
이 판단을 위해서는 코드를 '자산'과 '운영비'로 나눠 보는 회계적 시선이 필요하다. 어떤 모듈은 향후 수년간 가치를 만들어 낼 핵심 자산이고, 어떤 모듈은 다음 분기에 사라질 운영비다. 같은 더러움이라도 자산의 더러움은 청구서이고, 운영비의 더러움은 영수증이다.
> "지지자불여호지자, 호지자불여낙지자(知之者不如好之者, 好之者不如樂之者)." — 『논어』 옹야편
아는 것은 좋아하는 것만 못하고, 좋아하는 것은 즐기는 것만 못하다. 부채를 아는 단계, 갚기를 원하는 단계, 갚는 일 자체를 설계의 즐거움으로 삼는 단계는 결코 같지 않다. 마지막 단계에 이른 팀에게 리팩토링은 비용이 아니라 리듬이다.
## 유지보수 가능성이라는 환상과 실재
유지보수 가능한 코드란 무엇인가. 흔한 답은 "읽기 쉬운 코드"다. 그러나 이 답은 너무 평면적이다. 코드는 읽는 사람에 따라 다르게 읽힌다. 도메인 전문가에게 읽기 쉬운 코드와 신입 개발자에게 읽기 쉬운 코드는 종종 다르다.
더 정확한 정의는 이렇다. **유지보수 가능한 코드란, 변경의 이유가 한곳에 모여 있는 코드다.** 단일 책임 원칙의 본래 진술이 그러했다. 로버트 마틴은 "한 클래스가 한 가지 일을 해야 한다"가 아니라 "한 클래스는 변경의 한 가지 이유만 가져야 한다"라고 적었다. 둘은 닮아 보이지만 다르다. 전자는 기능의 분할이고, 후자는 변화의 분할이다.
변화의 결을 따라 쪼개진 코드는 변경 요청이 들어왔을 때 손이 가야 할 곳이 한 군데로 좁혀진다. 좁혀진 만큼 두려움이 줄고, 줄어든 두려움만큼 다음 변경이 더 가벼워진다. 이 양(陽)의 되먹임 고리가 유지보수성의 실체다.
장자는 「양생주」에서 포정해우를 이야기한다. 포정은 소의 뼈와 살을 가르는 결을 따라 칼을 움직였기에 19년 동안 칼날이 무뎌지지 않았다고 했다. 결을 거스르는 자는 매달 칼을 갈고, 결을 따르는 자는 평생을 한 칼로 산다. 리팩토링은 칼을 가는 일이 아니다. 칼이 들어갈 결을 다시 찾는 일이다.
## 닫으며: 부채를 기록하는 자의 자세
내가 부채에 대해 배운 가장 값진 교훈은 코드가 아니라 문서에서 나왔다. 한 팀에서 우리는 ADR(Architectural Decision Record)을 도입했다. 모든 중요한 결정에 대해 그 시점의 맥락과 대안, 트레이드오프를 한 페이지에 적었다. 처음에는 번거로웠다. 그러나 6개월 뒤 누군가가 "왜 여기를 이렇게 했지?"라고 물었을 때, 우리는 처음으로 정확히 답할 수 있었다. 그리고 그 답에 근거해, 처음으로 죄책감 없이 코드를 바꿀 수 있었다.
기술 부채는 결국 **기억의 부채**다. 왜 그렇게 결정했는지를 잊은 만큼 우리는 그 결정을 의심할 권리를 잃는다. 의심할 수 없는 코드는 바꿀 수 없는 코드가 되고, 바꿀 수 없는 코드는 결국 폐기된다.
리팩토링은 코드를 다시 쓰는 일이 아니다. 코드에 담긴 의도를 다시 발음하는 일이다. 잘 발음된 의도는 다음 사람이 이어 부르기 쉬운 노래가 된다.