팀 회의에서 "기술 부채를 갚아야 합니다"라는 말이 나왔다. 시니어가 꺼낸 주제였다. 레거시 코드가 쌓이고 있고, 새 기능을 추가할 때마다 시간이 오래 걸린다는 거였다. 다들 고개를 끄덕였다. 기술 부채를 줄여야 한다는 데 반대할 사람은 없으니까.
그런데 PM이 물었다. "구체적으로 어떤 부분이 기술 부채인가요?"
시니어가 대답했다. "음... 전반적으로 코드가 많이 지저분해져서요."
PM이 다시 물었다. "그걸 정리하면 얼마나 걸리나요? 그리고 정리하면 뭐가 좋아지나요?"
회의실이 조용해졌다.
이 대화가 내가 "기술 부채"에 대해 진지하게 생각하기 시작한 순간이었다. 우리 모두 기술 부채라는 단어를 쓰고 있었지만, 정확히 뭘 의미하는지는 각자 다르게 이해하고 있었다.
기술 부채는 하나가 아니다
Martin Fowler가 기술 부채를 네 가지로 분류한 사분면(Technical Debt Quadrant)이 있다. 세로축은 "무모한(Reckless)" vs "신중한(Prudent)", 가로축은 "의도적(Deliberate)" vs "비의도적(Inadvertent)"이다.
1. 무모하고 의도적인 부채
"설계 같은 거 신경 쓸 시간 없어, 그냥 빨리 짜."
마감에 쫓겨서 알면서도 대충 짜는 경우다. 코드 리뷰도 건너뛰고, 테스트도 안 쓰고, 하드코딩으로 때운다. 본인도 이게 나쁜 코드라는 걸 알지만 "나중에 고치면 되지"라고 한다.
Fowler는 이런 부채를 가장 위험하다고 봤다. 왜냐면 사람들은 대부분 "좋은 설계의 효과가 나타나는 시점"을 과대평가하기 때문이다. 좋은 설계는 몇 주 뒤에 효과를 보이는 게 아니라 며칠 만에 개발 속도를 높여준다. 그 며칠의 이점을 포기한 대가가 생각보다 크다.
이게 현실에서 어떻게 보이냐면, 누군가가 "일단 동작하게 만들자"라고 하면서 모든 비즈니스 로직을 하나의 컴포넌트에 때려넣는 거다. 300줄짜리 컴포넌트. 거기에 API 호출, 상태 관리, 조건부 렌더링, 이벤트 핸들러가 전부 섞여 있다. 당장은 동작한다. 근데 2주 뒤에 기획 변경이 오면 그 300줄 안에서 어디를 고쳐야 하는지 찾는 것만 30분이 걸린다.
2. 무모하고 비의도적인 부채
"계층화가 뭔데?"
이건 단순히 모르기 때문에 생기는 부채다. 설계 원칙을 모르는 상태에서 코드를 짜면, 의도하지 않았지만 결과적으로 부채가 된다. 본인은 최선을 다했는데 결과물이 지저분한 경우.
솔직히 주니어 때 내가 이 케이스였다. 관심사 분리가 뭔지도 모르고 컴포넌트를 짰다. 데이터 fetching 로직과 UI 로직이 한 파일에 뒤섞여 있었다. 그때는 그게 뭐가 나쁜 건지도 몰랐다. 시니어가 코드 리뷰에서 "이거 분리하면 좋겠어요"라고 할 때 왜 분리하는 게 좋은 건지 이해하지 못했다.
이 유형의 부채는 학습으로 해결된다. 경험이 쌓이고 좋은 코드를 많이 보면서 자연스럽게 줄어든다. 문제는 이미 작성된 코드가 남아있다는 거다. 주니어 때 짠 코드가 1년 뒤에 부채로 돌아온다.
3. 신중하고 의도적인 부채
"이 설계의 한계를 알지만, 지금 출시하는 게 더 중요하다."
이게 진짜 금융의 "부채"와 가장 비슷한 유형이다. 리스크를 알고 있고, 언제 갚아야 하는지도 알고 있지만, 전략적으로 지금은 부채를 지기로 결정하는 거다.
예를 들어, MVP를 빨리 출시해서 시장 반응을 보는 게 중요한 상황이 있다. 이때 완벽한 아키텍처를 설계하느라 3개월을 쓰는 것보다, 알려진 한계가 있는 설계로 2주 만에 출시하고 유저 피드백을 받는 게 나을 수 있다. Fowler도 "신중한 부채는 수정이 적은 코드 영역에서는 갚지 않는 것이 합리적일 수 있다"고 말했다.
우리 팀에서도 이런 판단을 한 적이 있다. 어드민 대시보드를 만들 때, 사내 5명만 쓰는 도구라서 상태 관리를 Redux 대신 컴포넌트 로컬 state로만 처리했다. 규모가 커지면 prop drilling 지옥이 올 거라는 걸 알고 있었지만, 사용자가 5명인 어드민에 상태 관리 라이브러리를 붙이는 건 오버 엔지니어링이었다.
1년이 지난 지금도 그 어드민은 잘 돌아간다. prop drilling이 좀 있지만 관리 가능한 수준이다. 이 부채는 결과적으로 안 갚아도 되는 부채였다.
4. 신중하고 비의도적인 부채
"지금은 이게 최선이었는데, 1년 뒤에 보니까 더 나은 방법이 있었다."
Fowler가 이 유형을 가장 흥미로워했다. "프로그래밍하면서 배우게 된다. 최선의 설계가 뭐였는지 이해하기까지 1년이 걸릴 수도 있다"는 거다.
이건 개발을 하면 필연적으로 생기는 부채다. 아무리 경험 많은 시니어라도 이걸 완전히 피할 수는 없다. 왜냐면 소프트웨어 설계의 정답은 "사후에" 보이는 경우가 많기 때문이다.
내 경험을 하나 들면, 처음 디자인 시스템을 만들 때 컴포넌트 API를 설계하면서 꽤 고민했다. 팀에서도 논의를 많이 했고, 합리적인 결정이라고 생각했다. 근데 6개월 뒤에 실제로 여러 서비스에 적용하면서 써보니까, 처음 설계의 구조적 한계가 드러났다. 당시에는 보이지 않았던 유스케이스들이 나온 거다.
이걸 "그때 왜 그렇게 했어"라고 비난하는 건 뒤에서 주먹 쥐기다. 그때는 그게 최선이었다. 이 유형의 부채는 비난이 아니라 지속적인 리팩토링으로 대응하는 게 맞다.
왜 이 분류가 중요한가
"기술 부채 좀 갚자"라는 대화가 팀에서 나올 때, 네 가지 유형 중 어떤 부채를 이야기하는 건지 구분하지 않으면 대화가 공전한다.
시니어가 "코드가 지저분하다"고 할 때, 그게 1번(무모하고 의도적)인 경우도 있고 4번(신중하고 비의도적)인 경우도 있다. 근데 이 둘의 대응 방식은 완전히 다르다.
1번은 프로세스의 문제다. 코드 리뷰를 제대로 안 하거나, "일단 배포"라는 문화가 문제인 거다. 코드를 고치는 것만으로는 해결이 안 된다. 다음에 또 같은 식으로 짤 거니까.
4번은 자연스러운 현상이다. 프로세스가 완벽해도 생긴다. 이건 정기적인 리팩토링 시간을 확보하는 것으로 대응한다.
PM과 기술 부채 대화하는 법
Gergely Orosz가 강조하는 것처럼, 엔지니어링 결정은 기술 언어가 아니라 비즈니스 언어로 설명해야 한다. "코드가 지저분하다"는 PM에게 아무 의미가 없다. PM은 "그래서 뭐가 어떻게 되는데?"가 궁금하다.
기술 부채를 비즈니스 임팩트로 번역하면 대화가 달라진다.
나쁜 설명: "이 모듈은 레거시 코드가 많아서 리팩토링이 필요합니다."
좋은 설명: "현재 결제 모듈에 새 기능을 추가하면 평균 5일이 걸립니다. 구조를 정리하면 2일로 줄일 수 있습니다. 정리하는 데 2주가 필요하고, 이후 기능 추가마다 3일씩 절약됩니다. 향후 6개월간 예정된 결제 관련 기능이 8개이므로, 2주 투자로 24일(약 5주)을 절약할 수 있습니다."
숫자가 들어가면 PM이 판단할 수 있다. "2주 투자해서 5주 아끼는 거면 당연히 하죠"라는 답이 나올 수도 있고, "지금 당장 급한 기능이 있어서 다음 분기에 하면 안 될까요?"라는 답이 나올 수도 있다. 어떤 답이든 상관없다. 중요한 건 데이터 기반의 대화가 된다는 거다.
Fowler가 빚(debt)이라는 비유를 쓴 이유도 이거다. PM과 비개발자가 이해할 수 있는 언어로 기술적 문제를 설명하기 위해서. "이자(interest)"가 높은 부채부터 갚자는 논의가 가능해진다.
실무에서 기술 부채 관리하기
완벽주의에 빠지면 모든 코드를 다 고치고 싶어진다. 근데 현실적으로 불가능하다. 기술 부채를 관리하는 건 "모든 부채를 갚는 것"이 아니라 "어떤 부채를 먼저 갚을지 우선순위를 정하는 것"이다.
1. 자주 바뀌는 코드부터
Fowler도 이 점을 강조했다. 1년 동안 한 번도 수정되지 않는 레거시 코드는 아무리 지저분해도 급하지 않다. 거기서 "이자"를 내고 있지 않으니까. 반대로 매주 수정되는 모듈의 코드가 지저분하면 이자가 엄청나게 빠르게 쌓인다. 매번 수정할 때마다 시간이 더 걸리고, 버그가 생기고, 다른 곳에 영향을 준다.
우리 팀에서는 git log를 돌려서 최근 3개월간 가장 많이 수정된 파일 10개를 뽑았다. 그 파일들의 코드 품질을 점검하고, 거기에 리팩토링 우선순위를 뒀다. 결과적으로 전체 코드의 5%만 정리했는데 개발 속도가 체감될 정도로 빨라졌다.
2. 스프린트마다 20% 확보
별도의 "리팩토링 스프린트"를 잡는 것보다, 매 스프린트에 리팩토링 시간을 20% 확보하는 게 효과적이었다. 한꺼번에 2주를 써서 대규모 리팩토링을 하면, 그 2주 동안 기능 개발이 멈추니까 PM 입장에서는 부담이 크다. 매 스프린트마다 금요일 하루를 리팩토링에 쓰면 부담이 적고, 부채가 누적되는 것도 방지된다.
3. 기술 부채 문서화
"이건 나중에 고쳐야 해"를 머릿속이나 TODO 주석에만 남겨놓으면 까먹는다. 우리 팀은 노션에 "기술 부채 목록"을 만들고, 각 항목에 이 정보를 기록했다.
- 어떤 부채인지 (4가지 유형 중 어디에 해당하는지)
- 이자 비용이 얼마나 되는지 (새 기능 추가 시 추가로 걸리는 시간)
- 갚으려면 얼마나 걸리는지
- 우선순위
이렇게 하면 분기별 계획을 짤 때 "이번 분기에는 어떤 부채를 갚을까"를 데이터 기반으로 논의할 수 있다. "코드가 지저분하다"라는 모호한 불만이 "결제 모듈의 신중한-비의도적 부채를 갚으면 기능 추가 시간이 60% 단축된다"는 구체적 제안으로 바뀐다.
기술 부채는 악이 아니다
마지막으로 하고 싶은 말은, 기술 부채 자체가 나쁜 게 아니라는 거다. 금융에서도 부채가 무조건 나쁜 건 아니다. 집을 살 때 대출을 받는 건 합리적인 결정이다. 문제는 감당할 수 없는 부채를 지거나, 이자가 얼마인지도 모르면서 부채를 지는 거다.
소프트웨어도 마찬가지다. 신중하게 지는 부채는 전략이다. 무모하게 지는 부채가 문제이고, 부채가 있다는 사실을 인식하지 못하는 것이 더 큰 문제다.
기술 부채 사분면을 팀에 공유한 뒤로, "기술 부채" 논의의 질이 올라갔다. "코드가 지저분하다"가 아니라 "이건 3번 유형 부채인데 이자가 낮으니까 당분간 괜찮다", "이건 1번 유형이니까 프로세스를 바꿔야 한다" 같은 대화가 가능해졌다.
모든 부채를 갚으려 하지 말고, 어떤 부채인지 먼저 이해하자. 그게 기술 부채를 대하는 첫 번째 단계다.
