styled component
사용중에 문제점을 발견하였다..!!
브라우저 로딩이 끝나야만 css 적용이 된다..
그리고.. 새로고침을 하면 css가 사라진다?..
처음엔 css가 적용되지만 새로고침을 하게되면,
css가 없어진다..?
styled-components
에 ssr
을 적용하면 처음부터 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
라고 구글에 검색해보니 나와같은 문제를 경험한분의 글을 볼수가 있었다...!!!!🙌
babel
이 리로드가 되지 않아 css가 적용이 안된다
.babelrc
에 아래와 같이 입력해줘서 리로드를 할 수 있게 해준다.
{
"presets": [
"next/babel"
],
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": false
}
]
]
}
새로고침시 css가 사라지는 문제는 해결되었지만...?
페이지가 로딩되는 동안에는 css가 적용되지 않았다가,
로딩이 끝나면 css가 들어온다..
스타일 렌더링이 너무 느리다는 단점이..
styled-components
는 javascript
여서 javascript 렌더링 순서에서 렌더링이 되어
유저 눈에 스타일 적용되는 게 다 보였던 거라고 한다.
처음 페이지에 접속했을때 Next.js
에서 랜더링될 HTML
에 스타일 정보 (styled-components)
를 포함하지 않은채 내려주었기 때문.
아래 React에서 SSR 플로우 이미지를 보면 유저가 처음 브라우저에 접속
했을때 서버
에서는 랜더링될 HTML을 준비해 응답
하고 그 후 브라우저는 자바스크립트 파일을 다운로드받고 실행
한다.
이미지 출처
SSR일때 서버에서 css( styled-component ) 정보를 포함하여 랜더링해주면 문제가 해결된다.
_document
를 사용해 SSR
에서 스타일 정보도 응답하게 하기_document
는 SSR
일때 html
및 body
태그에 내용을 추가
하거나 수정
할때 사용됩니다.
주의
_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;