Skip to content

DOC 06. [Writing App] Building your First App

NangChun edited this page Sep 7, 2012 · 6 revisions

Building your First App

첫번째 앱 만들기

Preparation

준비

This guide builds on the Getting Started Guide, which quickly gets you set up with the SDK and ensures your environment is fully functional. If you haven't been through this yet we suggest reading that guide first (it only takes a few minutes), then come back here to create your first app.

본 가이드는 빠르게 SDK를 설치하고 환경에서 완벽한 동작을 보장할 수 있도록 시작 가이드를 만들었다. 아직 익히지 않았다면 (오래 걸리지 않으므로) 다음의 첫 응용프로그램을 만드려면 시작가이드를 참고하길 바란다.

What we're going to build

만들어 보기

Today we're going to build a simple mobile website-like app that could be used for your company's mobile site. We'll add a home page, a contact form and a simple list to fetch our recent blog posts and allow our visitor to read them right there on the phone.

오늘은 회사의 모바일 사이트를 사용할 수 있는 간단한 모바일 웹 사이트와 같은 앱을 만들것이다. 홈페이지, 문의 양식 및 최근 블로그 게시물을 가져와 방문자가 휴대전화에서 바로 내용을 읽을 수 있도록 간단한 목록에 추가한다.

This is what we're going to build (it's interactive, try it yourself):

아래가 만들내용이다 (대화식이며, 따라서 작성하기 바란다):

@example raw portrait preview
Ext.application({
    name: 'Sencha',

    launch: function() {
        //The whole app UI lives in this tab panel
        //모든 앱의 UI는 이 탭 패널 안에 존재한다.
        Ext.Viewport.add({
            xtype: 'tabpanel',
            fullscreen: true,
            tabBarPosition: 'bottom',

            items: [
                // This is the home page, just some simple html
                // 이것은 홈페이지이며, 그냥 단순한 html페이지이다.
                {
                    title: 'Home',
                    iconCls: 'home',
                    cls: 'home',
                    html: [
                        '<img height=260 src="http://staging.sencha.com/img/sencha.png" />',
                        '<h1>Welcome to Sencha Touch</h1>',
                        "<p>Building the Getting Started app</p>",
                        '<h2>Sencha Touch (2.0.0)</h2>'
                    ].join("")
                },

                // This is the recent blogs page. It uses a tree store to load its data from blog.json
                // 이것은 최근 블로그 페이지다. blog.json에서 데이터를 로드하는 트리 저장소 사용한다.
                {
                    xtype: 'nestedlist',
                    title: 'Blog',
                    iconCls: 'star',
                    cls: 'blog',
                    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 contact page, which features a form and a button. The button submits the form                 
                // 폼과 버튼을 제공하는 연락처 페이지이다. 버튼으로 폼을 제출
                {
                    xtype: 'formpanel',
                    title: 'Contact Us',
                    iconCls: 'user',
                    url: 'contact.php',
                    layout: 'vbox',

                    items: [
                        {
                            xtype: 'fieldset',
                            title: 'Contact Us',
                            instructions: 'Email address is optional',

                            items: [
                                {
                                    xtype: 'textfield',
                                    label: 'Name',
                                    name: 'name'
                                },
                                {
                                    xtype: 'emailfield',
                                    label: 'Email',
                                    name: 'email'
                                },
                                {
                                    xtype: 'textareafield',
                                    label: 'Message',
                                    name: 'message',
                                    height: 90
                                }
                            ]
                        },
                        {
                            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
                                // 표시되는 첫 번째 form에 대한 참조를 받고, item 위의 스택을 조회
                                var form = this.up('formpanel');

                                // Sends an AJAX request with the form data to the url specified above (contact.php).
                                //(contact.php)위에서 지정된 URL로 form데이터와  AJAX요청을 보낸다.
                                // 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();
                                        });
                                    }
                                });
                            }
                        }
                    ]
                }
            ]
        });
    }
});

Getting Started

시작하기

The first thing we need to do is set up our application, just like we did in the Getting Started Guide. The app is using a {@link Ext.tab.Panel tab panel} that will hold the 4 pages so we'll start with that:

첫 번째 할 일은 시작 가이드에서 했었던 것처럼, 응용프로그램을 설정하는 일이다. 앱이 그렇게 시작될 수 있도록 4페이지를 가진 탭 패널을 사용한다:

@example raw miniphone
Ext.application({
    name: 'Sencha',

    launch: function() {
        Ext.create("Ext.tab.Panel", {
            fullscreen: true,
            items: [
                {
                    title: 'Home',
                    iconCls: 'home',
                    html: 'Welcome'
                }
            ]
        });
    }
});

If you run this in the browser (click the Preview button), a {@link Ext.tab.Panel TabPanel} should appear on top of the screen. The home page could be a bit more welcoming, so let's add some content to it and reposition the tab bar at the bottom of the page. We do that with the {@link Ext.tab.Panel#tabBarPosition tabBarPosition} config and by adding a bit of HTML:

이 브라우저 (미리보기 버튼을 클릭) TabPanel에서 작업을 실행하면 화면 상단에 나타난다. 홈 페이지가 좀 더 환영 페이지에 여기에 몇 가지 내용을 추가하고 페이지 하단으로 탭바의 위치를 바꾸자. Panel#tabBarPosition설정과 약간의 HTML을 추가하여 해당 작업을 수행한다 :

@example raw portrait
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("")
                }
            ]
        });
    }
});

Click the Preview button next to the example to have a look: you should see some HTML content but it won't look very good. We'll add a {@link Ext.Component#cls cls} config and add it to the panel, adding a CSS class that we can target to make things look better. All of the CSS we're adding is in the examples/getting_started/index.html file in the SDK download. Here's how our home page looks now:

결과를 볼수 있도록 예제 옆의 미리보기 버튼을 클릭 : HTML 컨텐츠를 볼 수 있지만 매우 보기 좋지 않다. 패널에 Component#CLS설정을 추가하면, 글자가 더 잘 보일 수 있도록 CSS 클래스가 적용된다.

@example raw preview portrait
Ext.application({
    name: 'Sencha',

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

            items: [
                {
                    title: 'Home',
                    iconCls: 'home',
                    cls: '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</h2>'
                    ].join("")
                }
            ]
        });
    }
});

Adding The Blogs Page

블로그 페이지를 추가

Now that we have a decent looking home page, it's time to move on to the next screen. In order to keep the code for each page easy to follow we're just going to create one tab at a time and then combine them all together again at the end.

이젠 적당한 홈페이지를 찾아서, 다음 화면으로 넣을 차례이다. 각 페이지를 간단한 코드로 작성하기 위해 한 번에 하나의 탭을 만든 다음 끝 부분에 다시 모두를 함께 결합할 것이다.

For now, we'll remove the first tab and replace it with a List. We're going to be using Google's Feed API service to fetch the feeds. There's a bit more code involved this time so first let's take a look at the result, then we can see how it's accomplished:

지금, 첫 번째 탭을 제거하고 리스트로 대체한다. Google의 Feed API 서비스에서 피드를 가져 온다. 시간이 있으니 먼저 결과를 살펴 보자 연관된 코드가 좀 더 있으므로, 이 다음엔 완성된 것을 확인할 수 있다 :

@example raw portrait preview
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'));
                        }
                    }
                }
            ]
        });
    }
});

You can click the 'Code Editor' button above the example to see the full code, but we'll go over it piece by piece. Instead of a panel we're using a {@link Ext.dataview.NestedList nestedlist} this time, fetching the most recent blog posts from sencha.com/blog. We're using Nested List so that we can drill down into the blog entry itself by simply tapping on the list.

위의 예제에서 '코드 편집기'버튼을 클릭 하면 코드 전체를 볼 수 있다, 하지만 천천히 진행하자. 패널의 sencha.com/blog에서 가장 최근의 블로그 게시물을 가져 오는 시간을 nestedlist가 사용하고 있다. 단순히 목록을 클릭하면 블로그item 자체로 가져와 다운 할 수 있도록 중첩 목록을 사용한다. 이번에는 패널보다 nestedlist를 사용한다, sencha.com/blog에서 가장 최근의 블로그 게시물을 가져온다. 간단히 목록을 클릭하면 블로그 item 자체를 검색 할 수 있도록 중첩목록을 사용하고 있다.

Let's break down the code above, starting with just the list itself:

위의 코드를 고장내어 직접 정리하고 시작해보자 :

@example raw portrait
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'
                            }
                        }
                    }
                }
            ]
        });
    }
});

We're giving the Nested List a few simple configurations - title, iconCls and displayField - and a more detailed one called {@link Ext.dataview.NestedList#store store}. The Store config tells the nested list how to fetch its data. Let's go over each store config in turn:

중첩 리스트에서 몇가지 간단한 환경설정을 줄 수 있다 - 제목, iconCls와 displayField - 그리고 더 자세한 내용은 NestedList#store 에서 가져온다. 중첩된 목록에 데이터를 어떻게 가져올지 저장소에 설정값을 알려준다. 다음의 각 저장소 설정값을 검토하자 :

  • type: tree creates a {@link Ext.data.TreeStore tree store}, which NestedList uses
  • fields tells the Store what fields we're expecting in the blog data (title, content, author etc)
  • proxy tells the Store where to fetch its data from. We'll examine this more closely in a moment
  • root tells the root node it is not a leaf. Above we'd set the leaf defaultValue to true so we need to override that for the root

** 유형 : 트리 *NestedList가 사용하는 트리 저장소를 생성한다. ** 필드 * 블로그 데이터(제목, 내용, 작성자 등)를 요구하는 필드 저장소를 알려준다. ** 프록시 * 데이터를 가져올 저장소를 알려준다. 중요해질 때 더 자세히 검토 ** 루트 * leaf가 아닌 루트 노드를 알려준다. 위에서 leaf의 기본값을 true로 설정 할 수 있도록 루트를 재정의 할 필요가 있다.

Of all the Store configurations, proxy is doing the most work. We're telling the proxy to use Google's Feed API service to return our blog data in JSON-P format. This allows us to easily grab feed data from any blog and view it in our app (for example try swapping the Sencha blog url for http://rss.slashdot.org/Slashdot/slashdot above to see it fetch Slashdot's feed).

프록시는 모든 저장소의 환경설정에 관련된 대부분의 일을 하고 있다. JSON-P 형식으로 블로그 데이터를 반환하기 위해 Google의 feed API 서비스를 사용하는 프록시를 알려준다. 블로그에서 feed 데이터를 쉽게 잡아 (예를 들면 Slashdot의 feed를 가져오는 것이 확인되면 위의 센차 블로그의 URL http://rss.slashdot.org/Slashdot/slashdot을 교환) 앱에서 볼 수 있다.

The last part of the proxy definition was a Reader. The reader is what decodes the response from Google into useful data. When Google sends us back the blog data, they nest it inside a JSON object that looks a bit like this:

위의 프록시 마지막 정의 부분은 reader이다. reader는 Google에서의 디코드 응답을 담은 유용한 데이터다. 구글에 다시 블로그 데이터를 돌려보내면, JSON 객체 내부에 아래와 비슷한 내용을 보내온다 :

{
    responseData: {
        feed: {
            entries: [
                {author: 'Bob', title: 'Great Post', content: 'Really good content...'}
            ]
        }
    }
}

The bit we care about is the entries array, so we just set our Reader's {@link Ext.data.reader.Json#rootProperty rootProperty} to 'responseData.feed.entries' and let the framework do the rest.

짧은 내용중 entries 배열을 관심있게 보자, 'responseData,feed.entries'와 프레임워크가 나머지를 처리하도록 두고 우리는 reader의 rootProperty를 설정한다.

Digging In

파고 들어가기

Now that we have our nested list fetching and showing data, the last thing we need to do is to allow the user to tap on an entry to read it. We're just going to add two more configurations to our Nested List to finish this off:

이제 데이터를 가져와서 보여주는 중첩목록을 가지고, 마지막으로 해야 할 것은 사용자가 읽어 항목을 터치 할 수 있도록하는 것이다. 이 기능을 완료하기 위해 중첩 목록에 두가지 환경설정을 추가한다 :

{
    xtype: 'nestedlist',
    //all other configurations as above
    //위와 같은 기타 환경설정
    
    detailCard: {
        xtype: 'panel',
        scrollable: true,
        styleHtmlContent: true
    },

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

Here we've just set up a {@link Ext.dataview.NestedList#detailCard detailCard}, which is a useful feature of Nested List that allows you to show a different view when a user taps on an item. We've configured our detailCard to be a scrollable {@link Ext.Panel Panel} that uses {@link Ext.Panel#styleHtmlContent styleHtmlContent} to make the text look good.

중첩리스트의 유용한 기능으로 사용자가 item에 들어갈때 다른 뷰를 표시 할 수 있게 detailCard를 설정한다. 텍스트가 잘 보이도록 styleHtmlContent를 사용하는 스크롤 패널로 detailCard를 구성하였다.

The final piece of the puzzle is adding an {@link Ext.dataview.NestedList#itemtap itemtap} listener, which just calls our function whenever an item is tapped on. All our function does is set the detailCard's HTML to the content of the post you just tapped on and the framework takes care of the rest, animating the detail card into view to make the post appear. This was the only line of code we had to write to make the blog reader work.

퍼즐의 완성을 위한 마지막 조각으로 itemtap 수신기를 추가, item을 탭할 때 마다 함수를 호출한다. 게시물 작성하면 detailCard의 HTML에 컨텐츠를 올리고 게시물이 생기면 애니메이팅 하였으며, 나머지는 프레임워크가 맡도록 모든 기능을 설정하였다. 오로지 블로그를 읽어들이는 작업을 하기위해 코드라인을 작성 하였다.

Creating a Contact Form

연락처 양식 작성하기

The final thing we're going to do for our app is create a contact form. We're just going to take the user's name, email address and a message, and use a {@link Ext.form.FieldSet FieldSet} to make it look good. The code for this one is simple:

마지막으로 앱을 위해 할 일은 연락처 양식을 만드는 것이다. 보기좋게 하기 위해 사용자의 이름, 이메일 주소와 메시지를 띄우고 FieldSet을 사용한다. 이 코드는 간단하다 :

@example raw portrait
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();
                            }
                        }
                    ]
                }
            ]
        });
    }
});

This time we're just creating a {@link Ext.form.Panel form} that contains a {@link Ext.form.FieldSet fieldset}. The fieldset contains three fields - one for name, one for email and one for a message. We've using a {@link Ext.layout.VBox VBox layout}, which just arranges the items vertically on the page one above the other.

fieldset을 포함하는 form을 만들고 있다. 이름, 이메일, 메시지 - fieldset은 각각 하나씩 세 필드를 포함하고 있다. VBox레이아웃을 사용하여 페이지 위에 item과 그 외 것을 수직으로 정렬한다.

At the bottom we have a {@link Ext.Button Button} with a tap {@link Ext.Button#handler handler}. This employs the useful {@link Ext.Container#up up} method, which returns the form panel that the button is inside of. We then just call {@link Ext.form.Panel#method-submit submit} to submit the form, which sends it to the url we specified above ('contact.php').

아래에는 버튼과 탭 처리기가 있다. 버튼의 내부에서 폼 패널을 반환하는 유용한 방법을 쓰고 있다. submit을 호출 하면 폼을 제출하고, 위에서 지정한 ('contact.php')URL에 보낸다.

Pulling it all together

모두 합치기

Now that we've created each view individually it's time to bring them all together into our completed app.

이젠 완료된 앱에 개별적으로 만든 각 뷰를 합칠 시간이다.

@example raw preview portrait
//We've added a third and final item to our tab panel - scroll down to see it
//탭 패널에 세 번째와 마지막 item 추가 - 보일 때까지 아래로 스크롤
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
                //새로운 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();
                            }
                        }
                    ]
                }
            ]
        });
    }
});

You can find the full source code of the getting started app in the examples/getting_started folder of the Sencha Touch 2.0 SDK download. 앱 시작하기 예제는 센차터치 2.0 SDK 다운로드의 /getting_started 폴더에서 전체 소스 코드를 찾을 수 있다.

Clone this wiki locally