카테고리 없음

Next.js 초기세팅( +TypeScript + Emotion (styled))

pajiyeee 2024. 3. 14. 18:49

React + Styled-Components로 작업했던 프로젝트를 Next.js로 리팩토링하는데 초기 설정도 쉽지 않았다.

프론트엔드에서 중요하다고 생각되는 next.js와 typescript를 알아가고 싶은 마음으로 세팅을 시작하게 되었다.

 

📎 Reference https://snupi.tistory.com/203

 

처음에 VSCode를 열고 desktop에서 작업할 폴더를 만들고 싶어 cd desktop을 한 다음

TypeScript 기반으로 Next.js 를 설치했다.

명령어에서 .은 vscode에 열려 있는 폴더 기준으로 생성하겠다는 의미다.

yarn create next-app *$*{*프로젝트 폴더명/.}* --typescript

그러면 아래와 같이 선택지가 나온다.

Tailwind Css 말고 Emotion 쓰려고 No 했고, 다른 건 다 권장해주는 대로 써보고 싶어서 Yes!

App Router는 낯설었는데 좋은 글을 발견하게 되어 써보고 싶어 Yes했고,

import alias는 전에 프로젝트 했을 때 상대경로가 아닌 절대경로가 더 편함을 경험해서 Yes로 해주었다.

✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias? … No / Yes
✔ What import alias would you like configured? … @/*

 

설치가 끝나고 yarn dev 명령어를 실행하면 브라우저에서 작동하는 걸 볼 수 있었다.

yarn dev

 

  1. Eslint & Prettier

.eslintrc.json에 typescript 관한 규칙을 찾아보고 추가했다.

prettier도 설치하고 .prettierrc.json에 규칙을 추가했다.

📎 Reference https://findmypiece.tistory.com/203

 

//.eslintrc.json
{
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "extends": [
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "rules": {
    "@typescript-eslint/no-unused-vars": "error", // 사용되지 않는 변수를 에러로 인식
    "@typescript-eslint/no-explicit-any": "error" // any 타입 정의를 에러로 인식
  }
}
yarn add -D prettier
//.prettierrc.json
{
  "semi": false,
  "trailingComma": "es5",
  "singleQuote": true,
  "tabWidth": 2,
  "useTabs": false
}

  1. emotion

전에 기업 협업에 나갔을 때 사용했던 CSS-in-JS 라이브러리로 theme을 쓸 수 있고

Styled-Components에서 썼던 형태로 쓸 수 있는 점이 좋아 좀 더 친숙하게 사용하고자 적용하게 되었다.

@emotion/styled 는 styled.div형태로 쓸 수 있다고 한다.

 📎 Reference https://velog.io/@godud2604/styled-components-를-emotion-으로-변환하기 https://dev-russel.tistory.com/60

 

yarn add @emotion/styled @emotion/react
//tsconfig.json
{
  "compilerOptions": {
		...
    "jsx": "react-jsx",
    "jsxImportSource": "@emotion/react",
    }
  }

src폴더 안에 styles폴더를 만들고, global.ts, theme.ts, mixin.ts 파일을 만들었다.

 

  1. Next.js + TypeScript

global.ts 파일은 전역으로 설정할 스타일을 담는다.

font와 버튼, 링크, border-box 등 기본 설정되어 있는 부분들을 reset 했다.

theme.ts 파일은 프로젝트에서 쓰는 컬러와 주로 쓸 폰트 크기를 설정했다.

 

global.ts

//global.ts
import { css } from '@emotion/react';

const globalStyle = css`

  * {
    box-sizing: border-box;
		font-size: 16px;
  }

  html,
  body,
  div,
  span,
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p,
  a,
  dl,
  dt,
  dd,
  ol,
  ul,
  li,
  form,
  label,
  table {
    margin: 0;
    padding: 0;
    border: 0;
    vertical-align: baseline;
  }

  body {
    line-height: 1.3;
    background-color: '#FFFFFF';
    margin-bottom: 80px;
  }

  ol,
  ul {
    list-style: none;
  }

  button {
    border: 0;
    background: transparent;
    cursor: pointer;
  }

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

export default globalStyle;

 

 

theme.ts

//theme.ts
import { DefaultTheme } from "@emotion/react";

declare module "@emotion/react" {
  export interface DefaultTheme {
    fontSize: {
      xxs: string;
      sm: string;
      base: string;
      md: string;
      lg: string;
    };
    color: {
      primary_light: string;
      primary_normal: string;
      primary_dark: string;
      //error
      error: string;
      //gray
      gray_50: string;
      gray_100: string;
      gray_200: string;
      gray_300: string;
      gray_400: string;
      gray_500: string;
      gray_600: string;
      gray_700: string;
      gray_800: string;
      gray_900: string;
      //black & white
      black: string;
      white: string;
    };
  }
}

const theme: DefaultTheme = {

  fontSize: {
    xxs: "12px",
    sm: "14px",
    base: "16px",
    md: "18px",
    lg: "24px",
  },

  color: {
    primary_light: "#7C21FF",
    primary_normal: "#6200EE",
    primary_dark: "#4A00B4",

    error: "#ED3124",

    gray_50: "#FAFAFA",
    gray_100: "#F5F5F5",
    gray_200: "#EEEEEE",
    gray_300: "#E0E0E0",
    gray_400: "#BDBDBD",
    gray_500: "#9E9E9E",
    gray_600: "#757575",
    gray_700: "#616161",
    gray_800: "#424242",
    gray_900: "#212121",

    black: "#000000",
    white: "#FFFFFF",
  },
};

export default theme;

  1. @next/font

next app을 세팅했는데 layout.tsx 파일에 next/font/google 라이브러리를 통해 폰트를 가져오는 걸 보게 되었다. @next/font 라는 라이브러리를 통해 폰트를 불러오는 글을 발견해서 따라서 적용해보게 되었다.

<aside> 📎 Reference https://velog.io/@sung-je-kim/NextJS-13v-nextfontgoogle-with-emotionreact

</aside>

yarn add @next/font

디렉토리 구성

styles 디렉토리 안에 fonts디렉토리 안에 index.tsx 와 가져올 폰트에 대한 notoSans.tsx 파일을 만든다. 원하는 폰트 두께만 불러왔다.

Google Fonts(https://fonts.google.com/ )에서 두께 같은 부분을 참고했다.

notoSans.tsx

//notoSans.tsx
import { Noto_Sans_KR } from '@next/font/google'

const bold = Noto_Sans_KR({
  weight: '700',
  display: 'fallback',
  subsets: ['latin'],
  style: 'normal',
  variable: '--noto-sans_KR-bold',
  fallback: ['system-ui'],
})

const regular = Noto_Sans_KR({
  weight: '400',
  display: 'fallback',
  subsets: ['latin'],
  style: 'normal',
  variable: '--noto-sans_KR-regular',
  fallback: ['system-ui'],
})

const extraLight = Noto_Sans_KR({
  weight: '300',
  display: 'fallback',
  subsets: ['latin'],
  style: 'normal',
  variable: '--noto-sans_KR-extraLight',
  fallback: ['system-ui'],
})

export {
  bold as notoSansKRBold,
  regular as notoSansKRRegular,
  extraLight as notoSansKRExtraLight,
}

index.tsx

//index.tsx
import { css } from '@emotion/react'

import {
  notoSansKRBold,
  notoSansKRRegular,
  notoSansKRExtraLight,
} from './notoSans'

const bold = css`
  font-family: ${notoSansKRBold.style.fontFamily};
`

const regular = css`
  font-family: ${notoSansKRRegular.style.fontFamily};
`

const extraLight = css`
  font-family: ${notoSansKRExtraLight.style.fontFamily};
`

export { bold, regular, extraLight }
  1. 공통 style 적용

공통된 style 세팅을 적용하려면 layout.tsx에서 설정한다.

layout.tsx

//layout.tsx
import type { Metadata } from 'next'
import { Global, ThemeProvider } from '@emotion/react'
import globalStyle from '../styles/global'
import theme from '../styles/theme'
import { bold, regular } from '../styles/fonts/index'

export const metadata: Metadata = {
  title: '뮤즈, 뮤지컬 빠른 예매',
  description: '뮤지컬 자리 예매도 복잡한 절차 없이 빠르게 진행할 수 있는 뮤즈',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <>
      <Global styles={globalStyle} />
      <ThemeProvider theme={theme}>
        <html lang="en" css={ bold }>
          <body>{children}</body>
        </html>
      </ThemeProvider>
    </>
  )
}

 

🚨Problem

  1. createContext only works in Client Components. Add the "use client" directive at the top of the file to use it.
  2. /node_modules/@emotion/react/dist/emotion-element

📎 Reference https://yzlosmik.tistory.com/121 https://github.com/emotion-js/emotion/issues/2928

 

초기 세팅이 됐는데 yarn dev할 때 error를 내뿜었다.

처음엔 createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. 에 대해 찾아보니 next.js app router 를 사용할 경우, React 관련 기능을 쓰려면 클라이언트로 컴포넌트로 작성하라는 뜻이라고 한다.

layout.tsx 와 page.tsx 두 파일에 ‘use client’를 써주었다.

'use client'

export default function Home() {
  return ...
}

/node_modules/@emotion/react/dist/emotion-element 관련해서도 파일에 ‘use client’ 와

함께 /** @jsxImportSource @emotion/react */ 를 추가하면 된다고 해서 따라서 써줬는데 혹시나 해서

다시 삭제해봤는데 된다.

 

3. Warning: Extra attributes from the server: data-new-gr-c-s-check-loaded,data-gr-ext-installed

 

 

📎 Reference https://stackoverflow.com/questions/45690202/how-to-have-yarn-not-issue-a-warning-for-the-license-field

 

검색해서 stack overflow 에 나와있는 대로

suppressHydrationWarning={true}를 body태그에 추가하니까 실행이 된다.

//layout.tsx

...
<body suppressHydrationWarning={true}>{children}</body>