Skeleton UI 적용

jiseong·2022년 3월 7일
0

T I Learned

목록 보기
189/291

현재는 아래와 같이 데이터를 가져오는 동안 결과에 대해 빈 화면만을 보여주고 있다.

api를 천천히 불러오고 싶을 때, 개발자도구의 Network 탭을 사용하면 유용하다.

테스트하고 있는 환경에서는 네트워크 속도가 좋기 때문에 크게 불편함이 없겠지만 속도가 느린 환경에서는 화면에 콘텐츠가 보이기까지 긴 시간이 걸릴 것이다. 그래서 그 사이에 빈 화면을 보여주기보다는 컨텐츠가 로딩중임을 표현해주는 Skeleton UI를 적용하여 빈 공백을 없애보려고 한다.

우선, 카드 컴포넌트에 대해서 Skeleton으로 표현해줄 목적이기 때문에 최대한 카드 컴포넌트와 비슷한 형식의 스타일을 만들어주었다.

Element 요소에 애니메이션 효과를 줄려고하기 때문에 카드 컴포넌트 내부에 있는 아이템들에 대해 Element를 재활용하였다.

const Element = css`
  background: ${({ theme }) => theme.skeletonBg};
`;

const Card = styled.div`
  width: 250px;
  padding: 10px;
  border-radius: 15px;

  background: ${({ theme }) => theme.cardBg};
  border: ${({ theme }) => `1px solid ${theme.border}`};
`;

const Thumnail = styled.div`
  width: 100%;
  height: 200px;
  ${Element}
`;

const Title = styled.p`
  margin: 7px 0;
  width: 150px;
  height: 10px;
  ${Element}
`;

const SubTitle = styled.p`
  margin: 5px 0;
  width: 50px;
  height: 10px;
  ${Element}
`;

function Skeleton() {
  return (
    <Card>
      <Thumnail />
      <Title />
      <SubTitle />
    </Card>
  );
}

이제 평소에 보던 loading 효과를 주면 되는데 깜빡이는 효과를 주는 animation을 무한 반복해주기만 하면 된다. 내가 사용한 방식은 카드 컴포넌트 내부에 있는 아이템들에 대해 가상요소를 생성하여 좌측에서 우측으로 무한반복시킨 방식이였다. 코드는 다음과 같다.

const loading = keyframes`
  0% {
    transform: translateX(-150%);
  }
  100% {
    transform: translateX(150%);
  }
`;

const Element = css`
  position: relative;
  background: ${({ theme }) => theme.skeletonBg};

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: ${({ theme }) => theme.skeletonLoading};
    animation: ${loading} 1.5s infinite;
  }
`;

하지만 이렇게만 작성해주면 불필요한 영역까지 애니메이션 효과가 보여지기 때문에 oveflow: hidden 옵션을 추가적으로 작성해주어야 한다.

const Element = css`
  overflow: hidden;
  // 생략...
`;

완성

데이터를 가져오는동안 보여주기 위해 isLoading과 같은 상태를 추가적으로 관리해야 했지만 react-query를 사용하고 있어서 쉽게 적용이 가능했다. 👍

const { data, isLoading } = useGetPlaylist({
    query: keyword,
    errorHandler: (message) =>
      toast({ title: '', message, type: 'error', duration: 5000 }),
});

0개의 댓글