원형 프로그레스 바가 도넛처럼 차오르는 원리

앱 로딩 화면이나 대시보드 통계 카드에서 원형 프로그레스 바가 도넛처럼 서서히 채워지는 걸 본 적 있을 것입니다. 이건 미리 만들어둔 이미지나 움직이는 GIF가 아니라, SVG 원 하나의 테두리 선을 부분적으로만 그려서 만든 결과입니다. 원리를 알면 라이브러리 없이도 직접 코드로 재현할 수 있습니다.
요약 ① 원형 프로그레스 바는 이미지가 아니라 SVG circle 요소의 테두리 선(stroke)입니다 ② stroke-dasharray로 선을 원 둘레 전체 길이만큼 긋고, stroke-dashoffset으로 보이지 않을 구간을 밀어냅니다 ③ 원 둘레(2πr)와 진행률만 알면 이 두 값을 직접 계산할 수 있습니다
원형 프로그레스 바, 왜 도넛처럼 차오르게 보일까
SVG circle 요소는 원래 fill(내부 채우기)과 stroke(테두리 선)를 따로 가집니다. fill을 투명하게 비우고 stroke만 두껍게 칠하면 도넛 모양의 링이 만들어지고, 이 링을 두 겹으로 겹쳐서 아래는 옅은 배경 원, 위는 진행률만큼만 보이는 색 원으로 구성합니다.
위쪽 원이 "진행률만큼만" 보이는 이유는 stroke-dasharray라는 속성 때문입니다. 원래는 점선을 그릴 때 쓰는 속성으로 선을 그렸다가(dash) 비웠다가(gap)를 반복하는데, 이 값을 원 둘레 전체 길이로 딱 맞춰 넣으면 dash 하나가 원 한 바퀴와 정확히 같아져서 끊김 없이 이어진 선처럼 보입니다.
여기에 stroke-dashoffset을 더하면 그 선의 시작 위치를 둘레 방향으로 밀어낼 수 있습니다. 선 길이에서 오프셋만큼을 밀어내면 그만큼이 원의 반대쪽으로 숨겨지면서 일부만 채워진 것처럼 보이고, transform으로 원을 -90도 회전시키면 시작점이 3시 방향이 아니라 12시 방향이 됩니다.
확인하는 방법 — 원 둘레 계산으로 직접 그리기
코드 몇 줄이면 라이브러리 없이 계산할 수 있습니다. 먼저 반지름(r)은 원의 지름에서 선 두께의 절반을 뺀 값입니다. 선이 원의 중심선을 따라 그려지기 때문에 선 두께만큼 안쪽 공간을 확보해야 두 원이 겹치지 않습니다.
반지름을 구했다면 원 둘레는 "2 × π × r" 공식으로 계산합니다. 예를 들어 지름 120px, 선 두께 8px이라면 반지름은 56px, 둘레는 약 351.86px이고 이 값을 stroke-dasharray에 그대로 넣으면 원 전체가 하나의 실선처럼 그려집니다.
진행률을 반영하려면 stroke-dashoffset을 "둘레 × (1 − 진행률 ÷ 100)"으로 계산합니다. 진행률 75%라면 351.86 × 0.25, 즉 약 87.96px을 오프셋으로 넣으면 됩니다. 이 두 값과 -90도 회전만 circle 태그에 넣으면 도넛형 진행률 바가 완성됩니다.
저희 도구로 확인하기. 진행률·두께·크기를 슬라이더로 바꿀 때마다 둘레와 오프셋이 자동으로 재계산되어, 값을 손으로 다시 구하지 않고도 결과와 코드를 바로 확인할 수 있습니다.
무료 · 가입 불필요 · 브라우저에서 바로 SVG·CSS 코드 생성

자주 막히는 지점
| 상황 | 원인 |
|---|---|
| 진행 방향이 반대로 돎 | stroke-dashoffset 계산 부호를 반대로 넣었거나 회전 방향을 잘못 준 경우입니다 |
| 시작점이 위쪽이 아니라 오른쪽(3시)에 있음 | circle에 transform: rotate(-90deg)를 안 준 상태입니다 |
| 배경 원과 진행 원의 두께 중심이 안 맞음 | 반지름 계산에서 선 두께의 절반을 빼지 않은 경우입니다 |
| 진행률이 바뀔 때 뚝 끊겨 보임 | stroke-dashoffset에 transition을 걸지 않아 애니메이션 없이 값만 바뀐 경우입니다 |
표에 정리된 것처럼, 어긋나는 지점은 대부분 오프셋 부호와 회전 방향 중 하나입니다. 계산식과 -90도 회전을 한 번 고정해두면 이후에는 진행률 숫자만 바꿔 넣으면 됩니다.
정리
원형 프로그레스 바는 이미지가 아니라 SVG circle의 테두리 선을 stroke-dasharray로 원 둘레 전체 길이만큼 긋고, stroke-dashoffset으로 안 보이게 할 구간을 밀어내서 만듭니다. 원 둘레(2πr)와 진행률만 있으면 두 값 모두 계산으로 구할 수 있습니다.
매번 반지름과 둘레를 손으로 계산하는 대신, 슬라이더로 값을 바꾸면서 코드가 어떻게 달라지는지 바로 확인하면 실수를 줄일 수 있습니다.
무료 · 가입 불필요 · 브라우저에서 바로 SVG·CSS 코드 생성
자주 묻는 질문
stroke-dasharray는 원래 점선 속성인데 왜 진행률 표시에 쓰나요?
dash(선) 길이를 원 둘레 전체와 똑같이 맞추면 점선이 아니라 끊김 없는 실선 하나가 됩니다. 여기에 dashoffset으로 그 실선의 시작 위치를 밀어내면, 밀려난 구간만 원의 반대쪽으로 숨겨지면서 결과적으로 일부만 그려진 것처럼 보이는 원리입니다.
진행률이 바뀔 때 부드럽게 채워지게 하려면 어떻게 하나요?
stroke-dashoffset 값에 CSS transition을 걸어두면 됩니다. 값이 바뀔 때마다 브라우저가 이전 오프셋에서 새 오프셋까지 자동으로 보간해서 그려주기 때문에 애니메이션처럼 채워지는 효과가 납니다.
원형 프로그레스 바를 PNG나 GIF 이미지로 만들면 안 되나요?
만들 수는 있지만 진행률 숫자가 바뀔 때마다 이미지를 새로 만들어야 하고, 화면 크기가 달라지면 화질도 함께 깨집니다. SVG는 좌표 값만 계산식으로 바꾸면 되고 어떤 해상도에서도 선명하게 보이는 차이가 있습니다.
