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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형

Routing, Deep Linking and the Back Button

센차 터치 2는 history 와 deep-linking 을 지원합니다. 이것은 여러분의 애플리케이션에 두가지 큰 잇점을 드립니다.

- 앱 내에서 back 버튼을 사용할 수 있고 여기 저기 메뉴(화면)간 이동을 페이지 refresh 없이 정확하고 빠르게 할 수 있습니다.
- deep-linking은 유저가 앱 내의 어떤 부분이든 한번에 이동할 수 있도록 합니다.


이 기능은  native 앱에서 느낄 수 있는 효과를 줍니다. 특히 안드로이드 디바이스에서는 back button 이 있는데 이 기능을 완전히 지원합니다.





Setting up routes

history를 사용하는 방법은 아주 간단합니다. routes라는 개념을 염두에 두고 작업 하셔야 합니다. Route는 url과 콘트롤러 action들과의 간단한 매핑입니다. 해당 Controller action 중에  address bar 에서 특정 형식의 url을 감지하면 자동적으로 이 Route가 call 됩니다. 아래 Controller에 대한 간단한 예제를 살펴보겠습니다.

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

    config: {
        routes: {
            'products/:id': 'showProduct'
        }
    },

    showProduct: function(id) {
        console.log('showing product ' + id);
    }
});


위와 같이 route를 특정해 줌으로서 Main controller는 브라우저 url 이 #products/123 같은 형식이면 반응하게 됩니다. 예를 들어 여러분의 어플리케이션이 http://myapp.com 에 디플로이 됐다면 http://myapp.com/#products/123, http://myapp.com/#products/456, http://myapp.com/#products/abc 같은 형식의 url 이 되면 자동적으로 showProduct 함수가 call 됩니다.

showProduct 함수가 이렇게 call 되면 url을 제외한 id 토큰을 pass  합니다. route에 :가 포함돼 있으면 :id를 사용하게 됩니다. 그러면 url을 제외한 정보를 가져와서 함수에 전해주게 됩니다. 이 파싱된 토큰은 항상 string 입니다. (왜냐하면 url은 언제나 string 이기 때문입니다.) 그렇기 때문에 http://myapp.com/#products/456 를 입력하는 것은 showProduct('456')을 call 하는 것과 같습니다.

아래와 같이 여러개의 route를 가질수도 있고 route는 여러개의 토큰 가질 수도 있습니다.

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

    config: {
        routes: {
            'products/:id': 'showProduct',
            'products/:id/:format': 'showProductInFormat'
        }
    },

    showProduct: function(id) {
        console.log('showing product ' + id);
    },

    showProductInFormat: function(id, format) {
        console.log('showing product ' + id + ' in ' + format + ' format');
    }
});


두번째 route는 url이 #products/123/pdf 일 경우 showProductInFormat 함수를 통해서 콘솔에 showing product 123 in pdf 를 찍을 겁니다.

물론 실제로는 콘솔에 문자를 찍는 일은 필요 없겠죠. 그냥 여러분들이 필요한 기능을 이곳에 넣으시면 됩니다. 데이터를 수집한다던가 UI를 업데이트 한다던가 하는 일들을요.

Advanced Routes

디폴트로 라우트 안의 와일드 카드는 어떤 문자나 숫자가 와도 해당됩니다. 즉 products/:id/edit 는 #products/123/edit 과 매치 됩니다. 하지만 #products/a ,fd.sd/edit 와는 매치되지 않습니다. 두번째에 있는 스페이스나 쉼표 그리고 점은 정해 놓은 규칙에 맞지 않기 때문입니다.

경우에 따라서는 이러한 스페이스나 쉼표 점도 url에 매치될 필요가 있습니다. 예를 들어 url이 가져오고 전달할 파일 이름을 포함하고 있는 경우가 그것에 해당 될 겁니다. 이것을 구현하기 위해서는 route에 configuration 객체를 패스할 수 있습니다.

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

    config: {
        routes: {
            'file/:filename': {
                action: 'showFile',
                conditions: {
                    ':filename': "[0-9a-zA-Z\.]+"
                }
            }
        }
    },

    //opens a new window to show the file
    showFile: function(filename) {
        window.open(filename);
    }
});


action string 대신에 action 프로퍼티를 포함한 configuration 객체를 가지게 되었습니다. 여기에 conditions configuration도 추가했습니다. :filename 토큰에는 어떤 숫자나 문자 그리고 마침표가 올 수 있게 됬습니다. 이렇게 되면 http://myapp.com/#file/someFile.jpg 를 someFile.jpg 를 controller의 showFile 함수에 인자로서 패스하게 됩니다.

Restoring State

history나 deep linking을 지원하려면 유저가 deep link로 어떤 페이지에 가고자 했을 때 그 페이지의 UI 상태가 완전히 어딘가에 restore 돼 있을 필요가 있습니다. 그래야지 그 페이지를 신속하게 보여줄 수 있으니까요. 이것은 약간 트릭으로 보일 수 있지만 좀 더 간편한 애플리케이션 사용을 위해 필요한 사항 입니다.

http://myapp.com/#products/123 이런 식의 url 일 경우 product를 로딩하는 간단한 예제를 만들어 봅시다. 위 예제에서 Products Controller를 약간 수정하겠습니다.

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

    config: {
        refs: {
            main: '#mainView'
        },

        routes: {
            'products/:id': 'showProduct'
        }
    },

    /**
     * Endpoint for 'products/:id' routes. Adds a product details view (xtype = productview)
     * into the main view of the app then loads the Product into the view
     *
     */
    showProduct: function(id) {
        var view = this.getMain().add({
            xtype: 'productview'
        });

        MyApp.model.Product.load(id, {
            success: function(product) {
                view.setRecord(product);
            },
            failure: function() {
                Ext.Msg.alert('Could not load Product ' + id);
            }
        });
    }
});


끝이 products/:id 일 경우 앱의 메인 뷰(TabPanel 이나 다른 컨테이너 등)에 즉각 view가 추가 됩니다. 그리고 나서 서버로부터 Product를 구하기 위해 product model (MyApp.model.Product)를 사용합니다. 여기에 call back을 추가하고 새로 로드된 Product와 함께 product detail view를 보유 합니다. 즉시 UI를 렌더링 해서 유저에게 가능한한 빨리 비쥬얼한 화면을 보여주도록 합니다. (Product 가 로드돼 있다면 렌더링만 하는것 과 같은...)

deeply-linked view 에 대한 restoring state를 만들려면 각 앱마다 서로 다른 logic을 필요로 하게 될 겁니다. 예를 들어 Kitchen Sink는 NestedList 네비게이션 상태가 restore 될 필요가 있고 해당 url에 맞는 view를 렌더링 해야 할 겁니다. 이 기능이 Phone과 Tablet 프로파일들에서 구현되는지를 보시려면 Kitchen Sink 의  app/controller/phone/Main.js 와 app/controller/tablet/Main.js 파일들의 showView 함수를 살펴 보세요.

Sharing urls across Device Profiles

대부분 여러분은 Device profiles 사이에서 같은 route structure를 공유하기를 원할 겁니다. 이렇게 하면 유저가 폰에서 해당 url을 태블릿을 사용하는 친구에 보낼 수 있고 그 친구가 태블릿에서 제대로 된 정보를 같이 볼 수 있게 될 겁니다. 이렇게 하려면 Phone 과 Tablet-specific Controllers 의 수퍼클래스에 이 route configuration을 정의하는것이 가장 좋은 방법일 겁니다.

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

    config: {
        routes: {
            'products/:id': 'showProduct'
        }
    }
});


이제 Phone-specific subclass에서는 해당 product에 대한 Phone에 맞는 view를 보여주기 위해 이 showProduct  함수를 implement 할 수 있습니다.

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

    showProduct: function(id) {
        console.log('showing a phone-specific Product page for ' + id);
    }
});


Tablet-specific subclass 에서도 같은 작업을 해서 tablet에 맞는 view를 보여줄 수 있습니다.

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

    showProduct: function(id) {
        console.log('showing a tablet-specific Product page for ' + id);
    }
});


이 규칙에는 몇가지 예외가 있습니다.  Kitchen Sink 예제에는 phone 과 tablet 에 맞는 view들이 있습니다. 두 프로파일에서 우리는 NestedList를 사용해서 navigation을 합니다. 하지만 Tablet 에서만 스크린의 왼쪽에 NestedList 가 옵니다. Phone에서는 화면에 꽉 찰겁니다. Phone에서 back 버튼이 원하는대로 작동하게 하려면 매번 NestedList 안에서 navigate 할 때마다 새로운 url을 history에 push 해야 합니다. 이 말은 Phone-specific controller는 추가적인 route를 가진다는 의미입니다. 이 경우를 보시려면 app/controller/phone/Main.js 를 살펴 보시기 바랍니다.


반응형