Croot Blog

Home About Tech Hobby Archive

Next.js 14 App router with Ant Design 5 & Styled-Components

๐Ÿ’ก
Next.js 14 App Router ์‚ฌ์šฉ ํ™˜๊ฒฝ์—์„œ Ant Design 5 ์™€ Styled-Components ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์„ค์น˜

๐Ÿ’ก
์˜ˆ์ œ ํ™˜๊ฒฝ
- Engine: Node 18.19.0
- Package Manager: yarn 1.22.19

1. Next.js 18 ์„ค์น˜

# node v.18.19.0 ์—์„œ ์‹คํ–‰
npx create-next-app@latest
# next v14.0.4 ์„ค์น˜๋จ.

์ฐธ๊ณ : https://nextjs.org/docs/getting-started/installation

2. antd ์„ค์น˜

yarn add antd styled-components @ant-design/cssinjs

3. ํ™•์ธ

styled-component ๋™์ž‘ ํ™•์ธ์„ ์œ„ํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ /app/pages ๊ฒฝ๋กœ์— ๊ธ€์ž ์ƒ‰์ƒ์„ ์Šคํƒ€์ผ๋ง ํ•ด๋ณด์•˜๋‹ค.

// src/app/page.tsx

'use client';

import styled from 'styled-components'

// hotpink ๊ธ€์ž์ƒ‰ ์ ์šฉ
const CustomEl = styled.h1`
  color: hotpink;
`

export default function Home() {
  return (
    <main>
      <CustomEl>
        Get started by editing&nbsp;
        <code>src/app/page.tsx</code>
      </CustomEl>
    </main>
  )
}

์ ์šฉ์€ ๋˜์ง€๋งŒ css-in-js ๋Š” ๋Ÿฐํƒ€์ž„์—์„œ ์Šคํƒ€์ผ์ด ์ƒ์„ฑ๋˜์ง€ ์•Š์•„ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ๊นœ๋นก์ž„์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ Antd ํ…Œ๋งˆ ํ† ํฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Registry๋ฅผ ์ ์šฉํ•ด์•ผํ•œ๋‹ค.

0ezgif-3-8fa65afe9e.gif

Theme ๋ฐ Registry ์ถ”๊ฐ€

1. Theme ์ž‘์„ฑ

// src/themes/base.ts

const baseTheme = {
    token: {
      fontFamily: 'Arial',
      colorPrimary: 'hotpink',
			//...
    },
    components: {
			//...
    },
  };
  
export default baseTheme

2. Registry ์ž‘์„ฑ

  1. Styled-Components Registry ์ž‘์„ฑ (๊ฒฝ๋กœ: src/providers/StyledComponentsRegistry.tsx)

     // src/providers/StyledComponentsRegistry.tsx
    	
     'use client'
    	 
     import { ReactNode, useState } from 'react'
     import { useServerInsertedHTML } from 'next/navigation'
     import { ServerStyleSheet, StyleSheetManager, ThemeProvider } from 'styled-components'
    	 
     const StyledComponentsRegistry = ({
       children,
     }: {
       children: ReactNode
     }) => {
       const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())
    	 
       useServerInsertedHTML(() => {
         const styles = styledComponentsStyleSheet.getStyleElement()
         styledComponentsStyleSheet.instance.clearTag()
         return <>{styles}</>
       })
    	 
       if (typeof window !== 'undefined') return <>{children}</>
    	 
       return (
         <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
           {children}
         </StyleSheetManager>
       )
     }
    	
     export default StyledComponentsRegistry;
    

    ์ฐธ๊ณ : https://nextjs.org/docs/app/building-your-application/styling/css-in-js#styled-components

  2. Antd Registry ์ž‘์„ฑ (๊ฒฝ๋กœ : src/providers/AntdRegistry.tsx)

     // src/providers/AntdRegistry.tsx
    	
     'use client';
    	
     import { StyleProvider, createCache, extractStyle } from '@ant-design/cssinjs';
     import { ConfigProvider } from 'antd';
     import { useServerInsertedHTML } from 'next/navigation';
     import { useState } from 'react';
     import baseTheme from '@/themes/base';
    	
     const AntdRegistry = ({ children }: { children: React.ReactNode }) => {
       const [cache] = useState(() => createCache());
    	
       useServerInsertedHTML(() => (
         <style dangerouslySetInnerHTML= id='antd'></style>
       ));
    	
       return (
         <StyleProvider cache={cache}>
           <ConfigProvider theme={baseTheme}>
             {children}
           </ConfigProvider>
         </StyleProvider>
       );
     };
    	
     export default AntdRegistry
    

    ์ฐธ๊ณ : https://ant.design/docs/react/use-with-next#using-app-router

  3. Registry ์ ์šฉ

     // src/app/layout.tsx
    	
     import type { Metadata } from 'next'
     import { ReactNode } from 'react'
     import AntdRegistry from '@/providers/AntdRegistry'
     import StyledComponentsRegistry from '@/providers/StyledComponentsRegistry'
    	
     export const metadata: Metadata = {
       title: 'Create Next App',
       description: 'Generated by create next app',
     }
    	
     export default function RootLayout({
       children,
     }: {
       children: ReactNode
     }) {
       return (
         <html lang="en">
                 {/* Antd Registry ์ ์šฉ */}
           <AntdRegistry>
                     {/* ํ…Œ๋งˆ ์‚ฌ์šฉ์„ ์œ„ํ•˜์—ฌ AntdRegistry ์•„๋ž˜์— StyledComponentRegistry๋ฅผ ๋„ฃ์—ˆ๋‹ค. */}
             <StyledComponentsRegistry>
               <body>{children}</body>
             </StyledComponentsRegistry>
           </AntdRegistry>
         </html>
       )
     }
    
  4. ํ™•์ธ

    ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ๊นœ๋นก์ž„์ด ์‚ฌ๋ผ์ง์„ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค.

๐Ÿ’ก
์œ„ ๋ฐฉ์‹๋Œ€๋กœ ์ž˜ ์•ˆ๋œ๋‹ค๋ฉด ์•„๋ž˜ ant-design ์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜ˆ์ œ ์†Œ์Šค๋ฅผ ์ด์šฉํ•ด๋ณด์‹œ๊ธธ..
๐Ÿ”—ย ant-design ์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜ˆ์ œ ์†Œ์Šค์ฝ”๋“œ

Styled-components์—์„œ Antd ํ…Œ๋งˆ ์‚ฌ์šฉ

styled-component ์ด์šฉ ์‹œ antd ํ…Œ๋งˆ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

1. ThemeProvider ์ถ”๊ฐ€

// src/providers/StyledComponentsRegistry.tsx

'use client'
 
//...์ƒ๋žต
// antd theme ์ถ”๊ฐ€
import { theme } from "antd";
 
const StyledComponentsRegistry = ({
  children,
}: {
  children: ReactNode
}) => {
	// token ์ด์šฉ์„ ์œ„ํ•ด hook ํ˜ธ์ถœ 
  const { token } = theme.useToken();
  //...์ค‘๋žต
 
  return (
    <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
			{/* Antd ํ…Œ๋งˆ ํ† ํฐ ์ ์šฉ */}
      <ThemeProvider theme=>
        {children}
      </ThemeProvider>
    </StyleSheetManager>
  )
}

export default StyledComponentsRegistry;

2. Theme ํ† ํฐ ์ ์šฉ

// src/app/page.tsx

'use client';

import styled from 'styled-components'

const CustomEl = styled.h1`
  &&& {
    color:${({ theme })=> theme.antd?.colorPrimary};
  }
`;

export default function Home() {
  return (
      <main>
        <CustomEl>
          Get started by editing&nbsp;
          <code>src/app/page.tsx</code>
        </CustomEl>
      </main>
  )
}