[plopjs] plop을 통한 코드 생성 자동화 (React)
1. 코드 생성 자동화의 필요성
리액트 컴포넌트를 개발한다고 합시다. 컴포넌트의 이름을 정하고, index.tsx, index.module.scss, stories.tsx를 만듭니다.
그리고 항상 똑같은 코드를 작성합니다.
컴포넌트의 이름만 바뀌고, 같은 코드가 반복됩니다.
// index.tsx
import classNames from 'classnames/bind';
import styles from './styles.module.scss';
const cx = classNames.bind(styles);
interface Props {}
export const Component = ({}: Props) => {
return <></>
};
// index.stories.tsx
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { Component } from '.';
export default {
title: '',
component: Component,
} as Meta<typeof Component>;
export const 기본: StoryObj<typeof Component> = {
name: 'Component',
args: {},
};
// styles.module.scss
...
뭐 이런 거까지 자동화를 해? 별다자(별걸 다 자동화 하네)라고 생각하실 수 있지만, 생각보다 절약되는 시간이 많습니다.
또한, 많은 작업자들이 관여하는 모노레포의 경우 일관된 코드 스타일을 구축하는 데에 도움을 줄 수 있습니다.
무엇보다 규모가 거대한 모노레포의 특징을 고려했을 때, 내가 생성할 컴포넌트의 적절한 위치를 찾는 것 또한 피곤한 과정이 됩니다.
plopjs가 이런 작업을 위해 존재합니다.
2. plopjs를 활용한 코드 생성 자동화
Plop: Consistency Made Simple
A little tool that saves you time and helps your team build new files with consistency. Generate code when you want, how you want.
plopjs.com
Plop is what I like to call a "micro-generator framework."
plop은 일관된 형식으로 코드를 생성할 수 있게 하는 프레임워크입니다.
위의 리액트 컴포넌트 생성 과정을 plop으로 자동화 해볼까요?
자동화를 위해 필요한 정보는 2가지 입니다.
- 컴포넌트의 이름
- 컴포넌트가 생성될 폴더
이 두 가지 정보로 컴포넌트를 자동 생성해보겠습니다.
2.1 plop 셋업
$ pnpm install --save-dev plop
plop을 설치하고, 프로젝트의 루트 디렉토리에 propfile.mjs 파일을 생성합니다.
import { execSync } from 'child_process';
export default function (plop) {
plop.setGenerator('component', {
description: '컴포넌트를 생성합니다.',
prompts: [
{
type: 'input',
name: 'name',
message: '컴포넌트 이름을 입력하세요:',
},
{
type: 'input',
nane: 'path',
message: '컴포넌트 폴더가 생성될 경로를 입력하세요:',
},
],
actions: () => {
const actions = [
{
type: 'addMany',
destination: '{{path}}/{{name}}',
base: 'plop-templates',
templateFiles: 'plop-templates/*.hbs',
abortOnFail: true,
},
function formatGeneratedFiles(answers) {
try {
const generatedDir = `${answers.path}/${answers.name}`;
console.log(`코드 스타일을 정리합니다: ${generatedDir}`);
execSync(`eslint --fix '${generatedDir}' | prettier --write '${generatedDir}'`);
return '코드 스타일 정리가 완료되었습니다.';
} catch (error) {
return '코드 포맷팅을 건너뛰었습니다.';
}
},
];
return actions;
},
});
}
이제 생성할 파일들의 포맷을 정의할 plop-templates 폴더를 루트에 만들고, 아래 파일들을 만듭니다.
// index.tsx.hbs
import classNames from 'classnames/bind';
import styles from './styles.module.scss';
const cx = classNames.bind(styles);
interface Props {}
export const {{pascalCase name}} = ({}: Props) => {
return <>This is {{pascalCase name}}</>
};
// index.stories.tsx.hbs
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { {{pascalCase name}} } from '.';
export default {
title: '',
component: {{pascalCase name}},
} as Meta<typeof {{pascalCase name}}>;
export const 기본: StoryObj<typeof {{pascalCase name}}> = {
name: '{{pascalCase name}}',
args: {},
};
// styles.module.scss.hbs
...
마지막으로 package.json에 plop용 스크립트를 추가합니다.
"scripts": {
"plop": "plop"
},
이제 CLI에 pnpm plop을 입력해서 컴포넌트를 생성하면 됩니다.
$ pnpm plop
> 컴포넌트 이름을 입력하세요: installment-chip
> 컴포넌트 폴더가 생성될 경로를 입력하세요: src/app/main/_component