아날로그 코딩 플라이트 데크: 다음 대규모 리팩터를 위한 물리적 컨트롤 패널 설계하기
Copilot, Claude, Cursor 같은 AI 코딩 도구를 비행기 조종석의 계기판처럼 활용해, 위험한 빅뱅식 재작성 대신 점진적인 리팩터로 더 높은 정밀도·가시성·통제력을 확보하는 방법을 살펴봅니다.
아날로그 코딩 플라이트 데크: 다음 대규모 리팩터를 위한 물리적 컨트롤 패널 설계하기
리팩터에는 안 좋은 전설이 있습니다. “금방 끝나는 간단한 정리”로 시작해서 “3개월짜리 죽음의 소용돌이”로 끝나는 일 말이죠. 코드는 온 힘을 다해 저항하고, 마감은 미끄러지며, 모두가 조용히 다짐합니다. “저 모듈은 두 번 다시 안 건드린다.”
꼭 그렇게 흘러갈 필요는 없습니다.
다음 대규모 리팩터를 절벽에서 뛰어내리는 일로 보지 말고, 비행기를 조종하는 일로 생각해 봅시다. 파일럿은 감(感)에만 의존하지 않습니다. 계기, 절차, 체크리스트에 의존합니다. 여러분도 현대적인 AI 코딩 도구를 활용해 똑같이 할 수 있습니다. 코드베이스를 다시 빚어가는 동안 가시성, 정밀도, 안전장치를 제공하는 플라이트 데크(flight deck) 로 바꾸는 것입니다.
이 글에서는 Copilot, Claude, Cursor 같은 AI 도구들을 리팩터를 위한 물리적 컨트롤 패널처럼 다루는 방법을 살펴봅니다. 다음을 배우게 될 것입니다.
- 코드 분석, 계획 수립, 리스크 탐지용 계기판으로 AI를 활용하는 방법
- 위험한 빅뱅식 재작성 대신 레거시 시스템을 점진적으로 현대화하는 방법
- 위험이 큰 영역을 추상화와 인터페이스 뒤로 격리하는 방법
- 기존 동작을 유지하면서 AI의 도움을 받아 복잡한 함수들을 분해하는 방법
- 명시적인 레버, 스위치, 인디케이터로 구성된 “리팩터 컨트롤 패널” 을 설계하는 방법
왜 리팩터에는 플라이트 데크가 필요한가
리팩터는 단순히 코드를 수정하는 작업이 아닙니다. 불확실성 속에서 시스템 전반을 바꾸는 일입니다.
- 현재 동작을 완전히 알지 못합니다. 문서는 오래됐고, 테스트는 들쭉날쭉하며, 코드를 작성한 사람들은 이미 회사를 떠났을 수 있습니다.
- 변경의 폭발 반경(blast radius)이 불분명합니다. 공용 유틸리티의 작은 변경이 여러 서비스에 걸쳐 데이터를 조용히 잘못 저장할 수 있습니다.
- 시간 압박이 현실적입니다. 기능 개발, 마감, 기술 부채를 동시에 억지로 끌고 가야 합니다.
계측 장비 없이 비행한다면, 사실상 깜깜한 밤에 눈 감고 나는 셈입니다.
AI 도구는 이 상황을 바꿀 수 있습니다. 제대로 사용하면, 리팩터를 안정적으로 유지해 주는 계기, 경고등, 오토파일럿이 됩니다.
- 자동화된 코드 분석 및 호출 그래프 탐색
- 라이브러리·프레임워크·언어 기능에 대한 업그레이드 제안
- 단계별 현대화를 돕는 가이드형 워크플로우
- 리스크, 커버리지, 영향 범위에 대한 지속적인 피드백
핵심은 AI를 마법 같은 “코파일럿”으로 생각하는 태도에서 벗어나, 여러분이 의도적으로 설계하는 구성 가능한 플라이트 데크로 취급하는 것입니다.
계기판: AI 도구를 리팩터 계측 장비로 바꾸기
AI 도구마다 플라이트 데크에서 잘하는 역할이 다릅니다. 전부를 똑같은 채팅창으로 쓰지 말고, 특화된 계측 장비처럼 사용하세요.
1. Copilot(및 유사 인라인 어시스턴트): 로컬 수정용 오토파일럿
잘 맞는 용도:
- 단일 파일 내 로컬 리팩터
- 반복적인 현대화 패턴 적용
- 메서드 추출, 변수 이름 변경, 코드 이디엄 업데이트
계측 장비처럼 사용하는 법:
- Copilot이 리팩터 제안을 하도록 두되, 방향은 여러분이 잡습니다. (추상화 경계와 이름은 직접 결정하세요.)
- 패턴을 표준화합니다. 예: “이 파일에서 레거시 콜백을 모두 async/await 패턴으로 변경해 줘.”
- 여러 호출 지점에 걸쳐 작고 기계적인 수정들을 안전하게 전파하는 데 활용합니다.
2. Claude, Cursor, 멀티파일 에이전트: 시스템 내비게이터
잘 맞는 용도:
- 모듈 간 의존성 이해
- 프레임워크/라이브러리 마이그레이션 같은 업그레이드 경로 제안
- 리팩터 계획과 분해 전략 설계
계측 장비처럼 사용하는 법:
- 기적이 아닌, 지도(map) 를 요구합니다. 예: “
BillingAdapter를 사용하는 모든 위치를 찾아서, 리스크 수준별로 분류해 줘.” - 단계적 업그레이드 플랜을 제안하게 합니다. 예: “이 코드베이스 기준으로, 우리 커스텀 ORM에서 벗어나는 3단계 안전 마이그레이션 플랜을 제안해 줘.”
- 구조화된 변경 계획을 생성하게 하고, 실제 구현과 검증은 여러분이 수행합니다.
3. 정적 분석기, 린터, CI 체크: 경고등
이들은 엄밀히 말하면 AI는 아니지만, 플라이트 데크의 일부로 보는 것이 맞습니다.
- 린터는 일관성 문제와 스타일 이슈를 드러냅니다.
- 정적 분석기는 널 가능성, 타입, 데이터 흐름 리스크를 강조합니다.
- CI 게이트는 깨진 변경이 머지되지 않도록 막아 줍니다.
AI와 결합해 이렇게 물어볼 수 있습니다.
- “이 정적 분석 경고를 설명해 주고, 안전한 수정안과 테스트 코드를 함께 제안해 줘.”
점진적 현대화: 한 번의 거대한 도약이 아닌, 수많은 작은 착륙
가장 위험한 리팩터는 한 번에 모든 것을 바꾸려는 리팩터입니다.
빅뱅식 재작성(big-bang rewrite) 은 이론상으로는 아주 깔끔해 보입니다. “그냥 새로, 제대로 다시 만들자.” 하지만 실제로는:
- 너무 오래 걸리고
- 진행 중에 요구사항이 바뀌면서 현실과 점점 어긋나며
- 롤백이 극도로 어렵습니다.
대신, 서비스 전반을 표준화하고 현대화하되, 점진적으로 진행해야 합니다.
실용적인 점진적 전략
-
목표 표준을 정의합니다.
- 예: “모든 서비스는 새 HTTP 클라이언트 래퍼와 구조화 로깅을 사용해야 한다.”
-
작은 범위부터 시작합니다.
- 하나의 서비스, 하나의 모듈, 혹은 하나의 수직 슬라이스.
-
AI로 패턴을 찾습니다.
- “이 서비스에서
legacyHttpClient를 사용하는 모든 위치를 찾고, 호출 패턴을 보여줘.” - 이렇게 모은 패턴을 바탕으로 단일 마이그레이션 레시피를 정의합니다.
- “이 서비스에서
-
레시피를 자동화합니다.
- Cursor 같은 도구에 이렇게 정의합니다. “이 호출 시그니처를 새 래퍼로 교체하되, 기존 에러 처리 의미는 그대로 보존해 줘.”
-
서비스 단위로 롤아웃합니다.
- 각 롤아웃은 지면이 어디 있는지 짐작하는 낙하가 아니라, 작고 관측 가능한 착륙입니다.
현대화를 수많은 작고 되돌릴 수 있는 단계로 취급하면, 전체 시스템을 위태롭게 만들지 않고도 멈추고, 측정하고, 조정할 수 있습니다.
폭발 반경 줄이기: 위험 구역을 인터페이스 뒤로 격리하기
모든 대규모 리팩터에는 위험 구역(danger zone) 이 있습니다.
- 트랜잭션 경계(결제, 청구, 인증 등)
- 여러 서비스에서 공유하는 유틸리티
- 분석/리포팅으로 이어지는 데이터 변환 계층
이 부분들을 코드베이스 전체에서 직접, 한 번에 리팩터 하고 싶지는 않을 것입니다. 대신, 다음과 같이 폭발 반경을 줄이세요.
-
추상화나 인터페이스를 도입합니다.
- 예: 새
PaymentGateway인터페이스나UserProfileService경계를 정의합니다.
- 예: 새
-
기존 동작을 그 경계로 우회시킵니다.
- 초기에 구현체는 레거시 코드를 얇게 감싸는 래퍼에 불과할 수 있습니다.
-
추상화 뒤에서 리팩터를 진행합니다.
- 외부에 노출된 인터페이스는 그대로 둔 채, 내부 구현만 마음껏 바꿉니다.
이때도 AI를 활용할 수 있습니다.
- “
LegacyBillingManager가 현재 수행하는 모든 작업을 포괄하는 인터페이스를 제안해 주고, 어떤 호출자들이 이 인터페이스에 의존하게 될지 보여줘.” - “기존 함수들에 위임(delegate)하지만 더 깔끔한 API를 노출하는 파사드(façade) 클래스를 생성해 줘.”
변경을 격리하면, 현대화를 위한 안전한 샌드박스를 만들 수 있습니다.
골치 아픈 괴물 함수, AI로 분해하기
다들 한 번쯤은 봤을 것입니다. 500줄이 넘고, 책임이 여럿 섞여 있고, 여기저기 손대는, 건드리기 너무 무서운 함수 말입니다.
AI는 이런 혼돈 속에서 구조를 찾아내는 데 특히 강합니다.
-
책임(responsibility) 맵을 요청합니다.
- “이 함수가 수행하는 일을, 서로 다른 책임과 사이드 이펙트 기준으로 설명해 줘. 항목별로 나눠서.”
-
추출 후보를 정의합니다.
- “동작을 그대로 유지하면서 추출할 수 있는 작은 순수 함수 또는 메서드 후보를 제안해 줘.”
-
하나씩 추출하고 검증합니다.
- 한 번에 한 조각씩 추출합니다.
- 테스트(또는 AI가 생성한 테스트)를 사용해 기존 동작을 검증합니다.
-
동작을 단단히 고정(anchor)해 둡니다.
- AI에게 명시적으로 지시합니다. “외부 동작은 절대 바꾸지 말고, 의미·엣지케이스·에러 경로를 모두 보존한 채로 리팩터만 수행해 줘.”
이 영역이야말로 AI가 리팩터 어시스턴트로 빛나는 곳입니다. 잠재된 구조를 보여주는 역할은 AI가 하고, 무엇을 어떤 이름으로, 어떤 순서로 정리할지는 여전히 여러분이 결정합니다.
리팩터 컨트롤 패널 설계하기: 레버, 스위치, 인디케이터
“X를 리팩터하자”라는 모호한 목표 대신, 작업을 물리적인 컨트롤 패널처럼 설계해 봅시다.
1. 레버(Lever): 큰 방향 전환
레버는 상위 수준의 계획 파라미터입니다.
- 범위 레버: 어떤 모듈/서비스를 포함할지, 제외할지
- 추상화 레버: 어떤 인터페이스나 경계를 먼저 도입할지
- 표준 레버: 어떤 코딩 표준·프레임워크를 목표로 삼을지
AI를 활용해 레버를 다듬을 수 있습니다.
- “현재 코드 구조를 보면, 먼저 새 인터페이스를 도입하는 편이 안전할까, 아니면 가장 큰 함수들부터 리팩터하는 게 나을까?”
2. 스위치(Switch): 안전 경계
스위치는 온/오프 가능한 안전 조건을 정의합니다.
- 피처 플래그: 환경·테넌트별로 새 동작을 켜고 끄기
- 호환 모드: 마이그레이션 기간 동안 구·신 코드 경로를 모두 지원하기
- 롤백 전략: 명확하고 문서화된 되돌리기 절차
AI는 다음을 도와줄 수 있습니다.
- 피처 플래그가 적용된 진입 지점을 설계하기
- 롤백 체크리스트를 자동으로 생성하기
3. 인디케이터(Indicator): 지표와 피드백
인디케이터는 지금 상승 중인지, 실속(stall) 상태인지 알려줍니다.
- 대상 모듈의 테스트 커버리지
- 변경 전후 에러율·지연 시간(latency)
- 배포 빈도와 롤백 빈도
- 정적 분석 경고 수
AI 쿼리와 결합하면, 예를 들어 이렇게 쓸 수 있습니다.
- “
UserService를 건드린 최근 세 개의 리팩터 PR에서, diff와 관련 테스트 결과를 요약해 줘.”
인디케이터가 구체적일수록, 리팩터는 직관 대신 데이터에 기대어 진행할 수 있습니다.
언제(그리고 정말 그럴 때만) 빅뱅식 재작성도 의미가 있는가
가끔은 점진적인 변경만으로는 충분하지 않을 때도 있습니다. 예를 들어:
- 현재 요구사항에 비해 아키텍처 자체가 근본적으로 잘못되었거나
- 사용하는 기술 스택이 더 이상 지원 불가 상태(지원 종료된 언어나 프레임워크 등)거나
- 어댑터와 호환 계층을 유지하는 비용이, 한 번에 갈아엎는 비용보다 더 클 때
그렇다 해도, 이것을 잘 계획된 대규모 기동(maneuver) 으로 다뤄야지, 믿음의 도약으로 다루면 안 됩니다.
- AI를 사용해 기존 동작을 전수 조사하고, 모든 중요 플로우를 식별합니다.
- 가능하다면, 하드 컷오버 대신 호환(shim)·마이그레이션 계층을 설계합니다.
- 완전 전환 전에, 구·신 경로를 병행 실행(섀도 모드)해 봅니다.
빅뱅식 재작성은 드물어야 하고, 명확하게 타당성이 있어야 하며, 계측된 상태여야 합니다.
결론: 리팩터를 ‘조종’하라, 그냥 자유낙하하지 말라
리팩터에는 언제나 리스크가 따릅니다. 하지만 “위험하다”가 꼭 “무모하다”를 의미할 필요는 없습니다. 플라이트 데크 관점을 채택하면 다음을 할 수 있습니다.
- AI 도구를 분석·계획·실행을 위한 계기(instrument) 로 사용하고
- 한 번에 다 갈아엎는 재작성 대신 점진적 현대화를 선호하며
- 위험이 큰 구역을 추상화·인터페이스 뒤로 격리하고
- AI의 도움을 받아 복잡한 함수를 분해하되, 올바른 동작은 여러분이 끝까지 지키며
- 레버·스위치·인디케이터로 구성된 컨트롤 패널을 설계해 진행 상황을 가시화할 수 있습니다.
다음 번에 무서운 리팩터를 앞에 두고 에디터부터 열고 “에라 모르겠다” 하지 마세요. 먼저 플라이트 데크를 구축하세요. 어떤 컨트롤이 있는지 정의하고, 계기를 켜고, 데이터를 보며 움직이세요. 그러면 코드베이스를 새 아키텍처로 옮길 때, 한 번에 떨어지지 않고 통제된 기동을 반복하며 안전하게 착륙시킬 수 있습니다.