본문 바로가기

FE/Next

[ Next / loading ] Nextjs에서 대표적으로 사용되는 Loading

Next js 에는 여러 가지 방식의 Loading 있다. 이것들을 어떻게 작동하는지
간략하게 알아보고 상황별 사용을 알아 보려고 한다.

 

 

wiki

 

요약

기능 분류 기능 핵심 특징 성능 영향
자동 라우트
레벨 로딩
폴더 - 라우트 탐색 시작 시 자동 활성화
- 자동 제어 방식
- 초기 로딩 화면 즉시 표시
(TTFB(Time To First Byte) 개선 효과)
eg. 간단한 페이지 전체 로딩 화면
명시적 컨포넌트
레벨 로딩
Suspense fullback
<loading />
- 특정 컴포넌트 영역 로딩 상태 격리 및 관리
- 컨포먼트 래핑 제어 방식
- 부분 화면 로딩으로 사용자 경험 향상
eg. 복잡한 대시보드, 특정 섹션 데이터 패칭, 부분 업데이트
스트리밍기반
콘텐츠 로딩
@defer 디렉티브 - 서버 컴포넌트 렌더링 스트리밍 방식으로 개선
- 디렉티브 적용 제어 방식
- 초기 페이지 로딩 성능 (TTFB) 극대화, 점진적 콘텐츠 로딩
eg. 복잡한 페이지, 초기 렌더링 시간이 긴 섹션 분리
사용자 경험 향상 Skeleton UI /
Placeholder UI
- 콘텐츠 뼈대 UI를 로딩 중에 표시하여 사용자 지루함 감소
- UI 컨포넌트 구현 제어 방식
- 사용자 인지 로딩 시간 단축 효과
eg. 모든 로딩 상황, 특히 콘텐츠 구조 예측 가능한 경우
즉시 피드백 Optimistic UI - 콘텐츠 뼈대 UI를 로딩 중에 표시하여 사용자 지루함 감소
- 로직 구현 제어 방식
- 사용자 체감 반응 속도 향상, 인터랙티브한 경험 제공
eg. 좋아요, 댓글 작성 등 사용자 액션 즉시 반영 필요한 기능
탐색 성능 최적화 Preloading
& Prefetching
- 현재 페이지 리소스 미리 로딩, 다음 페이지 예상 리소스 미리 다운로드
- <link rel="preload">,
<Link prefetch> 제어 방식
- 페이지 로딩 시간 단축, 페이지 간 탐색 속도 향상
eg. 모든 페이지, 특히 사용자 탐색 흐름 예측 가능한 경우 (메인 페이지에서 상세 페이지, 목록 페이지에서 상세 페이지 등)

 

 

 

폴더 Loading

개념
loading.js (또는 loading.tsx) 파일은 Next.js App Router의 라우트 세그먼트 
폴더 내에 위치하여, 해당 라우트 세그먼트 및 그 하위 라우트 세그먼트로의 탐색
(navigation) 이 시작될 때 자동으로 활성화되는 라우트 로딩 컴포넌트 입니다.  
즉, 특정 라우트 (폴더) 에 접근할 때, Next.js는 먼저 loading.js 파일을 렌더링하여 
사용자에게 로딩 화면을 보여줍니다. 데이터 패칭 및 컴포넌트 렌더링이 완료되면, 
loading.js 컴포넌트는 실제 라우트 컴포넌트로 대체됩니다.

 

장점
1. 라우트 기반 로딩: 라우트 탐색 단위로 로딩 상태를 관리하므로, 페이지 전체의 로딩 
상태를 효과적으로 표현할 수 있습니다.
2. 자동 활성화: 라우트 탐색 시 자동으로 활성화되므로, 별도의 코드 구현 없이 간편하게 
로딩 화면을 적용할 수 있습니다.
3. 전체 화면 로딩: 기본적으로 페이지 전체를 덮는 로딩 화면을 구현하는 데 적합합니다.
4. UI 일관성: 라우트 탐색 시 일관된 로딩 UI를 제공하여 사용자 경험을 향상시킵니다.

 

 

Suspense Fallback <loading />

개념
React의 Suspense 컴포넌트는 코드 분할 (Code Splitting) 및 데이터 패칭과 함께 
비동기 작업을 처리하고 로딩 상태를 관리하는 강력한 기능입니다. Next.js에서는 
Suspense를 활용하여 컴포넌트 레벨에서 더욱 세밀하게 로딩 상태를 제어할 수 있습니다.  
<Suspense> 컴포넌트의 fallback 프롭에 <loading /> 컴포넌트를 전달하면, 
<Suspense> 영역 내의 컴포넌트가 로딩되는 동안 fallback으로 지정된 <loading /> 
컴포넌트가 렌더링됩니다.

장점
1. 컴포넌트 레벨 로딩: 특정 컴포넌트 또는 컴포넌트 트리 내에서 발생하는 로딩 상태를 
격리하여 관리할 수 있습니다.
2. 부분 화면 로딩: 페이지의 특정 영역만 로딩 상태를 표시하고, 나머지 부분은 유지하면서 
사용자 경험을 향상시킬 수 있습니다.
3. 동시성 제어: 여러 비동기 작업을 병렬적으로 처리하고, 각 작업의 로딩 상태를 독립적으로 
관리할 수 있습니다.
4. 유연성 및 재사용성: <loading /> 컴포넌트를 재사용하여 다양한 <Suspense> 영역에 
적용할 수 있으며, 다양한 로딩 UI를 구현할 수 있습니다.

 

 

Streaming with @defer (스트리밍과 @defer 디렉티브)

개념
Next.js 14 버전부터 도입된 @defer 디렉티브는 서버 컴포넌트에서 렌더링되는 컴포넌트들을
스트리밍 방식으로 점진적으로 전송할 수 있도록 합니다. 페이지의 주요 콘텐츠는 빠르게
로딩되도록 먼저 보내고, 덜 중요한 부분 (예: 댓글 목록, 추천 상품) 은 나중에 스트리밍 방식으로
전송하여 초기 로딩 속도를 개선합니다.

장점
1. 향상된 초기 로딩 성능 (TTFB): 페이지의 주요 콘텐츠를 먼저 빠르게 보여주어 사용자 체감
로딩 속도를 향상시킵니다.
2. 점진적인 콘텐츠 로딩: 페이지의 나머지 부분은 백그라운드에서 로딩되어 사용자 경험의 끊김을
최소화합니다.
3. 코드 분할 효과: @defer 디렉티브를 통해 컴포넌트 단위로 렌더링 및 스트리밍을 제어하여 코드
분할과 유사한 효과를 얻을 수 있습니다.


export defualt async function HompePage() {
  return(
    <div>
      <article>
          ....
      </article>
      <section>
        <h3>코멘트</h3>
            <commentSection postId={1} />
       </section>

       <section>
         <h3>추천</h3>
         <Recommondations postId={1} deferred={true} />
       </section>


// app/Recommendations.js (서버 컴포넌트)
import { unstable_defer as defer } from 'next/server';

export defualt async function Recommomdation({postId, deffered}) {
  const recommendationsPromise = fetchRecommondations(postId);

  if(deffered) {
      defer(recommendationsPromise);
  }

  const recommendations = await recommendationsPromise;

  return(
  <ul>
    {recommendations.map((item) => (
      <li key={item.id}>{item.name}</li >
    ))}
  </ul>
  );
}

 

 

Placeholder UI / Skeleton UI (뼈대 UI)

개념
데이터가 로딩되는 동안 실제 콘텐츠 대신 콘텐츠의 레이아웃과 유사한 형태의 미리보기
 UI를 보여주는 방식입니다. 사용자는 콘텐츠가 어떻게 구성될지 미리 짐작할 수 있으며, 
로딩이 완료될 때까지 지루함을 덜 느끼게 됩니다.

장점
1 .향상된 사용자 경험: 로딩 중에도 시각적으로 덜 지루하고, 콘텐츠 로딩에 대한 기대감을
높입니다.
2. 인지 부하 감소: 콘텐츠가 갑자기 나타나는 것보다 자연스럽게 로딩되는 느낌을 줍니다.

 

 

Optimistic UI (낙관적 UI)

개념
데이터 변경 작업 (예: 댓글 작성, 좋아요) 과 같이 서버에 요청을 보내고 응답을 기다려야
 하는 상황에서, 실제 서버 응답을 기다리는 대신 요청이 성공할 것이라고 가정하고 UI를
즉시 업데이트 하는 방식입니다. 사용자에게 즉각적인 피드백을 제공하여 앱이 매우 빠르게
반응하는 것처럼 느끼게 합니다.

장점
1. 매우 빠른 사용자 경험: 즉각적인 UI 업데이트로 앱의 반응성이 매우 높게 느껴집니다.
2. 사용자 만족도 향상: 기다림 없이 즉각적인 피드백을 받아 사용자의 만족도가 높아집니다.

'use client';
import { useState } from 'react';
import { likePost } from '@/app/actions';


export default function LikeButton({ postId, initialLikeCount }) {
  const [isLiked, setIsLiked] = useState(false);
  const [likeCount, setLikeCount] = useState(initialLikeCount);

  const handleLike = async () => {
    setIsLiked(true); // 즉시 UI 업데이트 (낙관적 UI)
    setLikeCount(likeCount + 1);

    const success = await likePost(postId); // 서버 액션 호출
  
    if (!success) {
      setIsLiked(false);
      setLikeCount(likeCount - 1);
      alert('좋아요 실패. 다시 시도해주세요.');
    }
  };

  return (
    <button onClick={handleLike} disabled={isLiked}>
       {isLiked ? '❤️ 좋아요' : '🤍 좋아요'} ({likeCount})
    </button> );
  }