저녁마다 앱 만든다고 뚝딱뚝딱하고 있다가, Web Service에서도 사용해야하는 데이터가 있어서, PCL(Portable Class Library)를 추가하려고 했는데.. 않되더군요..쿨럭 이게 무슨일인가하고 내용을 잘 살펴보니 PCL은 Visual Studio 2013까지만 지원을하고 Visual Studio 2017에서는 .Net Standard를 사용하라고 나와있더군요..그래서, 공부도 할겸 포스팅을 하기로 했습니다.



1. .Net Standard가 뭔가요?


과거에 우리는 이렇게 복잡한 세상에서 개발을 진행 했습니다. 닷넷 프레임웍 따로, 닷넷 코어 따로, 자마린 따로..

클래스 라이브러리도 각각 만들어서 사용하고 있었죠..




이런 복잡한 구조를 개선하기 위해 닷넷 스텐다드가 추가되었습니다!


1) 닷넷 스탠다드는 코드를 공유하기 편리합니다.


과거 PCL을 이용해서 코드를 공유하던 것을 쉽게 대체할 수 있습니다.


2) 더 많은 API를 사용할 수 있습니다.


1.6에서 1.3k의 정도의 API를 사용할 수 있었지만, 2.0은 32k의 API 사용이 가능합니다.


3) 닷넷 프레임웍과 호환성이 좋습니다.


대부분의 NuGet package들이 닷넷 프레임웍을 대상으로 만들어져 있습니다. 그래서, 호환성을 높여서 닷넷 스텐다드 2.0을 이용할 경우 NuGet package의 70% 정도를 그대로 사용할 수 있습니다. 




위에서도 보았듯이 지원하는 플랫폼이 상당히 많습니다. 1.0이 나온것이 2016년이였는데(1.0 출시 안내).. 어느덧 2.0이..그동안 PCL만 사용했었는데..이제는 이 녀석을 이용해서 개발을 해야 할 것 같습니다.


.NET 표준 1.0 1.1 1.2 1.3 1.4 1.5 1.6 2.0
.NET Core 1.0 1.0 1.0 1.0 1.0 1.0 1.0 2.0
.NET Framework(.NET Core 1.x SDK 포함) 4.5 4.5 4.5.1 4.6 4.6.1 4.6.2
.NET Framework(.NET Core 2.0 SDK 포함) 4.5 4.5 4.5.1 4.6 4.6.1 4.6.1 4.6.1 4.6.1
Mono 4.6 4.6 4.6 4.6 4.6 4.6 4.6 5.4
Xamarin.iOS 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.14
Xamarin.Mac 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.8
Xamarin.Android 7.0 7.0 7.0 7.0 7.0 7.0 7.0 8.0

Universal Windows Platform (UWP)

10.0 10.0 10.0 10.0 10.0 16299 16299 16299
8.0 8.0 8.1
Windows Phone 8.1 8.1 8.1
Windows Phone Silverlight 8.0


UWP 지원에 대한 부분은 여기를 참고하세요. 

대충 아시겠죠? 이제 직접 사용해 보도록 하겠습니다.


닷넷 스탠다드 2.0에서 사용가능한 API 조회는 여기서~



2. .NET Standard 클래스 라이브러리 만들기


Windows Template Studio를 이용해서 Blank, Basic MVVM pattern 프로젝트를 생성합니다.


혹시 Windows Template Studio를 모르시는 분은 여기를 클릭하세요


이제 .Net Standard 프로젝트를 여기에 추가해줍니다.


솔루션에서 마우스 오른쪽 Add -> New Project...



.NET Standard선택 -> Class Libaray (.NET Standard) 선택 -> 이름 입력 후 OK



추가된 프로젝트 속성을 확인해보면 .NET Standard 2.0이라고 표시된 것을 확인할 수 있습니다.




3. UWP앱이랑 연결을 시키기..


UWP앱에서 마우스 오른쪽 -> Add -> Reference...선택



Projects -> NetStandard.Standard 프로젝트를 체크 하고 OK를 누르면 완료 입니다.



빌드를 해보도록 하죠.


이런..빌드를 했더니, 오류가 쫘르륵!! 위의 표에서 보면 이유를 알 수 있습니다. .NET Standard 2.0은 Windows 10 Fall Creators Update 버전만 지원합니다. 그래서, UWP 앱의 최소 지원 버전을 변경해 주어야 합니다.



UWP앱의 속성에 들어갓가서 아래와 같이 최소 버전을 Fall Creators Update로 변경해 주시고 다시 빌드를 하시면 완료됩니다.




4. DataSet 사용이 가능??


닷넷의 오래된 기능으로 메모리에 데이터를 저장하는 DataSet을 사용할 수 있었습니다. 그런데, 그동안 UWP에서는 지원이 앙되던 녀석이 였는데, .NET Standard 2.0에서 부활 했습니다.~


아래와 같이 TestClass를 만들어 보도록 하겠습니다.

* 아래 코드는 여기서 복사했습니다.


using System.Data;

namespace NetStandard.Standard.Helpers
{
    public static class TestClass
    {
        public static DataSet GetXmlFromDataSet()
        {
            // Create two DataTable instances.
            var table1 = new DataTable("patients");
            table1.Columns.Add("name");
            table1.Columns.Add("id");
            table1.Rows.Add("sam", 1);
            table1.Rows.Add("mark", 2);

            var table2 = new DataTable("medications");
            table2.Columns.Add("id");
            table2.Columns.Add("medication");
            table2.Rows.Add(1, "atenolol");
            table2.Rows.Add(2, "amoxicillin");

            // Create a DataSet and put both tables in it.
            var set = new DataSet("office");
            set.Tables.Add(table1);
            set.Tables.Add(table2);
           
            // Visualize DataSet.
            return set;
        }
    }
}




이 녀석을 UWP의 ViewModel에서 호출해서 사용해 보겠습니다.


리샤퍼를 사용하시는 경우에는 업데이트를 해야 인텔리 센스가 정상 동작하는 것 같습니다. 참고해주세요~

JetBrains ReSharper Ultimate 2017.2.2  Build 109.0.20171006.122324
ReSharper 2017.2.20171006.123800


using Windows.UI.Xaml.Navigation;
using NetStandardSample.Helpers;
using NetStandardSample.Services;
using NetStandard.Standard.Helpers;


namespace NetStandardSample.ViewModels
{
    public class MainViewModel : Observable
    {
        private string _xmlText;

        public MainViewModel()
        {
            NavigationService.Navigated += NavigationService_Navigated;
        }

        public string XmlText
        {
            get => _xmlText;
            set => Set(ref _xmlText, value);
        }

        private void NavigationService_Navigated(object sender, NavigationEventArgs e)
        {
            var set = TestClass.GetXmlFromDataSet();
            if (set == null) return;
            XmlText = set.GetXml();

        }
    }
}


MainPage.xaml


MainPage.xaml.cs에 


//public MainViewModel ViewModel { get; } = new MainViewModel(); 문장은 주석 처리했습니다.


<Page
    x:Class="NetStandardSample.Views.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewModels="using:NetStandardSample.ViewModels"
    Style="{StaticResource PageStyle}"
    mc:Ignorable="d">
    <Page.DataContext>
        <viewModels:MainViewModel />
    </Page.DataContext>

    <Grid
        x:Name="ContentArea"
        Margin="{StaticResource MediumLeftRightMargin}">

        <Grid.RowDefinitions>
            <RowDefinition x:Name="TitleRow" Height="48" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TextBlock
            x:Name="TitlePage"
            x:Uid="Main_Title"
            Style="{StaticResource PageTitleStyle}" />

        <Grid
            Grid.Row="1"
            Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}">
            <TextBox Text="{Binding XmlText, Mode=TwoWay}" AcceptsReturn="True" />
        </Grid>
    </Grid>
</Page>


실행 결과는 아래와 같습니다. 정상적으로 DataSet을 반환 받아서, 메소드까지 실행해서 xml형태로 화면에 출력했습니다.


그렇다면.. 다른 프로젝트에서도 동일하게 동작할까용?



콘솔 프로젝트를 추가하고 사용해 보았습니다. 결과는 대만족입니다.




5. 소스


NetStandard.Sample.zip



PS. 그런데..Fall Creators Update UWP에서는 property marker가 제대로 동작하지 않는 버그가 있습니다. 신고는 했구용 다음번 업데이트 때 개선될 것으로 예상합니다. 흐흐


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

라즈베리파이에서 한글 음성 출력은 이전 포스트에서 보듯이 나름 간단히 해결을 했습니다. 그런데, 진짜 문제는 음성 인식에서 막혔네요.. 기본적으로 윈도우 10에서 한국어 지원이 원할하지 않기 때문에, 여러가지 시도를 해보았지만, 결국 성공한 것은 Get started with speech recognition using REST API를 이용하는 방법이 였습니다. 



0. 기본적인 내용


UWP를 이용해서 언어 인식하는 작업은 SpeechAndTTS라는 셈플 앱만 보더라도 어렵지 않습니다. 다만, 한글이 정식 지원이 되지 않기 때문이 이러고 있는 것입니다.


Bing Speech이용하는 방법도 사실 Nuget package를 지원하기 때문에 사용하기 쉽습니다.  다만, 라즈베리 파이가 ARM을 사용하기 때문에 ARM에서 실행되는 Nuget package가 없어서 REST API를 이용하는 것입니다.


REST API 셈플대로 하면 간단할 것 같은데...라고 생각하실 수 있을 것 같습니다. 셈플에서는 HttpWebRequest를 이용하였고, 저는 HttpClient를 이용했는데, 이로인해 발생되는 차이점을 극복하는데 시간이 좀 걸렸습니다.


결국 엄청난 삽질의 결과라는 것을..



1. Azure서비스 가입 및 Bing Speech API 구독하기


우선 이 서비스는 Azure의 Cognitive Services 중 Bing Speech API라는 녀석으로, 사용하기 위해서는 여기서 사용자 등록을 해서 인증키를 받아야 합니다.


아래 이미지는 정상적으로 API 키를 발급 받은 모습입니다. 설명에는 20분당 5,000개의 음성 인식을 처리할 수 있고 남은 날짜가 25일이라고 나옵니다. 처음에는 30일이 였습니다.




2. Windows Template Studio를 이용해서 Blank앱을 생성 합니다.


Windows Template Studio에 대한 포스트는 블로그 검색으로 찾으시면 쉽게 하실 수 있습니다.


이번에 가을 업데이트가 되면서 버전이 올라가서 전에는 보이지 않던, Caliburn.Micro가 추가 되어 있습니다. MVVM Basic를 선택하시고 Next를 누르시고, 페이지는 추가하지 않고, 완료를 해서 프로젝트를 생성 합니다.




3. 화면 디자인


MainViewModel를 DataContext에 연결하고, 버튼 2개와 택스트 박스를 추가해 넣었습니다.


<Page
    x:Class="IoTSampleWithWTS.Views.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewModels="using:IoTSampleWithWTS.ViewModels"
    Style="{StaticResource PageStyle}"
    mc:Ignorable="d">
    <Page.DataContext>
        <viewModels:MainViewModel />
    </Page.DataContext>

    <Grid
        x:Name="ContentArea"
        Margin="{StaticResource MediumLeftRightMargin}">

        <Grid.RowDefinitions>
            <RowDefinition x:Name="TitleRow" Height="48" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TextBlock
            x:Name="TitlePage"
            x:Uid="Main_Title"
            Style="{StaticResource PageTitleStyle}" />

        <Grid
            Grid.Row="1"
            Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}">

            <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                    <Button Content="Start" FontSize="40" Margin="10" Command="{Binding StartRecodingCommand}" />
                    <Button Content="Stop" FontSize="40" Margin="10" Command="{Binding StopRecodingCommand}" />
                </StackPanel>
                <TextBox x:Name="Result" Text="{Binding ResponseText}" FontSize="20" />
            </StackPanel>

        </Grid>
    </Grid>
</Page>




4. 마이크 사용 권한 얻기 및 음성 파일 저장


우선 앱의 Package.appxmanifest 파일을 열어서, Capabilities를 선택하신 후 Microphone을 체크해 줍니다. 


그리고, 음성을 녹음한 후에 서비스로 전달 하고 결과를 반환 받아서 택스트를 출력합니다.


음성 녹음에 관련된 부분은 MicrophoneHelper이며, 오픈 소스를 참고 했으며, async - await 패턴으로 변경하고, 권한 체크하는 로징을 추가해서 완성 했습니다. (참고한 오픈 소스 링크가 남아 있지 않아서 링크를 추가하지 못했습니다.) 



5. BingSpeechHelper.cs


가장 고생한 부분이 이 부분입니다. 일단 저장된 파일을 열때 반드시 FileStream으로 열어야 제대로 전송이 됩니다.


그리고, 헤더 부분을 HttpWebRequest에서 처럼 구성 하는 부분이였습니다. 


한번 성공을 하고나서 인식율을 올리기 위해서 조정했던 옵션이 Interactive인지 Conversation인지 Dictation인지를 정하는 부분이 였습니다. 이 옵션에 따라서 반환되는 결과가 서로 다릅니다. DeserializeObject할 모델에 영향을 주는 부분이니 미리 택스트를 보시고 모델을 만들어 주셔야 합니다.


using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Windows.Storage;
using IoTSampleWithWTS.Interfaces;
using IoTSampleWithWTS.Models;
using Newtonsoft.Json;

namespace IoTSampleWithWTS.Helpers
{
    internal class BingSpeechHelper
    {
        private const string INTERACTIVE = "interactive";
        private const string CONVERSATION = "conversation";
        private const string DICTATION = "dictation";

        private readonly string _language = "ko-KR";
        private readonly string _requestUri;
        private IAuthenticationService _authenticationService;

        public BingSpeechHelper()
        {
            //&format=detailed
            _requestUri =
                $@"https://speech.platform.bing.com/speech/recognition/{
                    INTERACTIVE}/cognitiveservices/v1?language={
                        _language}";

        }

        public async Task<string> GetTextResultAsync(string recordedFilename)
        {
            var file = await ApplicationData.Current.LocalFolder.GetFileAsync(recordedFilename);

            using (var fileStream = new FileStream(file.Path, FileMode.Open, FileAccess.Read))
            {
                using (var client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
                    client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("text/xml"));
                    client.DefaultRequestHeaders.TransferEncoding.Add(TransferCodingHeaderValue.Parse("chunked"));
                    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "API키를 입력하세요");

                    var content = new StreamContent(fileStream);
                    content.Headers.Add("ContentType", new[] {"audio/wav", "codec=audio/pcm", "samplerate=16000"});

                    try
                    {
                        var response = await client.PostAsync(_requestUri, content);
                        var responseContent = await response.Content.ReadAsStringAsync();
                        var speechResults = JsonConvert.DeserializeObject<BinSpeechResult>(responseContent);

                        content.Dispose();

                        return speechResults.DisplayText;
                    }
                    catch (Exception e)
                    {
                        content.Dispose();
                        Console.WriteLine(e);
                        throw;
                    }
                }
            }
        }
    }
}



6. MainViewModel.cs


Start버튼을 클릭하면, test.wav 파일을 로컬스토리지에 저장하고, Stop 버튼을 클릭하면 해당 파일을 Speech API로 전송해서 결과를 받아오고 ResponseText에 넣어서 화면에 출력합니다.


using System.Windows.Input;
using IoTSampleWithWTS.Helpers;

namespace IoTSampleWithWTS.ViewModels
{
    public class MainViewModel : Observable
    {
        private string _responseText;

        public MainViewModel()
        {
            Init();
        }

        public ICommand StartRecodingCommand { get; set; }

        public ICommand StopRecodingCommand { get; set; }

        public string ResponseText
        {
            get => _responseText;
            set => Set(ref _responseText, value);
        }

        private void Init()
        {
            StartRecodingCommand = new RelayCommand(async () =>
            {
                await Singleton<MicrophoneHelper>.Instance.StartRecordingAsync("test.wav");
            });

            StopRecodingCommand = new RelayCommand(async () =>
            {
                await Singleton<MicrophoneHelper>.Instance.StopRecordingAsync();

                var result = await Singleton<BingSpeechHelper>.Instance.GetTextResultAsync("test.wav");
                if (result == null) return;
                ResponseText = result;

            });
        }
    }
}



7. 한계점


REST API는 15초 이상 오디오 전송 불가, 오디오 중간에 결과 반환 불가, 오디오를 LUIS로 직접 전달하는 것 불가라는 한계가 존재 합니다. 더 자세한 사항은 여기를 참고하세요.


아..그런데 방금 생각난 내용인데요..JavaScript용 라이브러리를 사용하면 ARM에서 사용이 가능할 것도 같습니다. 테스트를 해보아야 겠지만요..(테스트라 쓰고 삽질이라 읽어야 하지만..)





8. 소스


Speech API 키를 발급받아서 소스에 넣고 실행하셔야 정상적으로 결과를 보실 수 있습니다.


IoTSampleWithWTS.zip




블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

얼마전 구입했던 라즈베리 파이에 한국어 음성을 출력하기 위한 과정을 간단하게 포스팅 하겠습니다.

1. 음성 입/출력에 대한 기본 사항

UWP에 기본 내장되어 있는 Voice Recognition, TTS가 지원하는 언어는 여기를 참고하시면 됩니다.

Speech Recognition is only available for the following languages: English (United States and United Kingdom), French, German, Japanese, Mandarin (Chinese Simplified and Chinese Traditional), and Spanish.

그런데, 중요한 건... 아직 한글은 미지원이라는.. 물론 언젠가는 하겠지만.. 그 때까지 기다리기는 너무 오래 걸리니 일단 가능한 방법을 찾아 보도록 하겠습니다.


2. 라파에 한글 Speech Synthesis 파일 복사하기

윈도우에서 Speech 관련된 파일을 다운로드 받습니다.


여기서 다운받은 파일은 Speech Recognition 관련 파일은 아닙니고, Speech Synthesis 관련 파일 이며, 다운로드 경로는 C:\Windows\Speech_OneCore\Engines\TTS 폴더에 들어갑니다. 추가로 C:\Windows\System32\Speech_OneCore\common\ko-KR 폴더가 생성되며, tokens_TTS_ko-KR.xml 파일이 생성이 되며, 이 파일들을 라파로 복사하면 음성을 만들 수 있으며. 라파는 언어팩 다운로드 기능이 없기 때문에 다른 언어도 동일한 방법으로 작업을 해야 합니다. 딱히 한국어만 앙되는 것은 아닙니다. 더 자세한 사항은 블로그 포스트를 참고하시면 됩니다.

저는 라파의 c$를 네트워크 폴더로 잡고, 복사하는 방법을 사용했으며, System32\Speech_OneCore\common 폴더는 사용자가 임으로 파일을 복사해서 넣을 수 있는 권한이 없어서, 폴더 권한에 administrator를 추가해서 복사를 완료했습니다.

복사를 진행한 후 반드시 재부팅을 하셔야 인식을 하며, SpeechAndTTS와 같은 셈플 앱을 라파에서 실행하시면 한국어를 선택하 실 수 있습니다.



음성 출력 동영상을 촬영했는데, 소리가 좀 적어서 패스 하도록 하겠습니다.

다음 포스트에서 UWP ARM에서 한글 음성 인식 방법에 대해서 포스팅 하도록 하겠습니다.


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

Windows Template Studio를 이용해서 UWP 앱 개발 시작하기 포스트에서 이어서, 활용 및 심화 과정 포스트를 시작 하도록 하겠습니다. 자세한 설명보다는 간단하게 개념을 정리하고, 상세 내용을 알기 위해서는 어디를 참고하면 되는지에 대한 정보 제공을 목표로 합니다.



1. 프로젝트 생성


File -> New -> Project -> Windows Template Studio -> Name : WTSSample1 -> OK -> Blank -> MVVM Basic -> Next 여기까지는 공통으로 하고, 


Suspend and Resume +, Background Task + 이름은 WTSBackgroundTask 2개를 선택하고 Create를 눌러서 완료 합니다.



2. 백그라운드 작업에 대한 변경 사항


Windows 10 Anniversary Update가 되면서, 추가된 기능 중에 하나 중 기존 Multi Process Model에서만 Background 작업을 할 수 있던 것을 Single Process Model에서도 가능하도록 했습니다. 이 기능을 수행하기 위해서 새로운 앱 라이프 사이클이 필요하게 되었고, Background activity with the Single Process Model 페이지를 참고하면 변경된 사항을 자세하게 확인 할 수 있습니다.


Windows 10 universal Windows platform (UWP) app lifecycle

백그라운드 작업으로 추천되는 작업은 Background Triggers, App Services, Background media playback, Extended Execution가 있습니다.


이제 코드를 추가해서, 확인을 하도록 하겠습니다. 아래 코드에 굵은 글씨대로 추가를 한 후에 앱을 실행 합니다.


App.xaml.cs


        public App()
        {
            InitializeComponent();

            EnteredBackground += App_EnteredBackground;
            Suspending += (s, e) =>
            {
                Debug.WriteLine("Suspending");
            };
            LeavingBackground += (s, e) =>
            {
                Debug.WriteLine("LeavingBackground");

            };
            Resuming += (s, e) =>
            {
                Debug.WriteLine("Resuming");

            };

            // Deferred execution until used. Check https://msdn.microsoft.com/library/dd642331(v=vs.110).aspx for further info on Lazy<T> class.
            _activationService = new Lazy<ActivationService>(CreateActivationService);
        }


        private async void App_EnteredBackground(object sender, EnteredBackgroundEventArgs e)
        {
            var deferral = e.GetDeferral();
            Debug.WriteLine("EnteredBackground");
            await Helpers.Singleton<SuspendAndResumeService>.Instance.SaveStateAsync();
            deferral.Complete();
        }



Output 창 내용을 확인해 보면


LeavingBackground


라는 문자가 출력된 것을 볼 수 있습니다. 즉, 앱이 시작될 때부터 Background에서 시작한 것이라는 것을 알 수 있습니다. 


이제 Suspend를 상태로 변경해 보겠습니다.



Output 창을 보면 EnteredBackground 모드가 실행 된 후 쭈욱 내려가서 마지막에 Suspending이라는 글씨가 출력된 것을 볼 수 있습니다. 


Suspend 상태였다가 앱이 완전히 종료가 되던지, Resume으로 살아날 수 있습니다.

이번에는 Resume 상태로 변경해 보겠습니다.


LeavingBackground
EnteredBackground

'WTSSample.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'E:\samples\WTSSample\WTSSample\bin\x86\Debug\AppX\Newtonsoft.Json.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
...
'WTSSample.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'Anonymously Hosted DynamicMethods Assembly'.
Suspending


Suspending 뒤에 바로 Resuming과 LeveingBackground가 호출되면서 앱이 Activate되는 것을 확인 할 수 있습니다.


Suspending
The thread 0x10d4 has exited with code 0 (0x0).
Resuming
LeavingBackground




일단 기본적인 앱의 라이프사이클을 알 고 있어야, 백그라운드 작업을 만들고 이해하는데 도움이 되기 때문에 언급을 했으며, 추후 백그라운드 작업을 실제로 구현하는 내용은 나중에 다시 설명을 할 예정입니다.



3. 백그라운드 작업이 없을 때는 Suspend, Resume 상태로 변경될 때 무슨 작업을 하나요?


Suspend 상태가 될 때 현재 사용자가 보고 있는 화면의 상태를 저장했다가, Resume이 될 때 처음 상태대로 되 돌리는 작업을 추가 한다면, 앱의 사용자 경험을 높이는 좋은 앱의 기본이 되겠죠.


MainPage에 입력된 택스트 내용을 저장했다가, 앱이 시작했을 때 복구해 주는 방법을 간단하게 만들어 보겠습니다.


아래 내용을 추가합니다.


MainPage.xaml


        <Grid
            Grid.Row="1"
            Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}">
            <!--The SystemControlPageBackgroundChromeLowBrush background represents where you should place your content.
                Place your content here.-->
            <TextBox x:Name="SampleTextbox" VerticalAlignment="Center" HorizontalAlignment="Left" MinWidth="100"/>
        </Grid>


MainPage.xaml.cs


    public sealed partial class MainPage : Page
    {
        public MainViewModel ViewModel { get; } = new MainViewModel();

        public MainPage()
        {
            InitializeComponent();

            if (DesignMode.DesignModeEnabled) return;
            //백그라운드 모드로 전환되는 이벤트 핸들러 추가
            Singleton<SuspendAndResumeService>.Instance.OnBackgroundEntering += Instance_OnBackgroundEntering;

        }

        private void Instance_OnBackgroundEntering(object sender, OnBackgroundEnteringEventArgs e)
        {
            //백그라운드 모드로 전환되면, 입력된 텍스트를 저장합니다.
            e.SuspensionState.Data = SampleTextbox.Text;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            //네비게이션 파라메터가 저장된 내용인지 확인합니다. 이 예제에서는 MainPage가 첫번째 페이지 이기 때문에 발생하지 않습니다.
            if (e.Parameter is SuspensionState == false) return;
            var para = (SuspensionState) e.Parameter;
            SampleTextbox.Text = para.Data.ToString();
        }

        public void SetParameter(SuspensionState saveStateSuspensionState)
        {
            //파라메터 값을 전달 받기 위해서 추가한 메서드 입니다.
            SampleTextbox.Text = saveStateSuspensionState.Data.ToString();
        }

    }



SuspendAndResumeService.cs


        private async Task RestoreStateAsync()
        {
            var saveState = await ApplicationData.Current.LocalFolder.ReadAsync<OnBackgroundEnteringEventArgs>(StateFilename);
            if (saveState?.Target != null && typeof(Page).IsAssignableFrom(saveState.Target))
            {
                //첫번째 페이지와 네비게이션 하려는 페이지가 동일하다면, false가 반환됩니다.
                if (NavigationService.Navigate(saveState.Target, saveState.SuspensionState) == false)
                {
                    //이 경우 프레임에 Content를 뷰로 형변환해서 강제로 파라메터를 전달 합니다.
                    //이 내용은 일반적인 내용이 아니기 때문에 필요에 의해서만 추가되어야 할 것입니다.
                    var view = NavigationService.Frame.Content as MainPage;
                    if (view == null) return;
                    view.SetParameter(saveState.SuspensionState);
                }

            }
        }



App.xaml.cs


            LeavingBackground += async (s, e) =>
            {
                Debug.WriteLine("LeavingBackground");
                var deferral = e.GetDeferral();
                //저장된 내용의 복구를 시도합니다.
                await Singleton<SuspendAndResumeService>.Instance.HandleAsync(e);
                deferral.Complete();

            };


결과 화면 입니다. 한번 입력한 내용이 앱을 재시작 해도 계속 나타납니다.



생각나는데로 하나씩 올리도록 하겠습니다...


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

Tag lifecycle, UWP

2017년 1월부터 10월까지 NCSoft에서 프로젝트를 진행 했습니다.


어떤 프로젝트인지 블로그에서 이야기하기에는 보안 이슈가 발생할 수 있어서 자세한 내용은 이야기 하지 않고, 이길복 MVP님, 여러 팀원들과 함께 작업을 했습니다.


제가 게임 회사 프로젝트는 처음 이였고, 약 9개월 정도 근무를 했기에, 그동안 찍었던 사진을 몇 장 올려서 좋았던 기억을 간직 하려고 합니다.



NCSoft가 몇년 전에는 삼성동에 있었는데, 지금은 판교에 있습니다. 판교역에서 걸어서 5분~ 위치 좋습니다. 본사 이외에도 몇 군데 근무하는 곳이 있는데, 처음에는 본사에서 근무를 하다가, 삼성중공업 건물로 이사를 해서 마무리를 했습니다.



정문에서 바라본 모습입니다.


본사 2층에서 내려다 보면서 찍은 사진이네요



1층 로비인데 NC Dinos가 보이고, 왼쪽에 병풍에는 동양화가 움직입니다. 나름 가만히 앉아서 구경하는 재미가 있습니다.



게임 케릭터 피규어가 전시 되어 있습니다. 그런데, 판매는 않하는 것 같더군요..





각 층마다 휴식 공간이 존재 합니다. 테이크아웃 메뉴를 가지고 와서 먹기도, 이야기를 하기도하는 공간입니다.



양치 전용 공간~




제가 제일 좋아했던, 구내 식당입니다. 하지만, 구내 식당이라고 이야기하기가 뻘쭘해질 정도의 스케일로, 기본 한식2개 면1개 양식1개 분식 1개 총 5개의 코너가 존재하며, 테이크아웃 메뉴도 5개 정도 있었던 것 같습니다.












3달에 한번인가 특식이 나오는데..특식이 나오는 날 또한 멋집니다. 그런데, 아쉽지만 이제는 특식을 제공하지 않습니다. 사진은 처음 먹은 특식날만 찍었네요. 


 

 

 



직원들이 애용하는 2층 카페 입니다. 거의 항상 북적북적 했던 것 같습니다.





2층 카페 밖에도 자리가 있어서 봄, 가을에는 여기서 분위기 있게....여름에는 더워서 비추



본사 옥상에서 날씨가 좋을 때 찍은 사진입니다. 


20주년 기념이라고 가방도 받고, 닌텐도 스위치, 호텔 뷔페 이용권, 추석 상품권 등등 제가 다년간 프로젝트를 하면서, 이렇게 직원 복지가 좋은 곳은 처음이 였던 것 같습니다. 그래서, 다들 대기업 입사를 희망 하는 구나 하는 생각이 들더군요. 하지만, 약간만 다시 생각하면, 대학 졸업 후 신규 입사를 하기는 참 어려운 곳이지만, 자신의 전문성을 살려서 한 분야의 전문가가 된다면, 신규 입사보다는 쉽게 들어 올 수도 있는데..라는 생각도 해봅니다.




블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

Tag NCSoft

티스토리 툴바