강좌 & 팁
글 수 2,412
2014.11.25 17:32:52 (*.134.169.166)
40290
이번 강좌 좀 길어요 ㅠㅠ
(**) grunt-init 템플릿 만들기 준비
자 이제 템플릿을 만들어 봅시다.
아주 쉬운것 부터 하나 하나 만들어 갈 겁니다.
먼저 우리의 시험용 템플릿 디렉토리를 다음에 만들 겁니다.
$ cd ~/.grunt-init/
$ mkdir test
그리고 제대로 동작하는지 시험하기 위해서 템플릿 생성 디렉토리는
다음과 같이 만들 겁니다.
$ cd ~/grunt/init/
$ mkdir test_temp
$ cd test_temp
정리하면
~/.grunt-init/test : 템플릿 원형
cd ~/grunt/init/test_temp : 템플릿 생성 공간
(**) 필수 조건
정말 쓸데 없는지 모르겠지만..
최소한의 grunt-init 가 정상적으로 동작하기 위해서
필요한 것은
~/.grunt-init/test/template.js
이란 파일이고
이 파일은 최소한 다음과 같은 형태가 되어야 합니다.
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
console.log( '보이십니까?' );
done();
};
----------------------------------------------------------------------------------------
grunt-init 는 exports.template 에 지정된 함수를 수행함으로써 템플릿 처리를 하게 됩니다.
이 함수에 전달되는 매계변수에서
grunt 는 grunt 모듈의 함수와 프로퍼티를 제공하고
init 는 grunt-init가 제공하는 함수와 프로퍼티를 제공하게 됩니다.
대부분은 이 매계변수를 이용하여 처리하게 됩니다.
done 은 모든 처리가 끝났다는 것을 grunt-init 에 알리게 되어 정상 종료가 되도록 하는
콜백입니다.
이렇게 템플릿 동작 파일을 만들고
아래와 같이 실행하면 수행 결과가 다음과 같이 나올 겁니다.
$ cd ~/grunt/init/test_temp
$ grunt-init test
$ grunt-init test
Running "init:test" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
보이십니까?
Initialized from template "test".
Done, without errors.
frog@frog-Lenovo-IdeaPad-Y580:~/grunt/init/test_temp$
console.log() 함수를 사용해서 표출한 메세지를 보실수가 있습니다.
(**) grunt-init 템플릿 목록 도움말 설정
grunt-init 명령만 치면 템플릿 목록을 볼 수 있는데
이때 간단한 설명을 보여 주기 위해서는
exports.description
을 사용합니다.
template.js 에 exports.description 항목을 다음과 같이 추가 합니다.
--[template.js]--------------------------------------------------------------------------
exports.description = '시험용 템플릿입니다.';
exports.template = function(grunt, init, done) {
console.log( '보이십니까?' );
done();
};
----------------------------------------------------------------------------------------
grunt-init 을 실행하면 다음과 같이 템플릿 목록이 보이고 설명이 보이게 됩니다.
$ grunt-init
:
Available templates
gruntplugin Create a Grunt plugin, including Nodeunit unit tests.
test 시험용 템플릿입니다.
:
(**) grunt-init 템플릿 생성 실행시 최초 안내 문구 설정
grunt-init 명령을 통해서 템플릿을 생성할 때 최초에 안내 문구를
보여 주고 싶다면
exports.notes
을 사용합니다.
template.js 에 exports.notes 항목을 다음과 같이 추가 합니다.
--[template.js]--------------------------------------------------------------------------
exports.description = '시험용 템플릿입니다.';
exports.notes = '=============================================\n'
+ ' 시험용 템플릿을 만들기 시작합니다. \n'
+ '---------------------------------------------\n';
exports.template = function(grunt, init, done) {
console.log( '보이십니까?' );
done();
};
----------------------------------------------------------------------------------------
grunt-init test 을 실행하면 다음과 같이 표출되게 됩니다.
~/grunt/init/test_temp$ grunt-init test
Running "init:test" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
"test" template notes:
=============================================
시험용 템플릿을 만들기 시작합니다.
---------------------------------------------
보이십니까?
Initialized from template "test".
Done, without errors.
(**) 사용자 입력 처리
보통 초기 구축시 필요로 하는 값들이 있습니다 .
보통 이 값들은 사용자에게서 입력을 받아서
초기 구축 과정의 판단 근거나 결정 값으로 사용됩니다.
이런 행위를 하기 위해서 사용하는 함수로
init.process({}, [], function(err, props) {});
이 있습니다 .
첫번째 인자 {} 는 이 함수를 처리하는 과정에 사용될 옵션 설정 입니다.
두번째 인자 [] 는 사용자에게 물어 볼 내용들에 대한 것들입니다.
세번째는 사용자 입력을 모두 받아 들인 후 처리 될 내용입니다.
그래서 보통 다음과 같은 형태로 만들어 집니다.
--[template.js]--------------------------------------------------------------------------
exports.description = '시험용 템플릿입니다.';
exports.notes = '=============================================\n'
+ ' 시험용 템플릿을 만들기 시작합니다. \n'
+ '---------------------------------------------\n';
exports.template = function(grunt, init, done) {
init.process({}, [], function(err, props) {
console.log( '처리되었습니다.' );
done();
});
};
----------------------------------------------------------------------------------------
grunt-init test 을 실행하면 다음과 같이 표출 되게 됩니다.
$ grunt-init test
Running "init:test" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
"test" template notes:
=============================================
시험용 템플릿을 만들기 시작합니다.
---------------------------------------------
Please answer the following:
처리되었습니다.
Initialized from template "test".
Done, without errors.
두번째 인자에 빈 배열만 주었기 때문에 바로 함수가 처리 되어 버립니다.
이제 "프로젝트_이름" 이라는 변수를 선언하고 사용자 입력을 받아 들여 보겠습니다.
보통 사용자 입력을 받을 때 필요한 것은 다음과 같은 것입니다 .
- 변수명
- 사용자에게 입력할 내용을 알리는 문자열
- 사용자가 특별히 입력 값을 지정하지 않았을 경우 디폴트 값
- 사용자 유효 입력 패턴
- 사용자 입력 실수가 있을 경우 표출되는 문자열
이런 값을 지정하려면 다음과 같이 객체를 프로퍼티가 준비되어 있습니다.
name : 변수명
message : 사용자에게 입력할 내용을 알리는 문자열
default : 사용자가 특별히 입력 값을 지정하지 않았을 경우 디폴트 값
validator : 사용자 유효 입력 패턴 자바스크립트 정규 표현식을 사용합니다.
warning : 사용자 입력 실수가 있을 경우 표출되는 문자열
이 프로퍼티를 모두 지정해야 할 필요는 없습니다. 최소한 name 만 지정해도 문제 없이 동작합니다.
"프로젝트_이름" 변수명에 입력값을 받는 것을 만들어 보겠습니다.
--[template.js]--------------------------------------------------------------------------
exports.description = '시험용 템플릿입니다.';
exports.notes = '=============================================\n'
+ ' 시험용 템플릿을 만들기 시작합니다. \n'
+ '---------------------------------------------\n';
exports.template = function(grunt, init, done) {
init.process({},
[
{
name: '프로젝트명',
message: '프로젝트 이름을 입력해 주십시오',
default: '임시프로젝트',
},
], function(err, props) {
console.log(props);
console.log( '처리되었습니다.' );
done();
});
};
----------------------------------------------------------------------------------------
grunt-init test 을 실행하면 다음과 같이 사용자 입력을 기다리고
입력 값을 주지 않고 엔터를 누르면
변수는 props.프로젝트명 으로 생기고 디폴트값이 입력되는 것을 알 수 있을 겁니다.
$ grunt-init test
Running "init:test" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
"test" template notes:
=============================================
시험용 템플릿을 만들기 시작합니다.
---------------------------------------------
Please answer the following:
[?] 프로젝트 이름을 입력해 주십시오 (임시프로젝트)
[?] Do you need to make any changes to the above before continuing? (y/N)
{ '프로젝트명': '임시프로젝트' }
처리되었습니다.
Initialized from template "test".
Done, without errors.
(**) 입출력 처리 prompt
grunt-init 에서는
사용자 입출력 처리를 구현하기 위해서
prompt 란 모듈을 사용하고 있습니다.
이 모듈은
https://github.com/flatiron/prompt
에서 제공되고 있습니다.
grunt-init 에 대한 자세한 사용 방법에 대한 문서가 부족한 입장에서
실력 있는 분들은 이 모듈의 이 모듈의 사용법을 익히면
사용자 입출력에 대한 처리 부분에 대한
좀 더 확실하게 아실 수 있을 것 같습니다.
(**) init.prompt() 를 사용한 사용자 입력 처리
앞에서 사용자 입력을 받을 객체를 선언하는 것을 알아 보았습니다.
귀찮은 개발자를 위해서 이런 객체를 심플하고 보기 좋게 선언하는 방법은
init.prompt(name[, default]);
함수를 사용하는 것입니다.
이 함수는 사용자로 부터 특정값을 받기 위해서 간단하게 변수명과 기본값만을 지정하면 됩니다.
이 두 값중 기본값은 생략해도 정상적으로 동작하게 됩니다.
다음은 사용 예와 실행 결과입니다.
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
init.process({},
[
init.prompt('프로젝트명', '임시프로젝트'),
init.prompt('부제목'),
], function(err, props) {
console.log(props);
done();
});
};
----------------------------------------------------------------------------------------
$ grunt-init test
:
Please answer the following:
[?] 프로젝트명 (임시프로젝트)
[?] 부제목
[?] Do you need to make any changes to the above before continuing? (y/N)
{ '프로젝트명': '임시프로젝트', '부제목': '' }
:
Done, without errors.
(**) 기본 제공 사용자 입력 변수들
grunt-init 는 많이 사용될 만한 사용자 입력 변수들이 있습니다.
이런 변수는
init.prompts
이 가지고 있습니다.
일단 이 값을 표출하는 것을 예제로 보여 드리겠습니다.
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
var prompts = init.prompts;
init.process({}, [], function(err, props) {
console.log(prompts);
done();
});
};
----------------------------------------------------------------------------------------
$ grunt-init test
Running "init:test" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
Please answer the following:
{ name:
{ message: 'Project name',
default: [Function],
validator: /^[\w\-\.]+$/,
warning: 'Must be only letters, numbers, dashes, dots or underscores.',
sanitize: [Function] },
title:
{ message: 'Project title',
default: [Function],
warning: 'May consist of any characters.' },
description:
{ message: 'Description',
default: 'The best project ever.',
warning: 'May consist of any characters.' },
version:
{ message: 'Version',
default: [Function],
validator: [Function: valid],
warning: 'Must be a valid semantic version (semver.org).' },
repository:
{ message: 'Project git repository',
default: [Function],
sanitize: [Function],
warning: 'Should be a public git:// URI.' },
homepage:
{ message: 'Project homepage',
default: [Function],
warning: 'Should be a public URL.' },
bugs:
{ message: 'Project issues tracker',
default: [Function],
warning: 'Should be a public URL.' },
licenses:
{ message: 'Licenses',
default: 'MIT',
validator: /^[\w\-\.\d]+(?:\s+[\w\-\.\d]+)*$/,
warning: 'Must be zero or more space-separated licenses. Built-in licenses are: Apache-2.0 BSD-3-Clause GPL-2.0 GPL-3.0 MIT MPL-2.0, but you may specify any number of custom licenses.',
sanitize: [Function] },
author_name:
{ message: 'Author name',
default: [Function],
warning: 'May consist of any characters.' },
author_email:
{ message: 'Author email',
default: [Function],
warning: 'Should be a valid email address.' },
author_url:
{ message: 'Author url',
default: 'none',
warning: 'Should be a public URL.' },
jquery_version:
{ message: 'Required jQuery version',
default: '*',
warning: 'Must be a valid semantic version range descriptor.' },
node_version:
{ message: 'What versions of node does it run on?',
default: '>= 0.10.0',
warning: 'Must be a valid semantic version range descriptor.' },
main:
{ message: 'Main module/entry point',
default: [Function],
warning: 'Must be a path relative to the project root.' },
bin:
{ message: 'CLI script',
default: [Function],
warning: 'Must be a path relative to the project root.' },
npm_test:
{ message: 'Npm test command',
default: 'grunt',
warning: 'Must be an executable command.' },
grunt_version:
{ message: 'What versions of grunt does it require?',
default: '~0.4.5',
warning: 'Must be a valid semantic version range descriptor.' } }
Initialized from template "test".
Done, without errors.
이 변수들의 의미를 제가 다 해석하기는 그렇고
Project Scaffolding 를 해석한 글이 다음 위치에 있는데
http://gruntjs-kr.herokuapp.com/project-scaffolding
이 중 일부를 여기에 발췌해 놓겠습니다.
author_email : package.json에 사용할 작성자 이메일 주소. 여기에 사용할 기본값은
사용자의 git config에서 먼저 찾는다.
author_name : package.json에서 사용할 작성자 이름.
여기에 사용할 기본값은 사용자의 git config에서 먼저 찾는다.
author_url : package.json에서 사용할 작성자 웹사이트 URL.
bin : cli 스크립트를 위한 프로젝트 상대 경로.
bugs : 프로젝트 이슈 트랙커의 URL.
프로젝트 레파지토리가 github이면 github 이슈 트랙커를 기본값으로 사용한다.
description : package.json과 README 파일에서 사용할 프로젝트 부연설명.
grunt_version : 프로젝트에 필요한 Grunt 버전으로,
이 버전은 유효한 semantic version로 범위를 기술해야 한다.
homepage : 프로젝트 홈페이지 URL.
프로젝트 레파지토리가 github이면 github URL을 기본값으로 사용한다.
jquery_version : jQuery 프로젝트인 경우, 프로젝트에 필요한 jQuery 버전.
이 버전은 유효한 semantic version로 범위를 기술해야 한다.
licenses : 프로젝트의 라이센스(들), 라이센스가 여러 개인 경우 공백으로 구분한다.
MIT, MPL-2.0, GPL-2.0, Apache-2.0 라이센스가 내장되어있고, MIT가 기본값이다.
추가 라이센스는 init.addLicenseFiles로 추가한다.
main : 이 프로젝트의 진입점. 기본값은 lib 폴더 안의 프로젝트 명이다.
name : 프로젝트 명. 이 이름은 프로젝트 템플릿 전체에서 사용된다.
기본값은 해당 작업 폴더 명이다.
node_version : 프로젝트에 필요한 Node.js의 버전.
이 버전은 유효한 semantic version로 범위를 기술해야 한다.
npm_test : 프로텍트의 테스트 코드를 실행할 커맨드 명령어. 기본값은 grunt다.
repository : 프로젝트의 git 레파지토리. 기본값은 github URL이라고 가정한다.
title : 사람이 읽기 좋게 바꾼 프로젝트 명. name 값을 기본값으로 가독성 좋게 바꿔서 사용한다.
version : 프로젝트 버전. 기본값은 유효한 semantic version인 0.1.0이다.
9. 자주 쓰는 디폴트값 지정하기
grunt-init 에서 자주 쓰는 것은 다음 위치의 파일에 지정하면
의 디폴트 값을 지정하는 것을 생략할 수 있습니다.
이 파일은 각 템플릿 별로 지정하는 것이 아니고
모든 템플릿에 공통으로 적용됩니다.
위치는 다음과 같습니다.
~/.grunt-init/defaults.json
이 파일을 다음과 같이 작성하고
--[defaults.json]--------------------------------------------------------------------------
{
"author_name": "\"Cowboy\" Ben Alman",
"프로젝트명": "아무것도아님"
}
----------------------------------------------------------------------------------------
template.js 에서 이와 관련된 입력을 받도록 설정한 후
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
init.process({}, [
init.prompt("author_name"),
init.prompt("프로젝트명"),
], function(err, props) {
console.log(props);
done();
});
};
----------------------------------------------------------------------------------------
grunt-init test 를 실행하면 디폴트 값이 이 파일을 참조 함을 알 수 있습니다.
$ grunt-init test
:
Please answer the following:
[?] Author name ("Cowboy" Ben Alman)
[?] 프로젝트명 (아무것도아님)
[?] Do you need to make any changes to the above before continuing? (y/N)
{ author_name: '"Cowboy" Ben Alman', '프로젝트명': '아무것도아님' }
:
Done, without errors.
defaults.json 파일은 다음 변수에 할당 되어 있습니다.
init.defaults
다음 예는 이 변수를 보여 주는 예 입니다.
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
init.process({}, [], function(err, props) {
console.log(init.defaults);
done();
});
};
----------------------------------------------------------------------------------------
실행 결과 입니다.
$ grunt-init test
:
Please answer the following:
{ author_name: '"Cowboy" Ben Alman', '프로젝트명': '아무것도아님' }
:
Done, without errors.
(**) 파일 복사 대상이 있을 곳
프로젝트들은 초기 구축 과정에 반드시 필요한 파일이나 디렉토리 구조가 있습니다.
이런 파일이나 디렉토리는 grunt-init 에서 자동으로 만들어 주게 하려면
root 란 디렉토리에 만들어 놓으면 됩니다.
test 템플릿일 경우라면 이 디렉토리는 다음과 같은 위치여야 하죠..
~/grunt/init/test/root/
(**) 복사 정보 얻어 오기
root 디렉토리에 파일이 있거나 디렉토리가 있다고
grunt-init 명령 실행시 저절로 되는 것은 아닙니다.
왜냐하면 대부분의 경우 복사만 필요한 경우보다
중간 처리가 필요한 경우가 많기 때문이죠..
그래서 일단 이 root 밑 디렉토리에 대한 정보를 제공하는 함수가 존재 합니다.
그 함수 이름은
init.filesToCopy()
입니다.
사용방법의 예와 전달되는 정보를 보기 위해서 먼저 몇가지 파일과 디렉토리를 다음과 같이 만들고
시작하겠습니다.
~/grunt/init/test/root/test.txt
~/grunt/init/test/root/빈_디렉토리/
~/grunt/init/test/root/파일_있는_디렉토리/
~/grunt/init/test/root/파일_있는_디렉토리/파일.txt
root 밑에는 test.txt 란 파일이 있고
아무 파일이 없는 빈 디렉토리인 "빈_디렉토리/"
그리고 파일.txt 를 가지고 있는 "파일_있는_디렉토리/"
를 만듭니다.
우선 복사 정보를 어떻게 얻어 오는지
다음과 같은 template.js 를 만들고
실행해 보겠습니다.
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
init.process({}, [], function(err, props) {
var files = init.filesToCopy(props);
console.log(files);
done();
});
};
----------------------------------------------------------------------------------------
실행 결과 입니다.
$ grunt-init test
:
{ 'test.txt': 'test/root/test.txt',
'파일_있는_디렉토리/파일.txt': 'test/root/파일_있는_디렉토리/파일.txt' }
:
Done, without errors.
결론적으로 init.filesToCopy() 함수는
root 디렉토리 밑에 있는 복사 할 수 있는 파일 목록만 가져 오게 됩니다.
이걸 이용해서 복사를 진행 하는 함수가 따로 있지요
복사할 파일이 없을 경우 관련된 빈 디렉토리 정보는 가져 오지 않습니다.
(**) 가져온 파일 목록 정보를 이용하여 복사 하기
이제 이렇게 가져온 정보를 이용하여 자동으로 복사를 실행하려면
다음과 같은 함수를 사용합니다.
init.copyAndProcess(files, props[, options]);
다음과 같은 template.js 를 만들고
실행해 보겠습니다.
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
init.process({}, [], function(err, props) {
var files = init.filesToCopy(props);
init.copyAndProcess(files, props);
done();
});
};
----------------------------------------------------------------------------------------
실행 결과 입니다.
$ grunt-init test
Running "init:test" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
Please answer the following:
Writing test.txt...OK
Writing 파일_있는_디렉토리/파일.txt...OK
Initialized from template "test".
Done, without errors.
다음과 같이 만들어짐을 확인 할 수 있습니다.
$ ls -al
합계 16
drwxrwxr-x 3 frog frog 4096 9월 13 15:54 .
drwxrwxrwx 4 frog frog 4096 9월 10 21:35 ..
-rw-rw-r-- 1 frog frog 35 9월 13 15:54 test.txt
drwxrwxr-x 2 frog frog 4096 9월 13 15:54 파일_있는_디렉토리
(**) 파일 복사시 퍼미션 및 소유자 와 특수 파일
파일 복사가 수행될 때 다음과 같은 점을 주의해야 합니다.
1. 특수 파일은 복사 대상이 되지 않습니다.
2. 파일 및 디렉토리 소유자는 현재 명령 실행 사용자가 됩니다.
3. sudo 명령을 이용하여 grunt-init 를 실행할 경우 소유자는 root 가 됩니다.
4. 일반 파일은 "-rw-rw-r--" 형태로 접근 권한이 설정됩니다.
5. 디렉토리는 "drwxrwxr-x: 형태로 접근 권한이 설정됩니다.
6. root 만 접근이 허용된 파일이나 디렉토리를 템플릿으로 만들었을때
sudo 없이 grunt-init 명령을 수행하면 실패합니다.
(**) 생성된 디렉토리에 파일이나 디렉토리가 이미 존재할 경우 경고하게 하는 방법
사용자의 실수가 있거나 다른 이유로
템플릿 적용 디렉토리에 파일이 있거난 동일한 파일이 있을 경우가 있습니다.
이런 경우 사용자에게 경고를 주고 중지되거나
--force 옵션을 준 상태로 grunt-init 를 실행해야 할 경우가 있습니다.
이럴 경우 사전에 체크해야 하는 것에 대한 것은
exports.warnOn
에 적용시키면 됩니다.
이 변수에는 문자열이나 문자열을 포함한 배열을 설정하면 됩니다.
어떤 파일이나 디렉토리가 있으면 경고를 주고 싶을때는 다음과 같이 하면 됩니다.
exports.warnOn = "**";
이 적용 패턴은 grunt-init 가 minimatch 모듈을 사용하기 때문인데
이와 관련된 사용법을 알고 싶다면 다음을 가면 됩니다.
https://github.com/isaacs/minimatch
여기서 몇가지 소개된 예를 보죠..
exports.warnOn = 'Gruntfile.js'; // Warn on a Gruntfile.js file.
exports.warnOn = '*.js'; // Warn on any .js file.
exports.warnOn = '*'; // Warn on any non-dotfile or non-dotdir.
exports.warnOn = '.*'; // Warn on any dotfile or dotdir.
exports.warnOn = '{.*,*}'; // Warn on any file or dir (dot or non-dot).
exports.warnOn = '!*/**'; // Warn on any file (ignoring dirs).
exports.warnOn = '*.{png,gif,jpg}'; // Warn on any image file.
// This is another way of writing the last example.
exports.warnOn = ['*.png', '*.gif', '*.jpg'];
exports.warnOn 은 한번만 선언할 수 있습니다.
다음은 사용 예제 template.js 입니다.
--[template.js]--------------------------------------------------------------------------
exports.warnOn = "**";
exports.template = function(grunt, init, done) {
init.process({}, [], function(err, props) {
var files = init.filesToCopy(props);
init.copyAndProcess(files, props);
done();
});
};
----------------------------------------------------------------------------------------
(**) 디렉토리 생성 방법
프로젝트 초기화 과정에서 새로운 디렉토리를 만들어야 할때가 있습니ㅏㄷ.
특히 빈 디렉토리는 파일 복사 함수 인 init.copyAndProcess() 로는 생성이 되지 않습니다.
특정 디렉토리를 만들려면 어떻게 해야 할까요?
이때는
grunt.file.mkdir(dirpath [, mode]);
함수를 사용하면 됩니다.
grunt.file 에는 여러가지 파일을 처리하기 위한 함수들이 준비되어 있는데
이중 디렉토리를 만드는 함수가 grunt.file.mkdir() 입니다.
이 함수의 첫번째 인자는 생성할 디렉토리를 지정하면 됩니다.
이때 디렉토리는 절대, 상대 경로 모두 다 가능합니다.
파일 허가는 두번째 인자로 줄 수 있는데 만약 이 값을 생략하면
0777 & (~process.umask())
의 연산이 내부적으로 처리됩니다.
절대 경로를 쓰지 않으면 프로세스의 현재 디렉토리를 기준으로 하부에 적용 됩니다.
다음은 사용 예제 template.js 입니다.
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
init.process({}, [], function(err, props) {
grunt.file.mkdir( '/tmp/dir1' );
grunt.file.mkdir( 'dir2' );
grunt.file.mkdir( 'dir3/dir4' );
done();
});
};
----------------------------------------------------------------------------------------
이 예제는
/tmp/dir1/ 이라는 디렉토리를 생성하고
현재 템플릿 적용 대상 디렉토리의 하부에
dir2/ 디렉토리와
dir3/dir4/ 디렉토리가 생성됩니다.
dir3/dir4/ 디렉토리 생성 예를 보듯이
하부 디렉토리의 상단 디렉토리는 없으면 자동으로 만들게 됩니다.
(**) 파일 이름 변경
root/ 하부 디렉토리의 파일들을 같은 이름으로 복사해 넣을 수 있지만
경우에 따라서는 이름을 변경할 필요가 있습니다.
이럴 경우
rename.json
에 다음 예와 같이 변경할 이름에 대한 규칙을 지정하면 됩니다.
현재 템플릿 디렉토리 위치는 ~/.grunt-init/test 이므로
~/.grunt-init/test/rename.json
에 다음 예 처럼 만들면 됩니다.
--[rename.json]--------------------------------------------------------------------------
{
"test.txt": "시험.txt",
"빈_디렉토리": "empty_dir"
}
----------------------------------------------------------------------------------------
template.js 은 다음과 같은 형태로 수행시키면 위 파일의 규칙이 자동 적용됩니다.
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
init.process({}, [], function(err, props) {
var files = init.filesToCopy(props);
console.log( files );
init.copyAndProcess(files, props);
done();
});
};
----------------------------------------------------------------------------------------
다음은 적용 예 입니다.
$ grunt-init test
Running "init:test" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
Please answer the following:
{ '시험.txt': 'test/root/test.txt',
'파일_있는_디렉토리/파일.txt': 'test/root/파일_있는_디렉토리/파일.txt' }
Writing 시험.txt...OK
Writing 파일_있는_디렉토리/파일.txt...OK
Initialized from template "test".
Done, without errors.
적용 예를 보면 알수 있듯이
rename.json 에는
"원본 파일 이름" : "변경 파일 이름",
이런식으로 지정하게 됩니다.
이 파일들은 root/ 하부를 최상위 루트 디렉토리가 되는 점을 주의 해야 합니다.
콘솔 메세지를 보면 아시겠지만
init.filesToCopy() 에서 반환되는 데이터가
이미 원본 파일이름과 타켓 파일이름간의 변화가 반영되어 있음을 알수 있습니다.
원본 파일명에 한개의 파일뿐만 아니라
경우에 따라서 사용자에 의해서 입력된 내용이나 시스템에 변경된 데이터를 기반으로
파일이름 변경과 관련된 처리가 필요할 때가 있습니다.
예를 들어 파일 버전과 같은 정보를 추가 하고 싶거나 디렉토리를 주어진 변수에서
추출하고 싶을 경우 입니다.
이런 경우 "{%= 변수명 %}를 사용하며 변수는 props 에 지정합니다.
예를 들어 목적지 디렉토리를 의미하는 변수 인 타켓을 props 에 다음과 같이 추가 했다면
props.타켓 = "변경";
이때 test.txt 파일의 목적지를 이에 맞게 변경한다면 다음과 같이 rename.json 에 추가 하면됩니다.
"test.txt" : "{%= 타켓 %}/test.txt"
다음은 이것을 적용한 rename.json 과 template.js 그리고 실행 결과 입니다.
--[rename.json]--------------------------------------------------------------------------
{
"test.txt" : "{%= 타켓 %}/test.txt"
}
----------------------------------------------------------------------------------------
--[template.js]--------------------------------------------------------------------------
exports.template = function(grunt, init, done) {
init.process({}, [], function(err, props) {
props.타켓 = "변경";
var files = init.filesToCopy(props);
console.log( files );
init.copyAndProcess(files, props);
done();
});
};
----------------------------------------------------------------------------------------
실행 결과 입니다.
$ grunt-init test
Running "init:test" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
Please answer the following:
{ '변경/test.txt': 'test/root/test.txt',
'test2.txt': 'test/root/test2.txt',
'test3.txt': 'test/root/test3.txt',
'파일_있는_디렉토리/파일.txt': 'test/root/파일_있는_디렉토리/파일.txt' }
Writing 변경/test.txt...OK
Writing test2.txt...OK
Writing test3.txt...OK
Writing 파일_있는_디렉토리/파일.txt...OK
Initialized from template "test".
Done, without errors.
$ ls -al
합계 24
drwxrwxr-x 4 frog frog 4096 9월 13 21:39 .
drwxrwxrwx 4 frog frog 4096 9월 10 21:35 ..
-rw-rw-r-- 1 frog frog 35 9월 13 21:39 test2.txt
-rw-rw-r-- 1 frog frog 35 9월 13 21:39 test3.txt
drwxrwxr-x 2 frog frog 4096 9월 13 21:39 변경
drwxrwxr-x 2 frog frog 4096 9월 13 21:39 파일_있는_디렉토리
(**) 기타 참고
다양한 상황에 처리 방법은 아무래도 일단 간단한 이해는 다음을 참조 하세요
Project Scaffolding 한글 번역
http://gruntjs-kr.herokuapp.com/project-scaffolding
Project Scaffolding 영문
http://gruntjs.com/project-scaffolding
Grunt API
http://gruntjs.com/api/grunt
가장 좋은 것은 다른 것들을 참고 해서 이해하고 쓰는 것입니다.
아직까지는 마땅한 문서가 없으니 말입니다. ㅠㅠ