[UI/UX] Skeleton UI 적용기

tech-hoon·2022년 2월 1일
9
post-thumbnail

따분한 로딩창 말고, 사용자 친화적인 Skeleton UI로 사용자 이탈율을 줄여보자.

1. Skeleton UI란?

스켈레톤 UI는 실제 데이터가 렌더링 되기 전에 보이게 될 화면의 윤곽을 먼저 그려주는 로딩 애니메이션이다.
사용자의 이탈을 막고, ‘어떤 것들이 보여질 것이다’라고 미리 알려주는 효과를 준다.
기존 Spinner에 비해 훨씬 사용자 친화적이고, 사용자 이탈율도 실제로 적다고 한다.

2. 구현

Skeleton Item

먼저 Skeleton UI를 적용할 컨텐츠에 공통적으로 사용되는 Item을 정의한다.
CSS animation을 이용하여, 일정한 시간동안, 배경색의 투명도가 반복적으로 바뀌도록 한다.

import styled from 'styled-components';

const SkeletonItem = styled.div`
  width: 100%;
  height: 30px;
  background-color: #f2f2f2;
  position: relative;
  overflow: hidden;
  border-radius: 4px;

  @keyframes skeleton-gradient {
    0% {
      background-color: rgba(165, 165, 165, 0.1);
    }
    50% {
      background-color: rgba(165, 165, 165, 0.3);
    }
    100% {
      background-color: rgba(165, 165, 165, 0.1);
    }
  }

  &:before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    animation: skeleton-gradient 1.5s infinite ease-in-out;
  }
`;

export default SkeletonItem;

Card Skeleton

앞서 정의한 Skeleton Item을 활용하여, 게시물 카드의 Skeleton UI를 구현한다.

import styled from 'styled-components';
import SkeletonItem from './components/SkeletonItem';

interface Props {}

const CardSkeleton = (props: Props) => {
  return (
    <Wrapper>
      {new Array(6).fill('').map((_, i) => (
        <Card key={i}>
          <Title />
          <Content />
          <Bottom>
            <Circle />
            <Title />
          </Bottom>
        </Card>
      ))}
    </Wrapper>
  );
};

const Wrapper = styled.ul`
  ...
`;

const Card = styled.li`
  ...
`;

const Title = styled(SkeletonItem)``;

const Content = styled(SkeletonItem)`
  height: 130px;
`;

const Bottom = styled.div`
  display: flex;
  gap: 12px;
`;

const Circle = styled(SkeletonItem)`
  width: 35px;
  height: 30px;
  border-radius: 50%;
  background-color: #f2f2f2;
  position: relative;
  overflow: hidden;
`;

export default CardSkeleton;

기존 컴포넌트의 Layout 재사용

Skeleton UI는 적용하고자 할 컴포넌트와 가능한 비슷한 것이 좋은 사용자 경험을 제공할 수 있다. 그런데 만약, 기존 컴포넌트의 디자인이 수정된다면, 매번 Skeleton UI를 수정해야한다.

이를 개선하기 위해 PostCard와 공통적으로 사용하는 레이아웃을 모듈화한 Layouts을 import하여 필요한 컴포넌트를 재사용하는 방식으로 리팩토링하였다.

import styled from 'styled-components';
import SkeletonItem from './components/SkeletonItem';
import { Layouts as S } from 'components/common/PostCard/Layouts';

interface Props {}

const PostCardSkeleton = (props: Props) => {
  return (
    <S.Wrapper>
      {new Array(6).fill('').map((_, i) => (
        <CardSkeleton key={i}>
          <Title />
          <Content />
          <Bottom>
            <Circle />
            <Title />
          </Bottom>
        </CardSkeleton>
      ))}
    </S.Wrapper>
  );
};

const CardSkeleton = styled(S.PostCard)`
  gap: 20px;
  cursor: default;
`;

const Title = styled(SkeletonItem)``;

const Content = styled(SkeletonItem)`
  height: 130px;
`;

const Bottom = styled.div`
  display: flex;
  gap: 12px;
`;

const Circle = styled(SkeletonItem)`
  width: 35px;
  height: 30px;
  border-radius: 50%;
  background-color: #f2f2f2;
  position: relative;
  overflow: hidden;
`;

export default PostCardSkeleton;

3. 결과

렌더링 될 컨텐츠를 미리 그려줌으로써, 사용자는 현재 로딩중임을 인지하고 로딩 시간을 보다 친화적으로 받아들일 수 있을 것이다.

참고

https://ui.toast.com/weekly-pick/ko_20201110

profile
제 삽질을 공유합니다.

1개의 댓글

comment-user-thumbnail
2022년 12월 3일

잘보고갑니다!

답글 달기