단계구분도는 뉴스, 정책 보고서, 회사 대시보드에서 가장 흔히 보는 주제도입니다. 득표차로 색칠된 선거 지도, 감염률로 색칠된 COVID 지도, 소득으로 색칠된 GDP 지도를 본 적 있다면 이미 단계구분도를 읽은 셈입니다.
이 글에서는 단계구분도가 무엇인지, 언제 쓰는지, 흔한 실수와 함께, 벡터 타일 API로 프로덕션급 단계구분도를 만드는 방법을 정리합니다.
한 문장 정의
단계구분도는 지도의 각 영역을, 그 영역에서 측정된 단일 수치에 따라, 크기를 인코딩하는 컬러 스케일로 칠한 지도입니다.
어원은 그리스어 khoros(영역)와 plethos(다수). 1820년대 프랑스 통계학자에서 시작해, 그 뒤로 주제 지도학의 주력으로 자리 잡았습니다.
단계구분도 예시
대표적인 활용:
- 국가, 주, 우편번호별 인구밀도
- 선거구별 선거 결과, 색 진하기로 득표차 표현
- 국가별 1인당 GDP, 보통 로그 스케일 사용
- 지역별 실업률
- 카운티별 인구 10만 명당 COVID 감염률
- 동네별 평균 부동산 가격
- 인구조사 구역별 광대역 보급률
- 농업 지역별 작물 수확량
공통점은 데이터가 미리 정의된 영역마다 한 개의 숫자라는 것입니다.
단계구분도 vs 히트맵
자주 혼동되는데, 차이가 중요합니다.
단계구분도는 미리 정의된 영역이 필요합니다. 데이터는 이미 집계되어 있고, 영역마다 색이 하나씩 들어가며, 경계 자체가 메시지의 일부입니다.
히트맵은 점 데이터가 필요합니다. 밀도는 비닝이나 커널 스무딩으로 계산하고, 결과는 행정 경계를 무시하는 연속 그라데이션입니다. 영역 비교가 아니라 군집 탐색에 씁니다.
우편번호당 숫자 하나면 단계구분도, GPS 핑의 구름이라면 히트맵입니다.
단계구분도가 만들어지는 방식
세 가지 재료:
- 경계 지오메트리. 색칠할 영역의 GeoJSON 폴리곤(또는 벡터 타일 레이어). 영역마다 안정적인 식별자(ISO 국가 코드, 미국 주 FIPS, EU NUTS 코드, 우편번호 등)가 있어야 합니다.
- 영역별 값. 같은 식별자를 키로 쓰는 단순한 JSON 객체나 CSV.
- 둘을 조인하는 렌더러. 모던 웹 매핑 라이브러리(MapLibre GL, Mapbox GL, deck.gl, OpenLayers)는 feature-state나 속성 표현식으로 런타임에 데이터를 경계에 조인할 수 있습니다.
MapLibre GL JS에서의 개념적 패턴은 다음과 같습니다.
map.addSource('regions', {
type: 'vector',
url: 'https://api.mapatlas.xyz/v1/tiles/regions.json',
});
map.addLayer({
id: 'regions-fill',
type: 'fill',
source: 'regions',
'source-layer': 'regions',
paint: {
'fill-color': [
'interpolate',
['linear'],
['feature-state', 'value'],
0, '#f7fbff',
50, '#6baed6',
100, '#08306b',
],
'fill-opacity': 0.8,
},
});
// Push your data to each feature at runtime
for (const [regionId, value] of Object.entries(yourData)) {
map.setFeatureState(
{ source: 'regions', sourceLayer: 'regions', id: regionId },
{ value }
);
}
지도는 경계 타일을 한 번만 받아오고, 데이터가 바뀔 때마다 다시 스타일링합니다. 서버 측 재렌더링이 필요 없습니다.
적절한 컬러 스케일 고르기
세 가지 규칙:
단방향 데이터에는 시퀀셜. 한 색조의 옅은 색에서 진한 색으로. 의미 있는 중간값 없이 낮은 값에서 높은 값으로 흐르는 수치(인구, 소득, 비율)에 적합합니다.
양방향 데이터에는 다이버징. 중립적인 중간값과 양쪽으로 뻗는 두 색조(빨강과 파랑, 갈색과 청록 등). 0이나 평균이 의미를 가지고 값이 양쪽에 놓이는 경우(전년 대비 성장, 선거 득표차, 평균과의 편차)에 씁니다.
무지개 스케일은 피하세요. 사람이 균일하게 인지하지 못하는 순서를 암시하고 색각 이상 사용자에게도 작동하지 않습니다. ColorBrewer(colorbrewer2.org)가 지도 제작자가 검증한 단계구분도 팔레트의 표준 레퍼런스입니다.
흔한 실수는 구간 수를 너무 많이 잡는 것입니다. 5~7개가 최적이고, 그 이상이면 인접 색을 구분하기 어려워집니다.
분류 방법의 문제
스케일을 정했다면, 구간 경계를 어디에 둘지 결정해야 합니다.
- 등간격(Equal interval). 값의 범위를 균등하게 나눕니다. 균일 분포에 적합, 이상치가 있으면 무너집니다.
- 분위(Quantile). 구간마다 영역 수가 같도록 나눕니다. 모든 색을 쓰고 싶을 때 유용하지만 차이의 크기를 가릴 수 있습니다.
- 자연 분류(Jenks). 구간 내 분산을 최소화합니다. 데이터 기반으로는 가장 무난한 기본값입니다.
- 수동. 구간 경계 자체에 의미가 있을 때(세율 구간, 인구학적 임계값 등).
분류 방식은 지도가 들려주는 이야기에 실제로 영향을 줍니다. 진지한 리포트라면 어떤 분류를 썼는지 반드시 밝히세요.
흔한 함정
면적 편향. 넓고 인구밀도가 낮은 영역이 시각적으로 압도합니다. 미국 대선 결과를 득표차로 색칠한 단계구분도는 농촌 카운티가 거대해서 거의 빨갛게 보입니다. 카르토그램이나 도트 밀도 지도가 이를 보정해 줍니다.
비율이 아니라 원시 카운트. "주별 COVID 감염자 수" 지도는 사실상 인구 지도입니다. 절대값 자체가 메시지가 아닌 한, 인구, 면적 또는 다른 분모로 항상 정규화하세요.
오해를 부르는 스케일. 분위 스케일은 실제 차이가 작아도 지도를 흥미진진하게 보이게 하고, 등간격 스케일은 이상치가 있으면 지도를 평평하게 만듭니다. 묻고 있는 질문에 분류 방식을 맞추세요.
결측 데이터. 데이터가 없는 영역을 회색으로 보여줄지, 빼버릴지, 0과 같은 색으로 칠할지 미리 정해두세요. 선택마다 해석이 달라집니다.
다른 차트를 골라야 할 때
영역당 변수가 여럿이라면 단계구분도는 정보를 납작하게 만듭니다. 다음을 고려해 보세요.
- 스몰 멀티플. 변수마다 한 장씩 단계구분도를 격자로 배치.
- 이변량 단계구분도. 두 변수를 2차원 컬러 격자로 인코딩. 읽기는 어렵지만 잘 맞으면 강력합니다.
- 카르토그램. 영역 크기를 값에 맞춰 왜곡합니다. 면적 편향이 클 때 유용.
- 지도 위 막대그래프. 사실은 데이터가 표일 뿐이고 지도는 장식인 경우도 있습니다.
지도 제작은 커뮤니케이션입니다. 단계구분도는 여러 도구 중 하나입니다.
MapAtlas는 어디에 들어갈까
Dynamic Maps API는 국가, 지역, 우편번호 경계를 벡터 타일 소스로 제공해, 위 MapLibre 스니펫처럼 런타임에 본인 데이터로 스타일링할 수 있게 해 줍니다. 경계는 EU에 호스팅되고 버전이 찍혀 있으며, 표준 식별자(ISO 3166, NUTS, 각국 우편번호 체계)와 깔끔하게 조인되도록 설계되어 있습니다.
영역별 집계 통계를 보여줘야 하는 페이지(우편번호별 부동산 가격, 지역별 배송 비용, 영역별 매출)에서는 GeoEnrich API가 같은 경계 ID를 키로 영역별 값을 돌려주므로, 지도와 표에 동일한 조인을 그대로 쓸 수 있습니다. 주제 지도 전반에 대한 배경은 Thematic Map Guide에 정리되어 있습니다.
자주 묻는 질문
단계구분도란 무엇인가요?
단계구분도(choropleth map)는 지리적 영역(국가, 주, 우편번호, 인구조사 구역 등)을 해당 영역에서 측정된 통계 변수에 비례해 색칠하는 주제도입니다. 색의 진하기가 값을 나타내며 보통 진한 색이 큰 값, 옅은 색이 작은 값입니다. 인구밀도, 선거 결과, 1인당 GDP, 실업률처럼 행정 경계 단위로 집계되는 지표를 시각화할 때 표준으로 쓰입니다.
단계구분도와 히트맵은 어떻게 다른가요?
단계구분도는 미리 정의된 영역(국가, 우편번호, 행정구 등)을 영역별로 집계된 값에 따라 색칠합니다. 히트맵은 점 데이터의 밀도를 공간상에 연속적으로 보여주며 행정 경계를 따르지 않습니다. 데이터가 이미 영역별로 집계되어 있다면(예: 주별 득표, 우편번호별 매출) 단계구분도를, 점들이 어디에 몰려 있는지 보고 싶다면(예: 범죄 발생, 고객 위치, 센서 측정값) 히트맵을 쓰면 됩니다.
단계구분도는 언제 써야 하나요?
데이터가 영역당 한 개의 숫자이고, 영역 면적이 대체로 비슷하거나 면적 또는 인구로 정규화할 수 있을 때 적합합니다. 좋은 예: 카운티별 광대역 보급률, 지역별 인구 10만 명당 COVID 감염자 수, 우편번호별 평균 임대료. 영역 면적 차이가 극단적으로 크거나(농촌 광역이 인구는 적어도 시각적으로 압도) 영역당 변수가 여러 개인 경우엔 피하고 스몰 멀티플이나 다른 차트를 쓰세요.
단계구분도를 어떻게 만드나요?
세 가지가 필요합니다. 영역의 경계 지오메트리(GeoJSON 폴리곤), 안정적인 ID로 연결된 영역별 값, 그리고 둘을 조인해 컬러 스케일을 적용하는 렌더러입니다. 흔한 스택은 MapLibre GL이나 Mapbox GL + fill-color 표현식, deck.gl GeoJsonLayer + getFillColor, 정적 SVG라면 D3.js + d3-geo, 또는 호스팅형 동적 맵 API입니다. MapAtlas Dynamic Maps API는 국가, 지역, 우편번호 경계 타일을 제공하므로 런타임에 본인 데이터로 스타일을 입힐 수 있습니다.
단계구분도에 어떤 컬러 스케일이 좋나요?
자연스러운 0 지점이 있는 단방향 데이터에는 단일 색조의 시퀀셜 스케일(예: 옅은 파랑에서 진한 파랑)을 씁니다. 의미 있는 중간값을 기준으로 값이 양쪽으로 갈라지는 데이터(선거 득표차, 전년 대비 변화)에는 두 가지 색이 발산하는 다이버징 스케일(예: 빨강-흰색-파랑)을 씁니다. 무지개 스케일은 피하세요. 사람이 균일하게 인지하지 못하는 순서를 암시하고 색각 이상 사용자에게도 좋지 않습니다. ColorBrewer가 지도 제작자들이 검증한 표준 참고입니다.

