Rain Lag

뽀모도로 리팩터링: 작은 시간 사이클로 위험한 레거시 코드를 안전하게 다루는 법

짧고 집중된 뽀모도로 시간 블록을 활용해 레거시 코드를 점진적으로 리팩터링하고, 리스크를 줄이면서 오래된 코드베이스를 개선하는 동안 멘탈을 지키는 방법.

뽀모도로 리팩터링: 작은 시간 사이클로 위험한 레거시 코드를 안전하게 다루는 법

레거시 코드는 독특한 공포감을 줍니다.

아무도 손대고 싶어 하지 않는 거대한 깨지기 쉬운 모듈. 4,000줄에 달하는 뒤엉킨 로직, 전역 변수, 2012년 날짜가 찍힌 주석이 들어 있는 파일. “작은 수정 하나”가 어김없이 주말 화재 진압으로 이어지는 시스템의 그 부분.

리팩터링이 필요하다는 건 잘 알고 있습니다. 동시에, 한 번에 “다 고쳐버리겠다”는 시도가 곧 사고의 지름길이라는 것도 알고 있죠.

여기서 등장하는 것이 뽀모도로 리팩터링(Pomodoro Refactor) 입니다. 짧고 고정된 시간 블록을 사용해, 무서운 레거시 코드를 안전하고 예측 가능하게, 그리고 번아웃 없이 조금씩 깎아나가는 방식입니다.


레거시 코드가 압도적으로 느껴지는 이유

레거시 코드가 위협적으로 느껴지는 이유는 단지 오래됐거나 못생겼기 때문이 아니라, 보통 이런 특징을 동시에 갖고 있기 때문입니다.

  • 규모가 크다: 깔끔한 작은 모듈 하나에만 갇혀 있지 않습니다. 이곳저곳으로 뻗어나가 있습니다.
  • 이해하기 어렵다: 원 작성자는 떠났고, 문서는 오래됐으며, 실제 동작은 이상한 엣지 케이스에 숨어 있습니다.
  • 변경 리스크가 크다: 깨지기 쉬운 의존성과 알 수 없는 부작용들이 잔뜩입니다.
  • 비즈니스 핵심 로직과 강하게 결합돼 있다: 이 부분이 깨지면 뭔가 중요한 기능이 멈춥니다.

이 조합은 뇌가 “차라리 다른 걸 하자”라고 하게 만듭니다. 미루기만 하거나, 반대로 너무 깊이 뛰어들었다가 며칠 뒤에야 빠져나오는데, 그때 남는 건 이해하기 힘들고 위험한 거대한 diff뿐이죠.

뽀모도로 리팩터링은 이 심리적·기술적 복잡성을 시간과 범위를 줄이는 것으로 정면 돌파합니다.


핵심 아이디어: 작고 시간 제한된 리팩터링

클래식 뽀모도로 기법은 25분 집중 작업 후 짧은 휴식을 반복하는 방식입니다. 20–30분 안에서 조정해도 되지만 핵심은 다음과 같습니다.

  • 작업 블록은 짧고 고정된 길이다.
  • 그 시간 동안 집중하겠다고 스스로 약속한다.
  • 얼마나 “집중이 잘 되든” 타이머가 울리면 멈춘다.

레거시 리팩터링에선 이 각 뽀모도로를 작고 독립적인 리팩터링 단위로 만드는 게 핵심입니다. 각 블록은 다음을 가져야 합니다.

  • 명확하고 욕심 부리지 않은 목표
  • 안전하고 되돌리기 쉬운 변경
  • 테스트 가능한 결과물

거대한 모듈 전체를 한 번에 갈아엎는 ‘영웅적인’ 시도 대신, 수십 개의 작고 자신 있는 걸음을 옮기는 방식입니다.


1단계: 각 뽀모도로마다 마이크로 목표 정의하기

타이머를 시작하기 전에, 다음 시간 블록 동안 정확히 무엇을 개선할지 정합니다. 코드의 한 가지 측면만 생각합니다.

  • 가독성: 주석 추가, 긴 메서드 쪼개기, 로직 흐름을 더 명확하게 만들기
  • 구조: 클래스/함수 추출, 책임 분리, 중첩 줄이기
  • 네이밍: 변수, 메서드, 클래스 이름을 더 명확하게 바꾸기
  • 테스트 커버리지: 작은 동작 범위에 대한 테스트 추가 또는 개선

뽀모도로 크기에 딱 맞는 좋은 목표 예시는 다음과 같습니다.

  • "할인 계산 분기(브랜치)에 대한 특성화(characterization) 테스트 추가하기"
  • "data 변수 이름을 바꾸고, 헬퍼 함수 두 개를 추출하기"
  • "이메일 포맷팅 로직을 별도 함수로 뽑아내고, 그 함수에 대한 테스트 추가하기"
  • "긴 if-else 체인을 전략 패턴(strategy pattern)으로 교체하되, 이번에는 두 케이스만 적용하기"

목표가 “user 모듈 정리하기”, **“billing 시스템 리팩터링하기”**처럼 들린다면 너무 큽니다. 20–30분 안에 끝낼 수 있을 것 같은 크기까지 잘게 쪼개세요.


2단계: 동작을 먼저 보호하고, 그다음 리팩터링하기

레거시 코드의 가장 큰 덕목은 “일단은 돌아간다”는 점입니다. (적어도, 비즈니스가 현재 의존하고 있는 결과를 어찌 되었든 생산하고 있습니다.) 이걸 건드리기 전에, 최소한의 안전망이 필요합니다.

뽀모도로를 번갈아 사용해 다음 두 가지를 반복합니다.

  1. 현재 동작 이해하고 고정하기

    • **특성화 테스트(characterization test)**를 추가합니다. 코드가 현재 어떻게 동작하는지를 고정하는 테스트입니다. 결과가 이상해 보여도, 일단 현재 동작을 잠궈둡니다.
    • 핵심 경로와 이미 까다롭다고 알려진 케이스에 집중합니다.
  2. 테스트를 유지한 채로 리팩터링하기

    • 바꾸려는 동작 주변에 테스트가 있을 때만 구조적인 변경을 합니다.

유용한 패턴은 다음과 같습니다.

  • 뽀모도로 1: 코드 탐색 + 테스트 작성/보강
  • 뽀모도로 2: 테스트를 가이드 삼아 작은 부분 리팩터링
  • 뽀모도로 3: 다음 영역에 대한 테스트 추가
  • 뽀모도로 4: 그 영역 리팩터링

이 번갈아가는 리듬 덕분에 코드가 계속 무엇을 해야 하는지를 잊지 않을 수 있습니다. “그냥 리팩터링”만 하는 게 아니라, 이미 알고 있는 동작을 기준점으로 삼아 리팩터링을 하게 됩니다.


3단계: 변경은 작게, 되돌릴 수 있게, 그리고 테스트 가능하게

각 뽀모도로가 끝날 때쯤에는 변경 내용이 다음 조건을 만족하는 게 좋습니다.

  • 작다: 수정한 라인 수가 많지 않고, 개념적으로도 범위가 좁다.
  • 되돌리기 쉽다: 문제가 생기면 간단히 롤백할 수 있다.
  • 테스트로 뒷받침된다: 새로 작성했든 기존 것이든, 신뢰할 수 있는 테스트가 있다.

구체적으로, 각 시간 블록 동안 다음 흐름을 따릅니다.

  1. 먼저 테스트를 실행해 현재 상태를 확인합니다. 테스트가 불안정하다면, 그걸 안정화하거나 격리하는 작업을 별도 뽀모도로로 잡습니다.
  2. 한 가지 일관된 변경만 한다:
    • 메서드 추출
    • 개념 이름 변경(리네이밍)
    • 특정 로직을 새 클래스/함수로 이동
    • 하나의 기능 분기(branch)에 대한 테스트 추가
  3. 다시 테스트를 실행해 동작이 보존됐는지 확인합니다.
  4. 타이머가 울리면 멈춥니다. 더 하고 싶어도 거기까지.

하나의 뽀모도로 안에서 변경을 안전하고 테스트 그린 상태로 만들지 못한다면, 아마도 다음 둘 중 하나가 필요할 가능성이 큽니다.

  • 변경을 더 잘게 쪼개거나
  • 그 전에 테스트와 도메인 이해를 더 쌓는 데 시간을 투자하거나

4단계: 자주 커밋하고, 리팩터링 중심 메시지 쓰기

레거시 코드 리팩터링에선 자주, 목적이 분명한 커밋이 최고의 친구입니다. 이를 통해 다음이 가능해집니다.

  • 복잡한 영역 속에서 진행 상황을 추적하기
  • 시도했던 실험이 실패했을 때 쉽게 되돌리기
  • 미래의 나와 팀원들에게 의도를 명확히 전달하기

가능하다면 커밋 단위를 뽀모도로와 맞추는 것도 좋습니다. 한 블록이 끝날 때마다 다음 상태를 목표로 합니다.

  • 깨끗하게 동작하는 상태 (테스트 패스, 중간 변경 없음)
  • 방금 수행한 리팩터링 단위가 잘 드러나는 커밋

다음처럼 리팩터링 의도가 분명한 커밋 메시지를 활용해 보세요.

  • test: add characterization tests for invoice discounts
  • refactor: extract email template builder from OrderService
  • chore: rename legacy flag to isPreferredCustomer
  • refactor: simplify fee calculation branching logic

이렇게 하면 다음이 쉬워집니다.

  • 코드 리뷰에서 리팩터링 작업을 따라가고 이해하기
  • 어느 변경이 버그를 유입했을지 빠르게 좁혀보기
  • 거대한 뒤엉킨 diff 대신, 작은 단계 하나만 골라서 되돌리기

5단계: 타이머로 집중과 모멘텀 만들기

레거시 리팩터링은 정신 에너지를 많이 소모합니다. 뽀모도로 타이머는 다음을 돕습니다.

  • 집중 유지: 25분 동안은 이 작은 코드 조각만이 세상의 전부입니다.
  • 컨텍스트 스위칭 방지: 이메일, 슬랙, 인터넷 검색은 금지. 오직 리팩터링.
  • 모멘텀 형성: 완료한 각 뽀모도로가 작은 성취로 쌓입니다.
  • 과도한 확장 방지: 타이머가 끝나면, 변경 범위를 더 넓히지 않고 멈춥니다.

뽀모도로 중간에 또 다른 끔찍한 코드 조각을 발견하더라도, 당장 파고들고 싶은 충동을 참으세요. 대신 다음 뽀모도로를 위한 메모로 남깁니다.

  • "환불 로직 분기에 대한 테스트 추가"
  • "쿠폰 유효성 검사 규칙 추출"
  • "설정(configuration) 로딩 로직 단순화"

이렇게 하면 지금 진행 중인 변경을 작고 안전하게 유지하면서, 나중에 개선할 수 있는 타깃 목록(backlog)을 쌓을 수 있습니다.


예시: 4개 뽀모도로 리팩터링 세션

약 2시간(4 뽀모도로)짜리 리팩터링 세션이 어떻게 보일 수 있는지 예를 들어보겠습니다.

뽀모도로 1 (25분)
목표: 할인 계산 로직 이해 및 테스트 추가.

  • calculateDiscounts()를 읽고 주요 분기들을 파악합니다.
  • 핵심 시나리오 3–4개에 대한 특성화 테스트를 추가합니다.
  • 테스트를 실행하고, 너무 불안정한 부분이 있으면 고칩니다.
  • 커밋: test: add characterization tests for discount calculation

뽀모도로 2 (25분)
목표: 할인 계산 로직의 가독성 향상.

  • 긴 두 개의 분기를 applyLoyaltyDiscount, applySeasonalDiscount로 추출합니다.
  • x, temp, flag 같은 애매한 변수명을 구체적인 비즈니스 용어로 변경합니다.
  • 테스트를 실행해 모두 통과하는지 확인합니다.
  • 커밋: refactor: extract discount branches into named helpers

뽀모도로 3 (25분)
목표: 엣지 케이스에 대한 테스트 커버리지 확대.

  • 금액 0원 주문, 최대 할인 상한, 잘못된 입력 등에 대한 테스트를 추가합니다.
  • 테스트 이름을 더 설명적으로 바꿔, 까다로운 동작을 문서화합니다.
  • 커밋: test: cover discount edge cases and clarify test names

뽀모도로 4 (25분)
목표: 분기 구조 단순화.

  • 중첩된 if 체인을 더 명확한 가드 절(guard clause) 스타일로 바꿉니다.
  • 조기 리턴(early return)을 사용해 들여쓰기를 줄입니다.
  • 테스트를 실행해 동작이 그대로인지 확인합니다.
  • 커밋: refactor: simplify discount branching with guard clauses

이제 다음을 달성했습니다.

  • 테스트로 현재 동작을 고정했습니다.
  • 구조와 네이밍을 명확하게 만들었습니다.
  • 중요한 함수의 복잡도를 줄였습니다.

할인 영역 전체가 이전보다 훨씬 나아졌고, 그 과정에서 “처음부터 다시 쓰기”를 시도할 필요는 없었습니다.


언제 리팩터링을 멈출 것인가

리팩터링의 난점 중 하나는 언제 그만해야 할지 판단하기 어렵다는 점입니다. 뽀모도로는 이걸 주기적으로 재평가할 수 있는 자연스러운 타이밍을 제공합니다.

몇 개의 블록을 진행할 때마다 스스로에게 물어보세요.

  • 지금 이 영역은 당분간 “충분히 괜찮은” 상태인가?
  • 남은 문제는 리스크가 낮거나 비핵심적인가?
  • 추가 리팩터링이 여전히 투자 대비 효과가 큰가, 아니면 이제는 폴리싱(광택 내기)에 가까운가?

비즈니스 가치나 리스크 감소 효과가 줄어들고 있다고 느껴진다면, 백로그 메모를 잠시 접어두고 다른 일로 넘어가도 좋습니다. 레거시 코드 개선은 스프린트가 아니라 마라톤입니다.


결론: 가장 무서운 레거시 코드도 작은 타이머로 길들일 수 있다

레거시 코드는 사라지지 않습니다. 하지만, 이를 대하는 방식은 “전부 갈아엎기” 아니면 “영원히 방치하기” 둘 중 하나일 필요가 없습니다.

다음 요소들을 결합하면:

  • 짧고 고정된 시간 블록(뽀모도로)
  • 명확한 목표를 가진 작고 독립적인 리팩터링 단위
  • 동작을 앵커로 삼는 테스트
  • 작고 되돌리기 쉬운 변경 + 잦은 커밋
  • 이해하기(테스트)와 개선하기(리팩터링)를 번갈아 하는 리듬

…가장 무서운 코드베이스의 일부도 안정적으로, 꾸준히 개선해 나갈 수 있습니다.

전체 리라이트가 필요하지 않습니다. 필요한 건 타이머 하나, 테스트 스위트 하나, 그리고 잘게 나눈 한 걸음씩을 밟아 나가려는 규율입니다.

타이머를 맞추세요. 아주 작은 개선 하나를 정하세요. 타이머가 울릴 때까지 리팩터링하세요.

그리고, 다시 반복하면 됩니다.

뽀모도로 리팩터링: 작은 시간 사이클로 위험한 레거시 코드를 안전하게 다루는 법 | Rain Lag