Technical Debt & Refactoring Physics
빠른 배포를 위해 더 나은 설계를 나중으로 미룬 결과 생기는 추가 작업 비용. Ward Cunningham이 금융 부채 비유로 도입. 신중한 의도적 부채만 전략적으로 수용 가능. 나머지는 최소화 대상.
Article
M
Me
hyunyoun's Blog
software-engineering-devopssoftware-engineeringdev-opsqaquality-assurancetechnical-debtrefactoring-physicsreliability11 min read
1. Overview
기술 부채와 리팩터링의 물리학(Technical Debt & Refactoring Physics)은 "일단 돌아가게 짜고 나중에 고치자"라는 핑계로 쌓아 올린 젠가(Jenga) 탑이 어느 순간 개발팀 전체를 붕괴시키는 코드 이자의 복리(Compound Interest) 역학을 해부합니다.
학습자는 시간이 지날수록 새로운 기능을 추가하는 속도가 0에 수렴하게 만드는 기술 부채(Technical Debt)의 4가지 사분면을 뜯어봅니다. 나아가 코드가 썩었다는 것을 시각적으로 판별해 내는 **코드 스멜(Code Smell)**을 탐지하고, 기능(Behavior)은 단 1%도 바꾸지 않은 채 내부 구조만 우아하게 뜯어고치는 리팩터링(Refactoring)의 수학적 변환술을 장악합니다. 마지막으로, 리팩터링 중 시스템이 박살 나는 것을 막기 위해 **보이스카우트 규칙(Boy Scout Rule)과 테스트 밧줄(Test Harness)**을 활용하는 점진적 수술 역량을 확보합니다.
2. Scope & Boundaries
In-Scope
- Technical Debt: 부채의 사분면 (무모한/신중한/의도적/무의식적 부채), 부채 상환(Refactoring)과 파산(Rewrite)의 임계점.
- Code Smells: 중복된 코드(Duplicated Code), 거대한 클래스(Large Class), 긴 메서드(Long Method), 데이터 덩어리(Data Clumps) 등 코드 부패의 징후.
- Refactoring Techniques: Extract Method, Replace Conditional with Polymorphism, Rename Variable 등 의미 불변(Meaning-preserving) 변환.
- Refactoring Strategy: 보이스카우트 규칙(항상 떠날 때는 올 때보다 깨끗하게), 점진적 리팩터링(Strangler Fig Pattern).
Out-of-Scope
- 클린 아키텍처(Clean Architecture) 설계: 시스템 전체의 레이어를 어떻게 분리할 것인가 09-03-01 SOLID & GRASP 영역으로 위임.
- TDD(Test-Driven Development)의 작성법: 테스트 코드를 먼저 짜는 개발 방법론 자체 09-04-02 TDD & State Verification에서 기학습.
Boundaries
- Refactoring vs Rewriting: 둘은 목적도 결과도 완전히 다릅니다. 리팩터링은 건물의 외관과 용도는 그대로 둔 채, 기둥과 배관을 하나씩 튼튼한 것으로 교체하는 '영업 중 보수'입니다(기능 변경 금지). 리라이팅(Rewriting, 차세대 프로젝트)은 건물을 폭파하고 백지에서 다시 짓는 '영업 중단 재건축'입니다. "코드가 너무 더러워서 싹 다 다시 짤게요"라는 주니어의 외침은 리팩터링이 아니라 파산 선고(Rewriting)이며, 비즈니스 측면에서 극도로 위험한 도박이라는 경제적 경계를 명확히 긋습니다.
3. Counterexample
- 파산한 스타트업 (기술 부채의 복리): A 스타트업은 시장 선점을 위해 코드를 복붙하며(Copy & Paste) 1달 만에 앱을 런칭했습니다(의도적 기술 부채). 대성공했습니다. 하지만 1년 뒤, 결제 수단을 하나 추가해 달라는 요청에 개발팀이 "코드 한 줄 바꾸면 다른 데서 100개의 에러가 터져서(스파게티 코드) 3달이 걸린다"고 답합니다. 빚(Debt)을 지고 이자(시간 지연)를 갚지 않다가, 복리로 불어난 이자가 원금을 잡아먹어 회사가 파산하는 기술 부채의 전형적 말로입니다.
- 기능을 바꿔버린 거짓 리팩터링: "제가 결제 모듈 리팩터링 좀 할게요!" 시니어가 코드를 예쁘게 고쳤습니다. 그런데 다음 날 수많은 고객의 결제가 실패하기 시작했습니다. 알고 보니 리팩터링을 한답시고 기존에 있던
예외 처리(Exception)분기를 "코드가 안 예쁘다"며 삭제해 버린 것입니다. 리팩터링의 절대 원칙인 '외부 동작(Behavior) 불변의 법칙'을 어긴, 그냥 '내 맘대로 코드 부수기'입니다.
4. Prerequisites
- 객체지향 설계 원칙 (Basic): SOLID 원칙과 응집도/결합도. (09-03-01 SOLID)
- 자동화된 테스트 (Basic): TDD 및 유닛 테스트의 구조. 리팩터링 전에는 반드시 테스트 밧줄이 필요함. (09-04-02 TDD)
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: 복리의 저주 (The Physics of Technical Debt)
- Why to Learn: "우리 코드는 쓰레기지만 일단 돌아가니까 놔두자"는 태도가 왜 2년 뒤 회사의 신기능 개발 속도를
0으로 수렴시켜 회사를 망하게 하는지 경제학적 인과관계를 깨닫기 위함입니다. - What to Learn:
- Concepts: Technical Debt (기술 부채), Ward Cunningham's Metaphor, Intentional vs Unintentional Debt, Interest (부채의 이자), Velocity (개발 속도).
- Skills: "지금 당장 코드를 예쁘게 짤 시간이 없다"며 하드코딩(Debt)을 선택할 때, 이 부채가 미래의 나에게 갚아야 할 이자(버그 수정 및 기능 추가 지연 시간)로 어떻게 돌아오는지 트레이드오프 계산하기.
- How to Learn:
- 1단계: 은유 (Metaphor): 은행에서 대출을 받으면 빨리 집을 살 수 있습니다(빠른 런칭). 하지만 매달 이자를 내야 합니다. 기한 내에 원금(리팩터링)을 안 갚으면 이자가 복리로 불어나 파산합니다.
- 2단계: 나쁜 빚과 좋은 빚:
- 의도적(Good): "이번 주말 트래픽 폭주를 막기 위해 임시로 캐시 로직을 더럽게 짜 넣자. 다음 주에 리팩터링 할게." (통제된 부채)
- 무의식적(Bad): 주니어가 SOLID 원칙을 몰라서
God Class를 만들고 퇴사함. 팀은 부채가 있는지조차 모름.
- 3단계: 이자의 복리 폭발: 코드가 꼬여있으면 원래 1시간이면 추가할 기능을 10시간 동안 짜야 합니다. 잃어버린 9시간이 바로 '이자'입니다. 부채 상환(리팩터링) 일정을 스프린트에 20%씩 강제로 끼워 넣지 않으면 파산한다는 SRE 거버넌스를 뜯어봅니다.
- Implement: 기술 부채 이자율(Interest Rate) 차트 모델링.
X축(시간),Y축(기능 추가 속도).No-Debt 팀: 초기 속도는 느리지만 지속해서 일정한 속도 유지.High-Debt 팀: 런칭 속도 폭발적 6개월 뒤 스파게티 코드화 기능 추가 속도 0에 수렴. 결과적으로 장기적 관점에선 부채를 갚아가며 코딩하는 팀이 압도적 우위에 선다는 기술 경제학 렌더링.
Recommended
Core Topic 02: 썩은 코드의 징후 (Smell the Rot: Code Smells)
- Why to Learn: "코드가 안 좋은 것 같은데..."라는 모호한 직관을 버리고, 전 세계 개발자들이 합의한 명확한 '악취(Smell)' 패턴의 이름을 붙여 정확히 기소(Prosecute)하기 위함입니다.
- What to Learn:
- Concepts: Code Smell (코드 스멜), Duplicated Code (중복 코드), Long Method (긴 메서드), Large Class (거대 클래스), Data Clumps (데이터 뭉치), Shotgun Surgery (산탄총 수술).
- Skills: 10개의 파라미터를 받는 함수를 보고 "데이터 뭉치 스멜이 납니다!"라고 지적하고, 이를 객체(Object) 하나로 묶어 파라미터를 1개로 줄이는 리팩터링 방향 제시하기.
- How to Learn:
- 1단계: Long Method (긴 메서드): 스크롤을 3번 넘겨야 끝나는 500줄짜리 함수. 이 안에는 "DB 읽기", "데이터 가공", "이메일 전송"이 모두 섞여 있습니다. 함수는 1가지 일만 해야 한다는 원칙 위반의 가장 흔한 냄새입니다.
- 2단계: Shotgun Surgery (산탄총 수술): 데이터베이스의 테이블 컬럼 1개를 바꿨을 뿐인데, 소스코드 파일 25개를 열어서 똑같이 한 줄씩 수정해 줘야 합니다. 마치 산탄총을 쏴서 파편이 튄 것처럼 응집도(Cohesion)가 박살 난 최악의 냄새입니다.
- 3단계: Duplicated Code (중복 코드):
Ctrl+C, Ctrl+V의 산물. 이 로직에 버그가 발견되면 똑같이 복사된 10군데를 돌아다니며 다 고쳐야 합니다. '단일 진실의 원천(DRY)'이 박살 난 냄새를 해부합니다.
- Implement: Code Smell 탐지 및 카테고라이징 매트릭스 도출.
상황:calculateSalary(base, tax, bonus, overtime, deduction)인자가 5개나 됨.진단: "Data Clumps (데이터 뭉치) 스멜". 항상 몰려다니는 데이터들.처방:class SalaryDetail { base, tax, bonus... }객체를 생성하여 인자를calculateSalary(SalaryDetail info)1개로 줄이는 우아한 변환 시각화.
Practical
Core Topic 03: 기능 불변의 외과 수술 (The Surgery: Refactoring)
- Why to Learn: 코드를 예쁘게 뜯어고친답시고 로직의 결과물(Behavior)까지 바꿔버려 실서버에 대형 버그를 터뜨리는 '가짜 리팩터링'의 재앙을 막고, 수학적 동치(Equivalence)를 유지하는 진짜 수술법을 익히기 위함입니다.
- What to Learn:
- Concepts: Refactoring (리팩터링), Meaning-preserving Transformation (의미 보존 변환), Extract Method (메서드 추출), Replace Conditional with Polymorphism (조건문을 다형성으로 바꾸기).
- Skills: 코드의
입력과출력은 1비트도 달라지지 않는다는 전제하에,if-else가 10개 떡칠 된 스파게티 코드를 '전략 패턴(Strategy Pattern)' 기반의 우아한 객체 구조로 뜯어고치기.
- How to Learn:
- 1단계: 리팩터링의 철칙: 마틴 파울러(Martin Fowler)의 정의를 외웁니다. "리팩터링이란 외부 동작을 변경하지 않고(Without changing its observable behavior) 내부 구조를 개선하는 것".
- 2단계: Extract Method (수술의 기초): 500줄짜리 함수 안에서 '이메일을 조립하는 50줄'을 드래그해서
buildEmail()이라는 별도 함수로 뽑아냅니다(Extract). 기존 50줄의 코드 위치에는buildEmail()호출 딱 한 줄만 남습니다. - 3단계: Polymorphism (조건문의 척결):
if (type == 'BIRD') 날기() else if (type == 'DOG') 뛰기(). 이 촌스러운 조건문을Animal인터페이스를 상속받은Bird와Dog클래스의.move()메서드 오버라이딩으로 대체(다형성)하는 OCP(개방-폐쇄 원칙) 수술법을 뜯어봅니다.
- Implement:
if-else지옥 다형성(Polymorphism) 리팩터링 모사.Before:calculatePay(type)안에서if (part-time) { ... } else if (full-time) { ... } else if (freelancer) { ... }.Action:Employee인터페이스 도출 및 하위 구현체 3개 생성.After:employee.calculatePay().if-else블록 30줄이 완벽히 소멸하며, 새로운 직군(Contractor)이 추가되어도 기존 코드를 단 1줄도 수정하지 않는 기적 렌더링.
Advanced
Core Topic 04: 방탄조끼와 보이스카우트 (Boy Scout Rule & Test Harness)
- Why to Learn: "리팩터링 하다가 시스템 부술까 봐 무서워서 못 고치겠어요"라는 변명을 부수고, 테스트 코드(Test Harness)라는 튼튼한 방탄조끼를 입고 매일 1%씩 코드를 정화해 나가는 용기를 쥐기 위함입니다.
- What to Learn:
- Concepts: Test Harness (테스트 하네스/안전망), The Boy Scout Rule (보이스카우트 규칙), Strangler Fig Pattern (교살자 무화과나무 패턴), Continuous Refactoring.
- Skills: 레거시(Legacy) 코드에 리팩터링 칼을 대기 전에, 먼저 이 코드가 현재 어떻게 동작하는지를 보장하는
Unit Test(Characterization Test)를 짜서 방탄조끼를 입고, 그 뒤에 코드를 뜯어고치기.
- How to Learn:
- 1단계: 테스트 밧줄 (Test Harness): 등산(리팩터링)을 할 때 밧줄(Test) 없이 절벽을 타면 죽습니다. 10년 된 레거시 코드를 고치고 싶다면? 고치기 전에 먼저 "이 쓰레기 코드에 5를 넣으면 10이 나온다"는 테스트 코드부터 강제로 작성해 기계의 감시망을 만듭니다.
- 2단계: 무지성 리팩터링 금지: 그 후 코드를 예쁘게 고칩니다. 테스트를 돌립니다. 기계가 여전히 "5를 넣으면 10이 나옴(Pass)"이라고 뜨면 성공입니다. 11이 나오면 즉시 되돌립니다.
- 3단계: 보이스카우트 규칙: "캠핑장을 떠날 때는 당신이 도착했을 때보다 1%라도 더 깨끗하게 치우고 떠나라." 각 잡고 1달 내내 리팩터링만 하는 '빅뱅 리팩터링'은 비즈니스를 멈추게 하므로 금지됩니다. 내가 오늘 기능 A를 추가하러 들어간 김에, 그 주변에 있는 변수명 하나, 긴 함수 하나만 쪼개고 나오는 일상적(Continuous) 리팩터링의 철학을 해부합니다.
- Implement: Boy Scout Rule 실천 파이프라인 시나리오.
Task: 회원가입에 '추천인 코드' 입력란 추가 (소요 예정 시간 4시간).Action 1: 기존SignupService파일 오픈. 파일이 1,000줄인 것 발견(Smell).Action 2 (Refactor): 회원가입 로직 중 '비밀번호 암호화' 50줄을PasswordEncryptor클래스로 추출(Extract). 테스트 Pass 확인. (소요 30분)Action 3 (Feature): '추천인 코드' 기능 추가 후 PR(Pull Request). (소요 3시간 30분). 일정 지연 없이 기술 부채의 이자 원금을 몰래 상환해 낸 엔지니어의 규율 렌더링.
7. Terminology
8. References
Primary
- [P1] CS2023 - Software Engineering (SE) - Software Evolution (Refactoring and Technical Debt)
- [P5] SFIA - Programming/Software Development (PROG) - Code quality and Refactoring
Secondary
- [Refactoring] Martin Fowler - Principles in Refactoring, Bad Smells in Code
- [Clean Code] Robert C. Martin - Boy Scout Rule, Code Smells and Heuristics
Industry
- [MartinFowler.com] - TechnicalDebtQuadrant
- [Atlassian Agile Coach] - Technical debt: What it is and how to manage it
9. Final Checklist
Primary
- 스타트업이 초기 시장 선점을 위해 하드코딩으로 "의도적 기술 부채"를 지는 것은 비즈니스적으로 올바른 판단일 수 있으나, 이를 상환(리팩터링)하지 않고 방치할 경우 '복리 이자'가 쌓여 1년 뒤 기능 개발 속도가
0으로 수렴하는 현상을 경제학적으로 설명할 수 있는가? - 500줄이 넘어가는 '긴 메서드(Long Method)'나 데이터 테이블 하나 바꿀 때 파일 20개를 고쳐야 하는 '산탄총 수술(Shotgun Surgery)' 같은 코드 스멜(Code Smell)을 인지하고, 이를 객체지향 원칙을 통해 즉각 기소(Prosecute)할 수 있는가?
Secondary
- 리팩터링(Refactoring)의 절대 원칙인 "외부 동작(Behavior)을 변경하지 않는다"를 지키기 위해,
if-else분기문 10개를 인터페이스 기반의 다형성(Polymorphism)으로 뜯어고칠 때 시스템의 입출력 값이 수학적 동치(Equivalence)를 이룸을 증명할 수 있는가? - 기능 추가와 리팩터링이라는 두 개의 모자(Two Hats)를 동시에 쓰려다 실서버를 붕괴시키는 주니어의 실수를 지적하고, 오직 테스트 하네스(Test Harness, 방탄조끼)가 성공(Green)을 담보할 때만 리팩터링 칼을 대는 안전망 거버넌스를 설계할 수 있는가?
Industry
- 코드가 너무 더러워서 싹 다 새로 짜겠다는 '리라이팅(Rewriting, 빅뱅 재구축)'의 비즈니스적 파산 리스크를 거부하고, 보이스카우트 규칙(Boy Scout Rule)에 입각하여 내가 오늘 기능 패치하러 들어간 파일의 1%만 정화하고 나오는 '점진적 리팩터링'의 위력을 논증할 수 있는가?
- 거대한 레거시(Legacy) 코드가 테스트 코드조차 전혀 없는 상황에 직면했을 때, 가장 리스크가 큰 비즈니스 핵심 로직 주변에만 핀셋으로 '특성화 테스트(Characterization Test)' 밧줄을 먼저 설치한 뒤에야 메서드 추출(Extract Method) 수술에 돌입하는 SRE급 수술 절차를 구축할 수 있는가?