스켈레톤 UI를 만들자

메인페이지에 이미지가 많아, 끊기지 않는 UX를 위해 스켈레톤 UI를 만들어, 적용했다.

구현 과정

먼저, 로딩중에 스켈레톤 UI를 렌더링하기 위해 react의 Suspense를 사용했다.

구조는 다음과 같다.

<Suspense fallback={<RootSkeletonComponent />}>
  <RandomThemeListContainer />
  <GeoLocationThemeListContainer />
</Suspense>

우리는, 비동기 상태 툴로 Tanstack Query를 사용하고 있기 때문에, Suspense에서 로딩중임을 감지할 수 있게 하기위해 useSuspenseQuery를 사용하여 메인페이지에서 사용되는 데이터를 불러왔다.

import { useSuspenseQuery } from '@tanstack/react-query';

const useRandomThemesQuery = () => {
  const { data, isSuccess, isError, isLoading } = useSuspenseQuery({
    queryKey: [QUERY_MANAGEMENT['randomThemes'].key],
    queryFn: QUERY_MANAGEMENT['randomThemes'].fn,
  });

  return { data, isSuccess, isError, isLoading };
};

스켈레톤 애니메이션으로 뭘사용하지?

스켈레톤 UI를 사용하는 이유는 더 나은 UX를 위해 메인페이지가 로딩되는 동안 매끄러운 느낌을 주기위해서인데, 이를 위해 도입한 애니메이션이 끊기거나 성능이 안좋으면 안된다고 생각했다.

따라서, GPU에서 작동하고 리플로우가 발생하지 않는 transform을 사용해서 애니메이션을 처리했다.

스켈레톤 컴포넌트의 작은 부분을 들고오면 다음과 같다.

const loadingAnimation = keyframes`
  0% {
    transform: translateX(0);
  }
  50%,
  100% {
    transform: translateX(24rem);
  }
`;

const SkeletonLabel = styled.div([
  tw`rounded-default`,
  css`
    width: 8rem;
    height: 2.8rem;
    background: #f2f2f2;
    position: relative;
    overflow: hidden;
    align-self: flex-start;
    margin-left: 1rem;
    &::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 55px;
      height: 100%;
      background: linear-gradient(to right, #f2f2f2, #ddd, #f2f2f2);
      animation: ${loadingAnimation} 1s infinite linear;
      filter: blur(1px);
    }
  `,
]);