Next.js에서 서버 사이드 렌더링(SSR)을 활용하는 것은 웹 애플리케이션의 초기 로딩 시간을 단축하고, 검색 엔진 최적화(SEO)를 향상시키며, 전반적인 사용자 경험을 개선하는 데 중요한 역할을 한다. 이 글에는 최근 프로젝트에서 Emotion과 Material-UI(MUI)를 함께 사용하며 SSR을 구현하는 과정에서 페이지 로드 시 화면 깜빡임(FoUC) 현상을 해결했던 경험을 기록해두었다.
문제 상황
React 생태계에서는 다양한 스타일링 방식이 사용되며, CSS-in-JS 라이브러리인 Emotion과 UI 프레임워크인 MUI는 빠르고 유연한 개발을 위해 자주 함께 사용된다. 그러나 이러한 라이브러리들을 서버 사이드 렌더링(SSR)과 결합할 때, 초기 페이지 로딩 시 스타일이 적용되지 않아 내용이 잠깐 깜박이는 현상(FoUC)을 경험할 수 있다. 이는 사용자 경험을 저하시킬 뿐만 아니라 SEO에도 부정적인 영향을 미칠 수 있다. 내 포트폴리오 사이트에서도 이 현상이 특히 눈에 띄었는데, 랜딩하는 화면에 다양한 애니메이션 효과를 적용했기 때문에 더욱 그렇게 느껴졌다.
해결 방안
이 문제를 해결하기 위해 MUI 공식 문서에서 제시하는 Emotion의 서버 사이드 렌더링 가이드를 참고했다. 주요 해결 방안은 다음과 같다.
- _document.tsx 파일 수정: Next.js에서
_document.tsx
파일은 서버에서 생성되는 HTML 문서의 구조를 정의한다. Emotion과 MUI 스타일을 서버 사이드에서 추출하여 이 HTML에 포함시키기 위해서는 특정 로직을 추가해야 한다.
- Emotion Cache와 Server 인스턴스 생성: Emotion의
createCache
와createEmotionServer
함수를 사용하여 스타일 처리 준비를 한다.
- 스타일 추출 및 삽입: 페이지 렌더링 과정에서 생성된 스타일을 추출하고, 이를
<style>
태그로 변환하여 서버에서 생성된 HTML 문서의<head>
부분에 삽입한다.
필요한 라이브러리
이 과정을 위해 필요한 주요 라이브러리는 다음과 같다.
- @emotion/server: 서버 사이드 렌더링을 위한 Emotion 라이브러리.
- @emotion/cache: Emotion의 캐싱 메커니즘을 관리한다.
구현 예시
- Emotion Cache 생성 함수: 먼저, Emotion 캐시 인스턴스를 생성하는 함수를 정의한다. 이 캐시는 스타일 계산을 최적화하고, 서버와 클라이언트 사이의 스타일 일관성을 유지한다.
// src/createEmotionCache.ts import createCache from '@emotion/cache'; export default function createEmotionCache() { return createCache({ key: 'css' }); }
- _document.tsx 수정:
_document.tsx
에서 Emotion 서버 인스턴스를 사용하여 스타일을 추출하고,<style>
태그로 변환하여 삽입한다.
import Document, { Html, Head, Main, NextScript } from 'next/document'; import createEmotionServer from '@emotion/server/create-instance'; import createEmotionCache from 'src/createEmotionCache'; class MyDocument extends Document { static async getInitialProps(ctx) { const cache = createEmotionCache(); const { extractCriticalToChunks } = createEmotionServer(cache); const initialProps = await Document.getInitialProps(ctx); const emotionStyles = extractCriticalToChunks(initialProps.html); const emotionStyleTags = emotionStyles.styles.map(style => ( <style data-emotion={`${style.key} ${style.ids.join(' ')}`} key={style.key} dangerouslySetInnerHTML={{ __html: style.css }} /> )); return { ...initialProps, emotionStyleTags, }; } render() { return ( <Html> <Head> {this.props.emotionStyleTags} </Head> <body> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
마치며...
Emotion과 MUI를 사용하는 Next.js 프로젝트에서 Emotion의 SSR 처리 코드를 포함하면 초기 페이지 로드 시 스타일 깜빡임 없이 부드러운 사용자 경험을 제공할 수 있었다. 구현을 넘어 추후에는 좀 더 내용을 보완하여 근본적인 FoUC의 발생 원인, 스타일 캐싱의 개념과 작동 원리에 대해서도 다뤄보도록 하겠다.