styled component

마데슾 : My Dev Space·2020년 6월 3일
3

styled component 사용중에 문제점을 발견하였다..!!

문제점🤔

브라우저 로딩이 끝나야만 css 적용이 된다..

그리고.. 새로고침을 하면 css가 사라진다?..

처음엔 css가 적용되지만 새로고침을 하게되면,

css가 없어진다..?

해결방법😎

styled-componentsssr을 적용하면 처음부터 css가 적용 된 상태로 나오게 된다.

아래와같이 해주면 된다..! 포인트는 ServerStyleSheet!

// _document.js
import React from "react";
import Document, { Main, NextScript, Head } from "next/document"; //next의 html과 나머지 기타 기능들을 넣어주는 Main과 NextScript
import Helmet from "react-helmet"; // head태그에 넣을 정보를 jsx로 작성할 수 있게 도와준다.
import { ServerStyleSheet, createGlobalStyle } from "styled-components";
// _document.js는 index.html을 꾸며주는거다라고 생각하면 된다.
// class형으로 밖에 못 하는게 조금 아쉽다.
// ServerStyleSheet을 사용하여 서버사이드렌더링을 하게 할 수 있다.
// 전체적으로 css를 주고 싶은 부분은 createGlobalStyle을 사용하여 가능하다.
const GlobalStyles = createGlobalStyle`
       html, body {
            height: 100%;
            overflow: auto;
          }
          #__next {
            height: 100%;
          }
`;
class MyDocument extends Document {
  static getInitialProps(context) {
    const sheet = new ServerStyleSheet(); // 서버사이드 렌더링 할 수 있게함.
    const page = context.renderPage(App => props =>
      sheet.collectStyles(
        <>
          <GlobalStyles />
          <App {...props} />
        </>
      )
    ); // 아래의 스타일들을 모아서 페이지를 그려준다. 원래는 <GlobalStyles/> 없이 하는데 글로벌 스타일을 지정해주기 위해 저렇게 적었다.
    const styleTags = sheet.getStyleElement();
    return { ...page, helmet: Helmet.renderStatic(), styleTags };
  }
  render() {
    const { htmlAttributes, bodyAttributes, ...helmet } = this.props.helmet; // helmet으로 부터 받아온다.
    const htmlAttrs = htmlAttributes.toComponent();
    const bodyAttrs = bodyAttributes.toComponent();
    return (
      //html이랑 head, body 부분에 각각 props들을 넣어준다
      <html {...htmlAttrs}> 
        <Head> 
          {this.props.styleTags}
          {Object.values(helmet).map(el => el.toComponent())} 
        </Head>
        <body {...bodyAttrs}>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

export default MyDocument;

그런데 위와같이 하면 첫 렌더링시 css가 적용이 안되는 문제는 해결되었지만, 새로고침시 css가 적용이 안되는 문제가 해결되지 않았다..

styled component 새로고침 css 라고 구글에 검색해보니 나와같은 문제를 경험한분의 글을 볼수가 있었다...!!!!🙌

1) 첫번째 시도

babel이 리로드가 되지 않아 css가 적용이 안된다
.babelrc에 아래와 같이 입력해줘서 리로드를 할 수 있게 해준다.

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

새로고침시 css가 사라지는 문제는 해결되었지만...?
페이지가 로딩되는 동안에는 css가 적용되지 않았다가,

로딩이 끝나면 css가 들어온다..

스타일 렌더링이 너무 느리다는 단점이..
styled-componentsjavascript여서 javascript 렌더링 순서에서 렌더링이 되어 유저 눈에 스타일 적용되는 게 다 보였던 거라고 한다.

참고블로그

2) 두번째 시도

처음 페이지에 접속했을때 Next.js에서 랜더링될 HTML스타일 정보 (styled-components) 를 포함하지 않은채 내려주었기 때문.

아래 React에서 SSR 플로우 이미지를 보면 유저가 처음 브라우저에 접속했을때 서버에서는 랜더링될 HTML을 준비해 응답하고 그 후 브라우저는 자바스크립트 파일을 다운로드받고 실행한다.

이미지 출처

SSR일때 서버에서 css( styled-component ) 정보를 포함하여 랜더링해주면 문제가 해결된다.

_document를 사용해 SSR에서 스타일 정보도 응답하게 하기

_documentSSR일때 htmlbody 태그에 내용을 추가하거나 수정할때 사용됩니다.

주의
_document 파일에 정의 된 내용들은 SSR일때만 호출됩니다. CSR에서는 호출 되지 않습니다.

// _document.js (참고글)

import Document, { DocumentContext } from 'next/document';
import { ServerStyleSheet } from 'styled-components';

class CustomDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    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()}
          </>
        )
      };
    } catch (error) {
      throw error;
    } finally {
      sheet.seal();
    }
  }
}

export default CustomDocument;

위에 코드는 사용자처음 페이지를 접속했을때 Next.js에 서버에서 응답할때 styled-components 스타일 정보도 같이 포함하여 랜더링 될 수 있도록해주는 코드이다.

내 코드에 적용

import React from 'react';
import Helmet from 'react-helmet';
import PropTypes from 'prop-types';
import Document, { DocumentContext } from 'next/document';
import { ServerStyleSheet } from 'styled-components'; // styled-components SSR 적용하기 위함. 공식 문서 참고.
import GlobalStyles from '../components/GlobalStyles';

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: App => props =>
            sheet.collectStyles(
              <>
                <GlobalStyles />
                <App {...props} />
              </>,
            ),
        });

      const initialProps = await Document.getInitialProps(ctx);

      return {
        helmet: Helmet.renderStatic(),
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } catch (error) {
      throw error;
    } finally {
      sheet.seal();
    }
  }
}

MyDocument.propTypes = {
  helmet: PropTypes.object.isRequired,
  styleTags: PropTypes.object.isRequired,
};

export default MyDocument;

참고블로그

profile
👩🏻‍💻 🚀

0개의 댓글