Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

Grunt 0.4로 업그레이드하기

지난 달 자바스크립트 빌드 도구 Grunt에 대한 포스팅을 올렸었는데 지난 18일날 Grunt 0.4가 릴리즈되었다. 0.3.x에 대한 글을 포스팅할때도 한창 작업중이었기 때문에 어느정도 예상은 하고 있었지만 기본적인 구조가 변경되었기 때문에 앞서 올렸던 포스팅이 유효하지 않게 되었다. 슬슬 본격적으로 사용하려던 찰나에 대폭 업그래이드가 된 관계로 시간내서 0.4로 바꿔서 작업하고 있다.(새로 아키텍처를 짠 것이므로 당분간 구조가 또다시 크게 바뀔일은 없어보인다.) 이전 포스팅을 보완하는 글이므로 완전히 유효하진 않지만 이전 글을 먼저 읽어보길 권한다.


전체적인 구조의 변경
0.3.x에서는 grunt가 글로벌로 설치하는 CLI 도구로 다수의 기본 기능을 포함하고 있으면서 이 grunt가 현재 프로젝트의 grunt.js에 정의한 내용을 실행하는 방식으로 동작했었다. 그리고 내장된 기능이 아닌 경우에는 플러그인이라는 방식으로 프로젝트에 npm 모듈로 설치해서 로드해서 사용했었다. 솔직히 이러한 분리는 좀 불편한 구조였는데 0.4에서는 이부분이 대폭 개선되었다.(그래서 수정해 주어야 한다 ㅠㅠ)

grunt가 다음 3가지로 분리되었다.

  • grunt : 실제 grunt의 기능을 담고 있는 모듈이다. 기존에 글로벌에 설치했던 것에 비하면 이제는 프로젝트 내부에 의존성 모듈로 설치한다.(보통은 package.jsondevDependencies로 지정해서...) 실제 grunt의 기능은 대부분 여기에 담겨있고 프로젝트 내부에 설치하므로 한 PC에서 프로젝트별로 다른 버전의 grunt를 사용할 수 있게 되었다.
  • grunt-cli : 기존에 글로벌로 설치해서 터미널에서 grunt 명령어를 사용하던 기능이 grunt-cli라는 이름으로 분리되었다. grunt-cli는 별도의 특별한 기능은 담고 있지 않고 오직 현재 프로젝트의 grunt 모듈을 실행하는 역할만 하고 있다. 그래서 프로젝트에 grunt 0.4가 설치되어 있든 0.3이 설치되어 있든 모두 실행한다.
  • grunt-init : 기존에 grunt init이라는 명령어로 사용하던 기능이 별도의 모듈로 분리되었다.


기존에 grunt 0.3.x를 새로운 구조에 맞게 사용하기
앞에서 얘기했듯이 0.3.x는 글로벌에 설치했기 때문에 기존과 동일하게 npm install -g grunt로 설치하면 제대로 동작하지 않는다.(이제 grunt에는 CLI 기능이 들어있지 않다.) npm install -g grunt@0.3으로 설치하면 되기는 하지만 버전을 명시하는 것은 좀 귀찮은 일이다. 0.4.x는 grunt의 각 모듈의 의존성 없이 분리되었으므로 함께 사용할 수 있다.

npm install -g grunt-cli

위의 명령어로 grunt-cli를 글로벌에 설치한다.(기존에 grunt 0.3.x이 글로벌에 설치되어 있다면 npm rm -g grunt로 먼저 제거한다.)  그리고 grunt를 사용하던 프로젝트의 package.jsondevDependencies로 grunt 0.3.x버전을 명시해서 프로젝트의 의존성 모듈로 설치한다. 이렇게 바꾸면 기존의 grunt.js 파일을 바꾸지 않고도 그대로 사용할 수 있다.


grunt 0.4.x로 업그래이드하기
급한대로 위와 같이 사용하더라도 새로운 구조로 계속 개발될 것이로 플러그인들도 0.4에 맞게 개발될 것이므로 결국은 0.4.x에 맞게 수정을 해야한다. 플러그인 개발자가 아니라 grunt 사용자라고 하더라도 몇가지 변경사항들이 있다.

  • 일단 grunt의 설정내용을 담고 있던 파일인 grunt.jsGruntfile.js로 바뀌었다.(이건 굳이 왜 바꾸었는지 잘 모르겠다.) Gruntfile.coffee도 지원한다.
  • 0.3.x까지 기본으로 내장되어 있던 jshint, concat, watch, test등의 모든 기능이 별도의 모듈로 분리되었다.(grunt 저장소에서 grunt-contrib-*라는 이름으로 제공된다.) 그래서 이러한 기능중 사용하는 기능은 package.json에 의존성 모듈로(보통은 devDependencies로) 지정해서 프로젝트내에 설치해야 한다. 결과적으로는 플러그인이 활성화되면서 내장모듈을 제거하고 모두 플러그인 시스템으로 일원화 함으로써 사용자에게 선택권을 준 것인데 좋은 선택이라고 본다. 기존에 내장된 기능들은 다음 플러그인들로 분리되었다.

    플러그인으로 분리되었으므로 기존의 다른 플러그인과 마찬가지로 사용하려면 설치후 Gruntfile.js에서 grunt.loadNpmTasks('grunt-contrib-watch');로 불러들여서 사용해야 한다.
  • 각 플러그인의 설정 부분이 약간 표준화되었다. 모든 태스크는 filesoptions라는 항목을 공통적으로 갖는다. 이는 플러그인별로 약간씩 다른 설정을 공통화한 것으로 보이지만 어차피 각 플러그인의 가이드를 살펴보아야 한다. 그리고 디렉티브가 제거되었다.(디렉티브도 나쁘지 않아보이는데 왜 바꾼지는 잘 모르겠다.) 그래서 기존에 디렉티브(<config:<json:같은)는 다음과 같이 수정해서 사용해야 한다.

    • '<config:prop.subprop>' -> '<%= prop.subprop %>'
    • '<json:file.json>' -> grunt.file.readJSON('file.json')
    • '<file_template:file.js>' -> grunt.template.process(grunt.file.read('file.js'))

    그리고 <% %>같은 스크립틀릿방식을 사용해서 설정내에서 다른 설정값을 참조해서 사용할 수 있는 템플릿 기능이 추가되었다.
  • 대상 파일의 패턴을 지정할 때 제외할 대상을 지정하는 기능이 추가되었다.(기존에도 있기는 했던것 같은데 거의 제대로 동작하지 않았다.) 패턴앞에 !를 써주면 된다. '!**/node_modules/**/*.js'와 같이 지정하면 되고 기존의 대상에서 제외하는 방식으로 동작하므로 제외대상을 나중에 추가해 주어야 한다. 제외대상뒤에 '**/*.js'같은 규칙이 있다면 나중에 정의한 대상이 추가된다.
  • 별칭에 대한 태스크를 배열로 넘겨주어야 한다. 기존에 grunt.registerTask('default', 'jshint nodeunit concat');와 같이 사용했다면 이제는 grunt.registerTask('default', ['jshint', 'nodeunit', 'concat']);와 같이 사용해야 한다.(개인적으로는 이전 방식이 더 좋아보이기도....)


그외 다수의 내부 API가 변경되었으므로 grunt 플러그인을 작성한다면 마이그레이션 문서를 참고해야 한다. 기존과는 다르게 문서화가 꽤 되었으므로 이전보다는 플러그인 개발이 좀 나을듯 하다.(아직 개발해보진 않아서..) 그리고 기존에 플러그인들도 API가 변경되었으므로 0.4.x에 맞게 수정이 되어야하므로 당분간은 어느쪽 버전을 사용하던지 간에 각 플러그인의 문서나 히스토리를 참조해서 신경을 써야 할 것 같다.
2013/02/25 23:56 2013/02/25 23:56