IT/React & Next.js

[Next.js] SSR에서의 Streaming HTML

땅일단 2025. 2. 24. 23:36

원티드 프리온보딩 챌린지 "Next.js 확실히 알고 레벨업 하기" 3일차 내용 정리

 

Streaming HTML

  • 데이터(HTML)를 한번에 로드하지 않고 일정한 단위로 점진적으로 처리하는 기술이다.(HTTP 기술임)
  • 맨 처음에 사이트에 접속했을 때 GET 요청으로 index.html을 이미 줬는데 어떻게 html을 다시 주는 걸까?
  • HTTP Header에 Transfer-Encoding: chunked 라면 Streaming HTML로서 동작한다.
  • Content-Length를 지정하면, 지정된 값까지만 받는다는 것을 서버가 클라이언트에게 알려준다.
  • 하지만 Content-Length를 지정하지 않으면, 계속 통신을 열어놓으면서 지속적으로 html을 준다.
  • \r\n 으로 나누면서 하나씩 날아온다. (한번에 다 줄수 없으므로 JSON을 사용하지 않고 RSC Payload 사용)

 

 

왜 필요한가?

  • RSC랑 Suspense 쓰면 SSR도 비동기 통신이 가능하지만, html 파일은 한번에 받는 문제가 있음.
  • Streaming HTML을 쓰면 문제가 해결됨.
  • TTFB (HTTP 요청을 했을때 처음 데이터가 도착하기까지의 시간)가 감소한다.

 

 

SSR에 존재하던 문제와 해결 방법

  • SSR시 비대화형 HTML이 생성되려면 모든 Fetch를 기다려야 했던 문제 (페이지 단위 개발) -> 서버 사이드에서 Suspense 사용하여 컴포넌트 단위 개발 (RSC가 도입되면서 가능해짐)
  • 모든 JS가 로드되어야 Hydration이 진행되었던 문제 -> RSC로 나누어 js 번들을 줄이고 Selective Hydration(유저가 선택한 항목 먼저 Hydration하는 기술) 사용

 

 

Suspense

type Status = "IDLE" | "LOADING" | "SUCCESS";

// 명령형 컴포넌트
// 하지만 리액트는 선언형 컴포넌트이다. (간단한 뷰만 설계하고, 적절한 데이터만 갱신하는 것)
export function Component(): void {
    const [status, setStatus] = useState<Status>("IDLE");
    
    if (status === "IDLE") return <span>대기중</span>;
    if (status === "LOADING") return <span>로딩중</span>;
    
    return <span>컨텐츠</span>;
}
// 선언형 컴포넌트
export function NewsComponent(): void {
    return <span>컨텐츠</span>;
}

// NewsComponent가 Suspense의 Children으로 들어가는 구조이다.
// NewsComponent에서 일어나는 로딩, 오류는 위로 올라와서 fallback을 던진다.
// Suspense 객체는 계속해서 확인하다가, SUCCESS가 되었다면 Children 컴포넌트를 보여준다.
export function App(): Element {
    return (
        <div>
            <Suspense fallback {<span>로딩중</span>}>
                <NewsComponent/>
            </Suspense>
        </div>
    )
}

서버 사이드에서 Suspense를 사용하면, 로딩 중에 비대화형 HTML을 생성해서 내려주고, 로딩 다 되면 HTML을 더 내려주기 위해 Streaming HTML을 사용한다.