2025년 12월 7일 일요일

실시간 간트 차트 만들기: 10,000개 작업을 60fps로


"간트 차트를 웹에서 실시간으로? 성능이 나올까요?"

2년 전 받은 질문입니다. 답은 "YES, 하지만..."

오늘은 10,000개 작업을 60fps로 렌더링하는 간트 차트를 어떻게 구현했는지 공유합니다.

기술 스택 선택: 왜 SVG인가?

Canvas, SVG, DOM 중에 뭘로 그릴까 고민했습니다.

Canvas는 빠르지만 상호작용이 어렵고 접근성이 거의 없습니다.
DOM은 네이티브 이벤트를 지원하지만 1000개만 넘어도 2초가 걸립니다.
SVG는 그 중간입니다. 200ms 정도로 빠르면서도 네이티브 이벤트를 지원합니다.

간트 차트에는 SVG가 최적입니다.

SVG 최적화 전략

하지만 SVG도 그냥 쓰면 느립니다. 최적화가 핵심이죠.

class OptimizedSVGGantt {
  private taskPool: Map<string, SVGRectElement> = new Map();
  
  // 1. Object Pooling으로 DOM 조작 최소화
  getTaskBar(taskId: string): SVGRectElement {
    if (this.taskPool.has(taskId)) {
      return this.taskPool.get(taskId)!;
    }
    
    const rect = document.createElementNS(
      "http://www.w3.org/2000/svg", 
      "rect"
    );
    this.taskPool.set(taskId, rect);
    return rect;
  }
  
  // 2. CSS Transform 활용 (GPU 가속)
  moveTask(element: SVGElement, x: number, y: number) {
    element.style.transform = `translate(${x}px, ${y}px)`;
    element.style.willChange = 'transform';
  }
}

Virtual Scrolling: 10,000개 작업 처리하기

10,000개 작업을 모두 렌더링하면 메모리가 20MB를 넘어갑니다.
해결책은? 화면에 보이는 것만 렌더링하는 겁니다.

class VirtualGantt {
  private itemHeight = 30;
  private buffer = 5;
  
  calculateVisibleRange(
    scrollTop: number,
    viewportHeight: number,
    totalTasks: number
  ): [number, number] {
    const startIndex = Math.floor(scrollTop / this.itemHeight);
    const endIndex = Math.ceil(
      (scrollTop + viewportHeight) / this.itemHeight
    );
    
    // 버퍼 추가 (부드러운 스크롤)
    const bufferedStart = Math.max(0, startIndex - this.buffer);
    const bufferedEnd = Math.min(totalTasks, endIndex + this.buffer);
    
    return [bufferedStart, bufferedEnd];
  }
}

10,000개 중 실제로는 40개만 렌더링됩니다.
메모리 사용량이 95% 줄어듭니다.

WebSocket + CRDT: 실시간 동기화

여러 명이 동시에 같은 작업을 수정하면 어떻게 될까요?
CRDT(Conflict-free Replicated Data Type)가 답입니다.

import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

class RealtimeGanttSync {
  private yDoc: Y.Doc;
  private yTasks: Y.Map<any>;
  
  initialize() {
    this.yDoc = new Y.Doc();
    this.yTasks = this.yDoc.getMap('tasks');
    
    // WebSocket 연결
    new WebsocketProvider(
      'wss://api.plexo.work/gantt',
      'project-123',
      this.yDoc
    );
    
    // 변경사항 감지
    this.yTasks.observe(this.handleRemoteChange);
  }
  
  // 충돌 없이 자동 병합!
  handleRemoteChange = (event: Y.YMapEvent<any>) => {
    event.changes.keys.forEach((change, key) => {
      if (change.action === 'update') {
        const task = this.yTasks.get(key);
        this.updateGanttUI(key, task);
        this.showNotification(`${task.modifiedBy}가 수정함`);
      }
    });
  };
}

Yjs가 자동으로 충돌을 해결합니다.
A가 시작일을 바꾸고 B가 종료일을 바꿔도 문제없습니다.

성능 측정 결과

10,000개 작업 프로젝트 기준:

  • 초기 렌더링: 1.2초
  • 스크롤 FPS: 55-60fps
  • 실시간 업데이트 지연: 50ms
  • 메모리 사용: 50MB

최적화 전에는 1000개만 해도 10초가 걸렸습니다.
지금은 50,000개까지 테스트해봤습니다.

실전 구현 팁

프로파일링이 답이다

class PerformanceMonitor {
  measure(name, fn) {
    performance.mark(`${name}-start`);
    const result = fn();
    performance.mark(`${name}-end`);
    
    performance.measure(name, `${name}-start`, `${name}-end`);
    
    const measure = performance.getEntriesByName(name)[0];
    console.log(`${name}: ${measure.duration}ms`);
    
    return result;
  }
}

Chrome DevTools Performance 탭을 열고 측정하세요.
병목이 어디인지 정확히 알 수 있습니다.

점진적 개선

처음부터 완벽할 필요 없습니다.

1단계: 기본 기능 구현 (느려도 OK)
2단계: Virtual Scrolling 적용
3단계: WebWorker로 무거운 계산 분리
4단계: 캐싱 전략 도입
5단계: WebAssembly로 핵심 알고리즘 재작성

사용자 경험 우선

느려도 반응은 즉시 해야 합니다.

async handleUserAction(action) {
  // 즉시 피드백
  this.showLoadingIndicator();
  
  // 무거운 작업은 비동기로
  await this.processInBackground(action);
  
  // 완료 후 업데이트
  this.hideLoadingIndicator();
}

마무리: 기술은 수단일 뿐

아무리 기술적으로 완벽해도, 사용자가 안 쓰면 의미가 없습니다.

우리가 집중한 것:

  • 빠른 반응: 클릭하면 즉시 움직여야
  • 부드러운 스크롤: 버벅이면 안 써요
  • 실시간 동기화: 협업이 핵심이니까
  • 직관적 인터페이스: 매뉴얼 없이도 OK

가장 보람 있었던 순간은 사용자가 "우와, 이거 진짜 빠르네요!"라고 했을 때입니다.

여러분도 웹 기반 간트 차트를 만들 계획이신가요?
이 글이 도움이 되길 바랍니다.


고성능 실시간 간트 차트를 체험해보세요. Plexo

2025년 12월 6일 토요일

간트 차트가 실패하는 진짜 이유


"간트 차트 보면 모든 게 완벽해 보이는데, 실제로는 엉망진창이에요."

많은 PM들이 하는 말입니다.
1910년에 헨리 간트가 만든 이 도구를 왜 우리는 아직도 쓰고 있을까요?

답은 간단합니다: 더 나은 대안이 없었으니까요.

하지만 이제는 다릅니다.

구식 간트 차트의 7대 죄악

1. 경직된 계획의 함정

MS Project를 켜본 적 있으신가요?
작업 하나를 옮기려면 클릭, 속성 창, 날짜 수정, 의존성 확인, 충돌 해결...
5분이 훌쩍 지나갑니다.

그래서 결국 "그냥 엑셀 쓸게요"라고 하게 되죠.

2. PM만 보는 외로운 차트

const traditional_gantt = {
  주_사용자: 'PM 1명',
  보는_사람: 'PM + 보고받는 상사',
  실제_작업자: '간트 차트가 뭔지 모름',

  결과: {
    계획: '완벽한 간트 차트',
    실행: '카톡으로 일정 조율',
    괴리: '100%',
  },
};

3. 배우는데만 몇 주

대기업 PM 교육을 보면 MS Project 기초부터 WBS 입력, 리소스 관리, 의존성 설정까지 10일이 걸립니다.
그런데 실제 사용률은? 20%도 안 됩니다.

4. 현실과 동떨어진 계획

간트 차트는 모든 작업이 정확히 월요일 9시에 시작해서 금요일 6시에 끝난다고 가정합니다.
하지만 현실은? 이슈 생겨서 수요일부터 시작하고, 다음 주 화요일 새벽에 끝나죠.

5. 일방향 커뮤니케이션

PM이 "간트 차트 업데이트했으니 확인하세요"라고 하면,
팀원들은 어디서 봐야 하는지도 모릅니다.

6. 수동 업데이트 지옥

한 PM의 하루를 보면, 오전 내내 간트 차트 업데이트에 시간을 씁니다.
어제 완료된 작업 체크, 지연된 작업 날짜 수정, 의존성 재조정...
점심시간이 되어서야 실제 일을 시작할 수 있습니다.

7. 협업 불가능

PM이 간트 차트를 수정하고, PDF로 내보내고, 이메일로 공유하면,
팀원이 피드백을 이메일로 보내고, PM이 다시 수정하고...
버전 충돌이 생기면? 처음부터 다시입니다.

간트 차트가 실패하는 진짜 이유

인지적 과부하

인간의 단기 기억 용량은 7±2개 항목입니다.
그런데 간트 차트에는 평균 50-200개 항목이 있습니다.
700% 초과하는 정보량 앞에서 우리 뇌는 포기합니다.

컨텍스트 스위칭 비용

개발자가 간트 차트를 보려면?
코딩 중단, 프로그램 실행, 로그인, 프로젝트 찾기, 내 작업 찾기, 이해하기...
8분 30초가 지나갑니다. 그리고 얻은 정보는? 거의 없습니다.

혁신의 시작: 실시간 협업 간트

2006년 이전에는 워드 파일을 이메일로 주고받았습니다.
2006년 이후 구글 독스가 실시간 동시 편집을 가능하게 했죠.

이제 간트 차트도 그래야 합니다.

현대적 간트 차트의 조건

interface ModernGanttRequirements {
  // 실시간성
  realtime: {
    동시편집: true;
    즉시반영: true;
    충돌해결: '자동';
  };

  // 접근성
  accessibility: {
    웹기반: true;
    모바일: true;
    학습시간: '10분 이내';
  };

  // 유연성
  flexibility: {
    드래그앤드롭: true;
    자동조정: true;
    다양한뷰: ['간트', '캘린더', '칸반', '리스트'];
  };
}

실제 변화의 모습

한 스타트업이 전통적 간트에서 현대적 간트로 바꾼 후:

  • 간트 차트 사용률: 10% → 85%
  • 일정 조정 시간: 주 4시간 → 주 30분
  • 프로젝트 지연: 40% → 5%
  • 팀 만족도: 5/10 → 8.5/10

무엇이 달라졌나?

모두가 주인의식을 가짐

예전에는 PM이 "제가 일정 조정할게요"라고 했지만,
지금은 팀원이 "제 작업이 늦어질 것 같아요. 간트에서 직접 조정했어요"라고 합니다.

투명성이 책임감을 만듦

모두가 볼 수 있을 때 일정 준수율은 50%에서 85%로 올라갑니다.
사전 알림도 "가끔"에서 "100%"가 됩니다.

실시간 조정이 스트레스를 줄임

기존에는 일정 변경 요청부터 팀 공유까지 일주일이 걸렸습니다.
지금은? 드래그 앤 드롭, 즉시 알림, 즉시 확인. 끝입니다.

마무리: 간트 차트는 죽지 않았다

간트 차트 자체가 문제는 아닙니다.
구현 방식이 문제였을 뿐입니다.

100년 전 헨리 간트의 아이디어는 여전히 유효합니다.
시간을 시각화하고, 의존성을 표시하고, 진행상황을 보여주는 것.

다만 이제는 실시간이어야 하고, 협업이 가능해야 하고, 쉬워야 합니다.

당신의 팀은 아직도 PDF 간트 차트를 보고 있나요?


현대적 간트 차트를 경험하고 싶으신가요? Plexo를 확인해보세요.