개탕 IT FACTORY

Nextjs13 BoilerPlate제작 (Nextjs13, tailwindCSS, emotion, twin.macro, Jest, React-query) - 이슈사항 추가 본문

Front-end/Nextjs

Nextjs13 BoilerPlate제작 (Nextjs13, tailwindCSS, emotion, twin.macro, Jest, React-query) - 이슈사항 추가

rendar02 2023. 5. 16. 23:41
반응형

개요

프로젝트를 진행할때마다 CRA를 활용한 어플개발을 많이 하였다. 일단 환경 구축등의 시간 단축이 좋다는 이유로 많이 사용하였지만, CRA의 단점은 커스텀이 어렵다는 부분이 있다.

물론 npm run eject을 통해서 webpack설정을 바꿀순 있지만, 기존에 지원하던 좋은 장점을 버리는 거라 추천은 하지 않는다 (사실 직접 webpack으로 골라 만드는게 좋을듯…)

그래서 SSR, SSG의 장점이 가진 Nextjs를 나만의 boilerplate로 제작해볼까 한다.

준비사항

일단 어떤 요소를 넣어야 하는지에 대한 부분이 필요했다.

  1. 필수요소
    1. typescript
    2. ESLint
    3. Prettier
    4. React-testing-library, jest
    5. styled-components
    6. tailwindCSS
    7. 데이터페칭

위 필수요소를 기반으로 다져볼까한다. 무엇보다 tailwindCSS와 styled-components를 동시에 사용하는 Twin Macro 를 사용하여 요즘 인기좋은 tailwind를 적용해 볼까 한다.

제작 과정

당연히 필수인 Node.js의 경우 16.10 버전을 사용하도록한다.

본 글에서는 npm이 아닌 yarn으로 설명 할 것 입니다.

nextjs 최신버전 13 (작성일 기준으로)

npx create-next-app@latest

위 사진처럼

  • 프로젝트 이름
  • Typescript 사용여부
  • ESLint 사용 여부
  • Tailwind CSS 사용 여부
  • src/ 폴더를 사용할 것인지 여부
  • App Router를 사용할 것인지 여부 ⇒ 13부터 App 폴더 라우터도 사용 가능 한 것 같다.

기존에는 따로 설정해야되는 것들이 모두 통합적으로 설치할수 있도록 바뀌었다.

⇒ 엄청 꿀이 아닌가… vercel 찬양해요!

typescript를 쓸 예정이니 type정의 모듈도 설치해주도록하자

yarn add @types/node @types/react @types/react-dom @types/styled-components

확인결과 styled-components type빼고는 모두 설치된다… 갓 vercel

<aside> 💡 아래 import order부분은 꼭 필요한 부분은 아닙니다. import를 자동으로 order시켜주는 기능

</aside>

ESlint 설정

일단 Lint가 설치되었으니 lint설정을 하기로 한다.

일단 기본 모듈은 설치되었으나 추가적인 모듈 설치를 위해서 설치

yarn add -D eslint eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks @trivago/prettier-plugin-sort-imports @typescript-eslint/eslint-plugin @typescript-eslint/parser
{
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  // "parser": "@babel/eslint-parser",
  "parser": "@typescript-eslint/parser",
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "airbnb",
    "plugin:prettier/recommended",
    "next/core-web-vitals"
  ],
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": [
    "react",
    "react-hooks",
    "@typescript-eslint",
    "prettier",
    "import"
  ],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react/jsx-no-useless-fragment": 0,
    "import/order": 0,
    "import/prefer-default-export": "off",
    "import/extensions": 0,
    "import/no-unresolved": 0,
    // "arrow-body-style": "error",
    "prefer-arrow-callback": "off",
    "no-var": "error",
    "no-dupe-keys": "error",
    "react/prop-types": "off",
    "react/function-component-definition": [
      2,
      {
        "namedComponents": "arrow-function"
      }
    ],
    "@typescript-eslint/no-explicit-any": "error",
    "react/jsx-props-no-spreading": "off",
    "react/react-in-jsx-scope": 0,
    "react/prefer-stateless-function": 0,
    "react/jsx-filename-extension": 0,
    "react/jsx-one-expression-per-line": 0,
    "no-nested-ternary": 0
  },
  "globals": {
    "React": "writable"
  }
}

Lint 설정의 경우

기존 프로젝트와 다른 boilerplate 설정을 참고하여서 만들었다

prettier 설정

yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
module.exports = {
  printWidth: 80, //  줄 바꿈 할 폭 길이
  tabWidth: 2, // 탭 너비
  useTabs: false, // 탭 사용 여부
  semi: true, // 세미콜론 사용 여부
  singleQuote: true, // single 쿼테이션 사용 여부
  quoteProps: 'as-needed', // 객체 속성에 쿼테이션 적용 방식
  trailingComma: 'es5', // 여러 줄을 사용할 때, 후행 콤마 사용 방식
  bracketSpacing: true, // 객체 리터럴에서 괄호에 공백 삽입 여부
  arrowParens: 'avoid', // 화살표 함수 괄호 사용 방식
  proseWrap: 'preserve',
  endOfLine: 'auto', // EoF 방식, OS별로 처리 방식이 다름
  htmlWhitespaceSensitivity: 'css', // HTML 공백 감도 설정
  embeddedLanguageFormatting: 'off',
  importOrder: [
    '^@layouts/(.*)$',
    '^@api/(.*)$',
    '^@ui/(.*)$',
    '^@lib/(.*)$',
    '^@types/(.*)$',
    '^@states/(.*)$',
    '^@utils/(.*)$',
    '^@modules/(.*)$',
    '^@hooks/(.*)$',
    '^@components/(.*)$',
    '^@styles/(.*)$',
    '^[./]',
  ],
  importOrderSeparation: true,
  importOrderSortSpecifiers: true,
};

tsconfig.json

typescript 사용을 위한 파일

{
  "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": {
      "@/*": [
        "./*"
      ],
      "@/component/*": [
        "components/*"
      ],
      "@/types/*": [
        "types/*"
      ],
      "@/pages/*": [
        "pages/*"
      ],
      "@/styles/*": [
        "styles/*"
      ],
      "@/api/*": [
        "api/*"
      ],
      "@/modules/*": [
        "modules/*"
      ],
      "@/hooks/*": [
        "hooks/*"
      ],
      "@/utils/*": [
        "utils/*"
      ],
      "@/layouts/*": [
        "layouts/*"
      ],
      "@/store/*": [
        "store/*"
      ]
    },
    "plugins": [
      {
        "name": "next"
      }
    ]
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".next/types/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

테스트코드

테스트코드의 경우 CRA의 경우 jest, React-testing-library가 설치된 채로 오는데 Nextjs의경우 따로 설정이 있거나 템플릿이 있다고 한다. 공식문서에 자세히 나와있으니 참고바란다.

당연히 쉬운 방법은 제공되는 템플릿으로 자동완성 시켜주는 코드이다

npx create-next-app@latest --example with-jest with-jest-app

하지만 필자는 직접 설치하도록한다.

jest에 필요한 것들을 설치해주자

yarn add --dev jest jest-dom jest-environment-jsdom ts-jest babel-jest 

다음은 testing-library

yarn add --dev @testing-library/user-event @testing-library/react-hooks @testing-library/react @testing-library/jest-dom @testing-library/dom

package.json 파일에 옵션 추가

"test": "jest",

jest관련된 설정을 해주자

  • jest.setup.js
import '@testing-library/jest-dom/extend-expect'
  • jest.config.js
    • 12버전부터는 jest를 기본으로 제공하고있다.
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  dir: './',
})

// Add any custom config to be passed to Jest
const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
	moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
		'^@layouts/(.*)$': '<rootDir>/src/layouts/$1',
    '^@components/(.*)$': '<rootDir>/src/components/$1',
    '^@pages/(.*)$': '<rootDir>/src/pages/$1',
    '^@api/(.*)$': '<rootDir>/src/api/$1',
    '^@ui/(.*)$': '<rootDir>/src/ui/$1',
    '^@lib/(.*)$': '<rootDir>/src/lib/$1',
    '^@types/(.*)$': '<rootDir>/src/types/$1',
    '^@states/(.*)$': '<rootDir>/src/states/$1',
    '^@utils/(.*)$': '<rootDir>/src/utils/$1',
    '^@modules/(.*)$': '<rootDir>/src/modules/$1',
    '^@hooks/(.*)$': '<rootDir>/src/hooks/$1',
    '^@styles/(.*)$': '<rootDir>/src/styles/$1',
  },
  testEnvironment: 'jest-environment-jsdom',
}

module.exports = createJestConfig(customJestConfig)

이제 루트 디렉터리에 __tests__폴더를 만들어서 테스트 코드를 넣으면 된다.

간단한 테스트 코드를 추가해놨다

index.test.tsx

import Home from '@/app/page';
import { render, screen } from '@testing-library/react';

describe('<Home />', () => {
  it('renders a heading', () => {
    const { container } = render(<Home />);

    const home = screen.getByText('app/page.tsx');

    expect(home).toBeInTheDocument();
    expect(container).toMatchSnapshot();
  });
});

잘되는걸 확인할수 있다.

스타일 관련 (tailwindCSS, styled-components) → 업데이트 예정

 ⚠️ nextjs에서 emotion을 아직 지원하지 않는 것 같다. 무엇보다 두 라이브러리의 장점을 합친 twin.macro를 쓸려구 했으나 이마저도 아직 app 디렉토리는 지원이 안되는것 같다 😭

 

스타일 관련된 부분의 경우 tailwindCSS와 styled-components의 장점을 합친 Twin-macro를 사용 할려구 한다.

일단 기본적인 styled-components 사용을 위해 emotion을 설치해보자

yarn add @emotion/react @emotion/styled

tailwindCSS의 경우 기본 템플릿에서 설치를 하였으나 따로 구성을할려면 설치하면된다

yarn add tailwindcss

이슈사항

  • Nextjs13에서 app directory에 대한 emotion 지원이 아직인거 같다.
    • vercel측에선 안정적이라곤 하지만 아직 라이브러리에선 지원을 기다려야 될 것 같다.
    • 검색이 부족한걸수도 있어서 찾아보구 있긴한데 emotion에 관련된 이슈가 발견된거 같다
    • 무엇보다 서버컴포넌트와 클라이언트 컴포넌트에 대한 지식 부족도 한가지 사유인거 같다.
  • 두번째는 Nextjs12부터 지원했던 swc에 대한 부분인데 아직 완벽 지원이 어려운거 같다.
    • twin.macro등의 바벨 설정등의 이슈사항이 아직 존재하는거 같다.
    • twin.macro에서도 styled-componets 관련된 이슈가 존재하는거 같다.
  • 일단 이슈사항에 대해선 지속적으로 트래킹하면서 업데이트 해야될거 같다.
    • 일단 tailwindCSS가 잘작동되는데 의의를 둔다.

추가적으로 Twin-macro를 설치해봅시다

yarn add twin.macro babel-loader babel-plugin-macros @babel/plugin-syntax-typescript @babel/preset-react

그리고 nextjs가 babel이 아닌 swc로 컴파일되니 babel설정을 해줄 js파일을 만들어준다

const path = require("path");

const includedDirs = [
  path.resolve(__dirname, "components"),
  path.resolve(__dirname, "pages"),
  path.resolve(__dirname, "styles"),
];

module.exports = function withTwin(nextConfig) {
  return {
    ...nextConfig,
    webpack(config, options) {
      const { dev, isServer } = options;
      config.module = config.module || {};
      config.module.rules = config.module.rules || [];
      config.module.rules.push({
        test: /\\.(tsx|ts)$/,
        include: includedDirs,
        use: [
          options.defaultLoaders.babel,
          {
            loader: "babel-loader",
            options: {
              sourceMaps: dev,
              presets: [
                [
                  "@babel/preset-react",
                  { runtime: "automatic", importSource: "@emotion/react" },
                ],
              ],
              plugins: [
                require.resolve("babel-plugin-macros"),
                require.resolve("@emotion/babel-plugin"),
                [
                  require.resolve("@babel/plugin-syntax-typescript"),
                  { isTSX: true },
                ],
              ],
            },
          },
        ],
      });

      if (!isServer) {
        config.resolve.fallback = {
          ...(config.resolve.fallback || {}),
          fs: false,
          module: false,
          path: false,
          os: false,
          crypto: false,
        };
      }

      if (typeof nextConfig.webpack === "function") {
        return nextConfig.webpack(config, options);
      } else {
        return config;
      }
    },
  };
};

다음엔 next.config.js 파일을 변경한다

const withTwin = require("./withTwin");

const nextConfig = withTwin({});

module.exports = nextConfig;

그리고 타입 정의를 위한 tsconfig.json파일을 정의한다

//tsconfig.json
{
  ...,
  "types": [
    "@types"
  ]
}

twin.macro타입을 정의해준다

@types/twin.d.ts

import "twin.macro";
import { css as cssImport } from "@emotion/react";
import styledImport from "@emotion/styled";
import { CSSInterpolation } from "@emotion/serialize";

// `twin.macro`에 다음 타입을 넣음
declare module "twin.macro" {
  const styled: typeof styledImport;
  const css: typeof cssImport;
}

// DOM의 attribute에 다음 타입을 넣음
declare module "react" {
  interface DOMAttributes<T> {
    tw?: string;
    css?: CSSInterpolation;
  }
}

결과는?

매우잘 되는걸 확인할수 있었다.

이로써 스타일 설정도 마무리가 되었다 마무리단계는 데이터 페칭관련된 부분으로 마무리를 해볼까한다.

스타일의 경우 아래 게시글을 참고하여서 썻다.

https://www.inflearn.com/blogs/2858

데이터 페칭(추가예정)

데이터 페칭의경우 axios로 데이터를 받아오고 React-query로 상태관리를 해볼까한다.

  1. axios설치
    • 데이터 페치를 위한 라이브러리 axios를 설치해준다
    yarn add axios
    
  2. React-query 설치
    yarn add @tanstack/react-query
    # devtool 사용시
    yarn add @tanstack/react-query-devtools
    
    1. ReactQueryProvider.tsx 제작
    2. layout.tsx에 관련된 부분 삽입

결론

  • 보일러 플레이트 제작은 상당히 어렵다는 것을 알게되었다
  • 삽질하다가 느낀것이지만, 새로운 기술을 빠르게 습득하는것도 능력이라구 생각이든다…
    • emotion관련하여서 얼마나 삽질을 한지 ㅠ
  • 아직 미완성인 보일러 플레이트이다… 지속적으로 업데이트 예정
    • 일단 app directory 구조로 만들었지만 브랜치따서 따로 page directory 구조로 만들어야될것같다.
    • vercel에서 app directory가 stable하다곤 하지만, 라이브러리 지원은 아직인거 같다.

https://github.com/RendarCP/rendar-nextjs-starter

 

반응형