블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2013. 1. 28. 09:05 Dev Environment/Build System

참조의 원문에 대해서 테스트 해하고 글을 정리해 본다. JavaScript에 대한 빌드 자동화를 ant로 하는 방법을 알아보자.


Javascript코드에 대해 Ant를 이용하여 Java의 Maven처럼 compile -> test -> build를 자동으로 수행할 수 있는 방법을 소개하고 있다. 하지만 현재는 gruntjs를 통하여 본 글에서 수행하였던 것을 보다 더 쉽게 할 수 있다. gruntjs에서 하나 단점은 shell command 호출인데 grunt-jasmine-task를 통해  극복하였다. 


1) JavaScript 대응 테스트 & 빌드자동화 도구들

  - 구문오류 체크 : JSLint / JSHint 

  - 유닛 또는 행휘 테스트 : Jasmine 또는 QUnit

  - API 문서 생성 : JSDoc

  - JS 파일압축 : YUI compressor 또는 Uglify.js 

  - 위의 내역들 빌드 통합 수행 : Ant


각 내역들에 대해서 순차적으로 수행하는 것을 Ant로 만들어 보도록 한다 


2) Ant로 구현한 Build Automation 구조

  - GitHub 소스 : git clone https://github.com/creynders/Build-JS-example.git  수행하여 로컬에 내려 받는다 

  - Ant로 하다 보니 1)에서 필요로 하는 모든 프레임워크 라이브러리를 다운로드 받아 놓아야 한다   

   

  

  - 디렉토리 구조 

    + bin : ant의 destination 디렉토리, 배포되는 최종 .js 파일이 위치함 

    + docs : API 문서 생성 디렉토리 

    + lib : 3-rd party 도구들 

    + specs : unit test 파일 존재 (test 파일이 specification 즉, 명세라는 표현이 맘에 든다)

    + src : 원본 소스 


3) 도구 설명

  - Apache Ant : 의존관계를 만들어서 타겟소스에 대하여 빌드를 수행케 하는 command-line 도구 (다운로드)

  - JSHint : JSLint 기반의 자바스크립트를 위한 code 품질 측정 도구. 에러나 문제가능성 있는 것을 찾아줌 (다운로드)

  - JSDoc Toolkit : HTML (XML, JSON, TEXT) 포멧으로 자바스크립트 애플리케이션에 대한 자동 문서 생성 도구 (다운로드)

  - YUI compressor : 압축을 통하여 네트워크 전송 성능을 높여준다. (다운로드)

  - Jasmine : 자바스크립트 코드 테스트를 위한 행위지향 개발 프레임워크(behavior-driven) (다운로드)

  - PhantomJS : 자바스크립트에 대해서 브라우져 없이(without GUI client) 없이 브라우징이 가능토록 해준다 (다운로드)

    + 즉, HTML 파일의 parse & rendering을 해준 다음 수행하고 결과값 읽고 ant에게 전달한다 (Headless WebKit)

    + OS 버전에 맞는 것을 다운로드 받은후 path를 잡아준다

    + 좀 더 쉽게 사용할 수 있도록 casperjs 도 있다. (요걸 사용하는게 좋을듯)


4) 소스 빌드 프로세스 (순서)

  - 원본 소스는 3개 존재 : buildexample.js, Baz.js, Foo.js

    + buildexample.js에서 namespace를 정의한다 

  - 모든 .js 파일에 대해 JSHint를 통해서 QA tool을 수행한다 

  - unit test를 수행한다

  - 3개의 개별 .js 파일을 1개의 .js 파일로 통합한다

  - 한개의 파일을 축소된(minified) .js 파일로 나눈다 

  - API 문서를 생성한다 

  - 통합하고 축소화한 파일명칭과 API 문서에 version number를 사용한다 


5) build.xml 작성하기 

  * properties : 상수 속성값을 설정. build.properties

  * tasks : ant 수행전 전처리 환경 내역들 

  * targets : 각 단계에 대하여 ant에 task로 구분하여 설정. build.xml


  - 버젼 (Version Number)

    + version.txt 파일을 읽는다 

    + 테스트 : ant version

<target name="version">
    <loadfile property="version.old" srcFile="version.txt" />
    <input message="Current version number is ${version.old}. Please enter the new version number:"
        defaultValue="${version.old}" addproperty="version"/>
    <echo file="version.txt" message="${version}" />
</target>


  - 속성 선언(Properties Declaration)

    + build.properties 파일을 사용한다 

    + build.uer.properties에 속성 재정의 한다 

<target name="properties">
    <tstamp>
        <format property="timestamp" pattern="yyyyMMddHHmmss"/>
    </tstamp>
    <!-- allow user-specific overrides -->
    <property file="build.user.properties"/>
    <property file="build.properties"/>
</target>


  - 빌드 디렉토리 생성 

   + build 디렉토리 생성한다 : 생성 temporary 파일이 위치한다. 빌드과정이 성공하면 bin에 파일이 위치한다.

<target name="create_build" depends="properties">
    <echo>Creating build...</echo>
    <delete dir="${dir.build}" />
    <mkdir dir="${dir.build.current}" />
    <echo>Finished.</echo>
</target>


  - 파일 하나로 합치기 (Consolidation)

   + src 디렉토리의 buildexample.js를 복사해서 build 디렉토리에 buildexample-.js 로 넣는다

   + 다른 파일들을 buildexample-.js 에 포함시키고 version과 timestamp를 넣는다 

<target name="consolidate" depends="properties, create_build">
    <echo>Consolidating...</echo>
    <copy file="${dir.src}/${name.base.js}" tofile="${file.consolidated.js}"/>
    <replace file="${file.consolidated.js}" token="%VERSION%" value="${version}"/>
    <replace file="${file.consolidated.js}" token="%TIMESTAMP%" value="${timestamp}"/>
    <concat id="srcfiles" destfile="${file.consolidated.js}" append="true>
        <fileset dir="${dir.src}" includes="**/*.js" excludes="${name.base.js}"/>
    </conca>
    <pathconvert pathsep=";" property="files" refid="srcfiles"/>
    <echo>${files}</echo>
    <echo>Finished.</echo>
</target>


  - JSHint 구문 검사 (코드 품질 측정)

    + PhantomJS를 통하여 수행한다 (PhantomJS API)

    + phantom-jshint-runner.js 파일로 jshint.js를 수행토록 한다 (lib 디렉토리에 존재)

<target name="jshint" depends="properties, consolidate">
    <echo>Checking syntax...</echo>
      <exec executable="phantomjs" dir="${basedir}" failonerror="true" resultproperty="specs.results">
          <arg line="'${file.jshint-runner.js}'" />
          <arg line="'${file.jshint.js}'" />
          <arg line="'${file.consolidated.js}'" />
          <arg line="${timeout.phantom}" />
      </exec>
    <echo>Finished</echo>
</target>


  - Jasmine 테스트에서 .html 파일 수행 (specs/index.html 존재)

    + PhantomJS를 통해 html을 읽어와 파싱하고 결과값을 ant로 반환한다

    + phantom-jasmine-runner.js 파일을 작성한다 (web 애플리케이션 headless test 방법)

<target name="specs" depends="properties">
    <echo>Running specs...</echo>
    <exec executable="phantomjs" dir="${basedir}" failonerror="true" resultproperty="specs.results">
        <arg line="'${file.jasmine-runner.js}'" />
        <arg line="'${file.specs-runner.html}'" />
        <arg line="${timeout.phantom}" />
    </exec>
    <echo>Finished.</echo>
</target>


  - 압축 (Minifying)

    + YUI compressor를 사용한다 (사용법)

    + command line 사용 :  java -jar yuicompressor-x.y.z.jar [options] [input files]

<target name="minify" depends="properties,create_build, consolidate">
    <echo>Minifying...</echo>
    <exec executable="java" dir="${basedir}" failonerror="true">
        <arg line="-jar '${file.yui_compressor.jar}'" />
        <arg line="--type js" />
        <arg line="-o '${file.minified.js}'" />
        <arg line="'${file.consolidated.js}'" />
    </exec>
    <echo>Finished</echo>
</target>


  - JSDoc 파일 생성 

    + command line 사용 : java -jar jsrun.jar app/run.js myscript.js -t=templates/jsdoc

<target name="jsdocs" depends="properties, consolidate">
    <echo>recreating docs folder...</echo>
    <delete dir="${dir.docs}"/>
    <mkdir dir="${dir.docs}" />
    <echo>Generating...</echo>
    <exec executable="java" dir="${basedir}">
        <arg line="-jar '${file.jsdoc_toolkit.jar}' '${dir.jsdoc_toolkit}/app/run.js'" />
        <!-- -d tells JSDoc toolkit where to output the documentation -->
        <arg line="-d='${dir.docs}'" />
        <!-- use the default template -->
        <arg line="-t='${dir.jsdoc_toolkit}/templates/jsdoc'" />
        <!-- Create an arg element for each file you want to include in the documentation -->
        <arg line="'${file.consolidated.js}'" />
    </exec>
    <echo>Finished</echo>
</target>


  - Clean up

    + build 디렉토리 파일이 빌드가 성공하면 bin 디렉토리로 옮긴다 

<target name="finish" depends="properties">
    <echo>Finishing...</echo>
    <delete dir="${dir.bin}" />
    <mkdir dir="${dir.bin}" />
    <move file="${dir.build.current}" tofile="${dir.bin}"/>
    <echo>Finished.</echo>
</target>


  - 각 target을 한꺼번에 수행하기 

<target name="build" depends="version, properties, create_build, consolidate, jshint, specs, minify, jsdocs, finish">
</target>


  - 수행 : ant build

$ ant build

Buildfile: d:\git-repositories\build-automation-javascript\build.xml


version:

    [input] Current version number is 1.0.0. Please enter the new version number: [1.0.0]

1.0.1   <== 직접 입력


properties:


create_build:

     [echo] Creating build...

   [delete] Deleting directory d:\git-repositories\build-automation-javascript\build

    [mkdir] Created dir: d:\git-repositories\build-automation-javascript\build\20130128154436

     [echo] Finished.


consolidate:

     [echo] Consolidating...

     [copy] Copying 1 file to d:\git-repositories\build-automation-javascript\build\20130128154436

     [echo] concat (d:\git-repositories\build-automation-javascript\src\Baz.js;d:\git-repositories\build-automation-javascript\src\Foo.js)

     [echo] Finished.


jshint:

     [echo] Checking syntax...

     [exec] read: d:\git-repositories\build-automation-javascript/build/20130128154436/buildexample-1.0.1.js

     [echo] Finished


specs:

     [echo] Running specs...

     [exec] open: file:///D:/git-repositories/build-automation-javascript/specs/index.html

     [exec] success

     [exec] finished in 218ms.

     [exec] 2 specs, 0 failures in 0.196s

     [echo] Finished.


minify:

     [echo] Minifying...

     [echo] Finished


jsdocs:

     [echo] recreating docs folder...

   [delete] Deleting directory d:\git-repositories\build-automation-javascript\docs

    [mkdir] Created dir: d:\git-repositories\build-automation-javascript\docs

     [echo] Generating...

     [echo] Finished


finish:

     [echo] Finishing...

   [delete] Deleting directory d:\git-repositories\build-automation-javascript\bin

    [mkdir] Created dir: d:\git-repositories\build-automation-javascript\bin

     [move] Moving 2 files to d:\git-repositories\build-automation-javascript

     [echo] Finished.


build:


BUILD SUCCESSFUL

Total time: 16 seconds


* 현재는 Grunt를 많이 사용하고 있다. (참조)


<참조>

원문 : Automating JavaScript builds

posted by 윤영식
2013. 1. 27. 23:22 Languages/CoffeeScript

Java 처럼 Class를 쉽게 만들어 보자. 클래스끼리 상속하여 재정의(overriding)하는 부분도 알아보자 


1) Class 만들기 

  - class 로 표현한다

  - 컴파일 내역에 IIFE(즉시실행함수)안에 다시 Dog을 생성하여 호출함 

// clazz.coffee
class Dog

// 컴파일 내역 : clazz.js 
// Generated by CoffeeScript 1.4.0
(function() {
  var Dog;
  Dog = (function() {
    function Dog() {}
    return Dog;
  })();
}).call(this);


  - constructor 로 생성자를 정의한다

  - constructor를 사용하면 Dog 생성자가 생성되고 다른 프로퍼티는 prototype에 정의된다 

// clazz2.coffee
class Dog
	constructor: (@name) ->
	growl: -> console.log '*growl*'

dog = new Dog 'Dowon'
console.log dog.name
dog.growl()

// 결과
D:\Development\coffeescript\4>coffee clazz2
Dowon
*growl*

// 컴파일 내역 : clazz2.js
// Generated by CoffeeScript 1.4.0
var Dog, dog;
Dog = (function() {
  function Dog(name) {
    this.name = name;
  }
  Dog.prototype.growl = function() {
    return console.log('*growl*');
  };
  return Dog;
})();

dog = new Dog('Dowon');
console.log(dog.name);
dog.growl();


2) 상속 관계 만들기 

  - extends 사용하여 상속관계 정의 한다

  - super() 사용하여 동일 메소드에 대한 재정의(Overriding)을 한다 

// clazz3.coffee
class Dog
	constructor: (@name) ->
	growl: -> console.log '*growl*'

class BorderCollie extends Dog
	constructor: (name, @tricks = []) ->
		super name
	perform: (trick) -> console.log if trick in @tricks then "#{@name} is #{trick}" else '*whine*'	
	growl: (person) ->
		if person is @master
			console.log '*bark*'
		else
			super() # Dog.growl()
 
dog = new Dog 'Dowon'

console.log dog.name
dog.growl()

dog2 = new BorderCollie 'YoungSik', ['playing', 'catching', 'rolling']
dog2.master = "Dowon"

console.log dog2.name
dog2.perform 'rolling'
dog2.growl("Dowon")
dog2.growl("Yun")

// 결과 
D:\Development\coffeescript\4>coffee clazz3
Dowon
*growl*
YoungSik
YoungSik is rolling
*bark*
*growl*

  - BorderCollie extends Dog 으로 상속 만들어 줌 

  - BorderCollie의 constructor안에 "super name" 을 호출하여 super(name)을 넣어 줌

  - BorderCollie의 grow안에서 super() 를 호출하여 super.growl()을 넣어 줌 

// 컴파일 내역 : clazz3.js 
// Generated by CoffeeScript 1.4.0
(function() {
  var BorderCollie, Dog, dog, dog2,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

  Dog = (function() {
    function Dog(name) {
      this.name = name;
    }
    Dog.prototype.growl = function() {
      return console.log('*growl*');
    };
    return Dog;
  })();

  BorderCollie = (function(_super) {
    __extends(BorderCollie, _super);
    function BorderCollie(name, tricks) {
      this.tricks = tricks != null ? tricks : [];
      BorderCollie.__super__.constructor.call(this, name);
    }
    BorderCollie.prototype.perform = function(trick) {
      return console.log(__indexOf.call(this.tricks, trick) >= 0 ? "" + this.name + " is " + trick : '*whine*');
    };

    BorderCollie.prototype.growl = function(person) {
      if (person === this.master) {
        return console.log('*bark*');
      } else {
        return BorderCollie.__super__.growl.call(this);
      }
    };
    return BorderCollie;
  })(Dog);

  dog = new Dog('Dowon');
  console.log(dog.name);
  dog.growl();

  dog2 = new BorderCollie('YoungSik', ['playing', 'catching', 'rolling']);
  dog2.master = "Dowon";
  console.log(dog2.name);
  dog2.perform('rolling');
  dog2.growl("Dowon");
  dog2.growl("Yun");

}).call(this);

* 파일 

coffeescript-4.zip


posted by 윤영식
2013. 1. 27. 21:14 Languages/CoffeeScript

객체의 function으로 호출될 때 this. 값을 어떻게 줄여서 표현 하는지와 prototype 도 알아보자. 또한 extends를 어떻게 사용하는지도 알아본다


1) this.name = name 표현하기 

  - this. 을 @ 로 표현한다 

// prototype.coffee
###
function Dog (name) {
	this.name = name;
}
Dog.prototype.growl = function() {
	console.log("*growl*");
}
r = new Dog("Dowon");
r.growl();
###

Dog = (name) -> 
	@name = name # this.name = name

d = new Dog 'Dowon'
console.log d.name

console.log '----same thing----'

Dog2 = (@name) ->  # 파라미터로 @name을 쓰면 위와 같은 표현임 

d2 = new Dog2 'Dowon'

console.log d2.name 

// 결과 
D:\Development\coffeescript\3>coffee prototype
Dowon
----same thing----
Dowon

// prototype.js 컴파일 내역
(function() {
  var Dog, Dog2, d, d2;
  Dog = function(name) {
    return this.name = name;
  };
  d = new Dog('Dowon');
  console.log(d.name);

  console.log('----same thing----');

  Dog2 = function(name) {
    this.name = name;
  };
  d2 = new Dog2('Dowon');
  console.log(d2.name);
}).call(this);


  - Tip : 만일 (function(){ 코딩내역 }).call(this)  을 없애고 싶다면 --bare 옵션을 사용하면 됨



2) prototype. 표현하기

  - prototype. 을 :: 로 표현한다  

// prototype2.coffee
Dog = (@name) ->

Dog.prototype.growl = ->
	console.log '*growl*'

d = new Dog 'Dowon'
d.growl()

console.log '----same thing----'

Dog2 = (@name) ->
	
Dog2::growl = ->   # :: 로 표현한다 
	console.log '*growl*'

d2 = new Dog2 'Dowon'
d2.growl()

//결과 
D:\Development\coffeescript\3>coffee prototype2
*growl*
----same thing----
*growl*

// 컴파일 내역 : prototype2.js
// Generated by CoffeeScript 1.4.0
(function() {
  var Dog, Dog2, d, d2;
  Dog = function(name) {
    this.name = name;
  };
  Dog.prototype.growl = function() {
    return console.log('*growl*');
  };
  d = new Dog('Dowon');
  d.growl();

  console.log('----same thing----');

  Dog2 = function(name) {
    this.name = name;
  };
  Dog2.prototype.growl = function() {
    return console.log('*growl*');
  };
  d2 = new Dog2('Dowon');
  d2.growl();
}).call(this);


3) 상속에 대한 표현

  - extends 를 통하여 상속받기

// extend.coffee
Dog = (@name) ->

Dog.prototype.growl = ->
	console.log '*growl*'

BorderCollie = (@name, @tricks) ->

BorderCollie extends Dog

BorderCollie::perform = (trick) ->
	if trick in @tricks
		console.log "#{@name} is #{trick}"
	else
		console.log '*whine*'

dowon = new BorderCollie 'Dowon', ['playing dead', 'catching', 'rolling']

dowon.perform 'catching'
dowon.growl()	

// 결과 
D:\Development\coffeescript\3>coffee extend
Dowon is catching
*growl*

// 컴파일 내역 : extend.js
// Generated by CoffeeScript 1.4.0
(function() {
  var BorderCollie, Dog, dowon,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

  Dog = function(name) {
    this.name = name;
  };
  Dog.prototype.growl = function() {
    return console.log('*growl*');
  };

  BorderCollie = function(name, tricks) {
    this.name = name;
    this.tricks = tricks;
  };

  __extends(BorderCollie, Dog);

  BorderCollie.prototype.perform = function(trick) {
    if (__indexOf.call(this.tricks, trick) >= 0) {
      return console.log("" + this.name + " is " + trick);
    } else {
      return console.log('*whine*');
    }
  };
  dowon = new BorderCollie('Dowon', ['playing dead', 'catching', 'rolling']);
  dowon.perform('catching');
  dowon.growl();
}).call(this);

 @ :: extends 를 익히자 ^^

coffeescript-3.zip


posted by 윤영식
2013. 1. 27. 20:10 Languages/JavaScript

this를 자바스크립트에서 만나게 되면 이게 어느 인스턴스의 this인지 파악하기가 난해하다. this는 context(Execute Context)와 Scope(유효범위)에 대한 부분을 잘 알아야 한다. 

 

1) function안에서 변수 접근 권한

function someFunc() {
    var _this = this;
    something.on("click", function() {
        console.log(_this);
    });
};

  - var _this = this; 문구는 왜 사용하는 것일까?

  - 첫번재 유효범위(First Scope)는 Global Scope 이다

    + 모든 변수(Variable)과 함수(Function)은 global이다. 

    + Global Scope는 window 객체이다 

    + window.x = 9; 라고 property를 설정하면 어디서나 접근이 가능하다 

 

function myFunc() {
    var x = 5;
});
console.log(x); // undefined

  - x 는 myFunc()안에서 초기화 되었고, myFunc()안에서만 접근이 가능하다 

  - var 키워드를 사용하지 않으면 자동으로 global 유효범위로 만들어진다 

  - var 키워드를 사용하면 function이 호출 될때 생성되는 context에 지역변수로(local variable) 포함된다

function myFunc() {
    x = 5;
});
console.log(x); // 5

 

  - Outer Function에 정의된 모든 변수는 다른 Inner Function에서 접근을 할 수 있다

function outer() {
   var x = 5;
   function inner() {
      console.log(x); // 5
   }
   inner();
}

 

  - Outer Function에서는 Inner Function의 변수를 접근 할 수 없다 

function outer() {
    var x = 5;
    function inner() {
        console.log(x); //5 
        var y = 10;
    }
    inner();
    console.log(y); //undefined
}

 

2) this의 유효범위 

  - this는 function이 호출될 때 자동으로 설정되는 변수이다 

  - 변수의 값은 function이 어떻게 호출되냐에 따라 틀려진다

  - 자바스크립트에서 함수를 호출하는 주된 몇가지 방법을 가지고 있다. (주로 사용하는 3가지 방법)

    + function이 method처럼 호출하기 : when a function is called as a method === 오브젝트.function은 메소드라 칭함

    + 자기자신위에 호출하기  : when a function is called on it's own === function 자체가 호출 오브젝트.function이 아닐 경우

    + event handler로서 호출하기 : when a function is called as an event handler == 이벤트 핸들러 수행시 호출되는 function

function foo() {
   console.log(this); //global object
};

myapp = {};
myapp.foo = function() {
   console.log(this); //points to myapp object
또는 (같은 내용)
myapp = {
   foo: function() {
     console.log(this); //points to myapp object
   }
}

var link = document.getElmentById("myId");
link.addEventListener("click", function() {
   console.log(this); //points to link
}, false);

  - addEventLister를 사용하여 function을 붙이면 this의 값이 변경된다 : this값은 caller로 부터 function에 전달된다 

 

 

$("myLink").on("click", function() {
    console.log(this); //points to myLink (as expected)
    var _this = this;  //store reference
    $.ajax({
        //ajax set up
        success: function() {
            console.log(this); //points to the global object. Huh?
            console.log(_this); //better!
        }
    });
});

  - myLink을 click하면 function이 수행된다. 이때 이벤트 핸들러로서 펑션의 this는 myLink DOM element의 참조로 설정된다

  - 그러나 ajax 정규호출의 this는 global object로 this 값을 설정한다. 이는 "event handler"도 "오브젝트 method"도 아닌 function이기 때문이다

  - 위와 같이 하는 것이 function을 호출하는 곳에서 this에 대한 값을 정확히 사용하기 위한 방법이다

 

 

3) Crockford on JavaScrpt

  - 백발의 중년 신사분이 JavaScript 언어에 대해 설명을 하고 있다. 우리나라에선 상상할 수 없는 일이겠지, 큰 세미나에 가봐야 젊은 친구들의 열정만을 엿볼 수 있는 부분을 생각해 보면 그들의 환경이 참 부럽다.

  - 15분경의 동영상에서 this의 호출에 대해서 보자 

  - 31분경에서 정확한 Closure 사용법 설명

 

 

<참조> 

  - 원문 : Scope and this in JavaScript

  - Scope 한글 블로깅 연재

  - Naver Dev JavaScript 클로져, 유효범위

posted by 윤영식
2013. 1. 26. 00:53 HTML5, CSS3/CSS

트위터의 개발자와 디자이너는 웹 디자이너나 개발자에게 좀 더 나은 삶을 주고자 Bootstrap이라 불리는 프레임워크를 내놓았다. 원문의 Bootstrap 시작하기를 사용해 보고 원문과 함께 정리해 본다. 역시 Bootstrap 사이트에서 따라하기 해보는 것이 가장 좋겠다. 



1. Bootstrap 활용하기 

  - 반응형 레이아웃의 CSS Grid

  - 서체, 버튼, 폼, 테이블, 이미지에 대한 CSS

  - 네비게이션, 프로그레스바, 경고창등의 사용자 인터페이스 컴포넌트

  - 홈페이지에서 다운로드를 할 수 있다

  - 디렉토리 구조 

  


 - HTML 첨부 하기

  

 

  - 반응형 웹사이트 : width가 줄어들면 메뉴형태가 자동 변경 및 레이아웃 자동 조절됨 (데모사이트)

    + 넓이가 넓을때 메뉴 예

     

    + 넓이가 줄어들때 메뉴 변경 예

     



2. CSS Base: Button 

  - <button type="button" class="btn">Default Button</button>

  


  - <button type="button" class="btn btn-success">Button</button>

    <button type="button" class="btn btn-warning">Button</button>

    <button type="button" class="btn btn-danger">Button</button>


  - Bootstrap 스타일은 LESS로 빌드되었다.

   @btnSuccessBackground:              #bce895; //#62c462;

   @btnSuccessBackgroundHighlight:     #a0cd78; //#51a351;


 - 그룹 버튼

  <div class="btn-group">

   <button type="button" class="btn btn-success">Button</button>

   <button type="button" class="btn btn-warning">Button</button>

   <button type="button" class="btn btn-danger">Button</button>

  </div>



3. JQuery Plugin

  - $('#container').tooltip({

      selector: "a[rel=tooltip]"

    });

  <p id="container">Jujubes icing oat cake 

<a href="#" rel="tooltip" title="a Lolipop Tiramissu?">lollipop tiramisu</a>. 

Tiramisu sesame snaps croissant chupa chups chupa chups chocolate cake candy sugar plum 

jelly beans. Lollipop pudding jelly sweet jujubes cookie pudding. Oat cake topping gummi 

bears oat cake. Muffin jelly-o cake sesame snaps ice cream cotton candy.</p>




4. Bootstrap으로 만들어진 괴안은 사이트 

  - leanIX

  - 프리미엄 테마 구매 사이트 (강력추천)

  - 오픈소스 약간 메트로 스타일 사이트



<참조>

  - 원문 : http://www.hongkiat.com/blog/twitter-bootstrap/

  - Bootstrap 시작하기

  - 적은 자원으로 훌륭한 웹사이트를 만들기 위한 9가지 아이디어

  - Bootstrap+Node.js로 SPA 만들기

  - Bootstrap 267 리소스

  - CSS Layout 기초 배우기

  - Bootstrap에 대한 다양한 사용예제 블로그

  - 최근 version 3이 나왔다. 마이그레이션 관련내용

  - Twitter Bootstrap을 확장한 Jasny Bootstrap

  - Facebook 스타일의 Bootstrap 제공

posted by 윤영식
2013. 1. 25. 09:20 Git, GitHub

Git을 배우려면 다음과 같이 하자. 


1. Git 레퍼런스와 강좌 사이트 

  - Pro Git 한글 번역본 완독

  - http://gitimmersion.com : 사이트 튜토리얼 수행 

  - http://gitready.com : 초심자, 중급, 고급 명령 수행

  - http://gitref.org : Git Reference 에서 바로 Cheat

  - Git Cheatsheet : UI에서 명령흐름을 직관적으로 보여줌


2. 도전과제 

  - PPT의 내용을 직접 수행해 보자  


posted by 윤영식
2013. 1. 25. 09:05 Git, GitHub/Git Lec02

Git commit 할 때 가이드 라인을 잘 지켜서 원격 저장소에 소스가 꼬이지 않도록 하고, 커밋된 내역을 잘 이해할 수 있도록 메세지 작성 정책에 대해 알아보자


  - 공백 문자를 깨끗이 제거 한다 : 확인)  git diff --check (공백 문자 오류확인가능, 잘 못 사용한 공백은 X 문자로 바꿈)

  - 최대한 수정사하을 하나의 주제로 요약한다

    + 여러 가지 이슈에 대한 수정사항을 하나의 커밋에 담지 말아라

    + 적절한 메세지를 작성한다 : 반드시 좋으 커밋 메세지를 담는다


  - 같은 파일의 다른부분을 수정하는 경우에는 git add -patch 명령을 이용한다 

    + 한 부분씩 나누어 Stage 영역에 저장해야 한다 


  - 메세지 작성

    + 첫줄엔 50자내로 간략히 요약

    + 두번째줄 비우고 세번째줄에 자세히 설명글 :개발동기, 구현 상황, 제약조건/상황

    + 글은 현재형을 사용한다 

    + 추가 내용은 한줄 띄우고 시작한다 

    + 잘쓰여진 커밋 메세지는 git log --no-merges 명령으로 꼭 살펴본다 

   예)

   

posted by 윤영식
2013. 1. 25. 08:44 Git, GitHub/Git Lec02

Git은 분산 버전관리 시스템이다. 서로 각자의 저장소를 가지고서 어떻게 충돌없이 잘 사용할 수 있을지 알아보자. (5장)


1) 중앙집중식 Workflow

  - 예전의 Subversion에 익숙한 사용자들의 방식

  - 예로 2명이 작업, 한명이 서버로 update코드 push하면 다른한명이 서버에서 update코드를 pull하여 merge 수행

  - 모든 사용자에게 서버에 push 권한을 부여해야 함

  - 그러나 중앙 서버 저장소로 a코드가 다른이에 의해 push 되었고, 다른사람이 a코드를 수정하여 push해도 Git은 이것을 방지해 준다. 즉, 다시 로컬로 fetch 또는 pull 하지 않으면 중앙 서버로 push 할 수 없게 막아준다




2) 통합관리(Integration-Manager) Workflow

  - 여러 리모트 저장소를 두고 한개는 R/W 가능, 다른 한개는 R만 가능하게 설정가능 하다

  - 이것은 GitHub의 운영방식이다. Pull Request 수행하면 통합관리자가 이것을 보고 기여자의 저장소에서 원본 저장소로 pull하여 merge 하게 된다

 



3) 독재자 보조관(Dictator and Lieutenants) Workflow

  - 2)번 형태의 확장판

  - Linux Kernel과 같은 큰 프로젝트에서 사용한다 

 


* 다양한 변종의 Workflow가 존재함 


<참조>

  - 성공적인 Git Branch 전략 (한글번역본 - dogfeet)

    + feature : 기능 만들기 

    + develop : 개발진행

    + release : 릴리즈 

    + hotfixes : 긴급 패치 

    + master : 원본 



posted by 윤영식
2013. 1. 24. 21:58 AngularJS/Concept

Twitter Bootstrap과 Node.js를 이용하여 Single Page App을 만들어 보자. 여기서는 Backbone.js를 배제하고 만드는 방법이다. 외국 블로깅 글을 따라서 실행보면서 정리한다. (총 3 part중 part 1)


서버단 코드 만들기


1) GitHub에서 소스 다운로드 및 사전 설정

  - 소스 다운로드 : $ git clone https://github.com/iatek/one-page-app.git

  - node.js 와 npm 이 설치되어 있어야 한다

  - Express 프레이워크 설치 : npm install express

  - ejs 템플릿 엔진 설치 : npm install ejs npm install ejs-locals

$ npm install ejs-locals

npm http GET https://registry.npmjs.org/ejs-locals

npm http 304 https://registry.npmjs.org/ejs-locals

ejs-locals@1.0.2 node_modules\ejs-locals


// ejs 설치 안하면 에러 발생함 

UserXP@NUKNALEA /d/Git_repositories/one-page-app (master)

$ node server


module.js:340

    throw err;

          ^

Error: Cannot find module 'ejs'

    at Function.Module._resolveFilename (module.js:338:15)

    at Function.Module._load (module.js:280:25)

    at Module.require (module.js:362:17)

    at require (module.js:378:17)

    at Object.<anonymous> (d:\Git_repositories\one-page-app\node_modules\ejs-locals\index.js:1:73)

    at Module._compile (module.js:449:26)

    at Object.Module._extensions..js (module.js:467:10)

    at Module.load (module.js:356:32)

    at Function.Module._load (module.js:312:12)

    at Module.require (module.js:362:17)


UserXP@NUKNALEA /d/Git_repositories/one-page-app (master)

$ npm install ejs

npm http GET https://registry.npmjs.org/ejs

npm http 304 https://registry.npmjs.org/ejs

ejs@0.8.3 node_modules\ejs


// 정상 수행됨 

UserXP@NUKNALEA /d/Git_repositories/one-page-app (master)

$ node server

Listening on port 4000 in development mode


// 로컬에서 브라우져 호출시 reponsive web으로 반응한다 -width를 줄이면 메뉴자동변경됨- (데모확인)


  


2) Express 환경파일 설정

  - app.js

app.js


// ejs template engine 사용
var express = require('express'),
	engine = require('ejs-locals'),   
	app = express();

// init export
exports.init = function(port) {
    app.locals({
		_layoutFile:'layout.ejs'
	})
	
    app.configure(function(){
    	app.set('views', __dirname + '/views');
    	app.set('view engine', 'ejs');
    	app.use(express.bodyParser());
    	app.use(express.methodOverride());
        // Static pages 서비스 
        app.use(express.static(__dirname + '/static'));
        // Session과 Cookie 관리 
        app.use(express.cookieParser());
        app.use(express.cookieSession({cookie:{path:'/',httpOnly:true,maxAge:null},secret:'skeletor'}));
    	app.use(app.router);
    	//app.enable("jsonp callback");
    });

    app.engine('ejs', engine);	
    app.configure('development', function(){
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
        // app.use(express.logger({ format: ':method :url' }));
    });

    app.configure('production', function(){
	   app.use(express.errorHandler()); 
    });

    app.use(function(err, req, res, next){
	   res.render('500.ejs', { locals: { error: err },status: 500 });	
    });
	
    server = app.listen(port);
    console.log("Listening on port %d in %s mode", server.address().port, app.settings.env);
    return app;
}


3) Routing 만들기 

  - server.js : app이 시작하는 초기 파일이다. app route를 정의한다

server.js


  - "app.get", "app.post", "app.del" 을 사용한다 

  - 4개의 HTTP get routing 호출  

    /(default homepage)

    /login

    /logout

    /admin

var port = process.env.PORT || 4000,
    app = require('./app').init(port),
	dirty = require('dirty');
	
var locals = {
	author:'in1'
	// add other vars here
};

var userDb = dirty('user.db');

app.get('/', function(req,res){
    locals.date = new Date().toLocaleDateString();
	
	var appDb = dirty('app.db'),
		sectionsDb;
	
	appDb.on('load', function() {
		sectionsDb = dirty('sections.db');
		sectionsDb.on('load', function() {
			/*
			sectionsDb.forEach(function(key, val) {
				console.log('Found key: %s, val: %j', key, val);
			});
			*/
			res.render('index.ejs', {locals:locals,sections:sectionsDb,app:appDb.get('app'),page:appDb.get('page')});
		});
	});
});
... 중략 ...
app.post('/contact', function(req,res){
	appDb = dirty('app.db');
	console.log('contact form submit.');
	
	appDb.on('load', function() {
		console.log('Form submitted.');
		res.send('Thank you.');
	});
});

/* The 404 Route (ALWAYS Keep this as the last route) */
app.get('/*', function(req, res){
    res.render('404.ejs', locals);
});


4) Persistence 유지 하기 

  - Node-Dirty를 이용하여 disk log형태로 가벼운 데이터베이스겸으로 사용한다

  - 5개의 database 파일안에 관련 표현할 내역이 .db파일에 들어있음 (백만 record 미만 사용하는 앱에 적당)

    app.db : 애플리케이션 래벨 설정

    sections.db : 섹션 설정과 내용 저장

    user.db : 사용자 credential 과 admin 권한

    snippet.db : 미리 생성할 섹션 내용을 위한 코드 조각 저장

    post.db : 연락처와 이메일 폼 결과 저장 

  - server.js에서 require('dirty')


> 다음은 UI Part to be continued...


<참조>

  - Single Page App with Twitter Bootstrap and Node.js : 데모사이트

  - One page app GitHub 소스

  - SPA를 위한 storage : node-dirty

  - Node.js 기초 강의 동영상

  - Node의 MVC Framework : Express.js

  - Bootstrap 확장판

posted by 윤영식
2013. 1. 24. 21:57 NodeJS

Node.js 기반하에 Front-end 개발을 할 경우 텍스트 기반의 툴들을 알아보자.

  - Node.js로 개발할 때 commmand-line 유틸리티가 상당히 많고 이는 front-end 개발자에게 도움을 준다 

  - 다섯가지의 Node CLI 유틸리티를 알아보자 

  - Node와 npm이 설치되어 있음을 전제로 한다 



1) UglifyJS

  - JavaScript를 압축하고 사이즈를 최소화해준다 (네트워크 전송량을 작게해서 성능향상을 기대할 수 있다) 

  - UglifyJS의 기능을 바로 웹상에서 사용하기   

- 설치하기 

D:\Development>npm install -g uglify-js

npm http GET https://registry.npmjs.org/uglify-js


- 명령어

uglifyjs -b -o <output file> <input file>


-b 옵션 : beautifies

D:\Development>uglifyjs -b -o d:\development\if_uglify.js d:\development\if.js

D:\Development>uglifyjs -o d:\development\if_uglify.js d:\development\if.js

// coffee에서 컴파일된 javascript 소스 
// Generated by CoffeeScript 1.4.0
(function() {
  var x;

  x = 4;

  if ((0 <= x && x <= 10)) {
    console.log("true");
  }

}).call(this);

// -b 옵션을 주었을 경우 
(function() {
    var x;
    x = 4;
    if (0 <= x && x <= 10) {
        console.log("true");
    }
}).call(this);

// -b 옵션을 주지 않았을 경우 
(function(){var x;x=4;if(0<=x&&x<=10){console.log("true")}}).call(this);



2) Grunt

  - JavaScript 프로젝트를 위한 task-based command line 빌드 툴

  - 미리 정의된 템플릿을 만들어 준다 : 테스트(QUnit & PhantomJS), Lint파일(JSHint), 최소화파일(UglifyJS), Static WebServer

  - 템플릿들은 Node Server에서 구동할 수 있다. (jQuery 템플릿도 가능)

  - 시작하기 가이드

  - Grunt 간단 소개

  - 좀 더 자세한 튜토리얼

- 설치하기 : 상당히 많은 모듈을 함께 설치한다

D:\Development>npm install -g grunt

... 중략 ...

grunt

├── dateformat@1.0.2-1.2.3

├── colors@0.6.0-1

├── semver@1.0.14

├── async@0.1.22

├── hooker@0.2.3

├── underscore@1.2.4

├── underscore.string@2.1.1

├── uglify-js@1.3.4

├── nopt@1.0.10 (abbrev@1.0.4)

├── gzip-js@0.3.1 (crc32@0.2.2, deflate-js@0.2.2)

├── temporary@0.0.5 (package@1.0.1)

├── glob-whatev@0.1.8 (minimatch@0.2.9)

├── connect@2.4.6 (fresh@0.1.0, cookie@0.0.4, pause@0.0.1, bytes@0.1.0, crc@0.2.0, debug@0.7.0, formidable@1.0.11, qs@0.5.1, send@0.0.4)

├── prompt@0.1.12 (pkginfo@0.3.0, winston@0.5.11)

├── jshint@0.9.1 (minimatch@0.0.5, cli@0.4.3)

└── nodeunit@0.7.4 (tap@0.4.0)


- jquery 템플릿 만들어보기

// 신규 디렉토리를 만듦

D:\Development\grunt>mkdir jquery

// 디렉토리 이동

D:\Development\grunt>cd jquery

// 명령 수행 

D:\Development\grunt\jquery>grunt init:jquery

Running "init:jquery" (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 question


will leave its value blank.


"jquery" template notes:

Project name should not contain "jquery" or "js" and should be a unique ID not already in use at plugins.jquery.com. Project title should be a human-readable title, and doesn't need to contain the word "jQuery", although it may. For

example, a plugin titled "Awesome jQuery Plugin" might have the name "awesome-plugin". For more information please see the documentation at https://github.com/jquery/plugins.jquery.com/blob/master/docs/manifest.md


// 질문에 값을 입력한다 

Please answer the following:

[?] Project name jQueryTest

[?] Project title (jQuerytest) DoWonQuery

[?] Description (The best jQuery plugin ever.)

[?] Version (0.1.0)

[?] Project git repository (git://github.com/UserXP/jQueryTest.git)

[?] Project homepage (https://github.com/UserXP/jQueryTest)

[?] Project issues tracker (https://github.com/UserXP/jQueryTest/issues)

[?] Licenses (MIT)

[?] Author name (none)

[?] Author email (none)

[?] Author url (none)

[?] Required jQuery version (*)

[?] Do you need to make any changes to the above before continuing? (y/N)


Writing CONTRIBUTING.md...OK

Writing grunt.js...OK

Writing libs/jquery/jquery.js...OK

Writing libs/jquery-loader.js...OK

Writing libs/qunit/qunit.css...OK

Writing libs/qunit/qunit.js...OK

Writing README.md...OK

Writing src/jQueryTest.js...OK

Writing test/jQueryTest.html...OK

Writing test/jQueryTest_test.js...OK

Writing LICENSE-MIT...OK


Initialized from template "jquery".

Done, without errors.

// 필요한 라이브러리와 grunt.js 환경파일이 자동 생성된다 

D:\Development\grunt\jquery>dir

2013-01-25  오후 05:32    <DIR>          .

2013-01-25  오후 05:32    <DIR>          ..

2013-01-25  오후 05:32             2,091 CONTRIBUTING.md

2013-01-25  오후 05:32             1,547 grunt.js

2013-01-25  오후 05:32               551 jQueryTest.jquery.json

2013-01-25  오후 05:32    <DIR>          libs

2013-01-25  오후 05:32             1,066 LICENSE-MIT

2013-01-25  오후 05:32               199 package.json

2013-01-25  오후 05:32               604 README.md

2013-01-25  오후 05:32    <DIR>          src

2013-01-25  오후 05:32    <DIR>          test

               6개 파일               6,058 바이트

               5개 디렉터리  37,356,339,200 바이트 남음



3) GruntIcon

  - css icon 만들기

  - PhantomJS가 필요하다 (phantomjs.exe 파일을 프로젝트 폴더에 놓는다)

  - grunt.js 파일에 GruntIcon 로드 설정을 한다

- 설치

D:\Development\grunt\jquery>npm install grunt-grunticon

npm http GET https://registry.npmjs.org/grunt-grunticon

... 중략 ...

grunt-grunticon@0.1.4 node_modules\grunt-grunticon

└── grunt@0.3.17 (dateformat@1.0.2-1.2.3, colors@0.6.0-1, semver@1.0.14, asyn

c@0.1.22, hooker@0.2.3, underscore@1.2.4, underscore.string@2.1.1, uglify-js@1.3

.4, nopt@1.0.10, gzip-js@0.3.1, temporary@0.0.5, glob-whatev@0.1.8, connect@2.4.

6, jshint@0.9.1, prompt@0.1.12, nodeunit@0.7.4)


- grunt.js 파일 설정 넣기 

/*global module:false*/

module.exports = function(grunt) {


  // Project configuration.

  grunt.initConfig({

    pkg: '<json:jQueryTest.jquery.json>',

    meta: {

      banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +

        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +

        '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +

        '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +

        ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'

    },

    concat: {

      dist: {

        src: ['<banner:meta.banner>', '<file_strip_banner:src/<%= pkg.name %>.js>'],

        dest: 'dist/<%= pkg.name %>.js'

      }

    },

    min: {

      dist: {

        src: ['<banner:meta.banner>', '<config:concat.dist.dest>'],

        dest: 'dist/<%= pkg.name %>.min.js'

      }

    },

    qunit: {

      files: ['test/**/*.html']

    },

    lint: {

      files: ['grunt.js', 'src/**/*.js', 'test/**/*.js']

    },

    watch: {

      files: '<config:lint.files>',

      tasks: 'lint qunit'

    },

    jshint: {

      options: {

        curly: true,

        eqeqeq: true,

        immed: true,

        latedef: true,

        newcap: true,

        noarg: true,

        sub: true,

        undef: true,

        boss: true,

        eqnull: true,

        browser: true

      },

      globals: {

        jQuery: true

      }

    },

    uglify: {},

    grunticon: {

      src: "src/icons-source/",

      dest: "src/icons-output/"

    }

  });


  // Default task.

  grunt.registerTask('default', 'lint qunit concat min');

  grunt.loadNpmTasks('grunt-grunticon');

};


- grunticon 생성 (윈도우: grunt.cmd, 리눅스 : grunt )
D:\Development\grunt\jquery>grunt.cmd grunticon
Running "grunticon" task
Look, it's a grunticon!

grunticon now minifying the stylesheet loader source.
grunticon loader file created.
grunticon now spawning phantomjs...
Done, without errors.

// grunt.js에 환경설정한 icon-output이 생성된다 



4) JSHint

  - JavaScript의 코드 품질 측정 툴 

  - 잠재적인 위험 요소를 찾아준다 : 웹기반 측정도 가능

- 설치

D:\Development\grunt\jquery>npm install -g jshint

npm http GET https://registry.npmjs.org/jshint

npm http 304 https://registry.npmjs.org/jshint

npm http GET https://registry.npmjs.org/cli/0.4.3

npm http GET https://registry.npmjs.org/minimatch

npm http 304 https://registry.npmjs.org/cli/0.4.3

npm http 304 https://registry.npmjs.org/minimatch

npm http GET https://registry.npmjs.org/lru-cache

npm http GET https://registry.npmjs.org/glob

npm http 304 https://registry.npmjs.org/lru-cache

npm http 304 https://registry.npmjs.org/glob

npm http GET https://registry.npmjs.org/minimatch

npm http GET https://registry.npmjs.org/graceful-fs

npm http GET https://registry.npmjs.org/inherits

npm http 304 https://registry.npmjs.org/graceful-fs

npm http 304 https://registry.npmjs.org/inherits

npm http 304 https://registry.npmjs.org/minimatch

npm http GET https://registry.npmjs.org/lru-cache

npm http GET https://registry.npmjs.org/sigmund

npm http 304 https://registry.npmjs.org/sigmund

npm http 304 https://registry.npmjs.org/lru-cache

C:\Documents and Settings\UserXP\Application Data\npm\jshint -> C:\Documents and

 Settings\UserXP\Application Data\npm\node_modules\jshint\bin\hint

jshint@0.9.1 C:\Documents and Settings\UserXP\Application Data\npm\node_modules\

jshint

├── minimatch@0.0.5 (lru-cache@1.0.6)

└── cli@0.4.3 (glob@3.1.17)

 

- 수행 : jshint <file to check>

D:\Development>jshint loop.js

loop.js: line 15, col 2, Mixed spaces and tabs.


1 error



5) Node-static

  - 로컬 테스트시에 캐쉬/헤더 별도 셋팅가능

  - 로컬 웹서버로서 아무 디렉토리에서나 시작하면 ROOT 디렉토리가 된다

- 설치

D:\Development>npm install -g node-static

npm http GET https://registry.npmjs.org/node-static

npm http 200 https://registry.npmjs.org/node-static

npm http GET https://registry.npmjs.org/node-static/-/node-static-0.6.7.tgz

npm http 200 https://registry.npmjs.org/node-static/-/node-static-0.6.7.tgz

npm http GET https://registry.npmjs.org/colors

npm http GET https://registry.npmjs.org/optimist

npm http 304 https://registry.npmjs.org/colors

npm http 304 https://registry.npmjs.org/optimist

npm http GET https://registry.npmjs.org/wordwrap

npm http 304 https://registry.npmjs.org/wordwrap

C:\Documents and Settings\UserXP\Application Data\npm\static -> C:\Documents and

 Settings\UserXP\Application Data\npm\node_modules\node-static\bin\cli.js

node-static@0.6.7 C:\Documents and Settings\UserXP\Application Data\npm\node_mod

ules\node-static

├── colors@0.6.0-1

└── optimist@0.3.5 (wordwrap@0.0.2)


- 수행 : 아무 디렉토리나 가서 수행 -> 브라우져에서 호출한다 

D:\Development>static

serving "." at http://127.0.0.1:8080


- 또는 8080이 사용중이라면 옵션을 준다 

static -p <port>  <directory> 


* CLI를 이용하여 브라우져안에서 클라이언트단 유닛 테스트 도구 : bunyip


<참조> 

'NodeJS' 카테고리의 다른 글

[Node.js] 라이언 일병 이야기 이해하기  (0) 2012.12.08
[Node.js] 역사와 기초  (0) 2012.12.08
[Node.js] 시작하기  (1) 2012.10.30
posted by 윤영식
2013. 1. 24. 21:56 MongoDB/Concept

원본 데이터로 부터 데이터를 수집하고 이를 데이터베이스에 저장한 후에 레포팅 가공을 할 경우 MongoDB를 사용한 예를 보도록 하자


1) MongoDB의 Scale-out을 통하여 Sharding에 대한 아키텍쳐 Case


  - 데이터를 수집하는 Collection Server의 Write에 대하여 Scaleble Writes가 가능해 진다 

  - 레포팅 서버로 부터 데이터를 읽어오는 것에 유연하게 대처한다 

  - 각 내역은 Mongos를 통하여 routing 되어서 여러 MongoD의 Master를 통하여 Sharding 된다 


2) MongoDB를 통해서 실시간 분석을 수행하는 방법

    

3) 실시간 Logging 저장소로써 MongoDB를 사용할 경우 아키텍쳐


  - HTTP 로그 Header 정보를 key:value 도큐먼트로 저장 

  - Filtering하여 화면에 출력 

'MongoDB > Concept' 카테고리의 다른 글

[MongoDB] 왜 나오게 된것일까  (0) 2013.10.10
[MongoDB] 설치후 사용하기 (2)  (0) 2013.07.20
[MongoDB] 설치후 사용하기 (1)  (0) 2012.12.29
[MongoDB] 기본 개념, Replica/Shard  (0) 2012.12.29
posted by 윤영식
2013. 1. 24. 21:53 Languages/CoffeeScript

if, switch, for loop 구문등을 사용해 보자 


1) 조건문

  - yes : true, no : false, if not : unless 

  - if not 과 yes no 사용하기 : if2.coffee

machine = 
	running: no
	turnOn: -> this.running = yes
	turnOff: -> this.running = no

###
if not === unless
###
if not machine.running
	machine.turnOn()

machine.turnOne() if not machine.running
console.log machine.running

unless machine.running
	machine.turnOn()
else
	machine.turnOff()

// Generated by CoffeeScript 1.4.0
(function() {
  var machine;
  machine = {
    running: false,
    turnOn: function() {
      return this.running = true;
    },
    turnOff: function() {
      return this.running = false;
    }
  };

  /*
  if not === unless
  */
  if (!machine.running) {
    machine.turnOn();
  }
  if (!machine.running) {
    machine.turnOne();
  }
  console.log(machine.running);

  if (!machine.running) {
    machine.turnOn();
  } else {
    machine.turnOff();
  }
}).call(this);

// 결과 : 9번째 줄과 12번째 줄은 같은 구문이다 

D:\Development\coffeescript>coffee if2

true



  - switch ~ then 구문 사용하기 : switch.coffee

person = 
	name: "dowon"
	job: "programmer"

giveWork = (person) ->	
	switch person.job
	  when "programmer"
	  	console.log "1> Here's your code work, #{person.name}"
	  when "designer"
	  	console.log "1> Here's your design work, #{person.name}"
	  else 
	  	console.log "1> Um, do you work here?"	  	
giveWork person	  	

# or
person.job = "designer"
giveWork2 = (person) ->	
	switch person.job 
	  when "programmer" then console.log "2> Here's your code work, #{person.name}"
	  when "designer" then console.log "2> Here's your design work, #{person.name}"
	  else console.log "2> Um, do you work here?"
giveWork2 person

// Generated by CoffeeScript 1.4.0
var giveWork, giveWork2, person;
person = {
  name: "dowon",
  job: "programmer"
};

giveWork = function(person) {
  switch (person.job) {
    case "programmer":
      return console.log("1> Here's your code work, " + person.name);
    case "designer":
      return console.log("1> Here's your design work, " + person.name);
    default:
      return console.log("1> Um, do you work here?");
  }
};
giveWork(person);

person.job = "designer";
giveWork2 = function(person) {
  switch (person.job) {
    case "programmer":
      return console.log("2> Here's your code work, " + person.name);
    case "designer":
      return console.log("2> Here's your design work, " + person.name);
    default:
      return console.log("2> Um, do you work here?");
  }
};
giveWork2(person);

// 결과 

D:\Development\coffeescript>coffee switch

1> Here's your code work, dowon

2> Here's your design work, dowon



  - if 문 console.log를 앞으로 뽑아내기 : member.coffee

     + 컴파일 내역

     + 결과는 같지만 구문 코드는 틀리다 

     + 2번과 3번 구문이 제일 짧다 

person1 = 
	name: "dowon"
	relationship: "friend"
person2 = 
	name: "youngsik"
	relationship: "boss"

greet = (person) ->
	if person.relationship is "friend"
		console.log "1> hi, #{person.name}!"
	else if person.relationship is "boss"
		console.log "1> hello, papa!"
greet person1
greet person2
# or
greet2 = (person) ->
	msg = if person.relationship is "friend"
		"2> hi, #{person.name}!"
	else if person.relationship is "boss"
		"2> hello, papa!"
	console.log msg
greet2 person1
greet2 person2
# or
greet3 = (person) ->
	console.log if person.relationship is "friend"
		"3> hi, #{person.name}!"
	else if person.relationship is "boss"
		"3> hello, papa!"
greet3 person1
greet3 person2
# or
greet4 = (person) ->
	msg = switch person.relationship 
		when "friend" then "4> hi, #{person.name}!"
		when "boss" then "4> hello, papa!"
	console.log msg
greet4 person1
greet4 person2

// Generated by CoffeeScript 1.4.0
(function() {
  var greet, greet2, greet3, greet4, person1, person2;
  person1 = {
    name: "dowon",
    relationship: "friend"
  };
  person2 = {
    name: "youngsik",
    relationship: "boss"
  };

  greet = function(person) {
    if (person.relationship === "friend") {
      return console.log("1> hi, " + person.name + "!");
    } else if (person.relationship === "boss") {
      return console.log("1> hello, papa!");
    }
  };
  greet(person1);
  greet(person2);

  greet2 = function(person) {
    var msg;
    msg = person.relationship === "friend" ? "2> hi, " + person.name + "!" : person.relationship === "boss" ? "2> hello, papa!" : void 0;
    return console.log(msg);
  };
  greet2(person1);
  greet2(person2);

  greet3 = function(person) {
    return console.log(person.relationship === "friend" ? "3> hi, " + person.name + "!" : person.relationship === "boss" ? "3> hello, papa!" : void 0);
  };
  greet3(person1);
  greet3(person2);

  greet4 = function(person) {
    var msg;
    msg = (function() {
      switch (person.relationship) {
        case "friend":
          return "4> hi, " + person.name + "!";
        case "boss":
          return "4> hello, papa!";
      }
    })();
    return console.log(msg);
  };
  greet4(person1);
  greet4(person2);
}).call(this);

// 결과 : 전부 동일한 결과를 출력한다 

D:\Development\coffeescript>coffee member

1> hi, dowon!

1> hello, papa!

2> hi, dowon!

2> hello, papa!

3> hi, dowon!

3> hello, papa!

4> hi, dowon!

4> hello, papa!



2) for 구문 

  - for 구문을 다양하게 사용하는 방법

  - 5가지 for loop 문 사용방법 : loop.coffee

arr = ["Net", "Aet", "Photo", "Psd", "Cgt"]

obj = 
	name: "dowon"
	topic: "web development"
	editor: "yun"

###
for ( var i = 0; i < arr.length; i++) {
	console.log(arr[i]);
}	
###
console.log "---1---"
for siteName in arr
	console.log siteName

console.log "---2---"
console.log siteName for siteName in arr

console.log "---3---return array---"
console.log (siteName for siteName in arr)	

console.log "---4---"
for siteName, i in arr
	console.log "#{i}: #{siteName}"	

console.log "----5--"
for siteName, i in arr when siteName.indexOf("P") is 0	
	console.log "#{i}: #{siteName}"	

console.log "---6---"
###
by 2 ===  when i % 2 is 0
###
for siteName, i in arr by 2
	console.log "#{i}: #{siteName}"	


// Generated by CoffeeScript 1.4.0
(function() {
  var arr, i, obj, siteName, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _step;
  arr = ["Net", "Aet", "Photo", "Psd", "Cgt"];

  obj = {
    name: "dowon",
    topic: "web development",
    editor: "yun"
  };

  /*
  for ( var i = 0; i < arr.length; i++) {
  	console.log(arr[i]);
  }
  */
  console.log("---1---");
  for (_i = 0, _len = arr.length; _i < _len; _i++) {
    siteName = arr[_i];
    console.log(siteName);
  }

  console.log("---2---");
  for (_j = 0, _len1 = arr.length; _j < _len1; _j++) {
    siteName = arr[_j];
    console.log(siteName);
  }

  console.log("---3---return array---");
  console.log((function() {
    var _k, _len2, _results;
    _results = [];
    for (_k = 0, _len2 = arr.length; _k < _len2; _k++) {
      siteName = arr[_k];
      _results.push(siteName);
    }
    return _results;
  })());

  console.log("---4---");
  for (i = _k = 0, _len2 = arr.length; _k < _len2; i = ++_k) {
    siteName = arr[i];
    console.log("" + i + ": " + siteName);
  }

  console.log("----5--");
  for (i = _l = 0, _len3 = arr.length; _l < _len3; i = ++_l) {
    siteName = arr[i];
    if (siteName.indexOf("P") === 0) {
      console.log("" + i + ": " + siteName);
    }
  }

  console.log("---6---");
  /*
  by 2 ===  when i % 2 is 0
  */
  for (i = _m = 0, _len4 = arr.length, _step = 2; _m < _len4; i = _m += _step) {
    siteName = arr[i];
    console.log("" + i + ": " + siteName);
  }
}).call(this);	

// 결과

D:\Development\coffeescript\2>coffee loop

---1---

Net

Aet

Photo

Psd

Cgt

---2---

Net

Aet

Photo

Psd

Cgt

---3---return array---

[ 'Net', 'Aet', 'Photo', 'Psd', 'Cgt' ]

---4---

0: Net

1: Aet

2: Photo

3: Psd

4: Cgt

----5--

2: Photo

3: Psd

---6---

0: Net

2: Photo

4: Cgt



3) Scope 알아보기 

  - this 에 대한 유효범위(scope)를 보았을 때 object의 method로 사용되지 않는 단순 function 호출의 this는 global object (즉, UI에서는 window객체)를 가르킨다 (참조)

var classroom = {
	students: ["Jonh", "Jane", "Jill", "Joe"],
	print: function() {
		var thiz = this;
		function getName(i) {
			return thiz.students[i];
		}
		for(var i=0; i < this.students.length; i++) {
			console.log(getName(i));
		}
	}
}
classroom.print();


  - 위의 경우를 coffee로 표현하고자 할 경우 다음과 같이 => 사용하면 "var thiz = this;" 를 표현할 수 있다

classroom = 
	students: ["Jonh", "Jane", "Jill", "Joe"]
	print: ->
		getName = (i) =>    // =>를 사용하면 var thiz = this; 동일 효과가 가능하다 
			this.students[i]

		for s,i in this.students
			console.log getName i 

classroom.print()

// Generated by CoffeeScript 1.4.0
(function() {
  var classroom;
  classroom = {
    students: ["Jonh", "Jane", "Jill", "Joe"],
    print: function() {
      var getName, i, s, _i, _len, _ref, _results,
        _this = this;
      getName = function(i) {
        return _this.students[i];
      };
      _ref = this.students;
      _results = [];
      for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
        s = _ref[i];
        _results.push(console.log(getName(i)));
      }
      return _results;
    }
  };
  classroom.print();
}).call(this);


* 파일 

coffeescript-2.zip


posted by 윤영식
2013. 1. 24. 21:52 Languages/CoffeeScript

CoffeeScript는 ; { 등을 가급적 배제하고 휴먼리더블하게 작성하게 해준다. 한줄띄기, 스페이스, 탭 등으로 구분하여 작문(코딩)을 한다 


1) 기본적인 작성법

  - 문자, 오브젝트, 범위, 변수

  - 문자 다루기 : #{} 을 통하여 변수 맵핑 : hi.coffee

name = "dowon"
greeting = "hi, #{name}!"
multi = """
this is a greeting:
	#{greeting}

"""
console.log multi

// 결과 : """ 를 사용했을 때 내부의 띄워쓰기 그대로 적용됨 <pre> 태그 유사 

D:\Development\coffeescript>coffee hi


this is a greeting:

        hi, dowon!


D:\Development\coffeescript>



  - 범위 다루기 : ..과 ... 의 차이는 끝수 포함/미포함 : range.coffe

range = [0..10]
console.log range

range = [0...10]
console.log range

range = [15...10]
console.log range

// 결과

D:\Development\coffeescript>coffee range

[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

[ 15, 14, 13, 12, 11 ]


  

  - 오브젝트 다루기 : obj.coffee

obj = name: "dowon", job: "Programmer"
console.log obj
obj = 
	name: "dowon-01" 
	job: "Programmer-01"
console.log obj

name = "dowon-02"
job = "Programmer-02"
obj = name: name, job: job
console.log obj

obj = {name, job}
console.log obj

 - key:value 같은 줄에 쓰면 콤마(,) 구분

 - 한줄 띠기 하면 콤마 필요없음

 - {name} 하면 명칭을 그대로 key 명칭을 활용


// 결과 

D:\Development\coffeescript>coffee obj

{ name: 'dowon', job: 'Programmer' }

{ name: 'dowon-01', job: 'Programmer-01' }

{ name: 'dowon-02', job: 'Programmer-02' }

{ name: 'dowon-02', job: 'Programmer-02' }



  - 변수 다루기 : variable.coffee

[one, two, three] = ["1", "2", "3"]
console.log two
console.log three
name = "dowon"
obj = {name, job:"Programm er", other: {name}}
console.log obj

// Generated by CoffeeScript 1.4.0
(function() {
  var name, obj, one, three, two, _ref;
  _ref = ["1", "2", "3"], one = _ref[0], two = _ref[1], three = _ref[2];

  console.log(two);
  console.log(three);
  name = "dowon";
  obj = {
    name: name,
    job: "Programmer",
    other: {
      name: name
    }
  };
  console.log(obj);
}).call(this);

// 결과 

D:\Development\coffeescript>coffee variable

2

3

{ name: 'dowon', job: 'Programmer', other: { name: 'dowon' } }



2) Function 다루기 

  - 주석, 함수, 아규먼트 배열, Math.random 

  - 주석 : # 무시되는 문장 (컴파일시 주석문도 안남음), ### 주석 /* */ 으로 변환 : comment.coffee

one = 1 # comment
###
comment 2
###
two = 2


  - 함수 만들기 : -> 사용 : greet.coffee

###
function greet (name) {
	console.log("hi" + name +"!");
}
greet = function(name) {
}
###

greet = (name) -> console.log "hi #{name}!"
greet "Greeting" 
greet2 = (name = "friend") -> "hi #{name}"
console.log greet2
console.log greet2()

// 결과

D:\Development\coffeescript>coffee greet

hi Greeting!

[Function]

hi friend       <=== () 를 붙여서 함수 호출한다 



  - 배열 아규먼트 만들기 : ... 사용 : array.coffee

test = (x, y, z...) -> 
	console.log x
	console.log y
	console.log z

test "one", "two", "three"
console.log "---------------------"
test "one", "two", "three", "four", "five"	


test2 = (x..., y, z) -> 
	console.log x
	console.log y
	console.log z

test2 "one", "two", "three"
console.log "---------------------"
test2 "one", "two", "three", "four", "five"	

test3 = (x, y..., z) -> 
	console.log x
	console.log y
	console.log z

test3 "one", "two", "three"
console.log "---------------------"
test3 "one", "two", "three", "four", "five"	

// 결과 

D:\Development\coffeescript>coffee array

one

two

[ 'three' ]   

---------------------

one

two

[ 'three', 'four', 'five' ]


// test2 호출 

[ 'one' ]

two

three

---------------------

[ 'one', 'two', 'three' ]

four

five


// test3 호출 

one

[ 'two' ]

three

---------------------

one

[ 'two', 'three', 'four' ]

five



  - 자동 실행 함수 : do 사용하여 자동 실행토록 만들어준다 : anon.coffee

###
(() -> console.log())()
###

###
automatically invoke function
###
do (message = "hi dowon") ->
	console.log message

// Generated by CoffeeScript 1.4.0
/*
(() -> console.log())()
*/

/*
automatically invoke function
*/

(function() {
  (function(message) {
    return console.log(message);
  })("hi dowon");

}).call(this);

// 결과 : 만일 do를 넣지 않으면 수행되지 않음

D:\Development\coffeescript>coffee anon

hi dowon



  - Math.random 사용하기 : random.coffee

###
Math.random()
###

rand = (max = 10, min = 0) -> Math.floor(Math.random() * (max - min + 1)) + min

console.log rand()
console.log rand 100
console.log rand 60, 50

// 결과 : 수행때 마다 틀린 random값이 지정 범위내에서 출력됨 

D:\Development\coffeescript>coffee random

5

100

53


D:\Development\coffeescript>coffee random

1

32

53



3) 비교연산

  - ===, !== 사용하기 

  - if 문 사용하기 

  - ===, !== 를 사용하는 대신 is, isnt  문장으로 사용하기 : operator.coffee

###
is ===
isnt !==
###
name = "dowon"
if name is "dowon"
	console.log "great"

if name isnt "Dowon"
	console.log "poor"	

### if( !false ) ###
if not false
	console.log "cool"

###
var name = "dowon",
	job = "programmer";

if(name === "dowon" || job === "programmer") {
	console.log("right")
}	
###
name = "dowon"
job = "programmer"

if name is "dowon" and job is "programmer"
	console.log "right"

// 결과

D:\Development\coffeescript>coffee operator

great

poor

cool

right



  - ? 연산자를 통하여 null 체크 하기 : operator2.coffee

name = "dowon"
if name?
	console.log "hi"
person = 
	name: "dowon"
	job: "programmer"

console.log person?.name
name = name || "youngsik"
name ||= "youngsik"
name ?= "youngsik"
console.log name

// Generated by CoffeeScript 1.4.0
(function() {
  var name, person;
  name = "dowon";
  if (name != null) {
    console.log("hi");
  }
  person = {
    name: "dowon",
    job: "programmer"
  };

  console.log(person != null ? person.name : void 0);
  name = name || "youngsik";
  name || (name = "youngsik");
  if (name == null) {
    name = "youngsik";
  }
  console.log(name);
}).call(this);

// 결과

D:\Development\coffeescript>coffee operator2

hi

dowon

dowon



  - if 문을 재구성한다 : if.coffee

# x = 4
# if( x >= 0 && x <= 10) {}
x = 4
if 0 <= x <= 10
	console.log "true"

// Generated by CoffeeScript 1.4.0
(function() {
  var x;
  x = 4;
  if ((0 <= x && x <= 10)) {
    console.log("true");
  }
}).call(this);

// 결과

D:\Development\coffeescript>coffee if

true


* 테스트 파일 다운로드



4) CoffeeScript 문법 & 장/단점

  - if 문을 한줄로 사용할 때 조건 뒤에 then 구문 붙이기 

    예) if name is "dowon" then console.log "right"

posted by 윤영식
2013. 1. 24. 21:51 Languages/CoffeeScript

CoffeeScript를 설치하고 사용해 보자 


1. 설치하기

  - Node.js 설치함

  - NPM(Node Package Manager) 설치함 : npm을 통하여 coffe-script 를 설치한다 

  - 참조 : http://coffeescript.org

D:\Development\javascript>npm install -g coffee-script

npm http GET https://registry.npmjs.org/coffee-script

npm http 200 https://registry.npmjs.org/coffee-script

C:\Documents and Settings\UserXP\Application Data\npm\cake -> C:\Documents and Settings\UserXP\Application Data\npm\node_modules\coffee-script\bin\cake

C:\Documents and Settings\UserXP\Application Data\npm\coffee -> C:\Documents and Settings\UserXP\Application Data\npm\node_modules\coffee-script\bin\coffee

coffee-script@1.4.0 C:\Documents and Settings\UserXP\Application Data\npm\node_m

odules\coffee-script


D:\Development\javascript>coffee -v

CoffeeScript version 1.4.0



2. Coffee 사용하기 

  - 확장자 .coffee 파일을 만든다

  - coffee 명령으로 수행할 수도 있고, js 파일로 컴파일 할 수도 있다


  - 일반적인 js 코딩 : arr.js

function makeArray(dimension) {
        var arr = [], i = 0, j = 0;
	for(; i < dimension; i++) {
		arr[i] = [];
		for( j=0; j < dimension; j++) {
			arr[i][j] = '1111';
		}
	}
	return arr;
}

var myArr = makeArray(4);

console.log(myArr);

// 결과 

D:\coffeescript>node arr

[ [ '1111', '1111', '1111', '1111' ],

  [ '1111', '1111', '1111', '1111' ],

  [ '1111', '1111', '1111', '1111' ],

  [ '1111', '1111', '1111', '1111' ] ]



- CoffeeScript 코딩 방식 : arr.coffee (coffee 확장자)

makeArray = (dimension) ->
	arr = []
	for i in [0...dimension]
		arr[i] = []
		arr[i][j] = '1111' for j in [0...dimension]
	arr
	
myArr = makeArray 4

console.log myArr	

console.log 'dowon hi'

// 결과 

D:\coffeescript>coffee arr.coffee

[ [ '1111', '1111', '1111', '1111' ],

  [ '1111', '1111', '1111', '1111' ],

  [ '1111', '1111', '1111', '1111' ],

  [ '1111', '1111', '1111', '1111' ] ]



- CoffeeScript js를 일반 js 파일로 컴파일 하기 

D:\coffeescript>coffee -c arr.coffee


// 컴파일로 생성된 arr.js 소스 파일 

// Generated by CoffeeScript 1.4.0
(function() {
  var makeArray, myArr;

  makeArray = function(dimension) {
    var arr, i, j, _i, _j;
    arr = [];
    for (i = _i = 0; 0 <= dimension ? _i < dimension : _i > dimension; i = 0 <= dimension ? ++_i : --_i) {
      arr[i] = [];
      for (j = _j = 0; 0 <= dimension ? _j < dimension : _j > dimension; j = 0 <= dimension ? ++_j : --_j) {
        arr[i][j] = '1111';
      }
    }
    return arr;
  };

  myArr = makeArray(4);

  console.log(myArr);

  console.log('dowon hi');

}).call(this);



3. Coffee Watching & REPL(레플) 사용하기

  - coffee 파일이 변경되어 저장되는 시점에 자동으로 컴파일 되도록 한다 : watch

  - w 옵션을 준다 

////////////////////////////////

// watch 옵션 주기

D:\Development\coffeescript>coffee -cw arr.coffee

17:51:59 - compiled arr.coffee

17:52:29 - compiled arr.coffee   <--- arr.coffee 파일 마지막에 새로운 내용을 넣고 저장하면 자동으로 compile 됨


////////////////////////////////

// REPL을 통하여 coffee 수행해 보기

D:\Development\coffeescript>coffee

coffee> 1 + 1

2

coffee> arr = []

[]

coffee> arr[0] = 'dowon'

'dowon'

coffee> arr

[ 'dowon' ]

coffee> arr.push 'hi'

2

coffee> arr

[ 'dowon', 'hi' ]

coffee> arr[1]

'hi'

coffee>



4. Single Page Application (SPA : 스파) 를 만들때 CoffeeScript를 사용하자

  - 코딩을 간결하게 해준다

  - global variable등의 사소한 문법오류를 잡아준다 

  - 그러나 compile된 코드는 좀 긴것 같아서 byte 수의 증가를 초래하는 듯하다

  - BackboneJS와도 잘 어울린다 


<참조>

posted by 윤영식
2013. 1. 17. 11:13 Git, GitHub/GitHub

Git 서버는 사내에 설치할 수도 있고 GitHub과 같은 서비스를 사용할 수도 있다. 아무래도 스타트업 기업이라면 GitHub의 Private을 이용하는게 좋겠다. GitHub에 대해 알아보자 


1) GitHub 가입하고 저장소 만들기

  - 일반적인 서비스 가입절차와 동일하다 

  - 상단 오른쪽 아이콘 "Create a new repo" 클릭하여 저장소를 만든다. (수동, 자동방식이 존재한다)

  - 최초 저장소에 README.md markdown 형식으로 readme 를 만들어 준다 

  - Admin에서 동료를 추가할 수 있다


2) 프로젝트 Fork

  - 권한이 없는 프로젝트에 참여하고 싶으면 Fork한다. Fork가 되면 자신의 Repository로 복제가 되고 이곳에 마음대로 Push 할 수 있다. 


3) Pull Request 

  - jQuery 원본 repo에서 자신의 리모트 repo로 fork 한다 (in GitHub)

  - 자신의 리모트 repo에서 로컬 repo로 clone 한다 

  - jQuery 원본 repo에서 pull(fetch->merg)한다 (master 브랜치 commit 내역을 맞추기 위함. 물론 conflict 나면 수정해야 겠다)

  - 자신의 로컬 repo -> 자신의 리모트 repo로 push 한다 

  - 자신의 리모트 repo인 jQuery를 jQuery 원본 repo 쪽으로 Pull Request 한다 (Pull Request 받아주는 약속이 OSS마다 틀리니 사전숙지함)

  - 즉, 자신의 리모트 repo 를 원본의 repo의 committer에게 Pull 해달라고 요청을 보내는 것이다


<참조> 

  - http://blog.outsider.ne.kr/866 : GitHub Fetch, Pull, Push, Pull Request

  - http://blog.outsider.ne.kr/644 : GitHub에 Branch, Tag push 하기 

  - http://blog.outsider.ne.kr/641 : GitHub에 있는 Branch 로컬로 가져오기 (git checkout -b [로컬브랜치명] [원격alias]/[원격브랜치명])

posted by 윤영식
2013. 1. 16. 17:58 Lean Agile Culture

tistory에서 앞으로 코드를 넣을 때 SyntaxHighlighter를 사용해보자. 


- 설정방법 : http://gyuha.tistory.com/405

- 코드를 넣고 테스트 하기 

var a = function b() {
   alert('hi');
}


- 설정후 코드 넣을 때 : html 체크하고 pre 태그의 class="brush:[언어]"넣어서 해결


- 지원 문법

Brush nameBrush aliasesFile name
ActionScript3as3, actionscript3shBrushAS3.js
Bash/shellbash, shellshBrushBash.js
ColdFusioncf, coldfusionshBrushColdFusion.js
C#c-sharp, csharpshBrushCSharp.js
C++cpp, cshBrushCpp.js
CSScssshBrushCss.js
Delphidelphi, pas, pascalshBrushDelphi.js
Diffdiff, patchshBrushDiff.js
Erlangerl, erlangshBrushErlang.js
GroovygroovyshBrushGroovy.js
JavaScriptjs, jscript, javascriptshBrushJScript.js
JavajavashBrushJava.js
JavaFXjfx, javafxshBrushJavaFX.js
Perlperl, plshBrushPerl.js
PHPphpshBrushPhp.js
Plain Textplain, textshBrushPlain.js
PowerShellps, powershellshBrushPowerShell.js
Pythonpy, pythonshBrushPython.js
Rubyrails, ror, rubyshBrushRuby.js
ScalascalashBrushScala.js
SQLsqlshBrushSql.js
Visual Basicvb, vbnetshBrushVb.js
XMLxml, xhtml, xslt, html, xhtmlshBrushXml.js

posted by 윤영식
2013. 1. 16. 10:53 Git, GitHub/Git Lec02

Git 에서 한 브랜치에서 다른 브랜치로 합치는 방법은 Merge와 Rebase가 있다. Rebase 사용시 좋은 점과 사용하지 말아야 할 때에 대해서 알아보자 (ch 3.6)


1) Rebase 기초 

  - 일반적으로 3 way merge로 브랜치 합치기를 함    

    


  - Rebase는 한 브랜치에서 변경된 사항을 다른 브랜치에 적용한다 

    + Rebase할 브랜치를(B) checkout한 브랜치가 가르키는 커밋까지 diff하여 다른 내용을 임시로 저장해 놓음 (temp)

    + temp에 저장한 변경사항을 차례로 적용하여 Rebse될 브랜치(A)에 새로운 commit을 만든다 

    + Rebase될 브랜치(A)는 가장 최신 commit 개체가 되고 브랜치의 포인터는 fast-forward 된다 

    + 즉, Rebase는 기존 commit을 사용하는 것이 아니라 새로운 commit을 만들어 합치는 것이다

  -  Rebase가 merge 보다 좀 더 깔끔한 히스토리를 만든다. 일이 병렬로 하다 rebase 하면 선형적으로 된다 

  - 보통 리모트 브랜치에 커밋을 깔끔하게 적용하고 싶을 때 사용한다 (메인 프로젝트에 패치를 보낼 때)

  - Rebase=브랜치의 변경사항을 순서대로 다른 브랜치에 적용하면서 합치고, Merge는 두 브랜치의 최종결과만 합친다


2) Rebase 위험성 

  - 이미 공개 저장소에 Push 한 커밋을 Rebase 하지 마라 : 동료와 협업시 commit 객체 내용이 삭제되어 버린다 (p. 78)

  - 즉, 로컬에서 작업진행한 내 commit 개체만 Rebase하고 리모트에 있는 commit 개체의 rebase는 불허!!!

  - Push 하기 전에 정리하려고 Rebase하는 것은 괜찮다 


<사용법>

  - http://mobicon.tistory.com/165  : 리모트 저장소에서 pull하고 conflict 날 경우

  - http://mobicon.tistory.com/164  : 로컬 저장소에서 conflict 날 경우 

'Git, GitHub > Git Lec02' 카테고리의 다른 글

[Pro Git] 커밋 가이드라인  (0) 2013.01.25
[Pro Git] 협업 Workflow  (0) 2013.01.25
[Pro Git] Branch 사용하기  (0) 2013.01.16
[Pro Git] Git Alias 사용하기  (0) 2013.01.08
[Pro Git] Tag 사용하기  (0) 2013.01.08
posted by 윤영식
2013. 1. 16. 10:20 Git, GitHub/Git Lec02

Git은 Branch를 자유자래로 사용함으로써 그 진가를 실감할 수 있다. 복잡한 애플리케이션이나 프로젝트일 수도록 더욱 힘을 발휘한다. Branch에 대하여 알아보자. 


1) 분산 버전 관리 시스템

  - 클라이언트가 파일의 마지막 Snapshot을 Checkout 하지 않는다. 그냥 저장소를 전부 복제한다

  - 서버에 문제 생겨도 다시 작업시작 가능하다

  - DVCS 한경에서는 리모트 저장소가 존재하고 여러개 문제없다. 동시 다양한 그룹과 다양한 방법으로 협업이 가능하다. 



2)  Git 철학

  - 2005년 BitKeeper 사용하지 않으며 리누스 토발즈가 개발 

  - 빠른 속도

  - 단순한 구조

  - 비선형적인 개발 (수천 개의 동시 다발적인 브랜치)

  - 완벽한 분산

  - 리눅스 커널같은 대형 프로젝트에도 유용할 것 (속도나 데이터 크기면에서)


3) Git 데이터 저장 방식

  - Git 은 데이터를 일련의 Snapshot으로 기록한다 

  - Staging Area에서 Local Git 저장소에 파일 저장(=Blob). 해당 파일의 Checksum을(SHA-1) 저장한다.

  - Commit을 하면 Local Git 저장소에 

    + 하위 디렉토리의 "트리 개체" 생성

    + 트리 개체 밑으로 "커밋 개체" 생성 : 메타데이터(저자,메시지)와 트리 개체 포인터, 이전 트리 개체 포인터 정보 저장

  - 즉, Git 저장소에 Commit > Tree > Blob 이 생성된다 




4) 브랜치(Branch) 

  - Git 기본 master 브랜치를 만든다. master 브랜치는 자동으로 마지막 Commit 개체를 가르킨다 

   + 즉, 브랜치는 Git 저장소의 Commit 개체를 가르키는 것이다. 

  - 브랜치는 SHA-1 40자의 체크섬 파일에 불과하다. 즉 41바이트(줄바꿈포함) 크기

  


  - Git은 특수한 HEAD 라는 포인터가 존재 = 지금 작업 중인 브랜치를 가르킴 

    + 즉, Head > Master(브랜치) > Commit 포인터를 가르킨다 


  - 브랜치를 나누고 각각의 브랜치가 commit 이후 merge 방법 = 3 way merge

   + Merge시에 Merge에 대한 정보가 들어있는 커밋을 하나 만든다 


5) 충돌(Conflict) 

  - 3 way merge 시에 같은 파일의 한 부분을 각각의 브랜치가 수정하였을 때 발생

  - git status 명령으로 conflict 상황 체크 

  - 충돌부분을 직접 수정후 git add -> git commit 함 

  - Merge 브랜치 필터링 명령

    + git branch --merged : merge한 브랜치 목록 확인 (* 가 없는 브랜치는 이미 다른 브랜치와 merge 한 것임)

    + git branch --no-merged : 현재 ckeckout한 브랜치에 merge 하지 않은 브랜치를 보여줌 


6) 브랜치 접근 in Local

  - master 브랜치 : 배포/안정화 브랜치

  - develop 브랜치 : 개발 브랜치, develop 밑으로 topic 브랜치 (각 브랜치를 하나의 실험실로 생각한다)



  - topic 브랜치 : 어떤 한 가지 주제나 작업의 짧은 호흡의 브랜치이다 예) hotfix 브랜치

    + master merge후에 삭제

    + 내용별 검토, 테스트가 용이하다


7) 리모트 브랜치 

  - 리모트 브랜치란 리모트 저장소에 있는 브랜치이다. 

  - 명칭 = (remote)/(branch) 형식 예) 리모트 저장소가 origin 이고 master 브랜치이면 origin/master 로 표현함 

  - git clone으로 local에 내려 받으면 origin/master가 생기고 마음대로 조종할 수 없다. 

  -  git fetch origin을 할때 자동으로 리모트 저장소의 최신 master 브랜치로 local의 origin/master가 갱신된다

    + 누군가 리모트에 push를 하여 origin의 master commit을 갱신함 

    + 나는 옛날 origin/master를 가지고 있고 그동안 local에서 commit도 했음, pull로 최신으로 update 할 경우 

    + git fetch origin/hotfix01 로 브랜치 지정하여 로컬로 받으면 저장소에 브랜치가 생성되지 않고 포인터만 생성됨

      = git merge origin/hotfix01 로 새로받은 브랜치 내용을 merge 한다

      = 또는 merge 하지 않고 새로운 브랜치로 만들려면 git checkout -b hotfix01 origin/hotfix01  수행 


  - git push [remote] [branch] : 리모트에 쓰기 권한이 있어야 한다. 예) git push origin hotfix01

    + git push [remote] [local branch]:[remote branch] 로 로컬/리모트 브랜치 명이 틀릴 때 사용한다 


8) 브랜치 추적(Tracking)

  - Git은 리모트 저장소에서 Clone시에 master 브랜치를 origin/master 브랜치로 추적 브랜치를 만든다

  - checkout 할 때도 자동으로 추적 브랜치가 만들어 져서 git pull/push 할 때 별도의 옵션을 주지 않고 해당 리모트 브랜치로 수행

  - git checkout -b [branch] [remotename/branch] : 추적 브랜치 만듦 


9) 리모트 브랜치 삭제 

  - git push [remotename] [local branch] :[remote branch] 협업때 사용했던 서브 브랜치를 이제 삭제 하고 싶을 경우 

  - 예) git push origin :hotfix01  [local branch]를 비워두면 "로컬에서 빈 내용을 리모트의 [remote branch]인 hotfix01에 채워 넣어라" 라는 뜻이 된다   

posted by 윤영식
2013. 1. 14. 00:39 Git, GitHub

Remote 저장소인 GitHub과 Local 저장소의 commit 내역이 일치하지 않을 경우 rebase로 어떻게 해결하는지 알아보자. Local 저장소내에서 rebase를 사용하는 것과 약간의 차이점이 존재한다. (참조)


1. Remote 저장소 merge conflict 조건

  - GitHub에 이미 다른 Commit이 존재한다 (분홍색)

  - Local 저장소에 변경된 commit이 존재한다

  - git push 를 수행할 경우 merge conflict가 발생한다

  - 기존에 push후 conflict 발생시 pull 하고 문제 해결하고 다시 push 한다 (해결하기)

  



2. 이제 1)번의 경우 말고 fetch 와 rebase 사용

  - git fetch 를 통하여 local 저장소로 merge 하지 않고 origin/master의 별도 브랜치를 local 저장소로 복사하자 

    


  - git rebase를 수행한다. 이때 다음의 3단계로 진행이 된다. 

    + 브랜칭 된 이후의 master commit 내역을 temporary area로 이동시킨다-origin/master가 아니라- (초록색)

     


    + origin/master를 master 브랜치로 commit 한다. (분홍색)

       


    + temporary area에 있는 master commit 내역을 다시 master로 commit 한다. 

     



3. local과 remote의 같은 파일을 commit 하였을 때 rebase 하는 방법

  - master 브랜치에서 rebase하고 있음에 주의한다 

////////////////////////////////////////////////////////////////////////////

// remote 저장소와 commit 내역을 맞추고 확인해 보자 

$ git log --pretty=oneline

bbeb691d80512e17279764312f60416ebf28dd86 add content in rebase of local repo


// rebase.html 파일의 내용 


////////////////////////////////////////////////////////////////////////////

// 로컬에서도 rebase.html 파일을 변경하고 push를 시도하지만 reject 된다.

// 즉, remote 저장소의 rebase.html과 local 저장소의 rebase.html 내역이 틀리다.

$ git push

Username for 'https://github.com':

Password for 'https://ysyun@yuwin.co.kr@github.com':

To https://github.com/ysyun/pro_git.git

 ! [rejected]        master -> master (non-fast-forward)

error: failed to push some refs to 'https://github.com/ysyun/pro_git.git'

hint: Updates were rejected because a pushed branch tip is behind its remote

hint: counterpart. If you did not intend to push that branch, you may want to

hint: specify branches to push or set the 'push.default' configuration

hint: variable to 'current' or 'upstream' to push only the current branch.


////////////////////////////////////////////////////////////////////////////

// origin/master를 local 저장소로 가지고 온다. 

$ git fetch

remote: Counting objects: 5, done.

remote: Compressing objects: 100% (3/3), done.

remote: Total 3 (delta 1), reused 0 (delta 0)

Unpacking objects: 100% (3/3), done.

From https://github.com/ysyun/pro_git

   d895a7e..9274820  master     -> origin/master


////////////////////////////////////////////////////////////////////////////

// rebase를 위하여 master로 이동한다 

$ git branch

* (no branch)

  master


$ git checkout master

Warning: you are leaving 1 commit behind, not connected to

any of your branches:


  bbeb691 add content in rebase of local repo


If you want to keep them by creating a new branch, this may be a good time

to do so with:


 git branch new_branch_name bbeb691d80512e17279764312f60416ebf28dd86


Switched to branch 'master'

Your branch and 'origin/master' have diverged,

and have 1 and 3 different commits each, respectively.


$ git  branch

* master


////////////////////////////////////////////////////////////////////////////

// rebase를 수행한다. 

// rebase.html 파일 내역에서 conflict가 나온다고 메시지를 출력한다 

$ git rebase

First, rewinding head to replay your work on top of it...

Applying: updating rebase in local repo.

Using index info to reconstruct a base tree...

M       rebase.html

Falling back to patching base and 3-way merge...

Auto-merging rebase.html

CONFLICT (content): Merge conflict in rebase.html

Failed to merge in the changes.

Patch failed at 0001 updating rebase in local repo.


When you have resolved this problem run "git rebase --continue".

If you would prefer to skip this patch, instead run "git rebase --skip".

To check out the original branch and stop rebasing run "git rebase --abort".


$ git status

# Not currently on any branch.

# Unmerged paths:

#   (use "git reset HEAD <file>..." to unstage)

#   (use "git add/rm <file>..." as appropriate to mark resolution)

#

#       both modified:      rebase.html

#

no changes added to commit (use "git add" and/or "git commit -a")


////////////////////////////////////////////////////////////////////////////

// rebase.html 파일 내역을 보고 적절히 수정한다 

$ cat rebase.html

 this is rebase html file ok

<<<<<<< HEAD

     updating rebase in remote repo. again

     add content in remote repo.

=======

    updating rebase in local repo.

>>>>>>> updating rebase in local repo.


$ vi rebase.html

$ cat rebase.html

 this is rebase html file ok

     updating rebase in remote repo. again

     add content in remote repo.

    updating rebase in local repo.


////////////////////////////////////////////////////////////////////////////

// rebase.html 을 staging area로 add 한다. 

$ git add rebase.html


////////////////////////////////////////////////////////////////////////////

// rebase를 계속 진행한다 

$ git rebase --continue

Applying: updating rebase in local repo.


////////////////////////////////////////////////////////////////////////////

// commit log를 확인해 보면 rebase가 되어서 remote commit 내역이 local 저장소에 

// 복사되었다. 

$ git log --pretty=oneline

30d42e6785e441c3b515a4f4181dbfa051ad400a updating rebase in local repo.

9274820a9f62b2e08411ffb34d808508c7a74f93 add content in rebase of remote repo

d895a7e83d85d05f2c81585232f9c3e22426383a updating rebase again in remote repo

8c7c04b40d9fce0a1ae5a52b45b438fa14ea95e1 Update rebase.html


////////////////////////////////////////////////////////////////////////////

// 다시 push를 하면 정상적으로 remote 저장소로 push 가 된다 

$ git push

Username for 'https://github.com':

Password for 'https://ysyun@yuwin.co.kr@github.com':

Counting objects: 5, done.

Delta compression using up to 2 threads.

Compressing objects: 100% (3/3), done.

Writing objects: 100% (3/3), 323 bytes, done.

Total 3 (delta 2), reused 0 (delta 0)

To https://github.com/ysyun/pro_git.git

   9274820..30d42e6  master -> master


// remote에 local에서 직접 수정한 내역이 commit 됨 


// local repo(파란색, 30d42e6785)의 SHA 값이 가장 상위에 그 다음 remote repo 의 SHA(927480a9f...)가 밑에 있다. 


posted by 윤영식
2013. 1. 13. 22:00 Git, GitHub

git에서 merge conflict가 발생할 경우 좀 더 쉽게 하고 싶다면 rebase를 사용할 수 있다. rebase의 동작 순서와 언제 사용할지 알아보자. 


1) local rebase 명령 전제 조건

  - master로 부터 현재 commit 내용으로 신규 branch인 admin을 만든다

  - master로 새로운 commit 내용이 있고, 신규 branch(admin)도 새로운 commit이 존재한다 

  - admin 브랜치의 내역을 그대로 master 브랜치로 merge하지 않고 add 하고 싶을 경우 

  


  - git checkout admin : admin 브랜치로 checkout 한다 

  - git rebase master : master의 commit 내역을 admin 브랜치에 add 한다   

  


  - git checkout master : 다시 master로 돌아 온다

  - git merge admin : master에 admin을 merge 한다. 이럴 경우 admin 브랜치로 갈라진 이후 master의 신규 commit이 admin 브랜치에 들어 있으므로 fast-forward merge가 가능해 진다. 

////////////////////////////////////////////////////

// admin 브랜치를 신규로 만들고 checkout 하기 

$ git checkout -b admin

Switched to a new branch 'admin'


$ git branch

* admin

  master


////////////////////////////////////////////////////

// rebase.html 파일을 신규로 만들고 commit 2번 하기 

$ touch rebase.html

$ git add rebase.html

$ git commit -m "add rebase html"

[admin d375a11] add rebase html

 1 file changed, 1 insertion(+)

 create mode 100644 rebase.html


$ vi rebase.html  <- 내용 변경 

$ git commit -am "update rebase html"

[admin 22bbd02] update rebase html

 1 file changed, 1 insertion(+), 1 deletion(-)


////////////////////////////////////////////////////

// master 에서도 신규 파일을 생성하고 두번 commit 하기

$ git checkout master

Switched to branch 'master'


$ touch dowon.js

$ vi dowon.js

$ git add dowon.js

$ git commit -m "add dowon javascript file"

[master cc77217] add dowon javascript file

 1 file changed, 1 insertion(+)

 create mode 100644 dowon.js


$ vi dowon.js

$ git commit -am "add dowon javascript file"

[master 841c14f] add dowon javascript file

 1 file changed, 1 insertion(+), 1 deletion(-)


////////////////////////////////////////////////////

// admin 브랜치로 이동하여 master의 신규 commit 2 개를

// admin 브랜치로 복사하기 

$ git checkout admin

Switched to branch 'admin'


// admin 브랜치의 commit 내역 확인 

$ git log --pretty=oneline

22bbd02cf7c49a96efb5eb4d38768e67d0c120da update rebase html

d375a1105a24ccc69895ef74d041329bc947d204 add rebase html

f42a865365aa53a6071887237057f593b75d236a Merge branch 'master' of https://github.com/ysyun/pro_git

fbf93820f62f1bc1035f2ba38e941fcae6212fb9 change README.md

c917ae5f7787844b63826a460d82a998bad08a7f Update README.md


// admin 브랜치로 갈라진 이후 master에 추가된 commit 내역을 admin 브랜치로 복사하기 

$ git rebase master

First, rewinding head to replay your work on top of it...

Applying: add rebase html

Applying: update rebase html


// commit 내역 확인 

// 녹색 부분 : master의 commit 내역이 admin 브랜치로 복사되었다. 단, admin commit 내역 밑으로 복사됨 

$ git log --pretty=oneline

3166e4c833705ffc74295b8cc1465a9131b53ef9 update rebase html

d40464af367bdfddb6303feda754937e228e8d57 add rebase html

841c14fb69239bda408aee262e2aea09fb0b83ca add dowon javascript file

cc772174619fa2989c0b3728144f0b885c7faca8 add dowon javascript file

f42a865365aa53a6071887237057f593b75d236a Merge branch 'master' of https://github.com/ysyun/pro_git

fbf93820f62f1bc1035f2ba38e941fcae6212fb9 change README.md

c917ae5f7787844b63826a460d82a998bad08a7f Update README.md


////////////////////////////////////////////////////

// master 브랜치로 다시 이동하여 admin 브랜치와 merge

$ git checkout master

Switched to branch 'master'

Your branch is ahead of 'origin/master' by 2 commits.


// fast-forward merge를 수행했다. 

$ git merge admin

Updating 841c14f..3166e4c

Fast-forward

 rebase.html | 1 +

 1 file changed, 1 insertion(+)

 create mode 100644 rebase.html


$ git branch

  admin

* master


$ git log --pretty=oneline

3166e4c833705ffc74295b8cc1465a9131b53ef9 update rebase html

d40464af367bdfddb6303feda754937e228e8d57 add rebase html

841c14fb69239bda408aee262e2aea09fb0b83ca add dowon javascript file

cc772174619fa2989c0b3728144f0b885c7faca8 add dowon javascript file

f42a865365aa53a6071887237057f593b75d236a Merge branch 'master' of https://github.com/ysyun/pro_git

fbf93820f62f1bc1035f2ba38e941fcae6212fb9 change README.md

c917ae5f7787844b63826a460d82a998bad08a7f Update README.md


  - Local 저장소에서의 rebase 명령 :master 브랜치로 분기된 이후 신규 branch에서 master 브랜치의 commit 내역을 복사 하고 싶을 경우 사용한다  

posted by 윤영식