React 18 변경점들

nyongho·2022년 3월 7일
0

1. React Suspense

16부터 존재했지만 18부터 SSR과 함께 더욱 강력해졌다.

Data Fetching에 사용해 코드를 더욱 보기 좋게 할 수 있다는 장점이 있다.

// Suspense 사용 전
function Profile() {
    const [profile, setProfile] = useState();

    useEffect(() => {
        fetch('/api/profile')
            .then((response) => response.json())
            .then(setProfile);
    }, []);

    if (!profile) {
        return <Loader />;
    }

    return <div>Hello {profile.name}! Welcome!</div>;
}

React Query 또는 SWR과 같은 라이브러리를 사용해 다음과 같이 useEffect를 없앨 수 있었다.

function Profile() {
    const { data: profile, isLoading } = useQuery('profile', fn);
    if (isLoading) {
        return <Loader />;
    }
    return <div>Hello {profile.name}! Welcome!</div>;
}

16부터 있었지만 18에서 더 강력해진 Suspense를 사용하면 위 코드를 더욱 개선할 수 있다.

React로 가장 많이 하는 일은 API에서 데이터를 가져와서 state에 놓고,
아름답게 유저에게 보여주는 것.
따라서 컴포넌트 안에서 로딩 로직을 핸들링 하는 작업을 주로 한다.
이러한 로딩 상태를 컴포넌트와 분리할 수 있다면 어떨까?
Suspense를 사용하면 이러한 로딩 state를 컴포넌트 안에서 신경쓸 필요가 없다.

// Suspense 사용 후
function Profile() {
    const { data: profile } = useQuery('profile', fn);
    return <div>Hello {profile.name}! Welcome!</div>;
}

이는 로딩 state가 컴포넌트 외부에 있는 것이고,
다음과 같이 렌더링 된다는 뜻이다.

<Suspense fallback={<Loader />}>
    <Profile />
</Suspense>

React18에서는 Fetching Library가 React 커뮤니케이션하여
컴포넌트 안에 데이터를 가져오는지 여부를 알릴 수 있다.
컴포넌트 안에 데이터를 가져오면, React는 Loader를 Suspense에 렌더링한다.
그러면 컴포넌트를 따로 렌더링 하지 않고, 데이터를 가진 채로 보여줄 수 있다.
if/else를 사용해 로딩 여부를 체크하는 것 보다 훨씬 깔끔하게 처리할 수 있다.
덕분에 컴포넌트의 읽기와 쓰기, 데이터의 플로우를 이해하는 것이 쉬워진다.

하지만 진짜 장점은 SSR과 함께 사용되는 것에 있다.

2. Client Side Rendering

유저가 웹사이트에 입장하면 UI가 보이지 않는다.
유저에게 해당 브라우저가 다운받을 JS 파일을 제공한다.
브라우저는 React 코드가 들어있는 JS 파일을 실행한다.
React가 실행될 때, 유저가 볼 수 있는 모든 HTML 요소와 UI를 렌더링 한다.
모든 것이 Client Side에서 일어난다.
브라우저가 ReactJS 코드를 실행하고, 해당 코드가 유저가 볼 수 있는 UI를 빌드한다.
즉, 브라우저가 JS 파일을 다운로드하고, 실행하여 React가 실행되기 전까지
웹사이트에는 어떠한 콘텐츠나 UI가 없다.

⇒ 만약 JS가 브라우저에서 비활성화 되거나, 인터넷이 느려서 JS 파일 다운이 느려지면,
유저는 빈 화면만 보게된다.

3. Server Side Rendering

유저가 웹사이트에 입장하면, 유저에게 응답하기 전에 서버에서 React를 실행한다.
React가 UI를 렌더링하고 결과 HTML을 제공하면 해당 HTML을 유저에게 제공한다.
이 시점에서 유저는 HTML 페이지만 가지고 있고, Interaction은 없다.
(React가 브라우저에서 로딩되지 않아서)
유저가 HTML을 수신한 후, 브라우저에 의해 React 코드가 있는 JS 파일이 다운되어 실행된다. (Hydration)
이 시점에서 React는 이미 존재하는 HTML과 동기화하여 애플리케이션을 넘겨받아 Interactive하게 만든다.
SSR이 더 복잡하지만, 유저경험도 더 좋기 때문에 큰 회사들이 사용한다.
SSR을 사용하면 유저는 JS, React 로딩이 끝나 Interactive해지기 전에도 일부 UI를 볼 수 있다.

⇒ 서버에서 먼저 전체 애플리케이션을 렌더링하기 때문에, 어느 한 컴포넌트가 로딩하는데 오래 걸리면 백엔드에서(Server Side에서) 전체 렌더링하는 동안 유저는 빈 화면을 보게 된다.

React18은 Suspense가 활성화된 SSR을 사용하면서 이 문제를 해결한다.

<App>
    <Header />
    <Posts />
</App>

App이 위와 같은 구조를 가질 때, Posts 컴포넌트의 로딩이 오래 걸린다면 완료될 때까지 유저는 Header 컴포넌트도 볼 수 없다.

<App>
    <Header />
    <Suspense fallback={<Loader />}>
        <Posts />
    </Suspense>
</App>

위처럼 Suspense로 Posts를 감싼다면 HTML 코드는 다음과 같아진다.

<div>
    <header>...</header>
    <img src="loading.gif"/>
</div>

Posts 컴포넌트를 기다릴 필요없이 바로 Header 컴포넌트를 볼 수 있다.

Suspense 덕분에 React는 브라우저에서 React가 로딩되기도 전에,
Posts 컴포넌트가 Server Side에서 로딩을 끝낼 때 HTTP Stream을 사용해
Loader 컴포넌트의 HTML을 Post 컴포넌트의 결과 HTML로 대체한다.

<div>
    <header>...</header>
    <ul>...</ul>
</div>

느린 컴포넌트를 기다릴 필요없이 애플리케이션을 빠르게 렌더링할 수 있다.

또한 전체 애플리케이션을 한번에 Hydrate할 필요가 없다.
만약 유저가 아직 Hydrate 되지 않은 컴포넌트를 클릭한다면,
React는 다른 Hydrate 작업을 멈추고
유저가 Interaction하길 원하는 컴포넌트 먼저 Hydrate한다.

4. Server Components

React18의 첫 안정 버전은 아니지만, 마이너 릴리즈에서 제공될 것이다.

Server Components는 백엔드에서만 존재하는 ReactJS 코드를 쓸 수 있게 한다.
즉, 어느 컴포넌트가 브라우저가 렌더링할 컴포넌트인지,
서버가 렌더링할 컴포넌트인지, 둘 중 하나가 렌더링을 할지 등을 미리 선택할 수 있다.

만약 매우 무거운 패키지에 의존하는 컴포넌트를 갖고있다면,
CSR에서 유저는 코드와 해당 패키지를 다운로드 해야해
로딩 속도도 오래걸리고 애플리케이션의 사이즈도 커진다.

그러나 이를 Server Component로 변환하면 컴포넌트의 렌더링은 서버에서 수행되고,
렌더링의 결과값만 유저에게 스트리밍 된다.
즉, 유저가 JS 다운로드를 하지 않아도 되어 로딩 타임을 줄이고 UX를 향상 시킬 수 있다.

DB와 직접 커뮤니케이션 하는 React 컴포넌트가 생긴다.

일반적으로 React Client Side 컴포넌트에서는 API를 호출하고,
API는 서버에 있어 서버가 DB와 커뮤니케이션 한다.
Client Side 컴포넌트에서 직접 DB와 커뮤니케이션 하는 것은 보안적으로 위험하다.

그러나 Server Component를 사용한다면 React Js 컴포넌트에서 SQL 쿼리를 수행할 수 있다.
Server Component는 오직 서버에만 존재하고,
브라우저는 이 컴포넌트를 실행하지 않고 렌더링 결과만 가져오기 때문이다.

서버 컴포넌트는 componentName.server.js
클라이언트 컴포넌트는 componentName.client.js
서버와 클라이언트 모두가 렌더링 할 수 있는 범용 컴포넌트는 componentName.js로 이름을 지으면 된다.

profile
두 줄 소개

0개의 댓글