Rain Lag

아날로그 리팩터 스튜디오: 레거시 코드를 건드리기 전에 종이 위에서 설계하기

거대한 레거시 C/C++ 시스템을 리팩터링하기 전에, 종이 워크스루와 원자적 커밋, 탄탄한 테스트, AI 보조 도구를 활용해 한 줄의 프로덕션 코드도 바꾸지 않고 안전하게 설계하는 방법.

아날로그 리팩터 스튜디오: 레거시 코드를 건드리기 전에 종이 워크스루 설계하기

레거시 C/C++ 시스템은 작고, 깔끔하고, 잘 문서화되어 있는 경우가 거의 없습니다. 대개는 지저분하고, 미션 크리티컬하며, 누구도 전체를 완전히 이해하지 못할 만큼 큽니다. 그래도 이 시스템들은 계속 발전해야만 합니다.

20년 된 코드베이스를 열어 보고 이렇게 생각한 적이 있을 겁니다. “이걸 안전하게 리팩터링하는 건 불가능해 보이는데…” 혼자가 아닙니다. 핵심은, 이런 모놀리스를 한 번에 “영웅적으로” 갈아엎는 방식으로는 리팩터링할 수 없다는 사실을 인정하는 것입니다. 대신, 프로덕션 코드를 한 줄이라도 건드리기 전에 종이 위에서 차분하게 수를 짜는 것이 필요합니다.

이 아이디어를 체계화한 것이 바로 아날로그 리팩터 스튜디오(Analog Refactor Studio) 입니다. 종이 워크스루, 테스트 스캐폴딩, 원자적 변경을 활용해 대규모 리팩터를 설계하고 검증하는, 의도적이고 저기술(low-tech)이지만 영향력은 높은 접근법입니다.


왜 거대 레거시 리팩터를 “원샷”으로 할 수 없는가

ASML이나 Atlassian 같은 회사의 대형 C/C++ 코드베이스는 보통 다음과 같은 특징을 갖습니다.

  • 수백, 수천 개의 모듈
  • 수십 년에 걸쳐 쌓인 뒤엉킨 의존성
  • 부족하거나 오래된 문서
  • 한때 시스템을 잘 알았지만 이미 떠난 사람들

“한 번에 다 고치는” 거대한 리팩터를 시도하면 결국 다음과 같은 상황에 빠지기 쉽습니다.

  • 통제 불가능한 스코프 확장 – 작업 중 계속 새로운 의존성과 엣지 케이스가 튀어나옴
  • 장수 브랜치 – 최신 상태 유지도 고통스럽고, 머지도 위험함
  • 프로덕션 장애 – 테스트(있다면)로 잡기 어려운 미묘한 동작 변화

대신, 다음과 같이 접근해야 합니다.

  1. 어디서 시작할지 우선순위를 정한다. 성능, 유지보수성, 정확성 측면에서 개선 시 이득이 가장 큰 부분은 어디인가?
  2. 리팩터의 경계를 또렷하게 정의한다. 아키텍처 전체가 아니라 모듈 하나를 리팩터링한다.
  3. 코딩 전에 단계별 계획을 세운다. 여기서 아날로그 리팩터 스튜디오가 등장합니다.

아날로그 리팩터 스튜디오란 무엇인가?

아날로그 리팩터 스튜디오(Analog Refactor Studio) 는 종이를 중심에 둔 구조화된 세션입니다. 이 자리에서 다음과 같은 일을 합니다.

  • 리팩터에 관련된 코드를 맵으로 그려보고
  • 리팩터링 단계와 중간 상태를 스케치하고
  • ABI 변경, 인터페이스 깨짐, 순환 의존성 같은 위험 요소를 식별하고
  • 어떤 테스트로 성공을 검증할지 정의합니다.

이 모든 것을 IDE를 열기 전에 합니다.

목표는 리팩터를 지루할 정도로 예측 가능하게 만드는 것입니다. 실제로 코드를 작성할 즈음에는 이미 다음을 알고 있어야 합니다.

  • 어떤 파일을 건드릴지
  • 어떤 순서로 바꿀지
  • 각 단계마다 시스템을 어떻게 빌드 가능하고 테스트 가능하게 유지할지

이는 특히 C/C++처럼 빌드 시간, 링크 타임 의존성, 바이너리 호환성이 모두 중요한 대형 시스템에서 매우 효과적입니다.


1단계: 올바른 시작점을 고르기

모든 것을 다 리팩터할 수는 없습니다. 다음과 같은 영역부터 시작해야 합니다.

  • 레버리지가 큰 곳 – 로깅, 설정, 수학 유틸리티처럼 여러 곳에서 쓰이는 모듈은 한 번 정리하면 전체에 파급 효과가 큼
  • 고통이 큰 곳 – 버그가 잦거나 유지보수가 어려워 새로운 기능 추가를 가로막는 부분
  • 동작이 비교적 안정적인 곳 – 기능 요구사항이 자주 바뀌지 않아, 구조 개선에 집중할 수 있는 로직

스스로 혹은 팀에 이렇게 물어보세요.

  • 어느 컴포넌트가 변경 핫스팟인가?
  • 개발자들이 반복적으로 불평하거나 막히는 지점은 어디인가?
  • 이 부분이 개선되면 개발 속도나 결함률이 눈에 띄게 좋아질 영역은 어디인가?

Atlassian, ASML 같은 대형 플랫폼 팀들이 반복해서 확인한 사실이 있습니다. 국소적이지만 레버리지가 큰 리팩터가 가장 이득이 크다는 점입니다.


2단계: 테스트 안전망 구축 및 강화

리팩터링이란 “동작은 그대로 둔 채 구조를 바꾸는 것”입니다. 여기서 동작을 그대로 둔다는 말은, 동작을 측정할 수 있을 때에만 의미가 있습니다.

리팩터 전에 다음을 갖추는 것이 좋습니다.

  1. 변경하려는 로직에 대한 자동화된 단위 테스트
  2. 핵심 워크플로를 검증하는 통합 테스트 또는 시스템 테스트
  3. 빠른 피드백 루프 – 테스트가 자주, 안정적으로 돌 수 있어야 함

레거시 C/C++ 코드에서는 보통 이런 작업이 필요합니다.

  • 복잡한 함수를 추출해 테스트 가능한 단위로 쪼개기
  • 오래된 라이브러리 주변에 테스트 하니스(harness) 추가
  • 하드웨어, 네트워크, 파일시스템 같은 외부 의존성을 스텁으로 대체할 수 있도록 추상화 계층 도입

리팩터링 이전에 테스트가 많을수록, 구조를 과감하게 바꾸면서도 동작을 안정적으로 유지할 수 있습니다.


3단계: 아날로그 리팩터 세션 실행(종이 워크스루)

이제 아날로그 리팩터 스튜디오를 실제로 만들어 봅니다. 화이트보드, 인덱스 카드, 노트, 혹은 종이 대용으로 쓰는 협업 다이어그램 도구 등 무엇이든 상관없습니다. 중요한 건 종이처럼 쓴다는 점입니다.

3.1 현재 상태를 맵으로 그리기

종이 위에 다음을 정리합니다.

  • 핵심 struct/class와 그 주요 책임
  • 중요한 함수와 API
  • 컴포넌트 간 직접 의존 관계

코드베이스 전체를 다이어그램으로 표현하려고 할 필요는 없습니다. 다음을 파악할 수 있을 만큼만 그리면 됩니다.

  • 데이터가 어떻게 흐르는지
  • 의존성이 어디서 뒤엉키는지
  • 어디에 안전하게 “절개선(seam)”을 넣을 수 있는지

3.2 목표 상태 설계하기

다음으로 목표 설계(target design) 를 스케치합니다.

  • 어떤 책임이 어디로 옮겨지는가?
  • 어떤 새로운 인터페이스나 추상화가 생기는가?
  • 어떤 의존성을 제거하거나, 반전(inversion)시키는가?

예를 들어, 다음과 같은 일을 할 수 있습니다.

  • 하나의 거대한 모듈에 섞여 있던 파일 포맷 파서를 별도 라이브러리로 추출
  • raw pointer와 수동 메모리 관리를 RAII 래퍼로 대체
  • 런타임에 서로 다른 백엔드를 교체할 수 있도록 인터페이스 도입

이 스케치는 완성도 높은 UML일 필요가 없습니다. 박스와 화살표만으로도 충분합니다.

3.3 중간 단계 계획하기

대형 리팩터가 실패하는 가장 흔한 이유는 이전(before)이후(after) 상태만 정의하고, 그 사이의 여정을 건너뛰기 때문입니다.

종이 위에서 작고 일관된 단계들의 시퀀스 를 정의합니다.

  1. 기존 인터페이스와 나란히 새 인터페이스를 도입한다.
  2. 호출자를 하나씩 새 인터페이스로 옮긴다.
  3. 모든 호출자가 이동하면, 옛 인터페이스를 제거한다.

각 단계는 다음을 만족해야 합니다.

  • 컴파일 가능
  • 테스트 실행 가능
  • 동작은 그대로 유지

이렇게 하면 커밋 단위로 검증 가능한 체크포인트 들을 만들 수 있습니다.

3.4 위험 요소와 검증 방법 식별하기

각 단계마다 다음을 적어 둡니다.

  • 무엇이 잘못될 수 있는가? (예: ABI 깨짐, 초기화 순서의 미묘한 변화, 타이밍 이슈 등)
  • 어떤 테스트가 이를 잡을 수 있는가? (단위 테스트, 통합 테스트, 퍼징, 성능 테스트 등)

예를 들어 ASML에서 성능 크리티컬 컴포넌트를 리팩터링할 때, 보통 구조 리팩터와 함께 성능 회귀 테스트 를 붙여서 예기치 않은 성능 저하가 스며들지 않도록 합니다.


4단계: 원자적이고 테스트된 리팩터 커밋으로 실행하기

종이 워크스루가 안정되면, 이를 코드로 구현합니다. 보통 다음과 같은 형태가 됩니다.

  • 커밋(또는 작은 커밋 묶음)마다 논리적으로 원자적인 리팩터 하나씩
  • 각 커밋은 테스트가 통과한 상태로 유지

원자적 리팩터 커밋(atomic refactor commit) 의 목표는 다음과 같습니다.

  • 필요하다면 많은 파일을 수정하되
  • 동작은 바꾸지 않고 구조만 바꿀 것
  • 시스템이 계속 빌드 가능하고 릴리즈 가능한 상태를 유지할 것

이 방식이 유효한 이유는 다음과 같습니다.

  • 리뷰어는 일관되고 응집력 있는 변경을 한 번에 볼 수 있습니다.
  • main(또는 trunk)에 “반쯤 마이그레이션된” 상태가 남지 않습니다.
  • 문제가 생기면 git bisect 으로 원인 커밋을 빠르게 찾을 수 있습니다.

변경 규모가 아주 클 경우에는 다음과 같이 할 수 있습니다.

  • 시스템을 항상 안정적으로 유지하는 여러 개의 원자적 단계로 나눔
  • 공용 인터페이스를 바꿀 때는 기능 플래그나 dual-API 기간을 활용

종이 설계 + 원자적 커밋 + 테스트 조합은, 한 번에 많은 파일을 건드리더라도 거대하고 오래된 시스템을 놀랄 만큼 안정적으로 유지하게 해 줍니다.


5단계: 크로스 프로젝트 리팩터를 안전하게 처리하기

일부 리팩터는 여러 리포지토리나 서비스에 걸쳐 영향을 미칩니다. 대규모 조직에서 흔한 상황입니다.

안전한 크로스 프로젝트 전략은 보통 다음과 같습니다.

  1. 공용 라이브러리에 새 API를 추가 하되, 기존 API는 유지한다.
  2. (여러 리포에 걸친) 모든 소비자 코드를 새 API로 업데이트 한다.
  3. 모든 소비자 마이그레이션이 끝난 후에야 기존 API를 제거 한다.

이를 조율하는 방법으로는 다음을 활용할 수 있습니다.

  • 버전이 있는 패키지나 공용 라이브러리 릴리즈
  • 명확한 디프리케이션(deprecation) 계획과 타임라인 커뮤니케이션
  • 가능하다면 소비자 코드 업데이트 자동화

이런 방식은 시스템 레벨에서의 원자성을 지켜 줍니다. 어느 소비자든 항상 어떤 버전의 공용 라이브러리와는 호환되며, CI 파이프라인이 그 호환성을 검사합니다.


6단계: 스튜디오에 AI 보조 도구 끌어들이기

Rovo Dev 같은 현대적인 AI 보조 개발 도구들은, 특히 매우 큰 코드베이스에서 아날로그 리팩터 스튜디오의 강력한 동반자가 될 수 있습니다.

AI를 다음과 같이 활용할 수 있습니다.

  • 익숙하지 않은 코드 영역을 빠르게 탐색하고 요약
  • 목표 설계와 제약 조건을 바탕으로 리팩터링 단계 제안
  • 수백 개 파일에 걸친 기계적인 수정 작업(예: 이름 변경, 시그니처 변경, 래퍼 삽입)을 자동화
  • 기존 사용 패턴과 코드를 분석해 테스트 초안 생성

다만 AI를 자동 조종 장치 가 아니라 전동 공구(power tool) 로 대하는 것이 중요합니다.

  • 아날로그 리팩터 세션에서 전체 계획은 사람이 세운다.
  • AI 도구는 반복 작업을 실행하고 패턴을 제안하는 데 도움을 준다.
  • 올바른지 여부와 의도 적합성은 결국 테스트와 코드 리뷰로 검증한다.

Atlassian, ASML 등 대규모 시스템을 다루는 팀들은 다음 세 가지를 결합했을 때:

  • 신중한 사전 설계
  • 강력한 테스트 커버리지
  • 자동화와 AI 보조 리팩터링

이전에는 상상도 못 했던 규모의 레거시 리팩터가 현실적인 옵션 으로 바뀐다는 사실을 체감하고 있습니다.


마무리: 모든 것을 종합하기

대형 레거시 C/C++ 시스템에서의 대규모 리팩터는, 영웅적인 코딩 실력보다 체계적인 엔지니어링 에 더 가깝습니다.

아날로그 리팩터 스튜디오는 다음과 같은 실질적인 순서를 제공합니다.

  1. 레버리지가 크면서 범위가 명확한 리팩터 타깃 을 정한다.
  2. 반드시 보존해야 할 동작 주변에 테스트 안전망을 구축 한다.
  3. 머릿속이 아니라 종이 위에 현재와 목표 설계를 그려본다.
  4. 빌드 가능하고 검증 가능한 중간 단계 를, 위험 요소와 검증 방법까지 포함해 계획한다.
  5. main 브랜치를 항상 안정적으로 유지하면서 원자적이고 테스트된 커밋 들로 실행한다.
  6. dual-API와 버전 관리 로 크로스 프로젝트 변경을 조율한다.
  7. 설계의 주도권은 사람이 쥔 채, AI 도구로 기계적인 작업을 가속 한다.

다음번에 위압적인 레거시 코드베이스를 마주했을 때, “일단 리팩터부터 시작해 보자”는 충동을 잠시만 참아 보세요. 대신 노트를 펼치고, 아날로그 리팩터 스튜디오에 들어온 것처럼 워크스루를 먼저 설계 하십시오.

초반에는 더 느리게 움직이는 것 같겠지만, 전체적으로 보면 훨씬 더 빠르고, 안전하며, 자신감 있게 전진할 수 있을 겁니다.

아날로그 리팩터 스튜디오: 레거시 코드를 건드리기 전에 종이 위에서 설계하기 | Rain Lag