로딩 스켈레톤·에러·빈 상태·스피너 등 비동기 상태 표현 4종입니다.
실제 레이아웃 구조를 그대로 미러링하는 페이지 스켈레톤입니다.
<div className="space-y-1">
<Skeleton className="h-8 w-40" />
<Skeleton className="h-4 w-64" />
</div>
<div className="grid grid-cols-3 gap-1.5 md:grid-cols-5">
{Array.from({ length: 5 }).map((_, i) => (
<Card key={i}>
<CardContent className="space-y-1 px-2 py-2">
<Skeleton className="h-3 w-10" />
<Skeleton className="h-5 w-16" />
</CardContent>
</Card>
))}
</div>Alert 컴포넌트로 표현하는 페이지/인라인 에러입니다.
페이지 단위 에러 (재시도 액션 포함)
인라인 에러 (폼 필드)
<Alert variant="destructive">
<WarningCircleIcon size={16} />
<AlertTitle>서버에 연결할 수 없습니다</AlertTitle>
<AlertDescription>잠시 후 다시 시도해 주세요.</AlertDescription>
<AlertAction>
<Button size="sm" variant="outline">다시 시도</Button>
</AlertAction>
</Alert>아이콘 + 안내 메시지 + 선택적 액션으로 구성하는 빈 상태입니다.
<Empty className="border">
<EmptyHeader>
<EmptyMedia variant="icon">
<TrayIcon size={20} />
</EmptyMedia>
<EmptyTitle>아직 종목이 없습니다</EmptyTitle>
<EmptyDescription>관심 종목을 추가하면 이 목록에 표시됩니다.</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button size="sm">종목 추가</Button>
</EmptyContent>
</Empty>인라인 텍스트, 버튼 내부 등 짧은 대기 상태에 쓰는 스피너입니다.
<Spinner />
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Spinner className="size-4" />
목록을 불러오는 중입니다…
</div>
<Button disabled size="sm">
<Spinner className="size-4" />
저장 중
</Button>