๐Ÿ”ฅ Next.js + TypeScript + styled-components ์ดˆ๊ธฐ ์„ธํŒ…

hznยท2023๋…„ 3์›” 28์ผ
1

PROJECT๐Ÿ

๋ชฉ๋ก ๋ณด๊ธฐ
11/24
post-thumbnail

1. ์ƒˆ๋กœ์šด Next.js + TypeScript ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

npx create-next-app@latest --typescript ํ”„๋กœ์ ํŠธ๋ช…

1-1. ๊ธฐ๋ณธ ํด๋” ๊ตฌ์กฐ

1) Public ํด๋”

  • ์ •์  ํด๋”
  • ๋ธŒ๋ผ์šฐ์ €์— ์ง์ ‘ ์—‘์„ธ์Šค ๊ฐ€๋Šฅ

2) styles ํด๋”

  • global.css : _app.tsx์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ์ปดํฌ๋„ŒํŠธ๋ช….module.css : ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ import ํ•ด์„œ ์‚ฌ์šฉ (Home.module.css ๋“ฑ)

๐Ÿ‘‰๐Ÿฝ styled-components์„ ์‚ฌ์šฉํ•˜๋ฉด ์ „์—ญ ์Šคํƒ€์ผ์„ global-style.ts ์‚ฌ์šฉํ•˜๋ฏ€๋กœ global.css๋Š” ์ดํ›„ ์‚ญ์ œ

3) pages ํด๋”

index.tsx

  • ๊ธฐ๋ณธ ํŽ˜์ด์ง€

_app.tsx

  • ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ๊ฐ์‹ธ๊ณ  ์žˆ๋Š” ๊ณตํ†ต ๋ ˆ์ด์•„์›ƒ
  • ๊ฐ€์žฅ ์ตœ์ดˆ๋กœ ์‹คํ–‰๋จ
  • _app.tsx ์ดํ›„ _document.tsx๊ฐ€ ์‹คํ–‰๋จ

_document.tsx

  • ์ „์ฒด ํŽ˜์ด์ง€์˜ meta ํƒœ๊ทธ ์ •์˜์— ๊ด€์—ฌํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ
  • ๋”ฐ๋กœ ์„ค์ • ์•ˆํ•˜๋ฉด ๋””ํดํŠธ ๊ฐ’ ์ ์šฉ
  • ์ด ๊ณณ์˜ ์ฝ˜์†”์€ ์„œ๋ฒ„์—์„œ๋งŒ ๋ณด์ด๊ณ  ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ์•ˆ๋ณด์ž„

2. styled-components ์‚ฌ์šฉํ•˜๊ธฐ

  • next.js์—์„œ styled-components๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ถ”๊ฐ€์ ์ธ ์„ธํŒ…์ด ํ•„์š”ํ•˜๋‹ค!

2-1. ์„ค์น˜ํ•˜๊ธฐ

styled-components ์„ค์น˜

yarn add styled-components @types/styled-components

babel-plugin ์„ค์น˜

  • ๋ฌธ์ž์—ด ์•ˆ์— ์Šคํƒ€์ผ ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด
yarn add -dev babel-plugin-styled-components

styled-reset ์„ค์น˜

  • ์ „์—ญ ์Šคํƒ€์ผ๋ง ์‹œ ๋ ˆ์ด์•„์›ƒ ๋ฆฌ์…‹์„ ์œ„ํ•ด
yarn add styled-reset 

2-2. ํŒŒ์ผ ์„ธํŒ…

1) .barbelrc ํŒŒ์ผ

  • next.js๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” SSR์— ์˜ํ•ด styled-components ์Šคํƒ€์ผ ์ ์šฉ ์ „์— ํ™”๋ฉด ๋ Œ๋”๋ง ๋˜๋Š” ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์„ค์ •
{
        "presets" : ["next/babel"],
        "plugins": [
            [
                "styled-components",
                {
                    "ssr": true,
                    "displayName": true,
                    "preprocess": false
                }
            ]
        ]
    }

2) _document.tsx ํŒŒ์ผ

  • SSR ๋•Œ๋ฌธ์— styled-components์— ๋Œ€ํ•œ ์‚ฌ์ „ ์ž‘์—…์ด ํ•„์š”
  • ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ HTML, CSS(styled-components X)์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ Œ๋”๋งํ•œ HTML, CSS๊ฐ€ ๋‹ค๋ฅด๋ฉด Next.js๊ฐ€ warning์„ ๋„์šด๋‹ค
    ๐Ÿ‘‰๐Ÿฝ ์„œ๋ฒ„ ๋‹จ์—์„œ styled-components๋ฅผ ์ง€์›ํ•ด์„œ ์ด ๊ฐ„๊ทน์„ ๋งž์ถ”๊ธฐ ์œ„ํ•œ ์„ค์ •
import Document, {
  Html,
  Head,
  Main,
  NextScript,
  DocumentContext,
} from 'next/document';
import { ServerStyleSheet } from 'styled-components'; // 0)

export default class MyDocument extends Document {

  static async getInitialProps(ctx: DocumentContext) {
    const sheet = new ServerStyleSheet(); // 1)
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />), //2)
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: [initialProps.styles, sheet.getStyleElement()], // 2)
      };
    } finally {
      sheet.seal();
    }
  }

  render() {
    return (
      <Html lang="en">
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

1) ServerStyleSheet๋ฅผ ์ด์šฉํ•˜์—ฌ sheets๋ผ๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ.
2) sheets๋ฅผ ์ด์šฉํ•˜์—ฌ ์ง€์ •ํ•œ ์ปดํฌ๋„ŒํŠธ(์—ฌ๊ธฐ์„œ๋Š” <App />)์˜ ์Šคํƒ€์ผ ์š”์†Œ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ๊ทธ ์Šคํƒ€์ผ์„ <style> ํƒœ๊ทธ๋กœ ์ถ”์ถœ.
3) ์ถ”์ถœํ•œ ๊ฒฐ๊ณผ๋ฌผ์„ Document์— ์ „๋‹ฌ.
๐Ÿ‘‰๐Ÿฝ ์ด์ œ ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋˜๊ณ  ์†Œ์Šค ํŽ˜์ด์ง€์—์„œ๋„ ์Šคํƒ€์ผ์ด ํ‘œ์‹œ๋œ๋‹ค

3) styles ํด๋” : global-styles.ts์™€ theme.ts

    โ”œโ”€โ”€ styles
    โ”‚   โ”œโ”€โ”€ global-styles.ts      
    โ”‚   โ”œโ”€โ”€ theme.ts             

global-styles.ts

  • ์ „์—ญ์œผ๋กœ (๊ณตํ†ต์ ์œผ๋กœ) ์‚ฌ์šฉํ•˜๋Š” CSS
  • styled-reset์„ import ํ•ด์™€์„œ ์ด์ „์— ์ ์šฉ๋ผ์žˆ๋Š” ๋ ˆ์ด์•„์›ƒ์„ ๋ฆฌ์…‹์‹œํ‚จ๋‹ค.
import reset from 'styled-reset'; // ๋ ˆ์ด์•„์›ƒ ๋ฆฌ์…‹
import { createGlobalStyle } from "styled-components";

export const GlobalStyle = createGlobalStyle`
  html,
  body {
    padding: 0;
    margin: 0;
    letter-spacing: -1px;
    font-size: 15px;
    font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
      Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  }

  .txt-c {
    text-align: center;
  }
  .txt-r {
    text-align: right;
  }
  .txt-l {
    text-align: left;
  }
  p {
    margin: 0;
  }
`;

theme.ts

  • ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•  ํ…Œ๋งˆ (media query, color ๋“ฑ)

4) _app.tsx : ์Šคํƒ€์ผ ์ ์šฉํ•˜๊ธฐ

  • ์Šคํƒ€์ผ (global-styles.ts์™€ theme.ts) ์ ์šฉํ•˜๊ธฐ
import type { AppProps } from 'next/app';
import { ThemeProvider } from 'styled-components'; // 1)
import GlobalStyle from '../styles/global-style'; // 2)
import { theme } from '../styles/theme'; // 1)

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <ThemeProvider theme={theme}> // 1)
        <GlobalStyle /> // 2)
          <Component {...pageProps} />
      </ThemeProvider>
    </>
  );
}

1) ThemeProvider๋ฅผ ์ฃผ์ž…ํ•˜๊ณ  theme๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์„ธํŒ…
2) GlobalStyle ์ ์šฉ


๋ ˆํผ๋Ÿฐ์Šค

0๊ฐœ์˜ ๋Œ“๊ธ€