Hygen을 이용하여 템플릿 코드와 CLI 구현

Updated
2023/06/22 10:51
Category
Library
Tags
Hygen
npm
boilerplate
component
CLI
JavaScript
template
2 more properties

하이젠(Hygen)이란?

Node package인 Hygen은 CLI를 이용하여 미리 만들어둔 템플릿을 원하는 위치에 원하는 형식으로 생성해주는 도구입니다.
The scalable code generator that saves you time. 확장 가능한 코드 생성기이며, 이것을 통해 시간을 절약할 수 있습니다.

템플릿을 만드는 이유

궁극적인 목적은 시간절약을 하기 위함입니다.
우리에겐 control + c / control + v 가 있지만 이런 행동은 기존 코드를 이미 잘 알고있을 경우 효율적이지, 시간이 지나면 다시 코드를 이해하기위해서 분석하고 해석해야하는 피로감이 생깁니다.
또 새로운 개발자가 들어왔을땐 이 시간이 더 길이지게됩니다.
이러한 이유로 새로운 개발을 들어갈때나 새로운 컴포넌트를 생성하는 시점에서 하이젠을 이용한 cli 템플릿 생성은 장기적으로 봤을때 큰 도움이 될 수 있습니다.

하이젠 사용법

Hygen은 자바스크립트 기반의 코드 제너레이팅 툴입니다. fs-extra와 같이 파일시스템에 직접 접근하고 파일을 추가하는 작업을 자동화할 수 있으며, 핵심 기능을 간결하게 제공해 빠르게 구현 가능합니다.

설치

하이젠을 사용하기 위해선 우선 설치가 필요합니다.
하이젠은 자바스크립트 기반이기 때문에 npm을 통해서 설치도 가능하지만, macOS 사용자라면 brew 를 이용하여 설치도 가능합니다.

npm

$ npm i -g hygen
Bash
복사

brew

$ brew tap jondot/tap $ brew install hygen
Bash
복사

기본 설정

하이젠(Hygen)을 설정하는 방법을 배워보도록하겠습니다.
1.
우선 설정하고자하는 프로젝트에서 hygen init self 명령어를 실행합니다.
$ hygen init self
Bash
복사
2.
위 명령어를 실행하게되면 root 경로에 _templates 폴더가 생성됩니다. 이 폴더에 자신이 원하는 템플릿을 넣어주면됩니다.
3.
하이젠의 기본 명령어는 $ hygen generator action [NAME] 으로 구성되어 있습니다. 현재 생성된 폴더 구성을보면 디렉토리의 generator가 존재합니다. 이것과 명령어의 generator은 서로 연관이 있지않으므로 이름을 같게 할 필요는 없습니다. ( 이부분은 헷갈린다면 조금있다가 한번더 살펴보겠습니다.) generator , init 이 hygen 명령어의 generator가 되는것이고, generator/help, new, with-prompt , init/repo 가 action이 된다고 간단하게 이해하시면됩니다.
4.
간단히 예제를 만들어보겠습니다. Hygen 공식 문서에 작성된 예제를 실행시켜보도록 하겠습니다.
$ hygen generator new awesome-generator $ hygen awesome-generator new hello
Bash
복사
위 명령어를 실행하면 아래와 같은 폴더들이 생성됩니다.
첫 명령어 $ hygen generator new awesome-generator 를 통해 _template/awesome-generator/new/hello.ejs.t 가 생성되었고, $ hygen awesome-generator new hello 를 실행하여 app/hello.js 가 생성되었습니다.
위 명령어를 이해하기 위해선 _template/generator/new 경로에 있는 hello.ejs.t를 확인해봐야합니다.
// _template/generator/new/hello.ejs.t --- to: _templates/<%= name %>/<%= action || 'new' %>/hello.ejs.t --- --- to: app/hello.js --- const hello = ``` Hello! This is your first hygen template. Learn what it can do here: https://github.com/jondot/hygen ``` console.log(hello)
JavaScript
복사
확장자 ejs란 Embedded JavaScript templating의 줄임말로 자바스크립트 코드를 이용하여 HTML 태그를 사용가능하게하고 이것을 이용해 템플릿을 생성할수 있도록 도와주는 언어입니다.
어려운 언어가 아니기때문에 어렵게 생각하지말고 쉽게 생각하면 됩니다.
hygen은 ---를 이용해서 frontmatter sectionbody로 나뉠수 있습니다. 위 코드에선 ---가 두개 존재하지만 첫 명령어에선 첫 frontmatter section이 동작, 두번째 명령어에서 두번째 section이 동작합니다.
to 속성은 하이젠을 실행시 생성되는 경로입니다. to 말고도 많은 속성들이 존재합니다. 아래에서 더 자세히 살펴보겠습니다.
Property
Type
Default
Example
String (url)
undefined
my-project/readme.md
String (url)
undefined
shared/docs/readme.md
Boolean
false
true
Boolean
false
true
Boolean
false
true
Regex
undefined
devDependencies
Regex
undefined
myPackage
String
undefined
echo: "Hello this is a shell command!"
$ hygen generator new awesome-generator 명령어를 다시 살펴보면 $ hygen generator action [NAME] 구성으로 되어있어 “awesome-generator”가 <%= name %>으로 들어갑니다.
여기서 <%= %> 문법은 ejs 문법으로 cli를 통해 전달받은 값이 들어갔다고 생각하시면됩니다.
// 두 명령어가 동일 $ hygen generator new awesome-generator $ hygen generator new --name awesome-generator
JavaScript
복사
그렇다면 action엔 빈값이 들어가기 때문에 new가 선택되어 _template/awesome-generator/new/hello.ejs.t 가 생선된것을 확인할 수 있습니다.
5.
이전에 말씀드린 $ hygen generator action [NAME] 부분이 이해가 안될수도 있습니다. 이 부분은 다른 예제를 통해 설명하도록 하겠습니다.

$ hygen generator action [NAME]

_template 디렉토리를 비워두고 아래의 형태로 만들어둡니다.
// _template/modules/test/hello.ejs.t --- to: app/<%= path1 %>/<%= path2 %>.js --- const hello = "Hello World~!" console.log(hello)
JavaScript
복사
이제 아래와 같은 명령어를 실행합니다. 그럼 app/component/helloWorld.js 파일이 생성된것을 알 수 있습니다. 이것으로 generatoraction이 어떤 역할을 하는지 더 확실하게 알 수 있습니다.
generator는 _template 아래의 생성기 역할, action은 생성기에서의 실행 폴더를 말합니다.

입력 상호작용 추가하기

CLI에서 유저 입력을 받아 컴포넌트를 생성하기 위해, Enquirer 라이브러리를 이용하면 상호작용 프롬프트를 구성할 수 있습니다.
하이젠(Hygen)은 CLI 상호작용을 도와주는 enquirer를 내장하고 있기때문에 따로 설치할 필요없이 바로 사용할 수 있습니다.
이번엔 프롬프트를 구현해보겠습니다.
_template 내의 generator 자리에 prompt.js 파일을 생성해줍니다.
// _template/modules/test/prompt.js module.exports = { prompt: ({ prompter, args })=> prompter.prompt({ type: 'input', name: 'path1', message: 'Path1의 값을 입력해주세요.' }).then(({ path1 })=> prompter.prompt({ type: 'input', name: 'path2', message: 'Path2의 값을 입력해주세요.', }).then(({ path2 })=> { if (!path1) throw new Error('path1의 값이 비어있습니다. path1 의 값을 입력해주세요') if (!path2) throw new Error('path2의 값이 비어있습니다. path1 의 값을 입력해주세요') return { path1, path2, args } })) }
JavaScript
복사
Node.js 기반 모듈이므로 require 방식의 모듈 방식으로 코드를 구현합니다. 프롬프트 설정 자체도 module.export를 이용하여 내보냅니다. prompter 객체는 별도로 입력하지 않아도 자동으로 주입되며, args는 CLI 단계에서 입력받은 argument를 활용하고 싶을때 사용할 수 있습니다.
위 코드를 작성한뒤 실행해보겠습니다.
정상적으로 상호작용하는 모습을 볼 수 있습니다.
이 밖에도 옵션을 이용한 방법과 몇가지 내장 함수들을 지원하기도합니다.

option 기반으로 입력

prompter.select({ type: 'input', name: 'category', message: '카테고리 컴포넌트의 카테고리를 선택하세요.', choices: ['animation', 'common', 'core', 'util'] })
JavaScript
복사

내장 함수들

// example: <%= h.inflection.pluralize(name) %> pluralize( str, plural ) singularize( str, singular ) inflect( str, count, singular, plural ) camelize( str, low_first_letter ) underscore( str, all_upper_case ) humanize( str, low_first_letter ) capitalize( str ) dasherize( str ) titleize( str ) demodulize( str ) tableize( str ) classify( str ) foreign_key( str, drop_id_ubar ) ordinalize( str ) transform( str, arr )
JavaScript
복사
// example: <%= h.changeCase.camel(name) %> camel( str ) constant( str ) dot( str ) header( str ) isLower( str ) isUpper( str ) lower( str ) lcFirst( str ) no( str ) param( str ) pascal( str ) path( str ) sentence( str ) snake( str ) swap( str ) title( str ) upper( str )
JavaScript
복사

Frontmatter section 살펴보기

위에서 언급했듯이 frontmatter section에는 아래와 같은 속성들을 지원합니다.
Property
Type
Default
Example
String (url)
undefined
my-project/readme.md
String (url)
undefined
shared/docs/readme.md
Boolean
false
true
Boolean
false
true
Boolean
false
true
Regex
undefined
devDependencies
Regex
undefined
myPackage
String
undefined
echo: "Hello this is a shell command!"

1. from

from은 외부 파일로부터 읽어들여 body를 채워줍니다. incloude와 비슷한 역할을 하지만 이때 body template은 무시합니다.

2. inject

inject는 말그래도 template을 주입시켜주는 속성입니다.
inject를 사용시에는 어디에 주입시킬것인지 조건을 넣어줘야합니다. 조건으로는 after, before, prepend, append, at_line 이 있습니다. 그리고 skip_if를 통해 주입을 skip할것인지아닌지 조건을 달수있습니다.
before or after : 정규식을 이용해 조건에 맞는 라인을 찾고 이전라인 or 다음라인에 template 코드를 주입시킵니다.
prepend or append : 정규식을 이용해 조건에 맞는 라인을 찾고 해당 코드의 이전 or 다음 에 붙여서 template 코드를 주입시킵니다.
at_line : 원하는 라인에 template 코드를 주입시킵니다.
아래 코드로 한번더 설명하겠습니다.

예제코드 1

// app/menu.js export default menu = [ "apple", "banana", "tomato", ]
JavaScript
복사
위 메뉴에 새롭게 신규 메뉴(melon)를 개발하게되었습니다. 위 menu.js를 통해 화면에 메뉴를 노출된다고했을때, 새롭게 생성할 신규메뉴인 melon이 자동으로 주입되어야하는 상황입니다.
이때, 아래의 template을 이용해 코드를 주입할 수 있습니다.
// _template/generator/new/injectMenu.ejs.t --- to: app/menu.js inject: true after: menu --- "<%= menu %>", // --menu melon
JavaScript
복사
$ hygen generator new —menu melon 명령어를 실행하면 menu의 첫번째 인덱스로 코드가 주입됩니다.
이때 프로젝트에 린트설정이 되어있다면 린트룰에 맞게 설정하는것이 작업에 다음번 작업에서 더 편합니다.
after 속석은 정규식을통해 해당라인을 찾아내는데 가장 먼저 발견되는 텍스트에서 적용됩니다.

예제코드 2

// app/menu2.js export default menu = [ { id: "apple", children: [ { id: "apple2", } ] }, { id: "banana", children: [ { id: "banana2", } ] }, ]
JavaScript
복사
이번엔 신규 메뉴(apple3)를 개발해야하는 상황입니다. 이때는 apple.children에 코드를 주입해줘야합니다.
// _template/generator/new/injectMenu2.ejs.t --- to: app/menu2.js inject: true after: <%= menu %>..[^\[]*children // --menu apple --- { id: "<%= name %>", // --name apple3 },
JavaScript
복사
정규식을 이용해 코드를 작성한 후 $ hygen generator new —menu apple — apple3 명령어를 실행시켜주면 apple.children의 첫번째 인덱스에 코드가 주입됩니다.

참고