Rain Lag

디버깅 블루프린트 노트: 로그를 보기 전에 작은 시스템 다이어그램부터 그려라

로그를 열거나 디버거를 붙이기 전에, 손으로 그린 작은 시스템 다이어그램 하나가 복잡한 분산 환경—특히 마이크로서비스—에서 디버깅 속도를 어떻게 극적으로 높이는지 소개합니다.

디버깅 블루프린트 노트: 로그 한 줄 보기 전에 작은 시스템 다이어그램부터 그려라

무언가 장애가 나면 제일 먼저 하는 일이 곧장 로그를 열어 보거나 디버거를 붙이는 것이라면, 지금보다 훨씬 더 힘들게 일하고 있을 가능성이 큽니다.

요즘 시스템은 서비스, 큐, 캐시, 서드파티 API가 뒤얽힌 거대한 메시 구조로 진화하고 있습니다. 이럴 때 디버깅에서 가장 어려운 부분은 종종 코드 안의 버그를 찾는 것이 아니라, 애초에 어디를 살펴봐야 할지 정하는 것입니다.

여기서 아주 단순한 습관 하나가 엄청난 차이를 만듭니다.

로그 한 줄 건드리기 전에, 작은 시스템 다이어그램을 먼저 그려라.

깔끔한 아키텍처 다이어그램도 아닙니다. UML 의식도 아닙니다. 그저 노트나 메모 앱에 휘갈겨 그린 손 그림 하나—도구를 파고들기 전에 뇌를 먼저 정렬해 주는 “디버깅 블루프린트” 정도면 충분합니다.

이 글에서는 이 습관이 특히 마이크로서비스 같은 분산 시스템에서 왜 강력한지, 몇 분 안에 어떻게 그릴 수 있는지, 그리고 전통적인 디버거와 IDE를 어떻게 더 효과적으로 만들어 주는지 설명합니다.


복잡한 시스템이 디버깅을 고통스럽게 만드는 이유

엉성하게 설계된 소프트웨어는 시간이 지날수록 고통을 증폭시킵니다. 그때그때 때우는 빠른 수정은 결국 이렇게 쌓입니다.

  • 문서화되지 않은 새로운 결합
  • 서비스 간 보이지 않는 숨은 의존성
  • 데이터가 조용히 잘못될 수 있는 새로운 지점

모놀리식 시스템에서는 이게 물론 나쁘지만 그래도 모든 것이 한 프로세스 안에서 돌아갑니다. 그런데 마이크로서비스로 가면 이 고통이 배가 됩니다.

  • 하나 대신 여러 개의 프로세스
  • 컴포넌트 사이의 네트워크 경계
  • 서로 다른 일관성 모델을 가진 여러 데이터 저장소
  • 큐, 토픽, 배치/백그라운드 작업 같은 비동기 흐름

이런 시스템이 실패하면, 더 이상 단지 *“이 함수가 뭘 하고 있지?”*만 묻지 않습니다. 이렇게 물어야 합니다.

  • 이 요청은 어디서 시작됐는가?
  • 어떤 서비스들이 이 요청을 거쳐 갔는가?
  • 경로 상에 다른 시스템들(로드 밸런서, 캐시, 큐 등)은 무엇이 있는가?
  • 어디에서 지연되거나, 유실되거나, 데이터가 깨질 수 있는가?

명확한 멘탈 모델이나 시각적 모델이 없으면, 결국 우리 모두가 한 번쯤 해봤던 이런 행동을 반복하게 됩니다.

  • 수많은 서비스의 로그를 뒤지며 grep 남발하기
  • 아무 데나 print/console.log 찍어 보기
  • 애초에 문제 경로에 포함되지 않는 프로세스에서 코드 스텝 인/아웃 하기

문제는 디버깅 도구의 성능이 약한 게 아니라, 지도가 없다는 데 있습니다.


작은 시스템 다이어그램: 가벼운 나만의 지도

작은 시스템 다이어그램은, 지금 추적 중인 버그와 관련된 컴포넌트와 데이터 흐름만 빠르게 거칠게 그려 보는 스케치입니다.

보통 이런 것들이 포함됩니다.

  • 컴포넌트: 서비스, 큐, 데이터베이스, 캐시, 외부 API를 상자로 표현
  • 연결: 요청, 이벤트, 데이터 이동을 화살표로 표현
  • 핵심 데이터: “order JSON”, “user session ID”, “payment status” 같은 짧은 라벨
  • 경계: 네트워크 홉, 리전, 신뢰 구역(trust zone) 등을 필요하면 메모

이게 전부입니다.

전체 시스템을 다 담으려는 게 아닙니다. 지금 디버깅 중인 동작과 관련된 일부분만 다이어그램으로 자르는 게 목적입니다.

굳이 다이어그램까지 그릴 필요가 있을까?

작은 그림 하나를 그리는 것만으로도 생각이 강제로 정리되기 때문입니다.

  • 무엇이 범위 안이고 무엇이 아닌지 결정해야 합니다.
  • 가정이 드러납니다. “잠깐, 서비스 A가 서비스 B를 진짜 동기로 호출하나, 아니면 큐를 통해 비동기로 보내나?”
  • 데이터 흐름을 맥락 속에서 보게 되니, 어디에서 문제가 날 수 있는지 더 잘 보입니다.

몇 분만 투자하면, 흐릿한 머릿속 그림이 눈과 뇌가 직접 다룰 수 있는 구체적인 형태로 바뀝니다.


작은 다이어그램이 실패 지점과 병목을 드러내는 방식

한 번 종이에 스케치를 해 두면, 여러 패턴이 눈에 들어오기 시작합니다.

  1. 경로를 따라가는 실패 지점
    특정 사용자 요청이 실패한다면, 각 홉(hop)을 표시해 볼 수 있습니다.

    • Web → API Gateway → Service A → Queue → Service B → DB 그리고 이렇게 질문합니다. 어느 홉까지는 정상 데이터가 보이는가? 어디부터 깨진 데이터가 보이는가?
  2. 병목과 느린 지점
    전체 체인을 그려 보면 지연 시간을 더 잘 추론할 수 있습니다.

    • 어떤 호출이 동기이며, 진짜 크리티컬 패스 위에 있는가?
    • 어떤 서비스들이 과부하된 공용 자원에 의존하고 있는가?
  3. 숨은 의존성
    다이어그램을 그리다가 이런 걸 깨달을 수 있습니다.

    • “우리는 ‘서비스 C가 주문을 검증한다’고 말하지만, 실제로는 C가 D를 호출하고, D가 E를 호출하고, E가 외부의 부정 거래 탐지 API를 부른다.”
  4. 로그와 메트릭이 진짜 중요한 지점
    모든 로그를 다 따라다니는 대신, 몇 개의 전략적인 지점을 고를 수 있습니다.

    • 각 홉의 경계
    • 변환 전/후 지점
    • DB, 캐시, 큐 같은 중요한 공용 자원 주변

이제 무작정 로그 동굴 탐험을 하는 게 아니라, 가설을 세우고 가이드된 탐색을 하는 셈이 됩니다.


점진적 다이어그램: 디버깅은 대화다

디버깅은 직선으로 뻗어나가는 과정이 거의 아닙니다. 가설을 세우고, 실험을 하고, 무언가를 배우고, 다시 조정합니다.

다이어그램도 같은 방식으로 진화해야 합니다.

1단계: 최소한의 스케치로 시작하기

우선, 지금 일어나고 있다고 믿는 흐름부터 그립니다.

  • 사용자나 트리거 이벤트를 그립니다.
  • 참여한다고 알고 있는 서비스들을 그립니다.
  • 요청/응답, 이벤트 흐름을 화살표로 연결합니다.
  • 버그를 처음으로 관찰한 지점을 표시합니다. (예: 클라이언트에 잘못된 응답이 돌아오는 곳)

2단계: 관측 결과로 주석 달기

로그, 메트릭, 트레이스, DB 내용을 확인하면서 스케치를 업데이트합니다.

  • “여기서는 status = pending”, “여기서 2초 후 timeout”, “여기서 user_id가 null” 같은 짧은 메모를 추가
  • 기대한 대로 동작하는 지점에는 체크 표시
  • 아직 검증되지 않은 가정에는 물음표 표시

3단계: 배운 만큼 구조를 계속 정제하기

조사하다 보면 이런 사실들을 발견할 수 있습니다.

  • 서비스 B와 DB 사이에 사실은 캐시 레이어가 있었다.
  • 서비스 A는 큐 하나가 아니라 두 개의 큐로 이벤트를 발행한다.
  • 외부 API의 재시도 정책 때문에 특정 구간에서 지연이 생긴다.

발견할 때마다 다이어그램을 고칩니다. 목표는 예쁜 그림이 아니라, 현실과 최대한 가까운 그림을 유지하는 것입니다.

이렇게 점진적으로 다듬으면:

  • 엉뚱한 코드로 너무 깊이 들어가는 일을 막아 줍니다.
  • 헛된 추적(레드 헤링)을 하고 있을 때가 더 잘 보입니다.
  • 나중에 다시 쓰거나, 팀과 공유할 수 있는 ‘암묵지(tribal knowledge)’가 자연스럽게 축적됩니다.

“다이어그램 먼저, 도구는 나중에”: 디버거를 진짜 유용하게 만드는 법

GDB와 그 프런트엔드(Allinea DDT, Code::Blocks, CodeLite, Eclipse CDT) 같은 도구와 현대 IDE들은 정말 강력합니다. 이런 일들을 할 수 있죠.

  • 코드를 한 줄씩 스텝 인/아웃하며 실행
  • 콜 스택과 변수 값 확인
  • 조건부 브레이크포인트 설정
  • 스레드, 메모리 상황 시각화

하지만 복잡한 분산 시스템에서는, 이런 도구들이 문제의 일부만 해결해 줄 뿐입니다. 이 도구들은 올바른 프로세스, 올바른 함수, 올바른 시점을 이미 고른 뒤에야 진가를 발휘합니다.

명확한 아키텍처 모델이 없으면 생기는 일은 이렇습니다.

  • 애초에 잘못된 서비스에 디버거를 붙입니다.
  • 실제 실패 경로에서는 호출되지도 않는 코드를 열심히 따라갑니다.
  • 이미 상류에서 깨져 내려온 데이터를 열심히 들여다봅니다.

“다이어그램 먼저, 도구는 나중에”라는 습관은 이 순서를 뒤집습니다.

  1. 먼저 작은 다이어그램을 그립니다.
    어떤 서비스와 흐름이 실패한 동작에 관여하는지 파악합니다.

  2. 탐색 범위를 좁힙니다.
    가장 가능성이 높은 실패 지점과 경계 지점을 골라냅니다.

  3. 그다음에 도구를 붙입니다.

    • 다이어그램에서 표시한 핵심 홉의 로그만 우선 확인합니다.
    • 의심스러운 경로로 좁혀진 특정 서비스와 함수에만 디버거를 붙입니다.

이제 로그와 디버거는 해변 전체를 휘젓는 금속 탐지기가 아니라, 대략적인 해부도 위에서 정밀하게 사용하는 메스가 됩니다.


당장 내일부터 쓸 수 있는 간단한 워크플로우

특별한 도구나 템플릿은 필요 없습니다. 노트 한 권이나 화이트보드면 충분합니다. 최소한의 워크플로우를 예로 들면 이렇습니다.

  1. 증상을 한 문장으로 정의한다.
    예: “사용자가 수량을 수정한 뒤에도 장바구니에 예전 데이터가 보인다.”

  2. 내가 알고 있다고 믿는 경로를 그린다.

    • User → Web App → API Gateway → Cart Service → Cache → DB
  3. 기대 vs 실제를 표시한다.

    • 어디에서 최신 데이터가 보여야 하는지 메모
    • 어디에서 오래된 데이터가 관찰되는지 메모
  4. 전략적인 관측 지점 2–3군데를 고른다.
    다이어그램을 보고 가장 먼저 볼 곳을 정합니다.

    • Cart Service에서 캐시 쓰기 근처 로그
    • 해당 사용자의 캐시 키와 TTL
    • 해당 장바구니 DB 레코드
  5. 실험을 돌리고 다이어그램을 업데이트한다.

    • 가정을 검증하거나 깨뜨립니다. (예: “Cart Service가 캐시에 직접 쓰는 게 아니라, 이벤트를 발행하고 있었다.”)
    • 새로 알게 된 큐와 컨슈머를 다이어그램에 추가합니다.
  6. 그 후에야 스텝 단위 디버깅을 사용한다.
    이제 다이어그램이 지목한 의심스러운 컴포넌트와 코드 경로에 한해 디버거를 붙입니다.

필요할 때마다 이 루프를 반복합니다. 시간이 지나면 특정 문제 영역에 대한 작은 디버깅 블루프린트 모음이 생기게 됩니다.


팀 차원에서 습관으로 만들기

이 방법은 팀 전체의 문화가 되었을 때 훨씬 더 빛을 발합니다.

  • 장애 대응(incident response) 시: 모든 콜은, 누군가가 지금 파악한 시스템 슬라이스를 공유 화이트보드에 그려 보는 것부터 시작합니다.
  • 코드 리뷰 시: 여러 서비스를 가로지르는 복잡한 변경이라면, 영향을 받는 플로우에 대한 작은 다이어그램을 요청합니다.
  • 온보딩 시: 신규 엔지니어는 과거 인시던트 다이어그램을 보며, 장애가 어떻게 전파되었는지 함께 따라가 보는 것만으로도 훨씬 빨리 배울 수 있습니다.

이런 다이어그램이 정식 아키텍처 문서를 대체하는 건 아닙니다. 대신 이런 장점을 가진 보완재입니다.

  • 만들기 빠르고,
  • 특정 동작에만 집중되어 있고,
  • 실제 디버깅 스토리와 1:1로 연결되어 있습니다.

결론: 펜이 최고의 디버거가 될 때

고급 로깅 시스템, 트레이싱, 프로파일러, IDE가 넘쳐나는 시대에, 손으로 대충 그린 그림 한 장이 의미가 있을까 싶을 수 있습니다.

하지만 특히 마이크로서비스 같은 복잡한 분산 아키텍처에서는, 이 작은 시스템 스케치가 빠져 있는 마지막 레이어인 경우가 많습니다.

이 다이어그램들은 다음을 가능하게 합니다.

  • 흐릿한 멘탈 모델을 구체적인 지도(맵)로 바꾸고,
  • 실패 지점과 병목 구간을 부각시키며,
  • 올바른 도구와 올바른 코드 경로를 선택하게 도와주고,
  • 초점 없는 로그 탐험과 스텝 디버깅에 쏟을 수많은 시간을 아껴 줍니다.

다음번에 뭔가가 고장 나면, 바로 로그를 열고 싶은 충동을 잠깐만 참아 보세요. 노트를 집어 들고, 지금 궁금한 동작을 설명해 줄 수 있는 가장 작은 다이어그램을 하나 그려 보십시오. 그리고 그 스케치가 먼저 “어디부터 볼지”를 결정하게 하세요.

시스템이 복잡해질수록, 그 작은 다이어그램이야말로 최고의 디버깅 블루프린트가 되어 줄 것입니다.

디버깅 블루프린트 노트: 로그를 보기 전에 작은 시스템 다이어그램부터 그려라 | Rain Lag