IT/React

[React] Skeleton UI 구성하기

HJ:: 2023. 9. 11. 17:21

사용 이유

컨텐츠를 불러오는 동안 사용자로 하여금 지루함을 느끼게 하지 않고, 데이터를
불러오는 중임을 나타내주기 위해 빈 UI를 구성해서 불러오는 동안 틀에 맞춰 띄워준다.

 

 

 

미리보기

 

 

 

 

 

 

 

제품 유무별 상태 관리

 // PostCards.tsx
 
 const [_, setIsLoading] = useAtom(isLoadingAtom);
 
 // 데이터 유무에 따른 전역 상태 관리(Main.tsx- skeleton UI)
  useEffect(() => {
    if (posts) setIsLoading(false);
  }, [posts]);

  useEffect(() => {
    if (!posts) setIsLoading(true);
  }, [posts]);

- react-redux와 같은 기능인 jotai라이브러리를 사용해서 전역 상태 관리 처리를 해줬습니다.

- post 데이터가 없을 때는 Loading을 true로 기본값 설정해주고 posts를 가져오게 되면 false로

isLoading을 false상태로 해주었습니다.

 

 

// Main.tsx

export const isLoadingAtom = atom<Boolean>(true);

...
  <>
      {/* 데이터 불러올때 스켈레톤 UI */}
      {isLoading && (
        <>
          {Array.from({ length: 5 }).map((_, index) => (
            <PostSkeleton key={index} />
          ))}
        </>
      )}
      ...

- 변수로 받은 isLoading이 true/false 임에 따라 Skeleton컴포넌트를 랜더링 할지 결정해줍니다.

 

 

 

// ProdList.tsx

const [isProdLoading, setIsProdLoading] = useState<Boolean>(true);

useMemo(() => {
    if (productList) setIsProdLoading(false);
    
    }, [productList])
    
    ...
    
      {/* 제품 불러올때 스켈레톤 UI */}
      {isProdLoading && (
        <>
          <ProdSkeleton />
        </>
      )}

- 이건 제품 스켈레톤 구성인데 한 코드에서 작동될 수 있으므로 useState만 사용해주었습니다.

- useMemo는 react 훅 중 하나로 함수의 결과 값을 저장하고,

함수의 입력 값이 변경되지 않으면 이전 결과를 반환합니다.

그래서 productList가 들어오기 전과 들어오고 나서 총 2번만 수행합니다.

 

 

 

Styled-components로 css 구성하기

// styleBox.ts

export 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;
  }
`;
...
    <>
      <S.Container>
        {Array.from({ length: 12 }).map((_, index) => (
          <S.EmptyBox key={index} />
        ))}
      </S.Container>
    </>
    ...
    
    EmptyBox: styled(SkeletonItem)`
    width: calc(25% - 30px); /* 4 columns with 30px gap in between */
    height: 250px;
    border-radius: 10px;
    margin-bottom: 30px;
  `

- SkeletonItem 은 시간에 따른 애니매이션 효과를 주기 위함입니다.

- 적절한 크기를 분배해서 화면에 원하는 개수만큼 리스트로 화면에 띄어줍니다.

 

 

참조

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