본문 바로가기

Sencha Touch 2

4일차 : Sencha Touch 2 views 에 대한 이해!

App 에서 View 를 사용해 보자!!


사용자가 실제로 보는 부분은 view 이다. 오늘은 app 에서 어떻게 view 를 만들고 그렇게 만들어진 View 를 통해 어떻게 application 을 build 하는 지를 설명하겠다.


지난 시간에 배웠듯이, component 를 만들 때, 센차에서 제공하는 Component class 를 이용하는데 이 때, Ext.create 를 사용한다.


  
Ext.create('Ext.Panel', {
    html: 'Welcome to my app',
    fullscreen: true
});


그러나 이 것 보다 실제로 더 많이 쓰이는 방법은, Ext.define 을 사용하는 방법인데, 센차에서 기본적으로 제공하는 class 를 상속하여 프로그래머가 필요로 하는 class 를 만들고 그 class 를 가지고 instance 를 만드는 방법이다. 아래 코드를 보자.


  
Ext.define('MyApp.view.Welcome', {
    extend: 'Ext.Panel',

    config: {
        html: 'Welcome to my app',
        fullscreen: true
    }
});

Ext.create('MyApp.view.Welcome');


위 코드 결과는, 그 위의 코드 결과와 같지만, 프로그래머가 Ext.Panel 을 상속하여 새로 define 한 'MyApp.view.Welcome' 를 계속 사용할 수 있다는 장점이 있다. 실제로 이 방법이 app 을 개발할 때 많이 쓰이는 방법이다. 

즉, 센차가 제공하는 components class 를 상속하여 새로운 class 를 만들고, 그렇게 새로 만들어진 class 를 가지고 나중에 instance 를 만들 때 사용하는 것이다. 


Ext.define 을 사용하므로써 바뀐 부분은 아래와 같다.

- Ext.define allows us to create a new class, extending an existing one (Ext.Panel in this case) : 새로운 클래스를 만든다. 이 때 이미 있는 클래스를 상속하여 만든다.

- We followed the MyApp.view.MyView convention for our new view class. You can name it whatever you like but we suggest sticking with convention : MyApp.view.MyView 관습을 따른다. 

- We defined config for the new class inside a config object : config 객체 안에 새로 만드는 클래스에 적용할 속성을 define 한다. 

예를 들어 Ext.Panel 을 상속하여 새로운 class 를 define 할 때, 새로 define 하는 class 의 속성에 이미 부모 class 인 Ext.Panel 에 명시 돼 있는 각종 config 들 중에 일부는 값을 할당해 주면서 define 하고 싶을 수 있다. 이 때는, 상속 받을 새 class 의 config 객체에서 지정해 주면 된다.

혹은 Ext.Panel 을 상속해서 새로 class 만든 뒤, 그 새로 만든 class 를 가지고 instance 를 만들 때 config 에 값을 줄 수도 있다. 


이 두 가지 방법이 바로 아래 코드에 나와 있다.


  
Ext.define('MyApp.view.Welcome', {
    extend: 'Ext.Panel',

    config: {
        html: 'Welcome to my app',
        fullscreen: true
    }
});

Ext.create('MyApp.view.Welcome', {
    styleHtmlContent: true
});


예제를 한번 봅시다.


  
Ext.define('Twitter.view.SearchBar', {
    extend: 'Ext.Toolbar',
    xtype : 'searchbar',
    requires: ['Ext.field.Search'],

    config: {
        ui: 'searchbar',
        layout: 'vbox',
        cls: 'big',

        items: [
            {
                xtype: 'title',
                title: 'Twitter Search'
            },
            {
                xtype: 'searchfield',
                placeHolder: 'Search...'
            }
        ]
    }
});


위 예제 코드는 Ext.Toolbar 를 확장하여 Twitter.view.SearchBar 라는 class 를 만든다. 이 때 사용될 수 있었는 filed 값들은 extend 말고도 여러가지 속성들이 사용된다.(xtype, requires, config 등등) 


먼저 결과 화면 부터 보자.




여기서 두 가지를 유심히 보자.


1. requires 는 items 부분에서 사용하는 components 들 중에 추가로 필요로 하는 class 들을 명시를 해준다.


가령 Ext.Toolbar 를 검색 해 보면, requires 에 Ext.Button, Ext.Spacer, Ext.Title 가 있는 것을 알 수 있다. 




그러나 위의 코드에서는 searchfield 를 추가적으로 사용하고 있고, 이는 기본적으로 명시된 requires 에 없는 class 이기 때문에, dynamic loading system 은 이 순간에 이게 어떤 class 인지를 모른다. 그러므로 requires 에 searchfield 의 full name 을 명시해서 추가적으로 load 할 수 있도록 한다. 

requires: ['Ext.field.Search'], <-- 그래서 이게 필요한 것이다.


2. xtype : 우리가 새로 만든 type 에 대해 새로운 xtype 을 부여한다. 이 xtype 은 다른 곳에서 사용할 수 있다.


아래는 새로 만든 class 를 가지고 instance 를 만드는 두 가지 방법을 보여준다.


  
//creates a standalone instance
Ext.create('Twitter.view.SearchBar');

//alternatively, use xtype to create our new class inside a Panel
Ext.create('Ext.Panel', {
    html: 'Welcome to my app',

    items: [
        {
            xtype: 'searchbar',
            docked: 'top'
        }
    ]
});





커스톰한 설정 주기!


예제를 통해 알아보자! 지금 우리가 하려는 것은 image view 를 만드는데, 해당 image 를 touch 하면 그에 해당하는 title 과 설명(description) 을 보여주는 image view 를 만들려고 한다. Ext.img 가 대부분의 우리가 필요로 하는 view 로써의 속성을 가지고 있기 때문에 Ext.Img 를 활용을 할 것이다.


결과 화면 먼저 보자 (아래 코드를 그대로 실행한 결과 화면은 아니다. 거의 차이는 없지만..)




Ext.define('MyApp.view.Image', { extend: 'Ext.Img', config: { title: null, description: null }, //sets up our tap event listener initialize: function() { this.callParent(arguments); this.element.on('tap', this.defaultTap, this); }, //this is called whenever you tap on the image defaultTap: function() { Ext.Msg.alert(this.getTitle(), this.getDescription()); } }); //creates a full screen tappable image Ext.create('MyApp.view.Image', { title: 'Orion Nebula', description: 'The Orion Nebula is rather pretty', src: 'http://apod.nasa.gov/apod/image/1202/oriondeep_andreo_960.jpg', fullscreen: true });


config 에 보면 title 과 description 이 포함 된 것을 알 수 있다. 이는 기존의 Ext.Img 에서는 없는 config 속성이었다. 그러므로 앞으로 'MyApp.view.Image' 으로 image view 를 만들 때는 (다른 config 속성을 줄 때와 마찬가지로) title 과 description 속성을 주고 값을 줄 수 있다. 


주위 깊게 볼 부분은 initialize 부분이다. 해당 function 은 처음 instance 를 만들 때 호출 되는 function 이다. (모든 component 에 해당)

이 내부에서

this.callParent(arguments); 

는 superclass 의 initialize function 을 호출하는 역할을 한다. 이게 없다면 정상적으로 작동하지 않기 때문에 반드시 포함시켜야 한다.


callParent 를 했으니, 이제 tap event 가 발생했을 때 이미지의 title 과 description 을 출력할 수 있도록 해주기 위한 장치를 마련해야 한다. this.element.on('tap', this.defaultTap, this); 

는 tap 이라는 이벤트가 발생했을 때, defaultTap 이라는 함수가 event handler 가 되어 해당 이벤트를 처리 해주라는 의미이다. 이 때 this 는 핸들링하는 함수가 실행될 때의 scope 을 의미하는데, 보통 this 를 많이 쓴다. (on 은 addListener 의 별칭이다.  http://docs.sencha.com/touch/2-0/#!/api/Ext.mixin.Observable-method-addListener )


this.element.on('tap', this.defaultTap, this); 

를 좀 더 살펴 보면, 

on 은 tap 이라는 이름의 이벤트에 대해서 listener 를 해당 component 의 element 에 더할 수 있도록 해준다. 그러므로, 해당 component 의 element 가 tap 될 때 마다 defaultTap 이 호출되는 것이다. 모든 component 들은 자기 자신에 해당하는 element 속성을 가지고 있기 때문에, DOM 객체에 발생하는 이벤트들을 듣기 위해서는 위에서 보는 것처럼 처리하면 된다.

그 이외에도 각 components 에 속한 elements 에 style 을 추가/삭제 할 수 있으며, Ext.dom.Element 과 관련된 동작 등을 수행할 수 있다. (DOM 에 대한 보다 자세한 정보는  http://jokergt.tistory.com/62  를 참조!)


그리고 중요한 것은 config 에 새로운 속성을 추가할 경우, 자동적으로 getter setter 가 생성된다는 점이다




더욱 발전된 속성?!!


config 안에 주어진 속성들에 대해서 자동적으로 getter setter 가 생긴다는 것은 이미 위에서 언급한 사항이다. 아래 코드를 보자.


  
Ext.define('MyApp.view.MyView', {
    extend: 'Ext.Panel',

    config: {
        border: 10
    }
});

var view = Ext.create('MyApp.view.MyView');

alert(view.getBorder()); //alerts 10

view.setBorder(15);
alert(view.getBorder()); //now alerts 15


그러나 단지 getter setter 가 생기는 것만은 아니다. 



  
Ext.define('MyApp.view.MyView', {
    extend: 'Ext.Panel',

    config: {
        border: 0
    },

    applyBorder: function(value) {
        return value + "px solid red";
    },

    updateBorder: function(newValue, oldValue) {
        this.element.setStyle('border', newValue);
    }
});


위의 경우 처럼 추가적으로 apply~~ 와 update~~ 함수를 정의할 수 있다. 

위의 예제 에서는 applyBorder 의 경우, border 값이 정해지거나 바뀔 때마다 호출된다. (처음 instance 로 만들어 질 때도 호출이 된다.) 이 함수(~~border)는 config 속성의 값을 바꿀 때 사용하기 좋다. 이 함수가 제대로 작동하기 위해서는 값을 return 해야 하며, 이는 (위의 예제처럼) 입력된 값을 새로운 값으로 바꿔주는 역할을 한다. 


updateBorder 는 applyBorder 가 호출 된 뒤에 호출된다. 여기서는 바뀐 border 값을 바로 style 적용할 수 있도록 하였다.


아래는 이를 활용한 코드이다.


  
//as before
Ext.define('MyApp.view.MyView', {
    extend: 'Ext.Panel',

    config: {
        border: 0
    },

    applyBorder: function(value) {
        return value + "px solid red";
    },

    updateBorder: function(newValue, oldValue) {
        this.element.setStyle('border', newValue);
    }
});

//create an instance of MyView with a spinner field that updates the border config
var view = Ext.create('MyApp.view.MyView', {
    border: 5,
    fullscreen: true,
    styleHtmlContent: true,
    html: 'Tap the spinner to change the border config option',
    items: {
        xtype: 'spinnerfield',
        label: 'Border size',
        docked: 'top',
        value: 5,
        minValue: 0,
        maxValue: 100,
        increment: 1,
        listeners: {
            spin: function(spinner, value) {
                view.setBorder(value);
            }
        }
    }
});


결과 화면은 아래와 같다.




MVC 패턴 안에서 view !


MVC 방식을 아직 배우지는 않았지만 훗날을 위에 미리 지금 공부하자.

view 를 만들 때 따라야 하는 규칙이 있다. 


우리가 만든 클래스가 가령 

MyApp.view.MyView 

라면 해당 클래스를 정의하는 파일은 반드시

app/view/MyView.js 

이 파일 안에 정의 되 있어야 한다. 

파일 뿐 아니라 파일이 어디 속해있는지도 유심히 봐야 한다.


이렇게 규칙을 따라 줘야 application 이 원하는 클래스를 찾아서 load 할 수 있다. 


  
//contents of app.js
Ext.application({
    name: 'MyApp',
    views: ['MyView'],

    launch: function() {
        Ext.create('MyApp.view.MyView');
    }
});


위의 코드를 보면 views: ['MyView'] 를 통해 우리가 추가한 MyView class 를 load 하고, Ext.create 를 활용해서 해당 class 타입의 instance 를 만들어 주고 있다.