와탭 모니터링
2025-10-14
24억 건 실시간 로그 모니터링 UX 구현기

안녕하세요 와탭랩스입니다. 와탭랩스는 웹 기반으로 다양한 모니터링 서비스를 제공하고 있습니다. 그중 로그 모니터링은 하루 최대 24억 건의 데이터를 수집하고 조회할 수 있을 만큼 대규모 데이터를 처리합니다.

이번 글에서는 애플리케이션, 서버, 데이터베이스, 쿠버네티스, 네트워크 장비 등에서 수집된 로그를 실시간으로 확인할 수 있는 ‘라이브 테일(Live Tail)’ 화면의 UX를 개선하기 위해, 우리가 정의했던 문제와 그 해결 과정을 공유하려고 합니다.

문제 정의

위 영상을 보셨을 때 어떤 생각이 드셨나요? 몇몇 고객분들이 아래와 같은 문제들을 제기하였고, 저 또한 이 문제에 공감했습니다.

  1. 분석해야 할 로그를 발견했을 때 자동 스크롤을 멈춰서 보고싶어요.
  2. 데이터 갱신이 되고 있는지, 데이터 갱신되는 방향이 어디인지 알기 어려워요.

목표

로그 모니터링 고객은 터미널에서 tail -f 명령어를 입력하여 사용하는 UX에 익숙하여 이를 참고하기로 결정했습니다.

  1. 터미널 콘솔 스크롤 UX 구현
    - 새로운 데이터가 들어오면 아래에 계속 쌓입니다.
    - 스크롤 바 위치가 밑바닥에 있고, 새로운 데이터가 들어오면 자동으로 스크롤 바를 맨 아래에 이동시킵니다.
    - 사용자가 스크롤을 움직이면 자동 스크롤을 멈추고 스크롤 위치를 보존합니다. 이때, 데이터는 지속적으로 최신화합니다.
  2. 데이터 갱신 즉, 실시간 로그 스트리밍을 표현하기 위해 애니메이션을 구현합니다.

터미널 콘솔 스크롤 UX 분석

  1. 스크롤을 바닥에 두면 실시간으로 로그가 업데이트되고 있습니다.
  2. 스크롤을 움직이면 해당 위치의 데이터를 그대로 보존하면서 볼 수 있습니다.

고려 사항

와탭의 로그 데이터는 하루 최대 24억 건을 수용할 수 있기에 virtualized 기법을 사용하여 화면에 보이는 요소만 렌더링하고 있습니다.

구현

용어 정리

자동 스크롤: 새로운 로그가 들어왔을 때, 최하단으로 움직이는 스크롤 이벤트
수동 스크롤: 사용자가 스크롤을 직접 움직이는 스크롤 이벤트

요약

사용자가 과거 데이터를 확인하기 위해 스크롤을 위로 올리면 자동 스크롤이 일시 중지되어 로그를 안정적으로 읽을 수 있습니다. 이후 사용자가 스크롤을 최하단으로 내리면, 자동으로 최신 로그 스트리밍이 다시 시작됩니다.

  1. 새로운 데이터 추가 시 자동으로 스크롤이 최하단으로 이동
  2. 사용자 개입 감지: 사용자가 스크롤 조작을 감지해 수동 모드로 전환
  3. 사용자가 최하단까지 스크롤하면 자동 스크롤 모드로 변환

플로우

문제 1: 실시간 로그 스트리밍 애니메이션 구현

구현 플로우

애니메이션은 스크롤 이동 속도를 조절하여 구현할 수 있습니다. 애니메이션의 주기를 설정하고 진행률을 계산 후, scrollTop을 업데이트하는 방식으로 구현했습니다.

  1. 시간 기반 진행률: elapsed / duration로 0~1 사이 값 계산
  2. Easing 함수 적용: 진행률을 곡선으로 변환하여 자연스러운 움직임
  3. 위치 보간: 시작 위치 + (이동할 거리 × 진행률)로 현재 scrollTop 위치 계산
  4. rAF 반복: 60FPS로 부드러운 프레임 업데이트

애니메이션 종류

export const linear = (startValue: number, endValue: number, t: number): number => {
  return startValue + (endValue - startValue) * t;
};

export const easeInOutQuad = (t: number): number => {
  return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
};
  • Linear: 동일한 속도
  • Ease-in-out: 점진적 가속 후 점진적 감속
  • Ease-in: 점진적 가속감
  • Ease-out: 점진적 감속감


문제 2: 자동 스크롤, 수동 스크롤 구분 이슈

scrollTop값의 업데이트 전후 값을 비교하여 자동 및 수동 스크롤 여부를 판단할 수 있다고 생각했습니다. 하지만 virtualized 기법을 사용하고 있어 업데이트 전후 scrollTop값이 동일하다는 것을 예측하기 어렵다고 생각했고, 로그 컨텐츠는 높이가 컨텐츠에 따라 동적으로 달라지기에 scrollTop으로 비교가 불가능했습니다.

그리하여 자동 및 수동 스크롤을 판단하는 이벤트를 구분하는 방식을 접근했습니다.

  • 바닥으로 자동 스크롤(scrollTop을 움직이는 경우)했을 때 발생하는 이벤트: scroll
  • 사용자가 scroll을 움직였다는 것을 감지하는 이벤트: scroll

하지만 동일한 브라우저 이벤트를 사용하고 있어, 구분이 어려웠습니다.

해결 방안

사용자가 스크롤을 움직일 때는 mouse, touch, wheel 이벤트로 수동 스크롤 상태를 판단하고, mouse, touch이벤트에서는 사용자가 스크롤 바 영역을 조작했는지 여부를 판단했습니다.

  • 바닥으로 자동 스크롤했을 때 발생하는 이벤트: scroll
  • 사용자가 scroll을 움직였다는 것을 감지하는 이벤트: mousedown, touch, wheel
const handleMouseDown = (e) => {
  const isScrollbar = e.offsetX > e.currentTarget.clientWidth;
  if (!isScrollbar) {
    return;
  }
  updateScrollStatus('manual');
  stopScrollAnimation();
};

element.addEventListener('mousedown', handleMouseDown);

문제 3: 동적 Height Virtualized 리스트의 자동 스크롤 이슈

로그 데이터는 데이터마다 컨텐츠의 크기가 다르다는 특성이 있습니다.

요소마다 높이가 같은 정적 가상화 리스트
요소마다 높이가 같은 정적 가상화 리스트
요소마다 높이가 서로 다른 가상화 리스트
요소마다 높이가 서로 다른 가상화 리스트

요소의 높이가 고정된 가상화 리스트에서는 element.scrollTop = element.scrollHeight - element.clientHeight로 최하단 이동이 가능합니다. 하지만 요소마다 높이가 서로 다른 가상화 리스트에서는 DOM 요소들이 동적으로 생성/제거되고 높이가 실시간으로 변경되기 때문에 이 방법이 제대로 작동하지 않습니다.

그리하여 rAF를 활용하여 auto 모드일 때, 지속적으로 바닥으로 자동 스크롤하도록 설계했습니다.

function startContinuousScroll() {
  function continuousScrollLoop() {
	  // 이전 애니메이션 취소
    if (rAFIdRef.current) {
	    cancelAnimationFrame(rAFIdRef.current);
	  }
    if (scrollMode !== 'auto' || isUserInteracting) {
      return;
    }

    // 매 프레임마다 최신 scrollHeight 계산
    const maxScrollTop = element.scrollHeight - element.clientHeight;
    element.scrollTop = maxScrollTop;

    // 다음 프레임에서 다시 실행
	  rAFIdRef.current = requestAnimationFrame(() => {
      continuousScrollLoop();
    });
  }

	rAFIdRef.current = requestAnimationFrame(continuousScrollLoop);
}

데모

Before

After

세 가지 문제들을 해결함으로써,목표로 했던 사용자 경험을 제공할 수 있었습니다.

추가적으로 실시간 대용량 로그 렌더링에 필요한 힙 메모리를 효율적으로 사용하는 방법 또한 고민해야 하는데요. 이 경험은 다음 편에서 소개드리겠습니다. 감사합니다.

와탭 모니터링을 무료로 체험해보세요!