doc03 - KoreaSenchaUserGroup/Lab1 GitHub Wiki

DOC 3. [Overview] Upgrading from Sencha Touch 1.x to 2.x

์›๋ฌธ : http://docs.sencha.com/touch/2-0/#!/guide/upgrade_1_to_2

๋ฒˆ์—ญ : ์ •๊ฑด์šฐ [Github:NangChun]

Sencha Touch 2 brings a number of refinements to the framework. Some of these changes require you to update parts of your application's code - this guide takes you through the changes you'll need to make.

In addition to the guide, Sencha Touch 2 comes with a backwards-compatibility build that makes the migration process easier. Note that this build does not automatically guarantee your 1.x app will work out of the box. Wherever possible, the compatibility build will figure out what your 1.x app code is trying to do and route it through to the 2.x API. Whenever it does this, the compat build will log a warning to the console telling you what you need to update.

As of Sencha Touch3 PR4 the backwards-compatibility support is baked into all builds, from beta 1 onwards you will need to use a different build to keep the compatibility intact while you migrate your app. Following the steps below and then correcting any warnings the compatibility layer informs you about should result in an application that will run on Sencha Touch 2.x.

์„ผ์ฐจํ„ฐ์น˜ 1.x์—์„œ 2.x๋กœ์˜ ์—…๊ทธ๋ ˆ์ด๋“œ

์„ผ์ฐจํ„ฐ์น˜2๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ๋กœ๋ถ€ํ„ฐ ๋‹ค์ˆ˜์˜ ๊ฐœ์„ ๋œ ๊ฒƒ๋“ค์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ณ€ํ™” ์ค‘ ์ผ๋ถ€๋Š” ๊ท€ํ•˜์˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ์ฝ”๋“œ์˜ ์ผ๋ถ€๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋„๋ก ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. - ์ด ๊ฐ€์ด๋“œ๋Š” ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋‚ด์šฉ์„ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€์ด๋“œ ์ด์™ธ์—๋„ ๋ณด๋‹ค์‰ฝ๊ฒŒ ์„ผ์ฐจํ„ฐ์น˜ 2 ์ด์ „ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํ•˜์œ„ ํ˜ธํ™˜์„ฑ ๋นŒ๋“œ์™€ ํ•จ๊ป˜ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์ง€์†์ ์œผ๋กœ ์™ธ๋ถ€์—์„œ ์ด ๋นŒ๋“œ๊ฐ€ ๋‹น์‹ ์˜ 1.0 ์•ฑ์˜ ๋™์ž‘์„ ๋ณด์žฅํ•  ์ˆ˜ ์—†์Œ์„ ์ฃผ์˜ํ•˜์‹ญ์‹œ์š”. ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด, ํ˜ธํ™˜์„ฑ ๋นŒ๋“œ๋Š” 1.0 ์•ฑ ์ฝ”๋“œ๊ฐ€ 2.0 API ๋กœ ํ†ตํ•˜๋Š” ๊ฒฝ๋กœ๋ฅผ ์•Œ์•„๋‚ด๋ ค ๋…ธ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์–ธ์ œ๋“ ์ง€, ๋ฐฐ์น˜๋œ ๋นŒ๋“œ๊ฐ€ ์ฝ˜์†”๋กœ ๊ฒฝ๊ณ ๋ฅผ ๊ธฐ๋กํ•˜๋ฉฐ ์–ธ์ œ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ์ง€ ๋งํ•ด์ค๋‹ˆ๋‹ค.

์„ผ์ฐจํ„ฐ์น˜ 3 PR4 ๊ธฐ์ค€์œผ๋กœ ํ•˜์œ„ ํ˜ธํ™˜์„ฑ ์ง€์›์ด ๋ฒ ํƒ€ 1 ๋ถ€ํ„ฐ ๋ชจ๋“  ๋นŒ๋“œ๋ฅผ ์™„์„ฑํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋‹น์‹ ์˜ ์•ฑ์„ ์ด์ „ํ•˜๋Š” ๋™์•ˆ ๋นŒ๋“œ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜์—ฌ ํ˜ธํ™˜์„ฑ์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ๋‹จ๊ณ„์— ๋”ฐ๋ผ์„œ ๊ทธ๋•Œ๋งˆ๋‹ค ํ˜ธํ™˜ ๋ ˆ์ด์–ด๋กœ ์„ผ์ฐจํ„ฐ์น˜ 2.x๋ฅผ ์‹คํ–‰ํ•  ์‘์šฉํ”„๋กœ๊ทธ๋žจ์˜ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ๋ฉฐ ์–ด๋–ค ๊ฒฝ๊ณ ๋“  ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

Class System

When it comes to migrating your app, the biggest change you'll need to make is how classes are defined. Sencha Touch 2 uses the upgraded class system from Ext JS 4, which brings powerful new capabilities like mixins, dynamic loading and the config system.

In Sencha Touch 1.x, there were 2 main ways of defining classes - using Ext.extend or using one of the MVC-specific convenience functions like regModel and regController. In Sencha Touch 2, all classes are defined the same way using Ext.define. Let's take this 1.x class and see how we would migrate it to 2.x:

Geo.views.BillSummary = Ext.extend(Ext.Panel, {
    scroll: 'vertical',
    html: "Loading...",
    styleHtmlContent: true
    initComponent: function() {
        this.tpl = Ext.XTemplate.from('billsummary');
        Geo.views.BillSummary.superclass.initComponent.call(this);
    },

    /**
     * Get the billSummary and update the contents of the panel.
     */
    getBill: function(bill) {        
        Geo.CongressService.getBillSummary({
            bill: bill
        }, this.onBillSummaryResponse, this);
    },

    // private
    onBillSummaryResponse: function(billSummary) {
        if (Ext.isArray(billSummary.Paragraph)) {
            this.update(billSummary);
        } else {
            this.update('No Bill Summary Available');
        }

    }
});

In 2.x, we would instead write this:

Ext.define('Geo.view.BillSummary', {
    extend: 'Ext.Panel',

    config: {
        scroll: 'vertical',
        html: 'Loading...',
        styleHtmlContent: true
    },

    initialize: function() {
        this.callParent(arguments);

        this.tpl = Ext.Template.from('billsummary');
    },

    /**
     * Get the billSummary and update the contents of the panel.
     */
    getBill: function(bill) {        
        Geo.CongressService.getBillSummary({
            bill: bill
        }, this.onBillSummaryResponse, this);
    },

    // private
    onBillSummaryResponse: function(billSummary) {
        if (Ext.isArray(billSummary.Paragraph)) {
            this.setHtml(billSummary);
        } else {
            this.setHtml('No Bill Summary Available');
        }
    }
});

The first thing to notice is that we've swapped out Ext.extend for Ext.define. All of the old constituent parts are still present, we've just shuffled them around to use the new syntax. Notice that in 2.x all of the class names are string based. This is what enables the dynamic loading system to automatically load those classes onto the page if they are not already present. See the class system guide for more details.

The next thing we did is to move all of the configuration options into a config object. The configuration options for each class can be found in the its class documentation. Anything found in the configuration section of a class should be placed into the config object of the class when defining it.

The config system provides some key benefits, primarily a guarantee of API consistency. For example, the html config option guarantees that we can call getHtml and setHtml at any time, removing the guesswork from figuring out which functions to call. Every single config option has getter and setter functions that follow the same pattern as getHtml and setHtml. We use this to our advantage in the onBillSummaryResponse function, where we replaced the old 'update' function with the clearer setHtml function.

Finally, we replaced initComponent with initialize. In 1.x, initComponent was only available on Component classes, which excludes all of the other classes like Models, Controllers and utilities. In 2.x, every class has an initialize function that you can implement if you want some logic to be performed on instantiation. The other detail to note here is that you no longer need the ugly Geo.views.BillSummary.superclass.initComponent.call(this); - instead you can always call this.callParent(arguments) to call the superclass function.

ํด๋ž˜์Šค ์‹œ์Šคํ…œ

์•ฑ์„ ์ด์ „ํ•ด์˜ค๊ฒŒ๋˜๋ฉด, ์ปค๋‹ค๋ž€ ๋ณ€ํ™”๋กœ ์ธํ•ด ํด๋ž˜์Šค๊ฐ€ ์ •์˜๋œ ๋ฐฉ์‹์— ๋Œ€ํ•œ ํ™•์ธ์ด ํ•„์š”ํ•  ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ์„ผ์ฐจํ„ฐ์น˜ 2๋Š” EXT 4 ๋กœ๋ถ€ํ„ฐ์˜ ์—…๊ทธ๋ž˜์ด๋“œ๋œ ํด๋ž˜์Šค ์‹œ์Šคํ…œ, mixins์™€ ๊ฐ™์€ ์ƒˆ๋กญ๊ณ  ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”, ๋™์ ๋กœ๋”ฉ ๋ฐ config ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์„ผ์ฐจํ„ฐ์น˜1์—์„œ, 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ํด๋ž˜์Šค ์ •์˜๋ฅผ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค - Ext.extend์ด๋‚˜ regModel ๋ฐ regController ๊ฐ™์€ MVC ๊ณ ์œ ์˜ ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์„ผ์ฐจํ„ฐ์น˜ 2์—์„œ๋Š”, ๋ชจ๋“  ํด๋ž˜์Šค๋Š” Ext.define์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ํด๋ž˜์Šค 1.x๊ณผ 2.x๋กœ ์–ด๋–ป๊ฒŒ ์ด์ฃผํ•  ๊ฒƒ ์ธ์ง€๋ฅผ ๋ณด์‹ญ์‹œ์˜ค:

Geo.views.BillSummary = Ext.extend(Ext.Panel, {
    scroll: 'vertical',
    html: "Loading...",
    styleHtmlContent: true
    initComponent: function() {
        this.tpl = Ext.XTemplate.from('billsummary');
        Geo.views.BillSummary.superclass.initComponent.call(this);
    },

    /**
     *  billSummary๋ฅผ ๊ฐ€์ ธ์™€ ํŒจ๋„์˜ ๋‚ด์šฉ์„ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.
     */
    getBill: function(bill) {        
        Geo.CongressService.getBillSummary({
            bill: bill
        }, this.onBillSummaryResponse, this);
    },

    // private
    onBillSummaryResponse: function(billSummary) {
        if (Ext.isArray(billSummary.Paragraph)) {
            this.update(billSummary);
        } else {
            this.update('No Bill Summary Available');
        }

    }
});

2.x์—์„ , ์ด๋ ‡๊ฒŒ ๋Œ€์‹  ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

Ext.define('Geo.view.BillSummary', {
    extend: 'Ext.Panel',

    config: {
        scroll: 'vertical',
        html: 'Loading...',
        styleHtmlContent: true
    },

    initialize: function() {
        this.callParent(arguments);

        this.tpl = Ext.Template.from('billsummary');
    },

    /**
     *  billSummary๋ฅผ ๊ฐ€์ ธ์™€ ํŒจ๋„์˜ ๋‚ด์šฉ์„ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.
     */
    getBill: function(bill) {        
        Geo.CongressService.getBillSummary({
            bill: bill
        }, this.onBillSummaryResponse, this);
    },

    // private
    onBillSummaryResponse: function(billSummary) {
        if (Ext.isArray(billSummary.Paragraph)) {
            this.setHtml(billSummary);
        } else {
            this.setHtml('No Bill Summary Available');
        }
    }
});

์ฒซ๋ฒˆ์งธ๋กœ ์ฃผ์˜ํ•  ๋ถ€๋ถ„์€ Ext.define์„ ์œ„ํ•ด Ext.extend๋ฅผ ๋ฐ”๊พผ ์ ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ์˜ˆ์ „ ๊ตฌ์„ฑ ๋ถ€๋ถ„์ด ๋ชจ๋‘ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค, ๋‹จ์ง€ ์ƒˆ๋กœ์šด ๋ฌธ๋ฒ•์„ ์—ฌ๊ธฐ์ €๊ธฐ ์„ž์–ด ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. 2.x์—์„œ ํด๋ž˜์Šค ์ด๋ฆ„ ๋ชจ๋‘๊ฐ€ ๋ฌธ์ž์—ด์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ํด๋ž˜์Šค๊ฐ€ ๊ทธ๋•Œ๊นŒ์ง€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ํŽ˜์ด์ง€์— ์ž๋™์œผ๋กœ ๋กœ๋“œ๋˜๋„๋ก ๋™์  ๋กœ๋”ฉ ์‹œ์Šคํ…œ์„ ํ™œ์„ฑํ™” ์‹œํ‚ต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ํด๋ž˜์Šค ์‹œ์Šคํ…œ ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์š”.

๊ทธ ๋‹ค์Œ์€ ์„ค์ •๊ฐ์ฒด๋กœ ๊ตฌ์„ฑ์˜ต์…˜์„ ๋ชจ๋‘ ์ด๋™ ํ•˜๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ๊ฐ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ๊ตฌ์„ฑ ์˜ต์…˜์€ ํด๋ž˜์Šค ์„ค๋ช…์„œ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ •์˜ํ•  ๋•Œ ํด๋ž˜์Šค์˜ ๊ตฌ์„ฑ ์„น์…˜์— ์žˆ๋Š” ๊ฒƒ์€ ํด๋ž˜์Šค์˜ ๊ตฌ์„ฑ ๊ฐœ์ฒด์— ๋ฐฐ์น˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ •์˜ํ•  ๋•Œ ํด๋ž˜์Šค์˜ ๊ตฌ์„ฑ ์กฐ๊ฐ์— ์žˆ๋Š” ๊ฒƒ๋“ค์€ ํด๋ž˜์Šค์˜ ๊ตฌ์„ฑ ๊ฐœ์ฒด์— ๋ฐฐ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ์‹œ์Šคํ…œ์€ ๋ช‡ ๊ฐ€์ง€ ์ฃผ์š” ์ด์ ์„ ์ œ๊ณต, ์ฃผ๋กœ API ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด, html ๊ตฌ์„ฑ์˜ต์…˜์€ ์–ธ์ œ๋‚˜ getHtml๊ณผ setHtml๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅํ•˜๋ฉฐ, ์–ด๋ฆผ์žก์•„ ํ˜ธ์ถœ๋กœ ์–ด๋А ํ•จ์ˆ˜์ธ์ง€ ์•Œ์•„๋‚ด์–ด ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๋‹จ์ผ ๊ตฌ์„ฑ ์˜ต์…˜์€ getHtml๊ณผ setHtml ๊ฐ™์€ ํŒจํ„ด์„ ๋”ฐ๋ฅด๋Š” getter ๋ฐ setter ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. onBillSummaryResponse ํ•จ์ˆ˜์—์„œ ์ด์ต์„ ์œ„ํ•ด ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜์—ฌ, ๊ทธ๋•Œ ํด๋ฆฌ์–ด ํ•จ์ˆ˜์™€ ์˜ˆ์ „ '์—…๋ฐ์ดํŠธ'ํ•จ์ˆ˜๋ฅผ ๋Œ€์ฒดํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ดˆ๊ธฐํ™”์™€ ํ•จ๊ป˜ initComponent๋ฅผ ๊ต์ฒด ํ•˜์˜€์Šต๋‹ˆ๋‹ค.. 1.x์—์„œ, initComponent์€ ์ปดํฌ๋„ŒํŠธ ํด๋ž˜์Šค์—์„œ๋งŒ ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค, ์ด๊ฒƒ์€ ๋ชจ๋ธ, ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ ์œ ํ‹ธ๋ฆฌํ‹ฐ์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋Š” ๋ชจ๋‘ ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค. 2.x์—์„œ ๊ฐ ํด๋ž˜์Šค๋Š” ์–ด๋–ค ๋กœ์ง์ด ์ธ์Šคํ„ด์Šค์—์„œ ์ˆ˜ํ–‰ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๋Š” ๊ฒฝ์šฐ ๊ตฌํ˜„ํ•  ์ˆ˜์žˆ๋Š” ์ดˆ๊ธฐํ™” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•ด์•ผํ•  ๋‹ค๋ฅธ ์„ธ๋ถ€์‚ฌํ•ญ์€ ๋” ์ด์ƒ ํ‰ํ•˜๊ฒŒ ํ•„์š” ์—†๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค Geo.views.BillSummary.superclass.initComponent.call(this); - ์Šˆํผํ•จ์ˆ˜ ํ˜ธ์ถœ๋กœ ๋Œ€์‹  ์–ธ์ œ๋“ ์ง€ this.callParent(arguments) ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

MVC Migration

The MVC architecture in Sencha Touch 2 is a refinement on the approach from Sencha Touch 1. Most of the concepts are the same, but some of the syntax has been improved to make the API more expressive, and your code more readable and testable.

Models

As with all of the other classes in Sencha Touch 2, Models and the rest of the data package now expect their configurations to be placed into a config block. Sencha Touch 2 also unifies the way you define all of your application's classes: Ext.regModel is no longer needed - instead a Model is just defined like any other class. Migrating your Models is very simple - where once you had a Model like this:

Ext.regModel('MyApp.model.User', {
    fields: [
        {name: 'name',  type: 'string'},
        {name: 'age',   type: 'int'},
        {name: 'alive', type: 'boolean', defaultValue: true}
    ],

    validations: [
        {type: 'presence', field: 'age'},
        {type: 'length',   field: 'name', min: 2}
    ],
    
    sayName: function() {
        alert(this.get('name'));
    }
});

In Sencha Touch 2 the same Model would look like this:

Ext.define('MyApp.model.User', {
    extend: 'Ext.data.Model',
    
    config: {
        fields: [
            {name: 'name',  type: 'string'},
            {name: 'age',   type: 'int'},
            {name: 'alive', type: 'boolean', defaultValue: true}
        ],

        validations: [
            {type: 'presence', field: 'age'},
            {type: 'length',   field: 'name', min: 2}
        ]
    },
    
    sayName: function() {
        alert(this.get('name'));
    }
});

A Model is generally migrated in 4 steps:

  1. Replace Ext.regModel with Ext.define
  2. Make sure your new Model class extends Ext.data.Model
  3. Move all of your Model's configurations into its config block
  4. Leave any custom functions (like sayName above) outside of the config block

Views

Aside from migrating to the new class system syntax, view migration is quite simple. The main thing to keep in mind is the convention around view class names. In Sencha Touch 2 and onward we recommend that your view class names follow the pattern MyApp.view.SomeViewName. Note that the 'view' is singular - this enables the class system to automatically load the view class from the file app/view/SomeViewName.js.

Application

When it comes to the Ext.application definition, you can keep most of the syntax from your 1.x app:

Ext.application({
    name: 'MyApp',
    
    icon: 'resources/images/logo.png',
    
    launch: function() {
        Ext.create('MyApp.view.Main');
    }
});

The only thing new here is that we're using Ext.create to instantiate MyApp.view.Main, which is our app's main screen.

In addition to this change though is a slight change in approach when it comes to loading the models, views, controllers and stores your application needs. In 1.x, it was common to define all of your controllers inside the Ext.application block, along with some of your models, views and stores. The rest of the dependencies could be scattered throughout your application, making it difficult to easily understand exactly what the app is composed of.

In Sencha Touch 2 we encourage you to define all of your application's dependencies inside the Ext.application block instead of placing some of them inside Controllers. An example might look like this:

Ext.application({
    name: 'MyApp',
    
    icon: 'resources/images/logo.png',
    
    models: ['User', 'Group'],
    controllers: ['Users', 'Login'],
    views: ['Main', 'Users'],
    stores: ['Users'],
    
    launch: function() {
        Ext.create('MyApp.view.Main');
    }
});

Controllers

Just like with Model, Sencha Touch 2 expects your Controllers to be defined like any other class, so the Ext.regController function is deprecated. In 1.x we might have a controller like this:

Ext.regController("searches", {
    showSomething: function() {
        alert('something');
    }
});

Which in 2.x becomes:

Ext.define('MyApp.controller.Searches', {
    extend: 'Ext.app.Controller',
    
    showSomething: function() {
        alert('something');
    }
});

As mentioned above, if your 1.x controller defines additional model, view or store dependencies you should move these into the Application instead. The compatibility build will still attempt to load those dependencies but the non-compat build will not.

Routes

In Sencha Touch 1.x, a Controller was largely just a collection of functions that could be dispatched to externally. Often this would mean that an Application would call Ext.dispatch, specifying the Controller name, which function to call and which arguments to pass in to the function. Other times, the dispatching would be automatic, triggered by a change in the url picked up by the Router.

In 2.x, the Controller becomes a lot more proactive, actively registering routes that it cares about inside its config block:

Ext.define('MyApp.controller.Searches', {
    config: {
        routes: {
            'search/:query': 'doSearch'
        }
    },
    
    doSearch: function(query) {
        alert('searching for ' + query);
    }
});

This bypasses the need for a separate routes file and allows the Controller to indicate that whenever the page url matches 'search/:query', the doSearch function will be called with the query. For example, if the page url is currently http://myapp.com/#searches/abc123, doSearch will be called with 'abc123'. If the page url later becomes '#searches/sencha', doSearch is called again with 'sencha'.

New Capabilities

Controllers gained a raft of new capabilities in Sencha Touch 2, the most powerful of which are refs and control. For more information check out the controllers guide.

MVC ์ด์ „

์„ผ์ฐจํ„ฐ์น˜2์˜ MVC ์•„ํ‚คํ…์ฒ˜๋Š” ์„ผ์ฐจํ„ฐ์น˜1 ๋ถ€ํ„ฐ ์ ‘๊ทผํ•˜์—ฌ ๊ฐœ์„ ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐœ๋…์˜ ๋Œ€๋ถ€๋ถ„์€ ๋™์ผํ•˜์ง€๋งŒ ๊ตฌ๋ฌธ์˜ ์ผ๋ถ€ API๊ฐ€ ๋” ํ‘œํ˜„๋˜๊ฒŒ ๊ฐœ์„ ํ•˜๊ณ , ์ฝ”๋“œ๋ฅผ ์ฝ๊ธฐ ๋ฐ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋ธ

์„ผ์ฐจํ„ฐ์น˜2 ์—์„  ๋‹ค๋ฅธ ํด๋ž˜์Šค์˜ ๊ฒฝ์šฐ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋ชจ๋ธ ๋ฐ ๋ฐ์ดํ„ฐ ํŒจํ‚ค์ง€์˜ ๋‚˜๋จธ์ง€๋Š” ์ด์ œ ์ž์‹ ์˜ ๊ตฌ์„ฑ์„ ๊ตฌ์„ฑ ๋ธ”๋ก์œผ๋กœ ๋ฐฐ์น˜๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์„ผ์ฐจํ„ฐ์น˜2๋Š” ๋˜ํ•œ ๋ชจ๋“  ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ†ตํ•ฉ: Ext.regModel์€ ๋”์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค - ๋Œ€์‹  ๋ชจ๋ธ์€ ๋‹จ์ง€ ๋‹ค๋ฅธ ํด๋ž˜์Šค์ฒ˜๋Ÿผ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ์„ ์ด์ „ํ•˜๋Š”๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค - ํ•œ๋•Œ ์ด ๊ฐ™์€ ๋ชจ๋ธ์„ ๊ฐ€์กŒ์Šต๋‹ˆ๋‹ค :

Ext.regModel('MyApp.model.User', {
    fields: [
        {name: 'name',  type: 'string'},
        {name: 'age',   type: 'int'},
        {name: 'alive', type: 'boolean', defaultValue: true}
    ],

    validations: [
        {type: 'presence', field: 'age'},
        {type: 'length',   field: 'name', min: 2}
    ],
    
    sayName: function() {
        alert(this.get('name'));
    }
});

์„ผ์ฐจํ„ฐ์น˜2 ๊ฐ™์€ ๋ชจ๋ธ์€ ์ด๋ ‡๊ฒŒ ๋ณด์ผ ๊ฒƒ ์ž…๋‹ˆ๋‹ค :

Ext.define('MyApp.model.User', {
    extend: 'Ext.data.Model',
    
    config: {
        fields: [
            {name: 'name',  type: 'string'},
            {name: 'age',   type: 'int'},
            {name: 'alive', type: 'boolean', defaultValue: true}
        ],

        validations: [
            {type: 'presence', field: 'age'},
            {type: 'length',   field: 'name', min: 2}
        ]
    },
    
    sayName: function() {
        alert(this.get('name'));
    }
});

๋ชจ๋ธ์€ ์ผ๋ฐ˜์ ์œผ๋กœ 4๋‹จ๊ณ„๋กœ ์ด์ „ ๋ฉ๋‹ˆ๋‹ค:

  1. Ext.define๊ณผ Ext.regModel ๊ต์ฒด
  2. ์ƒˆ ๋ชจ๋ธ ํด๋ž˜์Šค Ext.data.Model๋ฅผ ํ™•์žฅํ–ˆ๋Š”์ง€ ํ™•์ธ
  3. ๊ทธ ๊ตฌ์„ฑ ๋ธ”๋ก์œผ๋กœ ๋ชจ๋ธ์˜ ๊ตฌ์„ฑ์„ ๋ชจ๋‘ ์ด๋™
  4. ๊ตฌ์„ฑ ๋ธ”๋ก ์™ธ๋ถ€์˜ ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜๋Š” (์œ„์˜ sayName ๋“ฑ) ๋‚จ๊ฒจ๋‘ก๋‹ˆ๋‹ค

๋ทฐ

์ด์™ธ์—๋„ ์ƒˆ ํด๋ž˜์Šค ์‹œ์Šคํ…œ ๋ฌธ๋ฒ• ์ด์ „์—์„œ ๋ทฐ ์ด์ „์€ ์•„์ฃผ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์ฃผ์˜ํ•ด์•ผ ํ•  ์ค‘์š”ํ•œ ๊ฒƒ์€ ๋ทฐ ํด๋ž˜์Šค ์ฃผ์œ„์˜ ์ด๋ฆ„ ๊ทœ์น™์ž…๋‹ˆ๋‹ค. Sencha ํ„ฐ์น˜ 2 ์ดํ›„์—์„œ๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ ๋ทฐ ํด๋ž˜์Šค ์ด๋ฆ„ ํŒจํ„ด MyApp.view.SomeViewName๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.'๋ณด๊ธฐ'๊ฐ€ ๋‹จ์ˆ˜ํ˜•์ž„์„์ฃผ์˜ - ์ด๊ฒƒ์€ ์ž๋™์œผ๋กœ ํŒŒ์ผ์„ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ /๋ณด๊ธฐ / SomeViewName.js์—์„œ ๋ทฐ ํด๋ž˜์Šค๋ฅผ ๋กœ๋“œํ•˜๋Š” ํด๋ž˜์Šค ์‹œ์Šคํ…œ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

Ext.application ์ •์˜๋กœ ์˜ค๋ฉด, 1.x ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์—์„œ์˜ ๋ฌธ๋ฒ•์„ ๋Œ€๋ถ€๋ถ„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค :

Ext.application({
    name: 'MyApp',
    
    icon: 'resources/images/logo.png',
    
    launch: function() {
        Ext.create('MyApp.view.Main');
    }
});

์—ฌ๊ธฐ์„œ ์ƒˆ๋กœ์šด ๊ฒƒ์€, MyApp.view.Main ์ธ์Šคํ„ด์Šค๋ฅผ Ext.create๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค, ์ด๊ฒƒ์€ ์•ฑ์˜ ๊ธฐ๋ณธ ํ™”๋ฉด์ž…๋‹ˆ๋‹ค.

๋ชจ๋ธ, ๋ทฐ, ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ ์ €์žฅ๋œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ฝ์–ด ์˜ค๊ฒŒ ํ•˜์—ฌ ์ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ด์™ธ์—์ง€๋งŒ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์—์„œ ๊ฐ€๋ฒผ์šด ๋ณ€๊ฒฝ์ž…๋‹ˆ๋‹ค. 1.x์—์„œ ๋‹น์‹ ์˜ ๋ชจ๋ธ, ๋ทฐ ๋ฐ ์ ํฌ์˜ * ์ผ๋ถ€ *์™€ ํ•จ๊ป˜ Ext.application ๋ธ”๋ก ์•ˆ์— ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ชจ๋‘ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์  ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ข…์†์ ์œผ๋กœ ๋‚˜๋จธ์ง€๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๊ณณ๊ณณ์— ํผ์ ธ์žˆ๋‹ค ๋˜๊ฐ€, ์–ด๋ ต๊ฒŒ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ๊ตฌ์„ฑ๋˜์–ด ์ •ํ™•ํ•˜๊ณ  ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์„ผ์ฐจํ„ฐ์น˜2์—์„œ ์ปจํŠธ๋กค๋Ÿฌ ๋‚ด๋ถ€์— ๊ทธ ์ค‘ ์ผ๋ถ€๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ๋Œ€์‹  Ext.application ๋ธ”๋ก ๋‚ด๋ถ€์˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ์ข…์†์„ฑ์œผ๋กœ ๋ชจ๋‘ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์˜ˆ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค :

Ext.application({
    name: 'MyApp',
    
    icon: 'resources/images/logo.png',
    
    models: ['User', 'Group'],
    controllers: ['Users', 'Login'],
    views: ['Main', 'Users'],
    stores: ['Users'],
    
    launch: function() {
        Ext.create('MyApp.view.Main');
    }
});

์ปจํŠธ๋กค๋Ÿฌ

๊ทธ๋ƒฅ ๋ชจ๋ธ์—์„œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Sencha ํ„ฐ์น˜ 2 ๊ท€ํ•˜์˜ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋‹ค๋ฅธ ํด๋ž˜์Šค์ฒ˜๋Ÿผ ์ •์˜๋œ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ, Ext.regController ๊ธฐ๋Šฅ์ด ์ค‘์ง€๋˜๋„๋ก. 1.x์—์„œ ์ด๋Ÿฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค :

Ext.regController("searches", {
    showSomething: function() {
        alert('something');
    }
});

2.x์— ์•Œ๋งž๋Š” ์ชฝ์ž…๋‹ˆ๋‹ค :

Ext.define('MyApp.controller.Searches', {
    extend: 'Ext.app.Controller',
    
    showSomething: function() {
        alert('something');
    }
});

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์•ž์„œ ์–ธ๊ธ‰ํ•œ, 1.x์—์„œ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ถ”๊ฐ€ ๋ชจ๋ธ, ๋ทฐ ๋˜๋Š” ์ €์žฅ ์ข…์†์„ฑ์œผ๋กœ ์ •์˜ํ•˜๋Š” ๊ฒฝ์šฐ ํ•˜๋Š” ๋Œ€์‹  ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ์ด๋“ค์„ ์ด๋™ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๊ฒฝ๋กœ

์„ผ์ฐจํ„ฐ์น˜ 1.x์—์„œ์—์„œ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋Œ€๊ฐœ ๋‹จ์ง€ ์™ธ๋ถ€์ ์œผ๋กœ ํŒŒ๊ฒฌ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์˜ ์ปฌ๋ ‰์…˜ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ข…์ข… ์ด๊ฒƒ์€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ Ext.dispatch ํ˜ธ์ถœํ•œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค, ์ปจํŠธ๋กค๋Ÿฌ ์ด๋ฆ„์„ ์ง€์ •, ํ•จ์ˆ˜์— ์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋•Œ๋กœ๋Š” ์ž๋™์œผ๋กœ ํŒŒ๊ฒฌํ•ฉ๋‹ˆ๋‹ค, ๊ฒฝ๋กœ์—์„œ ํฌ์ฐฉ๋œ url์˜ ๋ณ€ํ™”์—์„œ ๊ณ„๊ธฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

2.x์—์„œ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ํ›จ์”ฌ ๋” ์ ๊ทน์ ์œผ๋กœ ๋˜๋ฉฐ, ๊ตฌ์„ฑ ๋ธ”๋ก ๋‚ด๋ถ€์— ๋Œ€ํ•ด ๊ด€์‹ฌ์ด ์žˆ๋Š”์ง€ ์ ๊ทน์ ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๊ฒฝ๋กœ ์ž…๋‹ˆ๋‹ค:

Ext.define('MyApp.controller.Searches', {
    config: {
        routes: {
            'search/:query': 'doSearch'
        }
    },
    
    doSearch: function(query) {
        alert('searching for ' + query);
    }
});

๋ณ„๋„์˜ ๊ฒฝ๋กœ ํŒŒ์ผ์— ๋Œ€ํ•œ ํ•„์š”์„ฑ์„ ๋ฌด์‹œํ•˜๊ณ  ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋Š” ๊ทธ ๋•Œ๋งˆ๋‹ค ํŽ˜์ด์ง€ URL์ด ์ผ์น˜ '๊ฒ€์ƒ‰ / ์ฟผ๋ฆฌ', doSearch์€ 'abc123'๋กœ ๋ถˆ๋ฆฌ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ URL์€ ๋‚˜์ค‘์— '# searches / sencha ,'doSearch '๋Š” sencha'๋กœ ๋‹ค์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ

์„ผ์ฐจํ„ฐ์น˜2 ์—์„œ ๋‹ค์ˆ˜์˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ตํ˜”์œผ๋ฉฐ, ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ์ฐธ์กฐ๋“ค ๋ฐ ์ปจํŠธ๋กค ์ž…๋‹ˆ๋‹ค. ๋” ์ž์„ธํ•œ ์ •๋ณด๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ€์ด๋“œ ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

Further Help

Once you've migrated everything you can based on this guide and updated all of your code so that there are no more console warnings, your app should be mostly functional. For any specific problems the best place to get help is on the Sencha Touch 2 Forums.

๋” ์ž์„ธํ•œ ๋„์›€๋ง

์ผ๋‹จ, ์ด ๊ฐ€์ด๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋” ์ด์ƒ ์ฝ˜์†” ๊ฒฝ๊ณ ๊ฐ€ ์—†๋„๋ก ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ์ด์ „ํ–ˆ์Šต๋‹ˆ๋‹ค, ์•ฑ์€ ๋Œ€๋ถ€๋ถ„ ๊ธฐ๋Šฅ์ ์ด์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋„์›€๋ง์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ์ข‹์€ ์žฅ์†Œ๋Š” ์„ผ์ฐจํ„ฐ์น˜2 ํฌ๋Ÿผ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.