on
Next.js에 styled-components 사용하기
Next.js에 styled-components 사용하기
next.js에 styled-components를 사용하기에 앞서 두가지 모듈을 설치해줘야 한다.
설치
npm i styled-components
npm i styled-reset
내가 원하는 스타일로 화면을 그리고 싶다면
일단 브라우저가 기본적으로 가지고 있는 스타일을 리셋해주고 원하는 스타일을 입혀야 한다.
브라우저 기본 스타일을 리셋시켜주는 편한 라이브러리가 있다. style-reset
${reset}
으로 초기화 시키고 공통 css 설정을 해준다.
아래 코드는 내가 스타일을 초기화 하기 위해 기본적으로 사용하는 코드이다.
아래의 기본적인 설정에 프로젝트별로 추가되는 공통 css를 여기에 설정한다.
import { createGlobalStyle } from 'styled-components';
import { reset } from 'styled-reset';
export const GlobalStyle = createGlobalStyle`
${reset}
/* CSS 초기화 */
* {
box-sizing: border-box;
word-break: keep-all;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
&::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera*/
}
}
html {
width: 100%;
overflow-x: hidden;
}
body {
min-width: 360px;
overflow-x: hidden;
line-height: 1.25;
background: #fff;
}
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video, main {
margin: 0;
padding: 0;
border: 0;
font: inherit;
font-family:'Noto Sans KR', sans-serif;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, main {
display: block;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* button, input, select, textarea 초기화 */
button, input, select, textarea{
padding:0;
margin:0;
border-radius:0;
vertical-align:middle;
font-size:100%;
font-family: 'Noto Sans KR', sans-serif;
background:transparent;
}
button,
input[type="button"] {
border:0 none;
}
button{
cursor:pointer
}
button:focus {
outline: none;
}
/* input 기본 스타일 초기화 */
input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input:focus {
outline: none;
}
/* IE10 이상에서 input box 에 추가된 지우기 버튼 제거 */
input::-ms-clear { display: none; }
/* input type number 에서 화살표 제거 */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
textarea {
resize: none;
-webkit-appearance: none;
}
a {
color: #fff;
text-decoration: none;
outline: none;
&:hover, &:active {
text-decoration: none;
color:#fff;
}
}
b {
font-weight:700;
}
`;
주의할 점
next.js는 최초 SSR 이후 내부 라우팅을 통해 페이지가 이동되면서 CSR을 하게되는데
이때, 서버에서 생성하는 해시값과 브라우저에서 생성하는 해시값이 서로 달라서 styled-components의 클래스명이 매치가 안되는 문제가 발생하게 된다.
이를 해결하기 위한 방법으로는 두가지가 있는 데
- next.js 에서 서버사이드 렌더링시 호출되는
_document.js
에 넣어주고 관련 babel plugin을 설치해서 설정해주는 것 - 마운트가 된 이후 styled-component가 렌더링 되게 해주는 것.
1번은 이렇게 설정해도 tab 관련 라이브러리 등을 사용하게 되면 또 이슈가 발생한다.
그래서 1번으로 설정하여 사용하다가 이슈 부분을 마운트 이후 렌더링 되게 수정하는 일이 발생했었다.
1번 방법
_document.js
에 styled-components의 스타일 코드를 적용하는 로직을 추가한다
공식문서 예시 _document.js
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()
}
}
}
- babel-plugin-styled-components 설치
npm i -D babel-plugin-styled-components
- root path에 .babelrc 파일을 생성해 아래 설정을 추가한다.
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true, "displayName": true, "preprocess": false }]]
}
2번 방법
hook을 사용해서 마운트 된 이후 styled-component가 그려지게 한다.
import React, { useEffect, useState } from 'react';
import { AppProps } from 'next/app';
import { GlobalStyle } from '../styles/grobal-style';
import i18n from '@i18n';
import { I18nextProvider } from 'react-i18next';
function MyApp({ Component, pageProps }: AppProps) {
const [isMounted, setIsMounted] = useState<boolean>(false);
useEffect(() => {
setIsMounted(true);
}, []);
return (
<>
{isMounted && (
<I18nextProvider i18n={i18n}>
<GlobalStyle />
<Component {...pageProps} />
</I18nextProvider>
)}
</>
);
}
export default MyApp;
3번 방법
_document.tsx를 사용하지 않고 _app.js에서 probider를 사용해서 theme 등 styled-components가 작동하도록 한다.
global-style도 이 파일에서 공통으로 적용되도록 적용한다.
import { ThemeProvider } from 'styled-components';
import { GlobalStyle } from '@styles/global-style';
...
return (
<>
<Head>
<title>...</title>
<link rel="icon" href="/favicon.ico" />
<meta
name="..."
content="..."
/>
</Head>
...
<ThemeProvider theme={theme}>
<GlobalStyle />
<Fonts />
...
</ThemeProvider>
...
</>
);
}
...
Discussion and feedback