UX를 바꾼 작은 차이, 일관된 로딩 상태 관리

YuminPark·2025년 9월 2일

veco

목록 보기
5/5
post-thumbnail

백호 데모데이 직전, 중앙에서 웹 파트 종합 피드백을 받은 적이 있다.
그중 가장 눈에 띄었고, 동시에 당장 개선할 수 있겠다고 생각한 부분이 바로 “일관된 상태 처리 관리”였다.

이번 글에서는 그중에서도 로딩 스피너를 활용해 UX를 개선했던 경험을 정리해보고자 한다.


로딩 상태를 일관되게 표시하라

중앙 피드백에서 특히 주목한 부분은 다음과 같았다.

로딩 상태를 텍스트로만 표시하거나 아예 없는 경우가 많았습니다.
이는 사용자 경험을 크게 해치는 요소입니다.

개선이 필요한 코드

const UserList = () => {
  const { data, isLoading } = useQuery(['users'], fetchUsers);
  if (isLoading) return <div>로딩중입니다...</div>; // 텍스트만 표시
  return <div>{/* 컨텐츠 */}</div>;
};

사용자는 데이터가 로딩 중인지, 혹은 오류인지 구분하기 어렵다. 게다가 단순히 텍스트가 잠깐 떴다가 사라지는 경험은 오히려 혼란스럽게 느껴질 수 있다.

그래서 제안된 방식은 Skeleton UI 혹은 로딩 스피너 같은 시각적 피드백을 주는 것이다.

개선된 코드

// components/common/Skeleton.tsx
export const Skeleton = ({
  width = '100%',
  height = 20,
  rounded = 'md'
}: SkeletonProps) => {
  return (
    <div
      className={`animate-pulse bg-gray-200 rounded-${rounded}`}
      style={{ width, height }}
    />
  );
};
// components/common/UserListSkeleton.tsx
export const UserListSkeleton = () => {
  return (
    <div className="space-y-4">
      {Array.from({ length: 5 }).map((_, i) => (
        <div key={i} className="flex items-center space-x-4">
          <Skeleton width={40} height={40} rounded="full" />
          <div className="flex-1 space-y-2">
            <Skeleton height={16} width="60%" />
            <Skeleton height={14} width="40%" />
          </div>
        </div>
      ))}
    </div>
  );
};
// 사용 예시
const UserList = () => {
  const { data, isLoading, isPending } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });
  // isPending: 초기 로딩, isLoading: 리페칭 포함
  if (isPending) return <UserListSkeleton />;
  return <UserGrid users={data} />;
};

이렇게 하면 초기 로딩은 Skeleton, 이후 리패칭은 기존 UI에 반영되면서 UX가 한결 매끄러워진다.

내가 겪었던 문제

내가 Onboarding 로직을 구현할 때는 기능에만 집중했기 때문에 UX를 고려하지 못했다.
그때의 코드는 단순히 텍스트만 return하는 형태였다:

// 위는 로직 구현 부분
  return (
    <div className="min-w-max min-h-screen flex flex-col items-center justify-center">
      <h3 className="font-title-sub-r text-gray-600">초대 확인중입니다.</h3>
    </div>
  );
// 위는 로직 구현 부분
return <h3 className="font-title-sub-r text-gray-600">로딩중입니다.</h3>

이 페이지들은 로직 처리 후 바로 넘어가므로 사용자에게 “잠깐 보였다 사라지는 텍스트”가 되어버렸다.
곰곰히 생각해보니 사용자 입장에서는 오히려 갑작스럽고, 불친절하게 느껴질 수 있을 것 같았다.

로딩 스피너로 일관성 강화

위의 언급한 문제에 대한 UX의 개선이 필요했다.
여러 웹,앱 서비스에 로딩 스피너를 사용하기 때문에 해당 return 텍스트 부분을 로딩 스피너로 대체하기로 결정했다. (+또한 피드백의 스켈레톤을 딱히 적용할 만한 부분도 없었기 때문이다!)
다행히 팀원분이 공용으로 만들어둔 LoadingSpinner 컴포넌트가 있었다.

const LoadingSpinner = () => {
  return (
    <div className={`flex justify-center items-center w-full h-dvh`}>
      <ClipLoader color={'#132650'} />
    </div>
  );
};
export default LoadingSpinner;

내부 로직 페이지에서 단순히 텍스트를 반환하던 부분을 다음처럼 교체했다 :

return <LoadingSpinner />;

아래 실행 영상처럼 모든 로직 페이지에서 일관성 있는 로딩 경험을 제공할 수 있었다.
사용자는 단순 텍스트 대신 익숙한 로딩 UI를 보게 되면서 “서비스가 정상적으로 동작하고 있다”는 신뢰감을 느낄 수 있을 것이다! ♪(´▽`)

정리 및 느낀점

  • 일관된 로딩 상태 관리를 통해 UX를 크게 향상시킬 수 있으며, 이를 위해 로딩 스피너나 스켈레톤 UI 같은 패턴을 적극적으로 활용할 수 있다.

  • 설령 AI가 개발을 대신할 수 있는 시대가 오더라도, 작은 디테일을 발견하고 개선하는 일은 결국 개발자의 몫이라고 생각한다. 이러한 작은 차이가 서비스의 완성도를 결정짓는다.

  • 단순히 기능 구현에 머무르지 않고 사용자 경험까지 고려하는 개발자가 되고 싶다. 사용자를 생각할 수 있다는 건 프론트엔드의 매력 중 하나인 것 같다! 앞으로는 기능 완성 후 반드시 사용자 입장에서 불편한 점은 없는지 점검하는 습관을 들여야겠다.

profile
기억하고 싶은 것을 기록합니다

0개의 댓글