웹 개발에서 데이터 시각화는 필수 요소입니다. 특히 파이차트는 비율을 직관적으로 보여주는 도구로 자주 사용되죠. 하지만 많은 개발자가 간편함을 위해 무거운 JavaScript 차트 라이브러리를 도입합니다. 이는 페이지 로딩 속도에 부정적 영향을 미칠 뿐만 아니라, 접근성 측면에서도 문제가 될 수 있습니다. 이 글에서는 CSS의 conic-gradient()와 최신 CSS 함수들을 활용해, 시맨틱 마크업과 접근성을 모두 충족하는 파이차트를 만드는 방법을 단계별로 살펴봅니다. 자세한 구현 방법은 근거자료에서 확인할 수 있습니다.

시맨틱 HTML 구조 설계
가장 먼저, 스크린 리더가 이해할 수 있는 의미론적인 마크업을 구성해야 합니다. figure와 ul을 활용해 데이터를 구조화합니다.
<figure class="pie-chart">
<figcaption>지난 달 판매된 사탕</figcaption>
<ul>
<li data-percentage="30" data-color="#4A90E2">
<strong>초콜릿</strong>
</li>
<li data-percentage="25" data-color="#50E3C2">
<strong>젤리</strong>
</li>
<li data-percentage="20" data-color="#F5A623">
<strong>하드 캔디</strong>
</li>
<li data-percentage="25" data-color="#BD10E0">
<strong>풍선껌</strong>
</li>
</ul>
</figure>
CSS로 각 조각(Slice) 그리기
각 li 요소는 자신의 data-percentage와 data-color 속성을 읽어 고유한 조각을 그립니다. 최신 attr() 함수를 사용해 HTML 속성을 CSS 변수로 파싱합니다.
.pie-chart {
display: grid;
place-items: center;
}
.pie-chart li {
--radius: 20vmin;
--percentage: calc(attr(data-percentage number) * 1%);
--bg-color: attr(data-color color);
--weighing: calc(attr(data-percentage number) / 100);
width: calc(var(--radius) * 2);
aspect-ratio: 1;
border-radius: 50%;
grid-row: 1;
grid-column: 1;
/* conic-gradient로 조각 생성 */
background: conic-gradient(
from var(--offset, 0deg),
var(--bg-color) 0% var(--percentage),
transparent var(--percentage) 100%
);
}
JavaScript로 누적 값 계산
CSS만으로는 이전 조각들의 백분율을 누적할 수 없습니다. 간단한 JavaScript로 --accum 변수를 계산해 각 조각의 시작 각도를 설정합니다.
const pieChartItems = document.querySelectorAll('.pie-chart li');
let accum = 0;
pieChartItems.forEach((item) => {
item.style.setProperty('--accum', accum);
accum += parseFloat(item.getAttribute('data-percentage'));
item.style.setProperty('--offset', `${accum * 3.6}deg`);
});

레이블 원형 배치하기
cos()와 sin() CSS 함수를 이용해 각 조각의 중앙 지점에 레이블을 배치합니다. 이는 수학적 좌표 계산을 CSS에서 직접 가능하게 합니다.
.pie-chart li {
/* ... 기존 스타일 ... */
/* 레이블 위치 계산 */
--theta: calc((360deg * var(--weighing)) / 2 + var(--offset) - 90deg);
--gap: 4rem;
--pos-x: calc(cos(var(--theta)) * (var(--radius) + var(--gap)));
--pos-y: calc(sin(var(--theta)) * (var(--radius) + var(--gap)));
display: grid;
place-items: center;
}
.pie-chart li strong,
.pie-chart li::after {
grid-row: 1;
grid-column: 1;
transform: translateX(var(--pos-x)) translateY(var(--pos-y));
}
/* 퍼센트 표시 */
.pie-chart li::after {
content: attr(data-percentage) '%';
--pos-y: calc(sin(var(--theta)) * (var(--radius) + var(--gap)) + 1lh);
}
국내 개발 환경에서의 고려사항
국내 SI 프로젝트에서는 IE 지원이 종료되었지만, 일정 기간 레거시 브라우저 지원이 필요할 수 있습니다. conic-gradient()와 attr() with type()은 최신 브라우저에서만 완전히 지원됩니다. 프로덕션 적용 시 @supports를 이용한 점진적 향상(Progressive Enhancement) 전략이 필수적입니다. 또한, 국내 웹 접근성 가이드라인(KWCAG 2.1)을 준수하려면 색상 대비율과 스크린 리더 테스트를 추가로 수행해야 합니다.

주의사항 및 한계
이 방법은 순수 CSS에 가깝지만, 여전히 조각의 누적 각도를 계산하기 위해 최소한의 JavaScript가 필요합니다. 또한 cos()와 sin() 함수는 아직 모든 브라우저에서 지원되지 않을 수 있습니다. 대체方案으로 CSS transform: rotate()를 조합할 수 있지만, 계산이 더 복잡해집니다. 색상은 data-color 속성으로 하드코딩해야 하며, 동적 색상 생성은 color-mix() 함수나 추가 JavaScript가 필요합니다.
다음 단계 학습 방향
이 기본 구현을 바탕으로 여러 가지 고급 기능을 추가해 볼 수 있습니다.
- 원시 데이터로 백분율 자동 계산:
data-value속성에 원시 값을 입력하면 CSS나 JS가 총합 대비 백분율을 자동 계산하도록 개선합니다. - 다양한 차트 타입 적용: 동일한 시맨틱 마크업 원리로 막대차트, 도넛차트를 구현해 보세요.
- 인터랙션 추가:
:hover시 조각이 강조되거나 툴팁이 나타나는 효과를 적용하면 UX가 크게 향상됩니다. - CSS Houdini 검토: 향후
CSS Properties and Values API가 더 널리 지원되면, JavaScript 없이도 CSS 내에서 상태를 누적할 수 있을 것입니다.
접근성과 성능을 고려한 데이터 시각화는 현대 웹 개발의 핵심 역량입니다. 이 튜토리얼이 무거운 라이브러리 의존에서 벗어나, 기본 기술의 힘으로 문제를 해결하는 데 도움이 되길 바랍니다.