CRA는 무엇인가?
CRA는 React 개발을 위한 여러 가지 설정 작업을 미리 해둔 Boilerplate입니다.
CRA를 사용하는 이유는 다음과 같습니다.
1. react 프로젝트를 구성할 때 필요한 eslint, babel, webpack과 같은 종속성을 신경쓰지 않아도 된다.
2. 프로젝트 구성에 필요한 각 패키지들의 설정에 많은 시간을 소요하지 않아도 된다.
3. react 프로젝트 구성에 필요한 많은 양의 패키지를 깔끔하게 react-scripts하나로 퉁칠 수 있다.
하지만 CRA를 사용하다 보면 Webpack이나 babel의 설정을 변경해야 하는 경우들이 생기는데
이를 위해 eject나 customize-cra와 같은 것들을 사용합니다.
CRA의 eject를 사용하지 않는 이유
eject는 CRA의 명령 중 하나인데 사용하게 되면 react-scripts에 숨겨져 있던 명령 스크립트들과 종속성들이 밖으로 추출됩니다.
하지만 eject를 사용하게 되면 추출된 명령과 종속성들을 직접 관리해주어야 하기 때문에 CRA Boilerplate를 사용하는 이유가 사라지게 됩니다.
그래서 보통 customize-cra나 react-app-rewired와 같은 패키지를 사용하여 CRA를 커스텀해 사용합니다.
왜 CRA를 사용하여 Boilerplate를 만드는가?
제가 사용하는 eslint의 설정이나 다른 패키지들의 설정을 위해 매번 customize-cra와 react-app-rewired를 사용하여
커스텀을 하는 것이 불편해 react-scripts를 사용하는 boilerplate를 만들어야겠다! 라고 생각하게 되었습니다.
Github Packages Registry를 사용하여 Global Package를 배포해보자
우선 Github Packages Registry를 사용하여 test를 출력하는 Global Package를 만들어 볼 것입니다.
Github에서 Settings -> Developer Settings -> Personal Access Token에서 write:packages 권한이 있는 Token을 생성합니다.
vi ~/.npmrc 명령을 사용하여 다음과 같이 입력합니다.
@닉네임:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=write:packages권한이 있는 personal access token
이제 스크립트를 작성해야 합니다.
// package.json
{
"name": "@닉네임/create-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"create-app": "./index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/MKachi/create-app.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/MKachi/create-app/issues"
},
"homepage": "https://github.com/MKachi/create-app#readme",
"publishConfig": {
"registry": "https://npm.pkg.github.com/"
}
}
위의 package.json 파일에서 평소에 보지 못하던 "bin"이라는 property가 있는데
해당 property가 해당 패키지를 global 로 설치하였을 때 해당 패키지내의 특정 스크립트를 실행시키는 옵션 입니다.
즉 위의 내용은 create-app을 입력하면 index.js를 실행시키라는 명령이 됩니다.
"publishConfig"의 경우는 npm publish 명령을 사용하였을 때 Github Packages Registry에 배포하도록 하는 설정입니다.
// index.js
#!/usr/bin/env node
console.log('test')
"bin" property를 통하여 실행될 수 있는 스크립트들의 경우 Shebang을 작성하여 node로 해당 스크립트가 실행되어야 한다는 것을 꼭 명시해야합니다.
Shebang에 대한 자세한 것은 https://en.wikipedia.org/wiki/Shebang_(Unix) 여기를 참조해주세요
npm publish
이제 위의 명령을 통해 Github Packages Registry에 배포를 하면
정상적으로 배포된 것을 확인할 수 있습니다.
npx @mkachi/create-app
해당 명령을 사용하면
npx 명령을 사용하여 index.js를 실행시킨 것을 볼 수 있습니다!
이제 진짜 Boilerplate를 만들자
우선 위의 사진처럼 완전히 기본적인 정보들이 들어있는 package.json파일과 설정 파일 & 폴더를 templates폴더에 넣어 프로젝트 내부에 지니도록 하였습니다.
install-list.json에는 해당 프로젝트에 설치해야할 패키지들을 명시하였습니다.
#!/usr/bin/env node
const path = require('path')
const fs = require('fs-extra')
const execSync = require('child_process').execSync
const packageJson = require('./templates/package.json')
const installList = require('./install-list.json')
const createProject = async () => {
const args = process.argv.slice(2)
if (args.length <= 0) {
console.log('프로젝트 이름을 입력해주세요')
console.log('create-app {프로젝트 이름}')
}
const projectName = args[0]
const templatePath = path.resolve(__dirname, 'templates')
const createPath = path.resolve(process.cwd(), projectName)
await fs.copy(templatePath, createPath)
packageJson.name = projectName
await fs.writeFile(path.resolve(createPath, 'package.json'), JSON.stringify(packageJson, null, 2))
let command = 'yarn add '
const dependencies = installList.dependencies
dependencies.map((package) => {
command += `${package} `
})
execSync(command, { cwd: createPath })
command = 'yarn add -D '
const devDependencies = installList.devDependencies
devDependencies.map((package) => {
command += `${package} `
})
execSync(command, { cwd: createPath })
console.log(`${projectName} 프로젝트가 생성되었습니다.`)
}
createProject()
index.js의 내용입니다.
우선 해당 명령을 실행시킨 Directory에 templates의 내용을 복사하고 templates내의 package.json의 name property를 프로젝트의 이름으로 변경 후 새로 생성합니다.
그 뒤 install-list.json에 있는 패키지 정보들을 가져와 yarn으로 패키지 설치를 시도합니다.
프로젝트가 아주 잘 생성되었습니다!