도영스 공간

2022.06.23 TIL 리액트 18버전 본문

TIL/2022 TIL

2022.06.23 TIL 리액트 18버전

dogdogdodo 2022. 6. 23. 20:59
반응형

리액트 18버전에서 추가된 사항은 자동 배치, 동시성 제어 기능, 서스펜스를 지원하는 새로운 서버 사이드 렌더링 아키텍처입니다.

 

1️⃣ 자동배치 (Automatic Batching)

 

 배치란, 리액트가 더 나은 성능을 위해 여러 개의 상태 업데이트를 한 번의 리렌더링(re-render)으로 묶는 작업을 뜻합니다.

 

// ~React 17
ReactDOM.render(<App />, container);

// React 18
const root = ReactDOM.createRoot(container);
root.render(<App />);

 

이 변경은 단순하게 인터페이스만 바뀌는 것이 아닙니다.
기존의 render 함수는, 컴포넌트 트리를 React 17과 그 이전의 동작과 동일한 방식으로 동작합니다.
새로운 createRoot 함수는, 컴포넌트 트리를 동시성(Concurrent) 기능들과 자동 배치 등 React 18의 기능들이 동작하도록 합니다.

그러므로 React 18과 앞으로 나올 새로운 리액트의 기능들을 사용하기 위해서는 반드시 ReactDOM.createRoot 함수를 사용하도록 애플리케이션을 변경해야 합니다.

 

2️⃣  동시성 제어 기능

 

리액트에서 상태 업데이트는 항상 직접적인 상호 작용 반영에 반응하여 업데이트 되었습니다.

리액트 모든 상태 업데이트에서 제어를 하기 위해서는
setTimeout, throttle, debounce 등을 활용하는 것이 전부였습니다.

하지만 React 18부터는 startTransition API를 제공함으로써, 
업데이트를 명시적으로 구분하여 상태 업데이트를 진행할 수 있게 되었습니다.

 

import { useTransition } from 'react';

function TransitionTest() {
    const [isPending, startTransition] = useTransition();
    
    function handleChange(e) {
        const val = e.target.value;

        // 바로 보여줘야 하는(업데이트) 비지니스 로직
        setDataValue(val);

        // 다른 업데이트에 영향을 줄 수 있는 비지니스 로직을 감싼다.
        startTransition(() => {
            // 느린 렌더링 또는 네트워크 로직이 들어 갈 수 있다.
            setDataQuery(val);
        });
    }

}

 

3️⃣ 서스펜스를 지원하는 새로운 서버 사이드 렌더링 아키텍처

 

👀 Suspense

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

이러한 로딩 상태를 컴포넌트와 분리하는 것은 어떨까 ? 

Suspense를 사용하면 이러한 로딩 state를 잊어도 된다고 합니다.

 

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

위의 예시는 Suspense를 사용한다면, 이렇게 바뀔 수 있다고 합니다.

Loading state는 어디로 갔을까요 ?

 

이전에는 컴포넌트 안에 있다가 18버전 서스펜스를 사용하게 된다면, 외부에 있게된다는 의미입니다. 

 

????

즉, 프로필 컴포넌트를 렌더링 할때 아래와 같이 렌더링 한다는 의미입니다.

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

리액트 18버전을 이용할 때 Fetching 라이브러리는 리액트와 커뮤니케이션 하여서 컴포넌트 안에 데이터를 가져오는지의 여부를 알릴 수 있다고 합니다.

컴포넌트 안에 데이터를 가져오면, 리액트는 컴포넌트를 렌더링 하지 않는 대신 Loader를 suspense에 렌더링 하게 된다고 합니다.

데이터를 컴포넌트에 가져오지 않으면, 데이터와 함께 이미 있는 컴포넌트를 보여주게 됩니다.

컴포넌트에서 if / else를 사용하여 로딩이 되었는지 안되었는지를 확인하는 것보다 훨씬 더 깔끔해진 것을 알 수 있습니다.

 

서버사이드 렌더링 지원

 

CSR vs SSR

클라이언트 사이드 렌더링과 서버사이드 렌더링을 알아보겠습니다.

CSR

유저가 웹사이트에 입장하니 UI가 안보입니다. 우리는 유저에게 브라우저가 다운받을 js파일을 제공하게 됩니다. 

브라우저는 리액트 코드가 들어있는 js파일을 실행하게 됩니다. 리액트가 실행될 때 리액트는 유저가 볼 수 있는 모든 HTML요소와 UI를

렌더링 합니다.

 

만일 인터넷이 느려서 js파일 다운로드가 느려지게 된다면 , 유저는 빈화면만 보게 됩니다. 

 

SSR

유저가 웹사이트에 들어오면,  유저에게 응답하기 전에 서버에서 리액트를 실행합니다. 리액트는 UI를 렌더링하고 결과 HTML을 제공합니다. 그리고 해당 HTML을 유저에게 제공합니다. 이 시점에서 유저는 인터렉션이 없는 HTML파일만 가지고 있게 됩니다. (왜냐? 리액트가 브라우저에서 로딩이 되지 않았기 때문 !!)

바로 여기에서는 하이드레이션(Hydration)이라는 프로세스가 존재하는데, 유저가 HTML을 수신한 뒤 리액트 코드가 있는 js파일도 브라우저에 의해 다운로드 되고 실행됩니다. 

하이드레이션은, 브라우저에서 HTML을 렌더링할 때 페이지의 컴파일된 HTML에 JavaScript 동작을 다시 추가하는 프로세스를 말합니다.

이 시점에서 리액트 js는 이미 존재하는 HTML과 동기화하여서 애플리케이션을 넘겨받아 인터렉티브하게 만들어줍니다.

서버사이드 렌더링이 클라이언트 사이드 렌더링보다 더 복잡하지만, 유저 경험이 좋기 때문에 큰 회사들이 사용한다고 합니다.

클라이언트 사이드 렌더링을 이용하면, js와 리액트가 로딩 될 때까지 유저는 빈화면만 보게 되기 때문입니다.

 

서버사이드 렌더링을 이용하면 어떠한 UI는 볼 수 있는 상태이며, js 리액트 로딩이 끝나면 인터렉티브 해지게 됩니다.

서버사이드 렌더링의 단점은 서버에서 먼저 전체 어플리케이션을 렌더링 하기 때문에 어느 한 컴포넌트가 로딩하는데 오래걸리면, 백엔드에서 전체를 렌더링하는 동안 유저는 빈화면을 볼 수 있습니다.

 

예를들자면, posts가 로딩 되기전까지는 유저는 헤더 컴포넌트도 볼 수 없게 됩니다.

<APP>
	<Header />
    	<Posts/>
</APP>

리액트 18버전에서는 suspense를 활용해서 이러한 문제를 해결해줍니다. 

 

서버쪽에서 어플리케이션을 렌더링 하지만, 

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

suspense로 posts컴포넌트를 감싸줍니다. 이때 HTML을 살펴보면 아래와 같습니다.

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

 유저는 posts컴포넌트를 기다리지 않아도 헤더 컴포넌트를 바로 볼수 있습니다.

새로운 api덕분에 posts가 서버사이트 렌더링을 끝낼 때 http스트림을 사용하여 

리액트는 Loader컴포넌트의 HTML을 브라우저에서 리액트가 로딩 되기도 전에  posts 컴포넌트의 결과 HTML로 대체한다는 것입니다.

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

위의 코드를 보면, img 태그가 ul태그로 대체 됩니다.

 

Suspence 덕분에 어플리케이션을 빠르게 렌더링 할 수 있다는 말이다.

전체 어플리케이션을 한번에 하이드레이션(Hydration)을 할 필요 없습니다.

게다가 아직 Hydrate가 되지 않은 리액트 컴포넌트를 유저가 클릭하면, 리액트는 하던 Hydrate를 멈추고 

유저가 인터렉션 하기 원하는 컴포넌트를 우선적으로 Hydrate합니다.

 

찐 장점은 Sever Components 입니다. 

 

Sever Components

서버 컴포넌트는 백엔드에만 존재하는 리액트 js코드를 쓸 수 있게 합니다.

즉, 어느 컴포넌트가 브라우저가 렌더링할 컴포넌트인지 서버가 렌더링할 컴포넌트인지 둘 중 하나가 렌더링을 할지를 미리 선택할 수 있다는 것 입니다. 

 

만약 무거운 패키지에 의존하는 컴포넌트를 갖고 있다면,  클라이언트 사이드 렌더링에서는 유저는 코드 그리고 해당 패키지를 다운로드 해야됩니다. 덕분에 로딩 속도도 오래 걸리고, 어플리케이션 사이즈도 커지게 됩니다.

 

그러나 이것을 서버 컴포넌트로 변환한다면 컴포넌트의 렌더링은 서버에서 수행되고 렌더링의 결과값만 유저에게 스트리밍 되게 됩니다.

즉 유저는 js를 다운로드 받지 않아도 됩니다. 결과적으로 로딩 시간이 빨라지고 , ux도 향상됩니다.

 

또한 DB랑 직접적으로 커뮤니케이션 하는 리액트 컴포넌트가 생기게 됩니다. 

일반적으로 클라이언트 사이드 렌더링의 리액트에는 api를 호출하고 api는 서버에 있습니다. 그리고 서버가 DB랑 통신을 합니다.

이 상태에서 직접적으로 DB랑 통신하면 보안적으로 문제가 됩니다.

 

하지만 컴포넌트가 서버 컴포넌트였다면 리액트 JS컴포넌트에서 SQL 쿼리를 수행할 수 있습니다.

여기에서 컴포넌트가 서버 컴포넌트이기 때문에 서버에만 존재하고, 브라우저는 이 컴포넌트를 실행하지 않습니다. 

해당 컴포넌트의 렌더링 결과값만 가져옵니다. 

파일 이름 끝네 .server.js를 추가하면 서버 컴포넌트가 되고 .client.js를 추가하면 클라이언트 컴포넌트가 됩니다.

그냥 .js를 붙이면 서버, 클라이언트 모두가 렌더링 할 수 있는 컴포넌트가 됩니다. 

 

참고: https://www.youtube.com/watch?v=7mkQi0TlJQo /https://medium.com/naver-place-dev/react-18%EC%9D%84-%EC%A4%80%EB%B9%84%ED%95%98%EC%84%B8%EC%9A%94-8603c36ddb25

 

728x90
반응형
Comments