일상이 개발

Next.js 로딩 UX 완전 정복 – Skeleton, Suspense, 버튼 피드백까지 실전 적용 전략

아빠고미 2025. 5. 11. 12:28
반응형

Next.js 앱에서 로딩 상태 관리와 사용자 경험을 더욱 부드럽게 만드는 팁

Next.js는 빠른 렌더링과 퍼포먼스 중심의 프레임워크지만, 사용자 경험(UX)을 부드럽게 만드는 건 로딩 상태 처리에 달려 있습니다.

화면이 전환될 때, 데이터를 기다리는 동안, 클릭 이후 반응까지. 이 짧은 찰나의 순간이 쌓여 **서비스 전체 인상**을 좌우합니다.

Next.js 로딩 UX 완전 정복 – Skeleton, Suspense, 버튼 피드백까지 실전 적용 전략

 

이번 글에서는 Next.js 환경에서 실무적으로 활용할 수 있는 로딩 상태 UX 전략을 전방위로 다뤄봅니다.

  • ✅ Spinner vs Skeleton UI
  • ✅ Suspense + fallback
  • ✅ React Query + isLoading 처리
  • ✅ keepPreviousData 전략
  • ✅ 페이지 전환 시 로딩 처리
  • ✅ 트랜지션과 애니메이션

1. 🌀 기본 로딩 처리 – Spinner는 이제 그만?

✅ 기본 형태: isLoading

const { data, isLoading } = useQuery(['posts'], fetchPosts);

if (isLoading) {
  return <Spinner />;
}

하지만 이 방식은 사용자가 보기엔 갑자기 모든 콘텐츠가 사라졌다가 등장하는 UX를 유발할 수 있습니다.

📌 단점

  • 레이아웃이 깜빡임
  • 사용자 체감상 더 느림
  • “기다리는 느낌”이 강하게 남음

2. ✨ Skeleton UI – 사용자가 기다리기 편하게

✅ skeleton은 진짜 콘텐츠의 ‘모양’을 보여줌

import Skeleton from 'react-loading-skeleton';

{isLoading ? (
  <Skeleton height={180} />
) : (
  <img src={data.image} />
)}

✅ 장점

  • 레이아웃 유지 → 깜빡임 방지
  • 콘텐츠가 ‘곧 나올 것 같은’ 기대감 형성
  • UX 안정감 향상

📦 추천 라이브러리

  • react-loading-skeleton
  • @mui/lab/Skeleton
  • chakra-ui Skeleton

➡️ 로딩 시간 자체는 줄이지 못해도, 기다리는 느낌은 줄일 수 있습니다.

3. ⏳ Suspense + fallback – React 18 이후 필수 전략

React 18부터는 Suspense for Data Fetching이 공식적으로 지원되면서 데이터 기반 UI 로딩 처리가 더 정교해졌습니다.

✅ 기본 구조

import { Suspense } from 'react';

<Suspense fallback={<Skeleton />}>
  <PostList />
</Suspense>

✅ 서버 컴포넌트와도 연동 가능 (Next.js 13 이상)

App Router를 사용하는 경우, <suspense />를 활용해 레이아웃 레벨에서 로딩 UI를 처리할 수 있습니다.

// app/page.tsx
export default function Page() {
  return (
    <Suspense fallback={<Loading />}>
      <Feed />
    </Suspense>
  );
}

📌 장점

  • 컴포넌트 단위 로딩 가능
  • SSR과 연계하여 SEO/UX 동시 확보

4. 🔁 페이지 전환 로딩 처리 – 사용자에게 안내하자

📦 next/router events 활용

페이지 이동 시 로딩 UI를 보여주고, 완료되면 제거하는 구조입니다.

import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';

const usePageLoader = () => {
  const router = useRouter();
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const handleStart = () => setLoading(true);
    const handleComplete = () => setLoading(false);

    router.events.on('routeChangeStart', handleStart);
    router.events.on('routeChangeComplete', handleComplete);
    router.events.on('routeChangeError', handleComplete);

    return () => {
      router.events.off('routeChangeStart', handleStart);
      router.events.off('routeChangeComplete', handleComplete);
      router.events.off('routeChangeError', handleComplete);
    };
  }, [router]);

  return loading;
};

✅ 사용자 피드백 방식

  • 페이지 전체 덮는 Spinner
  • 상단 프로그레스 바 (예: NProgress)

➡️ 이동이 시작되었고, 완료되지 않았다는 걸 “시각적으로 알려주는 것”이 UX 개선의 핵심입니다.


5. 🧠 keepPreviousData 전략 – 페이지네이션/필터에 필수

✅ React Query의 기능

페이지네이션처럼 queryKey가 변경되더라도 이전 데이터를 유지하면서 부드럽게 전환하도록 도와줍니다.

useQuery({
  queryKey: ['posts', page],
  queryFn: () => fetchPosts(page),
  keepPreviousData: true,
});

📌 UX 장점

  • 데이터가 순간적으로 사라지지 않음
  • 사용자에게 ‘계속 이어지는’ 느낌 제공
  • 페이징 버튼 클릭 시 깜빡임 없음

6. ✨ Optimistic UI – 서버 응답 기다리지 말고 먼저 보여주자

사용자가 어떤 액션을 했을 때 즉각 반응하는 UI는 UX를 획기적으로 향상시킵니다.

📌 낙관적 UI 구성 흐름

  1. 1. UI 먼저 변경 (setQueryData)
  2. 2. 서버에 요청 전송
  3. 3. 성공 시 유지 / 실패 시 롤백
const mutation = useMutation(updateComment, {
  onMutate: async (newComment) => {
    await queryClient.cancelQueries(['comments']);
    const prev = queryClient.getQueryData(['comments']);
    queryClient.setQueryData(['comments'], (old) => [...old, newComment]);
    return { prev };
  },
  onError: (err, vars, ctx) => {
    queryClient.setQueryData(['comments'], ctx.prev);
  },
  onSettled: () => {
    queryClient.invalidateQueries(['comments']);
  }
});

➡️ “버튼 누르고 3초 후에 결과 보이는 것”보다 0.1초 안에 반응하는 UI가 훨씬 좋습니다.


7. 🧷 버튼 클릭 시 로딩 피드백 – 놓치기 쉬운 UX 핵심

✅ 가장 많이 실수하는 부분

  • 버튼 클릭 후 반응이 없음
  • “잘 눌린 건가?” 하고 사용자 불안

✅ 버튼에 로딩 표시

<button disabled={isSubmitting}>
  {isSubmitting ? '처리 중...' : '제출'}
</button>

📌 추가 UX 팁

  • 버튼 disable + 스타일 변화
  • 스피너 또는 텍스트 전환
  • 실패 시 토스트/에러 메시지 제공

➡️ 작은 피드백 하나로 전체 앱의 인상이 바뀝니다.


8. 🧠 마무리 – UX는 디테일에서 시작된다

✅ 총정리 핵심

  • Skeleton UI로 깜빡임 없는 로딩 UX 제공
  • Suspense fallback으로 SSR + 컴포넌트 단위 제어
  • 페이지 이동 시 로딩 애니메이션 or 상단 프로그레스
  • keepPreviousData로 데이터 유지
  • 낙관적 UI로 빠른 반응 + rollback 처리
  • 버튼 클릭 시 로딩 표시와 disable 처리

📌 UX 향상의 3원칙

  • 사용자에게 “앱이 반응하고 있다”는 신호를 줄 것
  • 콘텐츠가 갑자기 사라지거나 깜빡이지 않게 할 것
  • 모든 동작에 대해 “즉각적인 피드백”을 줄 것

Next.js로 성능만 빠른 앱을 만드는 걸 넘어, “사용자가 머물고 싶은 앱”을 만드는 것이 진짜 최적화입니다.


긴 글 읽어주셔서 감사합니다! 공감, 댓글, 공유로 응원해주시면 다음 글 제작에 큰 힘이 됩니다 🙌

반응형