안녕하세요. "생각의 웹"입니다.


기존의 기본 템플릿 기반으로 만든 블로그가 너무 구식이라는 절친의 지적이 있어 가장 빠르게 업데이트 하는 방법을 찾던 중, 마크쿼리 님이 만드신 스킨이 있어 이렇게 적용해 보았습니다. 설치 가이드는 아래와 같습니다.


티 스토리 반응형 스킨 설치 가이드 


가이드를 따라서 하면 크게 무리 없이 변경이 가능하나 몇 가지 직접 코드를 수정해 줘야 하는 사항이 있어 아래와 같이 정리합니다.


  1. 대문의 이미지(cover.jpg)와 메뉴 내용 수정 (사이드 메뉴에 마크쿼리 님의 템플릿 사이트가 올라오니 삭제 필요)
  2. 코드 꾸미기를 위해 사용했던 class tag 변경 (이전에는 설치형 beautifier를 사용했으나 이번엔 구글의 code priettify로 변경)
    • HTML를 제외한 코드 삽입 시 : <pre class="prettyprint linenums:1"> </pre>로 해당 코드를 둘러쌈.
    • HTML, XML 코드을 경우: <xmp class="prettyprint linenums:1"> </xmp>로 코들를 둘러쌈.
스킨을 바꾸니 수고가 많이 들기는 하나 왠지 새롭게 시작하는 느낌이 들어 색다르네요.

;-)


감사합니다.




안녕하세요. 생각의 웹입니다.


이번에는 KAIST에서 만든 교육용 프로그래밍 언어 엔트리와 관련 웹사이트를 소개하고자 합니다.


플레이 엔트리 가기


엔트리(Entry)의 의미인 초입부라는 뜻에 걸맞게 어린 아이들에게 쉽게 프로그래밍을 접근할 수 있도록 단계적으로 컨텐츠를 제공하고 있습니다.

플레이 엔트리 사이트 가입 후 학습하기 메뉴에서 10가지의 미션을 통해 블럭 프로그래밍의 묘미를 맛볼 수 있습니다.


언뜻 보면 간단한 문제지만 막상 코딩해보면 시행착오가 많습니다. (저 같은 경우는 '왼쪽으로 회전' 과 '오른쪽으로 회전'이 가장 혼란스러웠습니다. 군대에서도 좌우 구분 못해 고생 좀 했었죠;; ) 자만하지 마시고 심심풀이로 직접 한번 해보시는 것 추천합니다. ^^ 


아직 베타 서비스 중이라 불안한 점이 있지만 깔끔한 화면과 알찬 구성이 마음에 듭니다. 그럼에도 굳이 개선 희망사항을 하나 제안하면 터치 인터페이스의 줌 인, 줌 아웃 등을 지원해서 블럭 프로그래밍 편의를 도모했으면 합니다. (요즘은 터치 인터페이스를 장착한 기기가 많이 보급되고 있으니까요. ^^)


멋진 도구를 통해 많은 아이들이 프로그래밍에 친숙하게 다가갈 수 있기를 기대합니다!

화이팅입니다!!




안녕하세요, 생각의 웹입니다.


미국의 초등학생 대상으로 진행하는 컴퓨터 없이도 컴퓨터의 기본 원리 및 프로그래밍을 가르치는 단체인 CS 언플러그드 (http://csunplugged.org/)에서 만든 교육 자료를 다음과 같이 공유합니다.



CSUnplugged_OS_Part1_2012.pdf


CSUnplugged_OS_Part2_2012.pdf


이 과정의 목적은 초등학생들에게 컴퓨터를 구성하는 기본 원리를 가르치는데 있지만 각 단원마다 컴퓨터 과학의 기본 원리를 매우 쉽게 설명하고 있기에 컴퓨터에 대해 이해가 부족한 분들에게도 일독을 권합니다.


모든 사물에 컴퓨팅 기술이 접목되어 가는 세상에서 그 원리를 알고 지혜롭게 사용하는 사람들만이 기술의 홍수에 파묻히지 않을 것입니다.


감사합니다.

안녕하세요, 생각의 웹입니다.


오늘은 REST API에 대해 검색하다가 우연히 발견한 좋은 포스팅이 있어 기록 겸해서 이렇게 링크를 공유합니다.


1. 개념 소개

2. API 설계 가이드

3. API 보안


방대한 내용을 이렇게 잘 정리해 주셔서 덕분에 더 많은 것을 배웠습니다.

특히 API 보안에 관한 부분은 기존 Web Service 1.0 (SOAP)에 비해 취약하다는 논란이 있어서 좀 더 공부해 봐야 겠다는 찰나에 좋은 글을 만나게 되었습니다.


열린 마음으로 좋은 내용들을 공유해 주시는 고수님들을 본받아 저도 열심히 수련하고 잘 나눠야 겠다는 마음을 품습니다.

 

감사합니다.

행복한 하루 되시길!



안녕하세요, 생각의 웹입니다.


이번 포스팅은 요즘 잘 나가는 backbone.js를 이용해 객체 지향 Web App을 만든 사례를 소개하려 합니다.

backbone.js는 MVC (Model-View-Control) 모델을 기반으로 JavaScript 코드의 유지 보수성과 재사용성을 향상시킨 오픈소스 라이브러리입니다.

1. Prerequisites

backbone.js를 사용하기 위해서는 해당 라이브러리 코드를 사이트에서 다운로드 받아야 합니다. 또한, backbone.js은 underscore.js에 강한 종속성(dependency)가 있으니 함께 다운로드 받아야 합니다.

저는 코드를 다운로드 받아 js/libs 폴더에 두고 다음과 같이 index.html에 삽입했습니다. (이후 사용을 위해 가장 보편적인 DOM 관련 라이브러러인 jQuery 또한 참조하는 점도 주의깊게 봐 주시기 바랍니다. )

<script src="js/libs/underscore.js"></script> <script src="js/libs/backbone.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>


2. 모델 생성하기

모델을 생성하기 위해 Backbone.Model.extend() 함수를 사용합니다.

이번 웹앱으로 HTML5 기능을 이용한 간단한 생일 카드를 만들기 위해 다음과 같은 코드를 작성했습니다.

    var Card = Backbone.Model.extend({
    defaults: {
        img: 'Happy-Birthday-Greetings.png',
        title: 'Happy birthday to you!',
        music: 'Kevin_Lax_-_Happy_Birthday.mp3'
    },
    initialize: function () {        
        console.log("Card object initialized.");
    },
    getImageUrl: function () {
        return 'img/' + this.get('img');
    },
    getMusicUrl: function () {
        return 'res/' + this.get('music');
    }
});

카드에 포함될 기본 데이터로 이미지, 음악, 제목 속성을 지정하고 이미지는 img 폴더에 음악은 res 폴더에 두도록 설계했습니다.

initialize 함수 (정확히는 함수형 속성)는 해당 모델이 생성되는 시점에 호출되며 여기에서 생성 시 필요한 초기화를 할 수 있습니다.


이제 이 모델을 통해 인스턴스를 하나 생성해 보도록 합시다.

    var birthdayCard = new Card({    
    to: 'My beloved Joyan',
    from: 'Your father',    
    message:'May happiness be with you all!'
});

이 카드 모델에 추가로 필요한 속성들(수신인, 발신인, 내용)을 추가해서 인스턴스를 만들었습니다.

이상 없이 코딩했다면 console 창에 "Card object initialized."가 찍히겠죠.

3. 뷰 생성하기

앞서 소개드린 바로는 아직 별다른 이득(benefits)이 하나 없습니다. 이제 이걸로 뷰(view) 객체를 만들어 어떤 이득을 얻을 수 있는지 확인해 보겠습니다.

<div id="myCard"></div>


    var CardView = Backbone.View.extend({
    el: '#myCard',
    audio: null,
    canvas: null,

    initialize: function () {

        this.render();
        /* 
        * retrieving any HTML element by JQuery returns array object.
        * Select first one of the retrieved array.  
        */
        this.canvas = $('#myCanvas')[0];
        this.audio = $('#myAudio')[0];

        console.log('Card view intialized.');
    },
    render: function () {
        // append canvas, audio tag
        this.$el.append('<canvas id="myCanvas" width="730" height="414"></canvas>');
        this.$el.append('<audio id="myAudio"></audio>');
        return this;
    } 
});

모델을 생성할때와 유사하게 Backbone.View.extend() 함수를 사용해서 뷰를 생성합니다.

멤버 속성 중 하나인 el 에 index.html에 추가한 div 태그를 참조하기 위한 DOM selector를 저장합니다. 그리고, 앞서 설명드린 바와 같이 초기화 과정 중 호출되는 initialize() 함수를 살펴보면 index.html에 선언한 myCard라는 아이디의 div 태그를 참조하도록 되어 있으며 생성 시, HTML5 canvas, audio를 myCard div 내에 삽입하고 이를 멤버 속성인 audio와 canvas에 저장하도록 되어 있습니다.

이제 이 뷰의 인스턴스를 생성해 봅시다.

    var birthdayCardView = new CardView({
    model: birthdayCard
 });

CardView의 생성자에 model이라는 속성으로 앞서 생성한 birthdayCard를 지정합니다.

정상적으로 코드가 수행하면 console 창에 'Card view intialized.'가 출력됩니다.


3. 뷰에서 모델 정보 참조하기

이제 뷰와 모델이 연결되었습니다. 이제는 모델에 저장된 정보를 가지고 뷰를 꾸며보도록 하겠습니다.


    var CardView = Backbone.View.extend({
    el: '#myCard',
    canvas: null,
    audio: null,

    initialize: function () {
        this.render();
        /* 
        * retrieving any HTML element by JQuery returns array object.
        * Select first one of the retrieved array.  
        */
        this.canvas = $('#myCanvas')[0];
        this.audio = $('#myAudio')[0];

        this.showImage(); /* add image to canvas */

        console.log('Card view intialized.');
    },
    render: function () {
        /* omitted to short */
    },
 
    showImage: function () {
        var view = this;
        var context = view.canvas.getContext('2d');
        var imageObj = new Image();
        // clear canvas
        context.clearRect(0, 0, view.canvas.width, view.canvas.height);
        imageObj.onload = function () {
            context.drawImage(imageObj, 5, 5, 730, 414);
        };
        imageObj.src = view.model.getImageUrl();
        console.log('An image is shown.');
        this.state = 'image';
    }  
});

이로써 초기화 과정 중 birthdayCard 인스턴스에 저장된 image 파일을 가져다 canvas에다 뿌렸습니다.

showImage() 함수의 imageObj.src = view.model.getImageUrl(); 코드와 같이 view.model로 모델의 객체에 접근할 수 있습니다.

4. 뷰에서 이벤트 처리하기

이번에는 마우스를 이미지에 올릴 경우, 모델 객체에 저장된 음악을 출력하고 이미지에서 마우스를 내릴 경우 멈추도록 이벤트 핸들링을 해 봅시다.

    var CardView = Backbone.View.extend({
    el: '#myCard',
    canvas: null,
    audio: null,

    events: {
        'mouseover': 'playMusic',
        'mouseout': 'pauseMusic'
    },
    initialize: function () {
        this.render();
        /* 
        * retrieving any HTML element by JQuery returns array object.
        * Select first one of the retrieved array.  
        */
        this.canvas = $('#myCanvas')[0];
        this.audio = $('#myAudio')[0];
        this.setMusic(); /* 음악 파일 경로를 설정 */
        this.showImage();
        console.log('Card view intialized.');
    },
    render: function () {
        // append canvas, audio tag
        this.$el.append('<canvas id="myCanvas" width="730" height="414"></canvas>');
        this.$el.append('<audio id="myAudio"></audio>');
        return this;
    },
    setMusic: function () {
        // Set a birthday song from model
        this.audio.src = this.model.getMusicUrl();
    },

    showImage: function () {
       /* omitted to short*/
    },
 
    playMusic: function () {
        this.audio.play();
        console.log('The birthday song is playing.');
    },
    pauseMusic: function () {
        this.audio.pause();
        console.log('The birthday song paused.');
    }
});

뷰 객체의 events 속성으로 'mouseover', 'mouseout' 이벤트에 대한 핸들러를 playMusic, pauseMusic 과 같이 지정함으로써, 사진에 마우스를 올렸을때 음악이 흘러나오도록 하는 이벤트를 처리할 수 있습니다.

추가로 마우스 클릭 시, 이미지 대신 하트를 그리고 다시 클릭 시 메세지를 출력해 봅시다.

    var CardView = Backbone.View.extend({
    el: '#myCard',
    canvas: null,
    audio: null,
    state: null, /* 클릭 시 상태 변이를 저장하는 속성 */
    events: {
        'click': 'onClick',
        'dblclick': 'showImage',
        'mouseover': 'playMusic',
        'mouseout': 'pauseMusic'
    },
    initialize: function () {
        this.render();
        /* 
        * retrieving any HTML element by JQuery returns array object.
        * Select first one of the retrieved array.  
        */
        this.canvas = $('#myCanvas')[0];
        this.audio = $('#myAudio')[0];
        this.setMusic();
        this.showImage();
        console.log('Card view intialized.');
    },
    render: function () {
        // append canvas, audio tag
        this.$el.append('<canvas id="myCanvas" width="730" height="414"></canvas>');
        this.$el.append('<audio id="myAudio"></audio>');
        return this;
    },
    setMusic: function () {
        // Set a birthday song from model
        this.audio.src = this.model.getMusicUrl();
    },
    onClick: function () {
        switch (this.state) {
            case 'image':
                this.showHeart();
                break;
            case 'heart':
                this.showMessage();
                break;
            case 'message':
                // ignore state change
                break;
        }
    },
    showImage: function () {
        var view = this;
        var context = view.canvas.getContext('2d');
        var imageObj = new Image();
        // clear canvas
        context.clearRect(0, 0, view.canvas.width, view.canvas.height);
        imageObj.onload = function () {
            context.drawImage(imageObj, 5, 5, 730, 414);
        };
        imageObj.src = view.model.getImageUrl();
        console.log('An image is shown.');
        this.state = 'image';
    },
    showHeart: function () {
        var view = this;
        var context = view.canvas.getContext('2d');
        // clear canvas
        context.clearRect(0, 0, view.canvas.width, view.canvas.height);
        context.strokeStyle = "#000000";
        context.strokeWeight = 3;
        context.shadowOffsetX = 4.0;
        context.shadowOffsetY = 4.0;
        context.lineWidth = 10.0;
        context.fillStyle = "#FF0000";
        var d = 150;
        var k = 100;
        context.moveTo(k, k + d / 4);
        context.quadraticCurveTo(k, k, k + d / 4, k);
        context.quadraticCurveTo(k + d / 2, k, k + d / 2, k + d / 4);
        context.quadraticCurveTo(k + d / 2, k, k + d * 3 / 4, k);
        context.quadraticCurveTo(k + d, k, k + d, k + d / 4);
        context.quadraticCurveTo(k + d, k + d / 2, k + d * 3 / 4, k + d * 3 / 4);
        context.lineTo(k + d / 2, k + d);
        context.lineTo(k + d / 4, k + d * 3 / 4);
        context.quadraticCurveTo(k, k + d / 2, k, k + d / 4);
        context.stroke();
        context.fill();
        console.log('A heart is shown.');
        this.state = 'heart';
    },
    showMessage: function () {
        var view = this;
        var context = this.canvas.getContext('2d');
        // Print 'To' message
        context.font = 'bold 20pt Segoe UI';
        context.fillText('To. ' + view.model.get('to'), 10, 50);
        // Print 'title' message
        context.font = 'italic 40pt Calibri';
        context.fillStyle = 'gray';
        context.fillText(view.model.get('title'), 10, 310);
        // Print 'message' message
        context.font = 'italic 20pt Calibri';
        context.fillText(this.model.get('message'), 10, 350);
        // Print 'from' message
        context.font = '10pt Segoe UI';
        context.fillStyle = 'black';
        context.fillText('Sincerely, \n' + view.model.get('from'), 10, 400);
        console.log('The birthday message is shown.');
        this.state = 'message';
    },
    playMusic: function () {
        this.audio.play();
        console.log('The birthday song is playing.');
    },
    pauseMusic: function () {
        this.audio.pause();
        console.log('The birthday song paused.');
    }
});

이로써 이번 샘플 앱을 모두 완성했습니다.

onClick 함수에서 click 이벤트가 발생할 때마다 state 속성을 image -> heart -> message로 변경시켜 이에 따라 알맞는 함수를 호출하고 있습니다. showHeart()의 경우, canvas에 벡터 방식으로 heart를 그리고 있고 showMessage() 함수에서는 모델에서 가져온 속성들 (to, title, message, from)을 출력하고 있습니다.


이상으로 간략하게 나마 backbone.js를 이용해 코딩하는 법을 소개했습니다.

서두에 말씀드린 것처럼 backbone.js를 사용하면 기존 코드보다 훨씬 가독성이 좋은 코드로 구현되고 역할 별로 책임(responsibility)이 분리되어 유지 보수성이 향상됩니다.

추가적으로 컬렉션, REST API 연동 등의 우수한 기능을 제공하기 때문에 이번 기회에 익혀 두시면 좋을 듯 합니다.


긴 글 끝까지 읽어 주셔서 감사합니다. 행복한 하루 되시길!

안녕하세요, 생각의 웹입니다.


이번에는 이전에 소개드린 바 있는 Node.JS에서 Arduino를 제어하는 JavaScript API 모듈인 duino에 온습도 센서인 dht11 를 제어할 수 있도록 기능 추가한 사례를 소개하려 합니다.

이를 위해서 먼저 duino github 소스 코드를 다운로드 받습니다.

duino 프로젝트를 git clone 하거나 다운로드 받아 압축을 풀면 examples, lib, node_modules, src 폴더와 index.js 파일 등이 있습니다.


1. arduino에 DHT11 센서 코드 추가하기

src 폴더에는 du.ino 파일이 존재합니다. 이 파일은 Arduino IDE의 프로젝트 파일로써 Arduino 보드에서 동작할 firmware 코드를 담고 있습니다.

Arduino IDE를 이용해 du.ino 파일을 열면 다음과 같은 코드로 구성되어 있습니다.


#include <servo.h>
bool debug = false;
 // ... omitted ...
void setup() {
  Serial.begin(115200);
}
void loop() {
  while(Serial.available() > 0) {
    char x = Serial.read();
    if (x == '!') index = 0;      // start
    else if (x == '.') process(); // end
    else messageBuffer[index++] = x;
  }
}
/*
 * Deal with a full message and determine function to call
 */
void process() {
  // ... omitted ...
 switch(cmdid) {
    case 0:  sm(pin,val);              break;
    case 1:  dw(pin,val);              break;
    case 2:  dr(pin,val);              break;
    case 3:  aw(pin,val);              break;
    case 4:  ar(pin,val);              break;

    case 97: handlePing(pin,val,aux);  break;
    case 98: handleServo(pin,val,aux); break;
    case 99: toggleDebug(val);         break;
    default:                           break;
  }
}
 // ... omitted ...


코드를 간략히 설명하면, Arduino controller는 setup() 함수 호출하여 serial port의 전송 속도를 지정 후 loop() 함수로 진입하여 메세지를 패칭(fetching)하여 특정 동작을 수행하게 됩니다. 이때 process() 함수 내 switch() 문으로 명령 ID를 구분해 적절한 이벤트 핸들러 (event handler) 가 호출되는 방식입니다.

따라서, 여기에 DHT11 센서에서 정보를 가져오는 명령 ID를 추가하고 이에 걸맞는 이벤트 핸들러를 추가하도록 하겠습니다. 명령 ID는 96으로, 관련 이벤트 핸들러는 handleDHT11(pin, val); 로 정의하겠습니다.


#include <servo.h>
// webofthink:For supporting DHT11 sensor
#include 
dht11 rht01;
// webofthink:end

bool debug = false;
 // ... omitted ...
}
/*
 * Deal with a full message and determine function to call
 */
void process() {
  // ... omitted ...
 switch(cmdid) {
    case 0:  sm(pin,val);              break;
    case 1:  dw(pin,val);              break;
    case 2:  dr(pin,val);              break;
    case 3:  aw(pin,val);              break;
    case 4:  ar(pin,val);              break;
    case 96: handleDHT11(pin, val);    break; /* webofthink:add DHT11 support */
    case 97: handlePing(pin,val,aux);  break;
    case 98: handleServo(pin,val,aux); break;
    case 99: toggleDebug(val);         break;
    default:                           break;
  }
}
 // ... omitted ...

/* Handle DHT11 Humidity & Temperature
 * @author webofthink
*/
void handleDHT11(char *pin, char *val) {
  int p = getPin(pin);
  int chk = rht01.read(p - A0); 
  char m[12];
  sprintf(m, "%s::%03d,%03d", pin, rht01.temperature, rht01.humidity);
  Serial.println(m);
}


DHT11 센서에서 값을 가져오는 로직은 DHT11이라는 라이브러리로 제공되기 때문에 다운로드 받으신 후 헤더 파일을 추가(include)해서 사용하시면 쉽게 코딩할 수 있습니다.

여기에 주의 깊게 봐둘 사항은 Node.JS 측으로 전송할 메세지 형태입니다. handleDHT11() 함수의 sprintf() 문의 두 번째 인자를 참고하기 바랍니다.


2. duino library 추가하기


이번에는 lib 폴더로 이동합니다. 여기에 보면 duino 프로젝트가 기 지원하는 센서 및 엑추에이터들의 API 모듈이 존재합니다.

여기에 DHT11 센서를 지원하기 위한 API 모듈인 dht11.js을 추가합니다.


    var events = require('events'),
    util = require('util');
/*
 * The main DHT11 constructor
 * Connect to the serial port and bind
 */
var DHT11 = function (options) {
    if (!options || !options.board || !options.pin) throw new Error('Must supply required options to DHT11 Sensor');
    this.board = options.board;
    this.pin = options.pin;
    // Loop and trigger fire-read sequences
    setInterval(function () {
        var msg = '96' + this.pin;
        this.board.write(msg);
    }.bind(this), options.throttle ||  500);
    // When data is received, parse inbound message
    // match pin to instance pin value
    this.board.on('data', function (message) {
        
        var m = message.slice(0, -1).split('::'),
            err = null,
            pin, data;
        if (!m.length) {
            return;
        }
        pin = m[0]
        data = m.length === 2 ? m[1] : null;
        if (pin == this.pin) {
            // console.log("emitting data to read event: " + data);
            var values = data.slice(0).split(',');
            this.temperature = parseInt(values[0]);
            this.humidity = parseInt(values[1]);
            this.emit('read', err, this.temperature, this.humidity);
        }
    }.bind(this));
 };
/*
 * EventEmitter, I choose you!
 */
util.inherits(DHT11, events.EventEmitter);
module.exports = DHT11;


이 API 모듈 소스의 구성은 다음과 같습니다. 먼저 DHT11 클래스와 생성자를 선언합니다. 생성자에서는 options 라는 객체를 인수로 받아 필요한 초기 환경을 설정합니다. 아두이노 보드와 센서가 연결된 핀 정보가 설정되었다면, Arduino 측에 DHT11 정보를 요청하는 메세지를 setInterval() 함수를 통해 정기적으로 전달합니다.

이 명령이 Arduino 보드에 전송되면 첫번째 항목에서 구현한 이벤트 핸들러 handleDHT11()가 호출되어 요청받은 온습도 정보를 Node.JS에 회신합니다.

그러면 Node.JS에서는 this.board.on()의 'data' 이벤트가 발생(trigger)하게 되면서 해당 정보를 가져올 수 있게 됩니다.

마지막으로 이 온습도 정보를 EventEmitter의 'read' 이벤트로 재 전송하도록 구현합니다.


3. duino API에 DHT11 추가하기


다시 root 폴더로 돌아가서 index.js를 열어 DHT11를 다음과 같이 추가합니다.


    
module.exports = {
  Board:  require('./lib/board'),
  Led:    require('./lib/led'),
  Piezo:  require('./lib/piezo'),
  Button: require('./lib/button'),
  Servo:  require('./lib/servo'),
  Sensor: require('./lib/sensor'),
  Ping:   require('./lib/ping'),
  PIR:    require('./lib/pir'),
  LCD: require('./lib/lcd'),
    DHT11: require('./lib/dht11') /* add to support DHT11 sensor */
};


이로써, duino에서 DHT11 API를 사용할 수 있게 되었습니다.


4. DHT11 API 예제


examples 폴더로 이동하면 제공하는 API 모듈 별 예제 코드가 있습니다. 여기에 이번에 추가한 DHT11 API의 예제도 추가해 봅시다.


    
/* following is a test code for DHT11 sensor. */
var arduino = require('duino');
          
console.log("Initializing arduino board...");
var board = new arduino.Board({
    device: 'COM6',
    debug: false
});
var dht11 = new arduino.DHT11({
    board: board,
    pin : 'A2'
});
board.on('ready', function () {
    console.log("arduino board is ready to serve.");
});
dht11.on('read', function (err, temp, humidity) {
    console.log("temperature: " + temp + " degree of Celcius, " + "humidity: " + humidity + "%");
});


예제를 보면 알 수 있듯이 Arduino 보드 객체와 알맞은 핀 정보를 options 객체의 속성으로 전달 후 on 함수의 'read' 이벤트 핸들러로 온습도 정보를 가져올 수 있습니다.


5. DHT11 API


지금까지 추가했던 DHT11 API의 Web IDL은 다음과 같습니다.


    enum DHT11EmitterType { "read"};

    dictionary DHT11Option {
        Board board;
        DOMString pin;
        unsigned short throttle; // time interval per each measurements
    };
     
    callback DHT11OnCallback = void (Error err, short temperature, unsigned short humidity);
    
    [Constructor, Constructor (DHT11Option option)]
    interface DHT11 {
        void on(DHT11EmitterType type, DHT11OnCallback cb); 
    };


지금까지 Node.JS를 통해 arduino의 센서를 제어하기 위해 필요한 단계를 정리해 보았습니다. duino에는 상당한 센서와 엑추에이터를 지원하는 API가 존재하나 이처럼 지원하지 않는 센서가 있을 경우에도 추가 구현이 용이하도록 구조화 되어 있습니다.

서두에 링크한 문서를 살펴보면 arduino board 에 ethernet이나 wi-fi 모듈을 추가하여 이런 센서 정보를 곧바로 웹으로 노출시키는 방법도 있습니다만 이렇게 Node.JS를 gateway로 두고 서비스를 확장하는 것이 context data를 지식화 하는데 유용한 구현 방법이라고 생각합니다.

긴 글 읽어 주셔서 감사합니다. 늘 행복하시길!

"생각의 웹"입니다.


Server side JavaScript platform인 Node.JS에서 사용할 유닛 테스트 프레임워크를 찾던 중 xUnit 계열인  nodeunit이 있어 이렇게 소개드리려고 합니다.

1. 설치

nodeunit은 https://github.com/caolan/nodeunit 에서 다운로드 받아 컴파일하거나 NPM을 통해 다운로드 받을 수 있습니다. 이때 node와 별개의 프로세스로 동작하기 때문에 -g 옵션을 주고 설치해야 합니다.

npm install nodeunit -g

설치가 완료되면 console 상에서 nodeunit 명령을 실행할 수 있습니다. 다음과 같이 수행하면 nodeunit의 사용법을 출력합니다.

nodeunit --help

2. 테스트 케이스란?


유닛 테스트 케이스(unit test case)는 테스트 대상 API (API under test), 테스트 조건(pre-condition and post-condition), 테스트 입력 값 (test input) 그리고 예상 출력 값 (expected output)으로 구성됩니다. 테스트 결과 판별 (assertion)은 보통 입력 값과 예상 출력 값이 같은 지를 비교하여 이뤄지며 그 결과를 취합하여 출력하게 됩니다.

3. 테스트 케이스 작성


다음은 가장 간단한 형태의 테스트 케이스 작성법입니다.


exports.testSomething = function(test){
    test.expect(1);
    test.ok(true, "this assertion should pass");
    test.done();
};

exports.testSomethingElse = function(test){
    test.ok(false, "this assertion should fail");
    test.done();
};


상기 예제를 보면 두 개의 테스트 케이스 (testSomething, testSomethingElse)가 존재하며, 결과 판별을 위해 test.ok()라는 API를 제공하고 있습니다.

이 API의 상세는 다음과 같습니다. 

interface Test {
   /**
    * Test if is a true value
    *
    */
    void ok(boolean value, optional DOMString message);
};


또한, 유심히 볼 필요가 있는 부분은 test.expect(1); 와 test.done(); 인데 이 API들은 JavaScript 특유의 비동기 동작(asynchronous operation)으로 인해 의도치 않은 결과가 나오는 것을 막기 위해 추가되었습니다.

좀 더 상세히 설명드리면, JavaScript는 콜백 함수(callback function)라는 형태로 요청한 동작의 결과를 되돌려 받는 경우가 많은데 이때 이 콜백 함수는 비동기적으로 호출되기 때문에 테스트 케이스를 잘못 작성하는 경우에는 호출되기 전에 테스트가 종료되는 경우가 종종 발생합니다.

이런 실수를 방지하기 위해서 nodeunit에서는 테스트가 끝나는 시점을 알려주는 test.done() API를 명시적으로 호출하도록 설계되었습니다.

또한, 테스트 케이스가 종료되기 전에 수행되어야 할 판별 (assertion)의 수를 test.expect(number) API로 호출하도록 해서 의도치 않는 결과 (false-positive)가 나오지 않도록 했습니다.

4. 보다 복잡한 테스트 케이스 작성


DB API와 같이 시간이 오래 걸리고 테스트 조건을 설정하는 데 많은 비용이 걸리는 경우를 위해 테스트 스윗(test suite)이라는 테스트 묶음을 위한 구조가 존재합니다.

테스트 스윗의 경우, 각 테스트 마다 선행 조건과 후행 조건이 실행될 수 있도록 setUp, tearDown 이라는 API를 제공합니다.

module.exports = {
    setUp: function (callback) {
        this.mysql = require('mysql');
        this.connection = this.mysql.createConnection({
            host: 'localhost',
            user: 'root',
            password: 'password',
            database: 'db'
        });
        this.connection.connect();
        callback();
    },
    tearDown: function (callback) {
        // clean up code here
        this.connection.end();
        callback();
    },

    testSqlWrite : function (test) {

        test.expect(1);
        this.connection.query('insert into tempData(tempDate, tempCelsius) values(?, ?)', [new Date(), 10],
            function (err, rows, cols) {
                if (err) {
                    test.ok(false, err);
                } else {
                    test.ok(true, rows);
                }
                test.done();
            });
    },

    testMysqlRead : function (test) {   
        test.expect(1);
        this.connection.query('select * from tempData',
            function (err, rows, cols) {
                if (err) {
                    test.ok(false, err);
                } else {
                    test.ok(true, rows);
                }            
                test.done();
            });
    }
};


상기 예제 코드는 Node.JS에서 MySQL를 사용할 수 있도록 mysql 모듈을 이용해 테스트 케이스를 작성한 것입니다.

이 코드를 수행하면, 1) setUp -> testMysqlWrite -> tearDown 2) setUp -> testMysqlRead 순으로 호출되게 됩니다. 코드 중 비 동기로 결과를 반환하는 API가 많음을 유념하시기 바랍니다.


이상으로 Node.JS 상에서 유닛 테스트 케이스를 작성하는 간단한 사용법을 소개드렸습니다. 코드를 작성하면서 테스트를 위해 작성했던 코드 뭉치 (code snippet)를 이와 같이 테스트 케이스로 작성하면 이후 변경 시 품질 관리 등에 유용하게 활용할 수 있습니다.


이상입니다. 감사합니다.  

안녕하세요. "생각의 웹"입니다.


지난 포스트에서 말씀드린 바와 같이 이번 글에서는 객체에 의미를 부여하기 위해 객체 정의 이후 객체의 속성과 메소드를 추가하는 법에 대해 설명드리려고 하는데, 이에 앞서 지난 번에 생략하고 지나간 Web IDL에서 제공하는 형(type)들에 대해 먼저 언급하고자 합니다.


1. Web IDL 의 기본 형들 (types)

이런 글을 읽는 분이라면 이 형이 동네에서 노는 형들을 의미하는 것이 아닌 것은 아시리라 믿지만 왕초보자를 위해 프로그래밍 언어의 가장 기초적인 내용을 말씀드리죠. 


컴퓨터는 0 과 1 외에는 알지 못하지만 수 많은 0과 1을 (2진수라고 합니다.) 엄청나게 빠른 시간 내에 처리할 수 있는 능력을 지녔습니다. 하지만 컴퓨터에게 일을 시키는 역할을 하는 프로그램을 짜는 건 사람이다 보니 2진수보다 10진수로 표기되는 정수(integer)와 실수(float point number) 그리고 문자열(string) 등에 익숙합니다. 따라서 대부분의 프로그래밍 언어는 이런 형들을 지정해주면 컴퓨터가 이해할 수 있는 2진수로 변환해 주는 기능을 합니다. 더욱이 자바스크립트 언어는 타입이 없는 (untyped language) 언어라고 불리는 데 특별히 형을 지정해 주지 않아도 값을 통해 형을 유추하는 기능을 제공한다는 의미로 보면 됩니다.


뻔한 이야기를 다시 하는 이유는 자바스크립트에서는 형을 지정해 줄 필요가 없더라도 API 입장에서 보면 상황이 다릅니다. API를 사용하고자 하는 프로그래머에게 호출하고자 하는 대상에 전달할 값의 형을 알려주어야 하기 때문입니다. 자바스크립트에서는 필요에 따라 암묵적으로 형 변환 (implicit type conversion)이 일어나지만 종종 의도치 않는 변환이 일어날 수 있기에 주의해야 합니다.


다시 기본 제공하는 형들 (primitive types)의 예약어들(reserved keywords)를 보편적으로 많이 쓰는 C/C++/Java를 기준으로 나열해 보면,


  • 참/거짓 형: bool
  • 정수 형: { signed | unsigned } short, int, long
  • 실수 형: { signed | unsigned } float, double
  • 문자 형: char, char[], char*, String
정수 형이나 실수 형은 +/- 부호 유무와 숫자를 담을 수 있는 크기 (16 bytes, 32 bytes, 64 bytes)에 따라, 문자 형은 문자의 인코딩(encoding) 방식과 문자열의 길이 등에 따라 각기 다른 형을 지정해 주고 사용해야 합니다.

Web IDL에서는 이와 유사하게 다음과 같은 예약어들을 사용합니다.

  • 참/거짓 형: boolean
  • 정수 형: byte, {unsigned} short, long
  • 실수 형: {unsigned} float, double
  • 문자 형: DOMString
  • 객체 형: object  

이외에 임의의 형 (any) 등도 있으니 자세한 사항은 Web IDL spec.를 확인하시기 바라며, int 가 예약어가 아니라는 점 유의하시면 좋을 것 같습니다.

2. 속성 정의하기

형들에 대해 이해하셨다면 이런 형들을 가진 속성들(attributes)을 객체에 추가해 보기로 하죠. 지난 글에서 작성했던 Web IDL 설계를 다시 기재하겠습니다.

[Constructor]

[Constructor(DOMString model)]

interface ArduinoBoard {


};


이제 생성자에서 받은 model이라는 parameter를 속성으로 객체에 저장하기 위해 model attribute를 추가해 봅시다.


속성을 설계하는 문법은 다음과 같습니다.


interface 인터페이스_이름 {

attribute 속성의_형 속성의_이름;

};



[Constructor]

[Constructor(DOMString model)]

interface ArduinoBoard {


attribute DOMString modelName;

};

간단하죠? 이로써 멍텅구리 객체가 정보를 갖는 객체로 진화했습니다. ;-)



3. 메소드 정의하기


객체가 속성만을 지니고 있다면 소극적인 느낌이죠. 이번에는 객체에 행위를 부여하기 위해 메소드를 추가해 봅니다.


메소드는 메소드 이름, 메소드의 인자들(parameters), 메소드의 반환 형 (return type)으로 구성되어 있고 문법은 다음과 같습니다.


interface 인터페이스_이름 {

    반환_형 메소드_이름(인자_형 인자_이름, ...);

};


유념할 사항 중 하나는 설계 시, 메소드 오버로딩(method overloading) 기법을 사용하게 되면 의도치 않은 오류를 일으킬 수 있다는 점인데, 이는 자바스크립트에서는 자동 형 변환이 일어나기 때문입니다. 


백문불여일견이니 예시로 문자열을 전송하는 쓰기 메소드를 설계해 보죠.


[Constructor]

[Constructor(DOMString model)]

interface ArduinoBoard {


attribute DOMString modelName;

void write(DOMString msg);

};

이 메소드는 메세지를 쓰고 결과를 반환하지 않도록 (void) 설계했습니다. 


지금까지 객체에 속성과 메소드를 정의하는 법을 설명드렸습니다. 여기까지 설명드린 것으로도 간단한 자바스크립트 API를 설계하실 수 있게 되었습니다. 축하드립니다!


하지만, 자바스크립트는 생각하는 것보다 만만치 않은 언어입니다. 조금 더 공부해 보시면 아마 곧 깨닫게 되실 겁니다. 따라서 다음 포스팅은 자바스크립트 언어의 특징을 살려서 API를 보다 상세히 설명하는 내용으로 준비해 보려고 합니다.


긴 글 읽어주셔서 감사드리며 궁금한 점 댓글로 남겨주시면 성실히 답변드리도록 하겠습니다. 행복한 하루 되시길!!!

 


"생각의 웹"입니다.


최근 소프트웨어 교육이 핫 이슈라 제 블로그에 "스크래치" 키워드를 통해 방문하시는 분들이 꽤 많이 보입니다.



2012-8-22-Creative_Computing_Korean Version.pdf


상기 첨부한 Creative Computing PDF 문서는 MIT 미디어 랩에서 스크래치 도구를 만든 후 초등학생 대상으로 교육을 진행하기 위해 작성한 문서를 경인 교대에서 한글로 번역한 문서입니다.


꼭 교사가 아니더라도 우리 아이들의 창의력 향상을 위해 소프트 웨어 교육이 어떻게 진행되어야 하는지 통찰을 얻으실 수 있으리라 확신합니다.


감사합니다.

"생각의 웹" 입니다.


지난 두번의 포스팅에서 이 글을 쓰는 배경관련 도구에 대해 설명드렸습니다.


이 글부터는 본격적으로 JavaScript API를 Web IDL로 설계하는 방법을 duino 라는 Arduino 제어를 위한 JavaScript API를 예제로 해서 Web IDL 기본 문법부터 차근 차근 알려드리도록 하겠습니다.


먼저, duino의 샘플 코드를 다시 보죠.


var arduino = require('duino'),
    board = new arduino.Board({
     device: 'USB0'
});

var led = new arduino.Led({
  board: board,
  pin: 13
});

led.blink();

소스 코드에 대해 간단히 설명드리면 arduino 변수에 'duino' 모듈을 불러와 Board 초기화를 수행하는 Board 객체를 board 변수로 생성하게 됩니다. 이후 Led 점멸을 위해 board 변수를 초기 입력 값으로 받는 Led 객체를 생성해 led 변수로 저장후 blink() 함수를 통해 점멸 기능을 손쉽게 수행하게 됩니다. 


먼저, require() 함수는 Node.JS에서 자바 스크립트 모듈화를 위해 제공하는 함수로 아직 자바스크립트 표준 (ECMAScript 5)으로 제공되지 않기에 Web IDL에서도 표현할 수 없기에 따라서 모듈화 함수 및 모듈화 함수를 통해 가져온 변수를 마치 namespace로 사용하는 부분(new arduino.Board(...) )은 제약사항으로 두고 생성자 함수인 new 키워드를 이용해 설계하는 부분부터 시작하겠습니다.

1. 객체를 정의하기

가장 널리 알려진 객체 지향 언어인 C++, Java 등에서는 객체를 찍어내는 틀을 클래스(class)라고 합니다만 Web IDL에서는 인터페이스(interface)라는 키워드를 사용합니다. (인터페이스는 C++, Java에서는 다른 의미로 사용하는 키워드이기에 혼동하면 안됩니다. )


인터페이스를 설계할 때의 기본 문법은 다음과 같습니다.


interface 인터페이스_이름 {


};


여기서 인터페이스_이름에 알맞은 인터페이스 이름을 지정하면 됩니다. 이 이름은 보통 공백을 포함하지 않는 영 단어 혹은 영 단어의 조합을 의미하며 단어의 시작은 대문자로 시작하고 그 외는 소문자로 작성하는 것을 권고합니다.


예를 들면 올바른 인터페이스의 이름으로 Board, ArduinoBoard, Led, LedLamp 와 같이 명명할 수 있습니다.

  

2. 객체를 생성하기

기존 객체 지향 언어들과 다르게 Web IDL에서 정의한 인터페이스에 대한 객체를 생성하기 위해서는 [Constructor] 확장 속성을 지정해 주어야 합니다. 이 확장 속성은 생성자 함수가 가지는 인자(parameter)들에 대해 설명해 줄 수 있습니다.

앞서 정의한 인터페이스에 별다른 인자가 없이 생성 가능한 객체를 정의하려면 다음과 같이 설계하면 됩니다.


[Constructor]

interface 인터페이스_이름 {


};

상기와 같이 설계할 경우, 객체를 다음과 같이 생성할 수 있게 됩니다.


var myObj = new 인터페이스_이름();


생성자 함수에 인자가 필요할 경우, 다음과 같이 설계합니다.

[Constructor(인자의_형식 인자_이름)]
[Constructor(인자의_형식 인자_이름, 인자의_형식 인자_이름)] 


인자의_형식은 또다른 인터페이스의_이름이거나 추후 설명할 ECMAScript의 기본 형식 등이 될 수 있으며, 인자_이름은 소문자로 시작하는 영 단어나 복합 단어입니다. (복합 단어일 경우, 두번째 단어부터는 공백없이 대문자로 시작하는 단어로 표기하는데 이걸 낙타 모양을 닮았다고 해서 보통 camel case 라고 부릅니다.)


마지막으로 [Constructor] 확장 속성은 여타 객체 지향 언어의 생성자 함수처럼 여러 번 지정이 가능합니다. 예를 들면, 


[Constructor]

[Constructor(DOMString model)]

interface ArduinoBoard {


};

위와 같이 설계할 경우, 다음과 같이 두 가지 형태로 객체를 생성할 수 있습니다.

var board1 = new ArduinoBoard();
var board2 = new ArduinoBoard("ArduinoNano");


지금까지 객체를 정의하고 생성하는 법에 대해 설명드렸습니다. 아직 객체에 아무런 정보가 없기에 생성한 객체로는 아무 일도 할 수 없습니다. 다음 글에서는 이 객체에 속성(attribute)와 메소드 (method)를 정의하는 법을 설명드리도록 하겠습니다.


알고 있는 지식을 꾸준하게 글로 적어 가는 습관이 잘 들지 않아 관심가지고 계속 구독해 주시는 분들께 죄송할 따름입니다. ^^;


늘 행복하시고 해피 코딩하시기 바랍니다.


+ Recent posts