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


2월 9일(월) 15:00~18:00 강남역 근처 강남토즈 타워점에서 개최한 제 34차 W3C 대한민국 관심 그룹 회의 참석 후기를 공유하고자 이렇게 포스팅합니다.

http://onoffmix.com/event/41031 


이번 회의에서는 KIG를 이끌고 있는 이원석 박사님의 주관으로 W3C 표준화가 한창인 service worker, ETRI의 hybrid web platform인 HyWAI 소개 및 이를 이용한 web bluetooth app 개발 사례에 대해 다뤘습니다. 계획 상으로는 W3C CEO인 Jeff Jaffe가 참석할 예정이었으나 급작스런 부친 상으로 모든 일정을 취소하고 귀국한 관계로 대면할 기회를 갖지 못해 아쉬웠습니다. 각 세션에 앞서 이원석 박사님은 올 해 KIG 정기 회의를 통해 HTML5 최신 기술 동향 및 hybrid app 개발 사례에 포커스할 예정이며 공개로 진행되는 만큼 많은 참석과 의견 교류를 바란다고 말씀하셨습니다.



첫 번째 세션으로 service worker spec의 editor이자 삼성전자 책임 연구원인 송정기 님이 발표한 사례를 들어 알기 쉽게 정리한 service worker 입니다. 
발표자에 따르면 service worker API를 통해 개발 가능한 기능은 크게 오프라인 우선 지원(offline first)백그라운드 수행(background processing)을 통한 푸시 알림(push notification) 두 가지 입니다.
오프라인 우선 지원의 경우, service worker의 API와 개발자의 필요에 따라 캐쉬(cache)를 생성할 수 있는 전역 cache API 및 WHATWG에서 표준화 중인 fetch event를 처리하는 API를 통해 HTTP 요청(request)의 프록시(proxy) 역할을 수행하는 service worker를 구현할 수 있습니다. 이는 URL을 입력하거나 링크를 클릭하는 요청(client request)이나 AJAX 요청 혹은 내부 리소스 요청(resource request) 등을 대신하는 proxy를 service worker로 등록함으로써 offline 환경에서 캐쉬 데이터로 브라우징이 가능하도록 한 것으로 이에 대한 자세한 API 사용 사례(usage)는 spec. editor의 한 명이자 구글의 엔지니어인 jake의 블로그를 읽어 보기를 바랍니다. HTML5Rocks에 도창욱 님이 번역한 글도 참고하면 좋습니다.현재 구현이 상당 부분 진행되어 Chrome 40 버전 이상 혹은 Firefox의 nightly version에서 약간의 환경 설정을 통해 테스트 가능합니다. 마지막으로 service worker에 대한 spec 진행 사항에 대한 알고 싶은 분들은 트위터에서 @jungkee 를 follow 하고 spec 자체를 github를 통해 작성하고 있으니 자유롭게 의견을 개진할 수 있습니다.
시간 관계 상 백그라운드 수행을 통한 푸시 알림 기능은 다루지 못한 관계로 추후 KIG에서 이 부분에 대해 다룰 예정이라고 합니다.

두 번째 세션은 Hybrid Web Application Interface를 줄인 HyWAI (하이웨이)에 대해 ETRI 전종홍 책임 연구원 님이 발표했습니다. 이 프로젝트는 PhoneGap 이 발표된 시점인 2009년 1.0을 시작으로 현재 3.5 버전에 이르렀으며 3.5 버전의 경우 re-engineering를 통한 구조 개선 및 BT/BLE를 포괄하는 Web Bluetooth API가 주안점이라고 합니다. 
ETRI 정책 상 open source로 진행할 수 없어 간략한 sample app을 mobile2.tistory.com/8 에 공개했으니 다운로드 받아 써 볼 수 있습니다.


세 번째 세션은 HyWAI 프로젝트를 수행한 엠트리 소프트의 신현진 이사님의 HyWAI를 통한 Web Bluetooth app 개발 사례 소개입니다. hybrid app 개발 사례 소개에 앞서 hybrid app 개발 시 경험한 이슈 사항들을 공유해 주셨는데 그중 cross platform 지원 시 web view 호환성 및 파편화 문제(fragmentation issue)가 심도 있게 다뤄졌습니다. 특히   Crosswalk, wkwebview (iOS) 프로젝트는 지켜 볼만한 오픈소스 프로젝트라고 합니다. 데모 앱으로 구글이 주창한 BLE 기반의 physical web 사용 사례 (use case)를 hybrid app으로 구현해 bluetooth로 전송한 URL를 실행하는 과정을 코드와 함께 시연했습니다.


이상입니다.

이후 소개된 발표 자료가 공유되면 업데이트하도록 하겠습니다.



감사합니다.


 

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


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


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


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


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

;-)


감사합니다.



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


이번 포스팅은 요즘 잘 나가는 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 연동 등의 우수한 기능을 제공하기 때문에 이번 기회에 익혀 두시면 좋을 듯 합니다.


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

  1. 생각의 웹 WebofThink 2014.09.25 17:03 신고

    소스 코드를 아래 공유합니다: https://github.com/hyunghunny/HTML5BirthdayCard

안녕하세요.

주요 시장 조사 기관인 VisionMobile에서 발행한 앱 개발 시장 조사 보고서를 다음과 같이 포스팅합니다.

 내용의 요약은 다음 링크에서 확인하실 수 있습니다: http://www.visionmobile.com/blog/2014/02/developer-economics-q1-2014/

 

여기서 부터는 제가 번역한 key message 요약입니다.

  • 앱 시장 및 개발자 현황
    • 글로벌 앱 시장은 2013년에 680억 불 규모로 2016년까지 1430억 불 규모로 성장할 것으로 예상됨
    • 2013년 기준 전 세계에 230만 명의 모바일 개발자가 존재하며 이중 76만 명이 아시아에 거주

  • 생태계 전쟁
    • Android와 iOS가 스마트폰 시장의 94% 차지 (2014년 4분기 기준)
    • 전체 개발자의 71%가 Android 대상 앱 개발을 고려 함
    • 전체 개발자의 55%가 iOS 대상 앱 개발을 고려하고 있으며 주로 구주와 북미의 개발자들이 선호함.
      • 반면, 아시아, 남미, 아프리카의 개발자들은 HTML5를 iOS보다 더 선호함
    • 기타 플랫폼 (파이어폭스, 윈도우즈 폰, 윈도우즈8, 타이젠 등)에 개발자를 유치하려면 전체 개발자의 69%에게 iOS / Android를 버리고 기타 플랫폼을 사용해야 하는 동기를 부여해야 함
    • HTML5는 다양한 플랫폼에서 동작하는 가능한 앱를 만들 수 있는 플랫폼이자 native app을 만들 수 있는 기술 임
      • 37%의 개발자가 HTML5를 사용 (모바일 웹 사이트, 웹 앱 등)
      • 이중 15%의 개발자는 hybrid app이나 HTML5 to native tool를 사용
    • 평균적으로 한 개발자는 동시에 약 2.5 개의 platforms를 활용

  • 개발자 충성도
    • iOS가 개발자 충성도가 가장 높으며 59% 개발자가 iOS를 우선 고려함
    • HTML5는 cross-screen, cross-platform 배포가 필요할 시에만 우선 고려하며 평균 2.8 개의 screen를 대상으로 함
    • 북미와 서유럽에서는 iOS가 선호하는 플랫폼이나 다른 지역은 Android가 선호됨
      • 아시아에서는 개발자의 46%가 Android를 우선 고려하고 28%가 iOS를 고려

  • Connected screen
    • 머지 않은 미래에는 시계, TV 그리고 온도 조절 장치와 스마트 폰이 앱 개발자들의 주요 대상이 될 것으로 보임
    • 태플릿은 개발자의 83%가 매력적으로 느끼는 대상이나 단 12%의 개발자만이 주요 대상으로 고려 
    • Android 태플릿이 불티나게 팔리고 있으나 개발자의 52%는 iOS를 우선하여 태블릿 앱을 개발하고 있으며 Android는 개발자의 28% 만이 고려하고 있음

  • 수익 구조
    • 개발자의 60%는 '빈곤 층' (앱 당 한달 소득이 500불 이하) 에 있음
    • iOS에는 Android 보다 '중산층' (앱 당 한달 소득이 500불~ 만 불 사이)이 많아 개발자의 25%가 android를 선호하는 것 대비 개발자의 37%가 iOS를 선호함
    • 인 앱 광고가 가장 대중적인 수익 모델으로 26%의 앱 개발자는 윈도우즈폰이나 안드로이드에서는 이 모델로 수익을 얻음
    • 수주(Contract)에 따른 앱 개발이 2013년 기준 56%를 차지하고 있으며 중요한 점은 개발자의 26% 가 이런 형태로 앱을 개발 중임.
    • e-커머스를 수익 모델로 사용하는 개발자의 비중이 2013년 3분기 5%에서 2014년 1분기 8%로 급속히 증가
    • Windows 8 혹은 윈도우즈 폰 대상 개발자들이 가장 적은 수익을 얻고 있음 (한달에 앱 당 1~50불 규모)

  • 개발 도구
    • 엔터프라이즈 IT 개발자가 상대적으로 앱 개발 도구를 늦게 받아들이고 있음
      • 25%가 어떤 도구도 사용하지 않고 있어 백여 개의 도구 vendor에게는 기회임
    • iOS가 가장 도구 활용을 잘하는 개발자들을 보유함
      • 경쟁력이 있는 도구 포트폴리오는 매력적인 주요 개발자 segments의 플랫폼으로써 핵심임
    • 사용자 분석 도구cross platform tool은 가장 유명한 두 개발자 도구 category 이며 각각 개발자의 40%, 30%가 사용함


오역이 내포될 가능성이 농후한 포스팅이니 자세한 사항은 원문을 확인하시기 바랍니다. 

감사합니다.

+ Recent posts