본문 바로가기
공부/프로그래밍

[react, material-ui] material-ui style 사용중 SSR 에러 발생(Prop `className` did not match. Server)

by demonic_ 2021. 3. 19.
반응형

다음처럼 에러가 발생했다.

Prop `className` did not match. Server: "MuiTypography-root WithStyles(ForwardRef(Typography))-root-8 MuiTypography-h6" Client: "MuiTypography-root WithStyles(ForwardRef(Typography))-root-2 MuiTypography-h6"
...

 

해당에러는 SSR 에서 생성된 className이 CSR 로 옮겨졌을 때 동일한 className을 찾지 못해 발생하는 에러다.

 

SSR은 next.js를 사용으며 해결법은 다음과 같다.

 

page/_document.js 파일을 만든 후 다음 코드를 삽입한다.

 

import React from "react";
import Document, {DocumentContext, Head, Html, Main, NextScript} from "next/document";
import {ServerStyleSheets} from "@material-ui/styles";

class AppDocument extends Document {
    static async getInitialProps(ctx: DocumentContext) {
        const sheet = new ServerStyleSheets();
        const originalRenderPage = ctx.renderPage;

        ctx.renderPage = () => originalRenderPage({
            enhanceApp: App => props => {
                return sheet.collect(<App {...props}/>)
            }
        });

        const initialProps = await Document.getInitialProps(ctx);
        return {
            ...initialProps,
            styles: (
                <>
                    {initialProps.styles}
                    {sheet.getStyleElement()}
                    {/*{sheetStyled.getStyleElement()}*/}
                </>
            )
        }
    }

    render() {
        return (
            <Html>
                <Head />
                <body>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        );
    }
}

export default AppDocument

 

그리고 바벨설정(.babelrc) 을 위해 설치 & 파일 내용을 수정한다

 

설치

npm i babel-plugin-styled-components

.babelrc 파일 수정

{
    "presets": ["next/babel"],
    "plugins": ["babel-plugin-styled-components"]
}

이제 새로고침 하면 제대로 스타일 반영 된다

 

 

 

참고)

비슷한 설정이 styled-component 에도 있는데 다음 참조하는 부분이 다르다

 

# material-ui 경우
import {ServerStyleSheets} from "@material-ui/styles";

# styled-component 의 경우
import {ServerStyleSheet} from "styled-components";

 

안에 들어있는 메서드도 다른데 각각 이렇다.

material-ui 경우

 

import * as React from 'react';
import { StylesProviderProps } from '../StylesProvider';

declare class ServerStyleSheets {
  constructor(options?: object);
  collect(children: React.ReactNode, options?: object): React.ReactElement<StylesProviderProps>;
  toString(): string;
  getStyleElement(props?: object): React.ReactElement;
}

export default ServerStyleSheets;

styled-components 경우

...
export class ServerStyleSheet {
    collectStyles(tree: React.ReactNode): React.ReactElement<{ sheet: ServerStyleSheet }>;

    getStyleTags(): string;
    getStyleElement(): Array<React.ReactElement<{}>>;
    interleaveWithNodeStream(readableStream: NodeJS.ReadableStream): NodeJS.ReadableStream;
    readonly instance: this;
    seal(): void;
}
...

 

그래서 중간에 삽입하는 구분인 sheet.collect의 경우도 styled-components 의 경우 collectStyles 를 호출해야 하며, 종종 finally 에 들어가는 곳에 sheet.seal() 이란 문구도 있는데 이역시 styled-components 를 설정할때 사용하는 것이다.

 

 

끝.

 

반응형

댓글