그리드를 넘어 히트맵으로

그리드를 넘어 히트맵으로

1. 배경 및 문제 상황

A 프로젝트에서는 사용자 활동 데이터를 연 단위로 시각화하기 위해 365일 히트맵 UI를 구현해야 하는 요구사항이 있었습니다.

처음에는 단순히 “히트맵”이라는 공통 개념만 생각하고 접근하였기 때문에, 프로젝트에 이미 포함되어 있던 ApexCharts를 활용하는 것이 가장 효율적인 선택이라고 판단하였습니다.

이미 운영 중인 차트 라이브러리를 재사용하면 신규 의존성을 추가하지 않아도 되었고, 초기 개발 속도 측면에서도 유리할 것이라고 생각하였습니다.

하지만 실제 구현을 진행하면서 히트맵에도 명확한 종류와 목적의 차이가 존재한다는 점을 체감하게 되었습니다.처음에는 단순히 데이터를 색상으로 표현하면 모두 동일한 히트맵이라고 생각하였지만, 실제로는 데이터 모델과 렌더링 방식 자체가 서로 다른 구조를 가지고 있었습니다.

히트맵은 일반적으로 크게 세 가지 유형으로 구분할 수 있습니다.

첫 번째는 통계 및 분석 용도로 많이 사용하는 그리드 기반 히트맵입니다.

이 방식은 X축과 Y축이 명확한 카테고리 구조를 가지며, 특정 좌표의 값을 색상으로 표현합니다.

예를 들어 요일별 시간대 사용량, 상품별 판매량 비교, 부서별 성과 비교 같은 형태가 대표적입니다.

두 번째는 날짜의 흐름을 기준으로 데이터를 표현하는 시계열 캘린더 히트맵입니다.

GitHub Contribution Graph와 유사한 형태가 대표적인 사례이며, 날짜 기반의 흐름 자체가 핵심 데이터 모델이 됩니다. 단순히 좌표를 표시하는 것이 아니라 “시간”이라는 개념이 중심에 존재합니다.

세 번째는 지도나 위치 기반 데이터에서 사용하는 밀도 히트맵입니다.

이는 특정 좌표에 얼마나 데이터가 집중되어 있는지를 시각적으로 표현하기 위한 방식이며, 일반적으로 GIS 계열 서비스에서 많이 활용됩니다.

문제는 ApexCharts가 전형적인 그리드 히트맵 엔진이라는 점이었습니다.

즉, 좌표 기반 데이터 시각화에는 최적화되어 있었지만 날짜 흐름을 중심으로 하는 시계열 캘린더 히트맵에는 적합하지 않은 구조였습니다.

당시 구현 대상은 365일 데이터를 주 단위 기준으로 배치해야 하는 캘린더 히트맵 형태였습니다.

하지만 ApexCharts는 날짜 자체를 인식하지 못하고 단순한 좌표 데이터로만 처리하였기 때문에, 날짜 데이터를 차트가 이해할 수 있는 형태로 직접 변환해야 했습니다.

결국 1년 데이터를 7행 52열 구조의 좌표계로 매핑하는 별도의 전처리 로직이 필요하였습니다.

예를 들어 특정 날짜가 몇 번째 주인지, 무슨 요일인지, 어느 좌표에 배치되어야 하는지를 모두 직접 계산해야 했습니다. 구현 자체는 가능하였습니다.

하지만 문제는 라이브러리의 설계 목적과 실제 사용 방식이 맞지 않았다는 점이었습니다. 본래 통계용으로 설계된 그리드 차트 엔진에 캘린더 개념을 억지로 결합하다 보니 불필요한 데이터 가공 로직이 지속적으로 추가되었습니다.

특히 날짜 데이터를 좌표로 변환하는 과정에서 예외 처리 코드가 증가하였고, 월 경계 처리나 윤년 처리 같은 추가 로직도 함께 필요하였습니다.

이 과정에서 단순한 UI 구현 이상의 유지 보수 비용이 발생하기 시작하였습니다. 기능 하나를 추가할 때마다 차트 데이터 구조와 날짜 계산 로직이 동시에 영향을 받았고, 결과적으로 코드 복잡도가 빠르게 증가하였습니다.

또한 프로젝트 후반부로 갈수록 “현재 구현 방식이 과연 장기적으로 유지 가능한 구조인가”에 대한 고민이 커지기 시작하였습니다.단순히 구현 가능 여부만으로 기술을 선택하면 이후 운영 단계에서 더 큰 비용이 발생할 수 있다는 점을 실감하게 되었습니다.

2. 시계열 캘린더 히트맵 구현 과정에서 발생한 구조적 한계

이후 사내 UI 가이드 표준화를 진행하면서 MUI Charts를 활용하여 동일한 시계열 캘린더 히트맵을 다시 구현할 기회가 있었습니다.

처음에는 MUI 생태계와의 통합성이 좋다는 점 때문에 기대가 컸습니다.

특히 기존 디자인 시스템과의 결합 측면에서는 ApexCharts보다 더 유리할 것이라고 판단하였습니다.

하지만 실제 구현 과정에서는 이전과 유사한 구조적 문제가 다시 발생하였습니다.

가장 대표적인 사례는 툴팁 구현이었습니다.디자인 가이드상 히트맵 셀에 마우스를 올렸을 때 날짜와 값뿐 아니라 요일 정보까지 함께 표시되어야 했습니다.

하지만 MUI Charts 역시 기본적으로는 그리드 기반 데이터 모델을 사용하고 있었기 때문에, 차트 내부에서는 데이터를 단순 좌표값으로만 인식하고 있었습니다.

즉, 툴팁에서 “2025년 3월 15일 토요일” 같은 정보를 표시하려면 현재 좌표값이 실제 어떤 날짜에 해당하는지를 다시 역산해야 했습니다.이 과정은 생각보다 훨씬 복잡하였습니다.

차트 렌더링을 위해 이미 날짜 데이터를 좌표 기반으로 변환한 상태였기 때문에, 툴팁에서는 다시 좌표를 날짜로 복원하는 반대 방향 계산이 필요하였습니다.

결국 다음과 같은 문제가 발생하였습니다.

첫째, 차트 렌더링용 데이터와 실제 비즈니스 데이터 모델이 분리되기 시작하였습니다.

둘째, 단순한 UI 표시를 위해 역방향 데이터 매핑 로직이 추가되었습니다.

셋째, 디자인 레이아웃이 조금만 변경되어도 날짜 계산 구조 전체가 영향을 받게 되었습니다.

특히 월 시작 위치나 주 계산 기준이 변경되면 기존 계산 로직이 모두 흔들릴 가능성이 있었습니다.

이는 단순히 화면 구현의 문제가 아니라 운영 안정성과 직결되는 문제였습니다.

또한 차트 라이브러리 버전이 업데이트될 경우 기존 우회 로직이 정상 동작할 것이라는 보장이 없었습니다. 라이브러리 내부 렌더링 구조가 변경되면 커스텀 로직이 쉽게 깨질 수 있었기 때문입니다.

이처럼 라이브러리의 본래 설계 목적을 벗어난 과도한 커스터마이징은 결국 기술 부채로 이어질 가능성이 높다고 판단하였습니다.

특히 운영 관점에서는 “현재 구현 가능한가”보다 “1년 뒤에도 유지 가능한가”가 더 중요하다고 생각하였습니다. 초기 개발 속도만 보고 기존 스택을 유지하는 선택은 오히려 장기적인 유지보수 비용 증가로 이어질 수 있었습니다.

3. 전용 시계열 히트맵 라이브러리 검토 및 선정 과정

기존 그리드 기반 차트 라이브러리들이 가진 구조적 한계를 확인한 이후, 날짜 데이터를 중심으로 처리하는 전용 시계열 히트맵 라이브러리들을 별도로 검토하기 시작하였습니다.

가장 중요하게 본 기준은 다음과 같았습니다.

첫째, 날짜 데이터를 라이브러리 내부에서 기본적으로 이해하는 구조인지 여부였습니다.

둘째, 툴팁에서 날짜와 요일 정보를 자연스럽게 처리할 수 있는지 여부였습니다.

셋째, 반응형 레이아웃을 안정적으로 지원하는지 여부였습니다.

넷째, 사내 공통 디자인 시스템과 얼마나 자연스럽게 결합될 수 있는지 여부였습니다.

여러 후보군을 비교 검토한 결과, react-activity-calendar가 가장 적합한 선택이라고 판단하였습니다.

이 라이브러리는 GitHub Contribution 스타일의 시계열 캘린더 히트맵에 특화된 구조를 가지고 있었습니다.

즉, 날짜 자체가 데이터 모델의 중심에 존재하기 때문에 별도의 좌표 변환 로직이 거의 필요하지 않았습니다.

특히 날짜 데이터만 전달하면 내부 엔진이 자동으로 주 단위 배치와 셀 위치 계산을 수행해준다는 점이 매우 큰 장점이었습니다.

기존 방식처럼 “몇 번째 주인지”, “무슨 요일인지”, “어느 좌표에 위치해야 하는지”를 직접 계산할 필요가 없었습니다.

또한 툴 팁 구성 역시 훨씬 단순하였습니다. 이미 날짜 기반 데이터 구조를 내부적으로 유지하고 있었기 때문에, 날짜와 요일 정보를 추가적인 역산 없이 바로 사용할 수 있었습니다.

결과적으로 기존에 존재하던 우회 계산 로직 대부분을 제거할 수 있었습니다.

이는 단순히 코드 길이가 줄어든 수준이 아니라 데이터 구조 자체가 훨씬 직관적으로 개선되었다는 의미였습니다.

4. 반응형 처리와 디자인 시스템 연계

react-activity-calendar를 선택하게 된 또 하나의 핵심 이유는 반응형 처리 방식이었습니다. 일반적인 차트 라이브러리들은 부모 컨테이너 크기에 맞춰 width와 height를 직접 계산하여 전달해야 하는 경우가 많습니다.

특히 셀 기반 UI에서는 브라우저 크기 변화에 따라 셀 크기와 간격을 별도로 재계산해야 하는 경우가 자주 발생합니다.

하지만 react-activity-calendar는 이러한 계산을 내부 엔진이 자동으로 처리하고 있었습니다. 브라우저 크기가 변경되면 셀 크기와 간격을 자동 재배치하면서 레이아웃을 유지하였습니다. 이러한 구조 덕분에 ResizeObserver 기반의 별도 계산 로직을 구현할 필요가 없었습니다.

결과적으로 반응형 처리 코드 자체가 크게 단순화되었습니다.

또한 스타일 처리 방식 역시 매우 만족스러웠습니다. 기존 차트 라이브러리들은 대부분 CSS 클래스를 강제로 덮어쓰는 방식의 커스터마이징을 요구하였습니다.

하지만 이 방식은 라이브러리 내부 클래스 구조에 강하게 의존하게 되며, 버전 변경 시 쉽게 깨질 수 있다는 문제가 있었습니다.

반면 react-activity-calendar는 theme 객체 기반의 선언적 스타일 구조를 제공하고 있었습니다.

즉, 컬러 토큰을 객체 형태로 직접 주입할 수 있었기 때문에 사내 공통 UI 시스템인 vizend-ui와 자연스럽게 연결할 수 있었습니다.

특히 디자인 토큰 기반 구조를 사용하는 프로젝트에서는 이러한 선언적 테마 방식이 유지 보수 측면에서 매우 큰 장점으로 작용하였습니다.

5. 정리

이번 히트맵 라이브러리 전환 과정은 단순한 UI 컴포넌트 교체 작업 이상의 의미가 있었습니다. 처음에는 기존 프로젝트에 이미 포함된 라이브러리를 최대한 재사용하는 것이 효율적이라고 생각하였습니다.

하지만 실제 구현 과정에서는 라이브러리의 설계 목적과 데이터 모델의 본질이 맞지 않을 경우, 예상보다 훨씬 큰 유지보수 비용이 발생할 수 있다는 점을 경험하였습니다.

특히 시계열 캘린더 히트맵은 단순 좌표 기반 차트와는 완전히 다른 성격의 데이터 구조를 가지고 있었습니다. 날짜 자체가 핵심 데이터 모델이기 때문에, 이를 단순 좌표 기반으로 강제 변환하는 방식은 장기적으로 기술 부채를 증가시키는 결과로 이어질 수 있었습니다.

결국 중요한 것은 “현재 구현 가능한가”가 아니라 “운영 환경에서 얼마나 안정적으로 유지 가능한가”라고 생각하였습니다.

react-activity-calendar로 전환한 이후에는 불필요한 전처리와 역산 로직 대부분을 제거할 수 있었으며, 데이터 신뢰성과 코드 가독성 모두 크게 개선할 수 있었습니다.

또한 반응형 처리와 디자인 시스템 연계 측면에서도 훨씬 안정적인 구조를 확보할 수 있었습니다.

이번 경험을 통해 기술 선택에서는 단순한 기능 지원 여부보다 데이터 모델의 성격과 라이브러리의 설계 철학이 얼마나 잘 맞는지를 함께 고려해야 한다는 점을 다시 한번 실감할 수 있었습니다.

[참고 자료]

• MUI X Heatmap Documentation

https://mui.com/x/react-charts/heatmap/

• ApexCharts React Heatmap Demos

https://apexcharts.com/react-chart-demos/heatmap-charts/

KKAMJJING

Site footer