9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Next.jsを利用したプロジェクト構築手順について

Posted at

はじめに

お仕事で何個かNext.jsのプロジェクトを作っていると大体同じような手順を繰り返すことがわかったため手順書に起こしました。
もし良ければ参考・ご指摘頂けると嬉しいです。

セットアップ内容

  • 言語:TypeScript
  • ライブラリ:React
  • フレームワーク:Nextjs
  • UIライブラリ:MUI
  • パッケージマネージャー:npm
  • コードフォーマッター:Prettier
  • リント:ESLint
  • テスト:Jest, React Testing Libraly

Nextjsの初期設定

参考リンク:https://nextjs-ja-translation-docs.vercel.app/docs/getting-started

  • 以下のコマンドを実行する。
    npx create-next-app@latest --typescript
    または
    yarn create next-app --typescript
$ npx create-next-app@latest --typescript
Need to install the following packages:
  create-next-app@12.3.1
Ok to proceed? (y) y
✔ What is your project named? … <Project-name>
Creating a new Next.js app in /home/<Project-name>.

Using yarn.

Installing dependencies:
- react
- react-dom
- next

yarn add v1.22.19
warning ../package.json: No license field
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 17 new dependencies.
info Direct dependencies
├─ next@12.3.1
├─ react-dom@18.2.0
└─ react@18.2.0
info All dependencies
├─ @next/env@12.3.1
├─ @next/swc-linux-x64-gnu@12.3.1
├─ @next/swc-linux-x64-musl@12.3.1
├─ @swc/helpers@0.4.11
├─ caniuse-lite@1.0.30001412
├─ js-tokens@4.0.0
├─ nanoid@3.3.4
├─ next@12.3.1
├─ picocolors@1.0.0
├─ postcss@8.4.14
├─ react-dom@18.2.0
├─ react@18.2.0
├─ scheduler@0.23.0
├─ source-map-js@1.0.2
├─ styled-jsx@5.0.7
├─ tslib@2.4.0
└─ use-sync-external-store@1.2.0
Done in 23.06s.

Installing devDependencies:
- eslint
- eslint-config-next
- typescript
- @types/react
- @types/node
- @types/react-dom

yarn add v1.22.19
warning ../package.json: No license field
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 159 new dependencies.
info Direct dependencies
├─ @types/node@18.7.23
├─ @types/react-dom@18.0.6
├─ @types/react@18.0.21
├─ eslint-config-next@12.3.1
├─ eslint@8.24.0
└─ typescript@4.8.3
info All dependencies
├─ @babel/runtime-corejs3@7.19.1
├─ @babel/runtime@7.19.0
├─ @eslint/eslintrc@1.3.2
├─ @humanwhocodes/config-array@0.10.5
├─ @humanwhocodes/gitignore-to-minimatch@1.0.2
├─ @humanwhocodes/module-importer@1.0.1
├─ @humanwhocodes/object-schema@1.2.1
├─ @next/eslint-plugin-next@12.3.1
├─ @nodelib/fs.scandir@2.1.5
├─ @nodelib/fs.stat@2.0.5
├─ @nodelib/fs.walk@1.2.8
├─ @rushstack/eslint-patch@1.2.0
├─ @types/json5@0.0.29
├─ @types/node@18.7.23
├─ @types/prop-types@15.7.5
├─ @types/react-dom@18.0.6
├─ @types/react@18.0.21
├─ @types/scheduler@0.16.2
├─ @typescript-eslint/parser@5.38.1
├─ @typescript-eslint/scope-manager@5.38.1
├─ @typescript-eslint/typescript-estree@5.38.1
├─ acorn-jsx@5.3.2
├─ acorn@8.8.0
├─ ajv@6.12.6
├─ ansi-regex@5.0.1
├─ ansi-styles@4.3.0
├─ argparse@2.0.1
├─ aria-query@4.2.2
├─ array-union@2.1.0
├─ array.prototype.flat@1.3.0
├─ array.prototype.flatmap@1.3.0
├─ ast-types-flow@0.0.7
├─ axe-core@4.4.3
├─ axobject-query@2.2.0
├─ balanced-match@1.0.2
├─ brace-expansion@1.1.11
├─ braces@3.0.2
├─ callsites@3.1.0
├─ chalk@4.1.2
├─ color-convert@2.0.1
├─ color-name@1.1.4
├─ concat-map@0.0.1
├─ core-js-pure@3.25.3
├─ cross-spawn@7.0.3
├─ csstype@3.1.1
├─ damerau-levenshtein@1.0.8
├─ deep-is@0.1.4
├─ dir-glob@3.0.1
├─ emoji-regex@9.2.2
├─ es-to-primitive@1.2.1
├─ escape-string-regexp@4.0.0
├─ eslint-config-next@12.3.1
├─ eslint-import-resolver-typescript@2.7.1
├─ eslint-module-utils@2.7.4
├─ eslint-plugin-import@2.26.0
├─ eslint-plugin-jsx-a11y@6.6.1
├─ eslint-plugin-react-hooks@4.6.0
├─ eslint-plugin-react@7.31.8
├─ eslint-scope@7.1.1
├─ eslint-utils@3.0.0
├─ eslint@8.24.0
├─ esquery@1.4.0
├─ esrecurse@4.3.0
├─ estraverse@5.3.0
├─ fast-deep-equal@3.1.3
├─ fast-glob@3.2.12
├─ fast-json-stable-stringify@2.1.0
├─ fast-levenshtein@2.0.6
├─ fastq@1.13.0
├─ file-entry-cache@6.0.1
├─ fill-range@7.0.1
├─ find-up@5.0.0
├─ flat-cache@3.0.4
├─ flatted@3.2.7
├─ function.prototype.name@1.1.5
├─ get-symbol-description@1.0.0
├─ glob-parent@6.0.2
├─ glob@7.2.3
├─ grapheme-splitter@1.0.4
├─ has-bigints@1.0.2
├─ has-flag@4.0.0
├─ import-fresh@3.3.0
├─ imurmurhash@0.1.4
├─ is-bigint@1.0.4
├─ is-boolean-object@1.1.2
├─ is-callable@1.2.7
├─ is-date-object@1.0.5
├─ is-extglob@2.1.1
├─ is-negative-zero@2.0.2
├─ is-number-object@1.0.7
├─ is-number@7.0.0
├─ is-shared-array-buffer@1.0.2
├─ is-string@1.0.7
├─ is-symbol@1.0.4
├─ is-weakref@1.0.2
├─ isexe@2.0.0
├─ js-sdsl@4.1.4
├─ json-schema-traverse@0.4.1
├─ json-stable-stringify-without-jsonify@1.0.1
├─ json5@1.0.1
├─ jsx-ast-utils@3.3.3
├─ language-subtag-registry@0.3.22
├─ language-tags@1.0.5
├─ locate-path@6.0.0
├─ lodash.merge@4.6.2
├─ lru-cache@6.0.0
├─ merge2@1.4.1
├─ micromatch@4.0.5
├─ minimist@1.2.6
├─ ms@2.1.2
├─ natural-compare@1.4.0
├─ object-assign@4.1.1
├─ object-inspect@1.12.2
├─ object.assign@4.1.4
├─ object.entries@1.1.5
├─ object.fromentries@2.0.5
├─ object.hasown@1.1.1
├─ optionator@0.9.1
├─ p-limit@3.1.0
├─ p-locate@5.0.0
├─ parent-module@1.0.1
├─ path-exists@4.0.0
├─ path-key@3.1.1
├─ path-type@4.0.0
├─ picomatch@2.3.1
├─ prop-types@15.8.1
├─ punycode@2.1.1
├─ queue-microtask@1.2.3
├─ react-is@16.13.1
├─ regexp.prototype.flags@1.4.3
├─ regexpp@3.2.0
├─ resolve-from@4.0.0
├─ reusify@1.0.4
├─ rimraf@3.0.2
├─ run-parallel@1.2.0
├─ safe-regex-test@1.0.0
├─ shebang-command@2.0.0
├─ shebang-regex@3.0.0
├─ slash@3.0.0
├─ string.prototype.matchall@4.0.7
├─ string.prototype.trimend@1.0.5
├─ string.prototype.trimstart@1.0.5
├─ strip-ansi@6.0.1
├─ strip-bom@3.0.0
├─ strip-json-comments@3.1.1
├─ supports-color@7.2.0
├─ text-table@0.2.0
├─ to-regex-range@5.0.1
├─ tsutils@3.21.0
├─ type-check@0.4.0
├─ type-fest@0.20.2
├─ typescript@4.8.3
├─ unbox-primitive@1.0.2
├─ uri-js@4.4.1
├─ which-boxed-primitive@1.0.2
├─ which@2.0.2
├─ word-wrap@1.2.3
├─ yallist@4.0.0
└─ yocto-queue@0.1.0
Done in 15.86s.

Initialized a git repository.

Success! Created <Project-name> at /home/<Project-name>

(srcディレクトリを作成する場合)

srcディレクトリを作成し、pages、stylesを配下に移動させる。そしてsrc配下にcomponents、utils、typesディレクトリを作成する。

cd <Project-name>
mkdir src && mv pages/ styles/ src/
mkdir {src/components,src/utils,src/types}
touch {src/components/.gitkeep,src/types/.gitkeep}

tsconfigの修正

以下の設定値をcompilerOptionsに追加する。それによりcomponents等のディレクトリのファイルについて、@componentsで指定することが可能。

"baseUrl": ".",
"paths": {
    "@pages/*": ["./src/pages/*"],
    "@styles/*": ["./src/styles/*"],
    "@components/*": ["./src/components/*"],
    "@utils/*": ["./src/utils/*"],
    "@types/*": ["./src/types/*"],
}

package.jsonの修正

デフォルトではscriptsにexportがないため追加が必要。

"export": "next export",

Prettierの設定

  • 以下のコマンドを実行する。
    npm install -D prettier eslint-config-prettier
    または
    yarn add -D prettier eslint-config-prettier
$ npm install -D prettier eslint-config-prettier

added 2 packages, and audited 239 packages in 2s

79 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

コードフォーマットのルールを設定するために.prettierrc.jsonを作成する
設定値はPrettierの公式を参考に設定する
リンク:https://prettier.io/docs/en/options.html

{
  "arrowParens": "avoid",
  "printWidth": 120,
  "singleQuote": true,
  "jsxSingleQuote": true,
  "trailingComma": "all",
  "endOfLine": "auto"
}

Prettierの整形対象を設定

package.jsonのscriptsに以下を追記し、Prettierの整形対象を設定する。

"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",

ESLintの設定ファイルを修正

.eslintrc.jsonにprettierを追記し、ESLintとPrettierが干渉しないようにする。

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

デフォルトではルールが設定されていないため、追加でルールを設定したい場合は.eslintrc.jsonに追記する。

{
  "extends": ["next/core-web-vitals", "plugin:@typescript-eslint/recommended","prettier"],
  "rules": {
    "react/display-name": "off",
    "@next/next/no-img-element": "off",
    "react/no-unescaped-entities": "off",
    "import/no-anonymous-default-export": "off",
    "@typescript-eslint/no-unused-vars": "error",
    "@typescript-eslint/ban-ts-comment": "off",
    "@typescript-eslint/no-explicit-any": "off",
    "@typescript-eslint/no-non-null-assertion": "off",
    // add new line above comment
    "lines-around-comment": [
      "error",
      {
        "beforeLineComment": true,
        "beforeBlockComment": true,
        "allowBlockStart": true,
        "allowClassStart": true,
        "allowObjectStart": true,
        "allowArrayStart": true
      }
    ],
    // add new line above return
    "newline-before-return": "error",
    // add new line below import
    "import/newline-after-import": [
      "error",
      {
        "count": 1
      }
    ],
    "@typescript-eslint/ban-types": [
      "error",
      {
        "extendDefaults": true,
        "types": {
          "{}": false
        }
      }
    ]
  },
  "plugins": [
    "import"
  ],
  "settings": {
    "import/parsers": {
      "@typescript-eslint/parser": [
        ".ts",
        ".tsx"
      ]
    },
    "import/resolver": {
      "typescript": {
        "alwaysTryTypes": true,
        "project": [
          "./tsconfig.json"
        ]
      }
    }
  }
}

ルールを追加したため、対応するパッケージをインストールする。

npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
または
yarn add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin

MUIを追加

リンク:
https://mui.com/material-ui/getting-started/installation/
https://zenn.dev/ttani/articles/next-materialui-setup

  • 以下のコマンドを実行する。
npm install @mui/material @emotion/react @emotion/styled @mui/icons-material
または
yarn add @mui/material @emotion/react @emotion/styled @mui/icons-material

mkdir src/utils/cache
touch src/utils/cache/createEmotionCache.ts

# サイト全体のthemeを変更する場合
mkdir src/theme
touch src/theme/theme.ts

キャッシュ関連の共通処理を追加

キャッシュ関連の共通処理を追加する。そのためにsrc/utils/cacheディレクトリを作成し、createEmotionCache.tsを作成する。

import createCache from '@emotion/cache';

export const createEmotionCache = () => {
  return createCache({ key: 'css', prepend: true })
}

サイト全体のthemeを変更する

サイト全体のthemeを変更する場合、src/themeディレクトリを作成し、theme.tsを作成する。

import { createTheme } from '@mui/material/styles';
// Create a theme instance.
const theme = createTheme({
palette: {
   primary: {
      main: '#556cd6',
   },
   secondary: {
     main: '#19857b',
   },
   error: {
   main: #d32f2f,
   },
  },
});
export default theme;

_app.tsxを変更する

_app.tsxを変更し、createEmotnionCacheとthemeを読み込むように変更する。
また、stylesが不要となったため、削除する。

import type { AppProps } from 'next/app';
import Head from 'next/head';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { CacheProvider, EmotionCache } from '@emotion/react';
import theme from '../theme/theme';
import { createEmotionCache } from '@utils/cache/createEmotionCache';

// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();
interface MyAppProps extends AppProps {
  emotionCache?: EmotionCache;
}

function MyApp(props: MyAppProps) {
  const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;

  return (
    <CacheProvider value={emotionCache}>
      <Head>
        <meta name='viewport' content='initial-scale=1, width=device-width' />
      </Head>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <Component {...pageProps} />
      </ThemeProvider>
    </CacheProvider>
  );
}

export default MyApp;

pages/index.tsxを書き換える

import type { NextPage } from 'next'
import { Button } from '@mui/material';

const Home: NextPage = () => {
  return (
    <>
      <Button variant="contained">Hello World</Button>
    </>
  )
}

export default Home

Jestの設定

リンク:https://nextjs.org/docs/testing#jest-and-react-testing-library

  • 以下のコマンドを実行する。
    npm i -D jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom
    または
    yarn add -D jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom

  • package.jsonのscriptへの追加

"test": "jest --watch"
  • jest.config.jsを作成
// eslint-disable-next-line @typescript-eslint/no-var-requires
const nextJest = require('next/jest');

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

// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
module.exports = createJestConfig({
  moduleDirectories: ['node_modules', '<rootDir>/'],
  moduleNameMapper: {
    '^@components/(.*)$': '<rootDir>/src/components/$1',
    '^@pages/(.*)$': '<rootDir>/src/pages/$1',
    '^@utils/(.*)$': '<rootDir>/src/utils/$1',
  },
  testEnvironment: 'jest-environment-jsdom',
});

src配下に__test__ディレクトリを作成し、テストファイルを用意すればテストが可能となる。

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?