SUIN

[React] 타입스트립트로 스켈레톤 구현하기 본문

React

[React] 타입스트립트로 스켈레톤 구현하기

choi suin 2022. 2. 10. 16:53
728x90

스켈레톤이란 

웹페이지에서 로드 시간이 짧아 보이게 하는 몇 가지 방법 중 하나가 스켈레톤이다 

데이터가 로드되기 전에 컨텐츠의 자리비움을 표시하여 사용자가 기다리는 시간을 덜 지루하게 하는 ui이다 

 

장점 

- 실제 테스트 결과 블랭크 페이지 < 스피너 < 스켈레톤 순서대로 더 빠르다고 느끼게 된다.

 

단점 

- 스켈레톤을 화면마다 표시해야하기때문에 시간이나 비용이 많이 든다. 

 

실생활에서 볼수있는 스켈레톤 예시 

 

스켈레톤 규칙

  • 스켈레톤은 콘텐츠의 로드를 방해하면 안된다!
    실제 콘텐츠는 데이터를 가용할 수 있는 시점이 되면 즉시 스켈레톤을 대체해야함
  • 디자인 할 때 애니메이션을 사용하는 것이 좋다
    애니메이션은 wave, pulse 중 wave 를 사용하는 것이 로딩 시간을 더 짧게 느끼게끔 한다.
  • 느리고 안정적인 애니메이션을 사용하는 것이 로딩 시간을 더 짧게 느끼게끔 한다

 

 


타입 스크립트로 스켈레톤 만들어보기 

스켈레톤 컴포넌트 

interface Props {
  width?:number;
  height?:number;
  circle?:boolean;  //원형 스켈레톤
  rounded?:boolean; //모서리 
  unit?:string;     //px or % 단위 
  animation?:boolean; //애니메이션 유무
  color?:string;      
  style?:React.CSSProperties; //스켈레톤의 추가적인 스타일객체
}

//pulseKeyframe
const pulseKeyframe = keyframes`
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0.4;
  }
  100% {
    opacity: 1;
  }
`;
//pulseAnimation
const pulseAnimation = css`
  animation: ${pulseKeyframe} 1.5s ease-in-out 0.5s infinite;
`;

const Base = styled.span<Props>`
  ${({color}) => color && `background-color: ${color}`};
  ${({rounded}) => rounded && `border-radius: 8px`};
  ${({circle}) => circle && `border-radius: 50%`};
  ${({width, height}) => (width || height) && `display: block`};
  ${({animation}) => animation && pulseAnimation};
  width : ${({width, unit}) => width && unit && `${width}${unit}`};
  height : ${({height, unit}) => height && unit && `${height}${unit}`};
`;
const Content = styled.span`
  opacity: 0;
`;


const Skeleton: React.FC<Props> = ({
  animation = true,
  children,
  width,
  height,
  circle,
  rounded,
  unit = 'px',
  color = '#F4F4F4',
  style,
}) => {

  const styles ={
    style:style,
    rounded:rounded,
    circle:circle,
    width:width,
    height:height,
    animation:animation,
    unit:unit,
    color:color,
  }

  return (
    <Base {...styles}>
      <Content></Content>
    </Base>
  );
};

 

App.tsx

function App() { 
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => { 
    // 임의로 로딩 상태 표현
    setTimeout(() => setLoading(false), 3000);
  }, []);

  return (
    <Base>
      {
        loading ?  Array.from({ length: 25 }).map((_, idx) => (
          <Placeholder key={idx} />
        )) : Array.from({ length: 25 }).map((_, idx) => (
          <Item key={idx} />
        ))
      }
    </Base>
  );
}
// 스켈레톤 UI
const Placeholder: React.FC = () => ( // <Item /> 에 대응하는 Placeholder 제작
  <Container>
    <ImageWrapper>
      <Skeleton  height={220} />
    </ImageWrapper>
    <Info>
      {/* Title */}
      <Skeleton width={150} height={29} rounded />  
      <div style={{ height: '8px' }} />
      {/* Description */}
      <Skeleton width={200} height={19} rounded />
    </Info>
  </Container>
)

 

const Item: React.FC = () => ( // 실제 보여줄 컨텐츠
  <Container>
    <ImageWrapper>
      {/* 더미데이터  */}
      <Image src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSVqiN6h4EBIKut_XAn4KxgJKNNrY8LcFZq7Q&usqp=CAU" />
    </ImageWrapper>
      <Info>
        <Title>제목입니다.</Title>
        <Description>신이난다 신이나~신이난다 신이나~</Description>
      </Info>
  </Container>
)

 

구현화면 

 

 

GitHub - whl5105/React_Study

Contribute to whl5105/React_Study development by creating an account on GitHub.

github.com