하이브리드 앱 개발 on Visual Studio #3

AngularJS 샘플에 이어서 BackboneJS 샘플을 살펴보고자 한다. 우선 샘플을 다운로드하여, 빌드를 해봤다.

  • AngularJS 샘플과 마찬가지로 framework JS파일들을 추가해 주어야 하고,
  • Bing Map API 키를 geolocation.js 파일에 넣고,
  • Azure API 키 및 Mobile Service URL을 services/mobile services/todolist-xplat/service.js 파일에 넣는다.

image

제대로 빌드했다면, 위와 같이 AngularJS 샘플에서 작성했던 Azure 상의 ToDo 항목들이 보인다.

BackboneJS의 기초

이번에는 BackboneJS의 기초를 먼저 살펴보고 샘플을 분석해 보는 것이 좋을 것 같다. 일단 튜토리얼은 여기에서 볼 수 있다.

아래 그림은 초보자용 영상에 나오는 샘플 앱의 구조인데, index.html은 Backbone Router를 통해 /, /edit, /new 등의 페이지 처리를 구현하고, Backbone의 View와 Model을 통해 REST API로 서버와 통신을 하게 된다. 영상에서는 아래 설계를 처음부터 하나씩 구현하여 간단한 사용자 관리 클라이언트를 만드는 과정을 보여주는데 다소 긴 영상이긴 하지만 꽤 볼만하다.

image

image 

위 코드는 튜토리얼에서 작성하는 코드의 일부인데, 살펴보면 Backbone. 으로 시작하는, Collection, View, Router 등의 클래스를 상속하여 사용자 정의 클래스들을 만들고 이를 통해 Router와 View, Model을 구현한다.

Router를 이용하여 페이지 네비게이션을 구현하는 부분이 편리해 보인다. router를 이용해 특정 페이지 요청이 있으면 해당 페이지에 연결된 View의 render() 메서드를 실행하여 화면을 그린다.

Backbone.View를 상속한 UserList의 경우는 Underscore의 _.template 함수로 Users Collection에서 가져온 users.models을 HTML 템플릿을 이용해 화면에 그려내고 있다.

BackboneJS 샘플 살펴보기

image

그럼 다시 샘플로 돌어가서 index.html 파일을 살펴보자. BackboneJS 샘플은 ToDo 목록의 templating을 위해서 underscore.js를 사용하고 있다.

    <!-- Refer to underscore.js for more information on its templating language -->
    <script type="text/html" id="todo-template">
        <div class="templateWrapper">
            <div class="templateContainer">
                <input class="templateTitle" type="text" value="<%- title %>" />
                <h3 class="templateAddress"><%- location %></h3>
                <button class="templateLeft templateToggle"></button>
                <button class="templateLeft templateRemove"></button>
            </div>
            <div class="templateBorder"></div>
        </div>
    </script>

underscore는 이름처럼 _.template(템플릿 문자열) 이런 식으로 사용한다. template 함수 관련해서는 여기를 참고하자. <%- title %> <%- location %> 등이 변수에서 값으로 치환되어 HTML로 생성된다.

index.html에서 template외에 나머지 부분에서 별다를 것은 없다.underscore는 HTML의 내장 오브젝트를 확장하지 않으면서 사용할 수 있는 여러 가지 함수형 프로그래밍 헬퍼들을 제공한다. 그런 면에서 ng-로 시작하는 속성이나 directive로 HTML를 적극 확장하는 AngularJS와는 다른 접근인 셈이다.

코드 살펴보기

index.js 파일을 살펴 보면 다음과 같다.

var app = app || {};

$(function () {
    "use strict";

    document.addEventListener('deviceready', onDeviceReady.bind(this), false);

    function onDeviceReady() {
        // Handle the Cordova pause and resume events
        document.addEventListener('pause', onPause.bind(this), false);
        document.addEventListener('resume', onResume.bind(this), false);
        // Initialize storage and the app's base view.
        app.initializeStorage();
        new app.BaseView();
    };

    function onPause() {
        // TODO: This application has been suspended. Save application state here.
    };

    function onResume() {
        // TODO: This application has been reactivated. Restore application state here.
    };
});

AngularJS 샘플과 달리, Codova에서 제공하는 앱에 대한 이벤트(deviceready, pause, resume)를 사용하고 있다. (AngularJS는 대신 초기화를 위해 html이나 body에 ng-app 속성을 지정하였다.) app.initializeStorage()는 storage.js 파일에, app.BaseView는 baseView.js 파일에 정의되어 있다.

storage.js 파일은 localStorage와 azureStorage 두 가지를 구현하고 mobileServiceClient의 사용 가능 여부에 따라 적절한 storage 구현을 app.Storage에 전달하는 역할을 한다.

baseView.js 파일은 Backbone.View를 상속하여 초기 화면을 구현하는 부분이다.

    app.BaseView = Backbone.View.extend({
        // This is the DOM element for the base view
        el: '#todoapp',

        // Hook up event handler to DOM keypress event
        events: {
            'keypress #new-todo': 'createNewTodo',
        },

        initialize: function () {
            // Retrieve DOM elements
            this.$input = this.$('#new-todo');
            this.$todoList = this.$('#todo-list');

            // Hook up some event handlers to the data source
            this.listenTo(app.todoCollection, 'add', this.addTodo);
            this.listenTo(app.todoCollection, 'reset', this.createNewTodosFromCollection);
            this.listenTo(app.todoCollection, 'all', this.render);

            // Retrieve existing data from storage
            app.Storage.getData();
        },

        // Create a new view from a todo and append it to our list.
        addTodo: function (todo) {
            var view = new app.TodoView({ model: todo });
            this.$todoList.append(view.render().el);
        },

el 속성에 #todoapp을 지정하여, index.html 파일에 “todoapp”이라는 id를 가진 <section> 태그를 baseView의 요소로 사용한다. 마우스 엔터키 이벤트를 keypress 이벤트로 가져와서 createNewTodo를 실행하고, app.todoCollection 데이터 소스의 add, reset, all 이벤트에 각각 핸들러를 추가하여 데이터에 변경사항이 있을 때 이를 처리하는 부분이 구현되어 있다. addTodo에는 app.TodoView를 생성하여 #todo-list 요소에 추가하고 있다.

todoView.js를 보면 다음과 같이 정의되어 있다.

app.TodoView = Backbone.View.extend({
        // This is the tag we wrap around our template
        tagName: 'div',

        // HTML item template
        template: _.template($('#todo-template').html()),

        // Hook up event handlers to mousedown and change events
        events: {
            'mousedown .templateToggle': 'toggleCompleted',
            'mousedown .templateRemove': 'deleteTodo',
            'change .templateTitle': 'updateTodo',
        },

        // Hook up event handlers to our model
        initialize: function () {
            this.listenTo(this.model, 'change', this.render);
            this.listenTo(this.model, 'todoDescriptionChanged', this.render);
            this.listenTo(this.model, 'destroy', this.remove);
        },

        // "Refresh" the todo in response to a change in the model
        render: function () {
            this.$el.html(this.getTemplateData());
            this.$input = this.$('.templateTitle');
            this.$toggle = this.$('.templateToggle');
            this.updateCrossOut();

            return this;
        },

        // We supply the model (as json) to instantiate our template
        getTemplateData: function () {
            return this.template(this.model.toJSON());
        },

template로 #todo-template를 사용하고 있고, 이건 앞서 index.html에서 확인하였다. render() 메서드를 baseView에서 호출하였으므로 살펴보면, getTemplateData()의 this.template(this.modeul.toJSON())을 통해서 todo 모델의 데이터가 적용된 HTML 템플릿을 가져와서 적용하고 리턴한다. $input, $toggle은 HTML 템플릿의 요소에 코드로 변경을 하기 위해 생성하는 참조이다. 리턴한 결과는 baseView에서 받아서 append한다.

todoCollection.js는 단순하다.

var app = app || {};

(function () {
    'use strict';

    var TodoCollection = Backbone.Collection.extend({
        model: app.TodoModel,
    });

    app.todoCollection = new TodoCollection;
})();

Backbone.Collection을 상속하고, model로 app.TodoModel을 지정한다.

app.TodoMobel은 todoModel.js 파일에 정의되어 있다.

    app.TodoModel = Backbone.Model.extend({

        defaults: {
            title: '',
            done: false,
            // This placeholder text is displayed while the app is querying the device's
            // location and supplying it to the restful service to obtain a street address.
            location: 'Getting your location...'
        },

        toggleCompleted: function () {
            this.save({
                done: !this.get('done')
            });
        },

        // We don't want to sync. We have a local/zumo storage implementation
        sync: function () { return false; }
    });

defaults에 title, done, location 등의 속성이 있고 toggleCompleted() 경우는 todoView에서 Todo 항목의 토글 버튼을 클릭했을 때 호출한다.

Todo를 추가하고 수정하는 등의 처리는 app.Storage를 통해서 하는데, 이러한 호출은 baseView(추가)와 todoView(수정, 삭제)에 있다. 아래 코드는 baseView의 createNewTodo 메서드이다.

createNewTodo: function (e) {
            if (e.which === ENTER_KEY && this.$input.val().trim()) {
                var todo = app.todoCollection.create({
                    title: this.$input.val().trim(),
                    done: false
                });

                // This callback will take data from the RESTful service, retrieve the street address,
                // and set it in the todo, then save it.
                var onSuccess = function (data) {
                    todo.save({
                        location: data.resourceSets[0].resources[0].address.formattedAddress
                    })

                    app.Storage.saveTodo(todo);
                };

                var onError = function (error, position) {
                    todo.save({
                        location: position.coords.latitude + "," + position.coords.longitude
                    })

                    app.Storage.saveTodo(todo);
                };

                // Retrieve the location data
                app.Geolocation.getLocation(onSuccess, onError).then(app.Geolocation.getAddressFromLocation);

                // Clear the input
                this.$input.val('');
            }
        }

살펴보면 app.todoCollection.create()를 이용해서 새로운 Todo 데이터를 만들고, 이를 app.Storage.saveTodo(todo)를 호출하여 서버 또는 로컬에 저장한다.

정리

여기까지 대략적인 BackboneJS 샘플의 구조와 코드를 살펴보았다.

  • Codova의 deviceready 이벤트를 받아서 app.Storage와 app.baseView를 초기화한다.
  • app.baseView는 Backbone.View를 상속받아서 구현하며, index.html의 <section id=”todoapp”>을 View로 사용한다.
  • app.baseView는 다시 app.todoView를 이용하여 각 todo 항목에 대한 UI 및 수정/삭제 등의 기능을 처리한다. 이 때 TodoView는 index.html의 todo-list-template을 가져와서 underscore.js의 template() 함수로 각각의 Todo 항목의 HTML을 생성하고 app.baseView에 리턴하여 append한다.
  • TodoModel과 TodoCollection은 각각 Todo 항목과 리스트를 가지고 있는데, BaseView와 TodoView는 사용자가 항목을 생성/수정/삭제할 때 TodoCollection에 반영하고, 이를 app.storage에 적용한다.
  • TodoCollection에 변경사항이 있을 때 이벤트는 다시 BaseView와 TodoView로 전달되어 화면을 업데이트한다.

BackboneJS는 처음 앱 구조를 봤을 때에 생각한 것에 비하여 이해하는데 어렵지 않았다. AngularJS처럼 이해해야 할 개념이 많지 않고 View/Collection을 꼭 다른 파일로 해야 하는 것이 아니라서 생각보다 쉽게 접근할 수 있었다. 다만, View가 UI와 데이터를 처리하는 부분까지 모두 포함하고 있는 부분이나 View가 여럿 있을 때 코드가 복잡해 질 수 있는 부분은 신경이 많이 쓰일 것 같다.

다음은 마지막으로 WinJS 샘플을 살펴보도록 하겠다.

Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중