Next.js 프로젝트(v12)에서 styled-components
를 적용하며 발생한 스타일 FOUC(Flash of Unstyled Content) 문제와 이를 해결한 과정을 정리했습니다.
문제 상황: SSR 환경에서의 스타일 미적용
Next.js는 기본적으로 서버 사이드 렌더링(SSR) 을 지원하며, 초기 페이지 로드 시 HTML을 미리 생성해 클라이언트에 전달합니다.
프로젝트 초기에 아래와 같은 문제가 발생했습니다:
- 새로고침 시 스타일이 적용되지 않은 HTML이 먼저 렌더링됨
- 이후 JS가 로드되면서 styled-components 스타일이 적용됨
- 사용자 입장에서 순간적으로 깨진 UI가 보이는 경험을 하게 됨
이는 흔히 말하는 FOUC (Flash of Unstyled Content) 플리커 현상으로, SSR 환경에서 styled-components
를 사용할 때 자주 발생하는 이슈입니다.
스타일 누락된 SSR 초기 HTML
배경 지식: SSR과 Hydration
Next.js의 렌더링 과정은 다음 두 단계로 나눌 수 있습니다:
1. Initial Load
서버에서 static HTML을 먼저 클라이언트에 전달 (JS 미포함)
2. Hydration
클라이언트에서 JavaScript 번들을 로딩하고, static HTML을 interactive하게 만듦
이때 스타일 관련 이슈가 발생하면, 초기 HTML에는 스타일이 누락되고, hydration 이후에야 스타일이 적용됩니다.
개발자 도구에서 JavaScript를 끄고 새로고침하면 스타일이 빠진 HTML만 보이게 되며, 이 문제를 직접 확인할 수 있습니다.
원인: 서버 측에서 styled-components 스타일 미삽입
Next.js는 기본적으로 CSS-in-JS를 지원하지 않기 때문에, styled-components
스타일을 서버 사이드 렌더링 시 명시적으로 HTML에 삽입해줘야 합니다.
그렇지 않으면 HTML은 스타일 없이 전달되고, 클라이언트에서 JS가 로드되기 전까지 스타일이 적용되지 않습니다.
해결 방법: _document.tsx
에서 ServerStyleSheet
사용
Next.js 공식 문서에서는 SSR 환경에서 styled-components
를 올바르게 동작시키기 위해 커스텀 Document 구성을 권장합니다.ServerStyleSheet
를 사용하여 서버 렌더링 시 CSS를 추출하고 <head>
에 삽입하는 방식입니다.
해당 방식은 Next.js v12, styled-components v5 기준입니다.
_document.tsx
구현 예시:
// pages/_document.tsx
import Document, {
Html,
Head,
Main,
NextScript,
DocumentInitialProps,
DocumentContext,
} from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext,
): Promise<DocumentInitialProps> {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>,
],
};
} finally {
sheet.seal();
}
}
render() {
return (
<Html>
<Head>
<meta charSet="utf-8" />
<meta
httpEquiv="Content-Security-Policy"
content="upgrade-insecure-requests"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
_document.tsx
에서 ServerStyleSheet
를 활용해
초기 HTML에 스타일을 주입하면 깔끔하게 해결할 수 있습니다.