티스토리 뷰

반응형
이 포스트는 MSDN에 있는 매트로 스타일 앱 처음으로 만들어 보기 페이지(http://msdn.microsoft.com/en-us/library/windows/apps/br211380.aspx)의 내용을 기반으로 작성이 되었으며, 절반 정도까지만 진행한 셈플을 제공한다.

* 사용되는 용어
Channel : 예를 들으면 블로그라고 보면 된다.
Feed data : 수신된 RSS 데이터를 이야기한다.

* XAML 프로그램을 개발을 해본 개발자를 기준으로 작성되어 있음

처음에 나오는 헬로 월드는 패스

Creating Windows Metro style apps in Visual Studio 이 위치 부터 시작한다.
이 곳에서는 Blog Reader를 간단하게 만들어 보는 예제이다. (이게 제일 처음 만드는 예제라고 하니..제일 쉬운가 보다..;;)

1. 윈도우 매트로 스타일 프로젝트를 만든다.

1) 물론 Visual Studio 11 Express Beta for Windows 8. 이넘이 깔려 있어야한다.
2) File -> New Project
3) Installed 패널에서 Visual C# 선택 (여기서는 Visual Basic는 다루지 않는다.)
4) Windwods Metro style 선택
5) Blank Application 선택
6) 이름은 WindowsBlogReader

7) OK


2. 만들고 나면 생기는 여러 파일들에 대해서 설명을 하는데. 여기서는 패스한다.


3. Specifying app capabilities : 앱에서 사용하는 기능을 지정
기본 적으로 앱을 하나 만들면 internetClient라는 기능이 기본적으로 지정이 되어 있다. 여기서 또 다른 기능을 추가하기 위해서는 솔루션 익스플로에서 Application Manifest Designer을 열고 Capabilities 탭으로 들어가면 지정이 가능하다.
지정이 가능한 기능에 대해서는 원본 페이지를 참조 한다.


4. Getting data into an app : 앱으로 데이터 가지고 오기
윈8에서는 Windows.Web.Syndication 네임스페이스 안에 여러개의 기본적인 규격들이 지정이 되어 있다. 그래서 사용자 정의 데이터 클래스를 만들고, RSS or Atom data 들을 비동기로 수신해서 사용하기로 한다.

1) FeedData.cs 생성 : 난 Models란 폴더를 만들고 그 안에 클래스를 추가했다.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Windows.Web.Syndication;

namespace WindowsBlogReader
{
    // FeedData
    // Holds info for a single blog feed, including a list of blog posts (FeedItem)
    public class FeedData
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public DateTime PubDate { get; set; }

        private List<FeedItem> _Items = new List<FeedItem>();
        public List<FeedItem> Items
        {
            get
            {
                return this._Items;
            }
        }
    }

    // FeedItem
    // Holds info for a single blog post
    public class FeedItem
    {
        public string Title { get; set; }
        public string Author { get; set; }
        public string Content { get; set; }
        public DateTime PubDate { get; set; }
        public Uri Link { get; set; }
    }

    // FeedDataSource
    // Holds a collection of blog feeds (FeedData), and contains methods needed to
    // retreive the feeds.
    public class FeedDataSource
    {
        private ObservableCollection<FeedData> _Feeds = new ObservableCollection<FeedData>();
        public ObservableCollection<FeedData> Feeds
        {
            get
            {
                return this._Feeds;
            }
        }

        public async Task GetFeedsAsync()
        {
            Task<FeedData> feed1 =
                GetFeedAsync("http://windowsteamblog.com/windows/b/developers/atom.aspx");
            Task<FeedData> feed2 =
                GetFeedAsync("http://windowsteamblog.com/windows/b/windowsexperience/atom.aspx");
            Task<FeedData> feed3 =
                GetFeedAsync("http://windowsteamblog.com/windows/b/extremewindows/atom.aspx");
            Task<FeedData> feed4 =
                GetFeedAsync("http://windowsteamblog.com/windows/b/business/atom.aspx");
            Task<FeedData> feed5 =
                GetFeedAsync("http://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx");
            Task<FeedData> feed6 =
                GetFeedAsync("http://windowsteamblog.com/windows/b/windowssecurity/atom.aspx");
            Task<FeedData> feed7 =
                GetFeedAsync("http://windowsteamblog.com/windows/b/springboard/atom.aspx");
            Task<FeedData> feed8 =
                GetFeedAsync("http://windowsteamblog.com/windows/b/windowshomeserver/atom.aspx");
            // There is no Atom feed for this blog, so we use the RSS feed.
            Task<FeedData> feed9 =
                GetFeedAsync("http://windowsteamblog.com/windows_live/b/windowslive/rss.aspx");
            Task<FeedData> feed10 =
                GetFeedAsync("http://windowsteamblog.com/windows_live/b/developer/atom.aspx");
            Task<FeedData> feed11 =
                GetFeedAsync("http://windowsteamblog.com/ie/b/ie/atom.aspx");
            Task<FeedData> feed12 =
                GetFeedAsync("http://windowsteamblog.com/windows_phone/b/wpdev/atom.aspx");
            Task<FeedData> feed13 =
                GetFeedAsync("http://windowsteamblog.com/windows_phone/b/wmdev/atom.aspx");

            this.Feeds.Add(await feed1);
            this.Feeds.Add(await feed2);
            this.Feeds.Add(await feed3);
            this.Feeds.Add(await feed4);
            this.Feeds.Add(await feed5);
            this.Feeds.Add(await feed6);
            this.Feeds.Add(await feed7);
            this.Feeds.Add(await feed8);
            this.Feeds.Add(await feed9);
            this.Feeds.Add(await feed10);
            this.Feeds.Add(await feed11);
            this.Feeds.Add(await feed12);
            this.Feeds.Add(await feed13);
        }

        private async Task<FeedData> GetFeedAsync(string feedUriString)
        {
            // using Windows.Web.Syndication;
            SyndicationClient client = new SyndicationClient();
            Uri feedUri = new Uri(feedUriString);

            try
            {
                SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri);

                // This code is executed after RetrieveFeedAsync returns the SyndicationFeed.
                // Process it and copy the data we want into our FeedData and FeedItem classes.
                FeedData feedData = new FeedData();

                feedData.Title = feed.Title.Text;
                if (feed.Subtitle.Text != null)
                {
                    feedData.Description = feed.Subtitle.Text;
                }
                // Use the date of the latest post as the last updated date.
                feedData.PubDate = feed.Items[0].PublishedDate.DateTime;

                foreach (SyndicationItem item in feed.Items)
                {
                    FeedItem feedItem = new FeedItem();
                    feedItem.Title = item.Title.Text;
                    feedItem.PubDate = item.PublishedDate.DateTime;
                    feedItem.Author = item.Authors[0].Name.ToString();
                    // Handle the differences between RSS and Atom feeds.
                    if (feed.SourceFormat == SyndicationFormat.Atom10)
                    {
                        feedItem.Content = item.Content.Text;
                        feedItem.Link = new Uri("http://windowsteamblog.com" + item.Id);
                    }
                    else if (feed.SourceFormat == SyndicationFormat.Rss20)
                    {
                        feedItem.Content = item.Summary.Text;
                        feedItem.Link = item.Links[0].Uri;
                    }
                    feedData.Items.Add(feedItem);
                }
                return feedData;
            }
            catch (Exception)
            {
                return null;
            }
        }
    }
}

그냥 복사해서 붙여넣기 신공으로..뚝딱..했는데..이렇게 하면 사실 실력이 늘지는 않는다는..될 수 있으면 하나하나 입력하면서 어떤 내용들인지 확인하는 것이 좋다.

2) Retrieving the feed data : 피드 데이터를 수신하기
Windows.Web.Syndication.SyndicationClient 클래스를 이용해서 RSS나 Atom 피드를 수신하고 XML 파싱도 함께 수행 해준다. RSS 데이터를 받을 때는 무조건 저넘으로 받으면 걱정이 없다.

3) Using await in C# and Visual Basic : await 사용하기
닷넷 4.5의 핵심인 async, await에 대한 부분이다. (말로 설명하기 너무 힘든 부분이라는..과거의 싱글스레드 방식의 프로그램과, 멀티스레드 방식의 프로그램 경험을 가지고 있으면, 좀 이해가 빨리 되는데..)

축약을 하자면 await를 사용하면 피드 데이터를 비동기로 불러오는 작업이 완료될때까지 기다리고, 모두 읽어 오면 다음으로 넘어간다.

아래 소스를 보면 우선 GetFeedAsync 메소드를 이용해서 하나의 피드를 읽어오는 Task를 선언하고, 해당 작업이 완료될 때까지 기다리다가 완료가 되면 Feeds에 추가가 된다.

public async Task GetFeedsAsync()
{
    Task<FeedData> feed1 =
        GetFeedAsync("http://windowsteamblog.com/windows/b/developers/atom.aspx");
    ...
    this.Feeds.Add(await feed1);
    ...
}

이번에는 GeeFeedAsync 메소드를 살펴 보면, 메소드 이름있는 곳에 async라는 키워드가 있는 것을 알 수 있다.

private async Task<FeedData> GetFeedAsync(string feedUriString)
{
...
}

그리고, 메소드 안에 await라는 키워드가 사용된 것을 볼 수 있는데, await를 사용하기 위해서는 async로 선언된 메소드 안에서만 사용이 가능하다는 것도 알 수 있다.

SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri);

이 부분이 await가 하는 일에 대해서 설명을 자세하게 해주고 있는데..잘 읽어 보기 바란다.
The await keyword here tells the compiler to do a lot of work for us behind the scenes. The compiler schedules the rest of the method after this call as a callback to be executed when the call returns. It then immediately returns control to the calling thread, typically the UI thread, so that the app remains responsive. The Task that represents the eventual outcome of this method, a FeedData object, is returned to the caller at this point.

RetrieveFeedAsync가 SyndicationFeed를 데이터를 가지고 반환되었을 때, 코드에서 feedData 클래스에 데이터를 입력한다.
( 코드 생략 : 위에서 한번 만들어 놓은 코드를 부분 부분 분석하는 중이라 코드는 동일 )
return 문장을 만났을 때 모든 작업이 완료될 때가지 기다린다.(자세한 내용은 본문을 참조)


5. Using the data in the app : 앱에서 데이터 사용하기

App.xaml.cs 파일에 몇가지 추가
앱 클래스에 DataSource라는 이름의 스테틱 데이터를 만들어 주고, 생성자에서 인스턴스 해준다.

    sealed partial class App : Application
    {
        // Add a static instance of FeedDataSource.
        public static FeedDataSource DataSource;

        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
       public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;

            // Instantiate the data source.
            DataSource = new FeedDataSource();

        }


6. BlankPage.xaml.cs 수정
일단 메소드에 async를 추가해 준다. 그래야 await를 사용할 수 있다.
그리고 App.DataSource를 _feedDataSource에 연결하고
_feedDataSource.Feeds.Count 가 0이면 GetFeedsAsync() 메소드를 실행해서 피드 데이터를 가지고 온다.
그리고, this.DataContext에 불러온 피드 중 첫번째 피드를 넣어 준다.

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            FeedDataSource _feedDataSource = App.DataSource;

            if (_feedDataSource.Feeds.Count == 0)
            {
                await _feedDataSource.GetFeedsAsync();
            }

            this.DataContext = (_feedDataSource.Feeds).First();
        }
여기까지 하면 데이터를 사용할 준비는 완료가 된다. 이제 xaml을 만들어 보자


7. Defining the app layout in XAML

디자인을 하기 위한 스케치가 보이는데 한번 읽어 보고 xaml을 만들어 보자


8. BlankPage.xaml 의 1차 완료 소스 이다.

<Page
    x:Class="WindowsBlogReader.BlankPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WindowsBlogReader"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="140" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <!-- Title -->
        <TextBlock x:Name="TitleText" Text="{Binding Title}"
                   VerticalAlignment="Center" FontSize="48" Margin="56,0,0,0"/>

        <!-- Content -->
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="2*" MinWidth="320" />
                <ColumnDefinition Width="3*" />
            </Grid.ColumnDefinitions>

            <!-- Left column -->
            <!-- The default value of Grid.Column is 0, so we do not need to set it  
                 to make the ListView show up in the first column. -->
            <ListView x:Name="ItemListView" 
                      ItemsSource="{Binding Items}"
                      Margin="60,0,0,10"
                      SelectionChanged="ItemListView_SelectionChanged">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding Title}" 
                           FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
                            <TextBlock Text="{Binding Author}"
                           FontSize="16" Margin="15,0,0,0"/>
                            <TextBlock Text="{Binding Path=PubDate, Converter={StaticResource dateConverter}}"
                           FontSize="16" Margin="15,0,0,0"/>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

            <!-- Right column -->
            <!-- We use a Grid here instead of a StackPanel so that the WebView sizes correctly. -->
            <Grid DataContext="{Binding ElementName=ItemListView, Path=SelectedItem}"
                  Grid.Column="1" Margin="25,0,0,0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <TextBlock x:Name="PostTitleText" Text="{Binding Title}" FontSize="24"/>
                <WebView x:Name="ContentView" Grid.Row="1" Margin="0,5,20,20"/>
            </Grid>
        </Grid>
    </Grid>

</Page>

xaml은 기존에 사용하던 형태와 크게 다르지 않다.


9. 이번 포스트는 여기까지 작성 하려고 한다.
Windows 8 에서 데이터 처리를 하는 방법 중 중요한 async, await에 대한 설명만을 했다. 그 외의 사항은 본문을 참고해서 따라하기를 하다가 막히는 부분에 대해서 리플로 문의를 하면 답변을 하는 방식으로 처리를 하도록 하겠다.

포스트의 내용은 본문의 절반까지만 작성이 된 것이다. 그 이후 작업에 대해서도 셈플을 만든 후 이 곳에 추가 하도록 하겠다.



반응형
댓글