들어가며

주식 시장에는 약 4,000개 종목이 있다. 매일 장이 끝나면 "오늘 시장이 어땠지?"가 궁금한데 4,000개를 하나하나 볼 수는 없다.

KOSPI가 1% 올랐다고 해서 시장 전체가 좋은 건 아니다. 반도체만 폭등하고 나머지는 다 빠졌을 수도 있다. 지수 하나로는 시장의 "분위기"를 알 수 없다.

그래서 시장지도(트리맵)를 만들었다. 4,000개 종목을 한 화면에 전부 보여주는 지도. 

 

사실 미국 주식 정보 사이트인 finviz를 따라한거다. https://finviz.com/map.ashx 

 

S&P 500 Map

× Ever heard of Finviz*Elite? Our premium service offers you real-time quotes, advanced visualizations, technical studies, and much more. Become Elite and make informed financial decisions. Find out more --> Upgrade your FINVIZ experience Join thousands o

finviz.com

 

AI에게 학습시키고 동일한 형식으로 만들어 달라고 했다. 국내에도 비슷한 형식의 사이트들이 있다. 

트리맵이란

트리맵은 네모의 크기색깔로 데이터를 표현하는 시각화 방식이다.

  • 네모 크기 = 시가총액. 삼성전자가 가장 크고, 소형주는 점처럼 작다.
  • 네모 색깔 = 등락률. 빨간색이면 상승, 파란색이면 하락, 회색이면 보합.

화면을 한 번 훑으면 "오늘 반도체가 전부 빨갛네 = 반도체 강세", "바이오가 파랗네 = 바이오 약세" 같은 판단이 바로 된다. 종목 하나하나 안 봐도 시장 분위기가 보인다.

 

약세장인 날의 시장지도 대부분 종목이 시퍼렇고 일부 섹터만 빨간색이 보인다.

 

위 날짜의 예를 들면 전반적인 시장 전체가 약세장인데. 건설 섹터만 상승하고 있다. 이럴 땐 건설 섹터에 뭔가 호재가 있나 하고 찾아볼 수 있다. 

6가지 조합으로 시장을 본다

네모의 크기와 색을 바꿔가며 시장을 다른 관점으로 볼 수 있게 만들었다.

일간맵 하나만으로는 다른 사이트와 차별점이 없는 것 같아서 기준도 2개를 만들었다.

시가총액 / 거래대금, 기간도 일간 / 주간 / 월간 

크기 기준 (2가지)

기준 의미 언제 쓰나
시가총액 회사 규모가 큰 종목이 큰 네모 대형주 중심으로 시장 파악할 때
거래대금 돈이 많이 몰린 종목이 큰 네모 "오늘 돈이 어디로 갔나" 볼 때

기간 기준 (3가지)

기준 의미
일간 오늘 하루 등락률
주간(5일) 최근 5거래일 누적 등락률
월간(20일) 최근 20거래일 누적 등락률

크기 2가지 × 기간 3가지 = 6가지 조합. 같은 시장이라도 보는 관점에 따라 풍경이 완전히 달라진다.

예를 들어:

  • 시총 × 일간 — "대형주 기준으로 오늘 시장이 어땠나" (가장 기본)
  • 거래대금 × 일간 — "오늘 돈이 어디로 몰렸나" (테마주, 급등주 파악)
  • 시총 × 월간 — "이번 달 어떤 섹터가 강했나" (추세 파악)

Squarified 알고리즘

트리맵을 만드는 핵심은 "네모를 어떻게 배치할 것인가"다. 단순하게 가로로 쭉 나열하면 삼성전자가 화면 절반을 가로로 길게 차지하고, 나머지는 얇은 띠가 된다. 보기 끔찍하고 정보도 안 읽힌다.

그래서 Squarified Treemap 알고리즘을 구현했다. 핵심 원리는 간단하다:

  1. 가장 큰 값부터 시작
  2. 현재 줄에 하나씩 추가하면서, "네모가 정사각형에 가까운가?"를 계산
  3. 더 넣으면 비율이 나빠지는 시점에서 줄을 바꿈
  4. 남은 영역에서 반복

이 알고리즘 덕분에 모든 셀이 정사각형에 가까운 형태로 배치된다. 가로로 길쭉하거나 세로로 찌그러진 셀이 없으니 종목명과 등락률을 읽기 쉽다.

구현은 약 70줄의 JavaScript로 끝났다. 외부 라이브러리 없이 순수 JS로 만들었다. D3.js 같은 시각화 라이브러리를 쓸 수도 있었지만, 트리맵 하나를 위해 무거운 라이브러리를 통째로 넣고 싶지 않았다.

색상 설계

색상은 생각보다 까다로웠다. 단순히 "상승=빨강, 하락=파랑"만으로는 부족하다.

문제: 대부분의 종목은 ±1% 이내

장이 평범한 날, 종목 대부분은 0.1%~0.5% 범위 안에서 움직인다. 이걸 단순 선형으로 색칠하면 화면 전체가 거의 같은 색이 되어서 아무 정보도 읽을 수 없다.

해결: 비선형 색상 그래디언트

등락률 구간별로 색상 강도를 다르게 설정했다:

  • ±0.05% 미만 — 회색 (변동 없음)
  • ±0.05~1% — 연한 빨강/파랑 (이 구간에서 색 차이가 가장 크게)
  • ±1~3% — 중간 채도
  • ±3~7% — 진한 색 (강한 등락)
  • ±7% 이상 — 최대 채도 (상한/하한가 수준)

핵심은 0~1% 구간을 넓게 펼치는 것이다. 대부분의 종목이 이 범위에 있으니까, 여기서 미세한 차이를 눈에 보이게 해줘야 한다. 7% 이상은 어차피 드물어서 최대 채도로 고정하면 충분하다.

다크테마 기반이라 상승은 회색→주황→노랑→붉은빨강, 하락은 회색→연파랑→파랑→진파랑 그래디언트를 사용했다. finviz와 비슷한 느낌이면서도 다크 배경에 더 잘 어울리는 색감을 찾느라 꽤 시간을 썼다.

2단 레이아웃: 섹터 → 종목

4,000개 종목을 그냥 나열하면 혼돈이다. 그래서 2단계 구조로 만들었다.

  1. 1단계: 섹터별 영역 배치 — 전자, 화학, 바이오 같은 업종별로 큰 영역을 먼저 나눈다
  2. 2단계: 섹터 내 종목 배치 — 각 영역 안에서 개별 종목을 배치한다

섹터 이름이 각 영역 상단에 표시되니까, "반도체 섹터가 전체적으로 빨갛다" 같은 패턴이 바로 보인다. finviz도 이 2단 구조를 쓴다 — 이게 가장 직관적이다.

삽질 기록

소형주 3,000개의 저주

처음에는 모든 종목을 다 그렸다. 4,000개 전부. 결과는 참혹했다.

문제 1: 렌더링 느림

4,000개 DOM 엘리먼트를 한 번에 생성하니까 브라우저가 버벅거렸다. 특히 모바일에서 심했다. 각 셀에 호버 이벤트까지 붙어있으니 당연한 결과였다.

문제 2: 소형주는 점

시총 기준으로 배치하면 삼성전자가 화면의 15%를 차지하는 반면, 시총 500억짜리 소형주는 2×3 픽셀짜리 점이 된다. 종목명은커녕 색깔도 구별이 안 된다. 정보 가치가 0이다.

해결: 시총 5,000억 이상만

API에 min_mktcap=5000 필터를 넣었다. 시총 5,000억 이상인 종목만 보여준다. 이러면 대략 200~300개 종목만 남는다.

"3,970개 중 300개만 보여주면 시장 전체를 못 보는 거 아닌가?"라고 생각할 수 있다. 하지만 시총 상위 300개가 전체 시장 시가총액의 90% 이상을 차지한다. 나머지 3,600개를 합쳐도 10%가 안 된다. 시장의 움직임은 결국 대형주가 결정한다.

렌더링 속도도 3,970개 → 300개로 줄이니 체감이 확 달라졌다.

라벨 겹침

셀 안에 종목명과 등락률을 표시하는데, 셀이 작으면 글자가 넘친다.

해결은 단순했다. 셀 크기에 따라 표시 여부를 분기했다:

  • 가로 35px, 세로 24px 이상 — 종목명 표시
  • 가로 40px, 세로 32px 이상 — 종목명 + 등락률 표시
  • 그 미만 — 아무것도 표시 안 함 (색깔만 보임)
  • 가로 60px 미만 — 폰트 사이즈를 8px로 축소

대형주는 이름이 보이고, 중형주는 이름만 겨우 보이고, 소형주는 색깔 블록으로만 존재한다. 자연스럽게 중요도 순서가 시각적으로 드러난다.

호버 툴팁과 클릭

색깔 블록만으로는 정확한 정보를 알 수 없으니, 마우스를 올리면 툴팁이 뜬다:

  • 종목명 + 종목코드
  • 소속 섹터 + 시장(KOSPI/KOSDAQ)
  • 현재가 + 등락률
  • 시가총액 + 거래대금

클릭하면 해당 종목의 상세 페이지로 바로 이동한다. 트리맵에서 "이 종목 뭐지?" 싶으면 클릭 한 번으로 차트, 재무, 수급까지 전부 볼 수 있다.

툴팁 위치도 신경 썼다. 화면 오른쪽 끝에서 마우스를 올리면 툴팁이 화면 밖으로 나가니까, 남은 공간을 계산해서 왼쪽으로 뒤집어 표시한다. 작은 디테일이지만 이런 게 없으면 은근히 불편하다.

실시간 연동

장중에는 실시간 시세 캐시를 활용한다. 별도의 웹소켓을 쓰는 건 아니고, 다른 기능에서 이미 수집 중인 실시간 데이터를 가져와서 트리맵에 반영한다.

장 마감 후에는 DB에서 종가 기준 데이터를 읽는다. 프론트엔드에서는 120초 캐시를 걸어서, 필터(시장/크기/기간)를 바꿀 때 API를 다시 호출하지 않고 캐시된 데이터를 재활용한다. 필터 변경은 같은 데이터를 다른 방식으로 보여주는 것뿐이니까.

실제로 어떻게 쓰나

매일 장 마감 후 트리맵을 열면, 10초 안에 이런 판단이 된다:

  • "오늘 전자 섹터가 전부 빨갛네 — 반도체 호재가 있었나 보다"
  • "바이오가 파란데 한 종목만 빨갛다 — 뭔가 개별 호재가 있었구나"
  • "거래대금 기준으로 보니까 소형주 하나에 돈이 몰렸네 — 테마주 움직임인가"

시총 기준에서 거래대금 기준으로 바꾸면 풍경이 완전히 달라진다. 시총으로 보면 삼성전자가 가장 큰데, 거래대금으로 보면 그날 테마에 휩쓸린 종목이 갑자기 커진다. 같은 시장을 다른 렌즈로 보는 것이다.

주간/월간으로 기간을 넓히면 단기 노이즈가 사라지고 추세가 보인다. "이번 달 꾸준히 강한 섹터가 어디지?"를 알고 싶을 때 월간 트리맵이 유용하다.

정리

  • 트리맵 — 네모 크기(시총 또는 거래대금) × 색깔(등락률)로 시장 전체를 시각화
  • 6가지 조합 — 크기 2종(시총/거래대금) × 기간 3종(일/주/월)
  • Squarified 알고리즘 — 정사각형에 가까운 배치, 순수 JS 70줄 구현
  • 비선형 색상 — 0~1% 구간을 넓게 펼쳐 평범한 날에도 차이가 보이게
  • 2단 구조 — 섹터별로 묶어서 업종 단위 강약이 한눈에
  • 성능 — 시총 5,000억 이상 필터(~300종목)로 렌더링 속도 확보
  • 실시간 — 장중 RT 캐시 활용, 프론트 120초 캐시로 필터 전환 즉시 반영

트리맵은 만드는 데 하루가 걸렸지만, 매일 10초씩 시간을 아껴준다. 3,970종목을 하나하나 확인하는 대신, 한 번의 시선으로 시장 전체를 읽는다.

 

다음 편에서는 공시를 자동으로 분류해주는 페이지를 만든 이야기를 다룬다.

 

+ Recent posts