나만의 create-next-app을 npm에 배포하기(2)


1. boiler plate 만들기

2. git clone, npm i를 자동으로 수행하는 프로젝트 만들기

3. npm에 배포하기

위의 순서대로 진행해보자.

✨ Boiler plate 만들기

1. npx create-next-app으로 next 프로젝트를 시작하자.

나는 Tailwind를 쓰지 않고 TypeScript, ESlint, App router를 사용했다.

2. ESLint/Prettier 설정하기

항상 프로젝트를 시작할 때 ESLint와 Prettier를 설정하는게 매우 귀찮았다. 미리 설정해놓자.

내가 사용한 패키지는 아래와 같다.

1"devDependencies": {
2    "@typescript-eslint/eslint-plugin": "^7.11.0",
3    "@typescript-eslint/parser": "^7.11.0",
4    "eslint": "^8",
5    "eslint-config-airbnb": "^19.0.4",
6    "eslint-config-airbnb-typescript": "^18.0.0",
7    "eslint-config-next": "14.2.3",
8    "eslint-config-prettier": "^9.1.0",
9    "eslint-plugin-import": "^2.29.1",
10    "eslint-plugin-prettier": "^5.1.3",
11    "prettier": "^3.2.5",
12  }

ESLint, Prettier 설정은 다음과 같다.

1// .eslintrc.json
2{
3  "parserOptions": {
4    "project": "./tsconfig.json"
5  },
6  "plugins": ["@typescript-eslint/eslint-plugin"],
7  "extends": [
8    "airbnb-base",
9    "airbnb-typescript/base",
10    "plugin:@typescript-eslint/recommended",
11    "plugin:prettier/recommended"
12  ],
13  "rules": {
14    "import/extensions": [
15      "error",
16      "ignorePackages",
17      {
18        "": "never"
19      }
20    ],
21    "react/react-in-jsx-scope": "off",
22    "react/jsx-props-no-spreading": "off"
23  }
24}
1// .prettierrc
2{
3  "printWidth": 80,
4  "tabWidth": 2,
5  "useTabs": false,
6  "semi": true,
7  "singleQuote": true,
8  "jsxSingleQuote": true,
9  "trailingComma": "all",
10  "bracketSpacing": true,
11  "proseWrap": "never",
12  "endOfLine": "auto"
13}

3. react-query 설정하기

Next.js + react-query 설정하기 포스트 내용과 같이 설정했다.

4. zustand 설정하기

Next.js + zustand 설정하기 포스트 내용과 같이 설정했다.



위 내용과 같이 프로젝트 초기 설정을 마치고 github에 업로드 했다.

template repository로 설정을 했기 때문에 새 repo를 만들 때 템플릿으로 지정을 하면 기본적으로 위 설정과 똑같은 새 프로젝트가 생성이 된다.

하지만 나는 간지나게? 터미널 명령어 한 줄로 위 설정을 가져오고싶었다.

git clone, npm i를 자동으로 수행하는 프로젝트 만들기

create-next-boilerplate 라는 새 프로젝트 폴더를 만들었다.

1. npm init -y

물어보는 질문 모두 yes!

2. package.json 구성하기

1// package.json
2{
3  "name": "create-next-boilerplate",
4  "version": "1.1.0",
5  "description": "Next.js 14 app router boilerplate with Zustand and React Query",
6  "bin": {
7    "create-next-boilerplate": "./bin/create-next-boilerplate.js"
8  },
9  "keywords": [
10    "nextjs",
11    "zustand",
12    "react-query",
13    "boilerplate",
14    "setup"
15  ],
16  "author": "siyeol97",
17  "license": "MIT",
18  "homepage": "https://github.com/siyeol97/nextjs-boilerplate#readme",
19  "bugs": {
20    "url": "https://github.com/siyeol97/create-next-boilerplate/issues",
21    "repository": {
22      "type": "git",
23      "url": "git+https://github.com/siyeol97/create-next-boilerplate.git"
24    }
25  },
26  "type": "module",
27}

bin 속성을 이용해서 ./bin/create-next-boilerplate.js 파일이 create-next-boilerplate 명령어로 등록되어 사용자는 npx create-next-boilerplate 을 터미널에서 전역 명령어로 실행할 수 있다!

나머지 속성들은 npm 에 등록될 때 정보들을 제공한다.

3. create-next-boilerplate.js 구현하기

사용한 라이브러리는 다음과 같다.

  • inquirer : 터미널에서 사용자 입력을 인터랙티브하게 받을 수 있도록 해준다. '프로젝트 이름'을 입력받을 때 사용했다.
  • simple-git : node 환경에서 git 명령어를 쉽게 실행할 수 있게 해준다. git clone명령어를 실행할 때 사용했다.
  • execa : node 외부 명령어를 실행하고 결과를 쉽게 다룰 수 있게 해준다. npm install 명령어를 실행할 때 사용했다.

전체적인 코드는 다음과 같다.

1// create-next-boilerplate.js
2#!/usr/bin/env node
3
4import inquirer from 'inquirer';
5import simpleGit from 'simple-git';
6import { execa } from 'execa';
7import path from 'path';
8import fs, { rmSync } from 'fs';
9
10const REPO_URL = 'https://github.com/siyeol97/nextjs-boilerplate.git';
11
12async function promptProjectName() {
13  const answers = await inquirer.prompt([
14    {
15      type: 'input',
16      name: 'projectName',
17      message: 'What is your project name?',
18      validate: (input) => (input ? true : 'Project name cannot be empty'),
19    },
20  ]);
21  return answers.projectName;
22}
23
24async function cloneRepo(projectName) {
25  const targetDir = path.join(process.cwd(), projectName);
26  const git = simpleGit();
27
28  if (fs.existsSync(targetDir)) {
29    console.error(
30      'Target directory already exists. Please remove it or choose a different project name.'
31    );
32    process.exit(1);
33  }
34
35  console.log('Cloning repository...');
36  await git.clone(REPO_URL, targetDir);
37  console.log('Repository cloned.');
38
39  const gitDir = path.join(targetDir, '.git');
40  rmSync(gitDir, { recursive: true, force: true });
41  console.log('.git directory removed.');
42
43  const packageJsonPath = path.join(targetDir, 'package.json');
44  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
45  packageJson.name = projectName;
46  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
47
48  console.log('Installing packages...');
49  await execa('npm', ['install'], { cwd: targetDir, stdio: 'inherit' });
50  console.log('Packages installed.');
51}
52
53async function main() {
54  try {
55    const projectName = await promptProjectName();
56    await cloneRepo(projectName);
57  } catch (error) {
58    console.error('Error:', error.message);
59  }
60}
61
62main();

로직은 간단하다.

  1. npx create-next-boilerplate 를 실행하면
  2. 터미널에서 프로젝트 이름을 입력받고
  3. 해당 이름으로 파일을 생성한 후
  4. 앞서 만들어 두었던 boiler plate repository를 clone하고
  5. .git 파일을 삭제한 후 package.json의 name을 입력받은 프로젝트 이름으로 수정한다.
  6. 마지막으로 npm install 명령어를 실행!

마지막 단계인 npm 배포는 다음 포스트에!