Rain Lag

아날로그 디버깅 필드 컴퍼스: 거대한 코드베이스에서 길 잃지 않는 포켓 지도

“필드 컴퍼스”라는 멘탈 모델, AI 도구, 그리고 살아 있는 지도를 활용해 거대하고 낯선 코드베이스를 길 잃지 않고, 압도되지 않으면서 탐색·디버깅하는 방법을 다룹니다.

아날로그 디버깅 필드 컴퍼스: 거대한 코드베이스에서 길 잃지 않는 포켓 지도

거대한 코드베이스는 소프트웨어라기보다 지형에 더 가깝게 느껴집니다.

당신은 단순히 “프로젝트를 연다”기보다, 그 안으로 들어갑니다.

뷰 컴포넌트의 계곡, 서비스의 산맥, 오래된 유틸리티의 고대 유적, 그리고 아무도 소유를 인정하지 않는 스크립트로 이루어진 지하 동굴까지. 이런 시스템에서 버그가 나타나면, 울창한 숲에서 길 잃은 하이커가 된 기분이 들기 쉽습니다. 어느 방향을 봐도 다 비슷해 보이고, 모든 파일이 비슷해 보이며, 길이 어디로 이어지는지 알 수 없습니다.

이런 시스템에서 디버깅하려고 슈퍼 인간급 기억력이나 10년짜리 경력이 필요한 건 아닙니다. 필요한 건 **필드 컴퍼스(field compass)**입니다.

이 글에서는 거대하고 낯선 코드베이스를 자신 있게 탐색하기 위한 멘탈 모델과 실용적인 도구 세트, 즉 **아날로그 디버깅 필드 컴퍼스(Analog Debugging Field Compass)**를 만들어 볼 것입니다. 전체 지형을 어떻게 그려 나갈지, AI의 폭넓은 탐색 능력과 인간의 깊이 있는 이해를 어떻게 결합할지, 그리고 난잡한 시스템을 어떻게 경쟁 우위로 바꿀지 다룹니다.


왜 거대한 코드베이스에서 길을 잃게 되는가

대부분의 디버깅 조언은 두 가지를 전제로 합니다.

  1. 시스템을 대체로 이해하고 있다.
  2. 버그는 이미 알고 있는 코드 근처에 있다.

하지만 수년에 걸쳐 커진 거대 코드베이스에서는 이 두 전제가 모두 깨집니다.

  • 코드가 한 사람이 머릿속에 담기에는 너무 크다. 파일을 처음부터 끝까지 순서대로 읽는다고 해서 “한 번에 딱 이해”되는 일이 없습니다.
  • 오너십이 모호하다. 코드를 작성한 사람은 떠났거나, 도메인이 네 번쯤 바뀌었습니다.
  • 문서는 오래되었거나 애초에 없다. 설계 문서와 실제 구현은 세 번의 리팩터링 전에 이미 갈라섰습니다.

결국 우리가 의지하게 되는 건 이런 ‘지역 전술’입니다. grep으로 이리저리 찾고, IDE에서 아무 파일이나 클릭해 보고, 로그를 여기저기 뿌려 보면서, 우연히 범인을 발견하길 기대하는 식이죠. 이건 숲 속에서 같은 자리를 계속 빙빙 돌면서, 가끔 “거기 누구 없어요?” 하고 소리친 뒤 메아리를 기다리는 것과 비슷합니다.

필드 컴퍼스 모델은 이 문제를 이렇게 해결합니다.

우선 전체 방향을 잡고(global orientation), 그다음에 국소적인 디테일(local detail)에 들어간다.


1단계: 관광객이 아니라 탐험가처럼 생각하기

관광객은 이정표만 따라가고 큰길만 걷습니다. 탐험가는 애매함을 당연하게 여기고, 길을 가면서 자기만의 지도를 그립니다.

거대하고 낯선 코드베이스에서 디버깅을 시작할 때는, 이 탐험가 마인드셋을 가져야 합니다.

  • 당신은 단순히 버그를 고치는 게 아니라, 지도를 그리고 있다.
  • 모든 조사 과정은 당신의 지도를 확장하는 기회다.
  • 목표는 “티켓을 닫는 것”에 그치지 않고, **“다음 티켓이 더 쉬워지게 만드는 것”**이다.

이 마인드셋 전환은 미묘하지만 강력합니다. 문제 생길 때마다 반사적으로 때려 맞추는 **두더지 잡기(whack-a-mole)**식 디버깅에서 벗어나, 의도적인 지식 축적(knowledge accumulation) 모드로 옮겨 주기 때문입니다.


2단계: 한 줄도 건드리기 전에 지형부터 그려라

필드 컴퍼스는 바늘만 있는 도구가 아니라, 지형을 해석하는 방식입니다. 특정 파일로 바로 뛰어들기 전에, 먼저 프로젝트 전체의 큰 윤곽을 잡아야 합니다.

등산 전에 등고선을 그린 지도를 한 장 마련하는 것과 비슷합니다.

2.1 정적 분석과 인덱싱 실행하기

사용 중인 언어·프레임워크용 도구를 활용해 프로젝트의 구조를 먼저 잡아 봅니다.

  • 정적 분석 도구: ESLint, Clang-Tidy, SonarQube 등
  • Language Server / IDE 인덱싱: TypeScript server, LSP, IntelliSense 등
  • 타입 체커: TypeScript, MyPy, Flow 등

이 도구들은 다음을 알려 줍니다.

  • 프로젝트에 어떤 파일들이 존재하는지
  • 어떤 모듈이 어떤 모듈에 의존하는지
  • 에러가 자주 나는 모듈, 순환 의존(circular dependency), 지나치게 비대한 God object 같은 핫스팟은 어디인지

이 도구들은 항공에서 찍은 대략적인 항공 사진과 같습니다. 빠르고, 거칠지만, 아주 유용합니다.

2.2 의존성 그래프 만들기

가능하다면, 관계를 시각화하세요.

  • Graphviz, dependency-cruiser, ts-prune 또는 언어별 분석 도구를 사용해 모듈·패키지 의존성 그래프를 만듭니다.
  • incoming edge가 많은 코어 모듈(여러 곳에서 의존하는 모듈)과, 의존성이 거의 없는 leaf 모듈을 구분합니다.

코어 모듈은 대개 다음과 같습니다.

  • 데이터의 핵심 경로(critical path)를 이룸
  • 버그가 전파되기 쉬운 공통 지점
  • 먼저 이해해 두면 가치가 큰 위치

조악한 그래프라도 다음 질문에 답을 줄 수 있습니다.

“이 코드 조각을 건드리면, 뭐가 같이 딸려 움직일까?”

2.3 진입점과 경계를 찾기

시스템으로 들어오는 주요 **입구(gate)**를 찾습니다.

  • 애플리케이션 진입점: main, 서버 부트스트랩, 프레임워크의 부트 파일 등
  • Public API나 컨트롤러
  • 메시지 컨슈머, 크론 잡(cron job), 이벤트 핸들러

이 지점들이 외부 세계와 코드가 만나는 곳입니다. 버그가 사용자에게 보이는 경우라면, 그것은 어딘가에서 진입점부터 핵심 경로까지 이어지는 경로 위에 존재합니다. 필드 컴퍼스는 이 **진입점과 핵심 경로를 기준점(anchor)**으로 삼아야 합니다.


3단계: 레거시 코드를 미지의 땅처럼 대하라

레거시 코드는 본질적으로 나쁜 것이 아니라, 단지 당신의 머릿속 지도보다 오래된 코드일 뿐입니다.

흔한 실수는 레거시 시스템을 건드리면 안 되는 거대한 모놀리스처럼 취급하는 것입니다. 대신 이렇게 생각해야 합니다.

레거시 시스템은 아직 충분히 측량(survey)되지 않은 영토다.

탐색하면서 다음을 실천해 보세요.

3.1 발견한 진입점을 계속 기록하기

엔드포인트, CLI 커맨드, 스케줄된 잡 등 새로운 “게이트웨이”를 발견할 때마다, 당신의 개인 지도에 추가하세요.

예를 들면:

  • 간단한 마크다운 파일: maps/backend.md
  • 다이어그램 도구: Excalidraw, Miro, Draw.io
  • 심지어 종이에 그린 노트도 괜찮습니다.

다음 정도는 적어 둡니다.

  • 이름: POST /api/orders
  • 코드 위치: OrderController.handleCreate
  • 하류 호출들: service, repository, queue 등

3.2 데이터 흐름(Data Flow)을 따라가며 기록하기

특정 데이터를 하나 골라 그 흐름을 추적해 봅니다.

  • 어디서 입력을 받는가?
  • 어떻게 검증·변환·저장되는가?
  • 어디에서 다른 부분으로 퍼져 나가는가?

예를 들어 이런 식으로 적어 둘 수 있습니다.

사용자가 주문 생성 → OrderService.createOrderCreated 이벤트 발행 → InventoryWorkerBillingWorker에서 소비.

완벽한 다이어그램이 필요하지 않습니다. 나중에 다듬을 수 있는 유용한 뼈대(skeleton) 정도면 충분합니다.

3.3 크리티컬 패스(Critical Path) 찾기

메모와 추적 기록을 바탕으로 시스템의 **핵심 경로(critical path)**를 분리해 봅니다.

  • 결제 처리
  • 인증 및 인가(Auth & Authorization)
  • 재고나 잔액 같은 데이터 무결성 흐름

이런 크리티컬 패스는 당신의 “필드 노트북” 안에서라도 특별히 더 잘 문서화할 가치가 있습니다. 왜냐하면 대부분의 임팩트 큰 버그가 바로 이 선로 위에서 발생하기 때문입니다.


4단계: AI는 ‘정찰대’로 쓰되, 두뇌 대체제로 쓰지 말 것

Copilot, Cody, Replit Agent 같은 AI 코딩 보조 도구는 인간이 잘 못하는 일을 아주 잘합니다. 바로 코드베이스 전체를 빠르게 훑는 일입니다.

이들을 **정찰대(scout)**처럼 사용하세요.

  • 파일·사용처 탐색 질문
    • UserSession이 어디에서 생성되고, 어디에서 사용되는지 전부 보여 줘.”
    • OrderCreated 이벤트는 어디에서 처리하지?”
  • 패턴 탐색
    • PaymentProvider 구현체가 어디에 있는지 모두 찾아 줘.”
    • “인벤토리를 변경하는 엔드포인트를 전부 나열해 줘.”
  • 모듈 요약
    • BillingService가 무엇을 하고, 무엇에 의존하는지 요약해 줘.”

이렇게 하면 **폭넓은 시야(breadth)**를 얻을 수 있습니다.

  • 관련된 영역을 훨씬 빠르게 좁힐 수 있고
  • 네이밍 규칙과 반복되는 패턴을 빨리 파악할 수 있으며
  • 거대한 파일이나 어색한 추상화를 빠르게 개괄할 수 있습니다.

하지만 판단력은 절대 아웃소싱하지 마세요. AI는 헛소리(헛된 추론)를 자신 있게 내놓을 수도 있고, 미묘한 엣지 케이스를 놓칠 수도 있습니다. 그래서 다시 필드 컴퍼스가 필요합니다.


5단계: 도구로 넓이를, 인간의 추론으로 깊이를 확보하라

거대한 코드베이스에서 잘 먹히는 패턴은 다음 한 문장으로 정리할 수 있습니다.

자동화로 숲을 찾고, 뇌로 나무를 살핀다.

5.1 파기 전에 먼저 가설을 세워라

“일단 코드부터 읽어 보자”라는 태도 대신, 항상 구체적인 가설에서 출발합니다.

  • “이 버그는 아마 주문 총액을 계산하는 쪽 근처에 있을 거야.”
  • “이 회귀(regression)는 새로 추가된 user session 캐시 레이어에서 발생했을 가능성이 크다.”

그다음에는 다음 순서를 따릅니다.

  1. IDE 검색이나 AI를 써서, 그 가설과 관련된 후보 모듈·함수를 찾습니다.
  2. 버그와 관련된 실제 런타임 경로(request, event, thread 등)를 추적합니다.
  3. 의심 가는 지점을 중심으로 로그, 브레이크포인트, 테스트 하네스 등을 이용해 구체적으로 계측하고 실험합니다.

5.2 지도를 점진적으로 정교하게 만들기

조사 과정에서 다음을 습관으로 만드세요.

  • 가설이 틀렸다면, 왜 틀렸는지 기록하고, 지도를 수정합니다.
  • 새로운 추상화(예: 공통 미들웨어, 공통 헬퍼)를 발견하면, 노트나 다이어그램에 추가합니다.

시간이 지나면 머릿속과 노트에는 이런 연결망이 생깁니다.

  • “결제가 이상하면, 사실 통화 변환(currency conversion) 헬퍼를 먼저 의심해야 한다.”
  • “User 관련 버그는 자주 SessionContext로 거슬러 올라간다.”

암묵지(tacit knowledge)를 명시적인 지도 위의 마킹으로 옮기는 과정입니다.


6단계: 필드 컴퍼스를 ‘살아 있는 지도’로 확장하기

컴퍼스는 1일 차부터 유용한 도구입니다. 하지만 지속적으로 진화하는 지도가 있을 때, 그 가치는 기하급수적으로 커집니다.

6.1 가볍고, 거짓말 없는(Low-BS) 문서화

완벽한 Confluence 정원(위키 궁전)이 필요한 게 아닙니다. 필요한 건 이것뿐입니다.

  • 주요 디렉터리마다 짧은 README.md: 이 디렉터리의 목적과 주요 흐름을 간단히 설명
  • 몇 개의 핵심 다이어그램:
    • 주요 도메인 플로우(예: 주문 라이프사이클)
    • 핵심 인프라 구조(예: 인증, 결제, 메시징)

각 디버깅 세션은 아주 작은 개선 두세 개를 추가할 기회로 삼으세요.

  • 애매한 엣지 케이스를 설명하는 짧은 주석 한 줄
  • README에 “X를 디버깅할 때 참고할 점” 같은 작은 섹션 하나
  • “Y를 찾고 있다면, 여기서 시작하세요…”라는 한 줄 메모

6.2 지도를 공유하라

지도는 여럿이 쓸 때 힘을 발휘합니다.

  • PR에서 관련 다이어그램이나 노트를 링크합니다.
  • 새로 발견한 내용을 팀 문서에 추가합니다. 매끄럽지 않아도 괜찮습니다.
  • 페어 프로그래밍할 때, 새로 그린 플로우를 같이 보며 설명합니다.

이렇게 하면 개인적인 이점에서 그치지 않고, **조직의 집단 기억(organizational memory)**을 구축하게 됩니다.


7단계: 지저분한 시스템을 경쟁력으로 바꾸기

대부분의 팀은 다음과 같은 환경에서 작업합니다.

  • 서랍 속 종이로 된 스펙
  • 여기저기 흩어진 ad-hoc 스크립트
  • 여러 세대의 레거시 시스템이 덕테이프로 이어진 구조

이런 혼란은 대개 사람들을 겁먹게 만듭니다. 그리고 그 두려움 자체가 기회입니다.

탄탄한 코드 매핑·디버깅 습관을 들이면,

  • 어떤 환경에서도 더 빨리 온보딩할 수 있고,
  • “아직 모르는 부분이지만, 어떻게 알아낼지는 안다”라고 말할 수 있는 사람이 되며,
  • 다른 사람들이 손대기 싫어하는 시스템에 질서를 가져오는 사람이 될 수 있습니다.

지형을 지도화하고, 도구를 정찰대로 쓰고, 살아 있는 문서를 쌓아 가는 아날로그 필드 컴퍼스 마인드셋은, 엉성하거나 부분적으로만 문서화된 시스템조차 부담이 아니라 전략적 자산으로 바꾸게 해 줍니다.


결론: 버그만 고치지 말고, 지도를 업데이트하라

거대한 코드베이스에서의 디버깅은, 의미 없이 떠도는 방황이 될 수도 있고, 의미 있는 탐험이 될 수도 있습니다.

길을 잃지 않으려면, 다음 다섯 가지를 기억하세요.

  1. 탐험가처럼 생각하라. 단지 티켓을 닫는 게 아니라, 지도를 만든다고 생각합니다.
  2. 먼저 지형을 그려라. 정적 분석, 의존성 그래프, 진입점 파악부터 합니다.
  3. 레거시를 금단의 구역이 아니라, 미지의 영역으로 보라. 탐색하는 만큼 문서를 남깁니다.
  4. 폭은 AI에게, 깊이는 당신의 두뇌에게. 도구로 후보 영역을 찾고, 원인 규명은 직접 합니다.
  5. 지도를 계속 업데이트하라. 작은 문서·다이어그램 개선이 쌓이면 큰 차이가 납니다.

코드베이스 전체를 완벽하게 이해할 필요는 없습니다. 필요한 것은 신뢰할 수 있는 컴퍼스와, 지도를 조금씩 개선하려는 꾸준함입니다. 시간이 지나면, 처음에는 혼돈처럼 느껴지던 숲이 점점 익숙한 집처럼 느껴지기 시작할 것입니다.

아날로그 디버깅 필드 컴퍼스: 거대한 코드베이스에서 길 잃지 않는 포켓 지도 | Rain Lag