게이지 차트

Gauge chart

값 하나를 반원 눈금 위 바늘/호로 보여주는 차트. 속도계처럼 "지금 상태가 정상 범위 안인가"에 답합니다.

다른 이름: Speedometer chart반원형 게이지
···
html
<div class="viz">
  <svg id="svg"></svg>
</div>
css
body{display:block}
.viz{position:relative;width:100%;height:100%}
svg{display:block;width:100%;height:100%;overflow:visible}
js
var svg = document.getElementById('svg');
var NS = 'http://www.w3.org/2000/svg';
function se(t){ return document.createElementNS(NS,t); }
function sa(e,o){ for (var k in o) e.setAttribute(k,o[k]); }
function R(a,b){ return a + Math.random()*(b-a); }

var cur = 50;
var W=300, H=200;

function measure(){
  var r = svg.getBoundingClientRect();
  W = Math.max(r.width,10); H = Math.max(r.height,10);
  sa(svg,{ viewBox: '0 0 ' + W + ' ' + H });
}

function arcPoint(cx,cy,r,deg){ var rad=deg*Math.PI/180; return { x:cx+r*Math.cos(rad), y:cy-r*Math.sin(rad) }; }
function arcPath(cx,cy,r,d0,d1){
  var p0=arcPoint(cx,cy,r,d0), p1=arcPoint(cx,cy,r,d1);
  var large = Math.abs(d0-d1) > 180 ? 1 : 0;
  return 'M'+p0.x+','+p0.y+' A'+r+','+r+' 0 '+large+' 1 '+p1.x+','+p1.y;
}

function draw(){
  svg.innerHTML = '';
  var cy = H*0.64;
  var r = Math.max(20, Math.min(W*0.4, cy-12));
  var cx = W/2;
  var sw = Math.max(6, r*0.22);
  var fs = Math.max(14, Math.min(30, r*0.42));

  var track = se('path');
  sa(track,{ d:arcPath(cx,cy,r,180,0), fill:'none', stroke:'var(--line)', 'stroke-width':sw, 'stroke-linecap':'round' });
  svg.appendChild(track);

  var valDeg = 180-(cur/100)*180;
  var val = se('path');
  sa(val,{ d:arcPath(cx,cy,r,180,valDeg), fill:'none', stroke:'var(--accent)', 'stroke-width':sw, 'stroke-linecap':'round' });
  svg.appendChild(val);

  var tip = arcPoint(cx,cy,r*0.72,valDeg);
  var needle = se('line');
  sa(needle,{ x1:cx, y1:cy, x2:tip.x, y2:tip.y, stroke:'var(--fg)', 'stroke-width':2.4, 'stroke-linecap':'round' });
  svg.appendChild(needle);
  var hub = se('circle');
  sa(hub,{ cx:cx, cy:cy, r:3.4, fill:'var(--fg)' });
  svg.appendChild(hub);

  var num = se('text');
  sa(num,{ x:cx, y:cy+fs*1.05, 'text-anchor':'middle', fill:'var(--fg)', 'font-size':fs, 'font-weight':800 });
  num.textContent = Math.round(cur);
  svg.appendChild(num);
  var lbl = se('text');
  sa(lbl,{ x:cx, y:cy+fs*1.05+fs*0.5, 'text-anchor':'middle', fill:'var(--muted)', 'font-size':fs*0.34, 'font-weight':700 });
  lbl.textContent = '서버 사용률 %';
  svg.appendChild(lbl);
}

function animate(target){
  var from = cur;
  var t0 = performance.now();
  function step(t){
    var p = Math.min(1,(t-t0)/900);
    var e = 1-Math.pow(1-p,3);
    cur = from+(target-from)*e;
    draw();
    if (p<1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

new ResizeObserver(function(){ measure(); draw(); }).observe(svg);
measure();
draw();
animate(R(30,88));
setInterval(function(){ animate(R(15,95)); }, 3200);

게이지가 잘 통하는 경우는 딱 하나입니다 — 값 하나에 명확한 목표치나 안전 범위가 있고, 그 범위 안인지 밖인지가 핵심 질문일 때(속도 제한, 서버 사용률, 만족도 점수). 그 외에는 대개 숫자 하나 + 추세(스파크라인)가 차지하는 면적당 정보량이 훨씬 많습니다.

같은 면적에 막대나 스탯 타일을 쓰면 정확한 값·추세·비교까지 다 들어가는데, 게이지는 오직 "지금 위치"만 보여줍니다. 그래서 여러 KPI를 한 대시보드에 게이지로 쭉 늘어놓는 건 거의 항상 과합니다 — 정말 계기판처럼 "한눈에 위험 여부만" 봐야 하는 자리에만 아껴 쓰세요.

색을 구간별로 나눠 칠할 거라면(안전/주의/위험) 그 경계값을 라벨로 명시하세요. 색 구간만 보고 정확한 임계값을 추측하게 하면 안 됩니다.

언제 쓰나

명확한 목표·안전 범위가 있는 값 하나를, 그 범위 안인지가 핵심 질문일 때만.