반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 받은 트랙백

글 보관함


Using Device Profiles 

오늘날 모바일 웹 애플리케이션들은 다양한 디바이스들에서 동작하도록 해야 합니다. 아주 작은 모바일 폰에서부터 큰 태블릿까지 아주 다양한 디바이스들에서 말이죠. 이러한 디바이스들은 아주 다양한 스크린 해상도를 가지고 있고 각기 다른 사용법과 목적이 있습니다. 사람들은 집 밖에서 빠르게 정보를 구하거나 어떤 급한 일을 하기 위해 모바일 폰의 앱을 사용합니다. 태블릿 앱들은 폰 앱들보다는 비교적 긴 시간동안 사용되어 지는 경향이 있습니다. 태블릿은 집이나 어딘가에 앉아 있을 때 긴 시간동안 다루는 기기인 셈이죠.

이와같이 사람들은 각각의 기기(device)별로 각각의 앱 experience를 기대합니다. 또한 개발자 입장에서는 이런 다른 experience들 사이에서도 애플리케이션의 로직이나 asset들은 같이 사용할 수도 있습니다. 각각의 플랫폼에 맞춰 별도로 앱을 개발한다는 것은 시간을 많이 소비해야 하고 에러가 날 확률도 많습니다. 그리고 무엇보다 아주 지루하구요. 고맙게도 Device profile은 각각의 device 별로 맞춰서 코딩을 할 수 있도록 device type을 구하고 코드 내에서 별도의 로직으로 분화시킬 수 있어 각 플랫폼 별 별도의 앱 개발이라는 문제점에서 어느정도 벗어날 수 있도록 해 줍니다.





Setting up Profiles

Device Profile은 어플리케이션의 context 안에 존재합니다. 예를 들어 폰이나 태블릿에 이메일 앱을 생성하기를 원한다면 개발자는 app.js에 아래와 같이 하시면 됩니다. (이 방법이 생소하시면 Intro to Apps guide 를 보세요.

Ext.application({
    name: 'Mail',

    profiles: ['Phone', 'Tablet']
});


위 코드에서는 아직 Aplication에 launch function을 넣지 않았죠? 그러니까 저 코드에서는 이 두개의 프로파일들만 로드 할 겁니다. 관례적으로 이렇게 되면 app/profile/Phone.js 와 app/profile/Tablet.js 를 로드하게 됩니다. 그럼 아래에 Phone.js 가 주로 어떤 모습을 하고 있는지 예제 코드를 보겠습니다.

Ext.define('Mail.profile.Phone', {
    extend: 'Ext.app.Profile',

    config: {
        name: 'Phone',
        views: ['Main']
    },

    isActive: function() {
        return Ext.os.is.Phone;
    }
});


태블릿 프로파일도 위 폰 프로파일과 같은 패턴으로 만듭니다. 위 Phone profile을 보시면 단 3파트로만 구성돼 있죠? Profile name, 옵션으로 세팅하는 추가적인 view 그리고 profile이 activated 됐을 때 실행될 isActive 함수 이렇게 간단하게 세 파트로만 구성하시면 됩니다.

isActive 함수는 디바이스에서 이 앱이 실행 될 때 체크한 device type이 이에 해당되면 실행하게 됩니다. 가장 일반적인 경우는 built-in 프로퍼티인 Ext.os.is.Phone 과 Ext.os.is.Tablet를 사용해서 Phone 과 Tablet 프로파일을 별도로 만드는 겁니다. isActive function 안에는 여러분이 넣고싶은 어떤 코드도 들어갈 수 있습니다.

Determining the Active Profile

한번 프로파일이 로드되면 isActive function이 call 됩니다. 애플리케이션이 부팅되면서 처음 true를 리턴하는 것이 바로 profile 입니다. 그 다음 프로파일은 어플리케이션의 currentProfile을 세팅합니다. 그리고 어플리케이션은 그와 관련된 것들 (model,view,controller 기타 관련된 클래스들)을 모두 로드하기 위해 준비합니다. 이것은 현재 실행중인 프로파일에 맞게 필요한 것들을 조합하는 작업들을 하게 됩니다.

예를 들어 애플리케이션이 몇개의 model들과 몇개의 view들을 가지고 있다고 가정해 봅시다.

Ext.application({
    name: 'Mail',

    profiles: ['Phone', 'Tablet'],

    models: ['User'],
    views: ['Navigation', 'Login']
});


이렇게 되면 폰에서 앱을 로드할 때 Phone 프로파일이 activated 되고 어필리케이션은 아래 파일들을 로드하게 될 겁니다.

- app/model/User.js
- app/view/Navigation.js
- app/view/Login.js
- app/view/phone/Main.js


처음 세개의 아이템들은 Application 그 자체에 맞춰진 것들입니다. User 모델에 Navigation 뷰와 Login 뷰가 로드될 겁니다. 네번째 아이템이 바로 Phone 프로파일에만 해당하는 아이템입니다. 여기서 Phone에 맞는 form이 로드될 겁니다. 일반적으로 클래스들이 들어가는 폴더는 해당하는 프로파일의 이름을 사용하게 됩니다. 예를 들어 Phone 에 맞는 form 을 가지고 있는 Main 뷰는 app/view/phone/Main.js 경로에서 로드 될 겁니다. 그렇지 않고 프로파일에 속하지 않는 Main 뷰는 app/view/Main.js 경로에서 불려지게 될 겁니다.

models, views, controllers, stores 같은 프로파일에서 로드된 것들도 마찬가지 입니다. 이것은 behabior, view 로직들을 서로 공유할 수 있게 하므로 아주 중요한 부분입니다. 이 프로파일에 해당하지 않는 클래스들을 로드하려면 대신에 전체 클래스 경로와 이름을 넣어주시면 됩니다.

Ext.define('Mail.profile.Phone', {
    extend: 'Ext.app.Profile',

    config: {
        name: 'Phone',
        views: ['Main', 'Mail.view.SpecialView'],
        models: ['Mail.model.Message']
    },

    isActive: function() {
        return Ext.os.is.Phone;
    }
});


위에서 보듯이 전체 경로를 적는 클래스 이름을 명시할 수도 있고 (예: Mail.view.SomeView) 특정 클래스 이름만 적을 수 있고 ( Main - Mail.view.phone.Main 을 간략하게 표현) 또 이를 Mix 해서 사용할 수도 있습니다. 프로파일에 속한 모든 models,views,controllers, stores 는 이 방법을 사용해야 함을 명심하세요. 만약 태블릿 디바이스일 경우에만  로드되기를 원하는 Models 나 Stores 가 있다면 그리고 Mail.model.tablet.User 같은 식으로 클래스를 사용하고 싶지 않다면 fully-qualified 클래스 이름을 사용해야 합니다. (예. Mail.model.User)

The Launch Process


Profile을 이용한 launch 프로세스는 profile을 이용하지 않는 경우와 전혀 다를 것이 없습니다. Profile base 앱은 3단계의 launch process가 있습니다. 해당 프로파일과 관련된 클래스들이 로드 된 이후 다음과 같은 일들이 일어납니다.

1. Controllers 들이 instantiat 됩니다. 각 Controller들의 init 함수가 call 됩니다.
2. Profile의 launch 함수가 call 됩니다.
3. 어플리케이션의 launch 함수가 call 됩니다.


Profile을 사용할 때는 앱의 initial ui를 생성하기 위해 Profile launch 함수를 사용하는것이 일반적입니다. 많은 경우 Application의 launch 함수는 해당 프로파일의 initial UI가 다르다면 remove되고 Profile launch 함수의 initial UI가 실행 됩니다. (하지만 이 Application launch 함수에는 analytic이나 다른 profile-agnostic 셋업 등은 계속 존재 합니다.)

일반적인 Profile launch 함수는 아래와 같습니다.

Ext.define('Mail.profile.Phone', {
    extend: 'Ext.app.Profile',

    config: {
        name: 'Phone',
        views: ['Main']
    },

    isActive: function() {
        return Ext.os.is.Phone;
    },

    launch: function() {
        Ext.create('Mail.view.phone.Main');
    }
});


profile과 Application launch 함수들은 optional 한 겁니다. 이 부분을 정의하지 않으면 단지 call이 안될 뿐입니다.

Specializing Views

Most of the specialization in a Profile occurs in the views and the controllers. Let's look at the views first. Say we have a Tablet Profile that looks like this:
Profile 이 적용 되면 대부분 View나 Controller 부분이 그 프로파일에 맞는 클래스가 로드되야 합니다. view 부터 먼저 살펴 보겠습니다. 아래와 같은 Tablet Profile이 있다고 가정해 봅시다.


Ext.define('Mail.profile.Tablet', {
    extend: 'Ext.app.Profile',

    config: {
        views: ['Main']
    },

    launch: function() {
        Ext.create('Mail.view.tablet.Main');
    }
});


When we boot this app up on a tablet device, the file app/views/tablet/Main.js will be loaded as usual. Here's what we have in our app/views/tablet/Main.js file:
이 앱을 태블릿 기기에서 시작을 할 때 app/views/tablet/Main.js 파일이 로드될 겁니다. 이 Main.js 파일은 아래와 같습니다.

Ext.define('Mail.view.tablet.Main', {
    extend: 'Mail.view.Main',

    config: {
        title: 'Tablet-specific version'
    }
});


대개 view 클래스는 Sencha Touch에서 제공하는 view 를 extend 하게 되는데요 이 예제의 경우는 Mail.view.Main 을 Extend 했습니다. 이것은 자체적으로 만든 view 입니다. 아래 그 예제 코드가 있습니다.

Ext.define('Mail.view.Main', {
    extend: 'Ext.Panel',

    config: {
        title: 'Generic version',
        html: 'This is the main screen'
    }
});


이렇게 되면 우리는 Mail.view.Main이라는 수퍼클래스가 있고 수퍼클래스를 기반으로 customize 될 수 있는 Profile-specific subclass (Main.view.tablet.Main)를 가지게 됩니다. 여기서 우리는 Phone 인 경우 title 을 Generic version 에서 Phone-specific version으로 바꿀 겁니다.

이것은 일반적인 클래스이기 때문에 유연성있는 config 시스템을 이용해서 수퍼클래스의 어떤 부분이든지 쉽게 customizing 할 수 있습니다. 예를 들어 phone 버전도 있다고 하면 이 phone 버전의 Main view는 아래와 같이 customizing 할 수 있습니다. (app/view/phone/Main.js);

Ext.define('Mail.view.phone.Main', {
    extend: 'Mail.view.Main',

    config: {
        title: 'Phone-specific version',

        items: [
            {
                xtype: 'button',
                text: 'This is a phone...'
            }
        ]
    }
});


반응형

Comment

  1. Lipozene 2013.05.21 08:59

    바람직하게는, 기사는 정말 주목할만한 주제에 가장 적합합니다. 나는 당신의 결론과 조화 thirstily하여 접근 업데이 트를 기대합니다

  2. Lipozene 2013.05.23 00:14

    이제 다음이 같은 게시물 우연히 발견거야, 그리고 난 정말 웹에서 재미있는 페이지가 여전히 있다는 것을 기억할 것입니다. ^ _ ^. 감사합니다.


지난번에 보았던 센차 터치 쇼핑몰 소스를 분석해 보겠습니다.

일단 Index.html 을 볼께요.

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Shopping List</title>

    <link rel="stylesheet" href="css/app.css" type="text/css">
</head>

head 부분을 보시면 css를 css/app.css 를 사용하는 것을 볼 수 있습니다.


<body>
    <!-- In production use sencha-touch.js  -->
    <script type="text/javascript" src="sencha-touch/sencha-touch-1.1.0/sencha-touch-debug.js"></script>


이 부분을 보니까 이 소스코드는 sencha touch 1.1.0 버전을 사용하는 군요. sencha-touch-debug.js 파일을 불러오고 있습니다. 이 부분은 head 안에 있어도 되는데 이 소스코드에서는 body  첫 부분에 넣었군요. 이 js 파일은 따로 분석할 필요는 없습니다. 이 파일에 sench touch 의 수 많은 기능이 있는 파일이니까 그냥 계속 복사하면서 사용하시면 됩니다.


    <!-- Application starting point  -->
    <script type="text/javascript" src="js/app.js"></script>

    <!-- controllers -->
    <script type="text/javascript" src="js/controllers/Controller.Item.js"></script>

    <!-- models -->
    <script type="text/javascript" src="js/models/Model.Item.js"></script>
    <script type="text/javascript" src="js/models/Model.Unit.js"></script>

    <!-- stores -->
    <script type="text/javascript" src="js/stores/Store.Items.js"></script>
    <script type="text/javascript" src="js/stores/Store.Unit.js"></script>

    <!-- templates -->
    <script type="text/javascript" src="js/templates/Template.Item.js"></script>
    <script type="text/javascript" src="js/templates/Template.ItemDetail.js"></script>

    <!-- views -->
    <script type="text/javascript" src="js/views/View.ItemDetail.js"></script>
    <script type="text/javascript" src="js/views/View.ItemForm.js"></script>
    <script type="text/javascript" src="js/views/View.ItemList.js"></script>
    <script type="text/javascript" src="js/views/View.Viewport.js"></script>
</body>
</html>


body 부분에선 주로 js 파일을 불러오는 군요. 그래서 통일성을 주려고 sencha-touch-debug.js 도 body 안에서 불러온 것 같습니다.
괜찮은데요. 저도 센차터치로 프로그래밍을 할 때 이 프레임워크를 사용하게 될 것 같네요.

보시면 맨 처음에 앱이 시작할 때 참조하게 될 app.js를 지정해 줬습니다.

그리고 나서 MVC 패턴에 맞게 분리된 js 파일들을 불러오고 있습니다.

총 5개의 카테고리로 나눴네요. 맨 먼저 controllers 그리고 models, stores, templates, views 와 관련된 js 파일들을 불러옵니다.

폴더들은 이 카테고리에 맞게 js 폴더 밑에 따로 만들어진 것이 보이죠?

폴더 구조는 아래와 같습니다.


폴더 구조는 크게 css, img, js, scss 4가지 카테고리가 있습니다. sencha-touch 는 센차 터치에서 제공하는 파일들이구요. 이건 소스 분석 대상은 아니고 그냥 가져다가 사용하면 되는 센차터치 콘테이너죠. STEPS는 개발자가 이 소스코드를 설명하기 위해 강좌에 쓰인 각 개발 단계별 소스르 넣은 폴더 이구요.

제가 보고 싶은 부분은 js 폴더 였습니다.
MVC 모델을 이런식으로 사용하는 군요.
Model, View, Controller 가 기본적으로 있고 그 이외에 stores 폴더가 눈에 띕니다. local storage 기능을 사용할 거니까 이와 관련된 로직은 이 폴더 밑의 js로 따로 코딩을 했나 봅니다.
그리고 build,templates 가 있네요. 아직까지는 정확히 어떤 기능들을 따로 분리해 놓은 폴더인지는 모르겠지만 센차터치에서는 이렇게 js 파일들을 기능별로 나눠 관리하면서 MVC 모델을 사용하면 되는 것 같습니다.

이제 제가 이 소스코드를 통해서 얻고 싶은것의 80%는 다 공부 한 것 같습니다.

(참고로 scss는 css를 사용할 때 변수등을 사용해서 보다 간편하게 코딩할 수 있도록 도와주는 스크립트 언어입니다. 관심 있으신 분은 따로 찾아서 공부하셔도 좋을 거예요.)

index.html을 보았으니까 앱을 시작했을 때 가장 먼저 보게 될 app.js 파일을 보겠습니다.

이 app.js는 js 폴더 바로 밑에 있습니다. 같은 depth에 바로 models, views, controllers, stores ... 등등의 폴더가 있고 그 폴더 밑에 다른 js 파일들이 있게 됩니다.

Ext.regApplication('App', {
    name         : 'App',
    useLoadMask : true,
    phoneStartupScreen: './img/iStock_000004720241XSmall.jpg',

    launch: function() {
        this.viewport = new App.View.Viewport({
            application: this
        });

        Ext.dispatch({
            controller: 'Item',
            action    : 'showList'
        });
    }
});

음 이 app.js 파일을 보니까 이전에 본 구조이긴 하지만 Corona SDK 처럼 딱 한 눈에 해석이 되지 않네요.

센차 터치 튜토리얼을 좀 더 공부 해서 이 정도는 그냥 쉽게 해석이 되도록 해야 겠어요.
제 경험으로는 프로그래밍이든 뭐든 머리로 이해하려고 하는 것보다 계속 계속 반복해서 몸으로 이해하는게 더 좋은것 같더라구요.. ^^

찾아보니까 Ext.regApplication 는 1.1.0에서는 사용했지만 2.0.0 부터 없어진 메소드라고 하네요. 그래서 더 생소했나 봅니다. 2.0.0에서는  Ext.create 을 사용하면 될 겁니다. 그리고 그 다음에 Panel을 사용하죠. 지난 글에서 View를 할 때 아래 소스를 이용 했었습니다.

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

그 다음에 launch: function()은 눈에 익네요. 튜토리얼 몇개 정리했더니  이 부분은 조금 감이 잡힙니다.

이 앱의 viewport를 설정하고 그 다음에 Ext.dispatch 가 있습니다. 이 부분도 2.0.0 부터는 없어진 메소드 입니다. 2.0.0 에서는 Ext.app.Application.dispatch 를 사용하시면 됩니다.

일단 애초 이 소스코드를 통해서 제가 배우고자 했던 것은 센차 터치로 MVC 모델 사용하는 것 과 실전에서 자주 사용되는 메소드를 익혀서 튜토리얼 공부할 때 좀 더 집중하고자 하는 거였는데요.

첫번째 목표는 달성했고.. 두번째는 이 소스코드가 1.1.0 을 기반으로 한 거라서 2.0.0 과는 많이 다르네요.

이 소스코드 분석은 이정도로 하고 계속 튜토리얼 공부를 하도록 하겠습니다.

어쨌든 센차터치로 앱 만들 때의 기본적인 프레임워크에 대해서는 충분히 새로운 것을 배운 것 같습니다.

튜토리얼 공부를 통해서 기본 지식을 좀 더 쌓고 여러 샘플 코드들을 분석해 보아야겠습니다.

그럼 다음 시간에 뵐께요.

반응형

Comment


오랜만에 Sencha Touch를 들여다 보게 됐네요.
몇개의 튜토리얼을 정리했는데요. 아직 제대로 제가 감을 못 잡은 것 같아서요.

오늘은 샘플코드를 구해서 분석해 봤습니다.
Shopping List 를 Sencha Touch 로 구현한 샘플코드인데 독일의 Nils Dehl 이라는 개발자가  프랑크프루트 컨퍼런스에서 강의한 코드입니다.

일단 저도 Sencha Touch를 시작하는 사람으로서 이 소스코드를 자세하게 분석하지는 못합니다. 이 소스코드를 통해서 제가 공부할 목표는 아래 두가지 입니다.

1. MVC 모델 프로그래밍을 위한 디렉토리 및 파일 구조 파악
2. 실전에서 자주 쓰이는 API에 대한 감을 익혀서 튜토리얼 공부할 때 집중할 수 있도록 하기

이 이상은 지금 수준에서는 아직 할수가 없다고 봅니다.

일단 이 앱의 Storyboard 를 보죠.

1. 이 앱의 인트로 화면입니다. 실행하면 이 화면이 떴다가 잠시 후에 자동으로 다음 화면으로 넘어갑니다.



2. 앱의 첫 화면입니다. + 를 누르면 데이터를 입력할 수 있는 화면이 나옵니다.



3. 이름과 갯수 그리고 단위를 입력하고 Save를 누르면 2번화면으로 가고 입력한 데이터가 표시됩니다.



4. 3번화면을 통해서 여러 물품을 입력하면 아래와 같이 그 리스트가 뜹니다.
    오른쪽 마크 버튼을 누르면 색이 변합니다.


화면은 몇개 안 되지만 직접 만들려면 만만치 않을것 같네요.

그럴듯하게 쇼핑카트가 완성 됐죠?

이 앱에서는 MVC 모델과 list 그리고 localstorage, form 과 관련된 기술이 사용됐습니다.
그리고 CSS를 보다 간편하게 사용하기 위해서 SASS 를 사용했구요.

자세히는 아니지만 이 기술들도 간단히 살펴 보겠습니다.
아직 저는 센차터치의 MVC 구성과 실전에서 자주 사용되는 API와 그 사용법 파악이 주 목적이니까 그 부분에 집중해서 공부하겠습니다.

이 앱의 소스파일은 아래로 가시면 받아 보실 수 있습니다.

http://dl.dropbox.com/u/7549925/shoppinglist.zip

소스는 받아서 그냥 실행하시면 됩니다.
이 앱은 Sencha Touch 1.1.0을 사용했습니다.
그리고 Step1부터 Step 7 까지 단계별로도 소스를 따로 모았습니다.

이 단계는 컨퍼런스 때 강의를 하기 위해 나눈것인데요.
동영상으로도 있습니다.


Creating a sample Touch app from Grgur Grisogono on Vimeo.

오늘은 위에 동영상까지 올리겠습니다.

소스 받아서 보시고 동영상도 보시면 많은 공부가 될 겁니다.

저도 공부해서 나름대로 정리할 내용을 정리할 계획입니다.

지난주에는 Corona SDK + JQuery Mobile + HTML5 + CSS3 기술을 이용한 앱을 하나 만들었습니다.
Corona SDK를 이용한 Hybrid App 을 만들 목적이었는데요.
좋은 조합일 수도 있고 또 제한 사항들이 있어서 컨트롤하기 힘든 부분도 있더라구요.
원래 standard 조합인 Phonegap + JQuery Mobile 을 이용해서 Hybrid App을 만들어 보아야 겠어요.
그래야지 비교할 수 있고 서로간의 장 단점을 알 수가 있을 것 같아서요.

일단 오늘은 이 쇼핑카트 앱 소스를 공부해서 필요한 부분을 정리해야겠습니다.

다음 글에 제가 공부한 내용을 올릴께요. 그럼......


P.S. 이 소스는 저의 경우 크롬과 사파리 브라우저에서만 동작하고 오페라,익스플로러,파이어폭스에서는 작동 안하더라구요.

반응형

Comment

  1. 앱 개발 쉽지 않죠 ! 개발자분들에게 감사의 말씀 드려요 !

    • 솔웅 2012.04.05 18:37 신고

      저두요.. 이렇게 댓글 남겨 주셔서 정말 감사드려요.. ^^
      앱 개발이 쉽지는 않은데 재밌어요..

Sencha Touch 2 Tutorial - View - 02

2012. 3. 12. 05:54 | Posted by 솔웅


Using Views in your Applications 02

Custom Configurations and Behavior

Sencha Touch 2는 예측 가능한 API들을 제공하고 깔끔한 코드와 쉬운 테스트가 가능하도록 하하기 위해 configuration system에 extensive를 사용합니다. 개발자들에게도 클래스를 만들 때 같은 방식으로 만들기를 강하게 권장합니다.

이미지를 tap했을 때 이미지에 대한 정보가 팝업으로 뜨는 이미지 뷰어를 한번 만들어 봅시다. 우리의 목표는 이미지 url, title 그리고 description이 있을 수 있는 재사용 가능한 뷰를 만드는 것입니다. 그리고 이 이미지를 tap했을 때 title과 description을 표시해 줄 겁니다. 이 모든 기능을 재사용 가능하도록 하는 클래스를 만들어 보겠습니다.

이미지를 표시하기 위해서는 Ext.Img 컴포넌트를 사용해야 합니다. 아래처럼 subclass를 만들어 보겠습니다.

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.onTap, this);
    },

    //this is called whenever you tap on the image
    onTap: 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
});



이 소스를 보시면 우리의 클래스에 두개의 configuration들을 달았습니다. 이 두개는 null로 정의했습니다. 이 새 클래스의 인스턴스를 생성할 때 title과 description config를 pass 할 겁니다.

초가화와 onTap 함수가 일어날 때 어떤 동작들을 하게 됩니다. initialize 함수는 컴포넌트가 instant될 때 불려 집니다. 그러니까 이 위치는 이벤트 리스너와 같은 behavior를 셋업하기 아주 좋은 장소 입니다. 첫번째로 우리가 해야 할 일은 이 initialize 함수를 사용하는 것 입니다. (역자주:자바의 생성자가 연상되네요.) callParent(인수) 는 initialize 함수를 부른 superclass 입니다. 이 부분은 아주 중요합니다. 이것을 빼먹으면 여러분의 component는 정확하게 동작하지 않을 겁니다.

callParent를 한 다음에 tap 리스너를 달았습니다. 이 리스너를 단 객체를 tap 하게 되면 onTap 함수가 call 될 겁니다. Sencha Touch 2이 모든 컴포넌트들은 이런 식으로 DOM 객체나 styling을 추가하거나 제거할 때 또는 Ext.dom.Element에서 하는 일반적인 작업들에 대한 이벤트를 listen하기위해 사용할 수 있는 한개의 element property 가 있습니다.

onTap 함수는 아주 간단합니다. 이 함수를 부시면 이 이미지에 대한 정보를 pop up으로 보여주기 위해 Ext.Msg.alert을 사용했습니다. 여기서 title과 description 두개의 config들을 봅시다. 둘 다 getter 함수를 receive 받습니다. (getTitle, getDescription) 이렇게 되면 setter 함수(setTitle, setDescription)도 generate 되게 됩니다.


Advanced Configurations

클래스에 새 configuration option을 생성할 때 getter와 setter 함수들이 생성됩니다. 그래서 아래 코드에서 border는 자동적으로 getBorder와 setBorder 함수를 받게 됩니다.

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만 생성되는 것은 아닙니다. applyBorder와 updateBorder도 같이 생성이 됩니다.

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);
    }
});

우리의 applyBorder함수는 border configuration이 세팅되거나 바뀔 때 내부적으로 불려지게 됩니다. 여기에 유저가 (혹은 앱 실행중에) 값을 바꿀 때 어떤 변화를 주는 동작을 넣기에 아주 좋습니다. 이 예제에서는 border 의 width를 받아서 CSS border specification string에 전달을 해 줄 겁니다.

예를 들어 border 가 10dl ehlaus applyBorder 함수는 10픽셀의 빨간 선으로 만들어 줄 겁니다. 이 apply 함수는 선택사항입니다. 여기에는 반드시 return 값이 있어야 됩니다. 그렇지 않으면 아무 동작도 일어나지 않습니다.

updateBorder 함수는 applyBorder 함수가 값을 변경하고 나서 불려집니다. 이것은 대개 DOM을 수정하고 AJAX request를 send 할 때 혹은 다른 프로세스 종류를 수행하기 위해 사용됩니다. 이 예제에서는 단지 view의 element를 get 해서 setStyle을 이용해서 border style을 업데이트 할 겁니다. 그러면 setBorder가 call 될 때마다 우리의 DOM은 새로운 스타일을 반영하기 위해 즉시 업데이트 될겁니다.

아래 예제가 있습니다.
화면 위에 border의 width를 변경할 수 있도록 +,- spinner button을 달았습니다. Spinnerspin 이벤트를 받아서 우리가 만든 view의 새로운 setBorder 함수를 call 할 겁니다.

//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,
        incrementValue: 1,
        listeners: {
            spin: function(spinner, value) {
                view.setBorder(value);
            }
        }
    }
});



Usage in MVC

애플리케이션을 만들 때 MVC 모델을 따를 것을 권장합니다. 그러면 이 애플리케이션의 코드는 잘 구성되고 재사용하기 쉽도록 만들어 지게 될 겁니다. 그리고 위에서 사용했듯이 naming을 잘 사용하면 아주 심플하게 사용하기 쉽습니다.

위의 MyApp.view.MyView 클래스는 app/view/MyView.js 에 작성 되어야 합니다. 이래야지 어플리케이션이 자동적으로 이 클래스를 찾아서 로드를 할 수 있습니다. MVC 기반의 Sencha Touch 앱의 파일 구조에 익숙하지 않으시더라고 간단히 배울 수 있습니다. Sencha Touch app은 단지 하나의 html 파일과 하나의 app.js 파일이 있고 model과 view 그리고 controller들이 있게 됩니다. 이 세 요소들은 app/model, app/view 그리고 app/controller 디렉토리에 있게 됩니다.

index.html
app.js
app/
    controller/
    model/
    view/
        MyView.js
       
여러분은 여러분이 만들고 싶은 만큼의 view를 만들 수 있고 이 파일을 app/view 디렉토리에 넣으시면 됩니다. 그리고 이 것을 app.js안에 specifying 하시면 이것들은 자동적으로 로드 될 겁니다.

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

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

이 간단한 view naming convention을 따름으로서 우리는 애플리케이션 안에 우리가 만든 뷰 클래스들의 인스턴스를 쉽게 로드하고 생성할 수 있게 됩니다. 위에 있는 예제들이 이 convention을 따랐습니다. 어플리케이션의 launch 함수에서 MyView 클래스를 로드하고 인스턴스를 생성했습니다. Sencha Touch 의 MVC 앱에 대해 좀 더 많은 것을 알고 싶으시면 intro to apps guide를 보세요.

반응형

Comment

  1. 구르는돌 2012.03.27 18:12

    포스팅 늘 감사드립니다.
    알기쉽게 잘설명해주시네요
    항상 좋은일만 가득하시길..

Sencha Touch 2 Tutorial - View - 01

2012. 3. 8. 04:34 | Posted by 솔웅


이번주에 사무실이 이사갑니다.
짐을 다 싸 놔서 인터넷도 못하네요.

이제 봄도 되고 사무실도 새로운 곳으로 옮겨지니 새 기분으로 열심히 일 하자고 다짐해 보게 됩니다.

오늘은 Sencha Touch 2.0의 View에 대해서 공부하겠습니다.
오늘 그 내용을 다 다루지는 못하구요. 두번에 나눠서 다루겠습니다.

======= o ======= o ======= o ======= o ======= o

Using Views in your Applications

유저 입장에서 보면 어플리케이션은 단지 view의 모임들일 뿐입니다. 앱에 Model과 Controller가 아주 중요한 역할을 하지만 View가 바로 유저에게 직접 보여지는 부분입니다. 오늘은 이런 뷰를 어떻게 만드는지에 관해 공부해 보겠습니다.

Using Existing Components

View를 생성하는 가장 쉬운 방법은 Ext.create를 사용하는 겁니다. 예를 들어 HTML과 Panel을 만들기를 원한다면 아래처럼 하면 됩니다.

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



예제를 보시면 HTML이 들어간 이 Panel은 전체 화면을 차지하도록 돼 있습니다. Panel이외에도 Sencha Touch에서 제공하는 모든 컴포넌트들을 이런식으로 사용하시면 됩니다. 하지만 바람직한 방법은 특별히 구현하고자하는 것을 subclass로 만들고 나서 생성하는 방법입니다. 아래 예제를 보시죠.

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

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

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

phone에 나오는 화면은 이전과 똑 같습니다. 하지만 다른 점은 새로운 컴포넌트를 가지게 됐습니다. 이 컴포넌트 안에서 여러 작업을 할 수 있습니다. 이것이 일반적으로 앱을 만들때 사용하는 패턴입니다. component가 있는 subclass를 만들고 instance는 나중에 만드는 겁니다. 그럼 어떤게 바뀌었는지 살펴 볼까요?

- Ext.define은 새로운 class를 만들 때 사용합니다. 그리고 그 안에서 extend를 이용해서 Ext.panel 같은 컴포넌트들을 불러와서 사용할 수 있습니다. (Componen에는 4가지가 있는데 Navigation, Store-bound, Form, General Components 가 있습니다. 자세한 내용은 여기 를 참고하세요.)
- MyApp.view.MyView 형식으로 새 뷰 클래스를 만들었습니다. 이 형식은 개발자 마음대로 사용하셔도 되지만 이 형식을 유지할 것을 권장합니다.
- 이 새 클래스에서 우리는 config 를 정의했습니다.
이 config는 subclass의 config block에서 정의하셔도 되고 create() 로 instance를 만들 때 정의하셔도 됩니다.

아래 예제에서는 subclass가 아니라 create() 안에서 object를 pass하는 것을 보여 줍니다.
결과 화면은 CSS가 적용되서 보기 좋은 폰트가 display 됩니다.




A Real World Example

아래 예제는 Sencha Touch 의 Twitter 앱에서 실제 사용한 view class 중 한 부분 입니다.
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...'
            }
        ]
    }
});

이 예제는 바로 전 예제와 같은 패턴을 따르고 있습니다. 새로운 클래스로 Twitter.view.SearchBar를 생성했습니다. 이 클래스는 framwork의 Ext.Toolbar 클래스를 extends 했습니다. 그리고 몇가지 configuration 옵션이 있습니다. layout하고 items array 입니다.

몇개 새롭게 나온 옵션들에 대해서 알아보죠.

: requires - items array를 위해 searchbar를 사용하기 때문에 이 새 뷰에 Ext.field.Search 클래스를 사용할거라고 알려줘야 합니다.
: xtype - 새 클래스 만의 xtype을 줍니다. 간편하게 configuration object를 생성하도록 합니다. 이렇게 함으로서 우리는 여러 방법으로 새로운 뷰 클래스의 인스턴스를 생성할 수 있습니다. 아래와 같이 이 xtype을 이용하시면 편리하게 방금 만든 새로운 클래스를 쉽게 재 사용하실 수 있습니다.

//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'
        }
    ]
});

======= o ======= o ======= o ======= o ======= o

다음 시간에 나머지 View 부분 마저 다루겠습니다.

오늘도 추천 한방씩 ~~~~~ ~~~~~ 부탁드려여...
반응형

Comment

  1. 훅스 2012.03.26 02:32

    마지막 예제는 아래와 같이 해야 잘 나오네요 ^^
    Ext.application({
    name: 'Sencha',

    launch: function() {
    Ext.create('Twitter.view.SearchBar', {
    html: 'Welcome to my app',
    fullscreen: true,
    });
    Ext.create('Ext.Panel', {
    items: [
    {
    xtype: 'searchbar',
    docked: 'top'
    }
    ]
    });
    }
    });

    • 솔웅 2012.04.05 05:11 신고

      아 예.. 그렇네요...
      정말 감사합니다. 도움 많이 되었어요.. ^^ 앞으로도 계속 도움 부탁드려요.. .^^

Sencha Touch 2 Tutorial - Controllers -

2012. 3. 5. 19:19 | Posted by 솔웅


Controllers

Controller는 어떤 event가 일어 났을 때 다른 어떤 동작이 일어날 수 있도록 Control 하는 역할을 합니다. 만약 앱에 로그아웃 버튼이 있다면 유저는 이 버튼을 tap 할테고 Controller는 이 버튼에 대한 tap event를 listening 하고 있다가 이벤트가 발생하면 로그아웃 시키는 동작을 하도록 합니다. 이러한 기능은 View 클래스가 보여 주는 데이터를 변경하도록 할 수 있고 또 Model 클래스가 데이터를 로딩,저장 등의 동작을 할 수 있도록 해 줍니다. Controller는 이 사이에서 그런 동작들이 자연스럽게 이뤄질 수 있도록 Control 해 줍니다.

Relation to Ext.app.Application

Controller는 어플리케이션의 context 안에 존재합니다. 하나의 어플리케이션은 대개 어떤 특정한 부분을 handle 하는 여러 Controller들로 구성 돼 있습니다. 예를 들어 온라이 쇼핑 싸이트에서 주문을 하는 앱을 생각해 보면 이 앱에는 주문과 고객 그리고 상품들에 대한 Controller들이 있을 겁니다.

모든 컨트롤러는 그 어플리케이션의 Ext.app.Application.controller config에서 정해 주시면 됩니다. 이 어플리케이션은 각 컨트롤러를 자동적으로 instantiate 시켜주고 계속 참조하게 됩니다. 그래서 컨트롤러를 직접 instantiate 시키는 상황은 특별한 경우에만 한합니다. convention에 의해 컨트롤러들은 명명되는데 대개 Model 작업 수행 이전에 복수개의 컨트롤러들에 대해 이 작업이 이뤄집니다. 예를 들어 MyApp이라는 앱이 있고 여기에 컨트롤러가 Product를 관리한다면 convention은 app/controller/Products.js라는 파일에 MyApp.controller.Products 라는 클래스를 생성합니다.

Launching

어플리케이션이 launch 될 때 4가지의 주요 단계를 거치게 됩니다. 2가지는 컨트롤러 안에서 행해집니다. 첫번째로 각각의 컨트롤러는 init 함수를 정의할 수 있게 됩니다. 이것은 Application launch 함수 이전에 call 됩니다. 두번째는 Application과 Profile launch 함수가 call 된 이후인데요, 프로세스의 마지막 단계로 컨트롤러의 launch 함수가 call 됩니다.

    Controller#init functions called
    Profile#launch function called
    Application#launch function called
    Controller#launch functions called

대개 Controller-specific launch 로직은 Controller의 launch 함수 안에 있어야 합니다. 왜냐하면 이것은 Application과 Profile launch 함수 이후에 call 되기 때문입니다. 바로 이 시점에 initial UI가 있게 됩니다. 만약 app launch 이전에 Controller-specific processing이 필요하다면 Controller init 함수를 implement 할 수 있습니다.

Refs and Control

컨트롤러에서 중요한 두가지는 refscontrol configuration입니다. 이 둘은 앱의 Component들에 쉽게 reference들을 얻을 수 있게 해주고 어떤 이벤트가 발생하면 이에 대해 어떤 동작이 일어날 수 있도록 해 줍니다. refs 먼저 보겠습니다.

Refs

Refs는 아주 강력한 ComponentQuery 신택스에 영향을 주는것으로 각 페이지에 쉽게 Comopnent들을 위치시킬 수 있도록 합니다. 각 콘트롤러에 대해 원하는 만큼의 refs를 정의할 수 있습니다. 예를 들어 아래 예제에서는 maniNav라는 아이디라는 Component를 찾는 nav라 불리는 ref를 정의합니다. 그 다음에 그 아래에 addLogoutButton 안의 ref를 사용합니다.

Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            nav: '#mainNav'
        }
    },

    addLogoutButton: function() {
        this.getNav().add({
            text: 'Logout'
        });
    }
});

대개 ref는 key/value 조합입니다. 키(위의 경우 nav)는 reference의 이름으로 ref를 생성하기 위해 사용됩니다. 그리고 값(위의 경우 #mainNav)는 ComponentQuery selector로 Componet를 찾을 때 사용 됩니다.

그 아래에 addLogoutButton이라는 간단한 함수를 call 했습니다. 이것은 getNav 함수를 발생시키면서  이 ref를 사용하게 될 겁니다. 이런 getter 함수들은 당신이 정의한 refs를 바탕으로 발생됩니다. 이 함수의 이름은 get 다음에 ref의 대문자(NAV)를 붙여서 이름을 만드는 규칙을 따릅니다. 이 경우엔 nav reference가 툴바이고 함수가 호출 돼었을 때 이 툴바에 Logout 버튼을 추가하도록 했습니다. 이 ref는 아래와 같이 툴바를 인식할 겁니다.

Ext.create('Ext.Toolbar', {
    id: 'mainNav',

    items: [
        {
            text: 'Some Button'
        }
    ]
});

우리가 addLogoutButton 함수를 run 할 때 이미 툴바가 생성돼 있었다고 가정합시다. (이 과정이 어떻게 진행되는지는 나중에 볼 겁니다.) 이 경우에는 두번째 버튼이 생길 겁니다.

Advanced Refs

Refs는 name과 selector 이후에 몇개의 추가 옵션을 설정할 수 있습니다. autoCreate, xtype 등이 그것인데 대부분 같이 사용 됩니다.

Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            nav: '#mainNav',

            infoPanel: {
                selector: 'tabpanel panel[name=fish] infopanel',
                xtype: 'infopanel',
                autoCreate: true
            }
        }
    }
});

이제 우리의 Controller에 두번째 ref를 add 했습니다. 마찬가지로 이름이 key 입니다. 이경우에는 infoPanel이 되겠죠. 여기서는 value(값) 대신 object를 pass 하고 있습니다. 조금 더 복잡한 selector query도 있죠? 한번 상상해 보세요. 앱이 tab panel을 가지고 있고 그 tab 판넬의 아이템 중 하나가 이름이 fish라구요. 위 예제의 selector는 tab panel 아이템 안에 infopanel이라는 xtype을 가진 Component를 match 할 겁니다.

이 예제에서 좀 다른 점은 infopanel이 fish panel 안에 존재하지 않는다면 Controller안에서 this.getInforPanel을 call 할 때 자동적으로 생성하게 될 겁니다. 그 이유는 이 컨트롤러의 selector가 아무것도 return 하지 않고 event 시 instantiate 하도록 xtype을 제공하기 때문입니다.

Control

refs config와 짝을 이루는 것이 바로 control입니다. 콘트롤은 콤포넌트에 의해 일어나는 이벤트나 Controller의 어떠한 react를 listening 하는 수단입니다. Control은 자신의 key로 Component ComponentQuery selectors 와 refs를 받습니다. 아래 예제가 있습니다.

Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        control: {
            loginButton: {
                tap: 'doLogin'
            },
            'button[action=logout]': {
                tap: 'doLogout'
            }
        },

        refs: {
            loginButton: 'button[action=login]'
        }
    },

    doLogin: function() {
        // called whenever the Login button is tapped
    },

    doLogout: function() {
        // called whenever any Button with action=logout is tapped
    }
});

이 예제에서 우리는 두개의 control을 정의했습니다. 하나는 loginButton ref를 위한 것이고 다른 하나는 logout 기능을 할 한 버튼을 위한 겁니다. 이 각각의 정의에 하나의 이벤트 핸들러를 넣었습니다. 바로 tap 이벤트에 대해 listening 하도록 했습니다. 이 버튼들에 tap 이벤트가 발생하면 어떤 동작이 일어날 겁니다. tap 다음에 doLogin과 doLogout이 있죠? 이건 함수 이름입니다. 어딘가에 이 함수가 있고 그 안에 필요한 기능들이 코딩 돼 있을 겁니다. 중요한 부분이죠.

각각의 control 정의할 때 원하는 만큼의 이벤트 리스너를 달 수 있습니다. 그리고 키로서 key로서 ComponentQuery selector와 refs를 섞어서 매치할 수 도 있습니다.

Routes

Sencha Touch 2에서 Controller는 route를 direct하게 명시할 수 있습니다. 이것은 앱 안에서 history support 제공을 가능하게 하며 앱 내에 어떤 페이지든지 직접 링크를 걸어서 가도록 할 수 있습니다. 이것이 route를 제공하기 때문에 가능한 일들 입니다.

예를 들어 로그인과 유저 프로파일을 보여주는 일을 담당하는 Controller가 있다고 합시다. 그리고 이 화면이 url로 접근 할 수 있도록 하고 싶다고 합시다. 아래와 같이 하시면 될 겁니다.

Ext.define('MyApp.controller.Users', {
    extend: 'Ext.app.Controller',

    config: {
        routes: {
            'login': 'showLogin',
            'user/:id': 'showUserById'
        },

        refs: {
            main: '#mainTabPanel'
        }
    },

    // uses our 'main' ref above to add a loginpanel to our main TabPanel (note that
    // 'loginpanel' is a custom xtype created for this application)
    showLogin: function() {
        this.getMain().add({
            xtype: 'loginpanel'
        });
    },

    // Loads the User then adds a 'userprofile' view to the main TabPanel
    showUserById: function(id) {
        MyApp.model.User.load(id, {
            scope: this,
            success: function(user) {
                this.getMain().add({
                    xtype: 'userprofile',
                    user: user
                });
            }
        });
    }
});

위에서 명시한 routes는 간단하게 브라우저 address bar를 Controller 함수로 매핑 시킵니다. 이 routes는 login route 같이 http://myapp.com/#login에 매치 되는 간단한 텍스트가 될 수도 있습니다. 또는 http://myapp.com/#user/123 같이 url에 매치되는 user/:id route 같이 wildcard를 가지고 있을 수도 있습니다. 주소가 바뀔 때마사 콘트롤러는 명시된 함수를 자동적으로 call 합니다.

showUserById 함수에서 유저 인스턴스를 첫번째로 로드하는 부분을 주의해서 보세요. route를 사용할 때마다 이 함수는 그것과 관련한 데이터나 상태 저장에 대해 완료하는 책임을 갖는 route에 의해 불려지게 됩니다. 유저가 이 url을 다른 사람에게 전달할 수도 있고 아니면 단순하게 그 페이지를 refresh 할 수도 있기 때문입니다. 그래서 지금 로드 했던 것들을 cache에서 지워버릴 필요가 있기 때문입니다. route와 관련해서 restoring state에 대한 좀 더 자세한 설명은 application architecture guides에서 보실 수 있습니다.

Before Filters

마지막으로 Routing의 context내에서 Controller가 제공하는 것은  routes를 작성하기 이전에 작성되는 filter 함수 정의 before 입니다.

Ext.define('MyApp.controller.Products', {
    config: {
        before: {
            editProduct: 'authenticate'
        },

        routes: {
            'product/edit/:id': 'editProduct'
        }
    },

    // this is not directly because our before filter is called first
    editProduct: function() {
        //... performs the product editing logic
    },

    // this is run before editProduct
    authenticate: function(action) {
        MyApp.authenticate({
            success: function() {
                action.resume();
            },
            failure: function() {
                Ext.Msg.alert('Not Logged In', "You can't do that, you're not logged in");
            }
        });
    }
});

유저가 http://myapp.com/#product/edit/123같이 url로 navigate할 때마다 컨트롤러의 authenticate 함수가 불려질겁니다. 그리고 만약 before 필터가 존재하지 않을 경우 Ext.app.Action을 pass할 겁니다. Action은 단순하게 Controller, function(이 예제의 경우 editProduct)를 표현합니다.  rmflrh url에 있는 ID 같은 다른 데이터도 표현합니다.

이제 필터는 동기적으로나 비동기적으로 필요한 일을 할 수 있습니다. 이 예제의 경우에는 어플리케이션의 유저가 제대로 로그인 정보를 주었는지에 대한 authenticate 함수를 실행합니다. 이 동작은 서버에 있는 정보와의 비교가 필요하기 때문에 AJAX request를 사용할 것이고 비동기적으로 작동될 겁니다. 인증이 성공하면 action.resume() 함수를 call 해서 다음 동작을 이어 가게 됩니다. 만약 인증되지 않으면 다시 로그인 하도록 해야 합니다.

before 필터는 어떤 특정 action이 행해지기 이전에 추가적인 클래스들을 load하기 위해 사용 될 수 있습니다. 예를 들어 어떤 동작은 드물게 실행 되어서 필요한 상황이 될 때까지 로딩을 하지 않고 싶은 경우가 있을 수 있습니다. 그러면 애플리케이션이 시작할 때 좀 더 빠르게 시작되게 할 수 있겠죠. 이를 위해 간단히 필요할 때 로드될 수 있도록 Ext.Loader를 사용하면 됩니다.

각 action에 대해 원하는 만큼의 before 필터가 명시 될 수 있습니다. 1개 이상의 필터를 사용하려면 배열을 pass 해 주면 됩니다.

Ext.define('MyApp.controller.Products', {
    config: {
        before: {
            editProduct: ['authenticate', 'ensureLoaded']
        },

        routes: {
            'product/edit/:id': 'editProduct'
        }
    },

    // this is not directly because our before filter is called first
    editProduct: function() {
        //... performs the product editing logic
    },

    // this is the first filter that is called
    authenticate: function(action) {
        MyApp.authenticate({
            success: function() {
                action.resume();
            },
            failure: function() {
                Ext.Msg.alert('Not Logged In', "You can't do that, you're not logged in");
            }
        });
    },

    // this is the second filter that is called
    ensureLoaded: function(action) {
        Ext.require(['MyApp.custom.Class', 'MyApp.another.Class'], function() {
            action.resume();
        });
    }
});

이 필터들은 순서대로 call 됩니다. 그리고 다음 단계로 가기 위해서 반드시 각각 action.resume()을 call 해야 합니다.


Profile-specific Controllers

Superclass, shared stuff:

Ext.define('MyApp.controller.Users', {
    extend: 'Ext.app.Controller',

    config: {
        routes: {
            'login': 'showLogin'
        },

        refs: {
            loginPanel: {
                selector: 'loginpanel',
                xtype: 'loginpanel',
                autoCreate: true
            }
        },

        control: {
            'logoutbutton': {
                tap: 'logout'
            }
        }
    },

    logout: function() {
        // code to close the user's session
    }
});

Phone Controller:

Ext.define('MyApp.controller.phone.Users', {
    extend: 'MypApp.controller.Users',

    config: {
        refs: {
            nav: '#mainNav'
        }
    },

    showLogin: function() {
        this.getNav().setActiveItem(this.getLoginPanel());
    }
});

Tablet Controller:

Ext.define('MyApp.controller.tablet.Users', {
    extend: 'MyApp.controller.Users',

    showLogin: function() {
        this.getLoginPanel().show();
    }
});

===== o ===== o ===== o ===== o ===== o =====

오늘도 개념적인 부분을 많이 다뤘습니다. 다음시간에는 View에 대해서 다룰 겁니다.
제목에서 알 수 있듯이 다음 시간에는 코딩하고 결과를 눈으로 확인하면서 할 수 있을 겁니다.
어쨌든 오늘 다룬 Controller도 아주 중요한 부분이니까 어느정도 이해 될 때까지 읽어 보고 다음으로 넘어가야겠습니다.


반응형

Comment

  1. nabiko 2012.07.29 19:39

    input box에 쓰여있는 글의 길이에 따라 이벤트를 동작시키려고 하는데 아무리 검색해도 tap 이벤트 밖에 이벤트가 나와있질 않아서 질문드립니다.저런 경우라면 어떤 이벤트를 key로 설정해주는게 좋을 까요?

    • 솔웅 2012.09.04 13:32 신고

      자바스크립트를 보시면 input box에 글이 입력될 때 이것을 catch 하는 이벤트가 있을 겁니다.
      그 이벤트를 걸어서 input box 안에 있는 value를 체크해서 그 길이를 체크하고 원하는 동작을 하도록 하시면 될 것 같습니다.
      저도 센차터치는 많이 해보지를 않아서 센차터치에 관련한 이벤트가 따로 있는지는 모르겠는데요.
      자바스크립트에는 그런 작업을 많이 해 봤습니다.

      관련 소스를 찾아보시면 금방 찾으실 수 있을 거예요.

      감사합니다.


오늘 다룰 주제는 Sencha Touch 2 어플리케이션에 대한 기본적인 구조와 개념들을 다룰겁니다.
이론적인 부분이라서 약간 지루할 수는 있을텐데요. 이런 기본 개념들을 잘 이해하고 넘어가면 실제 코딩할 때 많은 도움이 됩니다.

오늘 글은 Sencha Touch Beta 2에서 제공하는 Tutorial 중 All about Application을 정리 했습니다.

====== o ====== o ====== o ======= o ======= o ======

Intro to Applications with Sencha Touch 2

Sencha Touch 2 는 다양한 플랫폼에서 동작하는 어플리케이션을 만들 수 있도록 제작됐습니다.
Sencha Touch 2는 어플리케이션을 최대한 간단하게 만들도록 하기 위해 간단하면서도 강력한 어플리케이션 아키텍쳐를 제공합니다.
이 아키텍쳐는 MVC(Model View Controller) 패턴에 맞게 만들어 졌습니다.

이 패턴은 가독성 있고 테스트하기 쉽고 유지보수 하기 쉬운 코딩을 지원합니다.
이 외에 다음과 같은 기능들을 제공합니다.

- History Support : 앱 내에서 full back button을 지원하고 어느 위치에서든지 link가 걸릴수 있도록 합니다.
- Deep Linking : 앱의 어떤 화면에서도 deep link들을 공유할 수 있습니다. 그냥 웹 페이지에 링크 걸듯이 하면 됩니다.
- Device Profiles : 어플리케이션의 UI를 phone, 태블릿 기타 디바이스의 종류에 맞게 쉽게 customizing할 수 있도록 지원합니다.

Anatomy of an Application

어플리케이션은 Model,View,Controller,Store,Profile과 아이콘, launch screen 이미지 등 추가적인 metadata들의 조합입니다.




- Models : 앱에서 객체의 타입을 표시합니다. 예를 들어 e-커머스 앱의 경우 유저, 제품, 주문과 관련한 모델이 필요합니다.
- View : 객체나 데이터들을 display하는 것을 다루는 부분입니다.
- Controllers : 어플리케이션 내의 상호 작용을 다룹니다. 어떤 이벤트를 listening하고 핸들링 하는 것도 콘트롤러에서 하게 됩니다.
- Store : 앱에 데이터를 로딩하는 일을 책임 집니다. 리스트나 데이타뷰 같은 컴포넌트들도 다룹니다.
- Profile : 앱의 UI를 디바이스 종류별로 쉽게 customizing 할 수 있도록 도와 줍니다.

어플리케이션은 우선 Sencha Touch 어플리케이션이란 것을 선언하면서 시작합니다.
코드는 대개 아래와 같습니다.

Ext.application({
    name: 'MyApp',
    models: ['User', 'Product', 'nested.Order'],
    views: ['OrderList', 'OrderDetail', 'Main'],
    controllers: ['Orders'],

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

name은 전체 어플리케이션을 대표하는 글로벌 namespace입니다. 여기에는 model,view,controller 그리고 다른 클래스들이 다 포함돼 있습니다.
예를 들어 name이 MyApp이라면 이에 따른 클래스들인 MyApp.model.User, MyApp.controller.Users, MyApp.view.Main 등이 포함 돼 있을 겁니다.
애플리케이션은 models, views, controllers 라는 사전 정의 된 configuration을 자동적으로 로딩해서 사용합니다.
이것을 기초로 효율적인 file구조를 만들 수 있습니다. 예를 들어 app/model 폴더나 app/controller 폴더를 만들 수 있고 그 안에 관련된 파일들을 넣을 수 있습니다.
custom dependency에 대해서는  Dependencies section of the Ext.app.Application docs 를 참조하세요.

Controllers

콘트롤러는 어플리케이션을 서로 연결시켜 주는 역할을 합니다. 콘트롤러는 이벤트를 리스닝(예를 들어 button을  tap 하는 이벤트 등)하고 어떤 이벤트가 발생하면 이에 대한 핸들링을 합니다.
이렇게 함으로서 코드를 보기 좋게 작성할 수 있고 view 로직과 control 로직을 구분해서 코딩 할 수 있습니다.
예를 들어 유저가 로그인 폼을 통해 로그인하려고 하는 경우. View에서는 필드나 버튼 같은 form을 작성할 겁니다.
controller는 겉으로 드러나는 부분은 미미하지만 그 역할을 아주 중요합니다. 그리고 몇가지 규칙이 있습니다. 여러분 앱에 있는 각각의 콘트롤러는 모두 Ext.app.Controller의 subclass 입니다. 콘트롤러는 MyApp.controller.* namespace에 존재합니다. 예를 들어 세션 콘트롤러가 있다면 그것은 MyApp.controller.Sessions 라고 불려질겁니다. 그리고 app/controller/Sessions.js 라는 파일로 존재할 겁니다.

각각의 콘트롤러들이 Ext.app.Controller의 subclass 임에도 이 콘트롤러들은 Application 이 처음 로드 될 때 한번 초기화 됩니다. 모든 콘트롤러는 각각 한개의 인스턴스만 있습니다. 그리고 이 콘트롤러 인스턴스 세트는 어플리케이션에 의해 내부적으로 manage 됩니다. 위 예제와 같이 어플리케이션의 콘트롤러 config를 사용하면 모든 콘트롤러와 인스턴스들은 자동적으로 로딩되게 됩니다.

A simple example

아래에 세션 컨트롤러와 관련한 예제가 있습니다. 이 예제에는 2개의 콘트롤러 configuration이 있습니다. (refs,control). Refs는 이 앱의 컴포넌트를 쉽게 찾을 수 있도록 도와 줍니다. 아래 예제의 경우는 formpanel 이라는 xtype과 매치되는 모든 콘트롤러를 검색하고 첫번재로 찾은 컴포넌트에 loginForm 프로퍼티를 할당하게 됩니다. 이 프로퍼티는 나중에 doLogin 함수에서 사용할 겁니다.

두번째는 control configuration입니다. refs처럼 이 configuration은 그 안에 버튼이 있는 formpanel을 찾기 위한 ComponentQuery selector를 사용합니다. 예를 들어 이 콘트롤러는 login form의 submit button을 찾을 겁니다. 유저가 이 버튼을 tap하면 컨트롤러의 doLogin 함수가 불려질 겁니다.

Ext.define('MyApp.controller.Sessions', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            loginForm: 'formpanel'
        },
        control: {
            'formpanel button': {
                tap: 'doLogin'
            }
        }
    },

    doLogin: function() {
        var form   = this.getLoginForm(),
            values = form.getValues();

        MyApp.authenticate(values);
    }
});

doLogin은 간단할 겁니다. 왜냐하면 위 예제에서 loginForm ref를 정의해서 Controller가 자동적으로 getLoginForm을 작동시키고 match 되는 formpanel을 리턴합니다. 이 form reference를 가지게 되면 유저 네임과 패스워드 값을 가져와서 이를 검증하는 함수에 전달할 겁니다. 이러한 작업이 컨트롤러가 하는 작업입니다. 이벤트가 발생하는지를 listening 하고 어떤 작업을 하게 됩니다. 이 경우에는 아이디,패스워드 검증을 하는것이죠.

컨트롤러에 대해 좀 더 자세히 알고 싶으시면 여기 controllers guide를 참조하세요.

Stores

store는 데이타를 다루는 매우 중요한 부분입니다. 반면에 그 사용법은 간단합니다. Store는 Model 인스턴스의 배열일 뿐입니다. Data-bound 컴포넌트는 DataView리스트 같이 Store의 각 Model 인스턴스에서 한 item을 render 합니다. Model 인스턴스가 추가되거나 삭제되는 store 이벤트가 발생하면 이 data-bound 컴포넌트가 listening 해서 update를 해 주게 됩니다.

Store가 무엇이고 이것을 앱에서 컴포넌트와 같이 어떻게 사용할 것인가에 대해 더 자세히 아시려면 store guide 부분을 참조하세요. 몇가지 꼭 알아 두셔야 할 부분들이 있으니 꼭 보세요.

Device Profiles

Sencha Touch는 성능이나 해상도가 다른 다양한 device들에서 사용할 수 있도록 해 줍니다. 태블릿에 맞게 작업하면 폰에 안 맞거나 그 반대의 경우가 있을 수 있습니다. 그래서 이런 서로 다른 device에는 그에 맞는 각각의 view들을 만들어야 합니다. 하지만 단지 다른 UI를 보여주가 위해 어플리케이션을 여러개 만들 필요는 없습니다. Sencha Touch는 이 경우에 가능한 많은 code를 서로 share 할 수 있도록 해 줍니다.

Device Profile은 서로 다른 device에 여러분의 앱이 제대로 보여지고 동작할 수 있도록 해 주는 간단한 클래스입니다. 처음에는 이 profile 없이 앱을 개발하실 겁니다. 그리고 이 profile은 나중에 추가 되게 됩니다. 그 앱이 특정 디바이스에서만 동작하는 앱이라면 profile은 작업을 할 필요가 없겠죠. 그리고 특정 디바이스일 경우에 추가적인 model,view,controller 등이 로드 되게 할 수 도 있습니다.

이 프로파일을 사용하기 위해서는 이 프로파일(device)들이 무엇인지 Application에 얘기를 해 주고 그 디바이스들을 위해 Ext.app.Profile 서브클래스를 생성하시면 됩니다.:

Ext.application({
    name: 'MyApp',
    profiles: ['Phone', 'Tablet'],

    //as before
});

위의 경우 어플리케이션은 app/profile/Phone.js와 app/profile/Tablet.js를 로드하게 될 겁니다. 아래에 Tablet에 대한 예제가 있습니다.

Ext.define('MyApp.profile.Tablet', {
    extend: 'Ext.app.Profile',

    config: {
        controllers: ['Groups'],
        views: ['GroupAdmin'],
        models: ['MyApp.model.Group']
    },

    isActive: function() {
        return Ext.os.is.Tablet;
    }
});

isActive 함수는 이 앱이 태블릿에서 작동을 할 때 true를 return 할 겁니다. 이 경우 약간 부정확 할 수도 있습니다. 왜냐하면 태블릿과 폰 사이에 무수 히 많은 모양과 size를 가진 device들이 있거든요. 태블릿인가 혹은 phone인가에 대한 확실한 구분이 사실은 없습니다. Sench Touch의 Ext.os.is.Tablet은 iPad에서 작동하면 true를 리턴하고 다른 경우엔 false를 리턴합니다. isActive에 다양한 디바이스를 넣어서 사용하시면 됩니다.

한가지 명심해야 할 것은 오직 하나의 Profile만이 isActive 함수에서 true를 리턴한 다는 것입니다. 1개 이상이 true일 경우 첫번째 매치되는 것만 true를 return 하고 나머지는 무시 될 겁니다. 그리고 이 후에 이 true로 return 된 첫번째 profile에 맞는 부분들이 실행이 될 거구요.

현재의 프로파일에 대해 추가적인 model,view,controller, store들이 있다면 이는 어플리케이션이 자동적으로 로딩을 할 겁니다. (currentProfile 에 관해서 좀 더 알고 싶으신 분은 여기를 클릭하세요.)아래와 같이 해당 파일을 로드하게 될 겁니다.

    views: ['GroupAdmin'] will load app/view/tablet/GroupAdmin.js
    controllers: ['Groups'] will load app/controller/tablet/Groups.js
    models: ['MyApp.model.Group'] will load app/model/Group.js

대부분의 경우 Profile은 특정 디바이스에 추가적인 기능이나 좀 다른 뷰나 store를 필요로 할 때 사용 될 겁니다. 좀 더 자세한 사항은 device profiles guide를 참고하세요.


Launch Process

각 어플리케이션들에는 launch 함수를 정의할 수 있다. 이 launch 함수는 앱이 로드되고 launch될 준비가 되어있을 때 실행 되는 함수 입니다. start up 로직을 넣을 최적의 장소죠. 특히 main view structure를 생성하기에 적합한 장소입니다.

launch 함수와 더불어 앱의 startup 로직을 넣을 좋은 장소가 두군데 더 있습니다. 첫번째로 각 controller에는 init 함수를 정의할 수 있습니다. 이 함수는 어플리케이션 launch 함수가 시작되기 이전에 call 됩니다. 두번째로는 Device Profile을 사용할 경우인데요. 각각의 Profile에는 나름대로의 launch 함수를 정의할 수 있습니다. 이 함수는 application launch 함수 전에 그리고 controller의 init 함수 이후에 실행 됩니다.

주의 할 것은 해당 Profile에 매치하는 Device에서만 이 Profile launch 함수가 실행된다는 겁니다. 예를 들어 Phone과 Tablet 프로파일을 정의했고 이 앱을 tablet에서 실행할 경우 tablet의 launch 함수만 call 됩니다.
앱이 시작할 때 call 되는 함수들의 순서는 아래와 같습니다.

    Controller#init functions called
    Profile#launch function called
    Application#launch function called
    Controller#launch functions called

Profile을 사용할 때 Profile launch 함수 안에 bootup 로직을 넣는 것이 일반적입니다. 왜냐하면 각 프로파일들은 startup때 실행될 필요가 있는 각기 다른 view 세트를 가지고 있기 때문입니다.

Routing and History Support

Sencha Touch 2는 Routing과 History를 support합니다. Kitchen Sink 같은 SDK 예제를 보면 각 화면별로 옮겨다니기 쉽게 history를 사용해서 지원합니다. 특히 안드로이드의 경우 유용합니다.

이 히스토리 기능을 제대로 보여주는 에제는 Kitchen sink 예제입니다. 이 예제에서는 히스토리를 위한 라우팅과 상대 restoration 과 관련한 많은 문서들이 있습니다.
~~~~~~ ~~~~~~
반응형

Comment


지난시간에 Sencha Touch 2.0 Beta SDK Tutorial에 있는 예제를 해 봤습니다.
3개의 Tab 이 있었고 첫번재 탭은 이미지와 텍스트 뿌리는 거였고 두번째 탭은 원격 블로그를 JSON으로 불러오는 거 였습니다.
여기 두번째 까지 다뤘었구요. 세번째 이메일 보내는 탭을 할 차례인데요. 이거 하기 전에 두번째 탭에 대해 조금 더 살펴 보고 가죠.

Tutorial에는 없는 내용인데 그냥 제가 궁금해서 한번 살펴 봤습니다.
두번째 탭에서 가장 중요한 부분은 아래 부분이었습니다.

 url: 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://feeds.feedburner.com/SenchaBlog',

구글 api를 이용해서 http://feeds.feedburner.com/SenchaBlog 의 내용을 가져 오는 거 였습니다.
저 웹사이트를 한번 가 봤습니다.

우리가 지난시간에 만들었던 List 첫번째 제목과 이 웹싸이트 첫번째 글 제목이 같네요.
그리고 이 List의 첫번째 아이템을 클릭하면 저 웹싸이트의 해당 글 내용이 그대로 나옵니다.
위 웹사이트의 소스를 한번 봤습니다.

제목이 <title> 태그 안에 있죠? <author>도 있고 <content> 도 있습니다.

이는 지난시간에 했던 아래 fields tag랑 일치합니다.

fields: [
                            'title', 'link', 'author', 'contentSnippet', 'content',
                            {name: 'leaf', defaultValue: true}
                        ],
즉 proxy에서 설정한 url로 가서 fields 에 명시한 필드들의 값을 googleapi를 거쳐서 가져 오는것 같습니다.

혹시나 해서 저 소스를 복사해서 제 local 에 넣고 proxy의 url 을 local의 해당 파일로 설정했는데 잘 안 되더라구요.
이번 tutorial에서는 client side에서 작업하는 것만 돼 있고 원격 server side에서 작업하는 방법이 자세하게 설명돼 있지 않아서 방법을 잘 모르겠습니다.

혹시 server side 에서 어떻게 하는지 아시는 분이나 이것 관련해서 자세히 설명된 강좌 싸이트 아시는 분 알려주시면 감사하겠습니다.

===== o ===== o ===== o ===== o ===== o ===== o ===== o =====

그럼 지난시간에 이어서 세번째 탭인 이메일 보내기를 해 보겠습니다.

***** Creating a Contact Form

마지막으로 우리가 다룰 부분은 contact form을 생성하는 겁니다.
우리는 유저이름과 이메일 주소 그리고 메세지를 받을겁니다. 그리고 이것을 FieldSet을 이용해서 보기 좋게 만들거구요. 아래 코드를 참조하세요.
Ext.application({
    name: 'Sencha',

    launch: function() {
        Ext.create("Ext.tab.Panel", {
            fullscreen: true,
            tabBarPosition: 'bottom',

            items: [
                {
                    title: 'Contact',
                    iconCls: 'user',
                    xtype: 'formpanel',
                    url: 'contact.php',
                    layout: 'vbox',

                    items: [
                        {
                            xtype: 'fieldset',
                            title: 'Contact Us',
                            instructions: '(email address is optional)',
                            items: [
                                {
                                    xtype: 'textfield',
                                    label: 'Name'
                                },
                                {
                                    xtype: 'emailfield',
                                    label: 'Email'
                                },
                                {
                                    xtype: 'textareafield',
                                    label: 'Message'
                                }
                            ]
                        },
                        {
                            xtype: 'button',
                            text: 'Send',
                            ui: 'confirm',
                            handler: function() {
                                this.up('formpanel').submit();
                            }
                        }
                    ]
                }
            ]
        });
    }
});

이 코드를 보시면 fieldset을 가지고 있는 form을 생성한 것입니다. fieldset에는 3개의 field가 있습니다. (name,email,message)
그리고 layout은 VBox layout을 사용했구요. 이것은 아래위로 아이템들을 정렬합니다. 아래쪽에는 tap handler와 함께 Button이 하나 있구요. 이 버튼이 눌려지게 되면 데이터가 url로 전달되고 submit을 통해서 결과값을 받습니다.



이 예제에서는 여기까지만 있는데요. example 폴더의 getting started 예제를 보면 버튼이 아래와 같이 돼 있습니다.
{
                            xtype: 'button',
                            text: 'Send',
                            ui: 'confirm',

                            // The handler is called when the button is tapped
                            handler: function() {

                                // This looks up the items stack above, getting a reference to the first form it see
                                var form = this.up('formpanel');

                                // Sends an AJAX request with the form data to the url specified above (contact.php).
                                // The success callback is called if we get a non-error response from the server
                                form.submit({
                                      success: function() {
                                        // The callback function is run when the user taps the 'ok' button
                                        Ext.Msg.alert('Thank You', 'Your message has been received', function() {
                                            form.reset();
                                        });
                                    }
                                });
                            }
                        }

버튼을 누르면 Contact.php로부터 결과값을 받아서 성공했을 경우 Message를 띄워 주는 겁니다.

Contact.php에는 간단하게 아래와 같이 돼 있네요.
{
    "success": true
}

일단 여기까지 3개 화면 모두 각각 만들어 봤습니다.

이제 이것을 하나로 모으는 일이 남았습니다.

모으는 방법은 .join("") 을 사용하는 방법이 있고 , 를 찍고 그냥 다음 item을 넣는 방법이 있습니다.

아래가 전체 소스입니다.
//We've added a third and final item to our tab panel - scroll down to see it
Ext.application({
    name: 'Sencha',

    launch: function() {
        Ext.create("Ext.tab.Panel", {
            fullscreen: true,
            tabBarPosition: 'bottom',

            items: [
                {
                    title: 'Home',
                    iconCls: 'home',
                    cls: 'home',
                    html: [
                        '<img width="65%" src="http://staging.sencha.com/img/sencha.png" />',
                        '<h1>Welcome to Sencha Touch</h1>',
                        "<p>You're creating the Getting Started app. This demonstrates how ",
                        "to use tabs, lists and forms to create a simple app</p>",
                        '<h2>Sencha Touch 2</h2>'
                    ].join("")
                },
                {
                    xtype: 'nestedlist',
                    title: 'Blog',
                    iconCls: 'star',
                    displayField: 'title',

                    store: {
                        type: 'tree',

                        fields: [
                            'title', 'link', 'author', 'contentSnippet', 'content',
                            {name: 'leaf', defaultValue: true}
                        ],

                        root: {
                            leaf: false
                        },

                        proxy: {
                            type: 'jsonp',
                            url: 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://feeds.feedburner.com/SenchaBlog',
                            reader: {
                                type: 'json',
                                rootProperty: 'responseData.feed.entries'
                            }
                        }
                    },

                    detailCard: {
                        xtype: 'panel',
                        scrollable: true,
                        styleHtmlContent: true
                    },

                    listeners: {
                        itemtap: function(nestedList, list, index, element, post) {
                            this.getDetailCard().setHtml(post.get('content'));
                        }
                    }
                },
                //this is the new item
                {
                    title: 'Contact',
                    iconCls: 'user',
                    xtype: 'formpanel',
                    url: 'contact.php',
                    layout: 'vbox',

                    items: [
                        {
                            xtype: 'fieldset',
                            title: 'Contact Us',
                            instructions: '(email address is optional)',
                            items: [
                                {
                                    xtype: 'textfield',
                                    label: 'Name'
                                },
                                {
                                    xtype: 'emailfield',
                                    label: 'Email'
                                },
                                {
                                    xtype: 'textareafield',
                                    label: 'Message'
                                }
                            ]
                        },
                        {
                            xtype: 'button',
                            text: 'Send',
                            ui: 'confirm',
                            handler: function() {
                                this.up('formpanel').submit();
                            }
                        }
                    ]
                }
            ]
        });
    }
});


위 화면이 완성된 화면입니다.

이 Tutorial은 여러분 localhost :  http://localhost/sencha-touch-2-rc/docs/#!/guide/first_app 로 가시면 보실 수 있습니다.
웹으로 보시려면 http://docs.sencha.com/touch/2-0/#!/guide/first_app
로 가시면 보실 수 있습니다.

그리고 Sencha Touch SDK 는 http://www.sencha.com/products/touch/download/ 로 가셔서 아래 2.0 베타버전을 다운 받으셔서 여러분 서버에 설치하세요.

2.0 Release Candidate for Developers


이번 시간은 본격적인 Tutorial로 들어가기 전에 샘플 앱을 하나 만들어 본 겁니다.
다음 시간부터 본격적으로 Sencha Touch Tutorial을 정리해 보도록 하겠습니다.

***** *****
반응형

Comment

  1. 구르는돌 2012.03.27 21:43

    구글 api 깔끔하게 슬라이딩 식으로 불러와주네요,,

    한가지 궁금한게 분석을 하고있는데 리스트가 4개로만 한정되어져서 나오는데,,
    혹시 그부분제여하는 소스는 어디에 있을까요??

    좋은포스팅 늘 감사드립니다

  2. 구르는돌 2012.04.05 21:52

    자답하고 가겠습니다 ㅎㅎ
    https://developers.google.com/feed/v1/jsondevguide#json_reference
    이쪽에 해당 api에 관한 설명이 나와있네요, 피드 주소 뒤에 &num=100 붙여주면
    100개리스트를 불러오고,, 해당숫자만큼 불러올 수 있다고 합니다..100이 최대갯수이고요,,

    • 솔웅 2012.04.05 05:03 신고

      하하.. 죄송... 저도 아직 해보지 못했던 부분이네요. 저도 많은 도움이 될 것 같습니다. 감사합니다. 다음에 질문 올리시면 빨리 확인해서 답변 드릴께요.. ^^

  3. 나그네 2012.07.03 18:34

    안녕하세요. 블로그 잘 보고 있습니다.
    제가 로그인 페지를 새롭게 만들어 보려고 하는데요. 테마변경이 잘 안되네요.
    그리고 아래쪽 태브패널에서 태브들을 없애려고 하는데 hide()를 사용하니깐 icon하고 string은 보이지 않지만 마우스로 원래 나타나던 자리를 클릭하면 동작이 페지가 전환됩니다.
    어찌 해야 할까요. 도와주세요.
    그리고 제 페지 sample 이미지를 여기에 올리려면 어떻게 해야 하나요?
    답변 기다리겠습니다.
    감사합니다.


처음 Sencha SDK 설치 한 후 지지난 번에 첫번째 Sencha Application을 만들어 봤습니다.

그 다음에 Sencha에서 제공하는 Tutorial대로 차근차근 배워보기로 하고 지난 시간에 간단한 맛보기 코드를 다뤘습니다.

오늘 공부할 걸 보니까 다시 Sencha Touch로 만든 첫 번째 앱이네요.
지난번 만든건 1.1.1 버전으로 만든거니까 이번에 할 거는 2.0 베타 버전으로는 처음 만드는 앱입니다. ^^


오늘 배울 앱을 보니까 메인 화면에 Sencha Logo 가 나오고 밑에 텍스트가 나오네요.
그리고 bottom에 toolbar가 있고 세개의 버튼이 있습니다.
두번째 blog는 JSON으로 원격 웹 페이지 내용을 보여주는 부분이구요.
세번째 Contact us는 이메일을 보내는 form이 나오더라구요.

정확히 어떤 앱이고 또 이 앱을 구현하려면 어떻게 코딩을 해야 하는지 자세히 공부해 보도록 하겠습니다.

***** Getting Started
첫번째로 할 부분은 우리가 지난시간에 했던 코드랑 거의 비슷합니다. 화면이 있고 툴바가 하나 있으니까 우선 그것부터 만들어 보겠습니다.
Ext.application({
    name: 'Sencha',

    launch: function() {
        Ext.create("Ext.tab.Panel", {
            fullscreen: true,
            items: [
                {
                    title: 'Home',
                    iconCls: 'home',
                    html: 'Welcome'
                }
            ]
        });
    }
});
처음엔 별거 없습니다. launch:function()안에 코딩을 하는데 Ext.create 을 사용해서  안에 Ext.tab.Panel을 사용합니다. fullscreen은 true로 하고 item에는 Home버튼을만들고 Welcome이라는 글을 쓰게 될 겁니다.

아주 기초적인 부분입니다. 처음부터 꼼꼼히 다 알려고 하면 지칩니다.
자꾸자꾸 반복해서 몸으로 익히세요. 첨부터 머리로 익히려고 하면 골치 아픕니다.

이제 할 일은 tabBar를 밑으로 내리고 Welcome 부분에 이미지도 넣고 텍스트도 조금 더 넣을 겁니다.
Ext.application({
    name: 'Sencha',

    launch: function() {
        Ext.create("Ext.tab.Panel", {
            fullscreen: true,
            tabBarPosition: 'bottom',

            items: [
                {
                    title: 'Home',
                    iconCls: 'home',
                    html: [
                        '<img src="http://staging.sencha.com/img/sencha.png" />',
                        '<h1>Welcome to Sencha Touch</h1>',
                        "<p>You're creating the Getting Started app. This demonstrates how ",
                        "to use tabs, lists and forms to create a simple app</p>",
                        '<h2>Sencha Touch (2.0.0)</h2>'
                    ].join("")
                }
            ]
        });
    }
});

tabBar를 밑으로 하는건 tabBarPosition을 Bottom으로 하면 되구요. 이미지와 텍스트를 추가하는건 html부분을 바꾸시면 됩니다.

이제 첫번째 화면은 완성 됐죠? tabBar 넣는 일은 이제 여러번 반복해서 쉽게 하실 수 있을 겁니다.

***** Adding The Blogs Page
이제 두번째 페이지를 넣을 차례입니다.

두번째 페이지는 위와 같이 display할 겁니다.
위 아래 tab bar가 있고 가운데에는 List 가 들어갑니다. 이 텍스트들은 JSON을 통해서 외부에서 불러옵니다.
그러면 저 텍스트를 클릭하면 그에 따른 내용들이 보여집니다. 일반 웹페이지를 불러온거니까 일반 웹페이지 Navigate하는 식으로 다 작동 될 겁니다.
코드는 아래와 같습니다.
Ext.application({
    name: 'Sencha',

    launch: function() {
        Ext.create("Ext.tab.Panel", {
            fullscreen: true,
            tabBarPosition: 'bottom',

            items: [
                {
                    xtype: 'nestedlist',
                    title: 'Blog',
                    iconCls: 'star',
                    displayField: 'title',

                    store: {
                        type: 'tree',

                        fields: [
                            'title', 'link', 'author', 'contentSnippet', 'content',
                            {name: 'leaf', defaultValue: true}
                        ],

                        root: {
                            leaf: false
                        },

                        proxy: {
                            type: 'jsonp',
                            url: 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://feeds.feedburner.com/SenchaBlog',
                            reader: {
                                type: 'json',
                                rootProperty: 'responseData.feed.entries'
                            }
                        }
                    },

                    detailCard: {
                        xtype: 'panel',
                        scrollable: true,
                        styleHtmlContent: true
                    },

                    listeners: {
                        itemtap: function(nestedList, list, index, element, post) {
                            this.getDetailCard().setHtml(post.get('content'));
                        }
                    }
                }
            ]
        });
    }
});

코드가 너무 복잡하죠? 중괄호{}도 너무 많구요. 직접 코딩하실 때는 중괄호 끝나는 부분에 } 이게 어떤 부분의 끝인지 주석을 달아 주시면 나중에 수정하거나 분석할 때 편하실 겁니다.
여기선 몇개의 못보던 configuration들이 있죠? xtype,displayField,store 같은 것들이요.
store config는 nested list에게 data를 어떻게 수집(fetch)하는지를 알려 줍니다.
store config 내에 여러가지 요소가 있는데요.
type을 tree로 한 건 tree store를 생성한다는 거구요.
fields는 블로그 내 데이터 중 어떤 fields들을 가져오겠다고 알려주는 겁니다.
이 경우엔 블로그에서 데이터를 어떤 field 이름을 사용해서 처리했는지를 알아야 겠죠?
proxy는 이 데이터를 어디에서 가져올 거라는 것을 알려 줍니다. 이 부분은 나중에 좀 더 자세히 다룰 겁니다.
root는 leaf가 아니라는 것을 알려 줍니다.
이 Store configuration중에서 proxy가 가장 중요한 역할을 합니다.
이 예제에서는 블로그의 데이터를 JSON-P 양식으로 리턴하기 위해 Google의 Feed API Service를 사용할 거라는 것을 proxy에게 알려줍니다.
Google의 Feed API Service는 어떤 블로그의 데이터에서도 쉽게 데이터를 가져와서 우리들의 엡에 display할 수 있도록 해 줍니다.
@@@@@ url: 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://rss.slashdot.org/Slashdot/slashdot', 이렇게 바꾸시면 다른 데이터를 가져 올 수 있습니다. 서버단에서 웹 페이지는 어떻게 만들어야 되는지는 나중에 다룰 겁니다. @@@@@
그 다음으로 proxy안에는 rootProperty: 'responseData.feed.entries' 부분이 있습니다.
이 부분은 그냥 이렇게 하면 Google에서 알아서 처리를 합니다. 마지막에 entries라고 복수형을 썼기 때문에 배열 형식으로 구글에서 처리할 겁니다.
이건 그냥 나중에 copy and paste해서 계속 이렇게 사용하시면 됩니다.

그 다음에 나오는 것이 detailCard와 listeners입니다.
detailCard는 유저가 tab했을 때 특정한 view를 제공할 수 있도록 해 줍니다. 우리는 스크롤이 가능하도록 세팅을 했고 글자를 좀 더 보기 좋게 하기 위해서 styleHtmlContent를 사용했습니다.

다음은 listeners에 있는 itemtap인데요.
이건 어떤 item을 tap했을 때 그 해당 item이 무엇인지 알 수 있도록 해 줍니다.
각 아이템을 클릭하면 detail들이 나올텐데요 클릭한 item을 보내줘야 해당 details를 가져올 수 있습니다.

조금 코드가 생소해서 어렵게 느껴 질 수도 있겠지만 이 코드로 직접 만들어 보시고 이것 저것 변형 시키면서 익히시면 훨씬 이해가 빠르실 겁니다.

오늘은 여기까지 하구요.
다음 시간에는 메일 보내는 화면을 마치고 세개의 화면을 하나의 앱속에서 표현될 수 있도록 하는 작업을 해 보겠습니다.

반응형

Comment

  1. 길어진 2012.02.27 17:56

    안녕하세요 솔웅님

    이번에 Sencha Touch를 공부하게 되면서 솔웅님의 블로그를 찾게 되었습니다.

    매 포스트마다 굉장히 배울점히 많다고 느끼게 되는데요*_*

    궁금한게 있어서 질문을 좀 드리고 싶습니다.

    html에서 일일이 script 태그를 사용해 js파일을 지정해 주어야 하는지요?
    <script type="text/javascript" src="file:///D:/Web%20workspace/SenchaTest/WebContent/src/tab/Panel.js"></script>이런식으로 써주지 않으면 해당 function들을 못읽어서 에러가 나는것 같더라구요...

    어제 처음 Sencha를 접해봐서 그런지 너무 생소하고 어려운게 많네요ㅜ

    • 길어진 2012.02.27 18:08

      아 해결되었습니다!!

      sencha-touch-all.js를 넣어주니까 다른 파일들을 안 넣어줘도 되네요..^^;

      멍청한 질문 드린것 같아 민망하고 쑥스럽네요 ㅎㅎㅎㅎ

      앞으로 블로그 열심히 보며 많은 부분 공유하도록 하겠습니다 수고하세요~~^^

    • 솔웅 2012.02.28 03:31 신고

      예 해결 되었다니 다행이네요.
      궁금한 점 있으시면 언제든지 글 올려 주세요.
      감사합니다.

  2. Freeduck 2012.03.15 00:09

    안녕하세요... 쉽게 잘 써주셔서 감사히 잘 보고 따라하고 있습니다.
    제 브라우저에서는 tabBarPosition: 'bottom'을 하면 탭바가 보이지 않는군요... top, left, right 다 보이는데 말입니다. 혹시 왜 그런 것인지는 몰겠네요... sencha-touch-all-debug.js 파일과 sencha-touch.css 파일은 이번에 정식 버전이라고 릴리즈 된 것을 받아서 사용하고 있습니다. 혹시 달라진게 있는지 몰겠네요... 요소검사로 보니까 분명히 나와있고 잡혀있는데 화면에만 보이지 않네요... 이상하네요.

  3. Freeduck 2012.03.15 00:12

    흐... 찾았네요...
    비쥬얼 스튜디오를 쓰다보니 <!DOCTYPE html /> 이렇게 자동으로
    변환되어서.... <!DOCTYPE html> 이렇게 바꿔주니까 나오는군요.
    ^^ 계속 좋은 포스트 부탁 드려요.

    • 솔웅 2012.03.20 06:11 신고

      아 예.. 편집기에서 자동적으로 닫는 태그를 생성해 주면 그런 문제가 생길 수 있겠네요.
      팁 감사합니다..

Sencha Touch Tutorial 1 Getting Started

2012. 2. 21. 11:36 | Posted by 솔웅


지난번까지 Sencha Touch 에 대해 대략적으로 알아보고 SDK를 설치하고 그리고 어플리케이션을 하나 만들어 봤습니다.

어플리케이션을 만들어보니 대충 감이 잡히던데요.

일단 Sencha Touch에서 제공하는 Tutorial을 한번 훑어보는게 센차터치를 좀 더 디테일하게 알 수 있는 가장 좋은 방법인것 같습니다.

Corona SDK도 코로나에서 제공하는 튜토리얼인 Corona DOC를 한번 다 훑고 나서 새로 추가되는 기능들을 하나하나 정리해 나가니까 정말 도움이 되더라구요.

오늘부터는 우공이산이라고 하나하나 기초적인 튜토리얼부터 공부해 나가볼 생각입니다.

***** Sencha Touch란?
센차터치란 안드로이드나 iOS, 블랙베리 같은 모바일 폰에 HTML5를 기반으로 앱을 쉽고 빠르게 만들 수 있도록 해 줍니다.

***** 필요한 것은?
- Sencha Touch 2.0 SDK beta
- 웹 서버 (로컬)
- 웹 브라우저 최신버전 (크롬하고 사파리를 추천합니다.)
=> 보다 자세한 사항은 제 글 Sencha Touch getting started - 설치 및 예제 실행 - 를 참조하세요.

***** 시작하기
웹 서버를 설치하지 않아도 아래 두개 파일만 있어도 예제를 실행할 수는 있습니다.
sencha-touch.css

sench-touch-all-debug.js

위 두 파일을 임의의 폴더에 복사해 넣으세요.
그리고 아래 html 파일을 작성해 넣으세요.
<!DOCTYPE html>
<html>
<head>
    <title>Getting Started</title>
    <link rel="stylesheet" href="sencha-touch.css" type="text/css">
    <script type="text/javascript" src="sencha-touch-all-debug.js"></script>
    <script type="text/javascript" src="app.js"></script>
</head>
<body></body>
</html>
html파일 이름은 마음대로 정하셔도 됩니다.
이제 app.js파일을 만드셔서 코딩을 하시면 됩니다.

첫번째 코딩은 alert화면 띄우기 입니다.
Ext.application({
    name: 'Sencha',

    launch: function() {
        alert('launched');
    }
});

이렇게 작성하시고 html파일을 브라우저에서 실행해 보세요.


이렇게 alert화면이 뜨면 성공하신겁니다.

app.js를 아래와 같이 바꾸고 실행해 보세요.
Ext.application({
    name: 'Sencha',

    launch: function() {
        Ext.create('Ext.Panel', {
            fullscreen: true,
            html: 'Hello World'
        });
    }
});

그러면 이렇게 Hello World가 나올 겁니다.
Ext.create()는 Ext.ClassManager.instantiate를 간단히 사용하기 위해 만든 겁니다.
Ext.create()대신 Ext.ClassManager.instantiate를 넣으셔도 결과는 똑 같습니다.
instantiate(String name, Mixed args) 가 신택스 입니다.

Tutorial 첫 시간은 여기까지 하겠습니다.
앞으로 Sencha Touch에서 공식적으로 제공하는 Tutorial을 기반으로 글을 정리해 나가겠습니다.

다음 시간에 뵈요....
~~~~~~~~ ~~~~~~~~
반응형

Comment

이전 1 2 3 다음