본문으로 건너뛰기
표시

복합 스탯 카드

지표 하나를 값, 앞 기간 대비, 흐름 차트, 하위 지표 줄까지 한 장에 담는 표시 카드다. StatCard 가 지금 값을 보여 준다면 이 카드는 그 값이 어떻게 나왔는지까지 보여 준다. 머리는 StatCard 와 같은 조각이라 두 카드를 한 화면에 섞어도 값 크기와 색이 같다. 차트 종류, 기간, 하위 지표 계산은 호출자가 정하고 카드는 넘겨받은 것을 세 칸에 배치한다.

상태

이번 주 주문
+12%
1284건지난주 1,146건

최근 8주

취소
31건
객단가
3.3만
재구매
41%
기본
이번 주 주문

최근 8주

취소
객단가
재구매
loading
점검 기록 · 30일
0회앞 30일 0회

최근 14일

이 기간에는 기록이 없다. 마지막 기록 9월 2일

대상
5곳
기록한 날
0 / 30일
마지막
9월 2일
차트 비어 있음
활성 사용자
3820명
최근 30일 안에 로그인한 사용자
신규
214명
복귀
87명
차트 없이 지표만

차트 칸은 chart 로 정한다. 넘기지 않으면 칸이 없고, null · false 를 넘기면 칸을 남긴 채 chartEmpty 문구를 chartHeight 높이로 보인다. 그래서 격자 안에서 데이터가 빈 카드도 옆 카드와 높이가 맞는다.

활용 방법

축 있는 차트

차트 칸에는 deux 차트를 그대로 넣는다. 막대에 마우스를 올리면 그날 값이 툴팁으로 뜬다. 넓은 카드는 value, 24rem 보다 좁은 카드는 compactValue 를 보인다.

Preview
이번 달 매출
+9%
₩42,800,0004,280만지난달 같은 날 3,910만

최근 14일 · 날마다 매출

주문
1,284건
객단가
3.3만
환불
12건
tsx
<CompositeStatCard
icon={<Wallet size={16} />}
tone="accent"
label="이번 달 매출"
valuePrefix="₩"
value="42,800,000"
compactValue="4,280만"
comparison="지난달 같은 날 3,910만"
trend={{ direction: "up", label: "+9%" }}
chartLabel="최근 14일 · 날마다 매출"
chart={<BarChart categories={days} series={[{ label: "매출", values: sales, tone: "primary" }]} height={120} />}
chartHeight={120}
metrics={[
{ label: "주문", value: "1,284", valueSuffix: "건" },
{ label: "객단가", value: "3.3만" },
{ label: "환불", value: 12, valueSuffix: "건", tone: "warning" },
]}
/>

내려가야 좋은 지표

trend.tone 이 배지 색을 정한다. 응답 시간처럼 내려가야 좋은 지표는 down 에 success 를, 비용처럼 올라가면 나쁜 지표는 up 에 danger 를 준다. 기준선은 차트의 references 가 그린다.

Preview
응답 시간 p95
-7%
182ms지난주 196ms

최근 8주 · 기준 250ms

오류율
0.4%
요청
1.2M
느린 요청
38건
이번 달 서버 비용
+19%
₩1,920,000192만지난달 같은 날 161만

최근 8주

연산
121만
저장
48만
전송
23만
tsx
<CompositeStatCard
label="응답 시간 p95"
value={182}
valueSuffix="ms"
trend={{ direction: "down", label: "-7%", tone: "success" }}
chart={<LineChart series={[{ label: "p95", points: p95 }]} labels={weeks} height={120} references={[{ value: 250, label: "기준" }]} />}
chartHeight={120}
metrics={[...]}
/>
<CompositeStatCard
label="이번 달 서버 비용"
value="1,920,000"
trend={{ direction: "up", label: "+19%", tone: "danger" }}
...
/>

분포 띠와 지표 링크

차트 칸에 DistBar 를 넣으면 전체가 어떤 상태로 나뉘는지 보인다. metrics[].href 를 준 칸은 값이 링크가 되어, 눌러서 그 조건으로 걸러진 목록으로 간다.

Preview
처리 대기 문의
48건담당자 6명

답변 기한까지

기한 지남
5건
담당 없음
3건
tsx
<CompositeStatCard
icon={<Inbox size={16} />}
tone="danger"
label="처리 대기 문의"
value={48}
valueSuffix="건"
chartLabel="답변 기한까지"
chart={<DistBar segments={segments} legend height={12} />}
metrics={[
{ label: "기한 지남", value: 5, valueSuffix: "건", tone: "danger", href: "?due=over" },
{ label: "오늘", value: 7, valueSuffix: "건", tone: "warning", href: "?due=today" },
{ label: "담당 없음", value: 3, valueSuffix: "건", href: "?assignee=none" },
]}
/>

비율과 게이지

한도가 정해진 지표는 큰 값에 쓴 양을, Gauge 에 한도 대비 비율을 둔다. 같은 숫자를 두 번 쓰지 않는다. 하위 지표가 둘이면 두 칸으로 나뉜다.

Preview
저장 공간
720GB한도 1TB
남음
280GB
이번 달 증가
42GB
tsx
<CompositeStatCard
icon={<GaugeIcon size={16} />}
tone="warning"
label="저장 공간"
value={720}
valueSuffix="GB"
comparison="한도 1TB"
chart={<Gauge percent={72} label="사용" tone="warning" size={150} />}
metrics={[
{ label: "남음", value: 280, valueSuffix: "GB" },
{ label: "이번 달 증가", value: 42, valueSuffix: "GB", tone: "warning" },
]}
/>

링크는 머리만

href 를 주면 머리(라벨과 값)만 링크가 되고 라벨 옆에 화살표가 붙는다. 카드 전체를 링크로 감싸지 않는 것은 차트 칸과 지표 칸에도 링크가 들어가기 때문이다. 링크 안의 링크는 HTML 에서 허용되지 않는다.

tsx
<CompositeStatCard
label="이번 주 주문"
value={1284}
valueSuffix="건"
href="?view=orders"
chart={<Sparkline points={weekly} width={280} height={40} />}
metrics={[
{ label: "취소", value: 31, valueSuffix: "건", href: "?status=canceled" },
{ label: "환불", value: 12, valueSuffix: "건", href: "?status=refunded" },
]}
/>

지표 네 칸

하위 지표는 네 칸까지 그리고 다섯 번째부터는 그리지 않는다. 칸 수에 따라 열이 정해지고 카드 폭으로 접힌다. 4칸은 20rem 보다 좁으면 2 × 2, 3칸은 16rem 보다 좁으면 한 줄에 하나다. 전체 화면(⤢)으로 열면 4칸이 한 줄로 선다.

Preview
이번 달 가입
642명
웹
388
앱
201
초대
41
기타
12
tsx
<CompositeStatCard
label="이번 달 가입"
value={642}
valueSuffix="명"
metrics={[
{ label: "웹", value: 388 },
{ label: "앱", value: 201 },
{ label: "초대", value: 41 },
{ label: "기타", value: 12 },
]}
/>

격자에 늘어놓기

카드는 배치를 맡지 않는다. 격자는 호출자가 짠다. 차트 칸이 남는 높이를 채우므로 한 줄의 카드는 하위 지표 줄이 같은 높이에 맞춰진다.

Preview
이번 주 주문
+12%
1284건

최근 14일

취소
31건
객단가
3.3만
재구매
41%
점검 기록 · 30일
0회

최근 14일

이 기간에는 기록이 없다

대상
5곳
기록한 날
0 / 30일
마지막
9월 2일
tsx
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
<CompositeStatCard ... chart={<BarChart ... height={100} />} chartHeight={100} />
<CompositeStatCard ... chart={hasData && <BarChart ... height={100} />} chartHeight={100} chartEmpty="이 기간에는 기록이 없다" />
</div>

언제 쓰지 않나

  • · 값 하나와 추세만 보이면 된다 → StatCard. 작은 스파크라인까지는 StatCard 의 chart 슬롯으로 충분하다.
  • · 회사 · 서비스 같은 대상 하나에 지표가 여럿 붙는다 → DataStatCard.
  • · 차트가 주인공이고 값은 제목 옆 보조다 → Panel 안에 차트를 둔다.
  • · 지표 여러 개를 행 · 열로 견준다 → DataTable · DataGrid.

속성

이름타입기본값설명
labelstring-카드 정체성 텍스트
valuenumber | string | undefined-큰 값. undefined면 '-'
iconReactNode-없으면 아이콘 자리를 그리지 않는다
tone"neutral" | "accent" | "success" | "warning" | "danger""neutral"아이콘 박스 의미색
valuePrefixstring-값 앞 단위 (₩, $ 등)
valueSuffixstring-값 뒤 단위 (건, % 등)
compactValuenumber | string-머리 폭 24rem 미만에서 value 대신 보일 짧은 값
sign"positive" | "negative"-값 색·부호: positive=green "+", negative=red "-"
comparisonstring-값 옆 비교 문구
descriptionstring-값 아래 보조 설명
trend{ direction: "up" | "down"; label: string; tone?: "success" | "danger" | "neutral" }-추세 배지. tone이 없으면 up=success, down=danger
chartReactNode-차트 칸. undefined면 칸 없음, null · false면 chartEmpty를 보인다
chartLabelstring-차트 칸 위 작은 제목
chartEmptyReactNode"데이터 없음"차트가 비었을 때 문구
chartHeightnumber96비었을 때 · 로딩 때 차트 칸 높이(px). 넘기는 차트의 height와 맞춘다
metrics{ label: string; value: number | string | undefined; valueSuffix?: string; tone?: "success" | "warning" | "danger"; href?: string }[]-하위 지표 1~4칸. 4칸은 좁은 카드에서 2 × 2
hrefstring-머리를 링크로. onClick보다 우선
onClickMouseEventHandler-머리를 버튼으로(href 없을 때)
loadingbooleanfalse값 · 차트 칸 · 지표 값 자리를 pulse로
classNamestring-root(div)에 붙는다. native props와 ref도 root로 간다