본문으로 건너뛰기

ZSSkeleton & ZSSkeletonBox

로딩 상태를 시각적으로 표현하기 위한 스켈레톤 UI 컴포넌트입니다. ZSSkeleton은 기존 컴포넌트에 shimmer 효과를 적용하는 래퍼 컴포넌트이고, ZSSkeletonBox는 고정 높이의 박스 형태 스켈레톤 컴포넌트입니다.

shimmer 밴드는 @shopify/react-native-skia 캔버스에 그리고 Reanimated 로 반복 이동시킵니다. 두 컴포넌트 모두 peer 로 @shopify/react-native-skia 가 필요합니다.

웹 미지원

shimmer 효과는 iOS·Android 전용입니다. 웹에서는 효과 없이 정적으로 렌더링됩니다 — ZSSkeleton 은 자식을 반투명하게만 표시하고, ZSSkeletonBox 는 배경색 박스만 표시합니다. 아래 미리보기는 웹 fallback 확인용이며, shimmer 속도·밴드·투명도는 example의 실제 iOS·Android 앱에서 확인하세요.

앱 예제 Playgroundv1.0.2
Interactive exampleZSSkeleton 로컬 웹 예제

예제를 선택하면 현재 저장소 소스로 만든 Expo Web 화면을 불러옵니다.

웹 미지원 또는 플랫폼 종속 기능은 실제 iOS·Android 앱에서 확인하세요.

ZSSkeleton

기존 컴포넌트를 감싸서 로딩 상태일 때 shimmer 효과를 적용하는 래퍼 컴포넌트입니다. isFetching prop을 통해 로딩 상태를 제어할 수 있습니다.

기본 사용법

import { ZSSkeleton, ZSText } from '@0610studio/zs-ui';

export default function App() {
return (
<ZSSkeleton isFetching={true}>
<ZSText typo="heading.1">로딩 중인 텍스트</ZSText>
</ZSSkeleton>
);
}

Props

PropTypeDefaultDescription
isFetchingbooleanundefined로딩 상태 여부. true일 때 shimmer 효과가 적용됩니다
styleStyleProp<ViewStyle>undefined컨테이너의 추가 스타일
childrenReact.ReactNodeundefined스켈레톤 효과를 적용할 자식 컴포넌트
overlayColorstring테마 background.baseshimmer 하이라이트 색상
overlayOpacitynumber0.6shimmer 밴드 중심의 최대 불투명도 (0~1)
durationnumber1100shimmer 밴드가 한 번 지나가는 시간 (ms)
...propsViewProps-React Native View의 모든 기본 속성

특징

  • 조건부 렌더링: isFetchingfalse일 때는 자식 컴포넌트를 그대로 렌더링합니다
  • Shimmer 애니메이션: 로딩 중일 때 좌우로 이동하는 shimmer 효과가 적용되고, 자식은 50% 불투명도로 비칩니다
  • 테마 통합: 테마 시스템과 통합되어 기본 색상이 자동으로 적용됩니다
  • 유연한 사용: 어떤 컴포넌트든 감싸서 스켈레톤 효과를 적용할 수 있습니다

ZSSkeletonBox

고정 높이의 박스 형태 스켈레톤 컴포넌트입니다. 항상 shimmer 효과가 적용되며, 다양한 크기와 스타일의 스켈레톤 박스를 만들 수 있습니다.

기본 사용법

import { ZSSkeletonBox } from '@0610studio/zs-ui';

export default function App() {
return (
<ZSSkeletonBox height={100} />
);
}

Props

PropTypeDefaultDescription
heightnumberRequired스켈레톤 박스의 높이
styleStyleProp<ViewStyle>undefined박스의 추가 스타일
overlayColorstring테마 background.layer1shimmer 하이라이트 색상
overlayOpacitynumber0.7shimmer 밴드 중심의 최대 불투명도 (0~1)
durationnumber1100shimmer 밴드가 한 번 지나가는 시간 (ms)
colorViewColorOptions'neutral'박스의 배경색 (테마 팔레트 기반)
...propsViewProps-React Native View의 모든 기본 속성

배경색 옵션 (ViewColorOptions)

테마 팔레트 기반의 배경색을 사용할 수 있습니다:

  • layer1, layer2, neutral, base
  • primary, primary.5, primary.10, ..., primary.100
  • danger, danger.5, ..., danger.100
  • warning, warning.5, ..., warning.100
  • success, success.5, ..., success.100
  • information, information.5, ..., information.100
  • grey.5, grey.10, ..., grey.100

특징

  • 고정 높이: height prop으로 정확한 높이를 지정할 수 있습니다
  • 지속적인 애니메이션: 항상 shimmer 효과가 적용되어 로딩 상태를 명확하게 표현합니다
  • 테마 통합: ZSView를 기반으로 하여 테마 시스템과 완전히 통합됩니다
  • 다양한 형태: style prop을 통해 원형, 둥근 모서리 등 다양한 형태로 커스터마이징 가능합니다

예제

ZSSkeleton 기본 사용

import { ZSSkeleton, ZSText } from '@0610studio/zs-ui';
import { useState } from 'react';

function MyComponent() {
const [isLoading, setIsLoading] = useState(true);

return (
<ZSSkeleton isFetching={isLoading}>
<ZSText typo="heading.1">로딩 중인 제목</ZSText>
</ZSSkeleton>
);
}

ZSSkeleton 다양한 타이포그래피

<ZSSkeleton isFetching={true}>
<ZSText typo="heading.1">큰 제목</ZSText>
</ZSSkeleton>

<ZSSkeleton isFetching={true}>
<ZSText typo="heading.3">중간 제목</ZSText>
</ZSSkeleton>

<ZSSkeleton isFetching={true}>
<ZSText typo="heading.6">작은 제목</ZSText>
</ZSSkeleton>

ZSSkeleton 조건부 렌더링

function DataComponent({ data, isLoading }) {
return (
<ZSSkeleton isFetching={isLoading}>
{data ? (
<ZSText typo="body.2">{data.title}</ZSText>
) : (
<ZSText typo="body.2">데이터 없음</ZSText>
)}
</ZSSkeleton>
);
}

ZSSkeleton 커스텀 오버레이 색상

<ZSSkeleton 
isFetching={true}
overlayColor="#f0f0f0"
>
<ZSText typo="body.2">커스텀 색상 스켈레톤</ZSText>
</ZSSkeleton>

shimmer 속도·세기 조절

{/* 느리고 옅은 shimmer */}
<ZSSkeleton isFetching duration={1800} overlayOpacity={0.35}>
<ZSText typo="body.2">천천히 반짝이는 스켈레톤</ZSText>
</ZSSkeleton>

<ZSSkeletonBox height={80} duration={1800} overlayOpacity={0.4} />

ZSSkeletonBox 기본 사용

import { ZSSkeletonBox } from '@0610studio/zs-ui';

<ZSSkeletonBox height={100} />
<ZSSkeletonBox height={50} />
<ZSSkeletonBox height={200} />

ZSSkeletonBox 다양한 형태

{/* 직사각형 박스 */}
<ZSSkeletonBox height={100} style={{ borderRadius: 10 }} />

{/* 원형 박스 */}
<ZSSkeletonBox height={80} style={{ borderRadius: 40, width: 80 }} />

{/* 둥근 모서리 박스 */}
<ZSSkeletonBox height={50} style={{ borderRadius: 25 }} />

ZSSkeletonBox 배경색 변경

<ZSSkeletonBox height={100} color="layer1" />
<ZSSkeletonBox height={100} color="primary.10" />
<ZSSkeletonBox height={100} color="grey.20" />

ZSSkeletonBox 커스텀 스타일

<ZSSkeletonBox 
height={120}
style={{
borderRadius: 15,
marginVertical: 10,
width: '90%'
}}
/>

실제 사용 예제

import { ZSSkeleton, ZSSkeletonBox, ZSText, ZSView } from '@0610studio/zs-ui';

function CardSkeleton({ isLoading }) {
return (
<ZSView style={{ padding: 20, borderRadius: 10 }}>
<ZSSkeleton isFetching={isLoading}>
<ZSText typo="heading.2">카드 제목</ZSText>
</ZSSkeleton>

<ZSSkeletonBox
height={150}
style={{ borderRadius: 8, marginTop: 10 }}
/>

<ZSSkeleton isFetching={isLoading}>
<ZSText typo="body.2" style={{ marginTop: 10 }}>
카드 설명 텍스트
</ZSText>
</ZSSkeleton>
</ZSView>
);
}

리스트 아이템 스켈레톤

function ListItemSkeleton() {
return (
<ZSView style={{ flexDirection: 'row', padding: 15 }}>
<ZSSkeletonBox
height={60}
style={{ borderRadius: 30, width: 60 }}
/>
<ZSView style={{ marginLeft: 15, flex: 1 }}>
<ZSSkeletonBox height={20} style={{ borderRadius: 4, marginBottom: 8 }} />
<ZSSkeletonBox height={16} style={{ borderRadius: 4, width: '70%' }} />
</ZSView>
</ZSView>
);
}