본문 바로가기

프로젝트/나무(나누고 나눔받는 무한 지식 품앗이)

[React] 로딩 스피너에서 스켈레톤 UI로 리팩토링

반응형

이번 프로젝트에서는 서버 데이터 관리에 리액트 쿼리를 사용했다. 리액트 쿼리를 사용하면 쉽게 로딩 처리를 해줄 수 있다. 이전 포스팅에서도 알 수 있듯이, 로딩 상태일 때 좋은 사용자 경험을 위해 로딩 스피너 라이브러리를 사용했었다.

https://bbeeyaks-moment.tistory.com/entry/React-%EB%A1%9C%EB%94%A9-%EC%8A%A4%ED%94%BC%EB%84%88-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

[React] 3분 컷 로딩 스피너 라이브러리 사용하기

예전에 '맛피' 프로젝트를 할 때 아쉬웠던 점이 몇 개 있었다. 그 중 하나가 로딩 인디케이터를 적용하지 못한 것이였다. 좋은 사용자 경험을 위해 꼭 필요한 로딩 처리, 간단하게 적용시켜 보았

bbeeyaks-moment.tistory.com

페이지가 렌더링 될 때 하나의 컴포넌트(특정 부분)에서만 로딩 스피너가 뜨면 큰 상관이 없겠지만 메인 페이지가 렌더링 될 때 게시글 캐러셀 컴포넌트와 채팅방 리스트 컴포넌트 총 두 개의 컴포넌트가 보이고 두 컴포넌트 모두 서버에서 데이터를 가져오며 로딩 스피너가 뜨니 난잡한 느낌이 들었다. 

내가 맡은 게시글 파트만 구현할 때는 몰랐었는데 팀원 분이 맡으신 채팅 파트에 리액트 쿼리를 적용하고 똑같이 로딩 스피너가 뜨도록 해주니 두 개의 로딩 스피너가 각기 다른 속도로 사라지니 영 마음에 들지 않았다.

그래서 리팩토링을 결심했다!

프로젝트 종료 몇 일 남지도 않은 그 상황에서..하하.. 어쩌겠어.. 이번 프로젝트는 정말 디자인 적으로 귀여운 느낌과 동시에 깔끔한 느낌을 가져가고 싶었다.

 

스켈레톤 UI는 다음 이미지와 같이 로딩 중에 사용자에게 문제가 있는 것이 아니라 로딩 중이니 불안해하지마세요! 라는 느낌을 주는 UI이다. 나는 유튜브를 통해 알게되었다!

이번 프로젝트에서는 공통 컴포넌트를 통해 겹치는 코드를 줄이기 위해 노력했었다. 스켈레톤 UI도 마찬가지로 공통 컴포넌트로 만들었다.

이것은 메인 컴포넌트에 있는 캐러셀 컴포넌트를 위한 스켈레톤 UI 이다. 이것 말고도 마이페이지용, 리스트 아이템 용 스켈레톤 UI를 공통 컴포넌트로 만들어 사용했다.

import styled, { keyframes } from 'styled-components';

const loadingAnimation = keyframes`
  0% {
    background-position: -200px 0;
  }
  100% {
    background-position: calc(200px + 100%) 0;
  }
`;

const SkeletonCarouselWrapper = styled.article`
  width: calc(90%);
  height: 100%;
`;

const SkeletonTagWrapper = styled.section`
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  margin: 30px 0px 20px 0px;

  p {
    margin: 0px 0px 0px 10px;
    font-size: x-large;
    font-weight: 700;
  }

  button {
    background-color: transparent;
    border: none;
    margin: 0px 1px 0px 0px;
    font-size: medium;
    font-weight: 600;
    cursor: pointer;

    &:hover {
      color: #c7d36f;
    }
  }
`;

const SkeletonItem = styled.div`
  width: 70%;
  height: 40px;
  background: linear-gradient(
    90deg,
    rgba(200, 200, 200, 0.4) 25%,
    rgba(220, 220, 220, 0.5) 37%,
    rgba(200, 200, 200, 0.4) 63%
  );
  background-size: 200px;
  animation: ${loadingAnimation} 1.8s infinite;
  margin: 6px;
  padding: 6px;
  border-radius: 20px;
  cursor: pointer;
  display: flex;
  align-items: center;
`;

const SkeletonSliderItem = styled.div`
  width: 100%;
  max-width: 800px;
  height: 110px;
  background: linear-gradient(
    90deg,
    rgba(200, 200, 200, 0.4) 25%,
    rgba(220, 220, 220, 0.5) 37%,
    rgba(200, 200, 200, 0.4) 63%
  );
  background-size: 200px;
  animation: ${loadingAnimation} 1.8s infinite;
  border-radius: 20px;
  margin: 20px 0px 20px 0px;
`;

export const SkeletonCarousel = () => (
  <SkeletonCarouselWrapper>
    <SkeletonTagWrapper>
      <SkeletonItem style={{ width: '50%', height: '30px' }} />
      <SkeletonItem style={{ width: '10%', height: '25px' }} />
    </SkeletonTagWrapper>
    <>
      <SkeletonSliderItem />
      <SkeletonSliderItem />
      <SkeletonSliderItem />
    </>
  </SkeletonCarouselWrapper>
);

로딩 스피너 자리에 적용해주면 다음과 같이 애니메이션 효과까지 더해진 스켈레톤 UI를 볼 수 있다. 개인적으로는 로딩 스피너보다 훨씬 세련되어 보이는 것 같다.

앞으로도 로딩 처리에는 스켈레톤 UI를 사용할 것 같다. 만족!

반응형