React 에서 스타일링을 위한 라이브러리에서는 개인적으로 단연 styled-components가 압도적으로 좋다고 생각한다.
하지만 Next에서는 styled-components가 제대로 동작하지 않을 수 있다.
오늘 그 오류들을 분석해보고 어떻게 하면 해결할 수 있는지 알아보자.
그냥 styled-components를 사용한다면 어떤 문제가 생가나요?
Next.js 앱에서 styled-components를 사용하면 스타일이 적용되지 않고 일정 시간 이후에 스타일이 적용된다.
마치 CSS 파일이 로드되지 않다가 갑자기 로드되는 것 처럼 보이는데, 이는 UX에서 나쁜 평가로 각인될 수 있고 사용자에게 아주 불쾌한 경험을 선사할 수 있다.
왜 그런 문제가 생기나요?
Next는 우선 모든 페이지가 Pre-Render 된다고 지난 시간에 배웠었다.
이런 Pre-Render에는 2가지 Stage가 존재하는데,
- Initial Load Stage
- Hydration Stage
각각의 Stage에 대해서 간략히 설명하자면, Initial Stage에서 Static하게 생성된 HTML이 렌더된 후, Hydration Stage에서 나머지 JS 파일들이 로드되어 Sync된다.
결국 우리가 getStaticProps
나 getStaticPaths
로 SSG를 이용해 static한 HTML을 만들어 SSR(SSG) 환경을 구축했다고 치더라도 JS에 의해 동적으로 CSS가 생성되는 CSS-In-Js 방식인 styld-components
는 SSG 과정에서 생성되는 HTML에 우리의 코드가 함께 Build 되지 않게 된다.
이를 한 문장으로 이야기 한다면,
Next.js는 SSR혹은 SSG를 기본으로 사용해서 Pre-Rendering 을 하면서 Initial Load 과정에서 미리 HTML을 로드하고 Hydration 과정에서 다른 파일들을 로드하기 떄문에 발생한다.
로 말할 수 있다.
어떻게 해결할 수 있나요?
해결 방법은 Next.js 공식 홈페이지에서 이야기하는 renderPage
함수로 해결할 수 있다.
공식 홈페이지에서는 renderPage 함수를 커스터마이징하는 것은 오로지 CSS-in-js 라이브러리를 사용할 떄만 사용하라고 나와있다.
그리고 우리는 다음과 같은 방식으로 이 문제를 해결할 것이다.
- pages 디렉토리에 Next 앱의 HTML Custom 설정을 할 수 있는
_document.js
파일을 생성 ServerStyleSheet
함수를 styled-components에서 import 하여 Global하게 설정renderPage
함수로 렌더링 조건 Customizing.
1. _document.js
파일 생성
pages 디렉토리 아래에 _document.js
파일을 생성한다.
next는 _document.js
파일을 Build 시점에 env 파일로 간주하고 생략한다.
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
2. ServerStyleSheet
함수 import
_document.js
에서 SC의 ServerStyleSheet
를 import 한다.
// ... 생략
import { ServerStyleSheet } from 'styled-components';
// ... 생략
3. renderPage
함수 조건 추가.
_document.js
에서 renderPage
함수 추가.
import Document from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default 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(<App {...props} />)
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
)
};
} finally {
sheet.seal();
}
}
}
그리고 다시 실행시키게 된다면 다음과 같이 정상적으로 styled-components가 잘 동작하는 것을 볼 수 있다.
'📺 Front End > -- react & redux & nextjs' 카테고리의 다른 글
[Next.js] Next.js 프로젝트에서 Storybook으로 TDD 하기 (Feat. Atomic Design Pattern) (2) | 2021.01.18 |
---|---|
[Next.js] _document.js로 레이아웃 템플릿을 사용해서 html 과 body 태그 커스터마이징 하기. (0) | 2021.01.03 |
[styled-components] ThemeProvider에서 MediaQuery를 적용하는 2가지 방법 (2) | 2021.01.02 |
[styled-components] ThemeProvider로 공통 스타일 속성 관리하기. (0) | 2021.01.02 |
[Next.js] Next.js에서 이미지와 동영상을 저장하고 사용해보자. (0) | 2020.12.30 |
댓글0