시작하기 전에
NextJS는 기본적으로 페이지를SSR
을 이용해pre-rendering
한다. Server Side에서 html 파일을 구성하여 브라우저 측에 전달해 렌더링한다.(CSS도 렌더링됨) 이후 JS 파일이 로드되어 자바스크립트 코드가 적용된다.
👉 NextJS의 페이지 렌더링 방식 알아보기
자바스크립트 코드가 적용이 되지 않은 페이지가 미리 렌더링되기 때문에 CSS-in-JS
로 스타일링을 하면 스타일이 적용되지 않은 html 코드가 먼저 렌더링되는 문제가 발생하게 된다.
NextJS는 이에 대한 해결책을 제시한다. html
파일에 CSS-in-JS
형식으로 작성된 스타일 요소들을 주입시켜서 스타일이 뒤늦게 적용되는 문제를 해결할 수 있다.
👉 NextJS 공식문서 - Customizing 'renderPage'
import Document, { DocumentContext, DocumentInitialProps } from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
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();
}
}
}
Next.js를 페이지 접속 시 SSR
를 이용하고 이후 페이지 이동 시 CSR
를 이용하여 페이지를 렌더링한다. 이때 server
와 client
에서 생성하는 class
해시값이 달라 충돌로 인해 문제가 발생한다.
👀 참고 : styled-component로 만들어진 컴포넌트는 내부적으로 태그의
className
을 해시하여 만들어지게 된다.
👉 babel-plugin-styled-components
를 설치하고 .babelrc
파일을 설정하도록 하자~
yarn add babel-plugin-styled-components
{
"presets": ["next/babel"],
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": true
}
]
]
}
NextJS를 공부하며 간단한 영화 정보 제공 애플리케이션🎬을 만들었다. 다크모드 기능을 추가했는데 어떻게 구현했는지 정리해보겠다. 😎
// styles/styled.d.ts
// theme 객체의 타입 지정
import "styled-components";
declare module "styled-components" {
export interface DefaultTheme {
bgColor: string;
menuColor: string;
textColor: string;
shadowColor: string;
name: string;
}
}
// styles/theme.ts
// theme 객체 정의
export const lightTheme = {
bgColor: "#fff",
menuColor: "blueviolet",
textColor: "#000",
shadowColor: "rgba(0, 0, 0, 0.15)",
name: "light",
};
export const darkTheme = {
bgColor: "#252525",
menuColor: "#D391FA",
textColor: "#fff",
shadowColor: "#000",
name: "dark",
};
// pages/_app.tsx
function MyApp({ Component, pageProps }: AppProps) {
const [isLightTheme, setIsLightTheme] = useState(true);
const changeTheme = () => {
setIsLightTheme(!isLightTheme);
};
return (
<ThemeProvider theme={isLightTheme ? lightTheme : darkTheme}>
<GlobalStyle />
<ThemeButton isLightTheme={isLightTheme} changeTheme={changeTheme} />
<Layout>
<Component {...pageProps} />
</Layout>
</ThemeProvider>
);
}