Rain Lag

아날로그 레이턴시 샌드박스: 프로파일러를 켜기 전에 종이 타임라인으로 느린 시스템부터 모델링하기

프로파일러나 트레이스 뷰어를 열기 전에, 종이 타임라인·간단한 측정·크리티컬 패스 사고만으로 엔드 투 엔드 레이턴시를 모델링하고 디버깅하는 방법을 배워보세요.

아날로그 레이턴시 샌드박스: 프로파일러를 켜기 전에 종이 타임라인으로 느린 시스템부터 모델링하기

소프트웨어 팀은 레이턴시 문제가 생기면 본능적으로 이렇게 말하곤 합니다. “프로파일러부터 열자.”

그게 필요할 때도 있지만, 대부분은 너무 이른 선택입니다.

플레임 그래프, 시스템 콜, 마이크로 최적화에 빠져들기 전에, 놀라울 만큼 멀리 데려다 줄 아날로그 도구가 있습니다. 바로 빈 종이와 펜입니다.

이 “아날로그 레이턴시 샌드박스”를 쓰면 종이 타임라인으로 느린 시스템을 모델링하고, 크리티컬 패스를 논리적으로 따져 보고, 프로파일러를 열기 전에 구체적인 성능 가설을 세울 수 있습니다. 그 결과, 더 빠른 진단, 더 나은 우선순위 결정, 덜 낭비되는 최적화를 얻게 됩니다.


왜 종이에서 시작해야 할까?

프로파일러는 CPU 시간이 어디에 쓰였는지를 보여줍니다. 하지만 레이턴시는 언제 어떤 일이 일어나는지, 그리고 무엇이 무엇에 의존하는지에 관한 문제입니다.

이는 전혀 다른 질문입니다.

종이 타임라인은 여러분에게 이렇게 생각하도록 강제합니다.

  • 개별 함수가 아니라 엔드 투 엔드 플로우 관점에서 보기
  • 병렬 작업순차 작업을 분리해서 보기
  • 바쁜 시간뿐 아니라 **유휴 구간(빈 시간)**과 대기 시간을 똑같이 뚜렷하게 보기
  • 실제로 레이턴시를 결정하는 크리티컬 패스를 찾기

이렇게 하면 아주 거칠지만 강력한 모델을 얻게 됩니다. 마이크로초 단위로 정확하진 않지만, 다음 1시간을 어디에 써야 할지를 알려주기엔 충분히 명확합니다.


1단계: 종이 타임라인 그리기

필요한 건 이것뿐입니다.

  • 종이 한 장(또는 화이트보드)
  • 가로 방향의 시간 축
  • “액터(Actor)” 또는 컴포넌트별 한 줄 (브라우저, 로드 밸런서, API 서버, DB, 캐시, 외부 서비스, SSD 등)

어떻게 스케치할까

  1. 왼쪽에서 오른쪽으로 시간 축을 그립니다.
  2. 위에서 아래로 컴포넌트 목록을 나열합니다. 보통 클라이언트를 위쪽, 깊은 의존성들을 아래쪽에 둡니다.
  3. 요청이 처리되는 각 단계를, 해당 소요 시간에 비례하는 가로 막대로 대략적으로 그립니다.
  4. 컴포넌트 간에는 화살표로 요청과 응답의 흐름을 표시합니다.

개념적인 예(스케일은 단순 예시):

  • 브라우저 → DNS → TCP/TLS → 로드 밸런서 → 앱 → 데이터베이스 → SSD
  • 중간에는 리다이렉트, 캐시 조회, 템플릿 렌더링, JS 실행 등이 들어갈 수 있습니다.

정밀도를 걱정하지 마세요. 첫 번째 버전에서 중요한 건 **모양(shape)**입니다.

  • 무엇이 먼저 일어나는지
  • 무엇이 서로 겹칠 수 있는지(병렬)
  • 어디에 눈에 띄는 “멈춤”이 있는지

이게 먼저 보이면 충분합니다.

첫 스케치를 보면 바로 보이는 것들

잘 그려진 종이 타임라인을 보면, 금방 이런 것들이 눈에 들어옵니다.

  • 위아래로 길게 쌓인, 서로 의존하는 작업들(잠재적인 크리티컬 패스)
  • 가로 방향의 빈 공간(화이트 스페이스) — 아무 일도 안 하는 유휴 구간
  • 이론상 더 많이 겹칠 수 있는데 실제로는 병렬로 실행되지 않는 작업들

이제 “백엔드 어딘가가 느린 것 같다”는 막연한 느낌 대신, 시각적인 스토리를 갖게 됩니다.


2단계: 시스템을 의존 작업들의 체인으로 모델링하기

요청을 작은 프로젝트라고 생각해 봅시다. 프로젝트에는 태스크와 의존성이 있습니다. 프로젝트 매니저들은 **크리티컬 패스 분석(critical path analysis)**을 사용해 어떤 작업들이 실제로 프로젝트 종료 날짜를 결정하는지 파악합니다.

레이턴시에도 똑같이 적용할 수 있습니다.

크리티컬 패스 찾기

크리티컬 패스는 이런 조건을 만족하는 단계들의 시퀀스입니다.

  • 각 단계가 바로 직전 단계에 의존하고,
  • 동시에 실행되는 다른 경로 중 더 빨리 끝나는 우회로가 없는 경로

실제로는 이렇게 합니다.

  1. “요청 수신(request received)” 시점에서 시작합니다.
  2. 타임라인을 따라가며, 반드시 먼저 일어나야 하는(must‑happen‑before) 화살표만 따라갑니다.
  3. 중간에 병렬 분기가 나오면, 그중 시간이 가장 긴(branch 시간이 더 긴) 경로를 따라갑니다.
  4. “응답이 완전히 전달됨(response fully delivered)” 지점에서 끝납니다.

이 체인이 바로 레이턴시의 크리티컬 패스입니다.

흔히 이런 식으로 보입니다.

DNS → TCP → TLS → HTTP 요청 → 앱 라우팅 → 인증 → 캐시 미스 → DB 쿼리 → SSD → 템플릿 렌더 → gzip → 네트워크 전송 → 브라우저 파싱 → JS 실행

이 경로에서 벗어난 모든 것들은 **서브크리티컬(subcritical)**입니다. 이런 것들은 일정 범위 내에서 변동이 있어도, (나중에 다룰 슬랙(slack) 안에만 머무른다면) 사용자에게 보이는 레이턴시는 변하지 않습니다.


3단계: 서브크리티컬 패스와 슬랙 찾기

아날로그 모델의 핵심 장점 중 하나는 무엇을 최적화하지 말아야 하는지가 보인다는 점입니다.

서브크리티컬 패스

서브크리티컬 패스는, 그 경로의 작업이 언제 끝나든 최종 레이턴시에 영향을 주지 않는 작업 분기입니다. 예를 들면:

  • 응답 전송이 끝난 뒤에 비동기로 처리하는 로깅
  • 메인 쿼리가 도는 동안 미리 계산하는 보조 캐시
  • 화면 상단(above-the-fold) 콘텐츠가 렌더링되는 동안, 화면 아래(below-the-fold) 이미지를 로딩하는 작업

종이 타임라인에서 이런 것들은 대체로 다음처럼 보입니다.

  • 크리티컬 패스가 바쁘게 뭔가를 하는 동안 동시에 시작해서 끝나는 막대
  • 종료 시점이 현재 크리티컬 패스의 끝나는 시점보다 더 이른 작업들

슬랙(slack)

슬랙은 어떤 태스크가 얼마나 늦어져도 전체 레이턴시에 영향을 주지 않는지를 뜻합니다.

종이에서 대략 눈대중으로 볼 수 있습니다.

  • 어떤 서브크리티컬 태스크가 사용자 입장에서 “완료”되는 시점보다 100 ms 먼저 끝난다면, 그 태스크의 슬랙은 약 100 ms 정도입니다.
  • 슬랙이 크게 남는 태스크를 최적화하는 데 시간을 쓰면, 응답 시간은 전혀 줄지 않습니다.

많은 팀이 프로파일러를 사용할 때 빠지는 함정이 바로 이것입니다. CPU 사용량이 많아 보이는 것, I/O가 무거워 보이는 것을 최적화하는 데 집중합니다. 하지만 그게 실제로는 레이턴시 크리티컬 패스가 아닐 수 있습니다.

여러분의 종이 모델은, 그런 곳에 노력을 낭비하지 않도록 보여줍니다.


4단계: 복잡한 부분은 블랙 박스로 다루기

실제 시스템은 내부 구조가 복잡한 컴포넌트들에 의존합니다.

  • SSD 및 스토리지 스택
  • 원격 마이크로서비스
  • 서드파티 API
  • 데이터베이스와 캐시

유용한 레이턴시 모델을 만드는데, 이 내부 구현까지 알 필요는 없습니다.

대신 이들을 **입출력 특성만 아는 블랙 박스(black box)**로 취급합니다.

예를 들어:

  • SSD 쓰기: 작은 쓰기 기준, 정상 부하에서 약 0.5–2 ms
  • 원격 서비스 호출: p50 20 ms, p95 80 ms
  • X 타입 DB 쿼리: 보통 15 ms, 부하가 걸리면 최악 50 ms

이런 값들을 종이 타임라인의 막대 위에 라벨과 대략적인 길이로 적어 넣습니다. 목적은 마이크로초 단위의 정확도가 아니라, 어디에 큰 시간 덩어리가 쓰이고 있는지를 한눈에 보는 것입니다.

이 방식에는 두 가지 이점이 있습니다.

  1. 너무 이른 시점에, 중요하지 않은 내부 구현에 파고들어 헤매지 않게 해줍니다.
  2. 그 블랙 박스의 막대가 크리티컬 패스를 압도할 때에만, 그때 필요에 따라 더 상세한 모델로 들어가면 됩니다.

5단계: 워터폴 뷰로 sanity check 하기

웹 레이턴시를 다루는 분이라면 이미 이런 **워터폴 차트(waterfall chart)**를 본 적이 있을 겁니다.

  • Chrome DevTools Network 탭
  • WebPageTest
  • Lighthouse

이 차트들은 종이 타임라인의 디지털 사촌입니다.

  • 각 네트워크 요청이 하나의 가로 막대로 표시되고,
  • 그 안이 DNS, TCP, TLS, 요청 전송, 응답, 콘텐츠 다운로드 등으로 구간 분할됩니다.
  • 전체 페이지 로드는 사실상 거대한 크리티컬 패스 퍼즐입니다.

이 도구들을 다음과 같이 활용할 수 있습니다.

  • 종이 모델과 교차 점검: “DevTools에서 보이는 모양이 종이에 그린 것과 비슷한가?”
  • 주요 구성 요소 파악: DNS vs TLS vs 서버 작업 vs 클라이언트 작업
  • 병렬 요청이 실제로 어디서 일어나는지, 어디서 직렬화되는지 확인

종이 타임라인을 거친 스케치, 워터폴 도구들을 정밀 계기판이라고 생각하면 이해하기 쉽습니다.


6단계: 몇 가지 측정값을 넣고, 추세를 예측해 보기

아날로그 모델이 강력해지기 위해 필요한 건 밀리초 단위의 정확도가 아닙니다. 필요한 건 **대략적인 규모(order of magnitude)**입니다.

간단한 지표 몇 개만 수집해 봅시다.

  • DNS 조회 시간(평균, p95)
  • TLS 핸드셰이크 시간
  • 서버 처리 시간(요청 도착부터 첫 바이트 전송까지, TTFB)
  • 주요 DB 쿼리 시간
  • 클라이언트에서의 time to first byte / time to first paint / time to interactive

이제 이 값들을 타임라인의 막대 길이에 대략 반영합니다.

그다음, 이런 “what if” 질문을 던질 수 있습니다.

  • “TLS에서 50 ms를 줄이면 어떻게 될까?”

    • TLS가 크리티컬 패스 위에 있다면, 전체 레이턴시는 대략 50 ms 줄어듭니다.
    • TLS가 300 ms짜리 DB 쿼리와 완전히 병렬로 돌고 있다면, 50 ms 줄여도 전체 시간은 전혀 안 줄어들 수 있습니다.
  • “디스크 쓰기를 배치(batch)하면 어떨까?”

    • 여러 번의 작은 SSD 쓰기(총 40 ms)를, 더 적은 횟수의 큰 쓰기(예: 10–15 ms)로 바꿀 수 있다면,
    • 그 쓰기 작업이 응답을 블로킹하고 있다면, 실제로 의미 있는 개선이 됩니다.
  • “이 API 호출을 더 앞 단계로 옮기면 어떨까?”

    • 다른 작업과 병렬로 실행되게 만들 수 있다면, 순차 경로를 더 짧은 크리티컬 패스로 바꾸게 됩니다.

목표는 추세와 병목을 예측하는 것입니다.

  • 부하가 올라갈수록 어떤 세그먼트가 레이턴시를 지배하게 될까?
  • 캐싱을 어디에 적용해야 가장 큰 효과가 날까?
  • 지금 건드려 봐야 전혀 의미 없어 보이는 부분은 어디일까?

아직 아무 것도 프로파일링하지 않았지만, 어디를 프로파일링해야 투자 가치가 있을지 이미 감이 잡힌 상태가 됩니다.


7단계: 프로파일러는 “떠돌기”가 아니라 “검증용”으로 쓰기

아날로그 모델로 구체적인 가설을 세웠다면, 그때 프로파일러와 트레이스 도구를 엽니다.

  • CPU 프로파일러(perf, Instruments, VTune 등)
  • 애플리케이션 레벨 트레이서(OpenTelemetry, Zipkin, Jaeger 등)
  • DB 쿼리 분석기와 slow query 로그

이 도구들은 다음을 위해 사용합니다. 검증과 정교화입니다.

  • “DB 호출이 크리티컬 패스에서 70 ms라고 가정했는데, 실제로 그런가?”
  • “TLS가 렌더링과 겹쳐서 실행된다고 모델을 세웠는데, 트레이스에서 그게 확인되는가?”
  • “로깅 최적화는 p95 레이턴시에 영향을 안 줄 거라고 예측했는데, 실험 결과도 그렇게 나오는가?”

이미 어떤 세그먼트가 중요한지 알고 있기 때문에, 다음과 같은 낭비를 피하게 됩니다.

  • 크리티컬 패스 밖의 핫스팟을 쫓아다니는 일
  • 사용자 레이턴시에 영향을 줄 수 없는 부분까지 과도하게 인스트루먼트하는 일
  • 뚜렷한 질문 없이 며칠씩 트레이스를 탐색하며 헤매는 일

아날로그 모델 덕분에, 도구 사용이 목적 지향적이 됩니다.


정리: 모두 한데 모으기

아날로그 레이턴시 샌드박스의 핵심은 아주 단순합니다.

  1. 각 컴포넌트별로 한 줄을 두고 종이 타임라인을 그립니다.
  2. 각 단계를 대략적인 길이의 막대로 스케치합니다.
  3. 크리티컬 패스, 서브크리티컬 패스, 슬랙을 식별합니다.
  4. 복잡한 부분은 입출력 특성이 있는 블랙 박스로 취급합니다.
  5. 워터폴 차트와 기본적인 측정값으로 스케치를 보정합니다.
  6. 종이 위에서 “what if” 질문을 던지며, 변화의 영향을 예측합니다.
  7. 구체적인 가설이 생긴 다음에야 프로파일러와 트레이스 도구를 사용합니다.

종이에서 마이크로초 단위의 진실을 얻을 순 없습니다. 하지만 시작 단계에서 그보다 더 중요한 것을 얻습니다. 바로 구조, 의존성, 우선순위에 대한 명확한 그림입니다.

다음번에 팀에서 “시스템이 좀 느린 것 같아요”라는 말이 나오면, 바로 프로파일러부터 켜고 싶은 충동을 잠시만 참아 보세요. 마커를 집어 들고, 타임라인을 그리고, 모델링부터 시작해 보세요. 미래의 여러분, 그리고 레이턴시에 민감한 여러분의 사용자들이 분명히 고마워할 겁니다.

아날로그 레이턴시 샌드박스: 프로파일러를 켜기 전에 종이 타임라인으로 느린 시스템부터 모델링하기 | Rain Lag