본문 바로가기

WEB개발/REACT

[React] ServerComponent, HTML Streaming

 

1. Server Components  VS  ClientComponent

: React 프레임워크의 새로운 기능 중 하나입니다. 이 기능은 서버와 클라이언트 간의 데이터 및 렌더링 로직을 효과적으로 분리하여 성능을 향상시킬 수 있는 방법을 제공합니다.

 

React v18 버전 이전까지는 React를 사용하여 어플리케이션을 렌더링하는 주요한 방법이 "전적으로(entirely)" 클라이언트에 있었다.

 

서버 컴포넌트와 클라이언트 컴포넌트의 가장 큰 차이점은 컴포넌트가 렌더링되는 장소가 서버냐 클라이언트냐의 차이입니다. 

 

 

서버 컴포넌트는 서버에서 한차례 해석된 이후 클라이언트로 전달되고, 클라이언트 컴포넌트는 클라이언트가 js 번들을 다운로드 받은 후 해석하게 됩니다.

 

 

 

서버컴포넌트 사용예시 (nginx + node(next))

 

-환경

[ 운영 체제 (Ubuntu, CentOS, Debian 등) ]
   ├── 🖥 Nginx (80번 포트, 정적 파일 제공 및 리버스 프록시)
   ├── 🖥 Node.js (3000번 포트, Next.js 실행)

 

(1) Nginx 설정 파일 수정 (/etc/nginx/sites-available/default)

server {
    listen 80;
    server_name myapp.com;

    # 정적 파일을 Nginx에서 직접 서빙
    location /_next/static/ {
        alias /home/user/my-app/.next/static/;
        expires 1y;
        access_log off;
    }

    # API 및 서버 컴포넌트 요청은 Node.js로 전달
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

 

(2) 특정영역에 배포 .next/ 폴더 구조 (빌드 후)

📂/home/user/my-app/
 ├── 📂 .next/                  # (빌드된 결과물)
 │   ├── server/             # ✅ 서버 컴포넌트 코드 (Node.js에서 실행)
 │   ├── static/             # ✅ 정적 파일 (Nginx에서 직접 서빙 가능)
 │   ├── cache/              # 빌드 캐시
 │   ├── build-manifest.json # 빌드 정보
 │   ├── routes-manifest.json# 라우트 정보
 │   ├── required-server-files.json  # 서버 실행에 필요한 파일 목록
 │   ├── package.json        # 서버 실행에 필요한 의존성 정보
 │   ├── prerender-manifest.json  # 사전 렌더링 정보 (ISR 관련)
 │   ├── images-manifest.json     # 이미지 최적화 정보
 │   ├── NEXT_VERSION           # Next.js 버전 정보
 │   ├── ... 기타 파일들

 

서버 컴포넌트(API 포함) 변경 시 → Node.js 재기동 필요
정적 파일만 변경 시 → Nginx 캐시만 갱신하면 됨.
운영 환경에서는 PM2를 사용하여 자동 관리하는 것이 좋음. 


2. HTML Streaming

 

React v18에서의 주요 변경점 중 하나가 HTML Streaming과 점진적인 Hydration입니다

 

개발자들은 Server Side 단에서 먼저 정적 페이지를 렌더링하고 JS파일들도 번들링한 후에 둘다 Client Side로 보내주는 생각을 해냈다. 하지만 그 DOM에는 동적인 이벤트가 하나도 없는 메마를 상태일 것이다. 그래서 이 메마른 뼈대에 수분을 보충해서, 즉 HTML 코드와 JS 코드를 서로 매칭시켜 동적인 웹사이트를 브라우저에 랜더링하는 기술이 등장했는데 이게 바로 
Hydratation이다. 그래서 Hydration을 한글로 직역하면 수분 보충이라고 말 할 수 있다.

=> 기존 HTML을 유지하면서 React가 이벤트 핸들러와 상태를 연결하는 과정

 

 

 

v18에서부터는 pipeToNodeWritable를 이용해 html 코드를 스트리밍 형식을 통해 작은 청크 형태로 나누어 보내줄수 있습니다.

import express from 'express';
import { renderToPipeableStream } from 'react-dom/server';
import App from './App';

const app = express();

app.get('/', (req, res) => {
    res.setHeader('Content-Type', 'text/html');

    const { pipe } = renderToPipeableStream(<App />, {
        onShellReady() {
            pipe(res);
        },
        onAllReady() {
            console.log("전체 렌더링 완료");
        }
    });
});

app.listen(3000, () => {
    console.log('Server is running on http://localhost:3000');
});

 

 

3. React 마운트 과정

 

1. 함수 컴포넌트 호출

 

2. 구현부 실행

  - props 취득, hook 실행, 내부 변수 및 함수 생성

  - 단, hook 에 등록해둔 상태값, 부수함수 효과 등은 별도 메모리에 저장되어 관리된다.

 

3. return 실행

  - 렌더링 시작

 

4. 렌더 단계(Render Phase)

  - 가상DOM을 생성한다.

 

5. 커밋 단계(Commit Phase)

  - 실제 DOM에 반영한다.

 

6. useLayoutEffect

  - 브라우저가 화면에 Paint 하기 전에, useLayoutEffect에 등록해둔 effect(부수효과함수)가 '동기'로 실행된다.

  - 이 때, state, redux store 등의 변경이 있다면 한번 더 재렌더링 된다.

 

7. Paint

  - 브라우저가 실제 DOM을 화면에 그린다. didMount가 완료된다.

 

8. useEffect

  - Mount되어 화면이 그려진 직후, useEffect에 등록해둔 effect(부수효과함수)가 '비동기'로 실행된다.

 

 

 

 

 

 

 


https://velog.io/@timosean/Server-component-vs.-Client-Component

https://react.dev/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html

https://curryyou.tistory.com/486

https://velog.io/@9rganizedchaos/React-Server-Components%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90