본문 바로가기
React 입문자 꼬리표 떼기/Frontend Setup

Next.js Project Init

by SmileRush 2022. 3. 13.
반응형

$ npx create-next-app . --ts

$ npm i recoil styled-reset styled-components babel-plugin-styled-components && npm i -D @types/styled-components

 


.babelrc 생성 후 추가 : styled-components의 SSR, CSR 간 className이 달라서 발생하는 에러를 잡아줌

{
  "presets": ["next/babel"],
  "plugins": ["babel-plugin-styled-components"]
}

// 둘 중에 아무거나 사용, 아래는 조금 더 상세한 정보를 표시해줌

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "babel-plugin-styled-components",
      { "fileName": true, "displayName": true, "pure": true, "ssr": true }
    ]
  ]
}

 

.eslintrc.json 수정 : "next/babel"이 없으면 에러가 나는데, 이를 포함해서 lint를 잡아줌.

{
  "extends": ["next/babel", "next/core-web-vitals"]
}

 


.next.config.js 수정하기 : next.js에서 svg파일을 사용하고 싶을 때

$ npm i intercept-stdout

// Recoil의 Key가 SSR, CSR 에서 달라지는데서 발생하는 에러 보기 싫을 때
const intercept = require('intercept-stdout')

function interceptStdout(text) {
  if (text.includes('Duplicate atom key')) {
    return ''
  }
  return text
}

intercept(interceptStdout)

// SVG 이용하기
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
}

module.exports = {
  nextConfig,
  webpack: (config) => {
    config.module.rules.push({
      test: /\.svg$/,
      use: ['@svgr/webpack'],
    })
    return config
  },
}

 


tsconfig.ts 수정하기 : 절대 경로를 사용하고 싶을 때

{
  "compilerOptions": {
    "target"                           : "es5",
    "lib"                              : ["dom", "dom.iterable", "esnext"],
    "allowJs"                          : true,
    "skipLibCheck"                     : true,
    "strict"                           : true,
    "forceConsistentCasingInFileNames" : true,
    "noEmit"                           : true,
    "esModuleInterop"                  : true,
    "module"                           : "esnext",
    "moduleResolution"                 : "node",
    "resolveJsonModule"                : true,
    "isolatedModules"                  : true,
    "jsx"                              : "preserve",
    "incremental"                      : true,
    "baseUrl": ".",
    "paths": {
      "/pages"      : ["/pages/*"],
      "/Recoil"     : ["/Recoil/*"],
      "/Components" : ["/Components/*"],
    }
  },
  "include" : ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude" : ["node_modules"]
}

 


pages/_document.js 생성 후 작성 : styled-components가 새로고침 등의 상황에서 깨지는 것을 막아줌.

import Document, { Head, Html, Main, NextScript } 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()
    }
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

 


Styles 디렉토리 1  -  Styles/Styled.d.ts 생성 후 추가

import 'styled-components'

declare module 'styled-components' {
  export interface DefaultTheme {
    colors: {
      White  : ['#FFFFFF'],
      Black  : ['#000000'],
      Red    : ['#fff5f5', '#ffe3e3', '#ffc9c9', '#ffa8a8', '#ff8787', '#ff6b6b', '#fa5252', '#f03e3e', '#e03131', '#c92a2a',],
      Pink   : ['#fff0f6', '#ffdeeb', '#fcc2d7', '#faa2c1', '#f783ac', '#f06595', '#e64980', '#d6336c', '#c2255c', '#a61e4d',],
      Orange : ['#fff4e6', '#ffe8cc', '#ffd8a8', '#ffc078', '#ffa94d', '#ff922b', '#fd7e14', '#f76707', '#e8590c', '#d9480f',],
      Yellow : ['#fff9db', '#fff3bf', '#ffec99', '#ffe066', '#ffd43b', '#fcc419', '#fab005', '#f59f00', '#f08c00', '#e67700',],
      Green  : ['#ebfbee', '#d3f9d8', '#b2f2bb', '#8ce99a', '#69db7c', '#51cf66', '#40c057', '#37b24d', '#2f9e44', '#2b8a3e',],
      Lime   : ['#f4fce3', '#e9fac8', '#d8f5a2', '#c0eb75', '#a9e34b', '#94d82d', '#82c91e', '#74b816', '#66a80f', '#5c940d',],
      Teal   : ['#e6fcf5', '#c3fae8', '#96f2d7', '#63e6be', '#38d9a9', '#20c997', '#12b886', '#0ca678', '#099268', '#087f5b',],
      Cyan   : ['#e3fafc', '#c5f6fa', '#99e9f2', '#66d9e8', '#3bc9db', '#22b8cf', '#15aabf', '#1098ad', '#0c8599', '#0b7285',],
      Blue   : ['#e7f5ff', '#d0ebff', '#a5d8ff', '#74c0fc', '#4dabf7', '#339af0', '#228be6', '#1c7ed6', '#1971c2', '#1864ab',],
      Indigo : ['#edf2ff', '#dbe4ff', '#bac8ff', '#91a7ff', '#748ffc', '#5c7cfa', '#4c6ef5', '#4263eb', '#3b5bdb', '#364fc7',],
      Violet : ['#f3f0ff', '#e5dbff', '#d0bfff', '#b197fc', '#9775fa', '#845ef7', '#7950f2', '#7048e8', '#6741d9', '#5f3dc4',],
      Grape  : ['#f8f0fc', '#f3d9fa', '#eebefa', '#e599f7', '#da77f2', '#cc5de8', '#be4bdb', '#ae3ec9', '#9c36b5', '#862e9c',],
      Gray   : ['#f8f9fa', '#f1f3f5', '#e9ecef', '#dee2e6', '#ced4da', '#adb5bd', '#868e96', '#495057', '#343a40', '#212529',],
    },
  }
}

 


Styles 디렉토리 2  -  Styles/Theme.ts 생성 후 추가

import { DefaultTheme } from 'styled-components'

export const Theme: DefaultTheme = {
  colors: {
    White  : ['#FFFFFF'],
    Black  : ['#000000'],
    Red    : ['#fff5f5', '#ffe3e3', '#ffc9c9', '#ffa8a8', '#ff8787', '#ff6b6b', '#fa5252', '#f03e3e', '#e03131', '#c92a2a',],
    Pink   : ['#fff0f6', '#ffdeeb', '#fcc2d7', '#faa2c1', '#f783ac', '#f06595', '#e64980', '#d6336c', '#c2255c', '#a61e4d',],
    Orange : ['#fff4e6', '#ffe8cc', '#ffd8a8', '#ffc078', '#ffa94d', '#ff922b', '#fd7e14', '#f76707', '#e8590c', '#d9480f',],
    Yellow : ['#fff9db', '#fff3bf', '#ffec99', '#ffe066', '#ffd43b', '#fcc419', '#fab005', '#f59f00', '#f08c00', '#e67700',],
    Green  : ['#ebfbee', '#d3f9d8', '#b2f2bb', '#8ce99a', '#69db7c', '#51cf66', '#40c057', '#37b24d', '#2f9e44', '#2b8a3e',],
    Lime   : ['#f4fce3', '#e9fac8', '#d8f5a2', '#c0eb75', '#a9e34b', '#94d82d', '#82c91e', '#74b816', '#66a80f', '#5c940d',],
    Teal   : ['#e6fcf5', '#c3fae8', '#96f2d7', '#63e6be', '#38d9a9', '#20c997', '#12b886', '#0ca678', '#099268', '#087f5b',],
    Cyan   : ['#e3fafc', '#c5f6fa', '#99e9f2', '#66d9e8', '#3bc9db', '#22b8cf', '#15aabf', '#1098ad', '#0c8599', '#0b7285',],
    Blue   : ['#e7f5ff', '#d0ebff', '#a5d8ff', '#74c0fc', '#4dabf7', '#339af0', '#228be6', '#1c7ed6', '#1971c2', '#1864ab',],
    Indigo : ['#edf2ff', '#dbe4ff', '#bac8ff', '#91a7ff', '#748ffc', '#5c7cfa', '#4c6ef5', '#4263eb', '#3b5bdb', '#364fc7',],
    Violet : ['#f3f0ff', '#e5dbff', '#d0bfff', '#b197fc', '#9775fa', '#845ef7', '#7950f2', '#7048e8', '#6741d9', '#5f3dc4',],
    Grape  : ['#f8f0fc', '#f3d9fa', '#eebefa', '#e599f7', '#da77f2', '#cc5de8', '#be4bdb', '#ae3ec9', '#9c36b5', '#862e9c',],
    Gray   : ['#f8f9fa', '#f1f3f5', '#e9ecef', '#dee2e6', '#ced4da', '#adb5bd', '#868e96', '#495057', '#343a40', '#212529',],
  },
}

 


Styles 디렉토리 3  -  Styles/GlobalStyles.ts

import { createGlobalStyle } from 'styled-components'
import reset from 'styled-reset'

const GlobalStyles = createGlobalStyle`
  ${reset};

  a {
    text-decoration : none;
    color           : inherit;
  }

  * {
    // @ 드래그 방지
    user-select : none; -ms-user-select : none; -khtml-user-select : none; -moz-user-select : -moz-none; -webkit-user-select : none;
    box-sizing      : border-box;
    // @ Input의 type="number" 속성에 따라, spin button 없애기
    ::-webkit-outer-spin-button,
    ::-webkit-inner-spin-button {
      -webkit-appearance : none;
      margin             : 0;
    }
  }

  html,
  body,
  /* #root { */
  #__next {
    -webkit-font-smooting : antialiased;
    font-family           : -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    font-size             : 16px;
    width                 : 100%;
    height                : 100%
  }
`

export default GlobalStyles

 


_app.tsx 에서 조립하기

import type { AppProps } from 'next/app'
import { RecoilRoot } from 'recoil'
import { ThemeProvider } from 'styled-components'
import GlobalStyles from 'Styles/GlobalStyles'
import { Theme } from 'Styles/Theme'

const App = ({ Component, pageProps }: AppProps) => {
  return (
    <RecoilRoot>
      <GlobalStyles />
      <ThemeProvider theme={Theme}>
        <Component {...pageProps} />
      </ThemeProvider>
    </RecoilRoot>
  )
}

export default App
반응형

'React 입문자 꼬리표 떼기 > Frontend Setup' 카테고리의 다른 글

Emotion 도입하기  (0) 2023.06.15
6. Grid Layout #2  (2) 2022.02.18
6. Grid Layout #1  (0) 2022.02.14
5. Apply Styled-Components  (1) 2022.02.12
4. TypeScript - Object (객체)  (1) 2022.02.12

댓글