1. FOUC(Flash of Unstyled Content)란?
FOUC란 Flash of Unstyled Content의 약자로, 페이지가 로드될 때 스타일이 적용되지 않은 상태의 콘텐츠가 잠깐 깜빡이는 현상을 뜻합니다. 주로 서버 사이드 렌더링(SSR) 환경에서 발생하며, HTML이 먼저 렌더링되고 이후에 CSS가 로드되기 때문에 발생합니다.
예시: 로딩 애니메이션이 있는 페이지가 있을 때, FOUC가 발생하면 스타일이 적용되지 않은 HTML이 잠깐 노출될 수 있습니다. 이는 사용자 경험에 좋지 않은 영향을 줄 수 있어, 초기 스타일을 잘 적용해 깜빡임을 최소화하는 것이 중요합니다.
2. Emotion 사용 시 FOUC 발생 원인
Emotion과 같은 CSS-in-JS 라이브러리는 SSR 환경에서 자주 FOUC 문제를 일으킵니다. SSR에서는 서버가 HTML을 먼저 렌더링한 후, 클라이언트에서 JavaScript로 CSS를 동적으로 로드합니다. 이 때문에 초기 렌더링 시 클라이언트와 서버 간의 스타일 불일치가 발생하여 스타일이 적용되지 않은 콘텐츠가 노출됩니다.
3. FOUC 해결 방법
FOUC 문제를 해결하기 위해 두 가지 방법을 함께 사용합니다. 하나는 DisableTransitionsOnLoad 컴포넌트를 사용하여 초기 css의 transition(전환)효과를 비활성화하는 것이고, 다른 하나는 NextJS _document.tsx에서 Emotion의 SSR 설정을 최적화하여 서버에서 스타일을 미리 추출하는 방법입니다.
4. DisableTransitionsOnLoad 컴포넌트로 초기 전환 효과 비활성화
전환 효과는 요소의 스타일이 변경될 때 시각적으로 부드럽게 보이도록 돕는 CSS 효과입니다. 그러나 초기 로딩 시 전환 효과가 활성화되어 있으면 FOUC와 겹쳐 보이면서 깜빡임 현상이 더욱 두드러질 수 있습니다. 이를 방지하기 위해, 초기 로딩 시 전환 효과를 비활성화하고 이후 활성화하여 깔끔한 화면을 제공할 수 있습니다.
import React, { useEffect, useState } from 'react';
const DisableTransitionsOnLoad = () => {
  const [isTransitionAllowed, setIsTransitionAllowed] = useState(false);
  useEffect(() => {
    setIsTransitionAllowed(true); // 컴포넌트가 마운트된 후 전환 효과 활성화
  }, []);
  if (isTransitionAllowed) return null; // 전환 효과 활성화 시 스타일 제거
  return (
    <style
      dangerouslySetInnerHTML={{
        __html: '*, *::before, *::after { transition: none !important; }',
      }}
    />
  );
};
export default DisableTransitionsOnLoad;- 
구현 설명: - 초기 전환 비활성화: dangerouslySetInnerHTML로 모든 요소에 대해transition: none스타일을 적용하여 전환 효과를 비활성화합니다.
- useEffect로 전환 활성화: useEffect로 컴포넌트가 처음 로드된 후- isTransitionAllowed상태를 true로 설정하여 스타일 비활성화를 해제합니다.
- 조건부 렌더링: isTransitionAllowed가true가 되면<style>태그가 제거되고 전환 효과가 다시 활성화됩니다.
 
- 초기 전환 비활성화: 
- 이 컴포넌트를 초기 로딩에 적용하면, 로딩 중에는 전환 효과가 비활성화되며, 이후에는 원래 전환 스타일로 복구됩니다.
5. _document.tsx 설정으로 SSR 스타일 최적화
SSR 환경에서 Emotion의 스타일을 미리 추출하여 클라이언트와 동일한 스타일을 제공하면 초기 스타일 불일치로 인한 FOUC 문제를 완화할 수 있습니다. 이를 위해 _document.tsx에서 캐시와 스타일을 미리 설정합니다.
- 
createCache와CacheProvider로 Emotion 캐시 생성- Emotion에서 SSR 환경에서 스타일을 캐시하여 일관된 스타일을 유지하려면 CacheProvider와createCache를 사용합니다. 이를 통해 동일한 캐시가 클라이언트와 서버에서 공유됩니다.
 
- Emotion에서 SSR 환경에서 스타일을 캐시하여 일관된 스타일을 유지하려면 
- 
extractCritical을 사용하여 서버에서 CSS 추출- @emotion/server의- extractCritical메서드를 사용하면, HTML에서 필요한 스타일만 추출하여 초기 로딩 시 클라이언트에 전달할 수 있습니다.
 
아래는 Next.js 프로젝트에서 _document.tsx를 설정하는 방법입니다.
// pages/_document.tsx
import Document, { Html, Head, Main, NextScript } from 'next/document';
import createEmotionServer from '@emotion/server/create-instance';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
const cache = createCache({ key: 'next' }); // 캐시 생성
const { extractCritical } = createEmotionServer(cache);
class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx); // 기본 초기 속성 가져오기
    const page = await ctx.renderPage(); // 페이지 렌더링
    const emotionStyles = extractCritical(page.html); // 스타일 추출
    return {
      ...initialProps,
      styles: (
        <>
          {initialProps.styles}
          <style
            data-emotion={`css ${emotionStyles.ids.join(' ')}`}
            dangerouslySetInnerHTML={{ __html: emotionStyles.css }}
          />
        </>
      ),
    };
  }
  render() {
    return (
      <Html>
        <Head>
          <link rel="icon" href="/favicon.ico" />
        </Head>
        <body>
          <CacheProvider value={cache}>
            <Main />
          </CacheProvider>
          <NextScript />
        </body>
      </Html>
    );
  }
}
export default MyDocument;- 
캐시 생성 및 설정: - createCache로 ‘next’라는 키로 Emotion 캐시를 생성합니다.
- 캐시를 <CacheProvider value={cache}>로 감싸 Next.js와 Emotion이 SSR에서도 동일한 캐시를 사용하도록 설정합니다.
 
- 
스타일 추출 및 삽입: - extractCritical메서드로 HTML에서 필요한 스타일만 추출하여 CSS와 ID 목록을 반환합니다.
- getInitialProps메서드에서- <style>태그를 생성하고,- dangerouslySetInnerHTML을 사용해 CSS를 HTML에 직접 삽입해 클라이언트에서 동일한 스타일을 바로 적용하도록 합니다.
 
- 
FOUC 방지: - data-emotion="css ..."속성으로 클라이언트와 서버가 동일한 스타일을 참조하게 하여, 초기 로딩 시 FOUC 현상을 방지합니다.
 
6. 적용 및 요약
- DisableTransitionsOnLoad컴포넌트를 통해 초기 로딩 중 전환 효과를 비활성화하여 깜빡임 현상을 줄입니다.
- _document.tsx설정에서 Emotion의 SSR 스타일을 미리 추출하여 클라이언트와 서버의 스타일 불일치를 방지합니다.