FrontEnd/React.js

React hydration 알아보기

SambaLim 2023. 5. 2. 21:11

기존 React 어플리케이션은 번들된 js를 가져와 DOM을 렌더링하는 CSR(Client Side Rendering) 방식으로 렌더링을 하였습니다. 이러한 방식은 번들된 js 코드를 사용하므로 서버구성을 간단하게 가져갈 수 있었고 초기에 번들된 js를 가져오므로 사용자에게 마치 네이티브 어플리케이션처럼 동작하는 경험을 제공해줄 수 있었습니다.

하지만 CSR은 번들된 js로 어플리케이션의 콘텐츠를 화면에 렌더링(FCP)하고 상호작용 까지 걸리는 시간(TTI)이 증가하는 단점을 가지고 있습니다. 따라서 이와 같은 단점을 해결하고자 상황에 따라 SSR을 선택합니다. SSR은 서버에서 정적 페이지를 렌더링하고 JS파일들도 번들링한 후에 Client Side로 전달해주는데, 이 경우 서버에서는 DOM을 조작할 수 없기에 DOM에는 동적인 이벤트가 없는 상태일 것입니다.

따라서 이와 같은 정적 페이지를 동적으로 사용할 수 있는 기술이 등장하였고 이를 Hydration이라고 부릅니다.

hydrate(), hydrateRoot()

React element를 렌더링하고, Client Side에서 서버에서 렌더링된 마크업을 재사용하기 위해 기존에는 ReactDOM.hydrate() React v18부터 hydrateRoot()를 사용합니다.

hydrate()

hydrate(reactNode, domNode, callback?)

ReactDOM.hydrate()함수는 기존 DOM element와 React 컴포넌트를 결합하고, React 컴포넌트가 이전에 서버에서 렌더링된 결과를 가져와 이를 사용하여 초기 상태를 설정합니다.

초기 상태를 설정한다는 공통점이 있어 ReactDOM.render() 함수와 비교되기도 합니다.

render(element, container, callback?)

ReactDOM.render()함수는 특정 컴포넌트를 container 요소에 하위로 주입하여 렌더링을 처리해주는데 ReactDOM.hydrate() 함수는 특정 컴포넌트를 domNode 요소에 하위로 hydrate처리만 합니다. 이해를 돕기 위해 정리하자면 ReactDOM.hydrate()는 렌더링을 통해 새로운 DOM을 생성하는 것이 아니라 기존 DOM Tree에서 해당하는 DOM element를 찾아 정해진 자바스크립트 속성(ex. 이벤트 리스너)들만 추가합니다.

hydrateRoot()

const root = hydrateRoot(domNode, reactNode, options?)

React v18부터 hydrate()함수가 hydrateRoot()함수로 대체되었습니다. 이 함수는 컴포넌트를 렌더링할 DOM element를 생성하고, 해당 요소에 root 컴포넌트를 렌더링합니다. 이를 통해 기존 DOM element를 재사용하지 않고 새로운 DOM element를 생성합니다.

기존 hydrate() 함수와 유사하지만, 비동기적인 상태 업데이트를 하는데 강점이 있습니다. startTransition()함수와 함께 사용하여 컴포넌트의 상태 업데이트를 비동기적으로 처리할 수 있습니다.

마무리

느린 FCP, TTI로 인해 사용자가 불편을 느끼는 경우가 있었고, 이를 개선하기 위해 SSR을 사용하였습니다. 이때 서버에서 전해준 정적파일에 자바스크립트 속성을 부여하여 동적으로 사용할 수 있도록 Hydration을 사용합니다.

Next.js, Gatsby와 같은 프레임워크에서 다양한 렌더링을 지원해주면서 렌더링을 사용할때 어떤 원리로 동작하는지 이해하는 것이 중요해졌습니다.

다양한 렌더링을 이해하여 상황에 맞추어 원하는 렌더링을 사용하고 문제들을 해결해 나갈 수 있는 것이 중요합니다. 이 글이 Hydration을 이해하는데 조금이라도 도움이 되기를 바랍니다.

참고자료