본문으로 바로가기

FOUC, FOUT 문제를 해결해보자

category React, Next, Redux/▲ Next.js 2021. 4. 29. 16:36

styled components SSR로 FOUC(flash of unstyled content) 해결

이 문제는 SPA라서 발생하는 문제다. SSR 단계에서 스타일링을 내려주면 되면 해결된다. 

뭘 사용하는 지에 따라 다르긴 한데 어쨌든 styled-components로 일단 해보자.

 

Next에서 styled-components를 사용하면서 css가 적용되기 전에 먼저 html이 적용되는, 혹은 아예 적용이 안되는 경우가 있습니다.

이 경우 _document.js에서 직접 css를 SSR 방식으로 넣어주는 방식으로 해결할 수 있습니다.

_document.js에 대한 설명은 생략하겠습니다.

(https://nextjs.org/docs/advanced-features/custom-document)

 

아래 두 문서를 참고해보자.

joseph.to/solve-fouc-nextjs-and-gatsbyjs/

styled-components.com/docs/tooling#babel-plugin

 

 

설치

npm i -D babel-plugin-styled-components

 

공식 문서 설명에 따르면

This plugin adds support for server-side rendering, minification of styles, and a nicer debugging experience.

라는 군요

 

루트 경로에 .babelrc 생성 후 작성.

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "styled-components",
      {
        "ssr": true,
        "displayName": true,
        "preprocess": false
      }
    ]
  ]
}

 

pages/_document.js 수정

import Document, { Head, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {

  static async getInitialProps(context) {
    const sheet = new ServerStyleSheet(); // Create an instance of ServerStyleSheet
    const originalRenderPage = context.renderPage;
    try {
      context.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
        });

      const initialProps = await Document.getInitialProps(context);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }

  render() {
    return (
      <html>
        <Head>
          {/* Output the styles in the head  */}
          {this.props.styleTags}
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

 

Typescsript를 사용한다면 아래처럼 props 만 타이핑해주면 됩니다.

interface IProps {
  styleTags: Array<React.ReactElement<{}>>;
}

export default class MyDocument extends Document<IProps> {
   ... 생략

 

다 했는데 왜 폰트가 깜빡거릴까? (FOUT : Flash Of Unstyled Text)

그런데 여기에서 이렇게도 했는데 폰트가 깜빡거리는 경우가 있다.

폰트가 로드되기 전에 페이지가 내려가서 발생하는 문제가 있다.

ㄹㅇ 암이 걸릴 것 같은데..

https://stackoverflow.com/questions/60841540/flash-of-unstyled-text-fout-on-reload-using-next-js-and-styled-components

 

일단 이 문제를 해결하는 방법은

 

(1) 폰트가 로드되기 까지 완전히 기다리기

(2) javascript보다 폰트를 미리 받기

(3) 폰트가 로드되기 전에 스켈레톤을 보여주기

 

인데, 지금 프로젝트에서는 스켈레톤 까지 하면 너무 많은 투입을 해야하기도 하고, 효과적인 방법도 아닌지라 2번이 가장 나은 방법인 것 같다. 2번의 경우에는 rel="preload" 붙이면 됩니다. 반대로 늦게 받고 싶으면 defer를 붙이면 됩니다.

<link rel="preload" type="text/css" href="https://cdn......css" />

 

그런데 preload를 모두 쓸 수는 없습니다. 초반에 필수로 로딩되야 되거나 하는 것들을 위주로 몇개를 써야하며, 남발하다다보면 초반 로딩속도가 길어지는 부작용이 생길수 있다.

 

* preload, prefetch, proconnect를 통해 리소스를 먼저 받을 수 있다.

beomy.github.io/tech/browser/preload-preconnect-prefetch/

 

[Browser] 리소스 우선순위 - preload, preconnect, prefetch

이번 포스트에서는 [Browser] Critical Rendering Path 최적화에서 이야기한 리소스 우선순위를 지정할 수 있는 link 태그의 preload와 preconnect, prefetch에 대해 이야기하도록 하겠습니다.

beomy.github.io

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체