Yeoman 으로 프로젝트 찍어내보기
안녕하세요. 항상 서문이 힘드 네요. 처음이라 가벼운 글로 시작해보고자 합니다.
저같은 월급루팡이 아니라면 다들 프로젝트를 여러 개 만들어보셨을텐데요. 프로젝트를 여러 개 만들다보면 특수한 형태가 아니고서는 공통적인 요소들을 발견하게 됩니다. 폴더구조, DB커넥션, 의존성설정, 로그인, 띄어쓰기, 깃 설정 등등... 개인이 공부를 위해 이것저것 해보는 게 아닌 이상, 특히 MSA(Micro Service Architectures)를 지향하는 회사라면 작은 기능 하나하나에도 똑같이 생겨먹은 프로젝트를 여럿 만들게 될겁니다.
그러다보면 어느 우스갯소리처럼 환경설정하다 몇 날 며칠 꼬박 지나갑니다. 마감은 다가오고... 그러다보면 비슷한 생김새의 프로젝트 틀을 약간의 설정만으로 만들어주는 걸 바라게 됩니다. 이번에 알아볼 요먼(Yeoman) 은 그런 도구의 일종입니다.
요먼(Yeoman) 으로 프로젝트 찍어내보기
요먼?
요먼(Yeoman)은 웹 애플리케이션을 위한 오픈 소스 클라이언트 사이드 스캐폴드 도구이다. Yeoman은 Node.js로 작성된 명령 줄 인터페이스로 실행되며 여러 기능(스타터 템플릿 생성, 의존성 관리, 유닛 테스트 실행, 로컬 디플로이먼트 서버 제공, 디플로이먼트를 위한 운영 코드 최적화 등)을 한 곳에 병합해놓았다.
구글 I/O 2012에서 공개되었다. from [위키백과](https://ko.wikipedia.org/wiki/%EC%9A%94%EB%A8%BC_(%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4)
위키백과의 글을 발췌하였습니다. 여기서 스캐폴드라는 말이 등장하는데요. 스캐폴드(Scaffold) 란 비계 건물 지을 때 잡아주는 틀 뭐 그런 단어입니다. 이 단어가 프로그래밍으로 온 건데요. 간단하게 말하면 사용자가 쉽게 뭘 할 수 있도록 틀을 만들어주는 것이라고 생각하시면 됩니다. 우스갯소리 좀 넣으면 피라미드 만들 때 썼던 더 큰 피라미드 같은 느낌이죠. 근데 여기서 "클라이언트 사이드" 라는 단어가 사람 헷갈리게 하는데요. 이 뜻은 서버 프로그래머들은 건드리지 말라는 뜻이 아니라 뼈대를 만드는 초점이 DB가 아닌 애플리케이션이라는 뜻입니다. 그러니 백엔드 개발자분들도 뒤로가기를 누르지 말아주세요.
Getting Started 해보기
만만한게 Getting Started 입니다. 흔히들 튜토리얼이라고 하죠. 먼저 yeoman 을 사용하기 위한 클라이언트 도구인 yo
을 npm
으로 설치해보도록 하겠습니다.
- 제 환경은
Mac OS X v10.14.6
입니다. 리눅스나 윈도로 따라하실 분들은 그 부분 신경써주세요. $
는 커맨드 라인의 시작을 나타냅니다. 복붙할 때 얘는 끼우지 마세요.XXX 라 썼다고 진짜 XXX 라고 입력하지 마세요.
$ npm install -g yo
클라이언트 도구를 설치했으니 써봐야죠. generator
라는 것을 다운받아야 하는데요. 이는 프로젝트의 생김새를 결정하는 주물 같은 거라고 보면 됩니다. 해당 사이트에서는 웹앱을 골랐네요. 목록을 보고싶다면 Generators | Yeoman 을 확인해보시거나 구글에 yeoman generator XXX(직접 입력)
이라고 검색을 해보세요. 이 이야기는 나중에 하고 webapp 제네레이터를 설치해줍니다.
$ npm install -g generator-webapp
설치가 되었는지 확인 겸, 도움말도 알아볼 겸 아래 명령을 쳐보겠습니다.
$ yo webapp --help
요래 치면 좌라락 나옵니다. 더 자세한 도움말은 generator-webapp
을 만든 사람을 찾아보면 됩니다. 구글에서 검색하거나 $ npm home generator-webapp
이라고 터미널에 입력하면 되겠습니다.
아무튼, 설치가 완료되었다면 직접 프로젝트를 만들어보겠습니다. 작업을 하기 위한 아무 디렉터리나 가서 아래 명령을 쳐봅시다.
- 이 제네레이터는 하위 디렉터리를 만들어주지 않으니 빈 디렉터리에서 작업하시기 바랍니다.
$ cd /아무/디렉터리나/가서
$ yo webapp
입력하면 몇 가지를 물어봅니다. 적당히 골라서 한 번 해봅시다. 그러면 뭔가 좌라락 뜨고 한참을 기다리다보면 프로젝트가 하나 생성되었을 것입니다. 너무 오래 걸리면 캔슬하고 $ npm install && npm rebuild
를 한 번 해줍시다. 설치가 끝났다면 $ npm run start
명령을 쳐서 웹 페이지를 한 번 띄워보겠습니다. 잘 뜨면 성공입니 다.
Generator 만들어보기
아까까지는 사용법이었습니다. 사용법만 알아도 어지간한 건 다 할 수 있습니다만 저희도 하나 만들어봐야죠. 이제 가이드에 따라 직접 generator 를 만들어보도록 하겠습니다.
가이드 링크: Writing Your Own Generator
yeoman 에서는 generator 를 쉽게 만들 수 있는 generator-generator 를 제공해주고 있습니다. 가이드는 어떻게 저런 코드가 나왔는지를 중점적으로 설명하고 있습니다. 한 번 따라해보도록 합시다.
주의사항
위에서 말했듯이$
는 커맨드 라인의 시작을 나타냅니다.중요하니 한 번 더제 환경은Mac OS X v10.14.6
입니다. 리눅스나 윈도로 따라하실 분들은 그 부분 신경써주세요.- yeoman generator 는
javascript
및node.js
에 대한 지식을 필요로 합니다. 1도 모르는 분이시라면 옆에 계신 전문가에게 도움을 요청하세요. - ... 은 생략을 나타냅니다. 이 부분까지 복사하시고 저한테 뭐라고 하시면 안됩니다.
먼저 빈 폴더를 하나 만들고 그 안에서 프로젝트를 초기화합시다. 이 때 프로젝트명은 generator-
로 시작하도록 해줍시다.
$ mkdir -p /아무/디렉터리/generator-만들자
$ cd /아무/디렉터리/generator-만들자
$ npm init
그 다음 package.json 을 열어 아래 내용을 추가해줍시다.
{
...
"files": [
"generators",
],
"keywords": ["yeoman-generator"],
...
}
yeoman-generator 가 되기 위한 가장 중요한 요소는
- "name" 이
generator-
로 시작해야 한다는 것 - "keywords" 에
"yeoman-generator"
가 있어야 한다는 것 - "description" 에 설명을 깔쌈하게 작성해야 한다는 것
입니다. "files" 는 다음에 말씀드리겠습니다.
이제 yeoman-generator
라이브러리를 설치해보도록 합시다.
$ npm install --save yeoman-generator
설치가 완료되었으면 아까 "files" 의 안에 있는 "generators" 와 같은 이름의 디렉터리를 만들어보겠습니다. 이 generators 안에 index.js 를 만들고 그 안에 내용을 삽입하면 됩니다.
// generators/index.js
const Generator = require('yeoman-generator');
module.exports = class extends Generator {
constructor(args, opts) {
super(args, opts);
this.option('babel');
}
method1() {
this.log('method 1 just ran');
}
method2() {
this.log('method 2 just ran');
}
};
감이 오시겠지만 yo <어쩌고>
명령을 치게 되면 generators 안의 index.js 를 실행하여 셋팅을 하게 되는 것입니다.
그리고 커맨드 창에서 $ npm link
라고 치시면 이 프로젝트가 임시로 로컬 npm 레지스트리에 등록이 되어서 generator 를 사용할 수 있게 됩니다. 실행할 때엔 아까 packge.json 의 "name"에 generator-
부분을 뺀 이름을 yo 뒤에 치시면 됩니다.
$ yo <name>
< 아까 위에 말했던 그 이름
그러면 대충 콘솔 로그 2번 뜨고 끝나게 됩니다. 여기까지가 간단한 generator 를 만드는 부분이었고, 더 복잡한 내용에 대해서는 다음에 다루도록 하겠습니다.
(진짜) Generator 만들어보기
위 단락을 보고 "그래서 이게 뭔데 X덕아" 하시는 분들 많으실텐데요. 이제 좀 쓸만한 Generator 를 한 번 만들어보도록 하겠습니다.
yeoman generator 의 실행 순서
yeoman generator 는 다음 순서로 함수를 실행합니다.
initializing
- 초기화 과정입니다. 프로젝트 상태나 설정값 등을 불러옵니다.prompting
- 프롬프트 과정입니다. 사용자로부터 입력을 받습니다.configuring
- 설정을 저장하고 프로젝트를 설정합니다.(.editorconfig
파일과 메타데이터 등을 만듭니다.)default
이름이 default 가 아닙니다.사전 정의된 함수를 제외한 다른 함수는 이 시점에 실행됩니다.writing
- 이 시점에서 파일을 작성하게 됩니다.(router, controllers 등등등)confilcts
- 충돌 발생 시 대응합니다.(내부적으로 실행됩니다)install
- 설치 작업을 실행합니다.(npm install
,pip install
등)end
- 마무리입니다. 파일 정리 및 안녕 인삿말 등을 넣습니다.
이 순서대로 진행되므로 구현하고 싶은 함수들을 구현하시면 되겠습니다. 내가 호출할 때 이외에 실행 시키기 싫다면 private 함수라고 선언하시면 됩니다. JS 에선 딱히 선언방법이 없으므로 함수명 앞에 _
를 넣으면 됩니다.
사용자 입력 받아보기
내부적으로 Inquirer.js 를 이용하여 구현하 므로 자세한 사용법은 해당 사이트로 떠넘겼습니다를 찾아보라고 합니다. 최신 yeoman 은 promise 를 지원하므로 async/await 패턴으로 구현이 가능합니다. 간단한 예시코드를 보겠습니다.
// generators/index.js
const Generator = require('yeoman-generator');
module.exports = class extends Generator {
constructor(args, opts) {
super(args, opts);
}
async prompting() {
this.answers = await this.prompt([
{
type: 'input',
name: 'name',
message: '프로젝트명을 입력하세요.',
default: this.appname, // Default to current folder name
},
{
type: 'confirm',
name: 'cool',
message: '쿨한 기능을 써볼래요?',
},
]);
this.log('app name', this.answers.name);
this.log('cool feature', this.answers.cool);
}
};
이렇게 하면 실행하였을 때 두 질문을 하게 되고 질문 결과가 저장됩니다. 그 밖에 커맨드라인에서 입력을 받는 부분(arguemnts, options) 는 공식 문서를 참고하시기 바랍니다.
파일 시스템과 상호 작용
진짜 중요한 게 하나 남았습니다. 바로 파일 저장입니다. 아무리 입력을 받고 뭘 해도 파일을 수정 못하면 아무 소용 없잖아요? 먼저 파일들이 어디에 위치하고, 파일 경로를 어떻게 불러오는 지부터 보도록 하겠습니다.
this.destinationRoot()
- 명령을 실행한 디렉터리this.destinationPath(path)
- 명령을 실행한 디렉터리와path
를 합칩니다.this.sourceRoot()
- 템플릿 파일이 위치한 경로입니다. 제네레이터 디렉터리의templates
디렉터리를 나타냅니다.this.templatePath(path)
- 템플릿 디렉터리와path
를 합칩니다.
폴더 구조로 나타내면 다음과 같습니다.
/
|____ path/to/.../node_modules
| |____ generator-name
| |____ generators
| |____ templates <- this.sourceRoot();
| |____ foobar.json <- this.templatePath('foobar.json');
| |____ index.js
|
|
|
|____ project <- this.destinationRoot();
|____ index.js <- this.destinationPath('index.js');
해보기
generators/templates 안에 index.html 하나 만들어봅시다.
<html>
<head>
<title><%= title %></title>
</head>
</html>
그 다음 generators/index.js 파일을 작성해줍시다.
// generators/index.js
const Generator = require('yeoman-generator');
module.exports = class extends Generator {
constructor(args, opts) {
super(args, opts);
}
async prompting() {
this.answers = await this.prompt([
{
type: 'input',
name: 'title',
message: '제목을 입력하세요.',
},
]);
}
writing() {
const templateHtmlPath = this.templatePath('index.html');
const destHtmlPath = this.destinationPath('public/index.html');
this.fs.copyTpl(templateHtmlPath, destHtmlPath, {
title: this.answers.title,
});
}
};
여기서 this.fs.copyTpl
이라는 함수가 보이는데 이는 EJS 템플릿을 치환하여 복제하는 유틸리티성 함수입니다. 파일시스템은 인 메모리 방식이며, 사용법은 여기 를 참고하시기 바랍니다.