안녕하세요,

"생각의 웹"입니다.


혹시 지난 "AppIN 세미나 - Do IoT Yourself" 강연에 참석하시고 꾸준히 이 블로그에 방문해 주시는 분들이 계신가요?

글을 시작하기 앞서 그 분들께 먼저 감사 말씀드립니다.


이번 포스팅에는 D.IoT.Y 두 번째 강연 시 대디스랩 송영광 대표님과 함께 만들었던 온도센서를 JavaScript로 구현해보고자 합니다.

이 강연은 다음 포스팅을 참조하시기 바랍니다.


Intuitive Understanding Arduino for IoT: Do IoT Yourself 2nd



여기서 실습한 온도 센서 만들기는  Sparkfun Starter Kit for RedBoard에 내장된 센서/엑추에이터로 한정해야 했기에 가격이 저렴한 서미스터 센서(Thermistor)로 기능을 구현하기 위해 arduino IDE에서 꽤 복잡한 코딩을 해야 했습니다. 


다음 코드는 실습 당시 복사/붙여넣기 했던 코드입니다.


#include <avr/pgmspace.h>

int led_state;
unsigned long time;

#define THERM_PIN   0  // 10k thermo & 10k resistor as divider.

/*
 *  Big lookup Table (approx 750 entries), 
 *  subtract 238 from ADC reading to start at 0*C. 
 *  Entries in 10ths of degree i.e. 242 = 24.2*C Covers 0*C to 150*C 
 *  For 10k resistor/10k thermistor voltage divider w/ therm on the + side.
 */
const int temps[] PROGMEM = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 
    15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
	34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 68, 
	69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 
	87, 88, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 
	102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 114, 
	115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 126, 127, 
	128, 129, 130, 131, 132, 133, 134, 134, 135, 136, 137, 138, 139, 140, 
	141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 
	153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 164, 165, 
	166, 167, 167, 168, 169, 170, 171, 172, 173, 174, 175, 175, 176, 177, 
	178, 179, 180, 181, 182, 182, 183, 184, 185, 186, 187, 188, 189, 190, 
	190, 191, 192, 193, 194, 195, 196, 197, 197, 198, 199, 200, 201, 202, 
	203, 204, 205, 205, 206, 207, 208, 209, 210, 211, 212, 212, 213, 214, 
	215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 225, 226, 227, 
	228, 228, 229, 230, 231, 232, 233, 234, 235, 235, 236, 237, 238, 239, 
	240, 241, 242, 243, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 
	252, 253, 254, 255, 256, 257, 258, 259, 260, 260, 261, 262, 263, 264, 
	265, 266, 267, 268, 269, 269, 270, 271, 272, 273, 274, 275, 276, 277, 
	278, 279, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 289, 
	290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 301, 302, 
	303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 315, 
	316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 
	330, 331, 332, 333, 334, 335, 335, 336, 337, 338, 339, 340, 341, 342, 
	343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 
	357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 
	371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 
	385, 386, 387, 388, 389, 390, 392, 393, 394, 395, 396, 397, 398, 399, 
	400, 401, 402, 403, 404, 405, 406, 407, 408, 410, 411, 412, 413, 414, 
	415, 416, 417, 418, 419, 420, 422, 423, 424, 425, 426, 427, 428, 429, 
	430, 432, 433, 434, 435, 436, 437, 438, 439, 441, 442, 443, 444, 445, 
	446, 448, 449, 450, 451, 452, 453, 455, 456, 457, 458, 459, 460, 462, 
	463, 464, 465, 466, 468, 469, 470, 471, 472, 474, 475, 476, 477, 479, 
	480, 481, 482, 484, 485, 486, 487, 489, 490, 491, 492, 494, 495, 496, 
	498, 499, 500, 501, 503, 504, 505, 507, 508, 509, 511, 512, 513, 515, 
	516, 517, 519, 520, 521, 523, 524, 525, 527, 528, 530, 531, 532, 534, 
	535, 537, 538, 539, 541, 542, 544, 545, 547, 548, 550, 551, 552, 554, 
	555, 557, 558, 560, 561, 563, 564, 566, 567, 569, 570, 572, 574, 575, 
	577, 578, 580, 581, 583, 585, 586, 588, 589, 591, 593, 594, 596, 598, 
	599, 601, 603, 604, 606, 608, 609, 611, 613, 614, 616, 618, 620, 621, 
	623, 625, 627, 628, 630, 632, 634, 636, 638, 639, 641, 643, 645, 647, 
	649, 651, 653, 654, 656, 658, 660, 662, 664, 666, 668, 670, 672, 674, 
	676, 678, 680, 683, 685, 687, 689, 691, 693, 695, 697, 700, 702, 704, 
	706, 708, 711, 713, 715, 718, 720, 722, 725, 727, 729, 732, 734, 737, 
	739, 741, 744, 746, 749, 752, 754, 757, 759, 762, 764, 767, 770, 773, 
	775, 778, 781, 784, 786, 789, 792, 795, 798, 801, 804, 807, 810, 813, 
	816, 819, 822, 825, 829, 832, 835, 838, 842, 845, 848, 852, 855, 859, 
	862, 866, 869, 873, 877, 881, 884, 888, 892, 896, 900, 904, 908, 912, 
	916, 920, 925, 929, 933, 938, 942, 947, 952, 956, 961, 966, 971, 976, 
	981, 986, 991, 997, 1002, 1007, 1013, 1019, 1024, 1030, 1036, 1042, 
	1049, 1055, 1061, 1068, 1075, 1082, 1088, 1096, 1103, 1110, 1118, 1126, 
	1134, 1142, 1150, 1159, 1168, 1177, 1186, 1196, 1206, 1216, 1226, 1237, 
	1248, 1260, 1272, 1284, 1297, 1310, 1324, 1338, 1353, 1369, 1385, 1402, 
	1420, 1439, 1459, 1480, 1502 
};


void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(19200);
  time=millis();
  //Serial.println("Ready");
}

void loop() { 

  int therm;   
  therm = analogRead(THERM_PIN) - 238;
  therm = pgm_read_word(&temps[therm]);
  
  //Serial.println(therm, DEC);
  float temp = (float)therm / 10.0;
  Serial.println(temp, 1);
  
  if (millis()-time > 1000) {   
    time = millis();
    digitalWrite(13,led_state);
    led_state =! led_state;
  }
  delay(2000);
}

약간 코딩 지식이 필요하기에 실습 시에는 많은 설명이 되지는 않았지만 여기서 조금 설명하자면
 look up table를 만들어 A0에서 읽어들인 값을 table에 있는 값에 대응해서 serial로 출력하는 logic이 되겠습니다.


보시듯, 여기서 이 프로그램의 logic의 핵심은 look up table을 어떻게 만들었는가이고 

이에 대한 답변은 table를 만들기 위해서는 서미스터 혹은 하드웨어 부품들에 대한 이해가 필요하다는 것입니다. 

즉 아래의 그림처럼 서미스터의 데이터 쉬트에서 table이 도출되었습니다. (송영광 대표님이 엑셀 신공을 사용하셨다고 하더군요. ^^a) 


서미스터는 회로 구성을 어떻게 했는가에 따라 (즉, 어떤 저항을 달았느냐)에 따라 다른 값을 내어 놓습니다. 

다시 말해 이 값은 위의 그래프에서 온도 X일때의 Y값과 같은 형태로 나옵니다.

하드웨어의 경우 물리의 세계에 닿아 있기에 재료의 특성과 환경에 따라 영향을 받기에 로직 만으로 이해하고자 하는 SW 쟁이는 이해하기 쉽지 않죠.  


따라서, 사실 원하는 동작을 얻기 위해서는 arduino 회로를 적절히 구성할 뿐 아니라
arduino IDE를 이용해 관련 로직을 구현한 후 해당 프로그램(속칭 펌웨어)을  

arduino 에서 전송해 테스트해 보는 일을 반복해야 합니다.


여기서 문제는 arduino에 이 동작 외에 추가로 기능을 구현해야 할 경우, 

반드시 arduino IDE로 펌웨어를 업드레이드해 주어야 합니다. 

이는 꽤 불편한 일이 아닐 수 없습니다. 


쓰다 보니 문제 제기로만으로도 꽤 길어졌는데 이에 대해 제가 제시하는 해결책은 다음과 같습니다.


1. arduino의 펌웨어에 호스트(host)의 명령을 처리해 동작을 수행하는 인터프리터(interpreter) 를 전송합니다.


2. 호스트는 arduino에 명령을 전송합니다. (e.g. A0 포트에서 아날로그 값을 읽어 오기)


3. 인터프리터는 명령을 받아 동작을 수행 후 결과를 호스트에 전송합니다.



혹시, 이 방식을 어디선가 본 듯한 느낌을 받은 분이 있나요? ;-)

앞서 소개한 바와 같이 이 방식으로 구현된 node.js 모듈이 바로 duino 입니다.   


관련 포스팅 : http://webofthink.tistory.com/13



이번에는 duino 모듈로 이 똑같은 동작을 수행해 보도록 하겠습니다. 


먼저, 아래 파일을 다운로드 받기 바랍니다.


duino.zip

(이 모듈은 현재 버전인 v0.0.9를 조금 수정해서 DHT11 센서 지원 및 윈도우즈에서 동작하도록 수정 확장한 버전입니다. 리눅스나 OS X는 기존 버전을 써도 무방합니다.)


이 파일의 압축을 풀어 src/du/du.ino 파일을 실행합니다. 

(먼저 Arduino IDE가 설치되어 있어야 합니다. 참고바랍니다.)


해당 arduino 펌웨어를 arduino 보드에 다운로드합니다.


정상적으로 전송되면 이제 node.js로 동일한 역할을 하는 코드를 구현해 봅니다.

코드를 다음과 같이 작성해 temp.js로 저장하고 콘솔에서 node temp 로 실행합니다.


var arduinoPort = 'COM7'; // only works in windows with patched duino module.
var pinName = '00'; // it means A0 pin

// look up table for resolving data to the appropriates temperature
var temps = [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 
    15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
	34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 68, 
	69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 
	87, 88, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 
	102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 114, 
	115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 126, 127, 
	128, 129, 130, 131, 132, 133, 134, 134, 135, 136, 137, 138, 139, 140, 
	141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 
	153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 164, 165, 
	166, 167, 167, 168, 169, 170, 171, 172, 173, 174, 175, 175, 176, 177, 
	178, 179, 180, 181, 182, 182, 183, 184, 185, 186, 187, 188, 189, 190, 
	190, 191, 192, 193, 194, 195, 196, 197, 197, 198, 199, 200, 201, 202, 
	203, 204, 205, 205, 206, 207, 208, 209, 210, 211, 212, 212, 213, 214, 
	215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 225, 226, 227, 
	228, 228, 229, 230, 231, 232, 233, 234, 235, 235, 236, 237, 238, 239, 
	240, 241, 242, 243, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 
	252, 253, 254, 255, 256, 257, 258, 259, 260, 260, 261, 262, 263, 264, 
	265, 266, 267, 268, 269, 269, 270, 271, 272, 273, 274, 275, 276, 277, 
	278, 279, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 289, 
	290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 301, 302, 
	303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 315, 
	316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 
	330, 331, 332, 333, 334, 335, 335, 336, 337, 338, 339, 340, 341, 342, 
	343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 
	357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 
	371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 
	385, 386, 387, 388, 389, 390, 392, 393, 394, 395, 396, 397, 398, 399, 
	400, 401, 402, 403, 404, 405, 406, 407, 408, 410, 411, 412, 413, 414, 
	415, 416, 417, 418, 419, 420, 422, 423, 424, 425, 426, 427, 428, 429, 
	430, 432, 433, 434, 435, 436, 437, 438, 439, 441, 442, 443, 444, 445, 
	446, 448, 449, 450, 451, 452, 453, 455, 456, 457, 458, 459, 460, 462, 
	463, 464, 465, 466, 468, 469, 470, 471, 472, 474, 475, 476, 477, 479, 
	480, 481, 482, 484, 485, 486, 487, 489, 490, 491, 492, 494, 495, 496, 
	498, 499, 500, 501, 503, 504, 505, 507, 508, 509, 511, 512, 513, 515, 
	516, 517, 519, 520, 521, 523, 524, 525, 527, 528, 530, 531, 532, 534, 
	535, 537, 538, 539, 541, 542, 544, 545, 547, 548, 550, 551, 552, 554, 
	555, 557, 558, 560, 561, 563, 564, 566, 567, 569, 570, 572, 574, 575, 
	577, 578, 580, 581, 583, 585, 586, 588, 589, 591, 593, 594, 596, 598, 
	599, 601, 603, 604, 606, 608, 609, 611, 613, 614, 616, 618, 620, 621, 
	623, 625, 627, 628, 630, 632, 634, 636, 638, 639, 641, 643, 645, 647, 
	649, 651, 653, 654, 656, 658, 660, 662, 664, 666, 668, 670, 672, 674, 
	676, 678, 680, 683, 685, 687, 689, 691, 693, 695, 697, 700, 702, 704, 
	706, 708, 711, 713, 715, 718, 720, 722, 725, 727, 729, 732, 734, 737, 
	739, 741, 744, 746, 749, 752, 754, 757, 759, 762, 764, 767, 770, 773, 
	775, 778, 781, 784, 786, 789, 792, 795, 798, 801, 804, 807, 810, 813, 
	816, 819, 822, 825, 829, 832, 835, 838, 842, 845, 848, 852, 855, 859, 
	862, 866, 869, 873, 877, 881, 884, 888, 892, 896, 900, 904, 908, 912, 
	916, 920, 925, 929, 933, 938, 942, 947, 952, 956, 961, 966, 971, 976, 
	981, 986, 991, 997, 1002, 1007, 1013, 1019, 1024, 1030, 1036, 1042, 
	1049, 1055, 1061, 1068, 1075, 1082, 1088, 1096, 1103, 1110, 1118, 1126, 
	1134, 1142, 1150, 1159, 1168, 1177, 1186, 1196, 1206, 1216, 1226, 1237, 
	1248, 1260, 1272, 1284, 1297, 1310, 1324, 1338, 1353, 1369, 1385, 1402, 
	1420, 1439, 1459, 1480, 1502
];


var arduino = require('duino');
var board = new arduino.Board({
    device: 'COM7',
    debug: false //true
});



board.on('ready', function () {
    console.log("arduino board is ready to serve.");
    board.pinMode(pinName, 'in');
    
    setInterval(function () {
        console.log('request to get an analog value from ' + pinName);
        board.analogRead(pinName);
    }, 1000);
});

board.on('data', function (message) {
    // message looks like {pin}::{data}. so it will be disassembled properly.
    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;

    console.log('reading value from pin' + pin + ': ' + data );
    if (pin === pinName) {        
        var converted = temps[parseInt(data) - 238] / 10.0;
        console.log('current temperature is ' + converted + ' degree of celsius');
    }
});


C:\tests>node temp.js
arduino board is ready to serve.
request to get an analog value from 00
reading value from pin00: 489
current temperature is 23.5 degree of celsius
request to get an analog value from 00
reading value from pin00: 489
current temperature is 23.5 degree of celsius
request to get an analog value from 00
reading value from pin00: 489
current temperature is 23.5 degree of celsius
^C
C:\tests>


단순히 보면 위의 코드가 앞서 arduino 펌웨어의 코드보다 더 간략해 보이지 않지만, 

이 코드는 세번 째 강연으로 진행했던 사물 간의 연결을 위한 Open API에서
다루었던 serialport 모듈의 기능을 포함하고 있습니다.

(즉 비슷한 코딩 량으로 두 가지 일을 한번에 한 것입니다.)


이외에도 앞서 문제로 제기했던 것들의 해결책으로써 

호스트에서 arduino 원격 제어와 펌웨어 업데이트 없이 추가 기능 구현이 가능해 졌습니다. 


더 나아가 이를 RESTful Web API와 JavaScript API 형태인 Open API로 재 설계한다면
웹을 통해서 아두이노를 원격 제어하는 게 가능해 집니다.


마지막으로 사족을 붙여 보겠습니다.


아래 포스팅을 통해 사물과 사물 간의 대화를 위해서는 수 많은 기술적 제약들이 현재 존재함을 말씀드린 바 있습니다.


- 사물 인터넷을 지탱하는 것 

- 사물 인터넷은 어떻게 생각하게 될까?


저는 이 장벽을 푸는 첫 번째 기술적 열쇠로 자바스크립트 언어를 바라보고 있습니다. 

이 언어를 사용하는 많은 개발자들이 존재하면서도 지적 재산권(IP) 문제에 자유롭고 계속적으로 기능이 향상되고 있기 때문입니다.


최근 뉴스에 등장한 오라클과 구글의 안드로이드에서의 자바 언어 사용 관련 특허 분쟁은 사물인터넷 시대를 맞이 할 우리에게 시사하는 점이 많습니다.

처음 기술을 개발한 세력이 개방과 공유를 빙자해 전파하더라도 나중에 가서는 그것을 사유화하려고 할 것이기 때문입니다. 


그러나 우리는 지난 사반세기 동안 이런 사유화를 막고 지구적 공동체를 만들어낸 훌륭한 사례를 본 적이 있습니다. 

따라서 웹과 arduino가 그러했듯이 오픈 소스 소프트웨어오픈소스 하드웨어가 초연결 사회로 나아가는 축으로 자리 잡길 기대합니다.


감사합니다.  


안녕하세요,

"생각의 웹"입니다.


AllJoyn Framework (이하 올조인)은 퀄컴에서 개발한 P2P 기술을 오픈 소스로 공개 후 AllSeen Alliance (이하 올신) 주관으로 리눅스 재단의 프로젝트로 진행하는 만물 인터넷 (사물 인터넷보다 한단계 더 나아간 개념입니다만 저는 아직까지는 동의어 혹은 파생어로 봅니다.  ) 프레임 워크입니다.


이 기술을 공개된 지도 일 년이 지난 것으로 기억하는데 가장 최근인 올 해 7월 IFA 2014에서 공개한 데모에 대해 정리한 포스팅이 있어 올조인에 대해 아직 문외한인 분들은 읽어 보면 좋을 것 같아 링크 공유합니다.


퀄컴 올조인으로 물꼬튼 올신의 현재 (칫솔 - 초이의 IT 휴게실)  



올조인의 가장 긍정적인 면은 처음부터 누구나 소스 코드를 사용할 수 있도록 공개하고도 더불어 다양한 플랫폼 개발자들을 위해 여러 프로그래밍 언어의 API 문서와 프로그래밍 가이드를 잘 정리해 두었다는 점입니다.


튜토리얼: https://allseenalliance.org/alljoyn-framework-tutorial



오픈소스의 영향으로 인지는 모르겠지만 간단한 검색어를 통한 구글링으로도 꽤 많은 관련 포스팅이 눈에 띄는데 아래 '바쁜척쟁이 개발자의 블로그'에 기능 별로 잘 정리되어 있는 것 같아 살펴 보시길 추천합니다.


http://busydeveloper.tistory.com/category/Alljoyn



개인적으로 아쉬운 점이자 이 포스팅을 쓰게 된 동기는  official site의 가이드에는 JavaScript API가 준비되지 않았다는 점입니다.

개발 이력을 살펴 보니 NPAPI를 이용해 browser에서 사용 가능한 API를 만들려는 시도가 있었다가 실패한 듯 하고 (github에서 관련 프로젝트가 없어졌더군요.) 
대신 아래 경로처럼  node.js를 위한 npm 모듈이 존재하는 것을 확인했습니다.


https://github.com/octoblu/alljoyn


따라서, 현재 클라이언트의 주변에서는 올조인 기기들을 찾을 수 없겠지만 node.js를 탑재한 모바일 기기나 
클라이언트와 node.js 서버가 같은 네트워크 상에 있다는 가정 하에서는 Open API를 통해 기기 간 통신이 가능할 것으로 보입니다.


각론으로 들어가면 사실 올조인의 API는 P2P 개념이 부족한 분들에게는 조금 생소할 수도 있습니다.

하는 역할인 기기 간 통신에 비해 복잡해 보이기도 하고요. 

각 인터페이스들의 역할과 상호 작용의 개념을 명확히 이해하기 위해 먼저 위의 튜토리얼을 읽어 보실 것을 권합니다.

어느 정도 동작 방식에 대해 이해 하셨나요? 아래 코드는 위의 깃허브의 샘플 코드에서 유추해서 Web IDL 형태로 간단히 정리해 본 JavaScript API 입니다.

 


typedef DOMString AllJoynAppName; 

callback SignalMessageCallback = void (DOMString message, DOMString info);
[Constructor (DOMString appName)]
interface BusAttachment {

    void createInterface(AllJoynAppName name, InterfaceDescription description);

    void registerBusListener(BusListener listener);

    void start();

    void connect();

    void findAdvertisedName(AllJoynAppName name);

   void registerSignalHandler(BusObject bus, 
       SignalMessageCallback  smcb,
       InterfaceDescription description, 
       DOMString signalType);
};

[Constructor ()]
interface InterfaceDescription {

    void addSignal(DOMString target, 
        DOMString messageType,
        DOMString message);
};

callback FoundAdvertisedCallback = void (DOMString name);
callback LostAdvertisedCallback = void (DOMString name);
callback NameOwnerChangedCallback = void (DOMString name);

[Constructor (FoundAdvertisedCallback facb, 
    LostAdvertisedCallback lacb, 
    NameOwnerChangedCallback nocb)]
interface BusListener {
};

[Constructor (DOMString busName)]
interface BusObject {

    void addInterface(InterfaceDescription description);
};

callback AcceptSessionJoinerCallback = void (unsigned short port, AllJoynAppName joiner);
callback SessionJoinedCallback = void (unsigned short port, DOMString sessionId, AllJoynAppName joiner);

[Constructor (AcceptSessionJoinerCallback asjcb, SessionJoinedCallback sjcb)]
interface SessionPortListener {
};

문서화 수준과 지원하는 OS (유닉스 계열 - OS X, linux 만 지원)이 부족한 것으로 보아 아직 신뢰할 만한 수준이 아닌 것으로 보입니다.
즉 이 모듈을 사용할 때 각자가 해야 할 삽질이 정해져 있다는 것을 의미합니다. -_-;; 


어쨌든 올신에는 꽤 많은 기기 제작업체가 가입해 있고 슬슬 호환 기기들도 출시를 앞두고 있은 것 같아 기존 레거시 시스템들과 혹은 올신에 가입하지 않는 업체들의 기기와의 대화를 위해 인터프리터 역할을 할 노드가 필요하리라고 예측합니다.

추후 좀 더 구체화된 내용이 있으면 포스팅하도록 하겠습니다.


감사합니다.


행복한 하루 되시길! 

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


이번에는 이전에 소개드린 바 있는 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를 보다 상세히 설명하는 내용으로 준비해 보려고 합니다.


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

 

"생각의 웹" 입니다.


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


이 글부터는 본격적으로 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)를 정의하는 법을 설명드리도록 하겠습니다.


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


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


"생각의 웹"입니다.


 "Web IDL로 JavaScript API를 설계하기" 시리즈의 첫 번째 포스팅에서 이 작업의 당위성에 대해 말씀드렸으니 이제는 도구를 설치하고 사용해 보기로 합시다.


지난 번 포스팅에서 소개해 드린 widlproc 도구는 Web IDL로 작성된 설계 문서를 XML 형태로 변환할 뿐만 아니라 연계된 도구를 통해 해당 xml에 대한 dtd와 xslt 문서를 통해 html 문서를 생성해 줍니다. 다시 말씀드리면, Web IDL로 설계한 사항에 대한 문법 적합성(문법 오류 및 완결성 등)를 검증(validation) 해서 이상이 있을 경우 해당 위치를 알려주고 문제가 없을 시 XML 문서로 내뱉어 줍니다. 이 XML를 이용해 사람이 읽을 수 있는 HTML 문서를 생성할 수 있으며 또한 이 정보를 기반으로 해당 API에 대한 코드 템플릿(stub code)을 생성하여 개발 생산성을 향상시킬 수 있습니다.


그럼 도구를 받아 설치해 보도록 합시다.

   

  1. 먼저 아래 github에서 widlproc를 다운로드 받습니다. 
    (https://github.com/dontcallmedom/widlproc)
    git clone 하거나 해당 페이지에서 다운로드 받을 수 있습니다.

  2.  해당 페이지에는 마치 windows에서 동작하는 것처럼 가이드되어 있으나 리눅스를 사용하시길 권장합니다. 윈도우즈 상에서 cygwin을 까느니 virtualbox 기반 ubuntu를 사용하는게 속 편하실 겁니다. XML 관련 도구들이 대부분 리눅스에서 제공되는 것들이기 때문입니다. 저는 아래와 같이 virtualbox에 ubuntu 14.04 LTS를 설치하여 작업했습니다.



  3. 다운로드 받은 파일이 존재하는 경로에서 Makefile 을 확인한 뒤 make 명령을 수행합니다. 빌드를 위해 필요한 라이브러리가 있다는 메세지가 나오는 경우, apt-get install 명령을 통해 설치하도록 합니다.

  4. 빌드가 정상적으로 완료되면 해당 경로 아래 obj 폴더가 생성되고 이 경로에 widlproc 이라는 실행 파일이 존재하게 됩니다.

  5. 도구의 사용법과 Web IDL 문법 및 documentation rule에 대한 정보는  /doc/widlproc.html 파일을 참고하시기 바랍니다.

  6. 간단한 테스트를 위해 함께 제공하고 있는 샘플 Web IDL 문서를 가지고 widlproc 도구를 실행해 봅시다. /examples 경로에 가면 *.widl 문서가 있습니다. 미리 알려드릴 사항은 이 문서들은 현재 widlproc 도구에서 더 이상 허용하지 않는 Web IDL 문법을 포함하고 있다는 점입니다. 따라서, 수행 결과 문법 에러 화면을 보시더라도 크게 놀라지 않으시기 바랍니다. (저는 놀랐거든요;;)

  7. *.widl 문서를 바로 *.html 문서로 생성해 주는 Makefile이 /examples 경로에 있습니다. 해당 파일을 열어 경로가 제대로 되었는지 확인합니다.



  8. make 명령을 수행하여 widl 문서를 html 문서로 변환합니다. 필요한 모든 도구가 설치되어 있다면  module ... { 으로 시작하는 라인에서 오류가 있다고 알려줍니다. (혹시 도구가 필요하다고 나오면 해당 도구를 apt-get install {도구 명} 명령으로 설치합니다. ) 여기까지 하시면 모든 작업을 제대로 수행하신 겁니다.


여기서 발생하는 오류들은 예제로 제공되고 있는 bondi.widl, filesystem.widl를 작성한 시점 이후에 Web IDL 문법은 많은 개정이 있었기 때문에 이에 맞춰 widlproc 코드는 업데이트 된 반면 예제 코드는 업데이트 되지 않아 발생한 문제입니다. 


따라서, 다음 포스팅은 지난 arduino JavaScript API 관련 포스팅에서 간략히 정리했던 Web IDL로 HTML 문서를 생성한 사례을 들어 Web IDL로 설계하는 법을 소개하도록 하겠습니다.


오늘 포스팅은 여기까지 입니다.


관심 가져주셔서 감사합니다.

늘 행복한 하루 되시길...    



  


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


이번 포스팅부터는 제목과 같이 Web IDL로 JavaScript API를 설계하는 방법을 여러분들께 공유해 보고자 합니다. 


먼저, 여러분에게 JavaScript API를 설계해야 하는 당위성과 그리고 Web IDL이 무엇인지에 대해서 설명드려야 하겠죠.


우선 첫번째 JavaScript API를 설계해야 하는 이유부터 말씀드리겠습니다.

  • JavaScript는 스크립트 언어라는 특성 상 type 선언 없이 변수를 생성하고 유연한 형 변환(type casting)을 통해 컴퓨터 언어의 지식이 부족한 초급 개발자들도 쉽게 사용할 수 있을 뿐만 아니라 함수형 언어의 특성을 갖는 객체 지향 언어로서 확장성과 유연성이 좋아 고급 개발자에게도 유용한 언어입니다.

  • 웹을 지원하는 모든 형태의 기기에서 동작하는 클라이언트 앱 외에도 Google이 만든 V8 엔진을 이용해 만든 Node.JS라는 서버 프레임워크에서도 동작하는 서버 앱 그리고 Mongo DB라는 NoSQL DB에서도 사용 가능한 매우 범용적인 개발 언어입니다.

  • 웹에서 동작하는 앱은 대부분 브라우저의 개발자 모드 (F12 키)를 통해 JavaScript code를 살펴 볼 수 있고 이런 수고가 아니라도 구글링을 통해 다양한 라이브러리 혹은 코드 조각 (code snippet)를 검색할 수 있습니다. 원하는 기능의 대부분을 단순히 복사 & 붙여 넣기로도 구현할 수 있습니다. 

  • 이렇게 코드를 훔쳐 사용하기 쉬운 구조 때문에 어차피 훔쳐 사용할 거라면 이걸 하기 위해 분석하는 수고를 덜어주는 마음씨 착한 개발자의 도움으로 인해 재사용하기 쉬운 형태로 만든 라이브러리가 매우 많이 있습니다. 물론 대부분 공짜구요. 이때 좋은 라이브러리는 API만 알아도 기능을 사용할 수 있도록 사용자에게 제공합니다. 코드를 모두 읽어 봐야 한다면 '복사 & 붙여넣기'보다 나은게 없으니까요.

  • API는 Application Programming Interface의 약자로 말 그대로 앱을 프로그래밍하는 인터페이스, 즉 앱에서 수행될 기능을 짜는 방법이라고 볼 수 있습니다. JavaScript 앱을 작성할 때 API의 도움을 받으면 적은 노력으로 원하는 기능을 구현할 수 있습니다.

  • API에 이용해 앱을 짜는데 자신감이 붙었다면 (게다가 혹시 개발 비 명목으로 돈도 벌었다면) 라이브러리가 제공하지는 않지만 앱을 짜는데 필요했던 기능을 API로 설계해서 추가해보면 어떨까요? 이런 너그러운 마음씨가 당신이 했던 수고를 반복하지 않고 당신이 알지 못하던 새로운 기능을 개발할 수 있도록 도울 겁니다. 당신이 사용했던 라이브러리를 개발한 원 저자가 그랬듯이 말입니다.

  • 좋은 앱은 많은 사람들에게 유용한 기능을 제공해 주지만 모든 사람들을 만족시켜 줄 수 없습니다. 사람들의 취향이란 사람들의 숫자 만큼이나 다양하니까요. 그들에게 DIY (Do It Yourself) 할 수 있도록 해 준다면 메일 함에 쌓이는 다양한 수정 요구사항에서 조금은 해방될 수도 있습니다. (그렇다고 버그에서 해방되는 걸 의미하지는 않습니다.)

  •  미래는 API의 시대가 될 것이라고 예견하는 사람들이 있습니다. (당연히 저도 그런 사람 중 하나입니다.) 소프트웨어 교육이 보편화 되고 있기에 대부분의 사람들이 프로그래밍을 할 수 있게 되리라고 보기 때문이죠. 따라서, 머지않아 단순히 API를 활용해 앱을 짜는 것은 더 이상 신기한 일이 되지 않겠지만, 새로운 기능을 제공하는 API를 만들어서 제공하는 역할은 여전히 전문가의 영역에 남아 있을 것입니다.

쓰다 보니 길어져서 한줄 요약하면 다음과 같습니다.

JavaScript는 시장이 크고 유용성이 높은 개발 언어로 API를 설계할 줄 알면 전문가로 대접 받을 수 있다. - 웹 오브 띵크

 

동기 부여가 되는 이야기를 한건지 모르겠지만 혹여 여기까지 읽으셨다면 필자는 여러분들이 API를 설계하기로 충분히 세뇌(?) 되었다고 생각하고 두 번째인 Web IDL에 대해 설명드리겠습니다.


일단 Web IDL 에 관련된 문서가 있는 아래 링크를 한번 방문해 보시죠. 


http://www.w3.org/TR/WebIDL/


영문으로 된 문서는 무조건 번역기를 돌리다 보니 무슨 소린 줄 모르시겠다는 분들과 영어에 자신있으나 배경 지식이 없어 이해가 안가시는 분들을 위해 요약 (abstract)을 한글로 번역해 보았습니다.


이 문서는 Web IDL 이라는 인터페이스 정의 언어를 정의하는데 웹 브라우저에서 구현될 것을 의도한 인터페이스들을 설명하는데 사용될 수 있다. Web IDL은 웹 플랫폼에서 사용되는 공용 스크립트 오브젝트들의 동작을 보다 읽기 쉽도록 명세화하기 위한 수많은 기능들을 가진 IDL의 변종이다. Web IDL로 명시된 인터페이스들이 어떻게 ECMAScript 실행 환경 내에서 구성되는 지에 대해서도 상세화 되어 있다. 이 문서는 이미 배포된 스펙 문서를 어떻게 구현할지에 대한 가이드을 개발자에게 제공하고 새로 배포하는 스펙 문서의 인터페이스의 구현체가 서로 호환되도록 보장하기 위해 참조하기 위함이다. - W3C Web IDL Abstract


등장한 용어들에 대해 간단히 설명 드리면 다음과 같습니다. 또 의아한 용어가 있다면 댓글로 남겨주시기 바랍니다. :-)

  • "정의" - Justice가 아닌 Definition 입니다. 전 공대 출신이니까요. ;-)
  • "웹 브라우저", "웹 플랫폼" - 모두 여러분이 웹 세계를 탐험하는 도구(앱)를 의미하나 브라우저는 사용자 관점에서 플랫폼은 개발자 관점에서 바라보는 겁니다.
  • "오브젝트" - 한글로는 객체라고 번역되나 프로그램에서 기능을 수행하는 단위라고 이해하면 됩니다.
  • "ECMAScript" - 여기서는 JavaScript와 동의어라고 이해하면 됩니다.
  • "스펙" - 대학이나 취업 시 적어내는 이력서가 아니라 정의한 내용을 적은 규격서입니다. 
  • "구현체" - 코딩을 완료해서 동작하는 프로그램을 의미합니다. 


발췌한 요약문에서 빨간 색 밑줄친 부분은 주의 깊게 봐주셨으면 합니다. 제가 Web IDL로 JavaScript API를 설계하는 데 사용하는 이유를 말해주는 대목이니까요. 이 내용에 대해 이후 포스팅에서 좀 더 자세하게 설명드리겠지만 먼저 간략히 말씀드리면 JavaScript 언어의 유연성에 대한 부작용으로 인해 API를 구체적으로 명세하기 어려운 점이 발생합니다. 이렇게 API의 동작을 명확하게 알려주지 못한다면 사용하는 사람 뿐 만 아니라 API를 구현하는 사람에게도 혼란을 초래하기 때문에 구체적인 명세화 방식이 꼭 필요합니다.


게다가 이외에도 Web IDL를 사용하면 추가 이득을 얻을 수 있는데 그것이 바로 API 설계 검증 및 문서화에 대한 자동화입니다. 아래 링크의 widlproc이라는 도구를 통해 설계자는 설계의 유효성을 검증하고 손쉽게 문서를 생성할 수 있습니다. 이 방법 역시 추후 포스팅으로 설명드리겠습니다.


widlproc: https://github.com/dontcallmedom/widlproc  

 


이번 포스팅은 여기까지 입니다.


감사합니다.


올해 한국에서 최초로 열린 WWW 2014 컨퍼런스의 Web API 워크숍에 제출한 논문의 국문 초안을 공유합니다. 한국인에겐 역시 한국말이죠. ;-)


발표 슬라이드는 아래와 같습니다.


영문 논문은 아래 링크에서 확인하실 수 있습니다.


http://ws-rest.org/2014/program


웹 API로 사람과 사물 그리고 모든 것이 연결되는 세상을 꿈꿉니다.

  



클라이언트 개발 효율화를 위한 REST API에서 JavaScript API 로 설계하기

ABSTRACT

Web-centric era의 도래로 web 서비스와 융합(mashup)이 가능하도록 RESTful web service가 탑재된 다양한 embedded system가 등장하고 있다. 
RESTful web service는 기존 web service (WS-* technology)에 비해 가볍고 쉬우며 별도의 추가 protocol 없이 HTTP를 지원하는 모든 형태의 platform에서 사용할 수 있다는 장점이 있다. 대신 개발자는 친숙하지 않은 개념인 GET, PUT, POST, DELETE 같은 표준 HTTP method를 이용해 URI가 가리키는 리소스의 상태 전이(State Transfer)라는 형태로 action을 표현하는 REST API 개념을 이해해야만 한다. 이런 난이함 때문에 모든 도메인의 API 설계에 적용 가능한 완전히 (fully) RESTful API 설계 방법론이 존재하지 않기에 현재 system이 제공하는 REST API 불완전하고 추후 변경될 소지가 많다. 또한 RESTful web service 설계를 돕는 WADL, WSDL 2.0, RDF 와 같은 도메인 언어가 존재하고 이를 이용한 자동화 도구도 소개되고 있으나, 주로 API를 제공(expose)하는 server 개발 측면에 집중되어 있다. 반면에 mediocre client 개발자는 해당 서비스의 도메인 지식이나 RESTful web service 이해가 부족해 개발에 어려움을 겪고 있다. 본 논문에서는 client 개발자 입장에서 어려움을 해결하기 위해서 REST API를 역 분석(reverse engineering)하여 사용하기 쉬운 JavaScript API로 재설계하는 방법을 고안하였다. 이 방법에 따라 REST API 대신 JavaScript API를 도입하고 이를 구현한 wrapper를 client 개발자에게 제공한 사례를 소개한다. 이를 통해 얻을 수 있었던 client 개발 효율화 이득을 설명한다. Future work으로 추가 발생하는 wrapper 개발 비용을 낮추기 위해 stub code를 자동 생성하는 방안 및 server side JavaScript에서도 사용할 수 있는 방안을 연구하려고 한다.



1. INTRODUCTION

모든 기기가 인터넷을 통해 연결되어 상호 연동하는 시대를 맞이하여 embedded system에서 인터넷을 접속하여 정보를 이용하는(information consumer) 형태 뿐만 아니라 마치 웹 서버 처럼 정보를 제공하는 (information producer) 형태가 등장하기 시작했다. 클라우드 컴퓨팅 시대가 본격화 되면서 개인이 가진 단말기에서는 주로 입/출력 작업만 이뤄지고 클라우드라 불리는 가상의 공간에서 정보 분석 및 처리, 저장, 관리, 유통 등의 작업이 이뤄지는 형태가 보편화 될 것으로 보았으나, 반면 단말기 내에 각종 센서가 탑재되고 계산 능력 (computing power)이 비약적으로 향상되어 단말기 자체가 클라우드 공간의 서비스 제공자 (service provider)의 역할을 수행하는 형태도 등장한다. 이를 기반으로 인간의 개입 없이 혹은 최소한의 개입으로 기기 간 협력하여 센싱, 정보 처리 및 교환, 네트워킹 하면서 상호 지능적 관계를 형성하고 서비스화 되는 사물 공간 연결망인 Internet of Things1 (a.k.a. IoT) 가 새로운 시대의 페러다임으로 등장하고 있다.


device vendor는 embedded system에서 인터넷의 정보 제공자 역할을 수행하기 위해 RESTful architecture style2의 web server를 탑재하고 있다. SOAP, WSDL, UDDI 표준을 사용하는 기존 web service architecture 는 embedded system에 구현하기에는 무겁고 복잡하기 때문에, Hypertext Transfer Protocol (HTTP) 기반으로 REST API를 제공하는 웹 애플리케이션 서버(WAS)가 채택 되었다. REST API는 RESTful architecture style에 따라 Uniform Resource Identifiers (URI), HTTP 와 표준 media type (e.g. XML, JSON, ... )로 resource를 구분하고 HTTP method를 이용해 동작하도록 구현하여 제공하는 Application Programming Interface (API)이다. 이 API는 기존 web service와 다르게 서버와 클라이언트 간 낮은 결합도(loosely coupled)를 가지는 장점을 있어 2 tier 구조 (서버-클라이언트) 뿐 아니라 3 tier 구조 (서버 - facade 혹은 gateway - 클라이언트) 이상의 복잡한 architecture로 구성된 다양한 서비스를 생성할 수 있다.


embedded system가 제공하는 REST API를 통해 기존 기기와 차별화된 융복합 (convergence) 서비스를 만들 수 있다. 예를 들어, file storage 및 synchronization 역할을 수행하는 Infra as a Service (IaaS) 없이도 기기 간 seamless data sharing이 가능한 cloud service3 를 제공할 수 있으며, client의 OS/제품군에 상관 없이 연동 가능한 TV 중심의 N-Screen Service4를 제공할 수 있다. 또한, home gateway server로서 원격에서 댁내의 가전 기기를 제어하고 모니터링할 수 있는 서비스5도 가능하다. 반면 REST API는 client 개발자가 사용하기에 난해하고 API 변경에 대응하기 힘들다는 문제를 가지고 있다. REST API를 사용하기 위해서는 친숙하지 않은 개념인 GET, PUT, POST, DELETE 같은 표준 HTTP method를 이용해 URI가 가리키는 리소스의 상태 전이(State Transfer)라는 형태로 action을 표현하는 REST API 개념을 이해해야만 하기 때문이다.


본 논문은 client 개발자에게 REST API의 기능을 JavaScript API를 제공하기 위해 REST API에서 JavaScript API를 설계하는 방법과 이를 구현한 wrapper를 제공함으로써 client 개발 생산성을 높이는 방안을 제시하였다. 이를 위해 Section 2에서는 REST API 와 별도로 JavaScript API를 도입하게 된 배경을 소개하고 Section 3에서는 REST API에서 JavaScript API를 설계하는 방법(approach)를 소개한다. Section 4에서는 Section 3에서 소개한 접근법을 통해 실제 진행한 과제를 소개한다. 마지막으로 Section 5에서 결론과 함께 추후 진행 방향(future work)을 설명한다.

2. MOTIVATION

embedded system에서 REST API를 제공하기 위해 설계할 때 있어서 전형적인 (traditional) REST API보다 고려해야 하는 제약 사항이 많다. 첫번째 제약은 예상 응답 시간이다. 기존 REST API는 대부분 계산(computation)을 요하는 논리적인 서비스(logical service)에 관련된 기능을 제공하는 데 반면 embedded system의 REST API는 기기가 수행(action)하는 실제적인 서비스(physical service)에 관련된 것이기 때문이다. 전자는 분산 환경 하에서 요청을 분배하여 빠르게 처리될 수 있어 응답 시간(response time)이 짧으나 후자는 요청의 동작을 기기가 완료할 때까지 대기해야만 하기 때문에 상대적으로 긴 응답 시간을 고려해야 한다. 두번째는 느린 진화 속도와 호환성이다. embedded system의 제품 생명 주기 (product life-cycle)는 제품 별로 천차 만별이고 대부분 차기 제품에서야 기존 제품의 개선 기능이 포함된다. 또한 차기 제품에서 기존 제품과 호환될 수 있도록 확장성 있는 설계가 필요하지만 실제 그렇게 구현하기가 어렵다. 그 이유는 호환성을 위해 고려해야 할 사항이 많아지면 제품 개발 비용이 증가하기 때문이다.


진정한(truly) REST API는 설계하는 것은 어렵고 기기 간 표준화된 REST API를 도출하는 것은 더욱 어렵다. REST API를 설계하는 명확한 가이드(concrete guide)와 설계 방법론(process)이 없고 대신 설계 과정 중 검토해야 할 복잡한 design decision들만이 존재한다. 때문에 동일 제품군의 동일 기능에 대해 규격 별로 상이한 API가 등장하고 있으며 Table 1에서는 동일 기능에 대해 가전 (consumer electronics, CE) 기기가 제공하는 다양한 형태의 API를 통해 보여주고 있다.


Table 1. 동일 기능에 대한 규격 별 URI 설계 사례

FeatureA 규격B 규격C 규격
Retrieving device status/allctrl/status/GetDeviceInfo/sd
Manipulating power status/allctrl/power/ SendCommandToDevice/sd
Retrieving power status/allctrl/power/GetDeviceInfo/sd
Retrieving available commandsN.A./GetCommandListOfDevice/sd

REST API는 API를 제공하는 측면에서 보면 진화 가능성(evolvability) 와 유지 보수성(maintainability)의 장점을 갖지만 도리어 API를 사용하는 측면에서 보면 사용하기 힘들게 만들고 작성한 client 코드의 가독성(readability)이 떨어지는 단점을 지닌다. 특히 REST API가 가지는 parameters가 많거나 다양한 media type을 지원하는 등의 조건에 따라 가질 수 있는 payload 형태가 달라지도록 REST API를 설계한 경우에 그렇다.


API 사용성은 도메인(domain)의 이해를 돕는 문서(document) 수준에 달려 있다. 엄밀히 말해 API를 제공하는 device 혹은 플랫폼에 대해 기본 지식이 있어야 API를 사용할 수 있다. 이를 위해서는 이런 기본 지식을 잘 정리된 문서가 필요하다. 이 문서에는 API에서 사용된 용어에 대해 기반 지식(background knowledge)이 없는 개발자도 이해할 수 있는 수준으로 기술되어 있어야 한다.


REST API guide 문서의 유지 보수에는 비용이 많이 든다. WADL6, WSDL 2.07, RDF8 와 같은 모델링 언어를 통해 설계한 경우 API reference 문서를 생성해주는 자동화 도구가 존재하나 programming guide를 생성해주는 도구는 없다. 개발자는 programming guide에 따라 개발하다가 세부 내용(detail)을 확인하기 위해 API reference를 참조하기 때문에 programming guide이 완전성(completeness)과 무결성(integrity)을 가질 수 있도록 관리해야 한다. 현실은 API 추가, 변경, 삭제로 인해 문서에 대한 관리 비용이 늘어나게 된다. 특히 다양한 media type를 지원하는 REST API의 경우, client 개발 시 예외가 발생할 가능성이 많아지고 예외 처리를 위해 client 코드는 복잡해 진다.


신뢰할 만한 서비스를 제공하기 위해서는 다양한 client 상에서 API 테스트가 필요하다. REST API는 server-client간 결합도가 낮고 (loosely coupled) API를 호출하는 측(caller)의 platform에 독립적이다. 이런 이유는 다양한 형태의 API caller에서 다양한 REST API의 호출 조합으로 정상 동작 여부를 테스트해야 한다. REST API의 경우 또다른 server에서 호출할 수도 있고 client 에서 호출할 수도 있는데 호출하는 server-client를 개발하는 언어/플랫폼은 매우 다양하기 때문에 모든 경우의 수에 대해 검증하기 위해서는 많은 비용이 든다. 특히, REST API를 통해 resource를 생성하거나 변경, 삭제하는 등의 device 상태를 변경하는 동작을 수행하는 경우에는 호출하는 쪽(caller)이 이를 수행할 수 있는 권한이 있는 지 확인하는 보안 정책이 고려되어야 한다. 이런 보안 정책의 부재로 web 표준을 따르는 web browser에서는 REST API가 호출되지 않는 현상이 제품 출시 후 발견된 사례를 Section 4에서 소개하겠다.


Web platform이 탑재된 기기 간 interaction을 위해 서로 다른 형태의 (heterogeneous) API가 필요하다. 현재 web application에서는 내부 resource를 이용하기 위해 JavaScript API를 사용하고 외부의 resource를 이용할 때는 REST API를 사용한다. 다시 말해, client application는 REST API를 통해 메세지를 host application에게 전달하고 host application는 JavaScript API로 client와 메세지를 받게 되는 구조가 된다. 따라서, web application 간의 상호 작용을 위해서 JavaScript - REST API 쌍(pair)이 필요하다.

현대 client 개발자에게 친숙한 API의 형태는 JavaScript API이다. 전형적인 web application는 Model-View-Controller (MVC) 개념으로 보면 모델(Model) 역할의 HTML, View 역할의 CSS 그리고 HTML 혹은 CSS의 정보를 가공(manipulation)하거나 간단한 연산을 수행하는 Control 역할로써 JavaScript 로 구성되어 있다. Ajax9, DHTML10 기술이 도입되면서 JavaScript로 대화형(interactive) application을 구현할 수 있게 되었고, HTML 511를 통해 멀티미디어 컨텐츠를 쉽고 용이하게 사용 수 있게 되면서 JavaScript API가 주목을 받게 되었다. REST API를 web application내에서 호출하기 위해서는 Figure 1.과 같이 Ajax JavaScript API를 사용해야 한다.

var xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4 && xmlhttp.status==200) {
        console.log(xmlhttp.responseText);
    }
}
xmlhttp.open("GET", url, true);
xmlhttp.send();

Figure 1. JavaScript Ajax API로 REST API 호출 코드 예제



JavaScript는 높은 수준의 (high-level) 추상화된 함수를 설계할 수 있도록 제공한다. 이는 JavaScript가 객체 지향 언어이며 또한 함수형 프로그래밍을 지원하는 특성을 가지기 때문이다. client 개발자는 business logic을 구현하는 중간에 Ajax API를 이용해 REST API를 호출하여 구현할 수 있다. 하지만, JavaScript 언어의 장점을 이용해 생산성을 높이기 위해서 Figure 4 와 같은 함수를 설계해서 활용할 수 있다. Figure 2에서 주의 깊게 볼 부분은 ajaxCall 함수의 6번째 argument로 successCallback를 전달 받는데 이는 함수 형 인자라는 사실이다. JavaScript에서는 이처럼 함수를 전달받아 호출이 가능하기 때문에 함수형 언어처럼 재사용성이 높은 함수를 만들 수 있다.

var addr = "http://foo.com/";
function ajaxCall(method, uri, headers, queries, payload, successCallback, isAsync) {
    var httpObj = new XMLHttpRequest();
    var url = addr + uri + "?" + queries;

    for (var i = 0; i < headers.length ; i++) {
        httpObj.setRequestHeader("header", headers[i]);
    }
    httpObj.onreadystatechange = function() {
        if (httpObj.readyState == 4 && xmlhttp.status == 200) {
            successCallback(xmlhttp.responseText);
        }
    }
    httpObj.open(method, url, isAsync);
    httpObj.send(payload);
}

Figure 2. JavaScript Ajax API를 함수로 은닉화 (encapsulation)한 코드 예제


JavaScript 로 작성한 logic를 공유와 재사용이 가능하다. 앞서 소개한 Ajax 함수와 DHTML 를 통해 JavaScript file 자체를 web application에 가져와서 활용할 수 있다. web application 개발 생산성이 높은 이유는 jQuery12 과 같은 open source JavaScript library를 활용하기 때문이다.


3. APPROACH

객체 지향적으로 설계한 JavaScript API를 역 공학(reverse engineering)를 통해 REST API 대신 만들어 제공한다. JavaScript의 문법(syntax)의 대부분은 객체 지향 언어인 Java에서 차용했다. Java는 객체 지향 특성을 이용해 구현한 다양한 종류의 API를 제공하고 개발자들은 이 API를 사용함으로써 개발 생산성을 향상시킬 수 있다. 따라서 JavaScript에서도 객체 지향 API를 통해 생산성을 향상시킬 수 있다. Figure 2 에서 소개한 수준으로 REST API와 JavaScript API를 맵핑하는 수준으로 JavaScript API를 만들 수 있으나 이렇게 생성된 JavaScript API는 기존 REST API와 비교해 장점이 없다. 하지만 REST API가 제공하는 기능을 역 분석을 통해 객체지향 적으로 재 설계하면 사용하기 쉬운 API로 탈바꿈하게 된다. 이를 위한 일반화된 변환 매커니즘을 소개하면 다음과 같다.


1) REST API의 사용 사례를 도출한다. 대개 REST API의 programming guide에서 사용 사례에 따라 필요한 API와 예시를 설명하고 있기 때문에 이를 통해 손쉽게 알 수 있다.


2) 사용 사례에서 client에서 가져야 할 state와 state relationship을 도출한다. REST API는 server에서 client의 상태 정보를 유지하지 않도록 API를 설계하기 때문에 client에서 해당 상태 정보를 유지해야 한다. 이에 착안하여 client가 유지해야 하는 state와 그 상태로 변경되는 조건 (state transition condition)을 유추할 수 있다. 방법은 간단하다. 먼저 특정 URI에서 GET method가 제공되는 경우 해당 resource에 대한 state를 가져온다고 보고 이에 해당하는 적절한 state를 명명한다. 그리고 이 state의 attribute(s)가 다른 REST API의 POST, PUT, DELETE method에 참조되는 지를 확인하면 state 사이의 relationship을 도출할 수 있다. 또한 URI는 resource의 상하 구조(hierarchy)로 구성되기 때문에 상위 resource가 존재하는 상태에서만 하위 resource가 존재할 수 있다. 때문에 이런 관계도 state relationship에 나타낸다.


3) 도출된 states와 state transition에 대한 책임(responsibility)을 담당할 entity를 도입한다. 객체 지향 설계 원칙13에 따라 client에서 해당 state(s)를 관리할 interface를 도입하는 것이다. 이때, 하나의 interface가 의미상으로는 여러 states와 연관 관계를 가질 수 있으나 하나의 책임을 맡도록 하는 단일 책임 원칙(Single Responsibility Principle)을 따라 설계하는 것이 좋다.


4) 도입한 interface에 state transition(s)에 해당하는 method를 추가한다. REST API 설계 시 POST, PUT, DELETE method에 생성, 업데이트, 삭제의 의미를 부여하기 때문에 method 이름을 지을 때 참조한다.


5) method의 parameter를 설계할때 해당 REST API의 parameters를 구분하여 parameter 수가 많지 않도록 맵핑한다. REST API에서는 parameter를 HTTP의 URI 일부 resource(s) 이름으로, HTTP header의 parameter로, URI 다음에 ? 토큰을 붙여 사용하는 query parameter로, payload body에 다양하게 설정할 수 있다. Figure 3.의 Sample REST API request를 예로 들면, resValueHeader-Param1Header-Param2query1query2, JSON 형태의 media type을 같는 parameters로 bodyParam1, bodyParam2가 이에 해당된다. REST API 설계 원칙에 따르면 URI 혹은 header의 parameters로는 범용 목적의 attribute(s)를 지정하고 query에는 보조적인 (ancillary) 기능에 해당하는 attribute(s)를 그 외는 특정 기능에 필요한 parameter(s) 지정한다. 따라서, JavaScript API로 설계할 때 이런 특성을 이용해 entity의 attribute로 parameter를 mapping할 지 아니면 method의 argument로 mapping하는 게 나을 지를 판단할 수 있다. 가능하면 범용 attributes는 별도의 interface를 도입해서 재사용할 수 있도록 하고 보조적인 attributes는 method의 optional argument(s)로, 나머지는 method의 mandatory parameter(s)로 설계한다. method의 parameter 개수가 너무 많아지면 API 사용성이 떨어지기 때문에 유사한 정보를 지니는 attributes는 묶어서 interface로 설계한 뒤 argument로 전달되도록 한다.

PUT http://127.0.0.1/ws/boo/resValue?query1=value1&query2=value2    HTTP/1.1

Accept: application/json
Accept-Language: ko-KR
Header-Param1 : headerValue1
Header-Param2 : headerValue2

{
    bodyParam1 : "bodyValue1",
    bodyParam2 : "bodyValue2" 
}

Figure 3. REST API에서의 parameters 예

[Constructor (DOMSting id)]
interface Boo {
    attribute DOMString id;           // originated from resValue
    attribute DOMString headerParam1; // originated from Header-Param1
    attribute DOMString headerParam2; // originated from Header-Param2

    void update(BodyParam params,            // grouping payload JSON data as BodyParam interface
                optional DOMString query1,   // orginated from query1 parameter
                optional DOMString query2);  // originated from query2 parameter
};

[NoInterfaceObject]
interface BodyParam {
    attribute DOMString param1; // originated from bodyParam1
    attribute DOMString param2; // originated from bodyParam2
};


Figure 4. Web IDL로 Figure 3의 REST API를 JavaScript API로 설계한 사례


구조 설계가 완료되면 JavaScript API를 모델링 언어로 상세 설계한다. Figure 4는 Figure 3의 REST API를 Web IDL14 언어로 설계한 사례이다. JavaScript API 설계 및 구현의 일관성을 돕기 위해 Web IDL을 존재하는데 이를 활용하면 구현 및 문서화에 도움을 받을 수 있다. 예를 들면 Web IDL에서 XML를 생성하는 open source tool인 widlproc15를 통해 모델링의 완결성(completeness) 검증이 가능하고 이를 확장하면 stub code 및 API 문서 자동 생성 등이 가능하기 때문에 이를 사용하는 것을 추천한다.


JavaScript API를 REST API와 mapping해 주는 wrapper를 구현한다. wrapper는 JavaScript API를 제공하는 web platform 종류에 따라 다양한 개발 언어로 구현될 수 있으며 JavaScript 언어로 구현할 경우 다양한 web platform에서 수정없이 재사용할 수 있다. JavaScript로 wrapper를 구현할 경우, Figure 4의 Ajax 함수를 사용한다.

개념 증명(proof of concept)를 통해 API 사용성을 검증하고 개선사항을 도출해 다음 설계에 반영한다. API 설계를 평가하는 최선의 방법은 설계한 API를 사용해서 직접 web application을 개발해 보는 것이다. 특히 기존 REST API가 제공하는 모든 기능을 한번에 JavaScript API로 설계하기보다 일부를 빠르게 설계하고 해당 기능을 구현해 보는 점진적인(iterative) 방법이 좋다. 좋은 설계 패턴을 찾아 다음 설계에 기준(criteria)으로 삼아 빠르게 API 사용성을 향상 시킬 수 있기 때문이다.


4. CASE STUDY

이번 case study에서는 Smart TV가 제공하는 convergence app API 중 client API를 REST API에서 JavaScript API로 변환해서 host-client 모두 JavaScript API로 제공한 사례를 통해 앞서 소개한 approach의 유용성을 다룬다.


TV Convergence App API (a.k.a N-Service)는 web platform를 탑재한 Smart TV 기기와 주변의(nearby) 모바일 기기가 연동하여 N-Screen 서비스를 개발할 수 있도록 애플리케이션 간의 통신 (inter application communication) 인터페이스를 제공하는 API이다. 


기존에는 Smart TV 기기의 application에서는 host 기능에 대한 JavaScript API를 제공하고 모바일 기기는 TV가 제공하는 REST API를 사용해서 client 기능의 application을 구현하도록 제공하고 있었으나 이번 case study를 통해 Figure 5와 같이 client에서도 host처럼 JavaScript API를 사용하여 application을 구현할 수 있도록 API와 wrapper를 개발자에게 제공하였다. 이 결과물은 Android16, Tizen17 OS에서 사용할 수 있는 API로써 samsung developer site18를 통해 공개되었다.




Figure 5. N-Service API의 system architecture


앞서 Section 3에서 소개한 접근 방식에 따라 첫번째, REST API의 기능을 파악하기 위해 API guide 문서에서 API 사용 사례를 정리한다. 일반적으로 API를 외부에 공개할 때 사용법을 가이드해주는 문서를 함께 공개한다. 이 문서는 API를 사용해서 개발자가 구현할 수 있는 기능을 설명하고 있는데 [19] 에서 client를 위한 REST API를 확인할 수 있다. 편의 상 Table 2.는 REST API가 제공하는 기능에 대한 사용 사례를 요약한 것이고 여기서 {appId}, {deviceId}, {groupId}는 각각 host application의 ID, host에 접속되어 있는 (connected) client device의 ID, 생성한 group 의 ID를 의미한다.


Table 2. 발췌한 client 를 위한 REST API list

HTTP methodURIUse case
POST/ws/apps/{appId}/connectconnect to host application
POST/ws/apps/{appId}/disconnectdisconnect with host application
GET/ws/apps/{appId}/infoget host application information
POST/ws/apps/{appId}/queuepush a message to host application or upload a file
GET/ws/apps/{appId}/queue/devices/{deviceId}pop a message of a specific client device from host application
POST/ws/apps/{appId}/queue/devices/{deviceId}push a message to a specific client device
POST/ws/apps/{appId}/queue/groups/{groupId}push a message to a specific group
GET/ws/apps/{appId}/queue/groups/{groupId}retrieve a group members
POST/ws/apps/{appId}/queue/groups/{groupId}/joinjoin a group
POST/ws/apps/{appId}/queue/groups/{groupId}/leaveleave a group

두번째 단계인 사용사례에서 client에서 가져야 할 state와 state relationship는 도출하면 Figure 5와 같다. 앞서 설명한 방법대로 Table 2의 use case list 중 GET method를 통해 도출한 state를 host applicationmessage queuegroup 으로 정의하고 각각의 state로 전이(transition)하기 위한 REST API를 맵핑하였다. 특히, URI hierarchy 상으로 {appId} 아래 queue, groups 가 존재하기 때문에 이점을 state diagram의 transition에 반영하였다.




Figure 6. State diagram from use cases


세번째, state와 state transition에 대한 responsibility를 담당할 entry를 도입한다. 앞서 소개한 바와 같이 host application을 위한 JavaScript API를 [20]에서 제공하고 있고 Table3.은 이를 발췌한 것이다. 따라서, 가능한 유사한 기능을 담당하는 신규 entity가 등장하지 않도록 기존 API에 등장하는 entity를 활용하도록 한다. 각 entity가 가져야 할 책임을 고려했을 때 host application를 담당할 NServiceHost interface를 새로 도입하기로 판단하고 message queue와 group은 기존 interface인 NServiceManager, NServiceDeviceGroup, NServiceDevice이 지니는 책임에 맞춰 기능을 나누기로 결정했다.


Table 3. host를 위한 JavaScript API list

InterfaceConstant/Attribute/MethodUse Case
NServiceDeviceManagervoid getNServiceDevices (successCallback, errorCallback)host와 연결된 (connected) client device 객체 (NServiceDevice)를 가져옴
NServiceDeviceManagervoid registerManagerCallback (callbackFn(ManagerEvent))client connection, disconnection 등의 이벤트를 수신하는 callback을 등록함
NServiceDeviceManagerNumber broadcastMessage (DOMString message)host와 연결된 모든 client 에게 메세지를 전달
NServiceDeviceManagerNumber multicastMessage (DOMString groupID, DOMString messasge)특정 group에게 메세지를 전달
NServiceDeviceDOMString getUniqueID ()특정 client의 고유 ID를 확인
NServiceDeviceDOMString getDeviceID ()특정 client의 device ID를 확인
NServiceDeviceDOMString getName ()특정 client의 이름을 확인
NServiceDeviceNumber getType ()특정 client의 유형 확인
NServiceDeviceNumber sendMessage (DOMString message)특정 client에게 메세지를 전달
NServiceDevicevoid registerDeviceCallback ( callbackFn(NServiceDeviceEventInfo) )특정 client가 전달하는 메세지를 host에서 수신하는 callback을 등록함
NServiceDevicevoid unregisterDeviceCallback ()특정 client에서 전달하는 메세지 수신을 해제함
ManagerEventNumber eventTypeconnection 혹은 disconnection 이벤트 유형을 확인
ManagerEventDOMString deviceName이벤트를 발생시킨 client 이름
ManagerEventDOMString uniqueID이벤트를 발생시킨 client의 고유 ID
ManagerEventNumber deviceType이벤트를 발생시킨 client 유형
NServiceDeviceEventInfoNumber eventType특정 client의 메세지 전송, 그룹 가입 탈퇴 이벤트 유형을 확인
NServiceDeviceEventInfoNServiceDeviceMessageInfo eventData or NServiceDeviceGroupInfo eventData메세지 내용
NServiceDeviceGroupInfoDOMString groupName특정 client가 가입 혹은 탈퇴한 그룹 이름
NServiceDeviceMessageInfoDOMString messageclient가 발송한 메세지
NServiceDeviceMessageInfoDOMString contextclient가 발송한 컨텍스트


네번째로 state transition에 해당하는 method를 설계하는데 있어 Figure 6에서 나타난 connect, disconnect, info, queue, join, leave를 JavaScript의 메소드로 설계하면 된다. 여기서 REST API는 ajax로 전송되기 때문에 응답 시간을 고려하면 되도록 비동기 함수로 설계하는 것이 좋다. 비동기 함수로 설계하기로 결정할 경우, 동작의 결과가 성공적이였을 때 호출될 success callback 과 실패했을 때에 호출될 error callback이 parameter로 추가되어야 한다.


마지막으로 REST API 특성 상 참조하는 parameters가 많기 때문에 범용 parameters나 유사한 의미를 지닌 parameters에 대해 새로운 인터페이스 안에 은닉화(encapsulation)함으로써 parameter 수를 줄인다. 예를 들어 host application과 connection를 하는 REST API의 예시는 Figure 7과 같다. 여기에 등장하는 parameters 중 application Id인 sampleWidget, header parameters인 SLDeviceID, VendorID, DeviceName, ProductID는 모두 공용 변수라고 판단하고 connection을 위한 메소드 뿐 아니라 다른 목적으로 사용할 수 있도록 별도의 인터페이스로 설계 한다. parameter가 가진 의미에 따라 해당 인터페이스를 도입한다. application Id는 host application에 관한 정보이고 SLDeviceID, VendorID, DeviceName, ProductID는 client에 관한 정보이기 때문에 전자는 NServiceHostInfo 인터페이스, 후자는 NServiceOwnDeviceInfo 인터페이스의 attribute로 설계한다. NServiceOwnDeviceInfo 인터페이스를 위한 get /set 함수를 도입해서 connection 하는 method를 호출하기 이전에 설정하도록 설계하고 연결에 필요한 추가 정보인 host server의 IP address와 port number를 NServiceHostInfo 인터페이스에 추가해서 connection method에 전달하고 응답 시간을 고려해서 비동기로 수행되도록 한다. 이렇게 설계한 JavaScript API를 사용하는 예시는 Figure 8과 같다.

POST /ws/app/sampleWidget/connect     HTTP/1.1

SLDeviceID: 12345
VendorID: VenderMe
DeviceName: IE-Client
ProductID: SMARTDev

Figure 7. N-Service REST API 중 Connect to Application에서 발췌

var ownInfo = new webapis.NServiceOwnDeviceInfo({
    deviceID : 12345, 
    deviceName : "IE-Client", 
    productID : "SMARTDev" 
});

webapis.nservice.setOwnDeviceInfo(ownInfo);

var hostInfo = new webapis.NServiceHostInfo({
   ipAddress : "162.168.1.2", // assume that it is the host server's address
   appID : "sampleWidget" 
}); 

webapis.nservice.connectToHost(hostInfo,
    function (hostObj) {
       console.log("successfully connected to host");
    },
    function (err) {
       console.log("fail to connect to host");
});

Figure 8. Figure 7의 REST API를 JavaScript API로 설계했을 때의 예시


Figure 9 는 REST API를 변환해서 client API로 만들고 기존의 host API와 융화(harmonize)되도록 설계한 ER-diagram이다. common I/F라고 표시한 부분은 host application에 존재하는 interface를 재사용 혹은 확장한 것을 의미하고 client specific I/F로 표시한 부분은 client API를 위해 추가한 interface를 의미한다. 이로써 common API라고 표시된 부분처럼 host의 JavaScript API에서 제공하던 method의 일부를 client API에서도 지원할 수 있게 되어 client-host 구분 없는 한 종의 API로 거듭하게 되었다.




Figure 9. N-Service API 의 overall entry-relation diagram


sequence diagram를 통해, API 사용 흐름(call flow)과 API를 구현함에 있어 논리적인 문제가 있는지 확인한다. Figure 10는 Figure 7의 REST API를 Figure 8처럼 JavaScript API를 통해 호출하는 과정을 표현하고 있다. 이런 과정을 통해 명확한 API 설계가 가능하고 구현 상에서 발생할 가능성이 있는 설계 오류를 줄일 수 있다.




Figure 10. N-Service 의 host-client connection를 위한 API sequence flow


구조 설계 (high level design) 과정이 끝났으면 모델링 언어로 상세 설계(detailed design)한다. 여기서는 Web IDL를 이용하여 설계하였고 Figure 11은 Figure 9, Figure 10를 토대로 Web IDL 문법으로 정의한 사례이다. 이 모델링 언어로 상세 설계하면 타입이 없는 언어(untyped language)인 JavaScript에서는 표시할 수 없는 argument type를 명시할 수 있어 API를 구현하거나 사용할 때 있어서 이해를 도울 수 있으며 자동화 도구를 통해 API의 완결성 검증이나 API 문서를 자동 생성 할 수 있다는 장점을 취할 수 있다. Web IDL로 설계하여 생성한 문서는 [21] 에서 확인할 수 있다.

[Constructor (NServiceOwnDeviceInfoInit init)]
interface NServiceOwnDeviceInfo {

    attribute DOMString deviceID;
    attribute DOMString vendorID;
    attribute DOMString deviceName;
    attribute DOMString productID;
};

[Constructor (NServiceHostInfoInit init)]
interface NServiceHostInfo {

    attribute DOMString ipAddress;
    attribute unsigned short portNumber;
    attribute DOMString appID;

    readonly attribute DOMString? version;
    readonly attribute DOMString? appName;
    readonly attribute NServiceHostStatus status;
};

[NoInterfaceObject] interface NServiceManager {

    boolean setOwnDeviceInfo(NServiceOwnDeviceInfo info);

    NServiceOwnDeviceInfo? getOwnDeviceInfo();

    void connectNServiceHost(NServiceHostInfo hostInfo, 
        NServiceHostConnectSuccessCallback onsuccess,
        optional ErrorCallback? onerror);
};

Figure 11. Web IDL 언어로 설계한 Connect to Application 관련 API


상세 설계와 같이 API를 제공할 wrapper를 구현한다. 변환 메커니즘의 산출물을 이용하면 REST API와 맵핑되는 JavaScript API의 logic를 구현하는 것은 어렵지 않다. 단, API를 구현할 플랫폼을 결정하고 이에 따라 발생할 수 있는 web application의 보안 정책에 대한 이해는 선행되어야 한다. 첫번째, JavaScript API를 제공하는 주체는 다양하다. 기본적으로 web browser부터 android native application에서 web application를 수행하는 webview22, 설치 형(installable) web application를 지원하는 Tizen, Firefox OS 같은 web platform등이 존재한다. API를 제공할 client 플랫폼을 무엇으로 할 지에 따라 API를 구현하는 언어가 달라질 수 있다.


개념 증명(Proof of concept)을 통해 시스템 제약 사항을 도출하고 개선한다. wrapper를 이용하는 sample client application을 개발해 수행해 본 결과 ajax를 이용해 wrapper를 구현하면 웹 보안 정책으로 인해 다음과 문제가 발생함을 알 수 있었다. cross-site scripting 이라고 알려진 보안 취약점 (security vulnerability)를 막기 위해 web application에서 수행하는 script는 모두 같은 도메인 주소에서 제공되어야 하는데 REST API는 REST API를 호출하는 wrapper와 같은 도메인에 존재할 수 없기 때문에 Ajax로 REST API 호출이 이뤄지지 않는 현상이 발생한 것이다. 이를 해결하기 위해 REST API를 제공하는 web server에서 W3C 표준 Cross Origin Resource Sharing (CORS)을 위해 [23]를 구현하도록 했다.


JavaScript API를 제공함으로써 얻는 client 개발 생산성 및 효과를 검토한다. 동일한 application에 대해 REST API를 사용해서 개발한 경우와 JavaScript API를 사용해서 개발한 경우와의 비교가 이뤄지지는 않았으나 client 개발자에게 JavaScript API를 통해 손쉽게 TV와 mobile 기기 간 convergence 기능을 만들 수 있도록 제공함으로써 이전에 비해 다양한 web application을 개발할 수 있음을 확인했다. 이 application들을 [24], [25], [26], [27], [28]에서 다운로드 받아 확인해 볼 수 있다.


마지막으로 case study를 통해 Section 3의 유효성을 평가하기 위해 Section 2에서 제기한 문제점들이 해결되었는지를 되짚어 보자. 첫째, 개념 증명 과정에서 발견한 REST API에 대한 제약사항을 반영해 제공함으로써 효과적으로 대처할 수 있었다. 둘째, 도출된 JavaScript API는 잘 알려진 객체 지향 설계 원칙에 따라 설계되어 client 개발자가 사용하기 편리했다. 셋째, JavaScript API를 사용한 client 코드는 REST API를 직접 호출한 것보다 가독성이 좋았다. 넷째, JavaScript API의 reference 문서로도 REST API의 programming guide 수준의 정보를 제공할 수 있다. client에서 가능한 동작(action)으로 API가 재설계되었기 때문이다. 마지막으로 비용 대비 효과적인 테스트가 가능했다. 기존의 REST API의 경우 테스트를 마치고 출시되었음에도 불구하고 web browser에서는 호출이 안되는 현상이 발생했는데 이번 case study를 통해 설계한 JavaScript API를 테스트 하면서 발견할 수 있었기 때문이다.

5. CONCLUSION AND FUTURE WORK

본 논문에서는 embedded system에서 제공하는 REST API가 client 개발의 입장에서는 사용하기 어렵다는 점에 주목했다. 따라서, client 개발 생산성을 향상시키고 추후 REST API 변경에도 적절히 대응할 수 있는 방안으로 REST API 대신 JavaScript API를 제공하는 방안을 제시했다. REST API의 사용 사례 분석과 client가 state를 가지는 REST API의 특성에서 부터 도출한 state diagram에서 객체 지향 방법론을 통해 사용성이 높은 JavaScript API를 설계하는 변환 메커니즘을 설명하고 JavaScript API 모델링 언어인 Web IDL로 상세 설계를 진행함으로써 설계 완결성 검증 및 자동 문서화 도구의 도움을 받을 수 있었다. 실제 수행한 case study를 통해 REST API 대신 JavaScript API를 제공하기 위해 발생하는 추가 개발 비용과 JavaScript API를 통한 client 개발 측면의 생산성 이득을 보여 주었다. 

REST API를 fully RESTful 하게 설계하기 위한 방법론은 아직 정립되지 않았고 특히 resource 제약이 있는 embedded system의 web server에서 제대로 구현하기도 어렵다. 따라서, 사물 간 통신, 즉 사물 인터넷(IoT)를 enabling 하는 기술로써 evolvability, maintainability, self-descriptiveness 등을 만족시키는 REST API가 device에 탑재되기까지는 더 많은 시간이 필요하리라 보인다. 

따라서, REST API를 client 개발자에게 공개하는 대신에 JavaScript API 형태로 포장(wrapping) 해서 제공하면 추후 embedded system의 RESTful API가 변경되거나 다른 모델로 변경되었을 때라도 손쉽게 client 유지 보수가 할 수 있으리라 기대한다.

future work으로 case study를 통해 얻은 API wrapper 개발 경험을 토대로 우리는 Web IDL로 설계한 문서에 REST API에 대한 정보를 추가(annotation)한 후 widlproc 기반으로 해당 stub 코드를 생성하는 도구를 개발하고자 한다. 또한 REST API 변경 및 삭제로 인해 JavaScript에서 발생하는 오류를 수집하는 logic를 추가해서 API wrapper의 수정이 이뤄질 수 있도록 하겠다. 마지막으로 API wrapper를 client 뿐 아니라 Node.JS28와 같은 server에서도 사용할 수 있도록 제공하여 IoT에서도 활용할 수 있는 방안을 연구하겠다.


6. ACKNOWLEDGEMENTS

We would like to thank the Samsung Web API team members for the case studies which they had been conducted together.



7. REFERENCES

[1] 사물 인터넷 기술, 서비스 그리고 정책, 조철회 외 www.nipa.kr
[2] Fielding RT (2000), Architectural styles and the design of network-based software architectures, Ph.D. Thesis, University of California. Irvine, USA
[3] Samsung Link. http://link.samsung.com/
[4] Smart TV Convergence App API. http://www.samsungdforum.com/Guide/ref00003/index.html
[5] Samsung Home Sync. http://developer.samsung.com/homesync
[6] M. Hadley. Web application description language (WADL). Techincal report, W3C, August 2009. http://www.w3.org/Submission/wadl/ 
[7] R. Chinnici, J. Moreau, A. Ryman, and S. Weerawarana. Web Service Description Language (WSDL) Version 2.0 Part 1 : Core Language. Technical report, W3C, June 2007. http://www.w3.org/TR/wsdl20
[8] Resource Description Framework. http://www.w3.org/RDF/
[9] Asynchronous JavaScript and XML http://en.wikipedia.org/wiki/Ajax_(programming)
[10] Dynamic HTML. http://www.w3.org/Style/#dynamic
[11] HTML5 http://www.w3.org/TR/html5/
[12] jQuery http://jquery.com/
[13] Robert C. Martin, Principles Of Object Oriented Desigin. http://c2.com/cgi/wiki?PrinciplesOfObjectOrientedDesign
[14] Cameron McCormack. Web Interface Definition Language. http://www.w3.org/TR/WebIDL/
[15] Tim Renouf, Paddy Byers, et al. widlproc https://github.com/dontcallmedom/widlproc
[16] Google. Android http://www.android.com/
[17] Linux Foundation. Tizen. http://tizen.org
[18] Samsung web API on developer site. http://developer.samsung.com/samsung-web-api
[19] Client (HHP) to TV Application Communication on samsung d forum site.http://www.samsungdforum.com/Guide/ref00003/convergence_app_clienttotvappcomm.html
[20] NService on samsung d forum site. http://www.samsungdforum.com/Guide/ref00008/nservice/dtv_nservice.html
[21] NService API Reference on Samsung developer site. http://img-developer.samsung.com/onlinedocs/samsung_webapi_guide_public_1.0/html/index.html
[22] WebView reference on Android Developers. http://developer.android.com/reference/android/webkit/WebView.html
[23] Anne van Kesteren. Cross-Origin Resource Sharing. http://www.w3.org/TR/cors/
[24] Colapicto http://developer.samsung.com/samsung-web-api/samples/Colapicto
[25] Draw Together http://developer.samsung.com/samsung-web-api/samples/Draw-Together
[26] Family Album http://developer.samsung.com/samsung-web-api/samples/Family-album
[27] YouTube Continue Play http://developer.samsung.com/samsung-web-api/samples/Continuous-Youtube-play
[28] Puzzle Game http://developer.samsung.com/samsung-web-api/samples/Puzzle-game
[28] Joyent.Inc. node.js. http://nodejs.org/

안녕하세요.


웹 API과 IoT의 만남을 주제로 블로깅을 시작한지도 꽤나 시간이 흐른것 같네요.

사실 의도하지는 않았지만 연구를 진행해가면 갈수록 더욱 재밌는 것들을 발견하게 되어 너무나 기쁩니다. 

이번 포스팅에서 소개하고자 하는 내용은 node.js에서 Arduino를 제어하는 API를 제공하는 duino입니다.  (여담입니다만 이 글을 Internet of Things 목록에 넣어야 할지 아니면 JavaScript API 목록에 넣어야 할지 한참 고민했네요. ;-) )


https://github.com/ecto/duino


자세한 소개가 링크에 나와 있기는 하지만 아래와 같이 한글로 번역해서 정리하니 참고하시기 바랍니다.

  1. 설치



  2. npm install duino
    

      상기 git hub의 ./src/du.ino 코드를 scatch 도구를 이용해 arduino에 flash

  3. LED 점멸 코드 예제

  4. var arduino = require('duino'),
        board = new arduino.Board({
         device: 'USB0'
    });
    
    var led = new arduino.Led({
      board: board,
      pin: 13
    });
    
    led.blink();
    
      라즈베리파이와 아두이노 연결 시 /dev/ttyUSB0를 통해 serial 통신하게 되어 device attribute에 USB0라고 입력
      MS Windows / MAC 계열에서는 아래 파일로 node_modules/duino/lib/board.js를 대체한 뒤 device 명으로 시리얼 포트가 연결된 COM포트 명 입력 ex) COM4

    board.js




  5. API Signatures (using Web IDL)

  6. // duino uses arduino namespace by default.
    
    enum EmitterType { "data", "connected", "ready" };
    enum Mode {"in", "out"};
    
    dictionary BoardOption {
        DOMString device;
        boolean debug;
    };
    
    dictionary LEDOption {
        Board board;
        DOMString pin = '13';
    };
    
    callback BoardOnCallback = void (optional DOMString msg);
    callback BoardSerialCallback = void (DOMString msg);
    
    [Constructor, Constructor (BoardOption option)]
    interface Board {
    
        const DOMString LOW = '000';
        const DOMString HIGH = '255';  
        void on(EmitterType type, BoardOnCallback cb); 
        void serial(BoardSerialCallback cb);
        void write(DOMString msg);
        void pinMode(DOMString  pin, Mode mode);
        void digitalRead(DOMString pin);
        void digitalWrite(DOMString pin, DOMString val);
        void analogRead(DOMString pin);
        void analogWrite(DOMString pin,DOMString val);
        void delay(unsigned short ms);    
    };
    
    [Constructor, Constructor (LEDOption option)]
    interface LED {
    
        void on();
        void off();
        void blink(unsigned short interval = 1000);
        void fade(unsigned short interval = 2000);
        unsigned short bright();
    };
    
    interface LCD {
      // TODO: See the details at the original link
    };
    
    interface Piezo {
      // TODO: See the details at the original link
    };
    
    interface Button {
      // TODO: See the details at the original link
    };
    
    interface Ping {
      // TODO: See the details at the original link
    };
    
    interface Servo {
      // TODO: See the details at the original link
    };
    
    

    Web IDL에 관한 문법은 http://www.w3.org/TR/WebIDL/ 를 참고하시기 바랍니다.


    API를 사용하는데 도움이 되시기 바랍니다.

    감사합니다. ;-)


+ Recent posts