[Next.js] Next.js + TypeScript 환경에서 styled-components 적용하기
📌 들어가며...
먼저 styled-components는 웹 애플리케이션에서 스타일을 지정할 수 있는 대표적인 CSS-In-JS 입니다.
저 역시 다양한 React 프로젝트를 진행하며 styled-components에 익숙한데요!
React 에서 styled-components를 사용할 때에는 따로 추가 설정이 필요없이 설치 후 바로 사용하면 됐습니다.
하지만, Next.js 에서 styled-components를 사용하기 위해선 추가 설정이 필요합니다.
그 이유는 바로 렌더링 방식의 차이에 있습니다.
React는 클라이언트 사이드 렌더링(CSR)이 기본이기 때문에, 웹 애플리케이션에 대한 렌더링이 클라이언트 측에서 모두 일어납니다. 따라서 styled-components에 대한 추가 설정 역시 필요가 없습니다.
Next.js는 서버 사이드 렌더링(SSR)을 지원하기 때문에, 페이지를 서버 측에서 미리 렌더링하여 전달합니다.
SSR은 초기 페이지 로드 시간이 단축되고 SEO에 유리하다는 단점이 있지만, 서버에서 렌더링된 스타일을 클라이언트로 전송해야 하며, 이를 위해서는 styled-components의 서버 스타일 시트를 설정해야 합니다.
서버에서 렌더링된 스타일을 클라이언트로 전달하기 위해 ServerStyleSheet를 사용하여 스타일을 수집하고, 클라이언트가 이 스타일을 재사용할 수 있도록 합니다.
Next.js 애플리케이션을 렌더링할 때마다 ServerStyleSheet 를 생성하고, 스타일을 React 트리에 추가하는 것입니다.
이 방법은 keyframes 또는 createGlobalStyle 과 같은 전역 스타일에는 영향을 주지 않으며, React DOM의 다양한 SSR API와 함께 styled-components를 사용할 수 있게 해줍니다.
📌 프로젝트 생성 및 styled-components 설치
우선 Next.js + TypeScript 프로젝트를 생성하겠습니다.
npx create-next-app my-next-app --typescript
cd my-next-app
이후 styled-components 를 설치합니다.
이때 TypeScript와 함께 사용하기 위해서, @types/styled-components 또한 설치합니다.
yarn add styled-components
yarn add -D @types/styled-components babel-plugin-styled-components
📌 컴파일러 옵션 설정
Next.js 에서 styled-components 를 사용하기 위해선 Babel 또는 SWC 컴파일러 옵션을 설정해야 합니다.
styled-components 공식 문서를 참고해보세요!
✅ Babel 사용 시
프로젝트 루트 위치에 .babelrc 파일을 생성한 뒤, styled-components 에 대한 설정을 추가합니다.
// .babelrc
{
"presets": ["next/babel"],
"plugins": ["styled-components"]
}
✅ SWC 사용 시
Next.js 버전 12 이상에서는 SWC라는 Rust 컴파일러를 사용합니다.
이 경우, next.config.js 파일에서 styledComponents: true 에 대한 설정을 추가합니다.
// next.config.js
const nextConfig = {
reactStrictMode: true,
compiler: {
styledComponents: true,
},
};
module.exports = nextConfig;
📌 _document.tsx 설정
Next.js에서 서버 사이드 렌더링(SSR) 시, 스타일이 올바르게 적용되도록 pages 폴더 아래 _document.tsx 파일을 설정합니다.
_document.tsx 파일은 Next.js의 커스텀 문서 컴포넌트로, 서버 사이드 렌더링(SSR) 시에 전체 페이지의 초기 HTML 구조를 정의하는 역할을 합니다.
주로 <html>, <head>, <body> 와 같은 문서 레벨의 설정을 하며, 스타일 시트를 주입하거나 메타 태그를 추가하는 등의 작업을 수행합니다.
즉, _document.tsx 파일을 SSR시에 초기 HTML 문서를 설정할 수 있습니다.
// pages/_document.tsx
import type { DocumentContext, DocumentInitialProps } from "next/document";
import Document from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
// static 키워드로 정의되어 있기 때문에, MyDocument 인스턴스를 생성하지 않고도 호출됩니다.
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
// styled-components를 사용하여 서버 측에서 스타일을 수집하는 데 사용되는 객체입니다.
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
// ctx.renderPage를 이용하여 렌더링 프로세스를 커스텀화합니다.
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
// Next.js의 기본 Document 클래스의 초기 속성을 가져옵니다.
const initialProps = await Document.getInitialProps(ctx);
// initialProps의 styles 속성을 업데이트하여, sheet.getStyleElement()에서 수집한 스타일을 포함시킵니다.
// 즉, 기본 스타일과 styled-components를 사용하여 서버 측에서 수집한 스타일이 통합됩니다.
return {
...initialProps,
styles: [initialProps.styles, sheet.getStyleElement()],
};
} finally {
// 스타일 시트를 최종화하고 추가적인 스타일 수집을 방지합니다.
sheet.seal();
}
}
}
📌 GlobalStyle 지정 (선택)
여러분의 웹 프로젝트에 맞게 전역으로 사용할 스타일을 지정하기 위해 styled-components 의 createGlobalStyle 을 사용할 수 있습니다.
이때 styled-reset 패키지도 함께 설치하시면, 브라우저의 기본 스타일을 쉽게 초기화할 수 있습니다.
저는 따로 styles/ 폴더로 분리하여 글로벌 스타일을 지정 후, _app.tsx 에서 불러오겠습니다.
_app.tsx 파일은 서버로 요청이 들어왔을 때 가장 먼저 실행되는 컴포넌트로, 페이지에 적용할 공통 레이아웃의 역할을 수행합니다.
즉, 모든 컴포넌트에 공통적으로 적용할 속성들을 관리하기 위한 파일입니다.
// src/styles/index.ts
import { createGlobalStyle } from "styled-components";
export const GlobalStyle = createGlobalStyle`
body {
margin: 0;
padding: 0;
box-sizing: border-box;
color: #333;
}
`;
// pages/_app.tsx
import type { AppProps } from "next/app";
import { GlobalStyle } from "../src/styles";
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<GlobalStyle />
<Component {...pageProps} />
</>
);
}
export default MyApp;
📌 Styled-components 사용하기
이제 모든 설정이 완료되었으니 pages/index.tsx 에서 styled-components 를 사용해보겠습니다.
// pages/index.tsx
import type { NextPage } from "next";
import styled from "styled-components";
const Home: NextPage = () => {
return <StyledDiv>hello world</StyledDiv>;
};
export default Home;
const StyledDiv = styled.div`
padding: 20px 30px;
width: max-content;
border-radius: 5px;
font-weight: bold;
background-color: orange;
`;