Skeleton UI 적용해보기

pds·2023년 8월 16일

Skeleton UI에 대해 공부하고 적용해보았던 경험을 기록했다!


Skeleton

Skeleton UI는 웹 또는 모바일 애플리케이션에서 사용자 경험을 개선하기 위한 디자인 패턴 중 하나로 Skeleton UI는 컨텐츠가 로딩되는 동안 사용자에게 컨텐츠의 예상 배치 및 레이아웃을 보여주는 시각적인 효과


Skeleton의 장점

정보 전달

어떤 컨텐츠를 보여주고자 할 때 사이즈가 크거나 응답 지연으로 인해 즉시 보여지지 않는 상황이라면 아무것도 보여지지 않을 것이다.

사용자는 오류라고 생각할 수도 있고 답답하다고 생각할 수 있어 이탈할 수도 있다고 한다.

현재 당신이 하신 요청에 대해 응답할 수 있도록 노력하고 있습니다 라는 정보를 보여주는게 아닐까 생각한다.

구조와 일관성

단순한 로딩스피너를 통해 노력하고 있음을 보여줄 수도 있지만 응답하기까지 오래걸린다면 어떤 컨텐츠가 보여질지 예상할 수 없을 뿐 아니라

실제 보여져야할 컨텐츠와 구조가 불일치하기 때문에 어떤 상황에서는 부적절할 수 있다.

실제 보여져야할 컨텐츠와 최대한 유사한 스켈레톤 UI를 사전에 보여주다가 실제 컨텐츠를 연속해서 보여줘 레이아웃을 유지해 시각적인 지속성을 통해 사용자 경험을 개선할 수 있을 것이다.

역시 이런 컨텐츠를 보여줄 예정입니다 라는 것을 인지하게 해주는 또다른 정보 제공의 수단이 되지 않을까 생각한다.


Skeleton의 단점

복잡도와 오버헤드

스켈레톤을 위한 디자인도 고려해야하고 추가적인 개발, 관리가 필요해지기 때문에 프로젝트 복잡도와 비용이 증가할 것이다.

로딩스피너에 비해 추가적으로 많은 스타일, 애니메이션, 엘리먼트를 구성하기 때문에 이 자체로 페이지의 사이즈가 커질 수 있다.

산만함에 대한 가능성

실제 UI와 너무 갭이 크다면 큰 레이아웃 이동이 일어나고 로딩스피너만도 못할 것이다.

스켈레톤은 가능한 한 콘텐츠와 같아야 한다.

실제 UI와 같아야 하기 때문에 결국 관리의 복잡도와 개발의 어려움이 있는게 아닌가 생각한다.

그리고 무조건 스켈레톤 화면을 보여주는게 사용자 경험에 도움이 될까요?를 읽고 얻은 정보와 경험을 바탕으로 정리해보면

오히려 컨텐츠를 불러오는데 짧은 시간이 걸리는 경우에 대해서는 스켈레톤 UI가 아주 잠깐 반짝거렸다가 실제 컨텐츠가 보이기 때문에 오히려 적용하지 않음을 고려해보는 것도 좋을 것 같다.


적용해보게 된 이유

차트와 워드클라우드를 동시에 여러개 띄운다던가 Quill 에디터를 로드하는데 이 모듈들은 사이즈가 꽤 크고 클라이언트 사이드에서 해당 페이지에서 필요해지는 시점에 로드하기 때문에 브라우저에 캐시되지 않았을 경우 네트워크 요청을 하여 다운 받기 까지 지연이 발생할 수 있고 사용자 환경에 따라 코드를 해석해 사용되기까지도 오래걸릴 가능성이 있다.

next bundle-analyzer를 통해 확인한 사이즈로 우측 상단의 번들은 wordcloud 라이브러리이다.

차트를 로드하는 대시보드 UI의 경우 사용해보니 네트워크가 조금 좋지 않은 환경에서는 체감이 될 정도로 보여지기까지 오래걸려서 로딩스피너를 오래 보고있어야 했다.

해당 DashBoard 컴포넌트 자체를 코드스플리팅 해서 무엇이 보여질지도 예상이 되지 않는다.


Skeleton 컴포넌트 만들기

여기를 참고하여 만들어보았다.

const WaveAnimation = keyframes`
  0% {
    transform: translateX(-100%);
  }
  50%, 100% {
    transform: translateX(100%);
  }
`;

const SkeletonWrapper = styled.div`
  position: relative;
  /* overflow: hidden; */
  width: 100%;
  height: auto;
  .skeleton-item::before {
    position: absolute;
    content: '';
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
    animation: ${WaveAnimation} 1.6s linear 0.5s infinite;
    background: linear-gradient(90deg, transparent, #ddd, transparent);
    transform: translateX(-100%);
  }
`;

const SkeletonContentWrapper = styled.div`
  overflow: hidden;
  position: relative;
  background: var(--color-lightgray);
  color: var(--color-lightgray);
`;

interface SkeletonProps {
  style?: CSSProp;
}

const Content = ({ style }: SkeletonProps) => (
  <SkeletonContentWrapper css={style} className="skeleton-item">
    &nbsp;
  </SkeletonContentWrapper>
);

const Skeleton = ({ children, style }: PropsWithChildren<SkeletonProps>) => (
  <SkeletonWrapper css={style}>{children}</SkeletonWrapper>
);

Skeleton.Content = Content;
export default Skeleton;

적용할 Wrapper 컴포넌트를 만들고 Content 합성 컴포넌트를 사용해 원하는 항목에 스켈레톤 처리를 할 수 있게 의도했다.

<Skeleton>
  <div style={{ display: 'flex', flexDirection: 'column', rowGap: '16px' }}>
    <Skeleton.Content style={{ height: '20px' }} />
    <Skeleton.Content style={{ height: '10px', width: '80%' }} />
    <Skeleton.Content style={{ height: '10px', width: '80%' }} />
  </div>
</Skeleton>


적용해보기

못생긴 감이 있지만 차트가 식별될 것이라는 예상 정도는 할 수 있게 되었고

불필요하게 통으로 모두 로드되기까지 지연시키지 않고 대시보드 Card는 그대로 유지하면서 내부의 차트만 스켈레톤 처리를 했다.

로드까지 걸리는 시간이 길어질 경우 더 이상 타이머 없는 신호등을 보고있는 느낌은 받지 않을 것이라고 생각한다!

근데 길게보니까 좀 많이 못생겼다

기존에는 대시보드 전체를 코드 스플리팅하여 내부의 모든 것들이 로드가 완료되기를 기다려야하는 형태였다면

이제는 큰 차이는 없지만 로드가 완료되는 UI가 먼저 보여질 수 있다.

const DonutChart = dynamic(() => import('@/components/@common/molecules/Chart/DonutChart'), {
  ssr: false,
  loading: () => <ChartSkeleton minHeights={['200px', '180px', '140px']} type="rounded" />,
});
const GroupBarChart = dynamic(() => import('@/components/@common/molecules/Chart/GroupBarChart'), {
  ssr: false,
  loading: () => <ChartSkeleton minHeights={['224px', '180px', '240px']} />,
});

한 종류의 차트 스켈레톤으로 두가지 타입 프로퍼티만으로 구분해 4가지 상황을 처리하고 있고 못생겨서 그렇지 각각 특성에 맞게 스켈레톤 UI를 구성하면 보여지는 그 자체로 유의미한 경험을 줄 수 있지 않을까 생각한다.


이런 무한스크롤 로드에서 스켈레톤 UI는 더 유용한 것 같다.

api 응답이 지연될 경우에 다음 데이터를 로드중이라는 정보를 제공해줄 수 있을 뿐만 아니라

연속되는 아이템이 끊김 없이 연속적으로 보여질 수 있는 부분이 로드 스피너와 비교했을 때 큰 장점으로 작용하는 것 같다


지연처리하기

에디터와 차트 모두 사이즈가 큰 모듈이고 처음에 보여질 때 어느정도 시간이 필요하지만

한 번 다운로드 받고 브라우저 메모리에 캐시되었을 경우에는 유의미한 지연시간이 발생하지 않을 것이다.


처음 로드했을 때(3g)

캐시되었을 때(3g)

네트워크 환경와 상관없이 브라우저에 캐시되면 아주 짧은 시간동안만 스켈레톤이 보여진다.

정확하지는 않겠지만 카페 와이파이 기준 처음 로드할 때는 150ms 내외이고, 캐시되었을 경우는 위와 동일했다.

기기 스펙에 따라 자바스크립트 코드 실행에 걸리는 시간 차이가 있겠지만 나의 환경 기준으로 에디터를 사용할 수 있기까지 80ms 정도면 충분했다.

초기에 접근하여 네트워크 요청을 하고 다운받아 사용하기 까지 오래걸리는 환경에서 스켈레톤을 보여주고자 하는 의도였기 때문에 짧은 로드시간에 대해서는 스켈레톤 UI가 보여지지 않도록 지연 처리를 해주었다.

  {
    loading: () => (
      <DeferredComponent delay={150} keepLayout>
        <EditorSkeleton />
      </DeferredComponent>
    ),
    ssr: false,
  },


느낀점

Skeleton UI자체를 구현하는 것도 쉽지 않고 특정 상황에 Skeleton이 정말 필요하고 적합한지 고민하고 판단해 적용하는 것 또한 필요한 것 같아 더더욱 어렵다고 느껴졌다.

사용자가 없는 토이프로젝트이지만 프론트엔드 개발자들이 사용자 경험을 위해 얼마나 많은 노력과 고민을 하는지 공부하고 적용해 흉내 내볼 수 있는 계기가 되어 좋았다.


References

0개의 댓글