귀찮은 일은 다 맡겨라. Grunt Javascript task runner.


특정 디렉토리 안에서 파일이 수정되면 컴파일을 해야 하는 상황이 생겼다.
글자 하나 바꿀 때마다 일일이 그런 수고를 하려니 여간 귀찮은 일이 아니다.
이런 때는 보통 bash 쉘 스크립트를 써 왔는데, os관계없이 어디서나 작동하고, 간편한 녀석을 찾다가 grunt(http://gruntjs.com)를 만나게 되었다. grunt는 프론트엔드 계통에선 꽤 이름을 날리는 녀석으로, 초반 웹 환경 구축을 도와주는 Yeoman(http://yeoman.io)의 한 부분이기도 하다. 요즘엔 grunt와 비슷한 gulp(http://gulpjs.com)이 성능 면에서 우수해서 인기가 좋은데, 여러 작업을 연달아서 할 때 pipe를 이용해서 I/O에 들어가는 시간을 줄이기 때문이란다. 그러나 나는 지금 연속적인 작업에 쓸 것도 아니고, grunt에서도 조만간(?) pipe를 지원할 예정이라고 하니, 커뮤니티가 활성화된 grunt를 쓰기로 했다.

grunt 설치

npm i grunt-cli -g

grunt를 실행할 해당 폴더에서 로컬 모듈 인스톨( 예 : static 폴더에서 실행시 static 폴더에 가서 인스톨)

npm install can-compile --save-dev
npm install grunt-shell --save-dev
npm install grunt-contrib-watch --save-dev
npm install time-grunt --save-dev


grunt 설정파일 예제 - Gruntfile.js

module.exports = function (grunt) {
    var static_path = '../static';

    // Project configuration.
    require('time-grunt')(grunt);
    grunt.initConfig({
//        pkg: grunt.file.readJSON('./package.json'),

    shell: {
      scsscompile:{ // scss컴파일은 외부 파이썬 스크립트를 이용해서 한다. 그래서 grunt-shell이 필요하다.
        command: 'python2 ../css.py '+static_path,
        options: {
            stdout: true
        }
      }
    },
    cancompile: { // mustache파일을 하나의 자바스크립트 파일로 합쳐주는 모듈
        dist: {
            src: [static_path+'/views/**/*.mustache'],
            out: static_path+'/js/views.build.js',
            wrapper: 'define(["can/view/mustache"], function(can) { {{{content}}} });'
        }
    },
    watch: { // grunt watch를 실행하면 해당되는 파일 변경사항이 생길 때 마다 스크립트를 자동으로 실행 한다.
      run_mustache: {
        files: [static_path+'/views/**/*.mustache'],
        tasks: ['cancompile']
      },
      run_scss: {
            files: [static_path+'/scss/**/*.scss'],
            tasks: ['shell:scsscompile']
      },
        options: { nospawn: true, livereload: true } // watch 성능이 향상된다.
    }
  });

//   grunt.registerTask('default', ['watch']); 로 설정하면, 파라미터 없이 grunt를 실행할 때 watch를 실행한다.
  grunt.registerTask('watch', ['watch']);
  grunt.registerTask('mustache', ['cancompile']);
  grunt.registerTask('scss', ['shell:scsscompile']);
  grunt.registerTask('static', ['cancompile','shell:scsscompile']);

  grunt.loadNpmTasks('can-compile');
  grunt.loadNpmTasks('grunt-shell');
  grunt.loadNpmTasks('grunt-contrib-watch');
};
이 간단한 설정 파일 하나로, 쓸만한 watcher가 만들어졌다. :D
앞으로도 귀찮은 작업을 떠넘길 때 종종 이용해야겠다.

문제 해결


npm이 sudo 권한 없이 설치되지 않을 때

http://stackoverflow.com/questions/16151018/npm-throws-error-without-sudo
sudo chown -R `whoami` ~/.npm
sudo chown -R `whoami` ~/node_modules
sudo chown -R `whoami` /usr/lib/node_modules


Grunt - Recursive process.nextTick detected. This will break in the next version of node. Please use setImmediate for recursive deferral. 에러가 발생할 때.

http://stackoverflow.com/questions/22285942/grunt-throw-recursive-process-nexttick-detected
grunt.registerTask('sass',['sass']); // 이런식으로 작업 이름과 등록 이름이 같을 때 문제가 발생한다.
grunt.registerTask('styles',['sass']); // 이런식으로 이름을 바꿔준다.

유용한 링크

http://gruntjs.com/
http://gulpjs.com
http://jaysoo.ca/2014/01/27/gruntjs-vs-gulpjs/
http://yeoman.io/blog/performance-optimization.html



by


Tags : , , , , , , ,

  • 재미있게 읽으셨나요?
    광고를 클릭해주시면,
    블로그 운영에 큰 도움이 됩니다!

잘 짜여진 자바스크립트 MVC 프레임워크. Canjs

Backbone, AngularJS, Spine….
그동안 나왔던 자바스크립트 프레임워크를 조금씩 건드려는 보았지만,
항상 아쉬움이 남았습니다.
이번에 Canjs를 써보니, 정말 잘 만들어진 프레임워크라는 생각이 들어요.
자바스크립트MVC 프레임워크를 써 볼까 생각 중이시라면, Canjs 한번 고려해 보세요.

can.Control

컨트롤러에서 이벤트 처리

"li .destroy {Event.destroy}": function( el, event) {
var todo = el.closet('li').data('todo);
todo.destroy();
event.stopPropagation();}
});
Events = {destroy : "click"};

css Selector, html.getElementBy, $(selector)가 Control의 셀렉터로 사용된다.

When the element your Control is bound to is removed from the DOM,
the Control destroys itself, cleaning up any bound event handlers.

can.Control 이 아는 이벤트.
* change
* click
* contextmenu
* dblclick
* focusin
* focusout
* keydown
* keyup
* keypress
* mousedown
* mouseenter
* mouseleave
* mousemove
* mouseout
* mouseover
* mouseup
* reset
* resize
* scroll
* select
* submit

링크 처리 방법(How can I make a link in Canjs?)

레이어(layer)
<a id="id" href="javascript://">href</a>

페이지(page)
<a id="id" href="#!id">href</a>

무스타치에서 콜백 받기(Mustache Element Callback)

{{data 'model'}}

경로 설정(RequireJS Paths)

requireJS can require is /can
requireJS에서 canJS를 쓸 땐 can.js를 패스로 잡고 사용해야 한다.
registerHelper이용을 위해서는 can/view/mustache가 필요하다.

404루트 잡기(How to define a catch all route to handle 404 in can js?)

http://stackoverflow.com/questions/13824665/how-to-define-a-catch-all-route-to-handle-404-in-can-js

can.route.bind('change', function(ev, newVal) {
if (newVal === 'route') {
var valid = false;
$.each(can.route.routes, function(k,v) {
if (new RegExp(v.test).test(window.location.hash)){
valid = true;
return; //exit loop
}
})
if (!valid) {
//handle the false route here
}
}
});

다국어 지원(Localization)

Localization is a good example of a custom helper you might implement in your application. The below example takes a given key and returns the localized value using jQuery Globalize.

1. Mustache.registerHelper('l10n', function(str, options){
2. return Globalize != undefined
3. ? Globalize.localize(str)
4. : str;
5. });

can.route에서 i18next 데이터를 받아오지 못할 때

can.when을 이용하여 초기화가 된 후 can라우팅 처리를 해 준다.


var lang =utils.getParam('lang');
var i18noption = {debug: true};
lang != undefined
? i18n
option.lng = lang
: i18n_option.lng = "en";
can.when(i18n.init(i18n_option)).then(function (){});

모델 관계(model associations)

https://forum.javascriptmvc.com/topic/questions-about-model-associations

<div class="header">
<h1>Association</h1>
</div>
<!-- YOUR CODE HERE -->
<div id="contacts"></div>

can.fixture("/contacts.json", function(){
return [{
'id': 1,
'name' : 'Justin Meyer',
'birthday': '1982-10-20',
tasks : [{
id: 1,
title: "write up model layer",
due: "2010-10-5"
}]},{
'id': 2,
'name' : 'Brian Moschel',
'birthday': '1983-11-10',
tasks : [{
id: 2,
title: "write up funcunit",
due: "2009-5-1"
},{
id: 3,
title: "test funcunit",
due: "2010-3-15"}]
},{
'id': 3,
'name' : 'Bobby Joe',
'birthday': '1980-2-10'
}];
})

can.Model.convert.date = function(raw){
if(typeof raw == 'string'){
var matches = raw.match(/(\d+)-(\d+)-(\d+)/);
return new Date( +matches[1],
(+matches[2])-1,
+matches[3] );
}else if(raw instanceof Date){
return raw;
}
};

// A task model that has a date
can.Model("Task",{
attributes : {
due : 'date',
}
},{
weeksPastDue : function(){
return Math.round( (new Date() - this.due) /
(1000*60*60*24*7 ) );
}
});

// A contact model that has many tasks
can.Model("Contact",{
attributes : {
birthday : 'date',
tasks: "Task.models"
},
findAll : "/contacts.json"
},{
ageThisYear : function(){
return new Date().getFullYear() -
this.birthday.getFullYear()
},
getBirthday : function(){
return ""+this.birthday.getFullYear()+
"-"+(this.birthday.getMonth()+1)+
"-"+this.birthday.getDate();
}
});

// Get all contacts and put them on the page
Contact.findAll({},function(contacts){
var contactsEl = can.$('#contacts');

can.each(contacts, function(contact){
var li = can.$('<li>')
.html(contact.name + " "+ contact.ageThisYear())
.appendTo(contactsEl);

var ul = can.$("<ul>");
var tasks = contact.attr('tasks')

if(tasks){
tasks.each(function(){
this.attr('contact', contact);
ul.append('<li>'+this.title+" "
+this.weeksPastDue()+' contact: '+ this.attr('contact.name') +'</li>')
});
}

ul.appendTo(li)
});
});

 

Canjs 웹사이트



by


Tags : , , , , , ,

  • 재미있게 읽으셨나요?
    광고를 클릭해주시면,
    블로그 운영에 큰 도움이 됩니다!

Javascript 모듈 관리엔 RequireJS

자바스크립트 모듈이 많아 관리가 어렵다면, RequireJS를 사용 해 보세요.

설정(Configuration)


require.config({
"baseUrl": "/static/js/vendor",
"paths": {
"app": "../app",
"bootstrap": [
'//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/js/bootstrap.min',
"bootstrap"
],
"jquery": [
"//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min",
"jquery-1.9.1.min"
],
"can": "can",
shim: {
"bootstrap": {
deps: ["jquery"],
exports: "$.fn.popover"
},
enforceDefine: true
}
}
});

모듈 정의(define function)


define(["jquery"],function($){
return {
getParam: function(paramname){
var value = new RegExp('[\?&]' + param
name + '=([^&#]*)').exec(window.location.href);
return value[1];
},
test: function(text){
console.log("test : "+text);
}
};
});

두 모듈을 한 파일에 넣는 방법(requirejs, two classes in one file)

http://stackoverflow.com/questions/9806940/requirejs-two-classes-in-one-file

define('test', ['jquery'], function() {
var exports = {};
exports.test1 = {
method1 : function () {
console.log("test1 - method 1");
},
method2 : function () {
console.log("test1 - method 2");
}
};
exports.test2 = {
method1 : function () {
console.log("test2 - method 1");
},
method2 : function () {
console.log("test2 - method 2");
}
};

return exports;
});

그리고 아래처럼 사용하면 된다.
require(['test'], function (test) {
var test1 = test.test1;
});

두 모듈을 한 파일에 넣는 다른 방법
http://blog.credera.com/topic/technology-solutions/java/modular-javascript-with-requirejs


define(['json!data/customers'], function(customers){
var getRow = function(id) {
return customers[id];
};

var getAll = function() {
return customers;
};

var update = function(data) {
// do something cool with the data
};
return {
get: getRow,
list: getAll,
update: update
};
});


오류 해결

  • Jquery CDN경로가 올바르지 못할 경우 Bootstrap로딩에 오류가 난다.
  • 같은 모듈(예: bootstrap)이 중복 로드될 경우 충돌이 일어난다.

RequireJS 홈페이지



by


Tags : , , , , , ,

  • 재미있게 읽으셨나요?
    광고를 클릭해주시면,
    블로그 운영에 큰 도움이 됩니다!

유용한 javascript 예제와 팁

URL 파라메터 받아오기(get PARAM)

http://www.codetreat.com/get-url-parameters-values-with-javascript

$.params = function(paramname){
var value = new RegExp('[\?&]' + param
name + '=([^&#]*)').exec(window.location.href);
return value[1];
}

Jquery 셀렉터 성능(JQUERY SELECTOR Performance)

http://jsperf.com/selectors-perf/6
cssSelector는 id 나 element와 함께 쓰지 않고 단독으로 사용한다.


var obj = document.getElementById("childDiv");
xxx = obj.getElementsByClassName("txtClass");

 

이벤트 처리

preventDefault() : 이벤트의 기본 행동을 막는다.
If this method is called, the default action of the event will not be triggered.
stopPropagation() : 상위 핸들러로 이벤트를 넘기지 않도록 막는다.
Prevents the event from bubbling up the DOM tree,
preventing any parent handlers from being notified of the event.

prepend() - 앞쪽에 내용을 붙인다.(to attach content as a prefix.)
append() - 뒷쪽에 내용을 붙인다. (to attach content as a suffix.)

배열 요소에서 데이터를 받고 싶을 땐 $()로 한번 더 감싼다.
$($(array_selector)[0]).data(something);


두번 submit되는 문제(prevent submit twice)
onclick이벤트 보다는 onsubmit 이벤트를 사용한다.
http://stackoverflow.com/questions/2830542/prevent-double-submission-of-forms-in-jquery

this.element.find('.save').attr('disabled','disabled');

// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function() {
$(this).bind('submit',function(e){
var $form = $(this);
if ($form.data('submitted') === true) {
// Previously submitted - don't submit again
e.preventDefault();
} else {
// Mark it so that the next submit can be ignored
$form.data('submitted', true);
}
// Keep chainability
return this;
};


JSON

Object to String
JSON.stringify(obj);
Parse JSON response
JSON.parse(obj);

JSON.parse()사용시 오류 : Uncaught SyntaxError: Unexpected token o
http://stackoverflow.com/questions/8081701/i-keep-getting-uncaught-syntaxerror-unexpected-token-o
데이터형이 올바르지 않아서 나는 오류이다.
Looks like jQuery takes a guess about the datatype.
It does the JSON parsing even though you're not calling getJSON()--
then when you try to call JSON.parse() on an object, you're getting the error.


loglevel

https://github.com/pimterry/loglevel
원하는 레벨의 로그를 콘솔 메시지로 보낸다.
켜고 끄는 것이 편리하다.
log.enableAll() and log.disableAll() methods.
* log.trace(msg)
* log.debug(msg)
* log.info(msg)
* log.warn(msg)
* log.error(msg)


JavaScript 디버깅

chrome dev tool -> Settings -> General -> Disable cache
Timeline을 이용하면 어떤 코드가 어플리케이션에 부하를 주는지 찾아준다.
메모리 누수가 발생한다면 찾아 개선한다.
Profiles도구에서 Heap Snapshot을 두 개 만들어 comparison view로 비교하면 성능 저하 원인을 찾기 편하다.



by


Tags : , , , , ,

  • 재미있게 읽으셨나요?
    광고를 클릭해주시면,
    블로그 운영에 큰 도움이 됩니다!

자바스크립트용 개발 문서 작성기. JSDOC

주석 달기


/** define namespace
* @namespace category
*/

/** use namespace with hash
* @name category#name
* @constructor
*/

/** use member of for constructor
*@memberof category#name
*/


설정(configuration)

remove all comments.
// You must remove the comments before adding these options to your .json file

{
"tags":{
"allowUnknownTags":true
},
"source":{
"include":[],
"exclude":[],
"includePattern":".+\.js(doc)?$",
"excludePattern":"(^|\/|\\)_"
},
"plugins":[],
"templates":{
"cleverLinks":false,
"monospaceLinks":false
},
"opts":{
"template":"default", // same as -t default
"encoding":"utf8", // same as -e utf8
"destination":"./out/", // same as -d ./out/
"recurse":true, // same as -r
"tutorials":"path/to/tutorials",// same as -u path/to/tutorials, default "" (no tutorials)
"query":"value", // same as -q value, default "" (no query)
"private":true, // same as -p
"lenient":true, // same as -l
// these can also be included, though you probably wouldn't bother
// putting these in conf.json rather than the command line as they cause
// JSDoc not to produce documentation.
"version":true, // same as --version or -v
"explain":true, // same as -X
"test":true, // same as -T
"help":true, // same as --help or -h
"verbose":true, // same as --verbose, only relevant to tests.
"match":"value", // same as --match value, only relevant to tests.
"nocolor":true // same as --nocolor, only relevant to tests
}
}


실행
jsdoc -c conf.json

JSDOC github



by


Tags : , , , , , ,

  • 재미있게 읽으셨나요?
    광고를 클릭해주시면,
    블로그 운영에 큰 도움이 됩니다!