๐Ÿ“ฆ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ƒ์„ฑ๊ธฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž

๐Ÿ“ฆ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ƒ์„ฑ๊ธฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž

์ตœ์ข… ํŽธ์ง‘ ์ผ์‹œ
Last updated June 22, 2022
ํƒœ๊ทธ
Javascript
๋ฐฐํฌ์ผ์ž
Mar 1, 2021
์ž‘์—…ํ•˜๋Š” ํ™˜๊ฒฝ์ด ์–ด๋””๊ฑด ์ž์ฃผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ํ™˜๊ฒฝ์„ค์ •ํ•˜๋Š”๋ฐ ์˜ค๋ž˜๊ฑธ๋ฆฌ๋Š” ์ฝ”๋“œ๋“ค์ด ์กด์žฌํ•œ๋‹ค. ๋‚˜์˜ ๊ฒฝ์šฐ๋Š” ์›นํŒฉ์ด ๊ทธ๋žฌ๋‹ค.
ํ•˜๋‚˜์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ ํ•œ๋ฒˆ ์„ค์ •ํ•˜๋ฉด ๊ทธ์™ธ์—๋Š” ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„์ด์ง€๋งŒ, ๊ฐ„๋‹จํ•˜๊ฒŒ JS๋‚˜ TS ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์„œ ๋Œ๋ ค๋ณด๊ณ  ์‹ถ์€๋ฐ ํฌ๋กฌ๊ฐœ๋ฐœ์ž๋„๊ตฌ ์ฝ˜์†”์ฐฝ์—์„œ๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์„ ๋•Œ๋งˆ๋‹ค ์˜ˆ์ „ ์›นํŒฉ ์„ค์ •์„ ํด๋ก ํ•ด์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์ฝ”๋“œ๋ฅผ ์ผ์ผํžˆ ๋ณต๋ถ™ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณคํ–ˆ๋‹ค.
์š”๋Ÿฐ ๋ฐฉ์‹์€ ๋„ˆ๋ฌด ๋น„ํšจ์œจ์ ์ด๊ณ , ์•ž์œผ๋กœ๋„ ์ด๋Ÿฐ ๊ฒฝ์šฐ๊ฐ€ ์ž์ฃผ ์ƒ๊ธธ ๊ฒƒ ๊ฐ™์•„์„œ ์ง์ ‘ย ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ƒ์„ฑ๊ธฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ธฐ๋กœ ํ•˜์˜€๋‹ค!!

1. ํ”„๋กœ์ ํŠธ ์„ค์ •

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋Š” ์ด๋ ‡๊ฒŒ ์„ค์ •ํ–ˆ๋‹ค.
notion image
  • bin : ํ”„๋กœ์ ํŠธ ์„ค์น˜, ์‹คํ–‰์‹œ ์‹คํ–‰๋  ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋“ค์–ด๊ฐˆ ๋””๋ ‰ํ† ๋ฆฌ
  • dist : ๋นŒ๋“œ๋œ ํ”„๋กœ๊ทธ๋žจ ๋””๋ ‰ํ† ๋ฆฌ
  • lib : ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๋“ค
  • src : ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ƒ์„ฑ๊ธฐ ์†Œ์Šค
๋จผ์ € package.json์˜ย binย ์—, ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•  ๋•Œ, ์‹คํ–‰๋  ์Šคํฌ๋ฆฝํŠธ ๊ฒฝ๋กœ๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.
notion image
๊ทธ๋ฆฌ๊ณ ย runย ์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ์‹ค์ œ ์‹คํ–‰๋  ํŒŒ์ผ์„ ์‹คํ–‰์‹œ์ผœ์ฃผ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์—ˆ๋‹ค.
#!/usr/bin/env node require("../dist")().run();
์ด ๋•Œ,ย runย ์Šคํฌ๋ฆฝํŠธ๋Š” ์–ด๋– ํ•œ ์‚ฌ์šฉ์ž๋„ ๋ชจ๋‘ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ถŒํ•œ์„ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ๋‹ค. (๊ถŒํ•œ ์„ค์ •์„ ํ•˜์ง€ ์•Š์„๊ฒฝ์šฐ, ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์‹œ, permission ์˜ค๋ฅ˜๊ฐ€ ๋œฌ๋‹ค.)
chmod 755 run chmod 755 run.cmd
๊ทธ๋ฆฌ๊ณ  typescript์™€ directory๋ฅผ ๋ณต์‚ฌํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œย fs-extraย ํŒจํ‚ค์ง€, ์˜ˆ์œ ํ„ฐ๋ฏธ๋„์ž…์ถœ๋ ฅ์„ ๋„์™€์ค„ย terminal-kitย ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€๋กœ ์„ค์น˜ํ•ด์ฃผ์—ˆ๋‹ค.
notion image

2. index.ts

์—”ํŠธ๋ฆฌํฌ์ธํŠธ์ธย index.tsย ์—์„œ๋Š” run ๋ฉ”์†Œ๋“œ๋งŒ ๊ฐ€์ง€๋Š” ๋ชจ๋“ˆ์˜ ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•ด์ฃผ์—ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ๋ณ€๊ฒฝ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ(๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ •๋ณด, ์ž…์ถœ๋ ฅ, ์—๋Ÿฌ๋ฉ”์‹œ์ง€ ๋“ฑ)์€ ๋ชจ๋‘ ๋”ฐ๋กœ ๋นผ๊ณ , ๋ฐ์ดํ„ฐ๋ฅผย createPromptย ๋ผ๋Š” ํ•จ์ˆ˜์— ์ „๋‹ฌํ•˜๋Š” ์—ญํ• ๋งŒ ์ฃผ์—ˆ๋‹ค.
const run = () => { const selectItemMap: SelectItemMap = new Map(selectItems) const options: PromptOptions = { DEFAULT_DEST_DIR_NAME, QUESTION_MESSAGE1, QUESTION_MESSAGE2, } createPrompt(selectItemMap, options) } module.exports = () => { return { run, } }
๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ์ˆœ์„œ๋ฅผ ๊ฐ–๋Š” ๊ฐ์ฒด์˜ ํ˜•ํƒœ๋ฅผ ์œ„ํ•ดย Mapย ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

3. data.ts

๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ •๋ณด๋Š” type, dirName, description ์„ธ๊ฐœ์˜ key๋ฅผ ๊ฐ–๊ฒŒ ํ•˜์˜€๋‹ค.
  • type : ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ์ธ์ง€, quit์ธ์ง€์— ๋Œ€ํ•œ ์ •๋ณด (โ€˜boiler-plateโ€™ | โ€˜quitโ€™)
  • dirName : ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ์˜ ๊ฒฝ์šฐ ๋””๋ ‰ํ† ๋ฆฌ์ด๋ฆ„
  • description : ํ„ฐ๋ฏธ๋„์— ์ถœ๋ ฅ๋  ๋ฌธ์ž์—ด
๊ทธ๋ฆฌ๊ณ  ๊ทธ ์™ธ ์ž…์ถœ๋ ฅ๋ฉ”์‹œ์ง€๋‚˜, ๋งŒ๋“ค์–ด์งˆ default ๋””๋ ‰ํ† ๋ฆฌ๋ช…, ์—๋Ÿฌ๋ฉ”์‹œ์ง€ ๋“ฑ๋„ ์ ์–ด์ฃผ์—ˆ๋‹ค.
export const selectItems: SelectItems = [ [ 0, { type: 'boiler-plate', dirName: 'ts-webpack', description: '- TypeScript + Webpack', }, ], [ 1, { type: 'boiler-plate', dirName: 'js-webpack', description: '- JavaScript + Webpack', }, ], [ 2, { type: 'quit', description: '- quit', }, ], ] export const defaultDestDirName = 'my-app' export const QUESTION_MESSAGE1 = '์ƒ์„ฑํ•  ํ”„๋กœ์ ํŠธ๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”(default : my-app, ํ˜„์žฌ์œ„์น˜: . ) > ' export const QUESTION_MESSAGE2 = '\n\n์ƒ์„ฑํ•  ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.\n' export const SUCCESS_MESSAGE = '\n์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!\n' export const FAILURE_MESSAGE = '\n์•„๋ฌด์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!\n' export const QUIT_MESSAGE = '\n์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!\n' export const EXIST_DEST_ERROR_MESSAGE = '\n๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.\n"' export const EXIST_TARGET_ERROR_MESSAGE = '\nํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ์— ํŒŒ์ผ๋“ค์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค\n'
ํ”„๋กœ๊ทธ๋žจ ๋กœ์ง๊ณผ ๋ฐ์ดํ„ฐ(์™€ ์ƒ์ˆ˜๋“ค)์€ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์„œ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•ด์ฃผ์—ˆ๋‹ค.

4. types.ts

types.ts์—์„œ๋Š” ๋ฐ์ดํ„ฐ์— ๋“ค์–ด๊ฐˆ QuitSelectItem (quit ์ •๋ณด), BoilerSelectItem (๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ •๋ณด) ์™€ options์— ๋“ค์–ด๊ฐˆ ์ •๋ณด์— ๋Œ€ํ•œ ํƒ€์ž…์„ ์ง€์ •ํ•ด์ฃผ์—ˆ๋‹ค.
interface QuitSelectItem { type: 'quit' description: string } interface BoilerSelectItem { type: 'boiler-plate' dirName: string description: string } export type SelectItems = Array<[number, QuitSelectItem | BoilerSelectItem]> export type SelectItemMap = Map<number, QuitSelectItem | BoilerSelectItem> export interface PromptOptions { defaultDestDirName?: string QUESTION_MESSAGE1?: string QUESTION_MESSAGE2?: string SUCCESS_MESSAGE?: string FAILURE_MESSAGE?: string QUIT_MESSAGE?: string EXIST_DEST_ERROR_MESSAGE?: string EXIST_TARGET_ERROR_MESSAGE?: string } export type DefaultPromptOptions = Required<PromptOptions>
์‚ฌ์‹ค type์ด ๊ผญ ํ•„์š”ํ•œ ํ”„๋กœ๊ทธ๋žจ์€ ์•„๋‹์ˆ˜๋„ ์žˆ์ง€๋งŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๊ณต๋ถ€๊ฒธ ๊ฒธ์‚ฌ๊ฒธ์‚ฌ ํƒ€์ž…๋“ค๋„ ์ง€์ •ํ•ด์ฃผ์—ˆ๋‹ค. ใ…‹ใ…‹; ๊ทธ๋ž˜๋„ ํƒ€์ž…์„ ์ง€์ •ํ•ด์ฃผ๋‹ˆ๊นŒ ์˜คํžˆ๋ ค ์ž‘์—…ํ•˜๋ฉด์„œ๋„ ์–ด๋–ค ์ธํ„ฐํŽ˜์ด์Šค์ธ์ง€ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•ด์„œ ๊ฐœ๋ฐœ๋„ ๋น ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ๊ณ  ์ข€๋” ์•ˆ์ •์ ์ผ ๊ฒƒ์ด๋ผ๋Š” ์ƒ๊ฐ์— ํ”„๋กœ๊ทธ๋žจ์— ์• ์ •๋„ ๋” ์ƒ๊ฒผ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

5. prompt.ts

prompt.tsย ์—์„œ๋Š” ์‹ค์ œ ์ƒ์„ฑ๊ธฐ์—์„œ ๋™์ž‘ํ•  ๋‚ด์šฉ๋“ค์„ ๋‹ด์•„์ฃผ์—ˆ๋‹ค.
๋จผ์ € ์ธ์ž๋กœ ๋„˜์–ด์˜จ options์— ํ‚ค๊ฐ’์ด ์—†์„ ๊ฒฝ์šฐ์—๋Š” default๋กœ ์ ์šฉํ•˜๊ณ  ํ‚ค๊ฐ’์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ํ‚ค๊ฐ’์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.
const { defaultDestDirName, QUESTION_MESSAGE1, QUESTION_MESSAGE2, SUCCESS_MESSAGE, FAILURE_MESSAGE, QUIT_MESSAGE, EXIST_DEST_ERROR_MESSAGE, EXIST_TARGET_ERROR_MESSAGE, } = { ...defaultOptions, ...options }
๊ทธ๋ฆฌ๊ณ ย getDestDirNameย ํ•จ์ˆ˜์™€ย getSelectItemTypeย ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž…๋ ฅ์„ ๋ฐ›๊ณ 
term.cyan(QUESTION_MESSAGE1) const destDirName = await getDestDirName(defaultDestDirName) const selectItemValues = selectItemMap.values() const descriptions = Array.from(selectItemValues).map( value => value.description ) term.cyan(QUESTION_MESSAGE2) const selectedItem = await getSelectItemType(descriptions)
๋ฐ›์€ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœย createDirectoryย ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๋ฅผ ๋งŒ๋“ค๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.
const selectedItemType = await createDirectory( selectItemMap, selectedItem.selectedIndex, destDirName )
๊ทธ๋ฆฌ๊ณ  ์ž˜ ์ ์šฉ์ด ๋˜์—ˆ๋Š”์ง€, ์—๋Ÿฌ๊ฐ€ ์žˆ์—ˆ๋Š”์ง€์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์—ˆ๋‹ค.
if (selectedItemType === 'exist-dest') { term.red(EXIST_DEST_ERROR_MESSAGE) createPrompt(selectItemMap, options) } if (selectedItemType === 'exist-target') { term.red(EXIST_TARGET_ERROR_MESSAGE) createPrompt(selectItemMap, options) } if (selectedItemType === 'boiler-plate') { term.cyan(SUCCESS_MESSAGE) process.exit(0) } if (selectedItemType === 'quit') { term.red(FAILURE_MESSAGE) process.exit(0) } if (!selectedItemType) { term.white(QUIT_MESSAGE) process.exit(0) }

6. ๋ฐฐํฌ

npm run devย ๋กœ ์ƒ์„ฑ๊ธฐ๊ฐ€ ์˜๋„๋Œ€๋กœ ์ž˜ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ , version์„ ์˜ฌ๋ ค์ค€ ๋’ค,ย npm publishย ๋กœ ๋ฐฐํฌํ•ด์ฃผ์—ˆ๋‹ค.
{ "name": "@taenykim/generator", "version": "0.0.3", "scripts": { "dev": "tsc && ./bin/run", "build": "tsc" } }

7. ์‚ฌ์šฉ

์‚ฌ์šฉ์€ย npx @taenykim/generatorย ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
notion image
npx๋Š”ย node_moduleย ๋กœ ํŒจํ‚ค์ง€๊ฐ€ ์ €์žฅ๋˜์ง€ ์•Š๊ณ , ํŒจํ‚ค์ง€ ์‹คํ–‰ ํ›„, ์ œ๊ฑฐ๋œ๋‹ค.

8. ํ›„๊ธฐ

๋ฏธ๋ฃจ๊ณ ๋ฏธ๋ฃจ๋‹ค๊ฐ€ ํ›„๋”ฑ ๋งŒ๋“ค์–ด์„œ ๋ฐฐํฌ๊นŒ์ง€ ํ•ด๋ดค๋Š”๋ฐ ์•ž์œผ๋กœ ์œ ์šฉํ•˜๊ฒŒ ๋งŽ์ด ์“ธ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. nodeJS fs ๋ชจ๋“ˆ๊ณผ npm๊ณผ ์ชผ๋”๋” ์นœํ•ด์ง„ ๊ฒƒ ๊ฐ™์•„์„œ ๋„ˆ๋ฌด ์ข‹์•˜๊ณ  ์•ž์œผ๋กœ ๋กค์—… ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๋‚˜ ๋ฆฌ์•กํŠธ ๋“ฑ๋“ฑ ๋” ์ถ”๊ฐ€ํ•ด๋‚˜๊ฐ€ ๋ณด์•„์•ผ๊ฒ ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์‹œ๊ฐ„์ด๋œ๋‹ค๋ฉด ๋ฆฌํŒฉํ† ๋ง๋„ ์ข€ ํ•˜๊ณ ,, ๋‹ค์–‘ํ•œ ์—๋Ÿฌ์ฒ˜๋ฆฌ๋„ ํ•ด๋ด์•ผ๊ฒ ๋‹ค.

Loading Comments...