Sencah Touch2를 이용한 하이브리드 앱 개발 - 7.General Components

서론

Sencha는 다양한 컴포넌트를 미리 제공하기 때문에 모바일 앱을 웹으로 만들어도 어색하지 않다는 것을 우리는 이전 포스팅의 예제를 직접 테스트하면서 알게되었다. 뷰를 구성하는 Ext.Container를 상속받은 컴포넌트들을 조합하면 UI를 효율적으로 개발할 수 있다는 것도 알게 되었다. 이 포스팅은 Sencha Touch 2의 뷰를 구성하는 포스팅의 마지막으로 General Components에 대해서 살펴볼 것이다.

우리는 Sencha Touch의 중요한 component를 대부분 실습해보았는데, 몇가지 정말 중요한 컴포넌트를 살펴보지 않았다. 그 중에서 하나가 바로 탭 기능을 하는 것이다. iOS에서는 TabBarController이고 Android에서는 TabHost라고 불려지는 것은 여러개의 ViewController나 Activity를 탭을 누를 때마다 변환하면서 선택한 것을 보여주는 기능을 하는 것인데, Sencha에서는 이러한 기능을 Ext.tab.Panel을 이용해서 구현할 수 있다.

Tab Panel

보통 Tab 이라는 기능을 가지는 UI는 서로 다른 뷰를 번갈아 보이게 하면서 뷰를 활성화시키는 역활을 한다. Sencha에서도 동일한 방법을 사용하는데 다음과 같이 app.js를 변경하여 실행시켜보자.

/**
* file : app.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.application({  
    name: 'SaltfactorySenchaTutorial',
    launch: function() {
        Ext.create('Ext.TabPanel', {
            fullscreen: true,
            tabBarPosition: 'bottom',

            defaults: {
                styleHtmlContent: true
            },

            items: [
                {
                    title: 'Home',
                    iconCls: 'home',
                    html: 'Home Screen'
                },
                {
                    title: 'Contact',
                    iconCls: 'user',
                    html: 'Contact Screen'
                }
            ]
        });
    }
});

위의 코드는 아래 그림과 같이 두가지 탭 아티템을 가지고있는 탭 메뉴를 생성할 수 있다.

만약에 Android의 TabHost와 같이 탭 메뉴가 상단에 위치하고 싶을 경우는 tabBarPosition을 top으로 변경하면 된다.

/**
* file : app.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.application({  
    name: 'SaltfactorySenchaTutorial',
    launch: function() {
        Ext.create('Ext.TabPanel', {
            fullscreen: true,
            tabBarPosition: 'top',

            defaults: {
                styleHtmlContent: true
            },

            items: [
                {
                    title: 'Home',
                    iconCls: 'home',
                    html: 'Home Screen'
                },
                {
                    title: 'Contact',
                    iconCls: 'user',
                    html: 'Contact Screen'
                }
            ]
        });
    }
});

tabBarPosition이 변경됨으로 탭 메뉴의 형태도 달라진다.

Sencha Touch를 이용하면 이렇게 간단하게 탭 메뉴를 구성할 수 있다.
우리는 Sencah Touch2를 이용한 하이브리드 앱 개발 - 2.View 생성 글에서 view를 구성하는 파일을 /app/view/ 밑에 구성한다는 것을 살펴보았다. 우리는 여러개의 뷰를 만들어서 Tab을 이용해서 서로 다른 뷰 사이를 전환해보도록 하겠다.

FirstView.js와 SecondView.js를 각각 /app/view/ 디렉토리 밑에 아래와 같이 저장하여 생성한다. 간단하게 tab에 나타날 아이콘과 타이틀 설정과 뷰 자체의 타이틀바의 타이틀을 지정하였다.

/**
* file : FirstView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.FirstView', {  
    extend: 'Ext.Container',
    alias: 'first_view',

    config: {
        iconCls: 'home',
        title: 'first',
        items: [{xtype:'titlebar', title:'first'}],
        html: '<h1>first view</h1>'
    }
});
/**
* file : SecondView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.SecondView', {  
    extend: 'Ext.Container',
    alias: 'second_view',

    config: {
        iconCls: 'user',
        title: 'second',
        items: [{xtype:'titlebar', title:'second'}],
        html: '<h1>Second View</h1>'
    }
});

다음은 app.js를 수정해보자. 위에 추가한 FirstView와 SecondView를 Ext.application에 views로 등록을 하고, 두가지 뷰를 생성하여 TabPanel의 items로 등록한다.

/**
* file : app.js
* author : saltfactory
* email : saltfactory@gmail.com
*/


Ext.application({  
    name: 'SaltfactorySenchaTutorial',
    views:['FirstView', 'SecondView'],
    launch: function() {

        var firstView = Ext.create('first_view');
        var secondView = Ext.create('second_view');

        var tab = Ext.create('Ext.TabPanel', {
            fullscreen: true,
            tabBarPosition: 'bottom',
            defaults: {
                styleHtmlContent: true
            },
            items: [firstView, secondView]
        });

    }
});

위의 코드는 다음과 같이 수정할 수도 있다. 우리는 Ext 컴포넌트를 생성할때 Ext.create 대신에 xtype으로 컴포넌트를 렌더링하는 할 수 있다는 것을 앞에서 같이 살펴본적이 있다. xtype과 마찬가지로 xclass는 해당되는 이름을 찾아서 렌더링하여 컴포넌트를 사용할 수 있게 해주는 것이다.

/**
* file : app.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.application({  
    name: 'SaltfactorySenchaTutorial',
    views:['FirstView', 'SecondView'],
    launch: function() {

        // var firstView = Ext.create('first_view');
        // var secondView = Ext.create('second_view');

        var tab = Ext.create('Ext.TabPanel', {
            fullscreen: true,
            tabBarPosition: 'bottom',
            defaults: {
                styleHtmlContent: true
            },
            // items: [firstView, secondView]
            items:[
                {
                    xclass:'SaltfactorySenchaTutorial.view.FirstView'
                },
                {
                    xclass:'SaltfactorySenchaTutorial.view.SecondView'
                }
            ]
        });

    }
});

탭 메뉴에 두가지 아이템을 선택할 수 있도록 되어졌고 각 아이템을 선택하면 뷰가 전환되는 것을 확인할 수 있을 것이다. 그런데 뷰의 전환이 iOS의 TabViewController와 Android의 TabHost와 다르다는 것을 느낄 것이다. Sencha는 기본적으로 뷰 전환 에니메이션을 Slide로 지정하고 있기 때문이다. 그럼 네이티브 앱과 같이 뷰가 전환되기 위해서는 에니메이션 효과를 없애면 된다.

/**
* file : app.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.application({  
    name: 'SaltfactorySenchaTutorial',
    views:['FirstView', 'SecondView'],
    launch: function() {

        // var firstView = Ext.create('first_view');
        // var secondView = Ext.create('second_view');

        var tab = Ext.create('Ext.TabPanel', {
            fullscreen: true,
            tabBarPosition: 'bottom',
            layout:{
                 animation:null
             },
            defaults: {
                styleHtmlContent: true
            },
            // items: [firstView, secondView]
            items:[
                {
                    xclass:'SaltfactorySenchaTutorial.view.FirstView'
                },
                {
                    xclass:'SaltfactorySenchaTutorial.view.SecondView'
                }
            ]
        });

    }
});

Image

Sencha를 이용해서 뷰를 구성하다보면 iOS의 UIImageView와 같이 이미지를 뷰에 올려서 사용하는 방법이 필요할 수 있다. 이미지 자원을 앱의 뷰에 올려서 뷰를 구성할때 필요하기 때문이다. 센차에서는 Ext.Img를 이용해서 이와 같은 작업을 할 수 있다.

FirstView.js에 Image를 올려보도록 하자.

/**
* file : FirstView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.FirstView', {  
    extend: 'Ext.Container',
    alias: 'first_view',

    config: {
        iconCls: 'home',
        title: 'first',
        items: [
            {
                xtype:'titlebar',
                title:'first'
            }
        ],
        html: '<h1>first view</h1>'
    },

    initialize:function(){
        var imageView = Ext.create('Ext.Img',{
            src:'http://www.sencha.com/assets/images/sencha-avatar-64x64.png',
            height:64,
            width:64,
            flex: 1
        });
        this.add(imageView);
    }

});

원격 URL 로 이미지를 불러와서 현재의 뷰 (Ext.Container)에 Ext.Img 컴포넌트를 add 시키는 방법을 사용했다.

MessageBox

네이티브 앱이나 웹 앱을 만들 때 사용자에게 뭔가 메세지를 출력시키기 위해서 우리는 MessageBox를 사용한다. iOS에서는 UIAlertView를 이용하고 Android 에서는 Toast 위젯을 이용한다.

FirstView.js에 버튼을 추가하고 버튼을 누르면 MessageBox가 보이도록 코드를 작성해보자.

/**
* file : FirstView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.FirstView', {  
    extend: 'Ext.Container',
    alias: 'first_view',

    config: {
        iconCls: 'home',
        title: 'first',
        layout: 'vbox',
        items: [
            {
                xtype:'titlebar',
                title:'first'
            },
            {
                xtype:'button',
                text:'show MessageBox',
                margin: 10,
                handler:function(){
                    Ext.Msg.alert('알림', '버튼을 누르셨습니다', Ext.emptyFn);
                }
            }
        ],
        html: '<h1>first view</h1>'
    },


});

MessageBox의 OK 버튼을 눌렀을 때 위 코드는 아무런 작업을 하지 않고 닫히기만 한다. 그래서 Ext.emptyFn 메소들를 입력했다. 그럼 버튼이 눌러졌을때 뭔가 일을 처리할 수 있도록 handler를 등록하자.

/**
* file : FirstView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.FirstView', {  
    extend: 'Ext.Container',
    alias: 'first_view',

    config: {
        iconCls: 'home',
        title: 'first',
        layout: 'vbox',
        items: [
            {
                xtype:'titlebar',
                title:'first'
            },
            {
                xtype:'button',
                text:'show MessageBox',
                margin: 10,
                handler:function(){
                    Ext.Msg.alert('알림', '버튼을 누르셨습니다', function(){
                         console.log('on ok button')
                     });

                }
            }
        ],
        html: '<h1>first view</h1>'
    },


});

Ext.emptyFn 대신에 작업을 처리할 수 있는 function으로 교체했다. 이제 다시 앱을 실행시키고 버튼을 누르면 등록해둔 handler 가 동작하는 것을 확인할 수 있다.

위의 MessageBox는 단순하게 버튼이 OK 하나만 되어 있는 MessageBox이다. 우리는 이러한 Alert 형태의 MessageBox 말고 사용자가 confirm을 할 수 있는 MessageBox를 많이 사용하기도 한다. 그래서 Sencha에서는 Ext.Msg에 Ext.Msg.alert와 Ext.Msg.confirm 두가지를 미리 정의해두었다.

/**
* file : FirstView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.FirstView', {  
    extend: 'Ext.Container',
    alias: 'first_view',

    config: {
        iconCls: 'home',
        title: 'first',
        layout: 'vbox',
        items: [
            {
                xtype:'titlebar',
                title:'first'
            },
            {
                xtype:'button',
                text:'show MessageBox',
                margin: 10,
                handler:function(){
                    Ext.Msg.confirm('알림', '버튼을 누르셨습니다', Ext.emptyFn);
                }
            }
        ],
        html: '<h1>first view</h1>'
    },

});

Ext.Msg.alert와 다르게 No, Yes 두가지 버튼이 포함되어 사용자가 결정할 수 있는 MessageBox가 만들어졌다.

Ext.Msg.alert에서 버튼을 누르면 이벤트를 처리할 수 있는 handler 를 등록한것 처럼 Ext.Msg.confirm을 처리하는 function을 정의해서 추가한다. 이 때 버튼을 누르면 어떤 것이 선택되었는지 알 수가 있다. 다음 코드로 변경해보자.

/**
* file : FirstView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.FirstView', {  
    extend: 'Ext.Container',
    alias: 'first_view',

    config: {
        iconCls: 'home',
        title: 'first',
        layout: 'vbox',
        items: [
            {
                xtype:'titlebar',
                title:'first'
            },
            {
                xtype:'button',
                text:'show MessageBox',
                margin: 10,
                handler:function(){
                    Ext.Msg.confirm('알림', '버튼을 누르셨습니다', function(button){
                         console.log('on ok button : ' + button)
                     });
                }
            }
        ],
        html: '<h1>first view</h1>'
    },

});

No 버튼을 누르면 no가 전달되고 Yes를 누르면 yes 값이 전달되는지 확인할 수 있다.

또는 Ext.Msg를 직접 정의하여 사용할 수도 있다. Ext.Msg의 buttons에 각각 버턴을 따로 지정하고 hander도 따로 지정하면 된다.

/**
* file : FirstView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.FirstView', {  
    extend: 'Ext.Container',
    alias: 'first_view',

    config: {
        iconCls: 'home',
        title: 'first',
        layout: 'vbox',
        items: [
            {
                xtype:'titlebar',
                title:'first'
            },
            {
                xtype:'button',
                text:'show MessageBox',
                margin: 10,
                handler:function(){
                    var msg = new Ext.MessageBox();
                    msg.show({
                        title: '알림',
                        msg: '버튼을 누르셨습니다.',
                        buttons: [
                            {
                                text:'확인',
                                handler:function(){ console.log('on ok button'), msg.hide()}
                            },
                            {
                                text:'취소',
                                handler:function(){ console.log('on cancel button'), msg.hide()}
                            }
                        ]
                    });
                }
            }
        ],
        html: '<h1>first view</h1>'
    },
});

ActionSheet

MessageBox 로는 사용자의 넓은 선택을 표현하기에 한계가 있다. 그래서 iOS에서는 사용자의 폭 넓은 선택을 받기 위해서 UIActionSheet를 사용하고 Android에서는 Dialog 위젯을 사용한다. Sencha 역시 ActionSheet 컴포넌트를 제공한다.

/**
* file : FirstView.js
* author : saltfactory
* email : saltfactory@gmail.com
*/

Ext.define('SaltfactorySenchaTutorial.view.FirstView', {  
    extend: 'Ext.Container',
    alias: 'first_view',

    config: {
        iconCls: 'home',
        title: 'first',
        layout: 'vbox',
        items: [
            {
                xtype:'titlebar',
                title:'first'
            },
            {
                xtype:'button',
                text:'show MessageBox',
                margin: 10,
                handler:function(){
                    var msg = new Ext.MessageBox();
                    msg.show({
                        title: '알림',
                        msg: '버튼을 누르셨습니다.',
                        buttons: [
                            {
                                text:'확인',
                                handler:function(){ console.log('on ok button'), msg.hide()}
                            },
                            {
                                text:'취소',
                                handler:function(){ console.log('on cancel button'), msg.hide()}
                            }
                        ]
                    });
                }
            },
            {
                xtype: 'button',
                text: 'show ActionSheet',
                margin: 10,
                handler:function(){
                    var actionSheet = Ext.create('Ext.ActionSheet', {
                        items: [
                            {
                                text: '글 삭제',
                                ui  : 'decline',
                                handler:function(){
                                    actionSheet.hide();
                                }
                            },
                            {
                                text: '글 저장',
                                ui : 'confirm',
                                handler:function(){
                                    actionSheet.hide();
                                }
                            },
                            {
                                text: '취소',
                                ui  : 'cancel',
                                handler:function(){
                                    actionSheet.hide();
                                }
                            }
                        ]
                    });

                    Ext.Viewport.add(actionSheet);
                    actionSheet.show();

                }
            }
        ],
        html: '<h1>first view</h1>'
    },
});

actionSheet의 items 들의 버튼 UI에 따라사 색상이 달라진다는 것을 확인할 수 있다.

결론

이 포스팅을 마지막으로 Sencha Touch 2에서 뷰를 구성하는 컴포넌트에 대해서 예를 실습해보면서 살펴보았다. 이제 거의 대부분의 웹 앱 또는 하이브리드 앱의 UI를 만들 수 있는 준비는 모두 마친 것이다. 다음 포스팅에서는 Sencha의 MVC 중에서 Model과 Store에 대해서 포스팅이 진행될 예정이다. Sencha Touch는 기본적으로 UI의 컴포넌트들의 스타일과 특성을 내장해서 앱 개발시 UI 프로그래밍의 시간을 대폭 감소 시켜줄 수 있다. 이 블로그에서 작성하는 Sencha의 내용은 Sencha Touch의 가장 많이 사용되는 일부의 기능만을 소개한 것이다. 좀더 관심있게 메뉴얼과 구글링을 통해서 유연하고 아름다운 UI를 개발할 수 있을 것으로 예상된다.

참고

  1. http://docs.sencha.com/touch/2-0/#!/api/Ext.tab.Panel
  2. http://docs.sencha.com/touch/2-0/#!/api/Ext.Img
  3. http://docs.sencha.com/touch/2-0/#!/api/Ext.MessageBox
  4. http://docs.sencha.com/touch/2-0/#!/api/Ext.ActionSheet