<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Windows 8 앱 개발자 블로그</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/windowsappdev_ko/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/feed.xml</link>
    <description>Windows 8 엔지니어링 팀에서 제공하는 Windows 8용 Metro 스타일 앱 개발의 이해</description>
    <lastBuildDate>Wed, 17 Apr 2019 03:28:52 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>JavaScript로 작성된 Windows 스토어 앱을 위한 promise의 모든 것</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/javascript-windows-promise</link>
      <pubDate>Wed, 26 Jun 2013 15:54:32 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/06/26/javascript-windows-promise/</guid>
      <description><![CDATA[JavaScript로 Windows 스토어 앱을 개발할 경우 비동기 API와 관련된 작업을 할 때 'promise'라는 구조를 자주 사용하게 됩니다. 또한 얼마 지나지 않아...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>JavaScript로 Windows 스토어 앱을 개발할 경우 비동기 API와 관련된 작업을 할 때 <i></i><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/Hh700339.aspx">'promise'</a>라는 구조를 자주 사용하게 됩니다. 또한 얼마 지나지 않아 순차적인 비동기 작업에도 기본적으로 promise 체인을 사용하게 될 것입니다.</p>  <p>하지만 개발 작업 과정에서 진행 상황을 확실하게 파악할 수 없는 경우 promise를 다른 방법으로 사용하게 됩니다. 이에 대한 좋은 예는 <a href="http://code.msdn.microsoft.com/windowsapps/ListView-performance-39fb71f0">HTML ListView 성능 최적화 샘플</a>에 나오는 ListView 컨트롤의 항목 렌더링 기능 최적화입니다. 전편에 이어 이번 글에서도 이 내용을 살펴보겠습니다. Josh Williams가 //build 2012의 <a href="https://channel9.msdn.com/Events/Build/2012/4-101">Deep Dive into WinJS</a> 세션에서 언급한 코드를 살펴보는 것도 좋습니다. 내용은 약간 수정되었습니다.</p>  <blockquote>   <div id="codeSnippetWrapper">     <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">list.reduce(<span style="color: rgb(0, 0, 255);">function</span> callback (prev, item, i) {<br />    <span style="color: rgb(0, 0, 255);">var</span> result = doOperationAsync(item);<br />    <span style="color: rgb(0, 0, 255);">return</span> WinJS.Promise.join({ prev: prev, result: result}).then(<span style="color: rgb(0, 0, 255);">function</span> (v) {<br />        console.log(i + <span style="color: rgb(0, 96, 128);">&quot;, item: &quot;</span> + item+ <span style="color: rgb(0, 96, 128);">&quot;, &quot;</span> + v.result);<br />    });<br />})<br /></pre>
  </div>
</blockquote>

<p>이 코드에서는 병렬 비동기 작업에 promise를 사용하여 <i></i>'list'의 순서에 따라 연속적으로 결과를 제공합니다. 이 코드를 보고 그 동작을 즉시 이해할 수 있는 분들은 이 글을 읽지 않아도 됩니다! 그렇지 않은 분들은 promise의 작동 방식과 WinJS(JavaScript용 Windows 라이브러리)에서 사용되는 식을 자세히 살펴보고 패턴을 이해하시기 바랍니다.</p>

<h2>promise란 정확하게 무엇일까요? promise 관계</h2>

<p>가장 기본적인 내용부터 살펴보겠습니다. promise는 코드 구조 또는 호출 규칙일 뿐입니다. 그렇기 때문에 promise는 기본적으로 비동기 작업과 관계가 없고 그래서 매우 '유용'합니다<i></i>! promise는 나중에 사용할 수 있게 되거나 이미 사용 가능한 값을 나타내는 개체일 뿐입니다. 이러한 맥락에서 인간 관계를 나타내는 용어인 promise를 사용하게 되었습니다. 제가 만약 “도너츠 12개를 보내 드리겠다고 약속합니다”라고 말한다면 지금 당장은 도너츠가 없지만 나중에 언젠가는 확실하게 도너츠가 생긴다는 의미입니다.<i></i> 그리고 도너츠가 생겼을 때 보내면 됩니다.</p>

<p>이러한 맥락에서 promise는 두 에이전트 간의 관계를 의미합니다. <i></i>한 에이전트는 어떤 물건을 공급하기로 약속하는 '주관자'이고 다른 에이전트는 약속과 물건을 받는 '소비자'입니다.<i></i> 주관자가 해당 물건을 취득하는 방법은 전적으로 주관자의 재량에 달렸습니다. 마찬가지로 소비자는 약속 그 자체와 받은 물건을 원하는 대로 사용할 수 있습니다. 심지어 약속을 다른 소비자와 공유할 수도 있습니다.</p>

<p>또한 주관자와 소비자 사이에는 '생성'과 '처리'라는 두 가지 단계가 있습니다<i></i><i></i>. 이 모든 관계가 다음 다이어그램에 정리되어 있습니다.</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0844.promise_diagram_577C7B9B.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0844.promise_5F00_diagram_5F00_577C7B9B.png"><img title="promise_diagram" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="promise_diagram" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0458.promise_diagram_thumb_0FBAF2B4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0458.promise_5F00_diagram_5F00_thumb_5F00_0FBAF2B4.png" width="700" height="341" /></a></p>

<p>다이어그램의 흐름을 관찰해 보면 promise가 비동기 작업과 잘 맞는 이유가 두 단계로 구성된 관계 때문이라는 사실을 알 수 있습니다. 요청이 접수(promise)된 것을 확인한 소비자가 계속 기다리지(동기식) 않고 다른 일을 할 수 있다는(비동기식) 것이 가장 중요합니다. 즉, 소비자는 약속이 처리되기를 기다리면서 다른 요청에 응답하는 등의 다른 일을 할 수 있습니다. 이것이 바로 비동기 API의 가장 중요한 목적입니다. 그렇다면 물건이 이미 제공되는 경우에는 어떨까요? 약속이 즉시 처리되어 모든 것이 동기 호출 규칙이 됩니다.</p>

<p>물론 이 관계에도 몇 가지 고려해야 할 사항이 있습니다. 누구나 살면서 약속을 하고 또 약속을 받기도 합니다. 약속이 지켜지는 경우도 많지만 그렇지 못한 경우도 실제로 많습니다. 예를 들어 피자 배달 직원이 배달 도중에 사고를 당할 수 있습니다! 약속 불이행은 어쩔 수 없는 현실입니다. 개인 일상에서나 비동기 프로그래밍에서나 이를 받아들여야 합니다.</p>

<p>promise 관계 내에서 이를 살펴보자면, 주관자는 "죄송하지만 이 약속을 지킬 수 없게 되었습니다"라고 말할 수 있는 방법이 필요하고 소비자는 이를 확인할 수 있는 방법이 필요합니다. 두 번째로, 소비자는 주관자의 약속 이행에 대해 인내심이 그리 많지 않습니다. 만약 주관자가 약속 이행 진행 상황을 추적할 수 있다면 소비자가 그 정보를 받아 볼 수 있는 방법이 필요합니다. 세 번째로, 소비자가 주문을 취소하고 주관자에게 더 이상 그 물건이 필요 없다고 말하는 경우가 있습니다.</p>

<p>이러한 요구 사항을 다이어그램에 추가하면 완전한 관계가 완성됩니다.</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/4251.promise_diagram_2_23D40F3D.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/4251.promise_5F00_diagram_5F00_2_5F00_23D40F3D.png"><img title="promise_diagram_2" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="promise_diagram_2" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0550.promise_diagram_2_thumb_1CB4D2C5.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0550.promise_5F00_diagram_5F00_2_5F00_thumb_5F00_1CB4D2C5.png" width="700" height="432" /></a></p>

<p>이러한 관계가 코드에서 어떻게 나타나는지 살펴보겠습니다.</p>

<h2>promise 구조 및 promise 체인</h2>

<p>실제로 promise에 대한 다양한 제안 또는 사양이 있습니다. Windows 및 WinJS에 사용된 것은 <a href="http://wiki.commonjs.org/wiki/Promises/A">Common JS/Promises A</a>이며, promise(나중에 제공할 가치를 나타내기 위해 주관자가 반환하는 것)를 <b>then</b>이라는 함수를 갖는 개체로 정의합니다. 소비자는 <b>then</b>을 호출하여 promise의 처리를 신청합니다. Windows의 promise도 promise 체인에 사용되는 <b>done</b>이라는 비슷한 함수를 지원하는데, 이 부분에 대해서는 잠시 후에 다루겠습니다.</p>

<p>이 함수에 대해 소비자는 최대 3개의 선택적 함수를 인수로 전달할 수 있으며 순서는 다음과 같습니다.</p>

<ol>
  <li><i></i>'completed handler'. 이 약속된 값이 사용 가능하게 될 때 주관자가 이 함수를 호출하고, 해당 값이 이미 사용 가능한 경우에는 <i></i><b>then</b> 내에서 즉시 completed handler가 호출됩니다.</li>

  <li>약속된 값을 얻지 못할 경우 호출되는 선택적 함수 <i></i>'error handler'. 어떠한 promise든 error handler가 호출되면 절대로 completed handler가 호출되지 않습니다.</li>

  <li>작업에서 지원할 경우 중간 결과와 함께 주기적으로 호출되는 선택적 함수 <i></i>'progress handler'. WinRT의 경우 API가 <b>IAsync[Action | Operation]WithProgress</b>의 반환 값을 갖고 <b>IAsync[Action | Operation]</b>는 그렇지 않다는 의미입니다.</li>
</ol>

<p><i></i>error handler만 연결하고 completed handler는 연결하지 않으려는 경우 이러한 인수 중 아무 인수에나 'null'을 전달할 수 있습니다. </p>

<p>관계의 반대편을 살펴보면, 소비자는 <b>then</b>을 여러 번 호출하여 같은 promise에 handler를 원하는 만큼 신청할 수 있습니다. 또한 중심 콘텐츠에 <b>then</b>을 호출할 수 있는 다른 소비자와 promise를 공유할 수도 있습니다. 이러한 기능이 완벽하게 지원됩니다.</p>

<p>즉, promise는 수신하는 모든 handler 목록을 관리하고 적절한 시기에 호출해야 합니다. 또한 전체 관계에서 살펴본 것처럼 promise는 취소를 허용해야 합니다.</p>

<p><i></i>'Promises A' 사양의 다른 요구 사항은 <b>then</b> 메서드 자체가 promise를 반환하는 것입니다. 두 번째 promise는 첫 번째 <b>promise.then</b>에 제공된 completed handler가 돌아올 때 처리되며 두 번째 promise의 결과로 반환 값이 제공됩니다. 다음 코드를 살펴보시기 바랍니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> promise1 = someOperationAsync();<br /><span style="color: rgb(0, 0, 255);">var</span> promise2 = promise1.then(<span style="color: rgb(0, 0, 255);">function</span> completedHandler1 (result1) { <span style="color: rgb(0, 0, 255);">return</span> 7103; } );<br />promise2.then(<span style="color: rgb(0, 0, 255);">function</span> completedHandler2 (result2) { });<br /></pre>
  </div>
</blockquote>

<p><b>someOperationAsync</b>가 시작되면 <i></i>'promise1'을 반환하는 것이 이 코드의 실행 체인입니다. 이 작업이 진행 중일 때 'promise2'를 즉시 반환하는 <b>promise1.then</b>을 호출합니다.<i></i> 비동기 작업이 가능하지 않으면 <b>completedHandler1</b>이 호출되지 않는다는 점을 확실히 이해하시기 바랍니다. 여전히 기다리고 있는 상황을 가정해 보겠습니다. 이 경우 바로 <b>promise2.then</b> 함수를 호출합니다. 이번에도 역시 completedHandler2가 호출되지 '않습니다'.<i></i></p>

<p>나중에 <b>someOperationAsync</b>가 값 14618로 완료되었다고 해 봅시다. <i></i>이제 'promise1'이 처리되고 해당 값을 가진 <b>completedHandler1</b>을 호출합니다. <i></i>'result1'은 14618이 됩니다. 이제 <b>completedHandler1</b>이 실행되고 값 7103을 반환합니다. 이 시점에서 <i></i>'promise2'가 처리되고 <b>completedHandler2</b>가 호출되며 <i></i>'result2'는 7103입니다.</p>

<p>completed handler가 다른 promise를 반환하는 경우는 어떨까요? 이 경우는 약간 다르게 처리됩니다. 위 코드의 <b>completedHandler1</b>이 다음과 같이 promise를 반환한다고 합시다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> promise2 = promise1.then(<span style="color: rgb(0, 0, 255);">function</span> completedHandler1 (result1) {<br />    <span style="color: rgb(0, 0, 255);">var</span> promise2a = anotherOperationAsync();<br />    <span style="color: rgb(0, 0, 255);">return</span> promise2a;<br />});<br /></pre>
  </div>
</blockquote>

<p>이 경우 <i></i><b>completedHandler2</b>의 'result2'는 <i></i>'promise2a' 자체가 아니라 'promise2a'의 <u>fulfillment</u> 값입니다<i></i>. completed handler가 promise를 반환하기 때문에 'promise2a'의 결과로 <i></i><b>promise1.then</b>에서 반환된 'promise2'가 처리됩니다.<i></i></p>

<p>바로 이러한 특징 때문에 순차적인 비동기 작업을 체인으로 엮을 수 있고, 체인의 각 작업의 결과는 다음 작업으로 전달됩니다. 중간 변수 또는 명명된 handler가 없는 경우 promise 체인에서 다음 패턴을 자주 보게 됩니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">operation1().then(<span style="color: rgb(0, 0, 255);">function</span> (result1) {<br />    <span style="color: rgb(0, 0, 255);">return</span> operation2(result1)<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (result2) {<br />    <span style="color: rgb(0, 0, 255);">return</span> operation3(result2);<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (result3) {<br />    <span style="color: rgb(0, 0, 255);">return</span> operation4(result3);<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (result4) {<br />    <span style="color: rgb(0, 0, 255);">return</span> operation5(result4)<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (result5) {<br />    <span style="color: rgb(0, 128, 0);">//And so on</span><br />});<br /></pre>
  </div>
</blockquote>

<p>물론 각 completed handler는 수신하는 결과를 가지고 다양한 일을 하지만, 모든 체인에서 이 핵심 구조를 보게 될 것입니다. 또 하나의 사실은, 이 코드의 모든 <b>then</b> 메서드는 주어진 completed handler를 저장한 후 다른 promise를 반환하기 때문에 하나씩 차례대로 실행된다는 것입니다. 따라서 이 코드의 마지막 부분에 도착하면 <i></i>'operation1'만 시작하고 completed handler가 호출되지 않습니다. 하지만 <b>then</b>을 호출할 때마다 중간 promise가 생성되고 서로 연결되어서 순차 작업이 진행될 때 체인을 관리합니다.</p>

<p>각 순차 작업을 이전 completed handler에 중첩하여 같은 순서로 배열할 수 있으며 이 경우 <i></i>'return' 문 중 일부가 제외됩니다. 하지만 이렇게 중첩하려면 들여쓰기 때문에 고생을 많이 하게 되며, 특히 <b>then</b>을 호출할 때마다 진행 상황과 error handler를 추가할 경우 더욱 어렵게 됩니다.</p>

<p>이에 대해 부연 설명을 하자면, WinJS에서 promise의 기능 중 하나는 체인에서 발생하는 모든 오류가 자동으로 체인의 끝부분에 전달된다는 것입니다. 다시 말해, 모든 수준에서 handler를 사용하지 말고 마지막으로 호출되는 <b>then</b>에 error handler를 하나만 연결할 수 있어야 합니다. 하지만 다양한 이유로 인해 체인의 마지막 링크가 <b>then</b> 호출인 경우 이러한 오류가 가려진다는 점에 주의해야 합니다. 이 때문에 WinJS에서는 promise에 <b>done</b> 메서드를 제공합니다. 이 메서드는 <b>then</b>과 같은 인수를 받아들이지만 체인이 완전하다고 표시합니다(또 다른 promise 대신 'undefined'를 반환<i></i>). 그러면 체인의 오류에 대해 <b>done</b>에 연결된 error handler가 호출됩니다. 뿐만 아니라 error handler가 없을 경우 <b>done</b> 메서드가 앱 수준에서 예외를 발생시키며, 이는 <b>WinJS.Application.onerror</b> 이벤트의 <b>window.onerror</b>로 처리할 수 있습니다. 정리하자면, 예외를 드러내서 적절하게 처리할 수 있도록 모든 체인을 <b>done</b>으로 끝내는 것이 가장 좋습니다.</p>

<p>물론 <i></i> <b>then</b> 호출의 긴 체인에서 마지막 promise를 반환하는 것이 '목적'인 함수를 작성하는 경우 마지막 부분에 <b>then </b>을 사용해야 합니다. 오류 처리의 책임은 또 다른 체인에서 해당 promise를 사용하는 호출자에게 있습니다.</p>

<h2>promise 생성: WinJS.Promise 클래스</h2>

<p><i></i>개발자는 'Promises A' 사양에 맞게 promise 클래스를 직접 만들 수는 있지만 너무 번거롭기 때문에 라이브러리에 맡겨 두는 것이 가장 좋습니다. 이러한 이유로 WinJS에서는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211867.aspx"><b>WinJS.Promise</b></a>라는 철저한 검증을 마친 강력하고 유연한 promise 클래스를 제공합니다. 이 클래스를 사용하면 주관자/소비자 관계 또는 <b>then</b> 동작을 일일이 관리할 필요 없이 다양한 값과 작업에 대한 promise를 간편하게 생성할 수 있습니다.</p>

<p>필요하다면 <b>new WinJS.Promise</b> 또는 적절한 도우미 함수를 사용하여 비동기 작업과 기존(동기) 값 모두에 대한 promise를 생성할 수 있습니다. 이에 대해서는 다음 단원에서 설명하겠습니다. promise는 단지 코드 구조일 뿐이라는 점을 기억하시기 바랍니다. promise가 비동기 작업을 래핑해야 할 필요는 '전혀' 없습니다.<i></i> <i></i>마찬가지로, promise의 어떤 코드 조각을 단지 래핑한다고 해서 자동으로 비동기식으로 실행되지는 않습니다.<i> </i>비동기식으로 실행하는 것은 여전히 개발자의 몫입니다.</p>

<p><b>WinJS.Promise</b>를 직접 사용하는 간단한 예를 들어 보겠습니다. 1부터 어떤 숫자까지 추가하면서 긴 계산을 수행하려고 하는데 비동기식으로 계산해야 한다고 가정해 보겠습니다. 일상적 작업에 대한 콜백 메커니즘을 직접 개발할 수 있지만 promise 내에서 래핑할 경우 다른 API의 다른 promise와 체인으로 연결하거나 이어 붙일 수 있습니다. 이러한 줄과 함께 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br229787.aspx"><b>WinJS.xhr</b></a> 함수가 promise 내에서 JavaScript의 비동기 <b>XmlHttpRequest</b>를 래핑하므로 개발자가 후자의 특정 이벤트 구조를 처리하지 않아도 됩니다.</p>

<p>물론 긴 계산에 JavaScript 작업자를 사용할 수도 있지만 설명을 위해 UI 스레드를 유지하고 <b>setImmediate</b>를 사용하여 작업을 여러 단계로 구분하겠습니다. 다음은 <b>WinJS.Promise</b>를 사용하여 promise 구조 내에서 구현하는 방법입니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> calculateIntegerSum(max, step) {<br />    <span style="color: rgb(0, 128, 0);">//The WinJS.Promise constructor's argument is an initializer function that receives </span><br />    <span style="color: rgb(0, 128, 0);">//dispatchers for completed, error, and progress cases.</span><br />    <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (completeDispatch, errorDispatch, progressDispatch) {<br />        <span style="color: rgb(0, 0, 255);">var</span> sum = 0;<br /><br />        <span style="color: rgb(0, 0, 255);">function</span> iterate(args) {<br />            <span style="color: rgb(0, 0, 255);">for</span> (<span style="color: rgb(0, 0, 255);">var</span> i = args.start; i &lt; args.end; i++) {<br />                sum += i;<br />            };<br /><br />            <span style="color: rgb(0, 0, 255);">if</span> (i &gt;= max) {<br />                <span style="color: rgb(0, 128, 0);">//Complete--dispatch results to completed handlers</span><br />                completeDispatch(sum);<br />            } <span style="color: rgb(0, 0, 255);">else</span> {<br />                <span style="color: rgb(0, 128, 0);">//Dispatch intermediate results to progress handlers</span><br />                progressDispatch(sum);<br />                setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });<br />            }<br />        }<br />            <br />        setImmediate(iterate, { start: 0, end: Math.min(step, max) });<br />    });<br />}<br /></pre>
  </div>
</blockquote>

<p><b>new WinJS.Promise</b>를 호출할 때 생성자의 단일 인수는 <i></i>'initializer' 함수입니다(이 예에서는 익명). initializer가 수행할 작업을 캡슐화하지만 이 함수 자체가 UI 스레드에서 '동기식으로' 실행될 것이 분명합니다.<i></i> 따라서 여기서 <b>setImmediate</b>를 사용하지 않고 긴 계산을 수행할 경우 그 시점의 모든 UI 스레드를 차단하게 됩니다. 다시 한 번 말씀 드리지만 promise 내에 코드를 배치한다고 해서 <i></i>자동으로 비동기식으로 실행되지는 않습니다. initializer 함수 자체에서 그렇게 설정해야 합니다.</p>

<p>인수의 경우 initializer 함수는 promise가 지원하는 completed, error 및 progress 사례의 '디스패처' 세 개를 수신합니다.<i></i> 보시다시피 작업하는 동안 적절한 인수를 사용하여 적절한 시기에 디스패처를 호출하고 있습니다.</p>

<p>저는 이러한 함수를 "디스패처"라고 부릅니다. 소비자가 promise의 <b>then</b> 메서드(또는 <b>done</b>, 설명은 생략)를 요청하는 handler와 다르기 때문입니다. 좀 더 자세히 알아보자면, WinJS는 handler 배치를 관리하며, 몇 명이 되었든 몇 개가 되었든 소비자가 handler를 요청할 수 있습니다. 이러한 디스패처 중 하나를 호출하면 WinJS가 내부 목록을 통해 반복하면서 개발자를 대신하여 모든 handler를 호출합니다. 또한 <b>WinJS.Promise</b>는 <b>then</b>이 체인 연결에 필요한 만큼 또 다른 promise를 반복하도록 합니다.</p>

<p>정리하자면, <b>WinJS.Promise</b>는 promise를 둘러싼 모든 것을 제공합니다. 따라서 개발자는 promise가 나타내는 핵심 작업에만 집중할 수 있으며, 이러한 것들은 initializer 함수에 포함되어 있습니다.</p>

<h2>promise 생성을 돕는 도우미</h2>

<p>promise를 생성하는 기본 도우미 함수는 정적 메서드인 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211664.aspx"><b>WinJS.Promise.as</b></a>입니다. 이 메서드는 <i></i>promise의 '모든' 값을 래핑합니다. 기존 값의 래퍼는 정상적으로 작동하고 <b>then</b>으로 전달되는 모든 completed handler를 호출합니다. 따라서 알고 있는 임의의 값을 promise로 처리할 수 있기 때문에 연결 또는 체인 연결을 통해 다른 promise와 혼합하여 구성할 수 있습니다. 기존 promise에 <b>as</b>를 사용하면 해당 promise만 반환합니다.</p>

<p>다른 정적 도우미 함수는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br229729.aspx"><b>WinJS.Promise.timeout</b></a>입니다. 이 함수는 <b>setTimeout</b> 및 <b>setImmediate</b>와 관련된 편리한 래퍼를 제공합니다. 두 번째 promise가 특정 시간(밀리초) 내에 처리되지 않을 경우 두 번째 promise를 취소하는 promise를 만들 수도 있습니다.</p>

<p><b>setTimeout</b> 및 <b>setImmediate</b>와 관련된 <b>timeout</b> promise가 <i></i>'undefined'를 사용하여 스스로 처리되는 것을 주의 깊게 살펴보시기 바랍니다. 이와 관련하여 “이 promise를 사용하여 시간 제한 이후에 다른 결과를 제공하려면 어떻게 해야 합니까?”라는 질문을 자주 받습니다. 답변을 드리자면, <b>then</b>이 completed handler의 반환 값을 통해 처리되는 또 다른 promise를 반환합니다. 이 코드 줄에서 예를 들자면 다음과 같습니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> p = WinJS.Promise.timeout(1000).then(<span style="color: rgb(0, 0, 255);">function</span> () { <span style="color: rgb(0, 0, 255);">return</span> 12345; });</pre>
  </div>
</blockquote>

<p>이 코드는 1초 후 값 12345로 처리되는 promise <i></i>'p'를 생성합니다. 다시 말해, <b>WinJS.Promise.timeout(…).then(function () { return &lt;value>} )</b>는 지정된 시간 제한 이후 <i></i>'&lt;value>'를 제공하는 패턴입니다. <i></i>'&lt;value>' 자체가 또 다른 promise인 경우 시간 제한 이후 특정 시점에 해당 promise의 처리 값을 제공합니다.</p>

<h2>취소 및 promise 오류 생성</h2>

<p>다들 보셨겠지만 조금 전에 본 코드는 두 가지 결함이 있습니다. 첫째, 시작 후 작업을 취소하는 방법이 없고 둘째, 작업 처리 오류를 표시하는 방법이 없습니다.</p>

<p>두 가지 문제를 해결하는 비결을 소개해 드리자면, <b>calculateIntegerSum</b> 함수처럼 promise를 생산하는 함수는 항상 promise를 반환해야 합니다. 어떤 작업이 완료되지 않거나 아예 시작되지 않을 경우 해당 promise는 소위 말하는 <i></i>'오류 상태'에 있는 것입니다. 즉, 해당 promise는 completed handler에 전달할 수 있는 값을 갖고 있지 않으며 앞으로도 가질 수 없습니다.<i></i> 단지 error handler만 호출할 수 있습니다. 사실, 소비자가 한 promise에서 <b>then</b>을 호출했는데 그 promise가 이미 오류 상태인 경우 해당 promise는 즉시(동기식으로) <b>then</b>에 전달되는 error handler를 호출합니다.</p>

<p><b>WinJS.Promise</b>가 오류 상태로 진입하는 두 가지 이유가 있습니다. 첫 번째는 소비자가 <b>cancel</b> 메서드를 호출하는 경우이고 두 번째는 initializer 함수 내의 코드가 error 디스패처를 호출하는 경우입니다. 이러한 상황이 발생하면 해당 promise에 전달된 오류 값이 무엇이든 error handler에 전달됩니다. <b>WinJS.Promise</b> 내에 작업을 생성할 경우 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211689.aspx"><b>WinJS.ErrorFromName</b></a> 인스턴스를 사용할 수도 있습니다. 이 인스턴스는 JavaScript 개체이며, 오류를 식별하는 <i></i>'name' 속성과 자세한 정보가 들어 있는 <i></i>'message' 속성을 갖고 있습니다. 예를 들어 한 promise가 취소되면 error handler는 <i></i><i></i>'name' 및 'message' 이름이 “Canceled”로 설정된 오류 개체를 받습니다.</p>

<p>작업을 아예 시작할 수 없는 경우는 어떨까요? 예를 들어 0, 0처럼 잘못된 인수로 <b>calculateIntegerSum</b>을 호출할 경우 카운트를 시도조차 하지 않고 그 대신 오류 상태의 promise를 반환합니다. 이것이 바로 정적 메서드 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br229784.aspx"><b>WinJS.Promise.wrapError</b></a>의 목적입니다. <b>WinJS.ErrorFromName</b>의 인스턴스를 가져와서 오류 상태의 promise를 반환합니다. 우리가 예로 든 코드에서도 새로운 <b>WinJS.Promise</b> 인스턴스를 사용하는 대신 오류 상태의 promise를 반환합니다.</p>

<p>또 다른 문제는 promise의 <b>cancel</b> 메서드를 호출하면 promise 자체가 오류 상태에 빠지는 것입니다. 진행 중인 비동기 작업을 중지하려면 어떻게 해야 할까요? 앞에서 구현한 <b>calculateIntegerSum</b>은 생성된 promise의 상태에 관계없이 작업이 완료될 때까지 계속해서 <b>setImmediate</b>를 호출할 것입니다. 사실, promise가 취소된 후 작업에서 complete 디스패처를 호출할 경우 promise가 해당 완료를 무시합니다.</p>

<p>이 문제를 해결하려면 promise가 해당 작업에 더 이상 계속할 필요가 없다고 알릴 수 있는 방법이 필요합니다. 이러한 이유로 <b>WinJS.Promise</b> 생성자는 promise가 취소될 때 호출되는 두 번째 함수 인수를 사용합니다. 이 글의 예에서는 이 함수를 호출할 때 <b>setImmediate</b>가 이어서 호출되지 않도록 하면 계산이 중지됩니다. 다음은 오류를 적절하게 처리한 코드입니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> calculateIntegerSum(max, step) {<br />    <span style="color: rgb(0, 128, 0);">//Return a promise in the error state for bad arguments</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (max &lt; 1 || step &lt; 1) {<br />        <span style="color: rgb(0, 0, 255);">var</span> err = <span style="color: rgb(0, 0, 255);">new</span> WinJS.ErrorFromName(<span style="color: rgb(0, 96, 128);">&quot;calculateIntegerSum&quot;</span>, <span style="color: rgb(0, 96, 128);">&quot;max and step must be 1 or greater&quot;</span>);<br />        <span style="color: rgb(0, 0, 255);">return</span> WinJS.Promise.wrapError(err);<br />    }<br /><br />    <span style="color: rgb(0, 0, 255);">var</span> _cancel = <span style="color: rgb(0, 0, 255);">false</span>;<br /><br />    <span style="color: rgb(0, 128, 0);">//The WinJS.Promise constructor's argument is an initializer function that receives </span><br />    <span style="color: rgb(0, 128, 0);">//dispatchers for completed, error, and progress cases.</span><br />    <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (completeDispatch, errorDispatch, progressDispatch) {<br />        <span style="color: rgb(0, 0, 255);">var</span> sum = 0;<br /><br />        <span style="color: rgb(0, 0, 255);">function</span> iterate(args) {<br />            <span style="color: rgb(0, 0, 255);">for</span> (<span style="color: rgb(0, 0, 255);">var</span> i = args.start; i &lt; args.end; i++) {<br />                sum += i;<br />            };<br /><br />            <span style="color: rgb(0, 128, 0);">//If for some reason there was an error, create the error with WinJS.ErrorFromName</span><br />            <span style="color: rgb(0, 128, 0);">//and pass to errorDispatch</span><br />            <span style="color: rgb(0, 0, 255);">if</span> (<span style="color: rgb(0, 0, 255);">false</span> <span style="color: rgb(0, 128, 0);">/* replace with any necessary error check -- we don’t have any here */</span>) {<br />                errorDispatch(<span style="color: rgb(0, 0, 255);">new</span> WinJS.ErrorFromName(<span style="color: rgb(0, 96, 128);">&quot;calculateIntegerSum (scenario 7)&quot;</span>, <span style="color: rgb(0, 96, 128);">&quot;error occurred&quot;</span>));<br />            }<br /><br />            <span style="color: rgb(0, 0, 255);">if</span> (i &gt;= max) {<br />                <span style="color: rgb(0, 128, 0);">//Complete--dispatch results to completed handlers</span><br />                completeDispatch(sum); <br />            } <span style="color: rgb(0, 0, 255);">else</span> {<br />                <span style="color: rgb(0, 128, 0);">//Dispatch intermediate results to progress handlers</span><br />                progressDispatch(sum);<br /><br />                <span style="color: rgb(0, 128, 0);">//Interrupt the operation if canceled</span><br />                <span style="color: rgb(0, 0, 255);">if</span> (!_cancel) {<br />                    setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });<br />                }<br />            }<br />        }<br />            <br />        setImmediate(iterate, { start: 0, end: Math.min(step, max) });<br />    },<br />    <span style="color: rgb(0, 128, 0);">//Cancellation function for the WinJS.Promise constructor</span><br />    <span style="color: rgb(0, 0, 255);">function</span> () {<br />        _cancel = <span style="color: rgb(0, 0, 255);">true</span>;<br />    });<br />}<br /></pre>
  </div>
</blockquote>

<p>대체적으로 <b>WinJS.Promise</b> 인스턴스를 생성하여 다양한 용도에 이용할 수 있습니다. 예를 들어 다른 비동기 메서드를 통해 웹 서비스에 정보를 전달하는 라이브러리를 갖고 있다면 promise 내에서 작업을 래핑할 수 있습니다. 새 promise를 사용하여 다양한 소스의 여러 비동기 작업(또는 다른 promise)을 모든 관계를 제어하고 싶은 하나의 promise로 결합할 수도 있습니다. <b>WinJS.Promise</b>의 initializer 코드 내에서 다른 비동기 작업과 그 promise에 대한 개발자 고유의 처리기를 사용할 수 있습니다. 이러한 처리기를 사용하여 네트워크 시간 제한에 대한 자동 재시도 메커니즘을 캡슐화하고, 일반적인 진행 상황 업데이트 UI를 연결하고, 자세한 로깅 또는 분석을 추가할 수 있습니다. 어떤 방법을 사용하든 나머지 코드에서 사소한 부분은 신경 쓰지 않고 소비자 측에서 promise를 처리할 수 있습니다.</p>

<p>다음 코드 줄을 사용하면 JavaScript 작업자를 매우 간단하게 promise로 래핑할 수 있습니다. WinRT의 다른 비동기 작업과 모양 및 동작이 비슷합니다. 잘 아시겠지만 작업자는 앱의 작업자 개체에 <b>message</b> 이벤트를 발생시키는 <b>postMessage</b> 호출을 통해 결과를 전달합니다. 다음 코드는 message에 전달되는 결과를 통해 처리되는 promise에 해당 이벤트를 연결합니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 128, 0);">// This is the function variable we're wiring up.</span><br /><span style="color: rgb(0, 0, 255);">var</span> workerCompleteDispatch = <span style="color: rgb(0, 0, 255);">null</span>;<br /><br /><span style="color: rgb(0, 0, 255);">var</span> promiseJS = <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (completeDispatch, errorDispatch, progressDispatch) {<br />    workerCompleteDispatch = completeDispatch;<br />});<br /><br /><span style="color: rgb(0, 128, 0);">// Worker is created here and stored in the 'worker' variable</span><br /><br /><span style="color: rgb(0, 128, 0);">// Listen for worker events</span><br />worker.onmessage = <span style="color: rgb(0, 0, 255);">function</span> (e) {<br />    <span style="color: rgb(0, 0, 255);">if</span> (workerCompleteDispatch != <span style="color: rgb(0, 0, 255);">null</span>) {<br />        workerCompleteDispatch(e.data.results); <span style="color: rgb(0, 128, 0);">/* event args depends on the worker */</span><br />    }<br />}<br /><br />promiseJS.done(<span style="color: rgb(0, 0, 255);">function</span> (result) {<br />    <span style="color: rgb(0, 128, 0);">// Output for JS worker</span><br />});<br /></pre>
  </div>
</blockquote>

<p>이 코드를 확장하여 작업자의 오류를 처리하려면 error 디스패처를 다른 변수에 저장하고 <b>message</b> 이벤트 처리기가 이벤트 인수의 오류 정보를 확인하도록 하고, complete 디스패처 대신 error 디스패처를 적절하게 호출해야 합니다.</p>

<h2>병렬 promise 연결</h2>

<p>promise는 비동기 작업을 래핑하는 데 자주 사용되기 때문에 개발자는 여러 작업을 병렬로 진행할 수 있습니다. 그렇다면 그룹의 한 promise가 처리되는 시기 또는 그룹의 모든 promise가 처리되는 시기가 언제인지 궁금할 것입니다. 이러한 용도로 사용되는 것이 정적 함수 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br229660.aspx"><b>WinJS.Promise.any</b></a> 및 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211774.aspx"><b>WinJS.Promise.join</b></a>입니다.</p>

<p>두 함수는 여러 값을 수락하거나 값 속성이 있는 개체를 수락합니다. 두 값은 promise일 수 있으며 promise가 아닌 값은 <b>WinJS.Promise.as</b>를 통해 래핑되고 결국 전체 배열 또는 개체가 promise로 구성됩니다.</p>

<p>다음은 <b>any</b>의 특징입니다.</p>

<ul>
  <li><b>any</b>는 다른 promise 중 하나가 처리되거나 오류(논리적 OR)와 함께 실패할 때 처리되는 단일 promise를 생성합니다. 기본적으로 <b>any</b><i></i>는 모든 promise에 completed handler를 연결하며, 한 completed handler가 호출되면 <b>any</b><i> </i>promise가 수신한 모든 completed handler를 호출합니다.</li>

  <li><b>any</b><i></i> promise가 처리된 후, 즉 목록의 첫 번째 promise가 처리된 후 목록의 다른 작업이 계속 실행되어 해당 promise에 개별적으로 할당된 모든 completed, error 또는 progress handler를 호출합니다.</li>

  <li><b>any</b>에서 promise를 취소할 경우 목록의 모든 promise가 취소됩니다.</li>
</ul>

<p><b>join</b>의 경우</p>

<ul>
  <li><b>join</b>은 다른 '모든' <i></i>promise가 처리되거나 오류(논리적 AND)와 함께 실패할 때 처리되는 단일 promise를 생성합니다. 기본적으로 <b>join</b><i></i>은 모든 promise에 completed 및 error handler를 연결하며, 모든 handler가 호출될 때까지 기다렸다가 수신하는 모든 completed handler를 호출합니다.</li>

  <li>또한 <b>join</b> promise는 개발자가 제공하는 모든 progress handler에 진행 상황을 보고합니다. 이 예의 중간 결과는 지금까지 처리된 개별 promise에서 얻은 결과입니다.</li>

  <li><b>join</b>에서 promise를 취소할 경우 아직 보류 중인 모든 promise가 취소됩니다.</li>
</ul>

<p><b>any</b>와 <b>join</b> 외에도 알아두면 편리하게 사용할 수 있는 정적 <b>WinJS.Promise</b> 메서드가 두 개 더 있습니다.</p>

<ul>
  <li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211765.aspx"><b>is</b></a>는 임의 값이 promise인지 확인하여 부울을 반환합니다. “then” 함수를 사용하는 개체인지 확인하는 것이 기본 역할이며, “done”은 테스트하지 않습니다.</li>

  <li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br229727.aspx"><b>theneach</b></a><i></i>는 <b>then</b>을 사용하여 promise 그룹에 completed, error 및 progress handler를 적용하고, 그 결과를 promise 내의 또 다른 값 그룹으로 반환합니다. 모든 handler는 <i></i>'null'일 수 있습니다.</li>
</ul>

<h2>연속 결과를 갖는 병렬 promise</h2>

<p><b>WinJS.Promise.join</b> 및 <b>WinJS.Promise.any</b>를 사용하여 병렬 promise, 즉 병렬 비동기 작업을 처리할 수 있습니다. 다시 한 번 말씀 드리지만 <b>join</b>이 반환하는 promise는 배열의 모든 promise가 처리될 때 처리됩니다. 하지만 promise는 무작위 순서로 완료됩니다. 이 방법으로 실행할 수 있는 일련의 작업이 있지만 그 결과를 잘 정의된 순서로, 다시 말해서 promise가 배열에 나타나는 순서대로 처리하려면 어떻게 해야 할까요?</p>

<p>이렇게 하려면 각 후속 promise를 앞에 있는 모든 promise의 <b>join</b>에 연결해야 합니다. 이 글을 시작할 때 소개한 코드가 정확하게 이 방법을 사용합니다. 그 코드를 다시 한 번 보여 드리겠습니다. 하지만 이번에는 promise가 잘 보이도록 약간 수정했습니다. <i></i><b></b>'list'는 가상의 promise를 생산하는 비동기 호출 <b>doOperationAsync</b>의 인수로 사용되는 값의 배열이라고 가정합니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">list.reduce(<span style="color: rgb(0, 0, 255);">function</span> callback (prev, item, i) {<br />    <span style="color: rgb(0, 0, 255);">var</span> opPromise = doOperationAsync(item);<br />    <span style="color: rgb(0, 0, 255);">var</span> join = WinJS.Promise.join({ prev: prev, result: opPromise});<br /><br />    <span style="color: rgb(0, 0, 255);">return</span> join.then(<span style="color: rgb(0, 0, 255);">function</span> completed (v) {<br />        console.log(i + <span style="color: rgb(0, 96, 128);">&quot;, item: &quot;</span> + item+ <span style="color: rgb(0, 96, 128);">&quot;, &quot;</span> + v.result);<br />    });<br />})<br /></pre>
  </div>
</blockquote>

<p>이 코드를 이해하려면 배열의 <a href="http://msdn.microsoft.com/ko-kr/library/ie/ff679975(v=vs.94).aspx"><b>reduce</b></a> 메서드가 작동하는 방식을 먼저 이해해야 합니다. 배열의 각 항목에서 <b>reduce</b>는 함수 인수를 호출합니다. 여기서는 <b>callback</b>인데 인수 네 개를 수신합니다. 이 코드에서는 네 개 중 세 개만 사용하고 있습니다.</p>

<ul>
  <li><i></i>'prev' <i></i>'previous' 호출에서 <b>callback</b>으로 반환된 값입니다(첫 번째 항목에서는 'null'<i></i>).</li>

  <li><i></i>'item' 배열의 현재 값입니다.</li>

  <li><i></i>'i' 목록의 항목 인덱스입니다.</li>

  <li><i></i>'source' 원래 배열입니다.</li>
</ul>

<p>목록의 첫 번째 항목에는 <b>opPromise<sub>1</sub></b>이라고 하는 promise를 사용합니다. <b>prev</b>가 <i></i>'null'이기 때문에 <b>[WinJS.Promise.as(null), opPromise<sub>1</sub>]</b>을 연결합니다. 하지만 <b>join</b> 자체를 반환하는 것은 아니라는 점에 주의하시기 바랍니다. 그 대신 <b>completed</b>라고 이름을 붙인 completed handler를 join에 연결하고 <b>then</b>으로부터 promise를 반환할 것입니다.</p>

<p><b>then</b>에서 반환되는 promise는 completed handler가 반환될 때 처리된다는 것을 기억하시기 바랍니다. 즉, <b>callback</b>에서 반환하는 것은 첫 번째 항목의 <b>completed</b> handler가 <b>opPromise<sub>1</sub></b>의 결과를 처리할 때까지 완료되지 않은 promise입니다. join의 결과를 되돌아보면 원래 목록의 promise에서 얻은 결과가 들어 있는 개체를 통해 처리됩니다. 즉, 처리 값 <i></i>'v'가 <i></i>'prev' 속성과 <i></i>'result' 속성을 모두 포함하며, 후자는 <b>opPromise<sub>1</sub></b>의 결과입니다.</p>

<p><i></i>'list'의 다음 항목을 사용하여 <b>callback</b>이 <i></i>이전 <b>join.then</b>의 promise가 들어 있는 'prev'를 수신합니다. 그런 다음 <b>opPromise<sub>1</sub>.then</b> 및 <b>opPromise<sub>2</sub></b>의 새로운 join을 생성하게 됩니다. 결과적으로 이 join은 <b>opPromise<sub>2</sub></b>가 처리되고 <i></i><b>opPromise<sub>1</sub></b>의 completed handler가 반환될 때까지 완료되지 않습니다. 놀랍지 않습니까? 이 <b>join</b>에 연결하는 <b>completed<sub>2</sub></b> handler는 <b>completed<sub>1</sub></b>이 반환될 때까지 호출되지 않습니다.</p>

<p>목록의 각 항목마다 동일한 종속성이 계속 구축됩니다. 항목 'n'에 대한 <b>join.then</b>의 promise는 <i></i><b>completed<sub>n</sub><i></i></b>이 반환될 때까지 처리되지 않습니다. 따라서 completed handler가 <i></i>'list'와 같은 순서대로 호출됩니다.</p>

<h2>결론</h2>

<p>이 글에서 promise 그 자체로는 미래의 어느 시점에 제공할 값을 갖고 있는 주관자와 그 값을 언제 받을 수 있는지 알고 싶어하는 소비자 사이의 관계를 나타내는 코드 구조 또는 호출 규칙에 불과하다는 것을 살펴보았습니다. 그렇기 때문에 promise는 비동기 작업의 결과를 매우 잘 나타내며 JavaScript로 작성된 Windows 스토어 앱 내에서 광범위하게 사용됩니다. 또한 promise의 사양에서는 순차적인 비동기 작업을 체인으로 연결하는 것을 허용하며, 각 중간 결과는 한 링크에서 다음 링크로 전달됩니다.</p>

<p>WinJS(JavaScript용 Windows 라이브러리)는 모든 작업을 개발자 혼자 래핑할 수 있도록 견고하게 구축된 promise를 제공합니다. 또한 promise를 병렬 작업에 연결하는 등의 일반 시나리오를 위한 도우미도 제공합니다. 따라서 WinJS를 사용하면 비동기 작업을 매우 효율적이고 효과적으로 처리할 수 있습니다.</p>

<p>Windows 에코시스템 팀 프로그램 관리자 
  <br />Kraig Brockschmidt 

  <br />작성자, <a href="http://blogs.msdn.com/b/microsoft_press/archive/2012/10/29/free-ebook-programming-windows-8-apps-with-html-css-and-javascript.aspx"><i></i>'HTML, CSS 및 JavaScript로 Windows 8 앱 프로그래밍하기'</a></p>

<!-- Title: “JavaScript로 작성된 Windows 스토어 앱을 위한 promise의 모든 것” Tags: “JavaScript, Windows 스토어 앱, promise” --></div>]]></content:encoded>
    </item>
    <item>
      <title>ListView 항목 렌더링 최적화</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/listview</link>
      <pubDate>Tue, 25 Jun 2013 14:30:24 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/06/25/listview/</guid>
      <description><![CDATA[컬렉션을 지원하도록 JavaScript로 작성된 Windows 스토어 앱의 성능에 가장 큰 영향을 미치는 것은 WinJS ListView 컨트롤과의 원활한 연동입니다. 사실 별로...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>컬렉션을 지원하도록 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211385.aspx" target="_blank">JavaScript로 작성된 Windows 스토어 앱</a>의 성능에 가장 큰 영향을 미치는 것은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211837.aspx" target="_blank">WinJS ListView 컨트롤</a>과의 원활한 연동입니다. 사실 별로 놀라운 일은 아닙니다. 수많은 항목을 관리하고 표시할 때 항목 최적화 하나하나가 모두 중요합니다. 그 중에서도 가장 중요한 것은 각 항목을 렌더링하는 방법입니다. 즉, ListView 컨트롤의 각 항목을 언제, 어떻게 DOM에 구축하고 앱에서 표시할 것인지가 가장 중요합니다. 사실, 사용자가 신속하게 목록을 둘러보고 목록의 페이스를 유지하려고 할 때에는 언제와 어디서 중 '언제'가 더 중요합니다.<i></i></p>  <p>ListView의 항목 렌더링은 HTML에 정의된 선언적 템플릿 또는 목록의 모든 항목에 대해 호출되는 사용자 지정 JavaScript <i></i>렌더링 함수를 통해 이루어집니다. 선언적 템플릿을 사용하는 것이 가장 간단한 방법이지만 전체 프로세스에서 특정 컨트롤을 융통성 있게 사용할 수 없다는 단점이 있습니다. 한편 렌더링 함수는 항목별로 렌더링을 사용자 지정할 수 있어서 <a href="http://code.msdn.microsoft.com/windowsapps/ListView-performance-39fb71f0" target="_blank">HTML ListView 성능 최적화 샘플</a>에서 보여 준 것처럼 다양한 최적화가 가능합니다. 최적화의 종류는 다음과 같습니다.</p>  <ul>   <li>기본 렌더링 함수에서 지원하는 기능을 사용하여 항목 데이터 및 렌더링된 요소를 비동기식으로 제공할 수 있습니다.</li>    <li>ListView의 전체적인 레이아웃에 필요한 항목 모양 생성을 내부 요소와 분리할 수 있습니다. 이 기능은 자리 표시자 렌더러를 통해 지원됩니다.</li>    <li>데이터를 대체하여 이전에 생성한 항목 요소 및 그 하위 요소를 재사용함으로써 요소 생성 단계 대부분을 생략하고 자리 표시자 렌더러를 재활용할 수 있습니다.</li>    <li>항목이 표시되고 ListView가 빠른 속도로 이동되지 않을 때까지 이미지 로딩이나 애니메이션처럼 리소스 소모가 많은 시각화 작업을 지연합니다. 이 기능은 다단계 렌더러를 통해 수행됩니다.</li>    <li>다단계 일괄 처리 렌더러를 통해 동일한 시각화 작업을 일괄 처리함으로써 DOM 재렌더링을 최소화합니다.</li> </ul>  <p>이 글에서는 이러한 단계를 살펴보고 ListView의 항목 렌더링 프로세스와 어떻게 연동하는지 알아보겠습니다. 어느 정도는 예상하시겠지만 항목 렌더링의 '시기'를 최적화하는 문제는 비동기 작업과 관련이 깊기 때문에 <i></i>다양한 promise가 나오게 됩니다. 그러므로 이 블로그의 이전 글 <a href="http://blogs.msdn.com/b/windowsappdev/archive/2013/06/10/all-about-promises-for-windows-store-apps-written-in-javascript.aspx" target="_blank">promise의 모든 것</a>을 바탕으로 promise 자체에 대해서도 자세히 알아볼 것입니다.</p>  <p>모든 렌더러에 적용되는 공통 사항으로, 언제나 핵심 항목 렌더링 시간(지연된 작업은 제외)을 최소로 유지하는 것이 중요합니다. ListView의 최종 성능은 업데이트가 화면 새로 고침 간격과 얼마나 일치하느냐에 따라 크게 좌우됩니다. 항목 렌더러에 소비하는 시간(밀리초)이 조금만 길어져도 ListView의 전체 렌더링 시간이 다음 새로 고침 간격까지 늘어나서 프레임이 손실되고 화면 끊김 현상이 발생합니다. 다시 말해서, 항목 렌더러는 JavaScript 코드 최적화에서 매우 중요한 역할을 합니다.</p>  <h2>기본 렌더러</h2>  <p>항목 렌더링 함수, 줄여서 <i></i>'렌더러'가 어떤 모양인지 신속하게 살펴보겠습니다. 렌더러는 ListView의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh700705.aspx" target="_blank">itemTemplate</a> 속성에 템플릿 이름 대신 할당하는 함수로, 필요에 따라 ListView가 DOM에 포함하려는 항목에 대해 호출됩니다. (렌더러에 대한 기본 설명서는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh700705.aspx" target="_blank">itemTemplate</a> 페이지에서 찾을 수 있지만 이 페이지에서는 최적화만을 간단하게 보여 줍니다.)</p>  <p>여러분은 항목 렌더링 함수에 ListView 데이터 원본의 항목이 제공되고, 그 후 해당 항목에 필요한 HTML 요소를 생성한 후 ListView가 DOM에 추가할 수 있는 루트 요소를 반환할 것으로 예상하실 것입니다. 기본적으로는 이 예상이 맞지만 두 가지 사항을 더 고려해야 합니다. 첫째, 항목 데이터 자체는 비동기적으로 로드되기 때문에 요소 생성을 해당 데이터의 가용성과 연결할 수 있습니다. 뿐만 아니라 항목 자체를 렌더링하는 프로세스에 원격 URI에서 이미지를 로드한다거나 항목 데이터에서 확인된 다른 파일을 읽는 등의 다른 비동기 작업이 관련될 수 있습니다. 앞으로 보게 될 다양한 최적화 수준에서 항목 요소를 요청하고 해당 요소를 실제로 제공하는 사이에 수많은 비동기 작업이 가능합니다.</p>  <p>그렇기 때문에 이번에도 promise가 관련될 것으로 예상할 수 있습니다! 첫 번째 근거로, ListView는 항목 데이터에 렌더러를 직접 제공하지 않고 해당 데이터에 대한 promise를 제공합니다. 그리고 함수는 항목의 루트 요소를 직접 반환하지 않고 해당 요소에 대한 promise를 반환합니다. 따라서 항목으로 구성된 전체 페이지가 렌더링될 때까지 ListView가 여러 항목 렌더링 promise를 함께 연결하여 기다릴(비동기) 수 있습니다. 이러한 방법을 통해 여러 페이지를 지능적으로 구축합니다. 시각적 항목으로 구성되는 페이지를 먼저 구축한 후 그 페이지 앞뒤로 사용자가 그 다음으로 이동할 확률이 높은 오프스크린 두 페이지를 구축합니다. 뿐만 아니라 이러한 promise를 모두 제 위치에 배치하면 사용자가 페이지를 이동할 때 ListView가 완료되지 않은 항목에 대한 렌더링을 쉽게 취소할 수 있으므로 불필요한 요소를 생성하지 않을 수 있습니다.</p>  <p>다음 샘플을 통해 이러한 promise가 <b>simpleRenderer</b> 함수에서 어떻게 사용되는지 살펴보겠습니다.</p>  <p> </p>  <blockquote>   <div id="codeSnippetWrapper">     <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> simpleRenderer(itemPromise) {<br />    <span style="color: rgb(0, 0, 255);">return</span> itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />        <span style="color: rgb(0, 0, 255);">var</span> element = document.createElement(<span style="color: rgb(0, 96, 128);">&quot;div&quot;</span>);<br />        element.className = <span style="color: rgb(0, 96, 128);">&quot;itemTempl&quot;</span>;<br />        element.innerHTML = <span style="color: rgb(0, 96, 128);">&quot;&lt;img src='&quot;</span> + item.data.thumbnail +<br />            <span style="color: rgb(0, 96, 128);">&quot;' alt='Databound image' /&gt;&lt;div class='content'&gt;&quot;</span> + item.data.title + <span style="color: rgb(0, 96, 128);">&quot;&lt;/div&gt;&quot;</span>;<br />        <span style="color: rgb(0, 0, 255);">return</span> element;<br />    });<br />}<br /></pre>
  </div>
</blockquote>

<p>이 코드는 가장 먼저 completed 처리기를 <b>itemPromise</b>에 연결합니다. 항목 데이터가 제공되면 처리기가 호출되어 호출에 대한 응답으로 요소를 효과적으로 생성합니다. 다시 한 번 강조하지만 요소를 직접 반환하는 것이 아니라 해당 요소로 처리되는 promise를 반환하는 것입니다. 즉, <b>itemPromise.then()</b>의 반환 값은 ListView가 <i></i>'요소'를 필요로 할 때 해당 요소에 의해 처리되는 promise입니다.</p>

<p>promise를 반환하기 때문에 필요하다면 다른 비동기 작업을 수행할 수 있습니다. 이 사례에서 렌더러는 중간 promise를 연결하여 체인의 마지막 <b>then</b>에서 해당 promise를 반환할 수 있습니다. 예를 들면 다음과 같습니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> someRenderer(itemPromise) {<br />    <span style="color: rgb(0, 0, 255);">return</span> itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />        <span style="color: rgb(0, 0, 255);">return</span> doSomeWorkAsync(item.data);<br />    }).then(<span style="color: rgb(0, 0, 255);">function</span> (results) {<br />        <span style="color: rgb(0, 0, 255);">return</span> doMoreWorkAsync(results1);<br />    }).then(<span style="color: rgb(0, 0, 255);">function</span> (results2) {<br />        <span style="color: rgb(0, 0, 255);">var</span> element = document.createElement(<span style="color: rgb(0, 96, 128);">&quot;div&quot;</span>);<br />        <span style="color: rgb(0, 128, 0);">// use results2 to configure the element</span><br />        <span style="color: rgb(0, 0, 255);">return</span> element;<br />    });<br />}<br /></pre>
  </div>
</blockquote>

<p><i></i>이 예에서는 마지막 <b>then</b> 호출에서 promise를 반환하기 때문에 체인의 마지막에 <b>done</b>을 사용하지 '않는다'는 점에 주의하시기 바랍니다. 발생하는 모든 오류는 ListView에서 처리합니다.</p>

<h2>자리 표시자 렌더러</h2>

<p>ListView 최적화의 다음 단계에서는 <i></i>요소 구축을 두 단계로 나누는 '자리 표시자 렌더러'를 사용합니다. 이렇게 하면 ListView가 각 항목 내에 모든 요소를 구축하지 않고도 목록의 전체적인 레이아웃을 정의하는 데 필요한 요소 부분만 요청할 수 있습니다. 결과적으로 ListView가 레이아웃 단계를 신속하게 완료하고 추가 입력에 대해 계속해서 민첩하게 반응할 수 있습니다. 그리고 나머지 요소는 나중에 요청할 수 있습니다.</p>

<p>자리 표시자 렌더러는 하나의 promise만을 반환하는 것이 아니라 다음과 같은 두 가지 속성을 가진 개체를 반환합니다.</p>

<ul>
  <li><i></i>element - 크기 및 모양을 정의하기에 충분하고 항목 데이터에 종속되지 않는 항목 구조의 최상위 요소입니다.</li>

  <li><i></i>renderComplete - 남은 요소의 콘텐츠가 구성될 때 처리되는 promise입니다. 다시 말해서 앞에서 본 것처럼 <b>itemPromise.then</b>으로 시작하는 체인에서 promise를 반환합니다.</li>
</ul>

<p>ListView는 렌더러가 promise를 반환하는지(이전과 같은 기본 사례) 아니면 <i></i>'element' 및 <i></i>'renderComplete' 속성을 반환하는지(좀 더 발전된 사례) 확인할 수 있을 정도로 똑똑합니다. 따라서 이전 <b>simpleRenderer</b>와 동등한 자리 표시자 렌더러는 다음과 같습니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> placeholderRenderer(itemPromise) {<br />    <span style="color: rgb(0, 128, 0);">// create a basic template for the item that doesn't depend on the data</span><br />    <span style="color: rgb(0, 0, 255);">var</span> element = document.createElement(<span style="color: rgb(0, 96, 128);">&quot;div&quot;</span>);<br />    element.className = <span style="color: rgb(0, 96, 128);">&quot;itemTempl&quot;</span>;<br />    element.innerHTML = <span style="color: rgb(0, 96, 128);">&quot;&lt;div class='content'&gt;...&lt;/div&gt;&quot;</span>;<br /><br />    <span style="color: rgb(0, 128, 0);">// return the element as the placeholder, and a callback to update it when data is available</span><br />    <span style="color: rgb(0, 0, 255);">return</span> {<br />        element: element,<br /><br />        <span style="color: rgb(0, 128, 0);">// specifies a promise that will be completed when rendering is complete</span><br />        <span style="color: rgb(0, 128, 0);">// itemPromise will complete when the data is available</span><br />        renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />            <span style="color: rgb(0, 128, 0);">// mutate the element to include the data</span><br />            element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>).innerText = item.data.title;<br />            element.insertAdjacentHTML(<span style="color: rgb(0, 96, 128);">&quot;afterBegin&quot;</span>, <span style="color: rgb(0, 96, 128);">&quot;&lt;img src='&quot;</span> +<br />                item.data.thumbnail + <span style="color: rgb(0, 96, 128);">&quot;' alt='Databound image' /&gt;&quot;</span>);<br />        })<br />    };<br />}<br /></pre>
  </div>
</blockquote>

<p><i></i>샘플의 css/scenario1.css 파일의 'itemTempl' 클래스가 항목의 너비와 높이를 직접 지정하기 때문에 'element.innerHTML'의 배치를 <b>renderComplete</b> 내부로 <i></i>이동할 수 있습니다. <i></i>'element' 속성에 포함된 것은 자리 표시자에 기본 “…” 텍스트를 제공하기 때문입니다. 개발자는 모든 항목 간에 공유하는(그래서 렌더링 속도가 빠른) 작은 인패키지(in-package) 리소스를 참조하는 <i></i>'img' 요소를 간편하게 사용할 수 있습니다.</p>

<h2>자리 표시자 렌더러 재활용</h2>

<p>다음 최적화인 <i></i>'자리 표시자 재활용' 렌더러는 promise와 관련이 있는 위치에 새 항목을 전혀 추가하지 않습니다. 그보다는 <i></i>앞에서 렌더링되었지만 더 이상 표시되지 않는 항목의 루트 요소인 'recycled'라는 렌더러에 두 번째 매개 변수의 존재를 인식시킵니다. 즉, recycled 요소의 하위 요소가 이미 제 위치에 있기 때문에 개발자가 데이터를 대체하거나 일부 요소를 수정할 수 있습니다. 완전히 새로운 항목을 추가할 경우 리소스가 많이 드는 요소 생성 호출 과정이 필요한데, 이 과정을 대부분 생략할 수 있으므로 렌더링 프로세스 시간을 크게 절약할 수 있습니다.</p>

<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211835.aspx" target="_blank">loadingBehavior</a><b></b>를 "randomaccess"로 설정할 경우 ListView가 recycled 요소를 제공할 수 있습니다. <i></i>'recycled'가 제공되면 요소 및 하위 요소에서 데이터를 삭제하고 자리 표시자로 반환한 다음 데이터를 채우고 필요하다면 <i></i>'renderComplete' 내에 하위 요소를 추가할 수 있습니다. ListView가 맨 처음으로 생성되거나 <b>loadingBehavior</b>가 "incremental"로 설정되어 있어서 recycled 요소가 제공되지 않을 경우 요소를 새로 생성해야 합니다. 다음은 이러한 변형에 대한 샘플 코드입니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> recyclingPlaceholderRenderer(itemPromise, recycled) {<br />    <span style="color: rgb(0, 0, 255);">var</span> element, img, label;<br />    <span style="color: rgb(0, 0, 255);">if</span> (!recycled) {<br />        <span style="color: rgb(0, 128, 0);">// create a basic template for the item that doesn't depend on the data</span><br />        element = document.createElement(<span style="color: rgb(0, 96, 128);">&quot;div&quot;</span>);<br />        element.className = <span style="color: rgb(0, 96, 128);">&quot;itemTempl&quot;</span>;<br />        element.innerHTML = <span style="color: rgb(0, 96, 128);">&quot;&lt;img alt='Databound image' style='visibility:hidden;'/&gt;&quot;</span> +<br />            <span style="color: rgb(0, 96, 128);">&quot;&lt;div class='content'&gt;...&lt;/div&gt;&quot;</span>;<br />    }<br />    <span style="color: rgb(0, 0, 255);">else</span> {<br />        <span style="color: rgb(0, 128, 0);">// clean up the recycled element so that we can reuse it </span><br />        element = recycled;<br />        label = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>);<br />        label.innerHTML = <span style="color: rgb(0, 96, 128);">&quot;...&quot;</span>;<br />        img = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;img&quot;</span>);<br />        img.style.visibility = <span style="color: rgb(0, 96, 128);">&quot;hidden&quot;</span>;<br />    }<br />    <span style="color: rgb(0, 0, 255);">return</span> {<br />        element: element,<br />        renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />            <span style="color: rgb(0, 128, 0);">// mutate the element to include the data</span><br />            <span style="color: rgb(0, 0, 255);">if</span> (!label) {<br />                label = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>);<br />                img = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;img&quot;</span>);<br />            }<br />            label.innerText = item.data.title;<br />            img.src = item.data.thumbnail;<br />            img.style.visibility = <span style="color: rgb(0, 96, 128);">&quot;visible&quot;</span>;<br />        })<br />    };<br />}<br /></pre>
  </div>
</blockquote>

<p><i></i>'renderComplete'에서 <i></i>새 자리 표시자에 대해 생성하지 않은 요소(예: 'label')가 있는지 확인하고 필요하다면 요소를 생성합니다.</p>

<p>좀 더 일반적인 방법으로 recycled 항목을 제거하려면 ListView의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211846.aspx" target="_blank">resetItem</a><b></b> 속성에 함수를 제공하는 방법을 고려해 보십시오. 이 함수는 위에서 보여 준 것과 비슷한 코드를 갖고 있습니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh700726.aspx" target="_blank">resetGroupHeader</a><b></b> 속성도 동일합니다. 그룹 헤더뿐만 아니라 항목에도 템플릿 함수를 사용할 수 있기 때문입니다. 그룹 헤더는 그 수가 훨씬 적고 일반적으로 성능에 미치는 영향이 같지 않기 때문에 자세히 다루지는 않았습니다. 하지만 그 성능은 보시는 바와 같습니다.</p>

<h2>다단계 렌더러</h2>

<p>이번에 살펴 볼 최적화는 <i></i>'다단계 렌더러'입니다. 이 최적화에서는 나머지 항목이 DOM에 모두 표시될 때까지 재활용 자리 표시자 렌더러를 로드 지연 이미지 및 기타 미디어로 확장합니다. 또한 항목이 실제로 화면에 나타날 때까지 애니메이션 같은 효과를 지연합니다. 이를 통해 사용자가 ListView를 신속하게 이동하는 것을 인식하므로 ListView가 안정적인 상태가 될 때까지 리소스가 많이 필요한 작업을 비동기적으로 지연할 수 있습니다.</p>

<p><b>item</b>의 멤버는 <b>itemPromise</b> 즉, <b>ready</b>라는 속성(promise)과 두 개의 메서드 <b>loadImage</b> 및 <b>isOnScreen</b>에서 오기 때문에 ListView가 필요한 후크를 제공하며 둘 모두 (더 많은) promise를 반환합니다. 예를 들어 다음과 같습니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// item.ready, item.loadImage, and item.isOnScreen available</span><br />})<br /></pre>
  </div>
</blockquote>

<p>사용 방법은 다음과 같습니다.</p>

<ul>
  <li><b>ready</b> 체인의 첫 번째 completed 처리기에서 이 promise를 반환합니다. 이 promise는 요소의 전체 구조가 렌더링되어 표시될 때 처리됩니다. 즉, 또 다른 <b>then</b>을 이미지 로딩 등의 시각화 후속 작업을 하는 completed 처리기와 체인으로 연결할 수 있습니다.</li>

  <li><b>loadImage</b> URI에서 이미지를 다운로드하여 제공된 <i></i>'img' 요소에 표시하고, 같은 요소에 의해 처리되는 promise를 반환합니다. 이 promise에 completed 처리기를 연결하면 스스로 <b>isOnScreen</b>에서 promise를 반환합니다. img 요소를 제공하지 않을 경우 <b>loadImage</b>가 <i></i>'img' 요소를 생성하여 completed 처리기에 제공합니다.</li>

  <li><b>isOnScreen</b> 처리 값이 항목의 표시 여부를 나타내는 부울인 promise를 반환합니다. 현재 구축에서는 알려진 값이기 때문에 promise가 동기적으로 처리됩니다. 하지만 promise에 래핑하면 보다 긴 체인에 사용할 수 있습니다.</li>
</ul>

<p>이 모든 것을 적용한 것이 아래 샘플의 <b>multistageRenderer</b> 함수이며, 이미지 로드가 완료되는 위치에서 페이드 인 애니메이션이 시작됩니다. <i></i>'renderComplete' promise에서 무엇이 반환되는지 잘 보시기 바랍니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// mutate the element to update only the title</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (!label) { label = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>); }<br />    label.innerText = item.data.title;<br /><br />    <span style="color: rgb(0, 128, 0);">// use the item.ready promise to delay the more expensive work</span><br />    <span style="color: rgb(0, 0, 255);">return</span> item.ready;<br />    <span style="color: rgb(0, 128, 0);">// use the ability to chain promises, to enable work to be cancelled</span><br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// use the image loader to queue the loading of the image</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (!img) { img = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;img&quot;</span>); }<br />    <span style="color: rgb(0, 0, 255);">return</span> item.loadImage(item.data.thumbnail, img).then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />        <span style="color: rgb(0, 128, 0);">// once loaded check if the item is visible</span><br />        <span style="color: rgb(0, 0, 255);">return</span> item.isOnScreen();<br />    });<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (onscreen) {<br />    <span style="color: rgb(0, 0, 255);">if</span> (!onscreen) {<br />        <span style="color: rgb(0, 128, 0);">// if the item is not visible, don't animate its opacity</span><br />        img.style.opacity = 1;<br />    } <span style="color: rgb(0, 0, 255);">else</span> {<br />        <span style="color: rgb(0, 128, 0);">// if the item is visible, animate the opacity of the image</span><br />        WinJS.UI.Animation.fadeIn(img);<br />    }<br />})<br /></pre>
  </div>
</blockquote>

<p>다양한 작업이 진행되지만 여전히 기본 promise 체인만을 사용하고 있습니다. 렌더러의 첫 번째 비동기 작업은 항목 요소 구조에서 텍스트처럼 간단한 부분을 업데이트하는 것입니다. 그러면 <b>item.ready</b>에서 promise가 반환됩니다. 해당 promise가 처리될 때, 좀 더 정확하게 말해서 해당 promise가 처리될 경우 항목의 비동기 <b>loadImage</b> 메서드를 사용하여 이미지 다운로드를 시작하면 해당 completed 처리기에서 <b>item.isOnScreen</b> promise가 반환됩니다. 즉, <i></i>'화면' 가시성 플래그가 체인의 마지막 completed 처리기에 전달됩니다. <b>isOnScreen</b> promise가 처리될 때, 조금 더 정확히 말해서 처리될 경우, 즉 항목이 실제로 표시될 때 애니메이션과 같은 관련 작업을 수행할 수 있습니다.</p>

<p>처리될 경우를 강조해서 말씀 드렸는데, 이 동작이 수행될 때 사용자가 ListView 내부를 돌아다니고 있을 확률이 매우 높기 때문입니다. 모든 promise를 체인으로 연결해 놓으면 항목이 화면에 표시되지 않거나 버퍼링된 페이지가 사라질 때 ListView가 비동기 작업을 취소할 수 있습니다. 이 정도면 ListView 컨트롤이 <i></i>'수많은' 성능 테스트를 거쳤다는 충분한 증거가 되지 않을까요?</p>

<p>또한 여전히 'renderComplete' 속성 내의 렌더링 함수에서 promise를 반환하고 있기 때문에 모든 체인에서 <b>then</b>을 사용한다는 점을 다시 한 번 강조하겠습니다.<i></i> 이러한 렌더러에서 체인 마지막까지 갈 일이 없기 때문에 마지막 부분에 <b>done</b>을 사용할 일도 없습니다.</p>

<h2>미리 보기 일괄 처리</h2>

<p>마지막 최적화는 ListView 컨트롤을 향해 날리는 '마지막 일격'입니다.<i></i> <b>batchRenderer</b>라는 함수를 보면 <i></i>다음과 같은 renderComplete 구조를 확인할 수 있습니다(대부분의 코드 생략).</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// mutate the element to update only the title</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (!label) { label = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>); }<br />    label.innerText = item.data.title;<br /><br />    <span style="color: rgb(0, 128, 0);">// use the item.ready promise to delay the more expensive work</span><br />    <span style="color: rgb(0, 0, 255);">return</span> item.ready;<br />    <span style="color: rgb(0, 128, 0);">// use the ability to chain promises, to enable work to be cancelled</span><br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// use the image loader to queue the loading of the image</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (!img) { img = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;img&quot;</span>); }<br />    <span style="color: rgb(0, 0, 255);">return</span> item.loadImage(item.data.thumbnail, img).then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />        <span style="color: rgb(0, 128, 0);">// once loaded check if the item is visible</span><br />        <span style="color: rgb(0, 0, 255);">return</span> item.isOnScreen();<br />    });<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (onscreen) {<br />    <span style="color: rgb(0, 0, 255);">if</span> (!onscreen) {<br />        <span style="color: rgb(0, 128, 0);">// if the item is not visible, don't animate its opacity</span><br />        img.style.opacity = 1;<br />    } <span style="color: rgb(0, 0, 255);">else</span> {<br />        <span style="color: rgb(0, 128, 0);">// if the item is visible, animate the opacity of the image</span><br />        WinJS.UI.Animation.fadeIn(img);<br />    }<br />})<br /></pre>
  </div>
</blockquote>

<p><b>item.loadImage</b> 호출과 <b>item.isOnScreen</b> 확인 사이에 <b>thumbnailBatch</b>라는 이 신기한 함수를 호출하는 것만 빼면 <b>multistageRenderer</b>와 거의 차이가 없습니다. 체인에 <b>thumbnailBatch</b>를 배치했기 때문에 그 반환 값은 자체적으로 또 다른 promise를 반환하는 completed 처리기여야 합니다.</p>

<p>어려운가요? 그렇다면 자세히 알아보겠습니다. 하지만 그 전에 우리가 하려는 것에 대한 배경 지식을 좀 더 쌓아야 합니다.</p>

<p>ListView에 항목이 하나밖에 없다면 다양한 로드 최적화가 눈에 띄지 않을 수 있습니다. 하지만 일반적으로 ListView에는 여러 항목이 있고 각 항목마다 렌더링 함수가 호출됩니다. 이전 단원의 <b>multistageRenderer</b>에서 각 항목을 렌더링하면 비동기 <b>item.loadImage</b> 작업이 시작되어 임의의 URI에서 축소판이 다운로드되며, 각 작업마다 임의의 시간이 배정될 수 있습니다. 따라서 목록 전체를 놓고 보면 여러 <b>loadImage</b> 호출이 동시에 진행되고, 각 항목의 렌더링은 특정 미리 보기가 완료될 때까지 기다립니다. 여기까지는 별 문제가 없을 것입니다.</p>

<p>하지만 <b>multistageRenderer</b>에서 전혀 보이지 않는 중요한 특징 한 가지가 있습니다. 그것은 바로 미리 보기의 <i></i>'img' 요소가 <i></i>'이미' DOM에 있으며, 다운로드가 완료되는 즉시 <b>loadImage</b> 함수가 해당 이미지의 <i></i>'src' 특성을 설정한다는 것입니다. 결국 promise 체인의 나머지 부분에서 돌아오는 즉시 렌더링 엔진에서 업데이트가 트리거되고, 해당 시점 이후부터는 근본적으로 동기 상태가 됩니다.</p>

<p>따라서 짧은 시간 내에 다수의 미리 보기가 UI 스레드로 돌아올 수 있고 그렇게 될 경우 렌더링 엔진에 과도한 변동이 일어나 표시 성능이 떨어지게 됩니다. 과도한 변동을 피하려면 이러한 <i></i>'img' 요소가 DOM에 있기 '이전에' img 요소를 완전히 생성한 후 <i></i>단일 렌더링 경로에서 모두 처리할 수 있도록 img 요소를 일괄적으로 추가해야 합니다.</p>

<p>샘플에서는 <b>createBatch</b>라는 마법과 같은 promise 코드 함수를 통해 이 작업을 처리했습니다. <b>createBatch</b>는 전체 앱에 대해 단 한 번만 호출되며, 그 결과(또 다른 함수)는 <b>thumbnailBatch</b>라는 변수에 저장됩니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> thumbnailBatch;<br />thumbnailBatch = createBatch();<br /></pre>
  </div>
</blockquote>

<p>지금부터 설명할 이 <b>thumbnailBatch</b> 함수를 호출하면 다시 렌더러의 promise 체인으로 삽입됩니다. 이렇게 삽입하는 이유는 잠시 후 보게 될 일괄 처리 코드의 특성을 감안한 것으로, 로드된 'img' 요소를 그룹화해 두었다가 나중에 적절한 간격으로 릴리스하여 처리하기 위함입니다.<i></i> 다시 한 번 강조하지만, 렌더러의 promise 체인을 살펴보면 <b>thumbnailBatch()</b> 호출의 결과로 promise를 반환하는 completed 처리기가 반환되어야 하며, 해당 promise의 처리 값(체인의 다음 단계를 살펴볼 것)은 이후에 DOM에 추가할 수 있는 <i></i>'img' 요소여야 합니다. 일괄 처리 '후' DOM에 <i></i>이미지를 추가하여 전체 그룹을 동일한 렌더링 경로로 결합할 수 있습니다.</p>

<p>이것이 바로 이전 단원에서 본 <b>batchRenderer</b>와 <b>multistageRenderer</b>의 결정적인 차이점입니다. 후자의 경우 미리 보기의 <i></i>'img' 요소가 이미 DOM에 있으며 <b>loadImage</b>에 두 번째 매개 변수로 전달됩니다. 따라서 <b>loadImage</b>가 이미지의 <i></i>'src' 특성을 설정할 때 렌더링 업데이트가 트리거됩니다. <b>batchRenderer</b> 내에서는 <i></i>'img' 요소가 <b>loadImage</b> 내에 별도로 생성되지만(<i></i>'src' 또한 설정됨) <i></i>'img'가 아직 DOM에 없습니다. <b>thumbnailBatch</b> 단계가 완료된 후에만 DOM에 추가되어 단일 레이아웃 단계 내에 있는 그룹에 속하게 됩니다.</p>

<p>그러면 지금부터 일괄 처리 작업이 어떻게 수행되는지 살펴보겠습니다. 다음은 완전한 <b>createBatch</b> 함수입니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> createBatch(waitPeriod) {<br />    <span style="color: rgb(0, 0, 255);">var</span> batchTimeout = WinJS.Promise.<span style="color: rgb(0, 0, 255);">as</span>();<br />    <span style="color: rgb(0, 0, 255);">var</span> batchedItems = [];<br /><br />    <span style="color: rgb(0, 0, 255);">function</span> completeBatch() {<br />        <span style="color: rgb(0, 0, 255);">var</span> callbacks = batchedItems;<br />        batchedItems = [];<br />        <span style="color: rgb(0, 0, 255);">for</span> (<span style="color: rgb(0, 0, 255);">var</span> i = 0; i &lt; callbacks.length; i++) {<br />            callbacks[i]();<br />        }<br />    }<br /><br />    <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> () {<br />        batchTimeout.cancel();<br />        batchTimeout = WinJS.Promise.timeout(waitPeriod || 64).then(completeBatch);<br /><br />        <span style="color: rgb(0, 0, 255);">var</span> delayedPromise = <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (c) {<br />            batchedItems.push(c);<br />        });<br /><br />        <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> (v) {<br />            <span style="color: rgb(0, 0, 255);">return</span> delayedPromise.then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />                <span style="color: rgb(0, 0, 255);">return</span> v;<br />            });<br />        };<br />    };<br />}<br /></pre>
  </div>
</blockquote>

<p>다시 한 번 말씀 드리지만 <b>createBatch</b>는 '한 번만' 호출되며 목록의 '각' 렌더링된 항목마다 <i></i><b>thumbnailBatch</b> 결과가 <i></i><i></i>호출됩니다. 그 후 <b>loadImage</b> 작업이 완료될 때 <b>thumbnailBatch</b>에서 생성하는 completed 처리기가 호출됩니다.</p>

<p>이러한 completed 처리기를 아주 간단하게 렌더링 함수에 직접 삽입할 수 있지만 여기서 우리가 하려는 것은 항목별 조정 작업이 아닌 <i></i>'여러 항목에 걸친' 조정 작업입니다. 이 조정 작업은 <b>createBatch</b> 시작 부분에서 생성 및 초기화되는 두 개의 변수를 통해 수행됩니다. 하나는 빈 promise로 초기화되는 <i></i>'batchedTimeout'이고, 다른 하나는 처음에는 비어 있는 여러 함수를 초기화하는 <i></i>'batchedItems'입니다. <b>createBatch</b> 역시 <b>completeBatch</b> 함수를 선언하며, 이 함수는 <i></i>'batchedItems'를 비우고 배열의 각 함수를 호출합니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> createBatch(waitPeriod) {<br />    <span style="color: rgb(0, 0, 255);">var</span> batchTimeout = WinJS.Promise.<span style="color: rgb(0, 0, 255);">as</span>();<br />    <span style="color: rgb(0, 0, 255);">var</span> batchedItems = [];<br /><br />    <span style="color: rgb(0, 0, 255);">function</span> completeBatch() {<br />        <span style="color: rgb(0, 0, 255);">var</span> callbacks = batchedItems;<br />        batchedItems = [];<br />        <span style="color: rgb(0, 0, 255);">for</span> (<span style="color: rgb(0, 0, 255);">var</span> i = 0; i &lt; callbacks.length; i++) {<br />            callbacks[i]();<br />        }<br />    }<br /><br />    <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> () {<br />        batchTimeout.cancel();<br />        batchTimeout = WinJS.Promise.timeout(waitPeriod || 64).then(completeBatch);<br /><br />        <span style="color: rgb(0, 0, 255);">var</span> delayedPromise = <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (c) {<br />            batchedItems.push(c);<br />        });<br /><br />        <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> (v) {<br />            <span style="color: rgb(0, 0, 255);">return</span> delayedPromise.then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />                <span style="color: rgb(0, 0, 255);">return</span> v;<br />            });<br />        };<br />    };<br />}<br /></pre>
  </div>
</blockquote>

<p>렌더링되는 각 항목에 대해 다시 호출되는 <b>thumbnailBatch</b>(<b>createBatch</b>에서 반환되는 함수) 내에서 어떤 일이 벌어지는지 살펴봅시다. 먼저 <i></i>기존 <i></i>'batchedTimeout'을 '취소'하고 바로 다시 만듭니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">batchTimeout<span style="color: rgb(255, 0, 0);">.</span>cancel();<br />        batchTimeout = WinJS<span style="color: rgb(255, 0, 0);">.</span>Promise<span style="color: rgb(255, 0, 0);">.</span>timeout(waitPeriod || 64)<span style="color: rgb(255, 0, 0);">.</span>then(completeBatch);<br /></pre>
  </div>
</blockquote>

<p>두 번째 줄을 보면 Promises의 모든 것&lt;TODO: link>에서 살펴본 미래의 제공/처리 패턴이 보입니다. 'waitPeriod' 밀리초(기본값은 64밀리초) 후 <b>completeBatch</b>를 <i></i>호출하라고 합니다. 다시 말해서 이전 호출의 'waitPeriod' 내에 <b>thumbnailBatch</b>를 다시 호출할 경우 <i></i> <b>batchTimeout</b>이 또 다른 waitPeriod에 대해 설정된다는 뜻입니다.<i></i> 그리고 <b>item.loadImage</b> 호출이 완료된 '후' <i></i><b>thumbnailBatch</b>만 호출되기 때문에 이전 호출의 'waitPeriod' 내에 완료되는 모든 <b>loadImage</b> 작업이 같은 배치에 포함된다고 자신 있게 말할 수 있습니다.<i></i> <i></i>'waitPeriod'보다 더 긴 간격이 있을 경우 배치가 처리되고(이미지가 DOM에 추가되고) 다음 배치가 시작됩니다.</p>

<p>이 시간 제한 작업을 처리한 후 <b>thumbnailBatch</b>가 complete 디스패처 함수를 'batchedItems' 배열에 단순히 푸시하는 새로운 promise를 생성합니다.<i></i></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> delayedPromise = <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (c) {<br />         batchedItems.push(c);<br />     });<br /></pre>
  </div>
</blockquote>

<p>Promise의 모든 것 &lt;TODO: link>에서 promise는 단지 코드 구조일 뿐이라고 했던 것을 기억하시기 바랍니다. 오늘 우리가 하는 모든 것이 그 연장선에 있습니다. 새로 생성된 promise는 그 자체로는 비동기 동작이 전혀 없습니다. 우리는 지금 complete 디스패처 함수 <i></i>'c'를 <i></i>'batchedItems'에 추가하고 있습니다. 물론 'batchedTimeout'이 비동기적으로 완료되기 전에는 디스패처에 아무 것도 하지 않습니다. <i></i>따라서 여기서는 사실상 비동기 관계가 하나 있습니다. 시간 제한이 발생하고 <b>completeBatch</b> 내의 배치를 비울 경우 다른 곳에서 <b>delayedPromise.then</b>에 제공되는 completed 처리기를 호출하게 됩니다.</p>

<p>그러면 <b>thumbnailBatch</b>의 코드 마지막 줄로 이동합니다. 이 함수는 <b>createBatch</b>가 실제로 반환하는 함수입니다. 이 함수는 렌더러의 전체 promise 체인에 삽입되는 바로 그 completed 처리기입니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> (v) {<br />           <span style="color: rgb(0, 0, 255);">return</span> delayedPromise.then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />               <span style="color: rgb(0, 0, 255);">return</span> v;<br />           });<br />       };<br /></pre>
  </div>
</blockquote>

<p>이 코드를 promise 체인에 바로 넣어서 그 결과로 나타나는 관계를 보겠습니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">return</span> item.loadImage(item.data.thumbnail);<br />          }).then(<span style="color: rgb(0, 0, 255);">function</span> (v) {<br />              <span style="color: rgb(0, 0, 255);">return</span> delayedPromise.then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />                  <span style="color: rgb(0, 0, 255);">return</span> v;<br />              });<br />          ).then(<span style="color: rgb(0, 0, 255);">function</span> (newimg) {<br /></pre>
  </div>
</blockquote>

<p>이제 인수 <i></i>'v'가 <b>item.loadImage</b>의 결과이고, 그 결과는 우리가 원하는 <i></i>'img' 요소라는 것을 알 수 있습니다. 일괄 처리를 원하지 않을 경우 <b>return WinJS.Promise.as(v)</b>만 사용해서 체인 전체가 작동하도록 하면 됩니다. 그렇게 하면 <i></i>'v'가 동기적으로 전달되어 다음 단계에서 <i></i>'newimg'로 나타납니다.</p>

<p>그 대신 현재 'batchedTimeout'이 처리될 때까지 'v'로 처리되지 않는 <b>delayedPromise.then</b>에서 promise를 반환하게 됩니다.<i></i><i></i> 이때 <i></i><b>loadImage</b> 완료 사이에 'waitPeriod' 간격이 또 있을 경우 <i></i>'img' 요소가 체인의 다음 단계로 전달되어 DOM에 추가됩니다.</p>

<p>그것으로 끝입니다!</p>

<h2>결론</h2>

<p><a href="http://code.msdn.microsoft.com/windowsapps/ListView-performance-39fb71f0" target="_blank">HTML ListView 성능 최적화 샘플</a>에서 보여 준 다섯 가지 렌더링 함수 사이에 한 가지 공통점이 있습니다. promise로 표현되는 ListView와 렌더러 간의 비동기 관계로 인해 렌더러가 매우 유연하게 언제 어떻게 목록에 있는 항목에 사용할 요소를 생성할지 알 수 있다는 점입니다. 여러분이 앱을 작성할 때 ListView 최적화에 사용하는 전략은 데이터 원본의 크기, 항목 자체의 복잡성, 해당 항목에 대해 비동기적으로 얻는 데이터의 양(예: 원격 이미지 다운로드)에 크게 좌우됩니다. 당연한 말이겠지만 여러분은 원하는 성능에 도달할 때까지 항목 렌더러를 최대한 단순하게 유지하고 싶을 것입니다. 이제는 어떤 경우에도 ListView와 여러분의 앱이 최고의 성능을 발휘하도록 도와 주는 도구가 준비되어 있습니다.</p>

<p>Kraig Brockschmidt 
  <br />Windows 에코시스템 팀 프로그램 관리자 

  <br />작성자, <a href="http://blogs.msdn.com/b/microsoft_press/archive/2012/10/29/free-ebook-programming-windows-8-apps-with-html-css-and-javascript.aspx"><i></i>HTML, CSS 및 JavaScript로 Windows 8 앱 프로그래밍하기</a></p>
<!-- Title: “ListView 항목 렌더링 최적화” Tags: “Windows 스토어 앱, 렌더, 일괄 처리, 배치, 렌더링, ListView” --></div>]]></content:encoded>
    </item>
    <item>
      <title>Game On Virtual Event 참여하기</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/game-on-virtual-event</link>
      <pubDate>Wed, 12 Jun 2013 11:40:47 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/06/12/game-on-virtual-event/</guid>
      <description><![CDATA[게임은 Windows 스토어 앱의 핵심 분야입니다. Windows 스토어용 게임 앱 개발을 생각하고 있다면 지금 소개할 온라인 게임 개발 이벤트가 도움이 될 것입니다. 이...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>게임은 Windows 스토어 앱의 핵심 분야입니다. Windows 스토어용 게임 앱 개발을 생각하고 있다면 지금 소개할 온라인 게임 개발 이벤트가 도움이 될 것입니다. 이 이벤트는 무료이며 초보 게임 개발자와 전문가 모두에게 개방되어 있습니다. </p>  <p>YoYo Games의 <a href="https://aka.ms/yoyogamesgamemaker" target="_blank">GameMaker: Studio</a>를 이용하여 더 빠른 시간 내에 고품질의 게임을 개발하고 게시하는 방법을 소개하겠습니다. GameMaker:Studio에는 다양하고 훌륭한 기본 디자인 옵션과 끌어서 놓기 도구가 있어서 아직 구체적인 게임 아이디어가 없거나 개발 경험이 없는 분도 쉽게 앱 개발을 시작할 수 있습니다. 하지만 게임 앱 개발 경험이 있는 개발자라면 고급 도구를 활용하는 것이 세상에 단 하나밖에 없는 게임 환경을 개발하는 데 도움이 될 것입니다. </p>  <p><a href="https://aka.ms/gameonevent"><img title="clip_image002" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="clip_image002" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/8666.clip_image002_0385EAB3.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/8666.clip_5F00_image002_5F00_0385EAB3.jpg" width="500" height="154" /></a></p>  <p>이번 온라인 이벤트는 6월 15일 오전 10시(태평양 연안 표준시)부터 온라인 강의와 라이브 채팅으로 진행됩니다.<sup></sup> 이 기회를 통해 여러분은 독창적인 게임을 개발하는 방법뿐만 아니라 다양한 게임 유형 및 기본 게임에 그래픽과 레벨, 소리를 추가하는 방법에 대해서도 배울 수 있습니다. GameMaker 포럼에서 이번 한 주간 내내 일대일 지원을 제공해 드립니다.</p>  <p>이번 이벤트는 학생이나 초보 개발자 또는 노련한 전문가 모두에게 스토어용 게임을 개발하고 업로드하여 Windows 스토어에 게시하는 데 도움이 될 것입니다. <a href="http://msdnevents.com/gameon/" target="_blank">http://msdnevents.com/gameon/</a>에서 자세한 사항을 확인할 수 있으며 이벤트에 등록할 수 있습니다. 무료 도구와 리소스도 확인하십시오. </p>  <p>- Windows 개발자 콘텐츠, 수석 콘텐츠 게시 관리자, Robert Rodriguez-Lawson</p> <!-- Title: “Game On Virtual Event 참여하기” Tags: “Windows 스토어 게임, 게임, Game On, 이벤트” --></div>]]></content:encoded>
    </item>
    <item>
      <title>자격 증명 보관: Windows 스토어 앱의 사용자 이름과 암호 처리를 위한 솔루션</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/windows-5094</link>
      <pubDate>Tue, 04 Jun 2013 19:43:00 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/06/04/windows-5094/</guid>
      <description><![CDATA[아직도 Windows 스토어 앱에서 사용 가능한 자격 증명 보관에 대해서 들어본 적이 없으십니까? 그렇다면 이 글을 꼭 보셔야 합니다. 왜냐하면 자격 증명 보관을 이용하면...]]></description>
      <content:encoded><![CDATA[<div class="PostContent">
<p>아직도 Windows 스토어 앱에서 사용 가능한 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465069.aspx" target="_blank">자격 증명 보관</a>에 대해서 들어본 적이 없으십니까? 그렇다면 이 글을 꼭 보셔야 합니다. 왜냐하면 자격 증명 보관을 이용하면 사용자 자격 증명의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465060.aspx" target="_blank">저장</a> 및 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/jj839731.aspx" target="_blank">검색</a> 작업이 간편해질 뿐만 아니라 자격 증명을 안전하게 저장할 수 있기 때문입니다. 또한 사용자는 자격 증명을 '무료로' Microsoft 계정과 로밍할 수도 있습니다.</p>
<p>여러분이 미디어 파일이나 소셜 네트워킹과 같은 보호된 리소스에 액세스하는 서비스를 제공하는 앱을 개발했다고 생각해 봅시다. 서비스 이용을 위해서는 각 사용자의 로그인 정보가 필요합니다. 그래서 여러분은 사용자가 서비스에 로그인할 수 있도록 앱에 사용자 이름과 암호를 입력할 수 있는 UI를 개발할 것이고 특별히 문제는 없습니다.</p>
<p>하지만 여러분은 사용자들에게 좀 더 나은 서비스를 제공하고 싶고 사용자들이 앱을 사용할 때마다 로그인 정보를 입력하지 않아도 되도록 로그인 정보를 안전하게 저장할 수 있기를 원합니다. 그렇다면 바로 자격 증명 보관이 필요합니다. 간단한 자격 증명 보관 API 호출로 사용자 이름과 암호를 저장하고 손쉽게 검색할 수 있으며 사용자가 다음에 앱을 사용할 때 자동으로 로그인할 수 있습니다.</p>
<h2>안전한 저장소</h2>
<p>자격 증명 보관을 앱에 적용했을 때 가장 좋은 점은 사용자 자격 증명이 안전한 위치에 저장되며 디스크에 저장할 때는 자격 증명 정보가 암호화된다는 점입니다. 물론 여러분의 앱에 대한 사용자 자격 증명을 로컬 저장소에 있는 파일에 저장할 수도 있습니다. 하지만 사용자 자격 증명을 일반 텍스트로 저장하면 보안에 상당한 위험이 생길 수 있습니다. 어떤 사용자의 시스템이 침해되면 그 사용자의 사용자 이름과 암호가 쉽게 액세스되고 도용당할 수 있습니다. 하지만 사용자 이름과 암호를 자격 증명 보관을 이용해 저장하면 악성 소스가 침투하기 쉬운 파일은 암호화됩니다.</p>
<p>자격 증명 보관은 각각의 Windows PC 사용자에게만 고유하게 적용되며 자격 증명에 액세스하는 것은 자격 증명을 저장한 앱에서만 가능합니다. 즉 여러분은 여러분의 앱에 저장한 자격 증명만 검색할 수 있으며, 마찬가지로 다른 앱에서는 여러분의 앱 보관소에 저장된 자격 증명에 접근할 수 없습니다.</p>
<h2>자격 증명 로밍</h2>
<p>사용자를 위한 새로운 혜택으로, 여러분이 자격 증명 보관을 이용하여 사용자 이름과 암호를 저장하면 사용자는 신뢰할 수 있는 컴퓨터 어디에서나 저장된 자격 증명을 Microsoft 계정과 로밍할 수 있습니다. 이 기능을 이용하면 앱의 보안을 유지하면서도 사용자들이 앱을 더 편리하게 이용할 수 있습니다. 앱이 설치되어 있고 Microsoft 계정이 연결되어 있다면 자격 증명을 다시 확인하지 않아도 신뢰할 수 있는 모든 컴퓨터에서 자동으로 앱에 로그인할 수 있기 때문입니다.</p>
<p>하지만 도메인 계정에서는 조금 다르게 작동합니다. Microsoft 계정으로 자격 증명이 저장된 경우, 이 계정이 도메인 계정(예를 들면, 직장에서 사용하는 계정)과 연결되어 있다면 자격 증명은 그 도메인 계정과 로밍될 수 있습니다. 하지만 도메인 계정에 로그인했을 때 새로 추가된 자격 증명이 있으면 로밍되지 않습니다. 도메인의 개인 자격 증명이 도메인 외부에 노출되는 것을 방지하기 위한 것입니다.</p>
<h2>사용자 자격 증명 저장</h2>
<p>사용자 자격 증명을 자격 증명 보관으로 저장하는 것은 두 단계로 아주 간단합니다. 먼저 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.aspx" target="_blank">Windows.Security.Credentials</a> 네임스페이스에서 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.passwordvault.aspx" target="_blank">PasswordVault</a> 개체를 사용하여 자격 증명 보관에 대한 참조를 가져옵니다. 그리고 앱의 식별자, 사용자 이름 및 암호를 포함하는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.passwordcredential.aspx" target="_blank">PasswordCredential</a> 개체를 만들어, 이 개체를 자격 증명을 보관소에 추가하기 위한 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh701231.aspx" target="_blank">PasswordVault.Add()</a> 메서드로 보냅니다.</p>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;">var vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />vault.Add(<span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordCredential(<br />    <span style="color: #006080;">"My App"</span>, username, password));</pre>
</div>
</blockquote>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />vault.add(<span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordCredential(<br />    <span style="color: #006080;">"My App"</span>, username, password));</pre>
</div>
</blockquote>
<h2>사용자 자격 증명 검색</h2>
<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.passwordvault.aspx" target="_blank">PasswordVault</a> 개체에 대한 참조를 가져온 후에는 다음과 같은 몇 가지 방법으로 자격 증명 보관에서 자격 증명을 검색할 수 있습니다.</p>
<ul>
<li>사용자가 보관소의 앱에 제공한 모든 자격 증명을 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.passwordvault.retrieveall.aspx" target="_blank">PasswordVault.RetrieveAll()</a> 메서드를 이용하여 검색할 수 있습니다.</li>
<li>저장된 자격 증명의 사용자 이름을 알고 있다면 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.passwordvault.findallbyusername.aspx" target="_blank">PasswordVault.FindAllByUserName()</a> 메서드를 이용하여 그 사용자 이름에 대한 모든 자격 증명을 검색할 수 있습니다.</li>
<li>저장된 자격 증명의 리소스 이름을 알고 있다면 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.passwordvault.findallbyusername.aspx" target="_blank">PasswordVault.FindAllByUserName()</a> 메서드를 이용하여 그 리소스 이름에 대한 모든 자격 증명을 검색할 수 있습니다.</li>
<li>마지막으로 자격 증명의 사용자 이름과 리소스 이름을 모두 알고 있다면 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.passwordvault.retrieve.aspx" target="_blank">PasswordVault.Retrieve()</a> 메서드를 이용하여 바로 그 자격 증명을 검색할 수 있습니다.</li>
</ul>
<p>리소스 이름을 전체적으로 저장해 놓은 앱에서 사용자의 자격 증명을 찾았을 때 자동으로 사용자가 로그인할 수 있는 예시를 살펴보겠습니다. 한 사용자당 여러 개의 자격 증명이 있을 경우 우리는 사용자에게 로그인 시 기본으로 사용할 자격 증명을 선택하도록 요청하고 있습니다.</p>
<p><strong>C#</strong></p>
<blockquote>
<div>
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">string</span> resourceName = <span style="color: #006080;">"My App"</span>;<br /><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">string</span> defaultUserName;<br /><br /><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> Login()<br />{<br />    var loginCredential = GetCredentialFromLocker();<br /><br />    <span style="color: #0000ff;">if</span> (loginCredential != <span style="color: #0000ff;">null</span>)<br />    {<br />        <span style="color: #008000;">// There is a credential stored in the locker.</span><br />        <span style="color: #008000;">// Populate the Password property of the credential</span><br />        <span style="color: #008000;">// for automatic login.</span><br />        loginCredential.RetrievePassword();<br />    }<br />    <span style="color: #0000ff;">else</span><br />    {<br />        <span style="color: #008000;">// There is no credential stored in the locker.</span><br />        <span style="color: #008000;">// Display UI to get user credentials.</span><br />        loginCredential = GetLoginCredentialUI();<br />    }<br /><br />    <span style="color: #008000;">// Log the user in.</span><br />    ServerLogin(loginCredential.UserName, loginCredential.Password);<br />}<br /><br /><br /><span style="color: #0000ff;">private</span> Windows.Security.Credentials.PasswordCredential GetCredentialFromLocker()<br />{<br />    Windows.Security.Credentials.PasswordCredential credential = <span style="color: #0000ff;">null</span>;<br /><br />    var vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />    var credentialList = vault.FindAllByResource(resourceName);<br />    <span style="color: #0000ff;">if</span> (credentialList.Count &gt; 0)<br />    {<br />        <span style="color: #0000ff;">if</span> (credentialList.Count == 1)<br />        {<br />            credential = credentialList[0];<br />        }<br />        <span style="color: #0000ff;">else</span><br />        {<br />            <span style="color: #008000;">// When there are multiple usernames,</span><br />            <span style="color: #008000;">// retrieve the default username. If one doesn&rsquo;t</span><br />            <span style="color: #008000;">// exist, then display UI to have the user select</span><br />            <span style="color: #008000;">// a default username.</span><br /><br />            defaultUserName = GetDefaultUserNameUI();<br /><br />            credential = vault.Retrieve(resourceName, defaultUserName);<br />        }<br />    }<br /><br />    <span style="color: #0000ff;">return</span> credential;<br />}</pre>
</div>
</blockquote>
<div><br /><strong>JavaScript</strong></div>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> resourceName = <span style="color: #006080;">"My App"</span>;<br /><span style="color: #0000ff;">var</span> defaultUserName;<br /><br /><span style="color: #0000ff;">function</span> login() {<br />    <span style="color: #0000ff;">var</span> loginCredential = getCredentialFromLocker();<br /><br />    <span style="color: #0000ff;">if</span> (loginCredential != <span style="color: #0000ff;">null</span>) {<br />        <span style="color: #008000;">// There is a credential stored in the locker.</span><br />        <span style="color: #008000;">// Populate the Password property of the credential</span><br />        <span style="color: #008000;">// for automatic login.</span><br />        loginCredential.retrievePassword();<br />    } <span style="color: #0000ff;">else</span> {<br />        <span style="color: #008000;">// There is no credential stored in the locker.</span><br />        <span style="color: #008000;">// Display UI to get user credentials.</span><br />        loginCredential = getLoginCredentialUI();<br />    }<br /><br />    <span style="color: #008000;">// Log the user in.</span><br />    serverLogin(loginCredential.userName, loginCredential.password);<br />}<br /><br /><br /><span style="color: #0000ff;">function</span> GetCredentialFromLocker() {<br />    <span style="color: #0000ff;">var</span> credential = <span style="color: #0000ff;">null</span>;<br /><br />    <span style="color: #0000ff;">var</span> vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />    <span style="color: #0000ff;">var</span> credentialList = vault.findAllByResource(resourceName);<br />    <span style="color: #0000ff;">if</span> (credentialList.length &gt; 0) {<br />        <span style="color: #0000ff;">if</span> (credentialList.length == 1) {<br />            credential = credentialList[0];<br />        } <span style="color: #0000ff;">else</span> {<br />            <span style="color: #008000;">// When there are multiple usernames,</span><br />            <span style="color: #008000;">// retrieve the default username. If one doesn&rsquo;t</span><br />            <span style="color: #008000;">// exist, display UI to have the user select</span><br />            <span style="color: #008000;">// a default username.</span><br /><br />            defaultUserName = getDefaultUserNameUI();<br />            credential = vault.retrieve(resourceName, defaultUserName);<br />        }<br />    }<br /><br />    <span style="color: #0000ff;">return</span> credential;<br />}</pre>
</div>
</blockquote>
<h2>사용자 자격 증명 삭제</h2>
<p>사용자 자격 증명을 자격 증명 보관에서 삭제하는 것 역시 두 단계로 아주 간단합니다. 마찬가지로 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.aspx" target="_blank">Windows.Security.Credentials</a> 네임스페이스에서 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.passwordvault.aspx" target="_blank">PasswordVault</a> 개체를 사용하여 자격 증명 보관에 대한 참조를 가져옵니다. 그리고 삭제하려는 자격 증명을 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh701242.aspx" target="_blank">PasswordVault.Remove()</a> 메서드로 보냅니다.</p>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;">var vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />vault.Remove(<span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordCredential(<br />    <span style="color: #006080;">"My App"</span>, username, password));</pre>
</div>
</blockquote>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />vault.remove(<span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordCredential(<br />    <span style="color: #006080;">"My App"</span>, username, password));</pre>
</div>
</blockquote>
<h2>간편하고 안전함</h2>
<p>앞서 살펴보았듯이 자격 증명 보관은 사용자 인증 작업은 물론, 이후에도 지속적으로 편리하게 로그인할 수 있도록 사용자 자격 증명을 저장하는 작업까지 간단하게 할 수 있는 손쉬운 기능입니다. 그리고 이 모든 과정이 아주 안전한 방식으로 이루어집니다.</p>
<p>Windows 8 앱 인증과 관련한 다른 주제로, OAuth와 같은 웹 사이트에서 인증 토큰을 검색하고 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh770853.aspx" target="_blank">사용자의 Microsoft 계정을 기반으로 앱을 개인 설정</a>할 수 있는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465281.aspx" target="_blank">웹 인증 브로커</a>도 있습니다.</p>
<p>기타 자세한 내용은 다음을 참조하십시오.</p>
<ul>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211367.aspx" target="_blank">사용자 정보 관리(JavaScript)</a></li>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/xaml/BR229572(v=win.10).aspx" target="_blank">사용자 정보 관리(C#/VB/C++ 및 XAML을 사용한 Windows 스토어 앱)</a></li>
<li><a href="http://code.msdn.microsoft.com/windowsapps/PasswordVault-f01be74a" target="_blank">자격 증명 보관 샘플</a></li>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.security.credentials.aspx" target="_blank">Windows.Security.Credentials</a></li>
</ul>
<p>&nbsp;</p>
<p>- Windows 개발자 콘텐츠, 수석 콘텐츠 개발자, Doug Rothaus</p>
<p>이 블로그 글을 작성하는 데 도움을 준 Yashar Bahman에게 특별히 감사드립니다.</p>
<!-- Title: “자격 증명 보관: Windows 스토어 앱의 사용자 이름과 암호 처리를 위한 솔루션” Tags: “자격 증명 검색, 자격 증명 저장, 안전한 저장소, 자격 증명 보관” --></div>]]></content:encoded>
    </item>
    <item>
      <title>지리적 위치 및 Bing 지도를 사용하는 위치 인식 앱 만들기</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/bing</link>
      <pubDate>Tue, 28 May 2013 11:04:55 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/05/28/bing/</guid>
      <description><![CDATA[여러분이 개발한 앱의 비즈니스 논리와 사용자 위치 정보가 연동되면 여러분은 앱 사용자들에게 그들의 현재 위치에 맞는 날씨, 쿠폰, 스포츠 행사 또는 콘서트 목록을 간편하게 제공할...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>여러분이 개발한 앱의 비즈니스 논리와 사용자 위치 정보가 연동되면 여러분은 앱 사용자들에게 그들의 현재 위치에 맞는 날씨, 쿠폰, 스포츠 행사 또는 콘서트 목록을 간편하게 제공할 수 있습니다. 여러분은 Windows 8의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br229921.aspx" target="_blank">지리적 위치</a> 기능과 Bing Maps SDK를 사용하여 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465148.aspx" target="_blank">위치 인식 Windows 스토어 앱</a>을 만들 수 있습니다.</p>  <p>여러분의 앱에서 IP 주소, WiFi 네트워크 또는 GPS 데이터를 가지고 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br225603.aspx" target="_blank">위치 서비스</a>를 통해 사용자에게 위치에 따른 특정 정보를 제공하는 방법을 알아보겠습니다. 위치 정보가 있다면 Bing Maps SDK를 사용해서 사용자가 자신의 위치를 확인하도록 만들 수 있습니다. </p>  <h2>위치 검색</h2>  <p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.devices.geolocation.aspx">Windows.Devices.Geolocation</a> 네임스페이스로 사용자 위치를 검색할 수 있습니다. 사용자 위치를 검색하기 위해 GPS, WiFi 네트워크, IP 주소 중 어떤 것을 사용할지는 다음과 같은 두 가지 주요 요인에 따라 달라집니다.</p>  <ol>   <li>요구되는 정확도 수준: 여러분의 앱에서 필요로 하는 기준을 충족하는 이상적인 정확도입니다. </li>    <li>위치 데이터 사용 가능성: 모든 장치에 GPS가 내장되어 있지는 않으며 위치 정보를 요청할 때 네트워크 또는 Wi-Fi 연결을 사용하지 못할 수도 있습니다.</li> </ol>  <h3><a>원하는 정확도</a></h3>  <p>위치 정보를 요청하는 경우, 사용자의 앱에서 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.devices.geolocation.geolocator.desiredaccuracy.aspx" target="_blank">원하는 정확도</a>를 지정하면 지정된 범위 내에서 위치 정보가 신속하게 반환됩니다. 위치 정보 유형에 따라 정확도도 다릅니다.</p>  <ul>   <li><a href="http://ko.wikipedia.org/wiki/GPS" target="_blank">GPS</a> – GPS의 오차 범위는 10m 이내로 정확도는 가장 높지만 GPS 센서를 '켜고' 위성 데이터를 수신 및 삼각 측량하여 자세한 위치 정보를 반환하려면 1~2분 정도의 시간이 걸릴 수 있습니다. 하지만 GPS의 위성 연결이 이루어지고 나면 이후의 위치 정보는 더 빨리 수신할 수 있습니다. 물론 GPS를 많이 사용할수록 전력 소비는 늘어나며 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.devices.geolocation.geolocator.desiredaccuracy.aspx" target="_blank">Geolocator.DesiredAccuracy</a> 속성을 High로 설정해야 GPS에서 정보를 수신할 수 있습니다.</li>    <li><a href="http://ko.wikipedia.org/wiki/Wi-Fi" target="_blank">Wi-Fi</a> – Wi-Fi 연결의 오차 범위는 300m~500m입니다. 연결이 유지되는 동안은 GPS가 위성에 연결해서 데이터를 수신하는 것보다 더 빠르게 위치 데이터를 반환할 수 있습니다. Wi-Fi를 통해 위치 정보를 수신하려면 DesiredAccuracy 속성을 Default로 설정해야 합니다.</li>    <li><a href="http://ko.wikipedia.org/wiki/IP_%EC%A3%BC%EC%86%8C" target="_blank">IP 주소</a> – IP의 오차 범위는 1km 이하입니다. 네트워크 연결이 지속되는 동안에는 GPS가 위성에 연결해서 데이터를 수신하는 것보다 더 빠르게 위치 데이터를 반환할 수 있습니다. IP를 통해 위치 정보를 수신하려는 경우에도 DesiredAccuracy 속성을 Default로 설정합니다. 위치 서비스는 반환할 수 있는 최고의 정확도로 반환합니다. 반환된 정확도의 수준은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.devices.geolocation.geocoordinate.accuracy.aspx" target="_blank">Geocoordinate.Accuracy</a> 속성에서 확인할 수 있습니다.</li> </ul>  <p>앱의 성능과 전력 소비에 영향을 줄 수 있으니 원하는 정확도 값을 신중하게 생각하여 정하십시오. 예를 들어, 여러분의 앱이 날씨 예보 서비스를 제공하는 앱이라면 GPS 수준의 높은 정확도는 필요하지 않습니다. 또한 반환된 데이터의 정확도를 기반으로 오류 반경을 표시함으로써 사용자 데이터의 정확도를 나타내야 합니다. 아래는 여러분이 원하는 정확도를 설정할 수 있는 C# 코드입니다.</p>  <p><strong>C#</strong></p>  <blockquote>   <div id="codeSnippetWrapper">     <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">Geolocator geolocator = <span style="color: rgb(0, 0, 255);">new</span> Geolocator();<br />geolocator.DesiredAccuracy = Windows.Devices.Geolocation.PositionAccuracy.High;<br /></pre>
  </div>
</blockquote>

<br />원하는 정확도 수준과 상관없이 위치 정보를 가져오는 호출 코드는 다음과 같습니다. 

<p><strong>C#</strong></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">Geoposition pos = await geolocator.GetGeopositionAsync().AsTask(token);</pre>
  </div>
</blockquote>

<p>Geolocator 클래스의 인스턴스를 만들고 원하는 정확도를 설정하여 현재 위치를 수신할 수 있는 JavaScript 코드입니다. 아래의 'promise' 변수는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211867.aspx" target="_blank">WinJS.Promise</a> 개체라는 것을 유의하십시오. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh701079.aspx" target="_blank">done</a> 메서드를 사용해서 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br225537.aspx" target="_blank">getGeopositionAsync</a>로 호출 데이터를 액세스합니다.</p>

<p><strong>JavaScript</strong></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> geolocator = Windows.Devices.Geolocation.Geolocator();<br />geolocator.desiredAccuracy =<br />Windows.Devices.Geolocation.PositionAccuracy.high;<br /><br /><span style="color: rgb(0, 128, 0);">// Get the geoposition, capturing the request in a 'promise' object.</span><br /><span style="color: rgb(0, 0, 255);">var</span> promise = geolocator.getGeopositionAsync();<br /></pre>
  </div>
</blockquote>

<br />원하는 정확도를 High로 설정하더라도 사용자 장치에서 반환하는 데이터의 정확도는 높지 않을 수도 있습니다. 사용자의 PC에 GPS가 없거나 꺼져있을 수도 있고 Wi-Fi에 연결이 되어 있지 않을 수도 있기 때문입니다. 정확도가 낮은 데이터의 인스턴스도 정상적으로 처리할 수 있도록 항상 준비하시기 바랍니다. 앱의 기능에 따라 처리 방법이 달라지는데, 더 낮은 정확도를 유지할 수도 있고 GPS 수준의 정확도가 필요할 수도 있습니다. 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465148.aspx" target="_blank">위치 인식 앱에 대한 지침</a>을 참조하십시오. 

<h3>Bing 지도</h3>

<p><a href="http://apps.microsoft.com/webpdp/app/97a2179c-38be-45a3-933e-0d2dbf14a142" target="_blank">Bing 지도 앱</a>은 사용 가능한 위치 데이터의 정확도를 조정하는 앱의 좋은 예입니다. 일반적으로 GPS나 Wi-Fi를 사용하지 않는 데스크톱 PC인 경우에는 IP 주소를 기반으로 위치를 찾고 <a>지도에서 현재 위치를 </a>지역 수준으로 확대해서 표시합니다. 일반적으로 무선 LAN 카드가 설치되어 있는 랩톱의 경우에는 Wi-Fi를 사용해서 보다 정확한 위치를 찾고 더 자세한 수준으로 확대해서 표시합니다. 사용자가 그보다 더 자세한 수준으로 확대하려고 하면 현재 위치 아이콘에서 오류 반경이 표시됩니다. GPS가 탑재된 장치의 경우에는 Bing 지도에서 GPS를 통해 정확도가 높은 위치 데이터를 수신하여 더 자세한 위치를 표시합니다.</p>

<h3>Bing 날씨</h3>

<p>위에서 언급했듯이 <a href="http://apps.microsoft.com/webpdp/app/421ba874-f903-4965-9b82-d60f3ba3cae0" target="_blank">Bing 날씨 앱</a> 같은 앱에는 지역 수준의 정보만 제공해도 되기 때문에 신속하게 위치 값을 반환할 수 있도록 필요한 정확도를 낮게 지정할 수 있습니다. 이러한 설정으로 앱은 사용자에게 더욱 빠르게 반응할 수 있습니다. 이와 같이 지역 수준의 위치 정보만 제공해도 되는 앱으로는 라디오 방송국 가이드, 특가 상품 쇼핑, 지역 뉴스 피드 등의 앱이 있습니다.</p>

<h2>Bing Maps SDK로 위치 데이터 표시</h2>

<p>사용자의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.devices.geolocation.geolocator.getgeopositionasync.aspx" target="_blank">GetGeopositionAsync</a>호출에서 얻은 위치 정보를 지도상에서 확인할 수 있습니다.</p>

<ul>
  <li>우선 다음 코드를 사용해서 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.devices.geolocation.geoposition.aspx" target="_blank">Geoposition</a>을 Bing 지도에서 사용할 수 있는 <a href="http://msdn.microsoft.com/ko-kr/library/hh846500.aspx" target="_blank">Location</a>으로 변환합니다.</li>
</ul>

<p> </p>

<p><strong>C#</strong></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">Location location = <span style="color: rgb(0, 0, 255);">new</span> Location(pos.Coordinate.Latitude, pos.Coordinate.Longitude);</pre>
  </div>
</blockquote>

<p><strong>JavaScript:</strong></p>

<blockquote>
  <div>
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">promise.done(<br />  <span style="color: rgb(0, 0, 255);">function</span> (pos) {<br />    <span style="color: rgb(0, 128, 0);">// Get the coordinates of the current location.</span><br />    <span style="color: rgb(0, 0, 255);">var</span> coord = pos.coordinate,<br />    location = <span style="color: rgb(0, 0, 255);">new</span> Microsoft.Maps.Location(coord.latitude, coord.longitude);<br /> <br />  },<br />  <span style="color: rgb(0, 0, 255);">function</span> (err) {<br />    <span style="color: rgb(0, 128, 0);">// Handle the error.</span><br />  });<br /></pre>
  </div>
</blockquote>

<p> </p>

<ul>
  <li>그리고 Bing 지도를 사용하여 사용자의 위치를 표시합니다. Bing Maps SDK는 <a href="http://visualstudiogallery.msdn.microsoft.com/bb764f67-6b2c-4e14-b2d3-17477ae1eaca">여기</a>에서 다운로드할 수 있습니다. Bing 지도 키는 Bing 지도 포털에 등록하고 <a href="http://msdn.microsoft.com/ko-kr/library/ff428642.aspx">Bing 지도 키 얻기</a>의 지침에 따라 얻을 수 있습니다. 등록할 때는 키 유형별 사용 제한사항을 확인하고 여러분의 앱에 적합한 키를 고르시기 바랍니다. 

    <br />디자이너에 Bing 지도 컨트롤을 추가하고 자격 증명에 해당 키를 삽입하십시오.</li>
</ul>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">Page</span><br />    <span style="color: rgb(255, 0, 0);">xmlns</span><span style="color: rgb(0, 0, 255);">=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:x</span><span style="color: rgb(0, 0, 255);">=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:local</span><span style="color: rgb(0, 0, 255);">=&quot;using:SimpleMapping&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:d</span><span style="color: rgb(0, 0, 255);">=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:mc</span><span style="color: rgb(0, 0, 255);">=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:Maps</span><span style="color: rgb(0, 0, 255);">=&quot;using:Bing.Maps&quot;</span><br />    <span style="color: rgb(255, 0, 0);">x:Class</span><span style="color: rgb(0, 0, 255);">=&quot;SimpleMapping.MainPage&quot;</span><br />    <span style="color: rgb(255, 0, 0);">mc:Ignorable</span><span style="color: rgb(0, 0, 255);">=&quot;d&quot;</span><span style="color: rgb(0, 0, 255);">&gt;</span><br /><br />    <span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">Grid</span> <span style="color: rgb(255, 0, 0);">Background</span><span style="color: rgb(0, 0, 255);">=&quot;{StaticResource ApplicationPageBackgroundThemeBrush}&quot;</span><span style="color: rgb(0, 0, 255);">&gt;</span><br />        <span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">Maps:Map</span> <span style="color: rgb(255, 0, 0);">x:Name</span><span style="color: rgb(0, 0, 255);">=&quot;Map&quot;</span> <span style="color: rgb(255, 0, 0);">Margin</span><span style="color: rgb(0, 0, 255);">=&quot;0,120,0,0&quot;</span> <span style="color: rgb(255, 0, 0);">Credentials</span><span style="color: rgb(0, 0, 255);">=&quot;Insert Your Bing Maps Key Here&quot;</span><span style="color: rgb(0, 0, 255);">/&gt;</span><br />    <span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">Grid</span><span style="color: rgb(0, 0, 255);">&gt;</span><br /><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">Page</span><span style="color: rgb(0, 0, 255);">&gt;</span></pre>
  </div>
</blockquote>

<ul>
  <li>이제 여러분의 앱에서 지도가 표시될 페이지에 해당 코드를 적용할 수 있습니다. 해당 코드는 지도의 확대 수준을 설정하고 위치를 표시합니다. 확대 수준에 대한 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/jj850315.aspx" target="_blank">Bing 지도에 위치를 표시하는 방법</a>을 참조하시기 바랍니다.</li>
</ul>

<p><strong>C#</strong></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">double</span> zoomLevel = 13.0f;<br /><br />Map.SetView(location, zoomLevel);<br /></pre>
  </div>
</blockquote>

<br />JavaScript 및 HTML로 작성된 여러분의 앱에 간단하게 Bing 지도 컨트롤을 추가할 수 있습니다. 해당 앱의 HTML에 다음 &lt;script&gt; 태그를 추가하면 됩니다. 

<div> </div>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">script</span> <span style="color: rgb(255, 0, 0);">type</span><span style="color: rgb(0, 0, 255);">=&quot;text/javascript&quot;</span> <br />   <span style="color: rgb(255, 0, 0);">src</span><span style="color: rgb(0, 0, 255);">=&quot;ms-appx:///Bing.Maps.JavaScript//js/veapicore.js&quot;</span><span style="color: rgb(0, 0, 255);">&gt;</span><br />/script<span style="color: rgb(0, 0, 255);">&gt;</span><br /></pre>
  </div>
</blockquote>

<p>그리고 액세스할 수 있게 ID 특성 값을 설정하여 지도가 들어가도록 &lt;div&gt; 태그를 만듭니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">div</span> <span style="color: rgb(255, 0, 0);">id</span>=’<span style="color: rgb(255, 0, 0);">map-div</span>’<span style="color: rgb(0, 0, 255);">&gt;&lt;/</span><span style="color: rgb(128, 0, 0);">div</span><span style="color: rgb(0, 0, 255);">&gt;</span></pre>
  </div>
</blockquote>

<p>마지막으로 HTML 페이지의 지도 컨트롤을 초기화합니다. 다음은 이에 해당되는 JavaScript의 코드입니다.</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 128, 0);">// Load the Microsoft.Maps.Map module and then call getMap when the </span><br /><span style="color: rgb(0, 128, 0);">// module has loaded.</span><br /><span style="color: rgb(0, 0, 255);">function</span> initMap() {<br />  Microsoft.Maps.loadModule(<span style="color: rgb(0, 96, 128);">'Microsoft.Maps.Map'</span>, <br />    { <br />      callback: getMap, <br />      culture: <span style="color: rgb(0, 96, 128);">&quot;en&quot;</span>, <br />      homeRegion: <span style="color: rgb(0, 96, 128);">&quot;US&quot;</span> <br />  });<br />}<br /><br /><span style="color: rgb(0, 128, 0);">// Get the div to contain the map from the interface of the app.</span><br /><span style="color: rgb(0, 0, 255);">function</span> getMap() {<br /><br />  <span style="color: rgb(0, 128, 0);">// Create a new instance of the Map class, passing in the app ID.</span><br />  <span style="color: rgb(0, 0, 255);">var</span> map = <span style="color: rgb(0, 0, 255);">new</span> Microsoft.Maps.Map(<br />    document.getElementById(mapDivId), <br />    { credentials: “Insert your credentials here” });<br /><br />  <span style="color: rgb(0, 128, 0);">// Set the initial zoom level and type of map.</span><br />  map.setView({<br />    mapTypeId: Microsoft.Maps.MapTypeId.road,<br />    zoom: 3<br />  });<br />}<br /></pre>
  </div>
</blockquote>

<ul>
  <li>마지막으로, 위치 정보를 사용하는 앱에서는 사용자 권한 설정을 준수하고 언제든지 사용자 권한 설정을 변경할 수 있어야 합니다. 예를 들어, 날씨 앱에서는 현재 위치의 날씨 정보를 얻기 위해 날씨 정보를 활용하지만 언제든지 사용자가 설정에서 위치 권한을 취소할 수도 있습니다. 앱에서 위치 권한을 사용할 수 없는 경우에 대해서도 항상 처리할 수 있어야 합니다. 이와 같은 경우에, 날씨 앱에서는 "사용자의 위치를 찾을 수 없습니다. 날씨 앱에서 사용자의 위치를 찾을 수 있게 권한을 변경해 주십시오."라는 메시지가 나타납니다. 위치 권한 처리 관련 예제는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/Hh465148(v=win.10).aspx" target="_blank">위치 인식 앱에 대한 지침</a>의 사용자 환경 지침을 참조하십시오. 

    <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0882.bing-weather_2FEEBCEF.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0882.bing_2D00_weather_5F00_2FEEBCEF.jpg"><img title="Bing 날씨 앱" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="Bing 날씨 앱에서는 현재 지역의 날씨 정보를 얻기 위해 위치 정보를 사용합니다." src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/7356.bing-weather_thumb_084840C5.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/7356.bing_2D00_weather_5F00_thumb_5F00_084840C5.jpg" width="600" height="450" /></a></li>
</ul>

<p> </p>

<p>지리적 위치 및 Bing Maps SDK 사용에 대한 자세한 내용은 <a></a><a>새롭게 선보이는 </a><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/jj850315.aspx" target="_blank">동영상과 How to 항목</a>, 그리고 사용자 위치를 비동기 방식으로 얻고 Bing Maps SDK를 통해 시각적으로 표시하는 방법을 단계적으로 안내하는 <a href="http://code.msdn.microsoft.com/windowsapps/Simple-geolocation-with-7b385c3c" target="_blank">코드 샘플</a>을 참조하십시오. 동영상, How to 항목, 샘플 가이드에서 위치 기능 활성화, 비동기 방식으로 위치 수신, 특정 지역으로 지도 설정, 위치 정보 정확도 기반 위치 아이콘 설정 등의 방법을 확인할 수 있습니다. <a href="http://msdn.microsoft.com/ko-kr/library/hh846481.aspx" target="_blank">Windows 스토어 앱용 Bing 지도</a>에서도 자세한 내용을 확인할 수 있습니다.</p>

<h2>결론</h2>

<p>위치 기능은 강력하고도 시간 절약에 도움이 되는 기능으로 여러분의 앱에 추가할 만합니다. 여러분은 사용자에게 그들이 있는 위치에 대해 보다 적합한 정보를 제공할 수 있으며, 지역 라디오 방송국 목록, 특가 상품 쇼핑, 교통 정보, 날씨 정보 등 어떤 것이든 상관없이 위치에 따른 맞춤형 정보를 제공할 수 있으므로 여러분의 앱이 더욱 유용해집니다. 앱을 굳이 조작하지 않아도 자동으로 맞춤형 정보를 제공하는 이 기능은 시간을 절약해 주기 때문에 사용자들이 여러분의 앱을 더욱 더 선호하게 될 것입니다.</p>

<p>- Windows 개발자 콘텐츠, 수석 콘텐츠 개발자, Rose Heise</p>
<p>   특별히 이 블로그 글을 작성하는 데 도움을 준 Jon Kay와 Eric Schmidt에게 특별히 감사드립니다.</p>
<!-- Title: “지리적 위치 및 Bing 지도를 사용하는 위치 인식 앱 만들기” Tags: “Bing 지도, 지리적 위치, Windows. 장치. 지리적 위치, 위치 서비스” --></div>]]></content:encoded>
    </item>
    <item>
      <title>Windows 개발자 센터 경험 소개</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/windows</link>
      <pubDate>Thu, 16 May 2013 09:55:00 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/05/16/windows/</guid>
      <description><![CDATA[저희는 2011년 10월 Windows 개발자 센터를 시작한 이후로 전 세계의 개발자, 디자이너들과 함께 긴밀히 작업하면서 앱 작성자들에게 지침을 제공하고, 영감을 불어넣으며,...]]></description>
      <content:encoded><![CDATA[<div class="PostContent">
<p>저희는 2011년 10월 <a href="http://msdn.microsoft.com/ko-kr/windows/default.aspx" target="_blank">Windows 개발자 센터</a>를 시작한 이후로 전 세계의 개발자, 디자이너들과 함께 긴밀히 작업하면서 앱 작성자들에게 지침을 제공하고, 영감을 불어넣으며, 샘플을 제공할 수 있는 최선의 방법에 대해 논의했습니다. 수백만 명의 방문자들과 여러분의 의견을 토대로 어떤 것이 잘 되고 있고 어떤 경험을 개선할 수 있는지를 평가하고 있습니다.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0172.windows_dev_center_1EC86298.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0172.windows_5F00_dev_5F00_center_5F00_1EC86298.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="Windows 개발자 센터" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0184.windows_dev_center_thumb_1DEFFCAE.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0184.windows_5F00_dev_5F00_center_5F00_thumb_5F00_1DEFFCAE.png" alt="Windows 개발자 센터" width="600" height="338" border="0" /></a></p>
<h2>새로운 소식</h2>
<p>Windows 팀은 생생하고 풍부한 앱 에코시스템 개발에 심혈을 기울이고 있습니다. 이러한 노력은 훌륭한 앱, 최고의 경제성, 풍부한 기회 제공이라는 세 가지 모습으로 나타납니다. 새로운 개발자 센터는 Windows를 통해 훌륭한 앱을 개발하고 비즈니스를 구축하는 데 필요한 리소스를 앱 작성자, 디자이너, 비즈니스에 제공합니다.</p>
<p>개발자 센터는 이를 지원하기 위해 다음과 같은 영역에서 발전을 거듭하고 있습니다.</p>
<ol>
<li>탐색 모델이 앱 개발 수명 주기에서 현재 작업이 진행되고 있는 부분에 필요한 콘텐츠를 찾을 수 있도록 변경되었습니다.</li>
<li>사이트 디자인에서 최상위 작업, 시나리오 및 새 콘텐츠를 강조합니다.</li>
</ol>
<p>개발자가 원하는 콘텐츠, 샘플, 다운로드뿐만 아니라 디자이너와 비즈니스에 필요한 리소스에 손쉽게 액세스할 수 있게 되었습니다.</p>
<p>어떤 앱을 작성하느냐에 따라 <a href="http://msdn.microsoft.com/ko-KR/windows/apps/br229512" target="_blank">Windows 스토어 앱</a>, <a href="http://msdn.microsoft.com/ko-KR/ie/" target="_blank">Internet Explorer</a>, <a href="http://msdn.microsoft.com/ko-KR/windows/desktop" target="_blank">Windows 데스크톱</a> 및 <a href="http://msdn.microsoft.com/ko-KR/windows/hardware" target="_blank">하드웨어</a> 등 네 가지 영역 중 탐색할 영역이 결정됩니다. 각 영역에서 여러분은 위에서 언급한 사항이 반영된 새로운 경험을 할 수 있습니다. 이 글에서는 Windows 스토어 앱에서 이루어진 특정 변경 사항에 대해 자세히 살펴보겠습니다.</p>
<h2>역할에 적합한 콘텐츠</h2>
<p>정보가 방대한 웹 사이트라도 사용자가 원하는 정보를 찾지 못하면 유용하다고 할 수 없습니다. 수백, 수천 가지 항목을 제공하는 개발자 센터에서 콘텐츠 구성 및 검색 기능은 매우 중요한 요소입니다. Windows 스토어 앱 개발의 경우 디자이너와 비즈니스를 더욱 효율적으로 지원할 수 있도록 하기 위해 개발을 시작하는 데 필요한 모든 것들을 한곳에 모아 놓는 것부터 역할 및 시나리오별로 세밀하게 집중해야 합니다. 다음은 주요 변경 사항입니다.</p>
<h3>개발자</h3>
<p>훌륭한 Windows 스토어 앱을 만드는 데 필요한 콘텐츠를 간소화했습니다. <a href="http://msdn.microsoft.com/ko-KR/windows/apps/" target="_blank">Windows 스토어 앱 홈 페이지</a>로 이동하면 다음 항목을 확인할 수 있습니다.</p>
<ul>
<li>필요한 콘텐츠로 바로 이동할 수 있도록 "주요 작업 및 콘텐츠"를 배치했습니다. 사용자의 지역에 해당하는 지역 이벤트와 사이트에 방금 추가된 새로운 리소스가 강조 표시됩니다.</li>
<li>코딩을 바로 시작할 수 있는 다운로드 및 시작하는 데 필요한 모든 리소스, 앱을 Windows 스토어에 배치하는 데 도움이 되는 자세한 지침이 제공됩니다.</li>
<li>중요한 코드 샘플이 강조 표시되며 개발 주기에 대해 한눈에 쉽게 파악할 수 있는 방법 지침이 제공됩니다.</li>
<li>사이트 전체에서 제공되는 "<a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211360.aspx" target="_blank">One Dev Minute</a>" 비디오는 앱에 기능을 추가하는 방법에 대해서 간략하게 설명합니다.</li>
</ul>
<h3>디자이너</h3>
<p>더욱 다양한 콘텐츠를 제공하며 이 콘텐츠를 쉽게 사용할 수 있도록 사이트의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh779072.aspx" target="_blank">디자인 섹션</a>을 점검했습니다. 이 사이트에서 유용한 다운로드와 다시 사용할 수 있는 디자인 자산을 더욱 손쉽게 찾아볼 수 있으며, 디자인 영감 및 사례 연구에서는 Windows에 대한 고유한 경험을 만드는 데 필요한 도구를 제공합니다. 터치 및 사용이 간편하게 페이지를 디자인하여 시선을 사로잡는 앱을 만드는 데 필요한 모든 것을 바로 얻을 수 있습니다.</p>
<h3>비즈니스</h3>
<p>Windows 스토어에서는 경쟁력 있는 플랫폼에서 개선된 경제적 모델과 여러분이 원하는 비즈니스를 구축할 수 있는 유연성을 갖춘 중요한 비즈니스 기회를 제공합니다. 사이트의 새로운 <a href="http://go.microsoft.com/fwlink/?LinkID=285998" target="_blank">시장 섹션</a>에서는 앱을 마케팅하고, 원격 분석 데이터를 평가하고, 앱 인증에 통과하는 데 도움이 되는 훌륭한 팁을 제공합니다. 더욱 쉽게 인증을 받을 수 있는 다양한 방법을 제공하고 있으며 이를 위한 프로세스를 모두 소화해 낼 수 있도록 콘텐츠를 개선했습니다.</p>
<p>여러 가지 역할을 동시에 수행하는 분들에게도 새로운 사이트 구성이 도움이 될 것으로 기대합니다. 새롭게 구성된 사이트를 통해 취미로 앱을 만드는 분에서 광고 회사, 대기업 소프트웨어 개발자들에 이르는 많은 분들이 Windows를 통해 창의적인 사업 비전을 성취할 수 있기를 희망합니다.</p>
<h2>여러분의 참여가 더 나은 결과를 만듭니다.</h2>
<p><a href="http://www.buildwindows.com">BUILD 2013</a>이 출시되고 한 달이 조금 넘은 시점에서 논의해야 할 것들이 많습니다. 올해 이벤트에서 많은 항목을 공유할 수 있도록 새로운 콘텐츠를 열심히 만들고 있습니다. 이 블로그 글을 읽는 것 외에도 Twitter에서 <a href="https://twitter.com/#!/windevs" target="_blank">@windevs</a>를 팔로우하고 <a href="http://www.windowsstore.com/newsletter-signup" target="_blank">Windows 스토어 뉴스레터</a>를 구독하는 등 여러 가지 방법으로 최근 소식을 접할 수 있습니다.</p>
<p>저희는 Windows 스토어 앱을 디자인하고, 개발하고, 마케팅하는 데 도움이 되는 다양한 콘텐츠를 제공하고 있습니다. 이런 콘텐츠를 통해 여러분이 좀 더 나은 개발자 센터 경험을 할 수 있게 되었으면 합니다. 여러분의 의견을 언제든 공유할 수 있도록 모든 페이지 하단에 새로운 피드백 옵션 두 가지를 제공하고 있습니다. 여러분의 의견을 보내 주세요. 여러분이 고민하는 사항을 해결할 수 있도록 항상 노력하겠습니다.</p>
<p>&nbsp;</p>
<p>Windows 개발자 콘텐츠, 콘텐츠 게시 관리자, Keith Boyd</p>
<!-- Title: “Windows 개발자 센터 경험 소개” Tags: “개발자 센터, 개발자 정보, 앱 작성, Windows 스토어 앱, 빌드, Windows 개발자 센터” --></div>]]></content:encoded>
    </item>
    <item>
      <title>대상 앱과 공유</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/83</link>
      <pubDate>Wed, 15 May 2013 09:13:00 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/05/15/83/</guid>
      <description><![CDATA[앱을 통한 공유에서는 Food with Friends 앱을 예로 들어 이 앱에서 여러 대상 앱으로 콘텐츠를 공유하여 풍부한 전체 시나리오를 수행할 수 있는 아이디어를...]]></description>
      <content:encoded><![CDATA[<div class="PostContent">
<p><a href="http://blogs.msdn.com/b/windowsappdev_ko/archive/2012/12/21/10380197.aspx" target="_blank">앱을 통한 공유</a>에서는 <a href="http://blogs.msdn.com/b/windowsappdev_ko/archive/2012/07/17/metro-from-the-crowd.aspx" target="_blank">Food with Friends</a> 앱을 예로 들어 이 앱에서 여러 대상 앱으로 콘텐츠를 공유하여 풍부한 전체 시나리오를 수행할 수 있는 아이디어를 소개했습니다. Windows 8의 앱은 여러분의 고객이 친구나 다른 앱과 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465251.aspx" target="_blank">공유</a>하고 싶어할 만한 흥미로운 콘텐츠를 제공합니다. 흥미롭고 잘 디자인된 공유 대상을 활용하면 여러분의 앱은 고객들이 많이 연결된 소셜 네트워킹의 중심이 될 수 있습니다.</p>
<p>이 글에서는 Food with Friends와 같은 앱에 대상 공유 계약을 사용하는 방법에 대해 알아봅니다. 이 계약을 통해 앱에서 다른 앱의 '콘텐츠를 받고'<em></em> 이 콘텐츠를 사용하여 품질을 높일 수 있습니다. 여러분의 앱이 해당 범주에서 최고의 앱이 될 수 있게 도와드리겠습니다.</p>
<p>대상 앱은 대상 공유 <a href="http://blogs.msdn.com/b/windowsappdev_ko/archive/2012/03/30/windows-8-activating-contracts.aspx" target="_blank">계약</a>을 사용하여 만들 수 있고, 쉽고 빠른 공유 환경을 제공합니다. Windows의 모든 앱이 공유 대상이 될 수 있는 것은 아니지만 자신의 앱에서 다른 앱을 통해 받은 콘텐츠를 게시, 변환, 저장하거나 약간의 가치를 더할 수 있다면 앱을 공유 대상으로 만드는 데 다음과 같은 이점이 있습니다.</p>
<ul>
<li>자신의 앱이 항상 대상 앱 목록에 표시됩니다. 따라서 다른 앱과 즉시 관련을 맺고 연결되어 원하는 데이터를 공유할 수 있습니다.</li>
<li>공유 계약으로 본인의 앱과 다른 원본 앱이 연결되므로 양쪽 앱을 일 대 일로 통합할 필요가 없습니다.</li>
<li>OS에 항상 공유 참 메뉴가 표시되어 사용 가능하므로 대상 앱을 OS에서 확인할 수 있어 앱의 기능 범위가 확대됩니다. 이는 더 많은 고객을 자신의 앱으로 끌어올 수 있는 잠재적인 요소가 됩니다.</li>
</ul>
<h2>공유 콘텐츠 수신</h2>
<p>공유 대상 앱을 통해 흥미로운 경험을 제공할 수 있는 몇 가지 예를 살펴보겠습니다.</p>
<p>Windows 8의 메일 앱과 피플 앱은 이미 익숙할 것입니다. 메일 앱은 콘텐츠를 받아서 여러 친구들과 공유할 수 있는 쉽고도 개인적인 공유 대상 경험을 제공합니다. 피플 앱을 통해서는 소셜 네트워크상의 많은 사용자들과 콘텐츠를 공유할 수 있으며, 소셜 공유 외에도 앱 간 공유를 통해 보다 앞서 나갈 수 있습니다. SkyDrive와 같은 생산성 앱은 Windows상의 어떤 파일이든 받아 클라우드에 저장할 수 있습니다. 메모 앱은 텍스트, 링크 및 다양한 형식의 데이터를 받아 나중에 참조하도록 보관할 수 있으며 창의력도 마음껏 발휘할 수 있습니다.</p>
<p>사진 앱에서 사진을 시각 퍼즐로 바꾸는 게임 앱인 PuzzleTouch로 사진을 공유할 때 공유 흐름이 어떻게 진행되는지 아래 두 그림을 살펴보십시오.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/8637.puzzletouch1_6E4332DA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/8637.puzzletouch1_5F00_6E4332DA.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="왼쪽에는 원본 앱이 있고 오른쪽의 공유 창에는 사진을 받을 수 있는 필터링된 대상 앱의 목록이 있습니다. " src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1307.puzzletouch1_thumb_2681A9F3.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1307.puzzletouch1_5F00_thumb_5F00_2681A9F3.png" alt="왼쪽에는 원본 앱이 있고 오른쪽의 공유 창에는 사진을 받을 수 있는 필터링된 대상 앱의 목록이 있습니다. " width="700" height="394" border="0" /></a></p>
<p align="center">그림 1: 왼쪽에는 원본 앱이 있고 오른쪽의 공유 창에는 사진을 받을 수 있는 필터링된 대상 앱의 목록이 있습니다. <br />PuzzleTouch는 사진 앱에서 사진을 받을 수 있는 공유 대상 중 하나입니다. <em></em></p>
<p align="left"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/6661.puzzletouch2_463083BB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/6661.puzzletouch2_5F00_463083BB.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="오른쪽에 있는 앱 다운로드 알림을 통해 어떤 유형의 퍼즐을 만들지 선택할 수 있습니다." src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/2474.puzzletouch2_thumb_65DF5D83.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/2474.puzzletouch2_5F00_thumb_5F00_65DF5D83.png" alt="오른쪽에 있는 앱 다운로드 알림을 통해 어떤 유형의 퍼즐을 만들지 선택할 수 있습니다." width="700" height="394" border="0" /></a></p>
<p align="center">그림 2: 오른쪽에 있는 앱 다운로드 알림을 통해 어떤 유형의 퍼즐을 만들지 선택할 수 <br />있습니다. 사용자가 앱으로 전환하면 퍼즐이 표시됩니다. <em></em></p>
<h2>공유 대상 앱이란?</h2>
<p>Windows의 전체 공유 환경은 두 가지 요소로 구성됩니다. Windows에서 콘텐츠를 다양한 형식으로 사용할 수 있는 원본 앱이 있고, 또한 특정 형식을 인식하도록 등록되어 있으면서 공유되는 콘텐츠의 대상 역할을 하는 공유 대상 앱이 있습니다. 원본 앱과 공유 대상 앱은 공유 계약을 통해 서로 통신합니다.</p>
<p>앱의 공유 환경을 위해서는 공유 대상 앱이 호스팅된 공유 창에서 렌더링된다는 것을 고려해야 합니다. 사용자가 이러한 보기에서 콘텐츠를 간단히 미리 보고 공유를 확인할 수 있도록 공유 환경을 구축하십시오. 다음 예는 사용자가 브라우저의 링크를 메일 연락처에 있는 사람과 간단히 공유할 수 있는 메일 공유 대상 앱 보기입니다.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/8561.Share-Target-Image_3ED44139.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/8561.Share_2D00_Target_2D00_Image_5F00_3ED44139.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="공유 대상 이미지" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/6011.Share-Target-Image_thumb_4FD86F1C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/6011.Share_2D00_Target_2D00_Image_5F00_thumb_5F00_4FD86F1C.png" alt="공유 대상 이미지" width="700" height="435" border="0" /></a></p>
<p align="center">그림 3: <em></em>메일 대상 앱을 사용하여 사진 앱의 링크 공유</p>
<h2>Food with Friends를 위한 시나리오</h2>
<p>이제 <a href="http://blogs.msdn.com/b/windowsappdev_ko/archive/2012/07/17/metro-from-the-crowd.aspx" target="_blank">Food with Friends</a>에서 어떻게 흥미로운 공유 대상 앱 경험을 만들어 내는지 알아보겠습니다. 이 앱에 익숙한 개발자라면 Food with Friends에서 사용자가 주로 수행하는 작업은 '계획 만들기'<em></em>와 'My Wishlist에 음식점 추가'<em></em>라는 것을 알 것입니다. 물론 후기 쓰기, 의견 올리기, 친구의 Wishlist 보기 등과 같은 다른 작업도 있지만 이러한 작업은 메인 앱의 컨텍스트 내에서 수행하는 것이 적절합니다. Food with Friends 대상 앱을 통해 핵심 시나리오 두 가지를 효과적으로 완료할 수 있는지 확인하기 위해 간단한 무결성 검사를 해보도록 하겠습니다.</p>
<h3>오늘 저녁 계획 만들기</h3>
<p>이것은 메인 앱의 핵심 시나리오이지만 공유 대상 경험을 위한 최상의 시나리오는 아닙니다. 왜냐하면 공유 대상 경험은 쉽고 빨라야 하기 때문입니다. 계획을 만들 때는 친구들이 가고 싶어하는 음식점을 알아보고, 친구들의 Wishlist에서 몇몇 음식점을 비교하고, 평가를 검토하는 등 거쳐야 할 작업이 많습니다. 계획을 세우려면 메인 앱 보기에 자세히 나와 있는 세부 정보를 참조해야 하므로 이러한 기능을 공유 대상 앱에 요약하는 것은 그리 좋은 생각이 아닙니다.</p>
<p>&nbsp;</p>
<h3>Wishlist에 추가</h3>
<p>이 시나리오는 공유 대상에서 짧고 통제된 작업으로 원활하게 작동합니다. 이 앱 경험의 주요 내용은 다음과 같습니다.<strong></strong></p>
<ul>
<li>사람들이 후기, 음식 블로그 등이 있는 여행 계획 앱이나 국내 도시 앱 같은 다른 앱을 통해 음식점을 발견합니다. Food with Friends는 &lsquo;My Wishlist&rsquo; 섹션에 음식점을 정리하기 때문에 매우 유용한 대상입니다.</li>
<li>앱의 대상 앱 보기는 다른 앱의 컨텍스트에 있으면서도 빠르게 작업을 수행할 수 있어야 합니다. 따라서 Wishlist에 음식점을 추가하는 옵션은 공유 대상에 표시하기에 매우 좋습니다.</li>
<li>물론 사용자가 앱을 전환하고 다시 검색해서 음식점을 찾은 후 Wishlist에 음식점을 추가할 수는 있지만 공유를 간단히 탭하는 것이 훨씬 더 편리합니다.</li>
</ul>
<p>Food with Friends Wishlist는 적절한 공유 대상 앱 경험으로 보이므로 여기에서는 이 공유 대상 앱 경험을 디자인하고 빌드하려고 합니다. 우리가 염두에 둔 전체 시나리오는 이렇습니다.</p>
<p>가까운 푸드 트럭을 찾아 주는 앱을 탐색하다가 흥미로운 푸드 트럭 후기를 찾습니다. 나중에 계획을 세울 때 참조할 수 있게 이 앱에서 Food with Friends의 My Wishlist로 푸드 트럭을 추가하려고 합니다.</p>
<p align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/7658.contoso_truck_6C263411.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/7658.contoso_5F00_truck_5F00_6C263411.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="가까운 푸드 트럭을 찾아 주고 명소를 추천하는 앱 탐색" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3022.contoso_truck_thumb_4B9EF45F.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3022.contoso_5F00_truck_5F00_thumb_5F00_4B9EF45F.png" alt="가까운 푸드 트럭을 찾아 주고 명소를 추천하는 앱 탐색" width="700" height="394" border="0" /></a>그림 4: <em></em>가까운 푸드 트럭을 찾아 주고 명소를 추천하는 앱 탐색</p>
<p>이 이미지는 우리가 사용하려는 공유 대상 앱 보기입니다.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3835.contoso_truck2_565C4BB4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3835.contoso_5F00_truck2_5F00_565C4BB4.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="공유 대상 앱 보기" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/8637.contoso_truck2_thumb_760B257C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/8637.contoso_5F00_truck2_5F00_thumb_5F00_760B257C.png" alt="공유 대상 앱 보기" width="700" height="394" border="0" /></a></p>
<p align="center">그림 5: <em></em>공유 대상 앱 보기</p>
<p>사용자는 대상 앱 보기에서 공유 작업을 완료하여 현재의 몰입형 앱 경험에서 컨텍스트를 전환하지 않고 Food with Friends에 음식점을 정리할 수 있습니다.</p>
<h2>공유 대상 구현</h2>
<p>공유 대상을 구현해야 하는 경우에 대한 몇 가지 예를 살펴보았으므로 이제 공유 대상을 구현하는 방법을 알아보겠습니다. 먼저 앱에서 받을 수 있는 데이터 형식이 무엇일지 정합니다.</p>
<h3>어떤 데이터 형식을 지원해야 할까요?</h3>
<p>공유를 사용하면 텍스트, URI, HTML, 비트맵, 파일 같은 표준 데이터 형식이나 <a href="http://schema.org" target="_blank">http://schema.org</a>에 나와 있는 사용자 지정 데이터 형식을 지원할 수 있습니다. Windows는 지원되는 데이터 형식으로 대상 앱을 필터링하여 공유 창에 필터링된 대상 앱 목록을 표시합니다. 앱은 앱 경험에 중요한 데이터 형식을 지원해야 합니다. 예를 들어, 사진을 보는 데 사용되는 앱은 텍스트 형식을 지원할 필요가 없습니다. 여러 데이터 형식을 지원한다고 선언한 경우 데이터 패키지에서 우선적으로 선택할 수 있도록 공유 데이터를 가장 잘 나타내는 형식을 결정해야 합니다.</p>
<p>Food with Friends는 사용자가 앱에 음식점을 공유하도록 지원하는 공유 대상 경험을 구현하고 있으므로 <a href="http://schema.org" target="_blank">http://schema.org</a>에 나와 있는 <a href="http://schema.org/Restaurant" target="_blank">Restaurant</a> 데이터 형식을 사용할 것입니다. Food with Friends는 대상 공유 계약에 등록하여 이러한 데이터 형식을 받을 수 있습니다. 그런 다음 받은 패키지의 데이터를 사용하여 대상 앱 보기에 사용자가 Wishlist에 음식점을 추가할 수 있는 멋진 경험을 구현할 수 있습니다.</p>
<p>원본 앱은 <a href="http://schema.org/Restaurant" target="_blank">Restaurant</a> 데이터 형식을 정확히 공유하는 경우도 있고, <a href="http://schema.org/" target="_blank">schema.org</a>에 나와 있는 <a href="http://schema.org/Place" target="_blank">Place</a>, <a href="http://schema.org/LocalBusiness" target="_blank">LocalBusiness</a> 및 <a href="http://schema.org/FoodEstablishment" target="_blank">FoodEstablishment</a>와 같은 다른 데이터 형식을 사용하여 유사한 정보를 표현하는 경우도 있습니다. 따라서 Food with Friends는 공유 계약에 여러 형식을 수신하여 보다 광범위한 원본 앱에 제공할 수 있도록 지원합니다.</p>
<p>Food with Friends는 앱 매니페스트의 공유 대상 선언에 다음과 같은 형식을 지원합니다.</p>
<ul>
<li><span style="text-decoration: underline;"><a href="http://schema.org/Restaurant" target="_blank">Restaurant</a></span></li>
<li><span style="text-decoration: underline;"><a href="http://schema.org/Place" target="_blank">Place</a></span></li>
<li><span style="text-decoration: underline;"><a href="http://schema.org/LocalBusiness" target="_blank">LocalBusiness</a></span></li>
<li><span style="text-decoration: underline;"><a href="http://schema.org/FoodEstablishment" target="_blank">FoodEstablishment</a></span></li>
</ul>
<p>&nbsp;</p>
<p>이 글에서는 편의상 <a href="http://schema.org/Restaurant" target="_blank">Restaurant</a> 데이터 형식을 주로 다루고 있지만 다른 데이터 형식도 개념은 비슷합니다.</p>
<h3>어떤 데이터 형식과 시나리오를 자세히 살펴보아야 할까요?</h3>
<p>Food with Friends는 앱이 이미 알고 있는 음식점과 일치하는 음식점 정보가 수신될 경우에만 사용자의 Wishlist에 음식점을 추가하도록 지원합니다. 앱에 중복된 음식점 정보가 생기지 않게 하기 위해서입니다.</p>
<p>표준 데이터 형식은 지원되지 않습니다. 원본 앱에서 공유되는 대부분의 텍스트, 파일, 사진, URI 또는 HTML은 음식점이나 지역 사업소에 매핑되지 않기 때문입니다. 예를 들어 Food with Friends가 텍스트 수신을 지원할 경우 메모 원본 앱의 대상 앱 목록에 나타납니다. 이 경우 Food With Friends는 사용자에게 해당 원본 앱과 이 앱이 연동되지 않는 이유를 설명하는 오류 메시지를 제공해야 합니다. 요점은 데이터 형식을 통해 대상 앱에 무언가를 제공하는 원본 앱이 연결된다는 것입니다. 데이터 형식이 다르면 공유할 만한 가치가 없습니다.</p>
<p>관심 있는 사항을 알아볼 때 도움이 되는 데이터 형식과 공유 경험을 방해할 만한 데이터 형식을 모두 자세히 살펴보십시오.</p>
<h3>대상 공유 계약 지원</h3>
<p>데이터 형식을 지원하려면 Visual Studio 매니페스트 디자이너에서 앱에 해당 데이터 형식을 선언하십시오. 사용자가 여러분의 앱을 공유 작업의 대상으로 선택할 경우 다음과 같은 작업이 수행됩니다.</p>
<ol>
<li>원본 앱을 통해 대상 공유 창에 앱의 새 인스턴스가 열립니다.</li>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.applicationmodel.activation.sharetargetactivatedeventargs.aspx" target="_blank">share target activation</a> 이벤트가 실행되고 앱이 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br205977.aspx" target="_blank">ShareOperation</a><strong></strong> 개체를 결과로 받습니다.</li>
<li>이 개체를 통해 사용자가 공유한 데이터를 검색할 수 있습니다.</li>
<li>이 데이터를 사용하여 대상 앱 보기를 렌더링하고 사용자의 공유를 완료할 수 있습니다.</li>
</ol>
<p>이 경험을 만드는 방법에 대한 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465255.aspx" target="_blank">공유 콘텐츠 받기</a> <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465255.aspx"><span style="text-decoration: underline;">빠른 시작</span></a> 가이드를 참조하십시오.</p>
<p>또한 Visual Studio는 공유 대상을 위한 템플릿을 지원합니다. 해당 프로젝트를 마우스 오른쪽 단추로 클릭하고 <strong>항목 추가</strong>, <strong>새 항목 추가</strong>를 선택한 후 목록에서 <strong>대상 공유 계약</strong>을 클릭한 다음 계약을 지원하기 위해 추가된 코드를 확인합니다. <a href="http://code.msdn.microsoft.com/windowsapps/Sharing-Content-Target-App-e2689782" target="_blank">공유 대상 코드 샘플</a>에 공유 콘텐츠를 받는 방법이 나와 있습니다.</p>
<h2>맞춤 대상 앱 보기 디자인</h2>
<p>사용자가 공유 창의 앱 목록에서 대상 앱을 선택하면 Windows의 앱의 공유 대상 보기에서 앱이 열립니다. 대상 앱 보기는 공유 창에서 호스팅되는 특별한 보기이며 앱의 기본 보기와는 다릅니다. <a href="http://blogs.msdn.com/b/windowsappdev_ko/archive/2012/03/30/windows-8-activating-contracts.aspx" target="_blank">앱에서 Windows 8 계약 활성화하기</a>에 호스트 보기와 이러한 보기의 활성화를 처리하는 방법에 대해 자세히 설명되어 있습니다. 대상 앱 보기는 사용자가 시작한 공유 작업이 완료될 수 있도록 지원하기 위해 만들어졌습니다. 이 보기에서 사용자는 콘텐츠를 한눈에 미리 보고, 간단한 작업을 수행하고, 공유 여부를 확인할 수 있어야 합니다.</p>
<p>Food with Friends의 대상 앱 디자인을 살펴보기 전에 여러분이 이미 알고 있을 듯한 대상 앱에 대해 알아보겠습니다. 아래 이미지는 Windows 8의 앱 전체 보기와 메일 앱의 대상 앱 보기입니다.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1307.target-app_4AC68E75.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1307.target_2D00_app_5F00_4AC68E75.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="오른쪽에 있는 메일 대상 앱으로 사진 공유" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0724.target-app_thumb_5583E5CA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0724.target_2D00_app_5F00_thumb_5F00_5583E5CA.png" alt="오른쪽에 있는 메일 대상 앱으로 사진 공유" width="700" height="438" border="0" /></a></p>
<p align="center">그림 6: <em></em>오른쪽에 있는 메일 대상 앱으로 사진 공유</p>
<p align="left"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1385.mail_154DCC50.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1385.mail_5F00_154DCC50.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="메일의 메인 앱 보기에서 새 메일 작성" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/2055.mail_thumb_510D6503.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/2055.mail_5F00_thumb_5F00_510D6503.png" alt="메일의 메인 앱 보기에서 새 메일 작성" width="700" height="438" border="0" /></a></p>
<p align="center">그림 7: <em></em>메일의 메인 앱 보기에서 새 메일 작성</p>
<p>메일의 공유 대상 앱 보기는 전체 앱 보기보다 간단합니다. 사용자가 공유하려고 하는 콘텐츠에 대해 깔끔하게 최소화된 미리 보기를 축소판 형식으로 제공하고 관련 텍스트와 HTML도 제공합니다. 또한 받는 사람을 추가할 수 있는 <strong>받는 사람</strong> 입력란, 간단한 메시지를 작성할 수 있는 입력 영역, 그리고 <strong>보내기</strong> 단추를 통해 메일 보내기를 확인하는 간단한 방법을 제공합니다.</p>
<p>이처럼 앱의 대상 앱 보기는 더 간단하면서도 메일 앱의 아바타로 명확히 식별할 수 있으므로 공유 콘텐츠의 대상에 대한 사용자의 신뢰를 높여줍니다.</p>
<h2>Food with Friends를 공유 대상 앱으로 사용</h2>
<p>대상 앱 보기의 디자인에 대한 일반적인 내용을 살펴보았으니 이제 Food with Friends를 사용하여 사용자가 &lsquo;My Wishlist&rsquo;에 음식점을 쉽게 추가하도록 도와주는 대상 앱을 만드는 방법에 대해 알아보겠습니다.</p>
<p>Food with Friends가 원본 앱에서 <a href="http://schema.org/Restaurant">Restaurant</a>이 포함된 데이터 패키지를 받으면 <a href="http://schema.org/Restaurant">Restaurant</a> 데이터 형식의 세부 정보를 사용하여 기존 데이터베이스에서 수신된 데이터 패키지와 일치하는 음식점을 식별합니다. 그러면 대상 앱 보기에 해당 음식점의 미리 보기를 표시할 수 있습니다. 이는 공유 콘텐츠의 무결성에 대한 신뢰를 높여줍니다.</p>
<p>이 이미지에서는 Food with Friends의 대상 앱 보기를 보여줍니다.</p>
<p align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3857.contoso_truck3_0305058E.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3857.contoso_5F00_truck3_5F00_0305058E.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="Food with Friends의 대상 앱 보기" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5584.contoso_truck3_thumb_1B94A2DE.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5584.contoso_5F00_truck3_5F00_thumb_5F00_1B94A2DE.png" alt="Food with Friends의 대상 앱 보기" width="700" height="394" border="0" /></a>그림 8: <em></em>Food with Friends의 대상 앱 보기</p>
<p>보시다시피 Food with Friends의 대상 앱 보기는 간단하고 공유 대상 앱에 맞춰져 있습니다. 사용자가 공유하려는 음식점의 미리 보기와 메인 앱의 공유 콘텐츠 대상을 알려 주는 <strong>Add to Wishlist</strong>라는 간단한 실행 단추를 표시합니다. 사용자가 이 단추를 탭 또는 클릭하면 공유 작업이 확인됩니다. 여기에서 탭 또는 클릭을 요구한다면 의도하지 않은 공유가 방지될 뿐만 아니라 전체 공유 경험에 대한 신뢰를 높이는 데도 도움이 됩니다.</p>
<p>앱이 데이터 패키지 콘텐츠의 정보와 일치하는 음식점을 여러 개 찾으면 Food with Friends에서 공유 대상 앱 보기를 사용하여 옵션을 표시하고 사용자에게 공유할 항목을 선택하도록 합니다.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5582.contoso_truck4_70500BD6.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5582.contoso_5F00_truck4_5F00_70500BD6.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="대상 앱 보기는 사용자가 공유할 항목을 선택할 수 있도록 옵션을 표시합니다." src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/6175.contoso_truck4_thumb_6FE3D8E1.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/6175.contoso_5F00_truck4_5F00_thumb_5F00_6FE3D8E1.png" alt="대상 앱 보기는 사용자가 공유할 항목을 선택할 수 있도록 옵션을 표시합니다." width="700" height="394" border="0" /></a></p>
<p align="center">그림 9: <em></em>대상 앱 보기는 사용자가 공유할 항목을 선택할 수 있도록 옵션을 표시합니다.</p>
<p>&nbsp;</p>
<p>앱이 수신된 데이터 패키지를 기준으로 음식점 또는 유효한 옵션 집합을 정확히 식별할 수 없을 경우를 대비해서 대상 앱 보기를 사용하여 해당 메시지를 전달하십시오.</p>
<h2>QuickLink 만들기</h2>
<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.quicklink.aspx" target="_blank">QuickLink</a>는 사용자가 대상 앱 내에서 신속하게 익숙한 대상으로 콘텐츠를 공유할 수 있는 공유 API의 프로비전입니다. QuickLink는 특정 작업을 제공하는 대상 보기에 대한 사용자 지정 링크로 사용됩니다. Windows에서 사용자가 사용할 수 있는 대상 앱 목록을 필터링하면 공유 창 맨 위에 사용 가능한 대상 목록으로 QuickLink 목록이 제공됩니다. 이러한 익숙한 대상 또는 사용자 작업에는 대상 앱을 통해 자주 공유되는 장소나 사람이 포함될 수 있습니다. 예를 들어 메일 앱은 이전에 메일을 보낸 연락처의 QuickLink를 반환합니다. 마찬가지로 대상 앱에 자주 공유하는 폴더, 그룹, 메모 및 재생 목록에 대한 QuickLink도 추가할 수 있습니다.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5001.quicklink_2FADBF67.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5001.quicklink_5F00_2FADBF67.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="메일 대상 앱에서 제공하는 이전에 대상 앱을 사용하여 메일을 보낸 연락처에 대한 QuickLink가 공유 창에 표시됨" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1374.quicklink_thumb_334BDA44.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1374.quicklink_5F00_thumb_5F00_334BDA44.png" alt="메일 대상 앱에서 제공하는 연락처에 대한 QuickLink가 공유 창에 표시됨 " width="700" height="438" border="0" /></a></p>
<p align="center">그림 10: 메일 대상 앱에서 제공하는 이전에 대상 앱을 사용하여 메일을 보낸 연락처에 대한 <br />QuickLink가 공유 창에 표시됨</p>
<p>대상 앱은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br205977.aspx" target="_blank">ShareOperation</a><strong></strong>개체의<strong></strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.reportcompleted.aspx" target="_blank">reportCompleted</a><strong></strong><strong></strong>메서드를 호출하여 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.quicklink.aspx" target="_blank">QuickLink</a>를 제공할 수 있습니다. QuickLink에는 제목, 아이콘, ID가 있어야 합니다. 사용자가 공유 참 메뉴를 탭하면 제목(예: "엄마에게 전자 메일 보내기")과 아이콘이 나타납니다. ID는 앱에서 전자 메일 주소, 폴더 위치 등과 같은 사용자 지정 정보에 액세스하는 데 사용됩니다. 이 도구에 대한 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh758299.aspx" target="_blank">QuickLink를 만드는 방법</a>을 참조하십시오.&nbsp;</p>
<p>참고: QuickLink는 앱에서 대상 앱의 여러 작업을 지원하는 경우에 유용합니다. Food with Friends는 공유 대상에서 단일 작업, 즉 <strong>Add to Wishlist</strong>만 지원하므로 QuickLink를 구현하지 않습니다.</p>
<h2>확장 공유 및 오류 보고</h2>
<p>원본 앱이 사진, 비디오 또는 파일과 같은 대형 데이터 청크를 공유하면서 데이터 전송 시간이 길어지는 경우가 있습니다. 이러한 공유 작업 유형을 '확장 공유'<em></em>라고 하며, 대상 앱이 데이터를 받고 처리하는 데 시간이 좀 더 필요할 수 있습니다.</p>
<p>공유는 빠르고 유동적이어야 하고, 대상 앱 보기는 메인 앱을 통해 호스팅되는 가벼운 보기이기 때문에 사용자는 대상 앱 보기에서 탭하여 메인 앱으로 돌아갈 수 있습니다. 따라서 대상 앱은 공유가 시작된 후 대상 앱 보기에 의존하지 않으며 사용자 조작을 예상하지 못합니다. 대상 앱은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br205977.aspx" target="_blank">ShareOperation</a><strong></strong> 개체와 메서드를 사용하여 Windows에 앱이 데이터를 처리하고 있다고 알려줄 수 있습니다.</p>
<ul>
<li><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.reportstarted.aspx" target="_blank">reportStarted</a></strong> &ndash; 원본 앱에서 DataPackage 개체의 데이터 검색을 시작했다고 Windows에 알려 주려면 이 메서드를 호출합니다.</li>
<li><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.reportdataretrieved.aspx" target="_blank">reportDataRetrieved</a></strong><strong></strong>&ndash; 패키지에서 데이터 검색을 마쳤다고 Windows에 알려 주려면 이 메서드를 호출합니다.</li>
<li><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.reportcompleted.aspx" target="_blank">reportCompleted</a></strong><strong><span style="text-decoration: underline;"> &ndash; </span></strong>공유 작업을 완료했다고 Windows에 알려 주려면 이 메서드를 호출합니다.<strong></strong><strong></strong></li>
</ul>
<p>앱이 확장 공유의 데이터를 처리 중일 때는 대상 앱 보기에 진행률 UI를 표시합니다. 이렇게 하면 사용자에게 요청이 진행 중임을 알려줄 수 있고, 사용자가 앱으로 돌아가서 공유 작업의 진행률을 확인했을 때 신뢰도가 높아집니다. Windows 8의 진행률 컨트롤에 대한 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465487.aspx" target="_blank">빠른 시작: 진행률 컨트롤 추가</a>를 참조하십시오.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0702.progress-bar_52FAB40C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0702.progress_2D00_bar_5F00_52FAB40C.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="진행률 표시줄" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3438.progress-bar_thumb_40B1ED4A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3438.progress_2D00_bar_5F00_thumb_5F00_40B1ED4A.png" alt="진행률 표시줄" width="700" height="438" border="0" /></a></p>
<p align="center">그림 11: <em></em>대용량 첨부 파일 전송과 같은 확장 공유를 완료하면 메일 대상 앱 보기에 진행률 링이 표시됩니다.</p>
<p>데이터 전송 중에 문제가 발생하거나 대상 앱이 공유 작업을 종료해야 하는 경우에는 <strong><span style="text-decoration: underline;">reportError</span></strong>를 호출하여 Windows에 오류 메시지를 보낼 수 있습니다. 사용자가 공유 창에서 공유 상태를 확인할 때 메시지를 볼 수 있습니다. 오류가 보고된 후 앱은 종료되고 공유도 종료됩니다. 사용자는 앱에 콘텐츠를 공유하는 작업을 다시 시작해야 합니다.</p>
<p>Food with Friends 공유 대상 예의 경우 앱이 데이터베이스에서 <a href="http://schema.org/Restaurant" target="_blank">Restaurant</a> 데이터 형식을 통해 원본 앱에서 공유한 음식점과 일치하는 음식점을 식별할 수 없는 경우 오류를 보고해야 합니다.</p>
<h2>내용 정리</h2>
<p>앱 간 공유는 대상 앱을 공유할 수 있는 엄청난 기회를 제공합니다. 앱에서 공유 콘텐츠를 받을 수 있다면 사용자가 다른 작업을 하고 있을 때도 앱을 활용할 수 있습니다. 이제 훌륭한 공유 대상 위치를 만드는 데 사용할 수 있는 데이터 형식, 클래스, 속성과 디자인 원칙에 대해 잘 이해되셨기 바랍니다. 우리는 여러분이 앱에서 공유 콘텐츠를 변환하고 더 다양하고 풍부한 최종 사용자 경험을 실현할 수 있는 방법을 알려드리게 되어 기쁩니다.</p>
<p>공유에 대해 자세히 알아보려면 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh464923.aspx" target="_blank">데이터 공유 및 교환</a>과 <a href="http://code.msdn.microsoft.com/windowsapps/Sharing-Content-Target-App-e2689782" target="_blank">공유 콘텐츠 대상 앱 샘플</a>을 읽고 시작해 보시기 바랍니다.</p>
<p>프로그램 관리자</p>
<p>Ishita Kapur</p>
<p>Windows 사용자 경험</p>
<!-- Title: “대상 앱과 공유” Tags: “앱 공유, 대상 앱, 공유 콘텐츠, 공유 대상, 대상 앱 공유” --></div>]]></content:encoded>
    </item>
    <item>
      <title>NFC로 첨단 앱 개발</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/nfc</link>
      <pubDate>Wed, 24 Apr 2013 15:40:09 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/04/24/nfc/</guid>
      <description><![CDATA[NFC(Near Field Communication, 단거리 무선 통신)는 새로운 단거리 무선 기술입니다. 정확히 2cm 거리에 있는 사람들끼리 물리적으로 장치를 함께 탭하여...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>NFC(Near Field Communication, 단거리 무선 통신)는 새로운 단거리 무선 기술입니다. 정확히 2cm 거리에 있는 사람들끼리 물리적으로 장치를 함께 탭하여 콘텐츠를 주고받을 수 있습니다. 탭 방식을 사용하면 대상을 빠르게 선택할 수 있을 뿐만 아니라 이해하기도 쉽습니다. 보면 바로 이해되므로 설명서도 필요 없습니다.</p>  <p>예를 들어 친구와 함께 사진을 보다가 친구가 사진을 달라고 할 때 NFC가 있으면 장치를 친구의 PC에 대고 탭하여 간단히 사진을 보낼 수 있습니다. 너무 간단하게 설명했지만 장치 간에 콘텐츠를 간편하게 공유할 수 있다는 것이 핵심입니다.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1732.NFC-image_04E645BB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1732.NFC_2D00_image_5F00_04E645BB.png"><img title="&#39;Nokia 360 스피커가 Windows Phone과 Bluetooth 연결 정보를 공유하는 모습" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="&#39;Nokia 360 스피커가 Windows Phone과 Bluetooth 연결 정보를 공유하는 모습" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3566.NFC-image_thumb_38420917.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3566.NFC_2D00_image_5F00_thumb_5F00_38420917.png" width="400" height="348" /></a></p>  <p align="center">그림 1: Nokia 360 스피커가 Windows Phone과    <br />Bluetooth 연결 정보를 공유하는 모습</p>  <p>Bluetooth 또는 Wi-Fi와 비슷한 NFC는 <a href="http://www.nfc-forum.org/home/" target="_blank">NFC Forum</a>에서 정의한 표준 무선 프로토콜입니다.<u></u><u></u> PC에서 NFC를 사용하려면 NFC 라디오가 필요합니다. Windows 8 PC에는 NFC 라디오가 직접 통합된 경우가 많이 있습니다. NFC가 통합되어 있지 않다면 NFC 동글을 구입하여 PC에 연결하면 됩니다.</p>  <p>NFC는 멋진 기능을 제공합니다. 사진을 탭하여 전송하거나, 레스토랑에서 메뉴를 탭하여 음식을 주문하거나, Bluetooth 장치를 탭하여 연결할 수도 있습니다. 전혀 다른 이야기 같지만 모두 '탭' 방식으로 시작한다는 데 공통점이 있습니다. NFC는 PC, 전화, 스피커, 헤드셋, 무선 디스플레이 등과 같은 다양한 장치에서 사용되며, 매우 직관적인 장치 연결을 지원합니다. 또한 NFC는 RFID 태그를 사용합니다. RFID 태그는 매우 저렴하고 가벼운 패시브 안테나로, 꽤 많은 양의 데이터가 들어가고 거의 모든 곳에 부착할 수 있기 때문에 포스터에 주로 사용됩니다. 예를 들어 영화 포스터를 탭하기만 하면 영화표를 간단하게 구매할 수 있습니다. 이러한 것들을 NFC 태그라고 합니다. 이제 NFC 태그를 탭하는 기본적인 예를 알아보면서 NFC API의 핵심 개념을 몇 가지 설명하도록 하겠습니다.</p>  <p>하지만 그 전에 장치를 함께 탭한다는 것이 무슨 의미인지 자세히 살펴보도록 하겠습니다.</p>  <h2>'탭' 정의</h2>  <p>Bluetooth 마우스를 탭하여 연결하든, 사진을 탭하여 공유하든, 장치를 같은 방법으로 함께 탭하는 것이 중요합니다. 그리고 탭한다는 것은 잘 알고 있는 개념이지만 PC를 다른 장치에 가까이 대고 탭한다는 개념은 대부분 생소할 것입니다. 탭과 관련하여 알아야 할 몇 가지 유용한 지침은 다음과 같습니다.</p>  <ol>   <li>     <div align="center"><b>장치를 함께 탭하는 지점</b> - '탭하여 실행' 비주얼 마크라고도 하는 터치마크를 사용여 NFC 안테나가 있는 위치를 표시합니다. PC 모델에 따라 탭하는 부분도 다릅니다. 예를 들어 태블릿의 경우 후면을 탭해야 하지만 올인원의 경우 전면을 탭해야 합니다. NFC 기능이 있는 Windows 8 PC에서 볼 수 있는 마크는 다음과 같은 모양입니다.         <br /></div>     <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0830.NFC-logo_66E85720.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0830.NFC_2D00_logo_5F00_66E85720.png"><img title="탭하여 실행 비주얼 마크" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="탭하여 실행 비주얼 마크" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5611.NFC-logo_thumb_3FAE0DEB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5611.NFC_2D00_logo_5F00_thumb_5F00_3FAE0DEB.png" width="200" height="147" /></a>      <p align="center">그림 2: 탭하여 실행 비주얼 마크</p>   </li>    <li><b>장치가 서로 통신 중</b> - 탭할 때 전송되는 데이터가 직접 보이지는 않아도 무언가 진행 중이라는 것을 사용자가 확신할 수 있어야 하므로 범위 내의 장치끼리 통신 중일 때는 Windows에서 소리가 재생됩니다.</li> </ol>  <p>이 부분은 Windows에서 자동으로 처리하므로 신경 쓰지 않아도 됩니다. 사용자 환경 요소에 대한 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/hardware/hh770524.aspx" target="_blank">Windows 8 근거리 근접 연결 구현 사양</a><u></u>을 참조하십시오. 이를 바탕으로 NFC로 경험할 수 있는 멋진 시나리오 몇 가지를 살펴보겠습니다.</p>  <h2>NFC를 사용하는 경우 </h2>  <p>사용자가 앱에서 어떤 대상을 선택해야 할 때는 NFC를 사용하십시오. NFC를 사용하면 직관적인 방식으로 선택할 수 있으며 수동으로 검색하는 것보다 빠르고 더 근사하게 보입니다. 탭은 앱에 따라 다르지만 사진 수신부터 재생 목록 시작까지 다양한 경험을 촉발하는 일종의 트리거가 될 수 있습니다. 탭 이후에 발생하는 일은 전적으로 앱이 결정합니다. 따라서 Microsoft는 이러한 경험의 범위를 간단히 '탭하여 실행' 경험으로 분류하고 있습니다.</p>  <p>다음은 NFC를 사용하여 앱에서 무언가를 선택하는 예입니다. 탭하여 다음을 실행할 수 있습니다.</p>  <ul>   <li><u>포스터의 정보 확인</u>: <a href="http://www.nfc-forum.org/news/pr/view?item_key=2c0cb92de7d47bbbe7c99f13912b3307fc03c1c6" target="_blank">NFC 태그</a>는 가볍고 저렴한 RFID 태그입니다. 가격은 0.15달러에서 1달러 정도이며 인쇄 비용에 따라 가격이 달라집니다. NFC 태그는 QR 코드와 비슷하지만 더 쉽고, 더 빠르게 사용할 수 있습니다. 카메라 각도를 맞추면서 바코드 사진을 찍는 것보다는 태그를 탭하는 것이 훨씬 더 간편합니다. 공항, 지하철 역, 버스 정류장과 같이 사람들이 붐비는 곳에 태그가 포함된 포스터를 광고하는 제조업체들이 점점 늘어나고 있습니다. NFC 태그에는 48B~4KB 데이터가 저장될 수 있으며 앱의 특정 페이지를 실행하는 태그도 프로그래밍할 수 있습니다. </li> </ul>  <ul>   <li><u>연락처 정보 교환</u>: 내 연락처 정보를 알려줄 때 친구가 잘못 받아적을까 봐 걱정할 필요 없이 장치를 탭하여 연락처 정보를 교환해 보십시오. 마찬가지로 NFC 명함/태그에 정보를 프로그래밍하거나 앱을 통해 정보를 직접 교환할 수 있습니다.</li>    <li><u>좋아하는 음악 방송 재생</u>: 운동하러 갈 때, 차에 시동을 걸 때, 또는 집에서 쉴 때, NFC 태그를 사용하여 라디오 방송을 들어보십시오. 헬스장용 태그, 휴식용 태그, 취침용 태그 등 다양한 태그에 음악 방송을 프로그래밍할 수도 있습니다.</li>    <li><u>붐비는 레스토랑에서 저녁 식사 주문</u>: 저녁 시간에 손님이 많은 레스토랑에서는 주문하는 데만도 엄청 오래 기다려야 합니다. 기다리지 말고 테이블에서 태그를 탭하여 음식을 주문하십시오.</li>    <li><u>친구와 함께 게임:</u> 장치를 함께 탭하여 배틀쉽, 체스 또는 스크래블과 같이 여럿이 같이 하는 게임을 친구와 함께 즐길 수 있습니다. 탭 후에는 Bluetooth 또는 Wi-Fi Direct와 같이 대역이 넓고 처리량이 많은 대역 외 전송을 통해 연결이 유지됩니다. </li> </ul>  <p>지금까지 NFC를 사용해야 하는 경우에 대한 유용한 아이디어를 알아보았습니다. 이제 Windows 8 근접 연결(NFC) API를 사용하는 앱을 개발하는 방법을 알아보는 재미있는 시간을 갖겠습니다.</p>  <h2>NFC를 구현하는 방법 </h2>  <p>지금까지 알아본 것처럼 NFC는 일상에서 느끼는 여러 가지 불편을 덜어 줍니다. 알람을 설정하는 예를 보겠습니다. 우리 모두 아침 알람 시간을 잘못 맞춰본 적이 있을 겁니다. 늦은 시각 잠들기 직전에는 누구나 분별력이 약간 떨어지기 마련인데, 이때 NFC를 사용하면 아주 쉽게 미리 설정된 태그를 탭하고 확인한 후 아무런 걱정 없이 잠들 수 있습니다. 그러면 사용자들의 일상적인 불편을 덜어 주기 위해 사용자가 NFC 태그를 사용하여 알람을 설정할 수 있는 기본 알람 앱을 한번 작성해 보겠습니다. 이는 두 가지 시나리오로 나눌 수 있습니다.</p>  <ol>   <li><u>태그에 <b>직접</b> 알람 설정</u>: NFC 태그는 재사용이 가능하므로 앱에 사용자가 알람을 프로그래밍할 수 있는 기능이 있어야 합니다. 예를 들어 평일과 주말을 구분해서 알람을 프로그래밍하는 경우가 있습니다. 이것을 태그에 데이터를 게시한다고 합니다.</li>    <li><u>태그를 <b>통해</b> 알람 설정</u>: 사용자가 태그를 탭하면 앱이 실행되어 알람 설정을 확인해야 합니다. 즉, 상황에 따라 또는 인수를 사용하여 앱을 실행할 수 있습니다. </li> </ol>  <p>NFC API로 이러한 시나리오를 구현하는 방법은 여러 가지가 있는데 그 중 가장 간단한 방법을 살펴보겠습니다.</p>  <p>NFC 태그에 직접 알람을 설정하는 과정을 알아보겠습니다.</p>  <ol>   <li>민수가 알람 앱을 실행하고 시간을 오전 7시로 설정합니다. 아직 NFC가 연결되지 않은 일반 알람 설정입니다.</li>    <li>민수가 'NFC 태그에 알람 설정' 옵션을 선택합니다. 그러면 앱이 NFC API를 호출하여 NFC 라디오에 앱 식별자 문자열과 07:00이라는 정보를 게시합니다. NFC 태그는 표준화된 메시지 형식인 NDEF(NFC Defined Exchange Format)를 사용합니다. Windows에서 알아서 처리하므로 앱에서 별도로 데이터의 형식을 NDEF 메시지로 지정할 필요가 <b>없습니다</b>. 이제 사용자가 NFC 태그를 탭할 수 있습니다.</li>    <li>민수가 태그를 PC에 가까이 대고 탭하면 앱이 태그 프로그래밍을 확인합니다. 태그를 탭한 후 사용자에게 앱이 태그 프로그래밍을 완료했음을 알려주는 것이 중요합니다. 앞서 말했다시피 PC를 탭하는 것은 대부분의 사람들에게 생소한 개념이므로 확인을 통해 사용자에게 탭이 완료되었다고 안심시켜 주어야 합니다. 메시지 전송 처리기를 등록하면 앱이 메시지가 전송되었다는 것을 알게 됩니다.</li> </ol>  <p>NFC API는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.proximity.aspx" target="_blank">Windows.Networking.Proximity</a> 네임스페이스에 있으며 사용자가 'NFC 태그에 알람 설정' 옵션을 선택한 후에 진행되는 2단계에서 실행됩니다. 먼저 앱은 근접 연결 개체를 초기화합니다. 근접 연결 개체는 태그(또는 장치)가 범위 내에 있거나 범위를 벗어났을 때 알려주는 데 사용됩니다. 다음으로, DeviceArrival 이벤트 처리기를 추가하겠습니다. 이 처리기는 태그를 탭한 시점, 즉 태그에 정보를 기록할 수 있는 시점을 인식합니다. 태그에 정보를 기록 중일 때 사용자에게 이를 알려주면 범위를 벗어나지 않게 할 수 있으므로 유용합니다. 또한 근접 연결 장치를 탭한 시점도 인식할 수 있습니다.</p>  <p>다음은 DeviceArrival 이벤트 처리기를 초기화하고 추가하는 방법을 보여주는 코드 예제입니다.</p>  <p><strong>JavaScript</strong></p>  <blockquote>   <div>     <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> proximityDevice;<br /><br /><span style="color: rgb(0, 0, 255);">function</span> initializeProximityDevice() {<br />    proximityDevice = Windows.Networking.Proximity.ProximityDevice.getDefault();<br /><br />    <span style="color: rgb(0, 0, 255);">if</span> (proximityDevice) {<br />        proximityDevice.addEventListener(<span style="color: rgb(0, 96, 128);">&quot;devicearrived&quot;</span>, proximityDeviceArrived);<br />       <br />}<br />   <span style="color: rgb(0, 0, 255);">else</span> {<br />        <span style="color: rgb(0, 128, 0);">// No NFC radio on the PC, display an error message</span><br />    }<br /><br /><br /><span style="color: rgb(0, 0, 255);">function</span> proximityDeviceArrived(device) {<br />        <span style="color: rgb(0, 128, 0);">// Let the user know we’re ‘Writing to Tag’</span><br /><br />}<br />}<br /></pre>
  </div>
</blockquote>

<div>
  <br /><strong>C#</strong></div>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> InitializeProximityDevice()<br />{<br /><br />Windows.Networking.Proximity.ProximityDevice proximityDevice;<br />    proximityDevice = Windows.Networking.Proximity.ProximityDevice.GetDefault();<br /><br />    <span style="color: rgb(0, 0, 255);">if</span> (proximityDevice != <span style="color: rgb(0, 0, 255);">null</span>) {<br />        proximityDevice.DeviceArrived += ProximityDeviceArrived;<br />    <br />    }<br />    <span style="color: rgb(0, 0, 255);">else</span><br />    {<br />        <span style="color: rgb(0, 128, 0);">// No NFC radio on the PC, display an error message</span><br />    }<br />}<br /><br /><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> ProximityDeviceArrived(Windows.Networking.Proximity.ProximityDevice device)<br />{<br />            <span style="color: rgb(0, 128, 0);">// Let the user know we’re ‘Writing to Tag’</span><br /><br />}<br /></pre>
  </div>
</blockquote>

<p>
  <br />이제 태그에 정보를 게시합니다. 앱은 앱 ID와 앱 플랫폼으로 이루어진 앱 식별자 문자열과 실행 인수를 게시합니다. Windows 8의 경우 앱 ID는 &lt;<a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.applicationmodel.packageid.familyname.aspx" target="_blank">패키지 제품군 이름</a>&gt;!&lt;<a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211415.aspx" target="_blank">앱 ID</a>&gt;이며 앱 플랫폼은 ‘Windows’입니다. 앱의 패키지 매니페스트에 있는 Application 요소의 ID 특성에서 앱 ID 값을 복사해야 합니다. 실행 인수는 사용자가 설정한 알람인 ’07:00’입니다. 이것을 <b>메시지</b>라고 하겠습니다.</p>

<p>앱이 다양한 플랫폼에서 작동하는 경우 대체 앱 ID 및 앱 플랫폼을 게시할 수 있습니다. 즉, Windows Phone 8과 같이 NFC를 지원하는 다른 장치에서 동일한 태그를 탭할 수 있습니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.proximity.peerfinder.alternateidentities.aspx" target="_blank">대체 ID</a>에 대한 자세한 내용은 MSDN을 참조하십시오.</p>

<p>앱은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh701134.aspx" target="_blank">publishBinaryMessage</a>라는 메서드를 사용하여 태그에 데이터를 게시합니다. 이 메서드는 messageType, message 및 messageTransmittedHandler라는 3개의 매개 변수를 받습니다. messageType을 ‘LaunchApp:WriteTag’로 설정해 보겠습니다. 이는 앱이 NFC 태그에 정보를 기록하겠다는 것을 Windows에 알립니다. message는 앞서 정의한 그 메시지입니다(앱 식별 문자열 및 실행 인수). message는 버퍼에 이진 메시지로 저장해야 합니다. messageTransmittedHandler 함수는 콜백에 등록됩니다. 그러면 message가 태그에 기록되었다는 것을 앱에서 알 수 있습니다. 이것은 사용자에게 태그에 메시지가 기록되었고 태그가 더 이상 범위 내에 있지 않아도 된다는 것을 알려줍니다.</p>

<p>메시지는 StopPublishingMessage 함수를 호출하거나 ProximityDevice 개체가 해제될 때까지 계속 게시됩니다. 이 예에서는 중지 함수를 사용하겠습니다. PublishBinaryMessage는 게시 ID를 반환합니다. 이 게시 ID를 사용하여 메시지가 NFC 라디오에 게시되지 못하게 중지하겠습니다.</p>

<p>다음은 NFC 태그에 데이터를 기록하는 방법을 보여주는 코드 예제입니다.</p>

<p><strong>JavaScript</strong></p>

<blockquote>
  <div>
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> proximityDevice;<br /><br /><span style="color: rgb(0, 0, 255);">function</span> getAlarmTime(){<br /><br />    <span style="color: rgb(0, 128, 0);">// Grab time set by the user, call this variable ‘Alarm’</span><br />    <span style="color: rgb(0, 0, 255);">return</span> Alarm;<br /><br />}<br /><br /><span style="color: rgb(0, 0, 255);">function</span> publishLaunchApp() {<br />    proximityDevice = Windows.Networking.Proximity.ProximityDevice.GetDefault();<br /><br /><span style="color: rgb(0, 0, 255);">if</span> (proximityDevice) {<br />        <span style="color: rgb(0, 128, 0);">// The format of the app launch string is: &quot;&lt;args&gt;\tWindows\t&lt;AppName&gt;&quot;.</span><br />        <span style="color: rgb(0, 128, 0);">// The string is tab or null delimited.</span><br /><br />        <span style="color: rgb(0, 128, 0);">// The &lt;args&gt; string can be an empty string (&quot;&quot;).</span><br />        <span style="color: rgb(0, 0, 255);">var</span> launchArgs = getAlarmTime();<br /><br />        <span style="color: rgb(0, 128, 0);">// The format of the AppName is: PackageFamilyName!PRAID.</span><br />        <span style="color: rgb(0, 0, 255);">var</span> praid = <span style="color: rgb(0, 96, 128);">&quot;AlarmApp&quot;</span>; <span style="color: rgb(0, 128, 0);">// The Application Id value from your package.appxmanifest.</span><br /><br />        <span style="color: rgb(0, 0, 255);">var</span> appName = Windows.ApplicationModel.Package.current.id.familyName + <span style="color: rgb(0, 96, 128);">&quot;!&quot;</span> + praid;<br /><br />        <span style="color: rgb(0, 0, 255);">var</span> launchAppMessage = launchArgs + <span style="color: rgb(0, 96, 128);">&quot;\tWindows\t&quot;</span> + appName;<br /><br />        <span style="color: rgb(0, 0, 255);">var</span> dataWriter = <span style="color: rgb(0, 0, 255);">new</span> Windows.Storage.Streams.DataWriter();<br />        dataWriter.unicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.utf16LE;<br />        dataWriter.writeString(launchAppMessage);<br />        <span style="color: rgb(0, 0, 255);">var</span> launchAppPubId =<br />             proximityDevice.publishBinaryMessage(<br />                 <span style="color: rgb(0, 96, 128);">&quot;LaunchApp:WriteTag&quot;</span>, <br />                 dataWriter.detachBuffer(), <br />                 proximityWriteTagLaunchAppMessageTransmitCallback);<br />        <br />    <span style="color: rgb(0, 0, 255);">if</span> (launchAppPubId != -1) {<br />            <span style="color: rgb(0, 128, 0);">// Stop publishing the message on NFC radio</span><br />        proximityDevice.stopPublishingMessage(launchAppPubId);<br />    }<br /><br />     }<br />}<br /><br /><span style="color: rgb(0, 0, 255);">function</span> proximityWriteTagLaunchAppMessageTransmitCallback() {<br />    <span style="color: rgb(0, 128, 0);">// Inform the user that: the message has been successfully written to a tag &amp; the tag no longer needs to be in range</span><br />}<br /></pre>
  </div>
</blockquote>

<div>
  <br /><strong>C#</strong></div>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">Windows.Networking.Proximity.ProximityDevice proximityDevice;<br /><br /><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">string</span> GetAlarmTime(){<br /><br />    <span style="color: rgb(0, 128, 0);">// Grab time set by the user, call this variable ‘Alarm’</span><br />    <span style="color: rgb(0, 0, 255);">return</span> Alarm;<br />}<br /><br /><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> PublishLaunchApp()<br />{<br />    proximityDevice = Windows.Networking.Proximity.ProximityDevice.GetDefault();<br /><br /><span style="color: rgb(0, 0, 255);">if</span> (proximityDevice != <span style="color: rgb(0, 0, 255);">null</span>)<br />    {<br />        <span style="color: rgb(0, 128, 0);">// The format of the app launch string is: &quot;&lt;args&gt;\tWindows\t&lt;AppName&gt;&quot;.</span><br />        <span style="color: rgb(0, 128, 0);">// The string is tab or null delimited.</span><br /><br />        <span style="color: rgb(0, 128, 0);">// The &lt;args&gt; string can be an empty string (&quot;&quot;).</span><br />        <span style="color: rgb(0, 0, 255);">string</span> launchArgs = getAlarmTime();<br /><br />        <span style="color: rgb(0, 128, 0);">// The format of the AppName is: PackageFamilyName!PRAID.</span><br />        <span style="color: rgb(0, 0, 255);">string</span> praid = <span style="color: rgb(0, 96, 128);">&quot;MyAppId&quot;</span>; <span style="color: rgb(0, 128, 0);">// The Application Id value from your package.appxmanifest.</span><br /><br />        <span style="color: rgb(0, 0, 255);">string</span> appName = Windows.ApplicationModel.Package.Current.Id.FamilyName + <span style="color: rgb(0, 96, 128);">&quot;!&quot;</span> + praid;<br /><br />        <span style="color: rgb(0, 0, 255);">string</span> launchAppMessage = launchArgs + <span style="color: rgb(0, 96, 128);">&quot;\tWindows\t&quot;</span> + appName;<br /><br />        var dataWriter = <span style="color: rgb(0, 0, 255);">new</span> Windows.Storage.Streams.DataWriter();<br />        dataWriter.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;<br />        dataWriter.WriteString(launchAppMessage);<br />        var launchAppPubId =<br />        proximityDevice.PublishBinaryMessage(<br />            <span style="color: rgb(0, 96, 128);">&quot;LaunchApp:WriteTag&quot;</span>, dataWriter.DetachBuffer(), <br />            proximityWriteTagLaunchAppMessageTransmitCallback);<br />    <br />    <span style="color: rgb(0, 0, 255);">if</span> (launchAppPubId!= -1)<br />    {<br />        proximityDevice.StopPublishingMessage(launchAppPubId);<br />    <span style="color: rgb(0, 128, 0);">// Stop publishing the message on NFC radio</span><br />    }<br /><br />    }<br />}<br /><br /><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> proximityWriteTagLaunchAppMessageTransmitCallback(<br />    Windows.Networking.Proximity.ProximityDevice sender,<br />    <span style="color: rgb(0, 0, 255);">long</span> messageId)<br />{<br />        <span style="color: rgb(0, 128, 0);">// Inform the user that: the message has been successfully written to a tag &amp; the tag no longer needs to be in range</span><br />}<br /></pre>
  </div>
</blockquote>

<br />아주 간단하지 않습니까? 이제 Windows 스토어 앱에서 NFC 태그에 기록하는 방법을 알았습니다. 너무 간단해서 더 이상 설명이 필요 없으므로 다음 시나리오인 태그를 <b>통해</b> 알람을 설정하는 시나리오로 넘어가 보겠습니다. NFC 태그에서 알람을 읽는 과정을 알아보겠습니다. 

<ol>
  <li>민수는 TV 감상 중/전자 메일 작성 중/게임 플레이 중/Windows 사용 중에 월요일 아침 알람을 설정해야겠다고 생각합니다. '수요일 알람' 태그를 PC 가까이 대고 탭합니다. 알람 앱을 실행하라는 알림이 표시됩니다. 이때까지는 Windows가 모든 것을 알아서 처리하므로 앱에서 별도로 할 일이 없습니다.</li>

  <li>민수가 수락하면 앱이 실행되고 오전 7시로 설정된 '알람을 확인하시겠습니까?' 화면이 열립니다. 민수가 알림을 수락하면 활성화 과정에서 Windows가 앞서 말한 것과 동일한 실행 인수를 앱에 전달합니다. 이를 상황별 실행이라고 하며, 앱이 특정 페이지에서 실행되게 하는 것과 동일합니다.</li>

  <li>민수가 알람을 설정합니다. NFC가 없는 일반 알람 설정입니다.</li>
</ol>

<p>NFC 태그에서 실행 인수를 받기는 매우 쉽습니다. 앱은 NFC 태그에서 상황별 실행을 처리해야 합니다. 상황별 실행은 앱을 특정 페이지에서 실행하는 것과 동일합니다. 실행 인수가 오전 7시 알람을 지정하고, 앱이 이를 통해 제안된 알람을 표시합니다. 또한 PC에 앱이 설치되지 않은 경우 Windows 스토어에서 앱을 설치하라는 알림을 자동으로 표시합니다.</p>

<p>다음은 상황별 실행을 구현하는 방법을 보여주는 코드 예제입니다.</p>

<p><strong>JavaScript</strong></p>

<blockquote>
  <div>
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">app.onactivated = <span style="color: rgb(0, 0, 255);">function</span> (args) {<br />    <span style="color: rgb(0, 0, 255);">if</span> (args.detail.kind === activation.ActivationKind.launch) {<br />        <span style="color: rgb(0, 0, 255);">if</span> (args.detail.arguments == <span style="color: rgb(0, 96, 128);">&quot;Windows.Networking.Proximity.PeerFinder:StreamSocket&quot;</span>) {<br />            <span style="color: rgb(0, 128, 0);">//do nothing here.</span><br />        }<br />        <span style="color: rgb(0, 0, 255);">else</span> {<br />    <span style="color: rgb(0, 128, 0);">// Use args.detail.arguments to parse out ’07.00’ string, and display to the user</span><br />        }<br /><br />        args.setPromise(WinJS.UI.processAll());<br />    }<br />}<br /></pre>
  </div>
</blockquote>

<div>
  <br /><strong>C#</strong></div>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">async <span style="color: rgb(0, 0, 255);">protected</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> OnLaunched(LaunchActivatedEventArgs args)<br />        {<br />            <span style="color: rgb(0, 0, 255);">if</span> (args.Arguments == <span style="color: rgb(0, 96, 128);">&quot;Windows.Networking.Proximity.PeerFinder:StreamSocket&quot;</span>)<br />            {<br />                _isLaunchedByTap = <span style="color: rgb(0, 0, 255);">true</span>;<br />            }<br />            <span style="color: rgb(0, 0, 255);">else</span><br />            {<br /><span style="color: rgb(0, 128, 0);">// Use args.Arguments to parse out ’07.00’ string, and display to the user</span><br /><br />            }<br />            Window.Current.Activate();<br />        }<br /></pre>
  </div>
</blockquote>

<p>
  <br />앱이 NFC 태그를 읽고 NFC 태그에 기록하는 것을 지원하기 위해 해야 할 일은 이것이 전부입니다. 최첨단 시나리오치고는 매우 단순합니다. 마지막으로 매우 유용한 오류 처리 방법에 대해 알아보겠습니다.</p>

<h2>오류 처리</h2>

<p>앱에는 자주 발생하는 오류가 몇 가지 있습니다.</p>

<ul>
  <li>탭한 태그가 NDEF 형식이 아닙니다. Windows 8은 태그를 다시 자동으로 NDEF 형식으로 지정하도록 지원하지 않으므로 NDEF 포매터를 다운로드하고 설치해야 합니다.</li>

  <li>탭한 태그가 읽기 전용입니다. 일부 NFC 태그는 읽기 전용으로 잠겨 있을 수 있습니다(기존의 <a href="http://ko.wikipedia.org/wiki/VHS" target="_blank">VHS 테이프</a>와 유사).</li>

  <li>탭한 태그가 너무 작고 데이터가 다 들어가지 않습니다.</li>

  <li>사용자 PC에 NFC가 없습니다. 처음에 말한 것처럼 NFC는 새로운 기술이며 보급률이 점차 높아지고 있습니다. PC가 근접 연결을 지원하는지 확인하려면 ProximityDevice.getDefault() 메서드를 사용하십시오. NFC 라디오가 설치되지 않은 경우 이 메서드에서 NULL을 반환합니다.</li>
</ul>

<h2>재미있고 직관적입니다!</h2>

<p>Windows는 마침내 주요 소비자를 위한 NFC를 도입하여 에코시스템과 잘 만들어진 포괄적인 UX를 갖추게 되었습니다. 이 기술은 앱과 장치 환경을 놀랍도록 직관적으로 만들어줄 많은 잠재력을 가지고 있습니다. 재미있고 직관적입니다. </p>

<p>NFC는 광범위한 분야이므로 다른 멋진 Windows 8 NFC 개발자 환경에 대해서도 다양한 블로그 게시물이 기대됩니다.</p>

<p>NFC 및 근접 연결에 대한 자세한 내용은 아래 리소스를 참조하십시오.</p>

<h2>리소스</h2>

<div align="center">
  <table class="b8table" cellspacing="0" cellpadding="3" width="298" align="center" border="0"><tbody>
      <tr>
        <td valign="top" width="197">
          <p><strong>링크</strong></p>
        </td>

        <td valign="top" width="99">
          <p><strong>형식</strong></p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><u><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465215.aspx" target="_blank">근접 연결을 사용한 개발에 대한 지침</a></u></p>
        </td>

        <td valign="top" width="99">
          <p>문서</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br241250.aspx" target="_blank">근접 API</a></p>
        </td>

        <td valign="top" width="99">
          <p>문서</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh967765.aspx" target="_blank">앱의 근접 연결 테스트 및 문제 해결</a></p>
        </td>

        <td valign="top" width="99">
          <p>문서</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465223.aspx" target="_blank">게시 및 구독(자세한 내용)</a></p>
        </td>

        <td valign="top" width="99">
          <p>문서</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Proximity-Sample-88129731" target="_blank">근접 연결 앱 샘플</a></p>
        </td>

        <td valign="top" width="99">
          <p>샘플</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://social.msdn.microsoft.com/Forums/ko-kr/category/windowsapps" target="_blank">근접 연결에 대해 궁금한 점이 있습니까? 여기에서 물어보십시오.</a></p>
        </td>

        <td valign="top" width="99">
          <p>포럼</p>
        </td>
      </tr>
    </tbody></table>
</div>

<p>감사합니다.</p>

<p>Priya Dandawate 
  <br />장치 및 네트워킹 프로그램 관리자</p>

<p>도움을 주신 분: Max Morris, Marzena Makuta, Mike Loholt, Jake Sabulsky, Vishal Mhatre</p>

<!-- Title: “NFC로 첨단 앱 개발” Tags: “공유, NFC, 근접 무선 통신, 파일 공유, Bluetooth 연결, 데이터 공유” --></div>]]></content:encoded>
    </item>
    <item>
      <title>연결된 Windows 스토어 앱 만들기</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/windows-2</link>
      <pubDate>Wed, 17 Apr 2013 13:57:00 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/04/17/windows-2/</guid>
      <description><![CDATA[우리 곁에는 항상 네트워크에 연결할 수 있는 장치가 있습니다. 최신형 냉장고와 세탁기에도 인터넷과 홈 네트워크에 연결하는 기능이 있습니다. 따라서 최종 사용자들은 앱을 연결하는...]]></description>
      <content:encoded><![CDATA[<div class="postcontent">
<p>우리 곁에는 항상 네트워크에 연결할 수 있는 장치가 있습니다. 최신형 냉장고와 세탁기에도 인터넷과 홈 네트워크에 연결하는 기능이 있습니다. 따라서 최종 사용자들은 앱을 연결하는 기능도 당연히 기대하게 될 것입니다. 이렇게 "연결된 앱"에서는 소셜 미디어, 디지털 미디어, 블로그 등 웹의 최신 콘텐츠를 사용합니다. 연결된 앱 개발은 표준이 되었지만 네트워크 연결 끊김, 네트워크 요금제 비용 또는 성능 문제와 같은 일반적인 문제를 처리해야 하는 숙제가 여전히 남아 있습니다. Windows 8에서는 연결된 앱을 예전보다 훨씬 더 쉽게 작성할 수 있습니다.</p>
<p>이 글에서는 Windows 스토어 앱 사용자에게 빠르고 유연하며 번거롭지 않은 연결된 환경을 제공하는 데 도움이 되는 유용한 팁 몇 가지를 살펴봅니다.</p>
<ul>
<li>시나리오에 적합한 API 선택</li>
<li>적합한 네트워크 기능 선택</li>
<li>네트워크 요금제에 맞게 앱 동작 조정</li>
<li>네트워크 상태 변경에 대응</li>
<li>유연성을 위한 콘텐츠 캐시</li>
</ul>
<p>각 팁을 자세히 알아보겠습니다.</p>
<h2>적합한 API 선택</h2>
<p>집을 지으려면 적절한 공구가 필요합니다. 못을 박으려면 망치가 필요하고, 나무판을 자르려면 톱이 필요하고, 나사를 고정하려면 드라이버가 필요합니다. 마찬가지로 연결된 Windows 스토어 앱을 개발할 때도 적절한 네트워킹 API를 사용해야 합니다. Windows 8에서는 앱이 인터넷이나 사설 네트워크를 통해 다른 컴퓨터나 장치와 통신하는 데 사용할 수 있는 다양한 네트워킹 API를 제공합니다. 따라서 앱에 필요한 네트워킹 기능을 파악하는 것이 첫 단계입니다.</p>
<p>가장 일반적인 네트워킹 시나리오는 웹 사이트에 액세스하여 정보를 검색하거나 저장하는 것입니다. 간단한 예로 웹 사이트를 사용하여 사용자 정보와 게임 점수를 저장하는 게임이 있습니다. 이보다 복잡한 예로는 REST 기반 웹 서비스에 연결하여 웹 서비스에서 제공하는 라이브러리를 통해 정보에 액세스하거나 정보를 저장하는 앱이 있습니다. Windows 8에는 웹 서비스 및 웹 사이트에 연결하는 API가 몇 가지 있습니다. 앱은 이러한 API를 통해 REST를 지원하거나 기본 HTTP 프로토콜 명령(예: GET 및 POST)을 웹 서버로 보내는 웹 서비스에 액세스할 수 있습니다. 웹 액세스의 경우 앱을 개발할 때 사용한 언어에 따라 다른 API를 사용해야 합니다.</p>
<ul>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh453379.aspx" target="_blank">XMLHTTPRequest</a> 및 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br229787.aspx" target="_blank">WinJS.xhr</a> - JavaScript 및 HTML로 작성된 앱</li>
<li><a href="http://msdn.microsoft.com/ko-kr/library/system.net.http.httpclient.aspx" target="_blank">HttpClient</a> - C# 또는 Visual Basic .NET 및 XAML로 작성된 앱</li>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh831163.aspx" target="_blank">XML HTTP Request 2</a>(IXHR2) - C++ 및 XAML로 작성된 앱</li>
</ul>
<p>다음 코드는 REST 기반 웹 서비스를 사용하여 기본 요청 및 응답을 수행하는 방법을 보여줍니다. 이 경우 웹 서비스가 웹 서버에서 ASP.NET 스크립트를 실행할 수 있습니다.</p>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">function</span> makeXHRCall() {<br />    WinJS.xhr({ uri: <span style="color: #006080;">"http://www.microsoft.com/en-us/default.aspx&rdquo; }).done(<br /> function onComplete(result) {<br /> print(result.responseText);<br /> },<br /> function onError(err) {<br /> print("</span>Error: " + err.responseText);<br />        });<br />}</pre>
</div>
</blockquote>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">private</span> async <span style="color: #0000ff;">void</span> MakeHttpCall()<br />{<br />    HttpClient httpClient = <span style="color: #0000ff;">new</span> HttpClient();<br />    <span style="color: #0000ff;">try</span> {<br />        <span style="color: #0000ff;">string</span> response = await httpClient.GetStringAsync(<span style="color: #006080;">"http://www.microsoft.com/en-us/default.aspx"</span>);<br />    }<br />    <span style="color: #0000ff;">catch</span> (Exception) {<br />        <span style="color: #008000;">// Handle exception.</span><br />    }<br />}</pre>
</div>
</blockquote>
<p><strong>WinJS.xhr</strong>에 대한 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh761502.aspx" target="_blank">웹 서비스에 연결(JavaScript 및 HTML을 사용하는 Windows 스토어 앱)</a>을 참조하십시오. <strong>HttpClient</strong>에 대한 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/xaml/hh781239.aspx" target="_blank">빠른 시작: HttpClient를 사용하여 연결(C#/VB/C++ 및 XAML을 사용하는 Windows 스토어 앱)</a> 및 <a href="http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664" target="_blank">HttpClient 샘플</a>을 참조하십시오.</p>
<p>또 다른 일반적인 네트워킹 시나리오는 파일 전송 시간이 오래 걸릴 수 있는 파일을 다운로드하거나 업로드하는 경우입니다. 예를 들어 웹 서비스의 사진이나 사진 앨범을 업로드하거나 다운로드해야 하는 카메라 또는 사진 갤러리 앱이 있습니다. 이 경우 전송 시간이 오래 걸릴 수 있으므로 전송이 완료될 때까지 사용자를 기다리게 하는 것은 부적절합니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.backgroundtransfer.aspx" target="_blank">Windows.Networking.BackgroundTransfer</a> API는 앱을 실행하고 있지 않을 때에도 파일을 다운로드 또는 업로드할 수 있는 기능을 제공합니다. 앱이 포그라운드에서 실행 중이고 포커스가 있을 때 전송을 시작하면 앱을 종료하더라도 백그라운드에서 전송이 계속 진행됩니다.</p>
<p>좀 더 구체적인 다른 시나리오는 신디케이티드 콘텐츠에 액세스하는 경우입니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.web.syndication.aspx" target="_blank">Windows.Web.Syndication</a> API는 RSS 또는 Atom 형식의 피드를 검색할 수 있습니다. 또한 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.web.atompub.aspx" target="_blank">Windows.Web.AtomPub</a> API는 앱이 다양한 AtomPub 형식의 데이터를 게시하도록 지원합니다.</p>
<p>또한 API에서 상위 수준의 특정 네트워킹 프로토콜을 지원하지 않을 경우를 대비해 Windows 런타임에서 멀티캐스팅과 함께 TCP 및 UDP 소켓도 지원합니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.sockets.aspx" target="_blank">Windows.Networking.Sockets</a> API는 다른 상위 계층의 프로토콜을 구현할 수 있는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.sockets.streamsocket.aspx" target="_blank">StreamSocket</a>(TCP) 및 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.sockets.datagramsocket.aspx" target="_blank">DatagramSocket</a>(UDP)을 제공합니다.</p>
<p><strong>참고: </strong>Windows 8에는 <em>WebSocket</em>이라는 새로운 유형의 소켓이 도입되었습니다. WebSocket이란 무엇이고 어떤 앱에 적합한지 자세히 알아보려면 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh761442.aspx" target="_blank">WebSocket을 사용하여 연결</a>을 참조하십시오.</p>
<p>다음 표에는 지원되는 네트워킹 기능의 전체 목록과 추가 정보 링크가 나와 있습니다.</p>
<div align="center">&nbsp;
<table class="b8table" border="0" cellspacing="0" cellpadding="3" align="center">
<tbody>
<tr>
<td valign="top" width="217">
<p align="center"><strong>API</strong><strong></strong></p>
</td>
<td valign="top" width="221">
<p align="center"><strong>기능</strong><strong></strong></p>
</td>
<td valign="top" width="145">
<p align="center"><strong>예</strong><strong></strong></p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.web.syndication.aspx" target="_blank">Windows.Web.Syndication</a></strong><strong></strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.web.atompub.aspx" target="_blank">Windows.Web.AtomPub</a></strong><strong></strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">RSS 또는 Atom 형식의 피드를 버전에 관계없이 검색합니다. OData와 같은 최신 형식에 대한 지원 기능을 쉽게 구현할 수 있는 API입니다. 또한 Windows 런타임은 Atom 컬렉션을 게시하는 데 사용되는 Atom Publishing Protocol도 지원합니다. <br />자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh452973.aspx/" target="_blank">신디케이티드 콘텐츠 액세스 및 관리</a>를 참조하십시오.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Syndication-sample-07ef6b0d" target="_blank">신디케이션 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/AtomPub-sample-c1fcdc8e" target="_blank">AtomPub 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">RSS 판독기</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.backgroundtransfer.aspx" target="_blank">Windows.Networking.BackgroundTransfer</a></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">호출 앱이 포그라운드에 있지 않을 때도 사용 요금을 인식하며 콘텐츠 업로드 및 다운로드가 계속 진행되고 다시 시작할 수 있습니다. 이 API는 HTTP, HTTPS 및 FTP 프로토콜을 사용한 콘텐츠 전송을 지원합니다. <br />자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh452979.aspx" target="_blank">백그라운드로 데이터 전송</a>을 참조하십시오.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Background-Transfer-Sample-d7833f61" target="_blank">백그라운드 전송 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">SkyDrive</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">Xbox 동영상</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh453379.aspx" target="_blank">XMLHTTPRequest</a></strong><strong>(JavaScript</strong><strong>) </strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br229787.aspx" target="_blank">WinJS.xhr</a></strong><strong></strong><strong>(JavaScript)</strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/system.net.http.httpclient.aspx" target="_blank">HttpClient</a></strong><strong></strong><strong>(C#)</strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh831163.aspx">XML HTTP 요청 2</a></strong><strong>(IXHR2)</strong><strong></strong><strong>(C++)</strong><strong></strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">REST 기반 웹 서비스 및 다른 HTTP 기반 프로토콜과 상호 작용합니다. <br />자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh761502.aspx" target="_blank">웹 서비스에 연결</a>을 참조하십시오.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/XHR-handling-navigation-50d03a7a" target="_blank">XHR, 탐색 오류 처리 및 URL 스키마 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664" target="_blank">HttpClient 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">Bing 지도</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.proximity.aspx" target="_blank">Windows.Networking.Proximity</a></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">앱이 아주 가까이 있는 두 장치를 탐지한 후 소켓 API를 사용하여 두 장치 간에 네트워크 통신을 실행할 수 있습니다. <br />자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465229.aspx" target="_blank">근접 연결 및 탭 지원</a>을 참조하십시오.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Proximity-Sample-88129731" target="_blank">근접 연결 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">게임</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">공동 작업 앱</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/library/windows/apps/BR207928" target="_blank">Windows.Storage.Pickers</a></strong><strong></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">원격 파일 공유와 통신합니다. <br />자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh464959.aspx" target="_blank">데이터 및 파일에 액세스</a>를 참조하십시오.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/File-picker-sample-9f294cba" target="_blank">파일 선택기 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">Xbox 음악</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">사진</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.sockets.aspx" target="_blank">Windows.Networking.Sockets</a></strong><strong></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">앞서 언급한 SMTP, MAPI 또는 텔넷과 같은 API에서 지원하지 않는 프로토콜을 사용하는 서비스에 연결하거나 동일한 로컬 네트워크의 다른 장치에 연결합니다. HTTP 프록시를 포함한 웹을 통해 새 서비스에 연결하기 위해 소켓과 유사한 의미 체계(비동기, 양방향)가 필요한 앱에도 사용됩니다. <br />자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh452977.aspx" target="_blank">소켓을 사용하여 연결</a>을 참조하십시오.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/StreamSocket-Sample-8c573931" target="_blank">StreamSocket 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/DatagramSocket-sample-76a7d82b" target="_blank">DatagramSocket 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Connecting-with-WebSockets-643b10ab" target="_blank">WebSocket과 연결 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/site/search?query=controlchanneltrigger&amp;f%5B0%5D.Value=controlchanneltrigger&amp;f%5B0%5D.Type=SearchText&amp;ac=3" target="_blank">ControlChannelTrigger 샘플</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">Lync, 공동 작업 화이트보드 또는 주식 투자</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/library/windows/apps/BR241587" target="_blank">Windows.Storage.ApplicationData</a></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">Windows 8은 사용자 장치 간 특정 응용 프로그램 데이터를 자동으로 전환합니다. 사무실에서 PC를 사용하고 집에서 태블릿을 사용하는 등 둘 이상의 장치를 활용하고 있는 사용자가 여러 장치에 앱을 설치한 경우 응용 프로그램 데이터 로밍이 매우 유용합니다. <br />자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh465094.aspx" target="_blank">응용 프로그램 데이터 로밍에 대한 지침</a>을 참조하십시오.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<li>
<div align="left"><a href="http://go.microsoft.com/fwlink/p/?linkid=231478" target="_blank">응용 프로그램 데이터 샘플</a></div>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<h2>적합한 네트워크 기능 선택</h2>
<p>네트워크 격리는 Windows 8에서 사용하는 앱 보안 모델의 일부입니다. Windows는 네트워크 경계를 적극적으로 검색하여 네트워크 격리에 대한 네트워크 액세스 제한을 적용합니다. 이러한 기능을 올바르게 배포하면 사용자와 앱을 악의적인 공격으로부터 보호하는 데 도움이 됩니다.</p>
<p>네트워크 액세스 범위를 정의하려면 앱이 네트워크 격리 기능을 선언해야 합니다. 이러한 기능을 선언하지 않으면 앱이 네트워크 리소스에 액세스할 수 없습니다. Windows에서 앱에 네트워크 격리를 적용하는 방법에 대해 자세히 알아보려면 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/Hh770532.aspx" target="_blank">네트워크 기능을 설정하는 방법</a>을 참조하십시오.</p>
<p>동일한 장치에서 Windows 스토어 앱과 데스크톱 앱 간에 프로세스 간 통신 메커니즘으로 네트워킹을 사용할 수는 없습니다. 따라서 Windows 스토어 앱에는 IP 루프백 주소를 사용할 수 없습니다. Visual Studio 디버거를 사용할 경우 일부 개발 목적에 한해 IP 루프백 주소를 사용할 수 있는 예외가 적용됩니다. 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/Hh780593.aspx" target="_blank">루프백을 사용하도록 설정하고 네트워크 격리 문제를 해결하는 방법</a>을 참조하십시오.</p>
<p>네트워크 액세스 요청은 두 가지 범주로 분류됩니다.</p>
<ol>
<li><strong>클라이언트에서 시작되는 아웃바운드 요청</strong>: 앱이 클라이언트 역할을 하며, 원격 컴퓨터(일반적으로 서버)에 초기 네트워크 요청을 보내 네트워크 액세스를 시작합니다. 앱이 하나 이상의 요청을 서버에 보내고, 서버가 단일 응답 또는 여러 응답을 다시 보냅니다. 예를 들어 웹 클라이언트 앱에서 웹 서버로의 모든 트래픽이 이 범주에 속합니다.</li>
<li><strong>자발적 인바운드 요청</strong>: 앱이 네트워크 서버 역할을 하고, 원격 컴퓨터에서 들어오는 인바운드 네트워크 요청을 수신합니다. 원격 컴퓨터가 서버 역할을 하는 앱으로 초기 요청을 보내 네트워크 액세스를 시작합니다. 원격 컴퓨터가 하나 이상의 요청을 앱으로 보내고, 앱이 하나 이상의 응답을 원격 컴퓨터로 다시 보냅니다. 예를 들어 미디어 서버 역할을 하는 앱이 이 범주에 속합니다.</li>
</ol>
<p>최소 권한의 원칙을 따라 앱에 필요한 기능만 추가하십시오. 앱은 클라이언트에서 시작하는 아웃바운드 요청만 필요할 수도 있지만 원치 않는 인바운드 요청을 수신해야 할 수도 있습니다. 네트워크를 통해 인증할 때 사용자 자격 증명 및 인증서에 액세스해야 하는 앱도 있습니다.</p>
<p>다음 표에는 연결된 앱에 주로 필요한 네트워크 격리 기능 및 기타 관련 기능이 자세히 설명되어 있습니다. 처음 3개는 연결된 앱에서 사용하는 주요 네트워크 격리 기능입니다. 실제로 연결된 앱은 이러한 기능 중 하나 이상을 사용해야 합니다. 나머지 기능은 일부 연결된 앱에 필요한 추가 기능입니다.</p>
<p>&nbsp;</p>
<div align="center">
<table class="b8table" style="width: 612px;" border="0" cellspacing="0" cellpadding="3" align="center">
<tbody>
<tr>
<td valign="top" width="146">
<p><strong>네트워크 기능</strong></p>
</td>
<td valign="top" width="273">
<p><strong>설명</strong></p>
</td>
<td valign="top" width="191">
<p><strong>앱의 예</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">인터넷(클라이언트)</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">공항이나 카페 등 공공 장소에서의 인터넷과 네트워크에 아웃바운드 액세스합니다. 인터넷 액세스가 필요한 대부분의 앱은 이 기능을 선언해야 합니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">RSS 판독기</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">소셜 네트워크</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">게임</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">인터넷(클라이언트 및 서버)</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">공항이나 카페 등 공공 장소에서의 인터넷과 네트워크에 인바운드 및 아웃바운드 액세스합니다. 중요한 포트로의 인바운드 액세스는 항상 차단되어 있습니다. 이 기능은 인터넷(클라이언트) 기능의 상위 집합입니다. 둘 다 선언할 필요는 없습니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">P2P 앱</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">멀티캐스트를 사용하여 플레이어를 검색하는 멀티 플레이 게임</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">사설 네트워크(클라이언트 및 서버)</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">사용자가 신뢰하는 전용 공간에서의 네트워크에 인바운드 및 아웃바운드 액세스합니다. 일반적으로 집이나 직장의 네트워크입니다. 중요한 포트로의 인바운드 액세스는 항상 차단되어 있습니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">NAS(Network Attached Storage) 콘텐츠에 액세스하는 앱</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">LOB(기간 업무) 앱</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">사설(집 또는 직장) 네트워크에서 멀티캐스트를 사용하여 플레이어를 검색하는 멀티 플레이 게임</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">근접 연결</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">장치와의 필드 근접 통신에 필요합니다. 사용자가 초대장을 보내거나 수락하면 앱이 네트워크에 액세스하여 아주 가까이 있는 장치에 연결할 수 있습니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">아주 가까이 있는 사람과 멀티 플레이 게임<em></em></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">엔터프라이즈 인증</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">도메인 자격 증명이 필요한 기업 인트라넷 리소스에 연결하도록 접근 권한을 줍니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">LOB(기간 업무) 앱</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">공유 사용자 인증서</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">사용자 ID를 인증하기 위해 스마트카드 인증과 같은 소프트웨어 및 하드웨어 인증에 액세스하도록 접근 권한을 줍니다. 런타임에 관련 API가 호출될 때 사용자가 조치를 취해야 합니다(예: 카드 삽입 또는 인증서 선택 등).</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">가상 사설 네트워크 앱</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<li>
<div align="left">LOB(기간 업무) 앱</div>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<p>이 검사 목록을 사용하여 앱에 네트워크 격리를 구성하십시오.</p>
<ul>
<li>앱에 필요한 네트워크 액세스 방향을 결정합니다(클라이언트에서 시작하는 아웃바운드 요청, 자발적 인바운드 요청 또는 둘 다).</li>
<li>앱이 통신할 네트워크 리소스 유형을 결정합니다(집 또는 직장 네트워크의 리소스, 인터넷의 액세스 또는 둘 다).</li>
<li>앱 매니페스트에 필요한 최소 네트워크 격리 기능을 구성합니다. 이 기능은 Microsoft Visual Studio 2012의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br230259.aspx" target="_blank">앱 매니페스트 디자이너</a>를 사용하여 구성하거나 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br211477.aspx" target="_blank">수동으로 추가</a>할 수 있습니다.</li>
<li>문제 해결용으로 제공된 네트워크 격리 도구로 앱을 배포 및 실행하여 테스트해 보십시오. 자세한 내용은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/Hh780593.aspx" target="_blank">루프백을 사용하도록 설정하고 네트워크 격리 문제를 해결하는 방법</a>을 참조하십시오.</li>
</ul>
<p>다음은 Microsoft Visual Studio 2012의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/br230259.aspx" target="_blank">앱 매니페스트 디자이너</a>를 사용하여 네트워크 기능을 사용하도록 설정하는 방법을 보여주는 스크린샷입니다.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/7268.app-manifest-design_47107BBF.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/7268.app_2D00_manifest_2D00_design_5F00_47107BBF.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="Visual Studio 2012에서 앱의 package.appxmanifest의 네트워크 기능 선택" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/7752.app-manifest-design_thumb_5EC7B325.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/7752.app_2D00_manifest_2D00_design_5F00_thumb_5F00_5EC7B325.png" alt="Visual Studio 2012에서 앱의 package.appxmanifest의 네트워크 기능 선택" width="600" height="318" border="0" /></a></p>
<p align="center">Visual Studio 2012에서 앱의 package.appxmanifest의 네트워크 기능 선택<em></em></p>
<h2>네트워크 요금제에 맞게 앱 동작 조정</h2>
<p>가장 최근에 월 데이터 한도에 도달하거나 해외 여행을 한 적이 언제인지 생각해 보십시오. 이러한 경우에는 일반적으로 불필요한 네트워크 요금이 부과되지 않도록 장치와 앱을 주의해서 사용하게 됩니다.</p>
<p>Windows 8에서는 앱이 사용 가능한 네트워크 리소스를 모니터링하고 네트워크 요금제에 맞게 작동하도록 지원함으로써 이러한 사용자 문제를 해결합니다. 앱에 대한 사용자의 신뢰도를 높이려면 연결 요금이 부과될 수 있는 경우에 앱에서 이를 알려줄 수 있게 만들고 요금 발생을 피하거나 줄이도록 동작을 조정해야 합니다.</p>
<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.aspx" target="_blank">Windows.Networking.Connectivity</a> API는 네트워크 연결 유형과 금액에 대한 정보를 제공합니다. 이를 통해 앱에서 네트워크 리소스를 평소대로 사용하는 시기, 아껴서 사용해야 하는 시기, 사용자에게 물어봐야 하는 시기를 결정할 수 있습니다.</p>
<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectionprofile.aspx" target="_blank">ConnectionProfile</a>은 네트워크 연결을 나타냅니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectionprofile.aspx" target="_blank">ConnectionProfile</a>의 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a>를 사용하여 동작을 조정할지 여부를 결정할 수 있습니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a> 속성은 네트워크 연결 유형을 알려줍니다. 이 속성에는 다음 4개 값 중 하나가 올 수 있습니다.</p>
<ul>
<li><strong>Unrestricted</strong> - 무제한 이용 가능한 네트워크 연결입니다. 사용 요금이 부과되지 않고 사용량에 제한이 없습니다.</li>
<li><strong>Fixed</strong> - 특정 한도까지 무제한 이용 가능한 네트워크 연결입니다.</li>
<li><strong>Variable</strong> - 사용량이 바이트 단위로 측정되는 네트워크 연결입니다.</li>
<li><strong>Unknown</strong> - 요금 관련 정보가 없는 네트워크 연결입니다.</li>
</ul>
<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a>의 다른 여러 부울 속성도 자세한 정보를 제공합니다.</p>
<ul>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.roaming.aspx" target="_blank">Roaming</a> - 홈 제공자 외부 네트워크에 연결되어 있습니다.</li>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.approachingdatalimit.aspx" target="_blank">ApproachingDataLimit</a> - 데이터 요금제에 명시된 사용 한도에 도달했습니다.</li>
<li><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.overdatalimit.aspx" target="_blank">OverDataLimit</a>- 데이터 요금제에 명시된 사용 한도를 초과했습니다.</li>
</ul>
<p>이러한 속성이 나타내는 상황을 앱에서 대응하게 하십시오. 연결이 <strong>Roaming</strong>일 때는 네트워크 사용 관련 데이터 요금이 비쌉니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a>이 <strong>Variable</strong>일 때는 사용자가 네트워크에서 주고받은 데이터 양에 대해 요금을 지불하는 네트워크 요금제를 이용하고 있는 것입니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a>이 <strong>Fixed</strong>일 때는 사용자가 이미 데이터 한도를 초과했거나 데이터 한도에 도달한 경우 우려할 만한 원인이 있는 것입니다.</p>
<p>앱은 이러한 정보를 참조하여 다음과 같은 지침을 따라 네트워크 리소스를 가장 잘 사용하는 방법을 결정할 수 있습니다.</p>
<div align="center">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<table class="b8table" border="0" cellspacing="0" cellpadding="3" align="center">
<tbody>
<tr>
<td valign="top" width="97">
<p><strong>동작</strong></p>
</td>
<td valign="top" width="150">
<p><strong>연결 요금</strong></p>
</td>
<td valign="top" width="168">
<p><strong>앱 지침</strong></p>
</td>
<td valign="top" width="175">
<p><strong>예</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="97">
<p align="left">일반</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="150">
<p align="left">&nbsp;</p>
<p align="left"><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a>이 <strong>Unrestricted</strong> 또는 <strong>Unknown</strong>이고 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a>가 <strong>Roaming</strong>이 아닙니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="168">
<p align="left">&nbsp;</p>
<p align="left">앱이 제한 사항을 구현하지 않습니다. 앱이 연결 요금을 <strong>Unlimited</strong>로 처리하고 사용 요금 및 사용량 제한의 적용을 받지 않습니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="175">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">미디어 플레이어 앱이 전체 HD 영화를 재생할 수 있습니다.</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">앱이 제한 사항이나 프롬프트 없이 대용량 파일을 다운로드할 수 있습니다.</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="97">
<p align="left">&nbsp;</p>
<p align="left">절약</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="150">
<p align="left">&nbsp;</p>
<p align="left"><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a>이 <strong>Fixed</strong> 또는 <strong>Variable</strong>이고 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a>가 <strong>Roaming</strong> 또는 <strong>OverDataLimit</strong>이 아닙니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="168">
<p align="left">&nbsp;</p>
<p align="left">앱이 네트워크 사용량을 최적화하기 위한 제한 사항을 구현하여 네트워크 요금제에 대한 작업을 처리합니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="175">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">미디어 플레이어 앱이 저해상도로 영화를 재생할 수 있습니다.</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">앱이 중요하지 않은 다운로드를 연기할 수 있습니다.</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="97">
<p align="left">&nbsp;</p>
<p align="left">옵트인</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="150">
<p align="left">&nbsp;</p>
<p align="left"><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a>가 <strong>Roaming</strong> 또는 <strong>OverDataLimit</strong>입니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="168">
<p align="left">&nbsp;</p>
<p align="left">네트워크 액세스 요금이 요금제 금액을 훨씬 초과한 예외적인 경우를 앱에서 처리합니다.</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="175">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">앱이 사용자에게 네트워크에 액세스할지 묻습니다.</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<li>
<div align="left">앱이 모든 백그라운드 데이터 네트워크 작업을 일시 중지합니다.<em></em></div>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<p>다음은 연결 요금을 확인하고 적절한 앱 동작에 대한 제안 사항을 반환하는 코드입니다.</p>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> CostGuidance = { Normal: 0, Conservative: 1, OptIn: 2 };<br /><span style="color: #008000;">// GetCostGuidance returns an object with a Cost (with value of CostGuidance), </span><br /><span style="color: #008000;">// CostName (a string) and Reason, which says why the cost is what it is.</span><br /><span style="color: #0000ff;">function</span> GetCostGuidance() <br />{<br />    <span style="color: #0000ff;">var</span> connectionCost = Windows.Networking.Connectivity.NetworkInformation.getInternetConnectionProfile().getConnectionCost();<br />    <span style="color: #0000ff;">var</span> networkCostConstants = Windows.Networking.Connectivity.NetworkCostType;<br />    <span style="color: #0000ff;">var</span> Retval = <span style="color: #0000ff;">new</span> Object();<br />    <span style="color: #0000ff;">if</span> (connectionCost.roaming || connectionCost.overDataLimit)<br />    {<br />        Retval.Cost = CostGuidance.OptIn;<br />        Retval.CostName = <span style="color: #006080;">"OptIn"</span>;<br />        Retval.Reason = connectionCost.roaming<br />            ? <span style="color: #006080;">"Connection is roaming; using the connection may result in additional charge."</span><br />            : <span style="color: #006080;">"Connection has exceeded the usage cap limit."</span>;<br />    }<br />    <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (connectionCost.networkCostType == networkCostConstants.<span style="color: #0000ff;">fixed</span><br />        || connectionCost.networkCostType == networkCostConstants.variable)<br />    {<br />        Retval.Cost = CostGuidance.conservative;<br />        Retval.CostName = <span style="color: #006080;">"Conservative"</span>;<br />        Retval.Reason = connectionCost.networkCostType == NetworkCostType.<span style="color: #0000ff;">fixed</span><br />            ? <span style="color: #006080;">"Connection has limited allowed usage."</span><br />            : <span style="color: #006080;">"Connection is charged based on usage. "</span>;<br />    }<br />    <span style="color: #0000ff;">else</span><br />    {<br />        Retval.Cost = CostGuidance.Normal;<br />        Retval.CostName = <span style="color: #006080;">"Normal"</span>;<br />        Retval.Reason = connectionCost.networkCostType == networkCostConstants.unknown<br />            ? <span style="color: #006080;">"Connection is unknown."</span><br />            : <span style="color: #006080;">"Connection cost is unrestricted."</span>;<br />    }<br /><br />    <span style="color: #0000ff;">return</span> Retval;<br />}</pre>
</div>
</blockquote>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">enum</span> NetworkCost { Normal, Conservative, OptIn };<br /><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> CostGuidance<br />{<br />    <span style="color: #0000ff;">public</span> CostGuidance()<br />    {<br />        var connectionCost = NetworkInformation.GetInternetConnectionProfile().GetConnectionCost();<br />        Init(connectionCost);<br />    }<br />    <span style="color: #0000ff;">public</span> NetworkCost Cost { get; <span style="color: #0000ff;">private</span> set; }<br />    <span style="color: #0000ff;">public</span> String Reason { get; <span style="color: #0000ff;">private</span> set; }<br /><br /><br />    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> Init(ConnectionCost connectionCost)<br />    {<br />        <span style="color: #0000ff;">if</span> (connectionCost == <span style="color: #0000ff;">null</span>) <span style="color: #0000ff;">return</span>;<br />        <span style="color: #0000ff;">if</span> (connectionCost.Roaming || connectionCost.OverDataLimit)<br />        {<br />            Cost = NetworkCost.OptIn;<br />            Reason = connectionCost.Roaming<br />                ? <span style="color: #006080;">"Connection is roaming; using the connection may result in additional charge."</span><br />                : <span style="color: #006080;">"Connection has exceeded the usage cap limit."</span>;<br />        }<br />        <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (connectionCost.NetworkCostType == NetworkCostType.Fixed<br />            || connectionCost.NetworkCostType == NetworkCostType.Variable)<br />        {<br />            Cost = NetworkCost.Conservative;<br />            Reason = connectionCost.NetworkCostType == NetworkCostType.Fixed<br />                ? <span style="color: #006080;">"Connection has limited allowed usage."</span><br />                : <span style="color: #006080;">"Connection is charged based on usage. "</span>;<br />        }<br />        <span style="color: #0000ff;">else</span><br />        {<br />            Cost = NetworkCost.Normal;<br />            Reason = connectionCost.NetworkCostType == NetworkCostType.Unknown<br />                ? <span style="color: #006080;">"Connection is unknown."</span><br />                : <span style="color: #006080;">"Connection cost is unrestricted."</span>;<br />        }<br />    }<br />}</pre>
</div>
</blockquote>
<p>네트워크 요금제에 맞게 앱 동작을 조정하는 방법에 대해 자세히 알아보려면 <a href="http://code.msdn.microsoft.com/windowsapps/Network-Information-Sample-63aaa201" target="_blank">네트워크 정보 샘플</a>을 사용하십시오.</p>
<p>사용자는 또한 작업 관리자를 실행하여 각 앱이 사용하고 있는 네트워크 데이터의 양을 볼 수 있습니다. 다음은 예를 보여주는 스크린샷입니다.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5618.task-manager_6C99F920.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5618.task_2D00_manager_5F00_6C99F920.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="앱별 CPU 및 네트워크 사용량을 볼 수 있는 작업 관리자의 앱 기록 탭" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3554.task-manager_thumb_4C7EEC63.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3554.task_2D00_manager_5F00_thumb_5F00_4C7EEC63.png" alt="앱별 CPU 및 네트워크 사용량을 볼 수 있는 작업 관리자의 앱 기록 탭" width="600" height="545" border="0" /></a></p>
<p align="center">앱별 CPU 및 네트워크 사용량을 볼 수 있는 작업 관리자의 앱 기록 탭<em></em></p>
<h2>네트워크 상태 변경에 대응</h2>
<p>모바일 장치의 경우 네트워크 연결이 불안정할 수 있습니다. 모바일 3G 또는 4G 광대역 네트워크는 사용자의 집이나 창고 안에서 연결이 끊길 수 있지만 Wi-Fi는 계속 사용할 수 있습니다. 마찬가지로 사용자가 집 밖으로 나가면 Wi-Fi 연결이 끊길 수 있습니다. 네트워크를 아예 사용할 수 없을 때도 있습니다. Wi-Fi와 모바일 광대역 네트워크의 보급으로 이러한 네트워크 변경은 자주 일어날 수 있습니다.</p>
<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 이벤트는 적용 요금<em></em> 또는 연결 옵션<em></em>이 변경되었다는 의미입니다. 네트워크 상태 변경에 대응하고 변경이 생겼을 때 원활한 고객 경험을 제공하려면 연결된 앱에서 이러한 시나리오 지침을 따라야 합니다.</p>
<h3>오류로 인한 연결 끊김</h3>
<p>대부분의 경우 네트워크 작업을 다시 시도하면 연결을 다시 설정할 수 있습니다. 실패할 경우 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 이벤트를 기다리십시오. 연결이 계속 실패할 경우 50밀리초 값부터 기하급수적으로 늘어나는 백오프 재시도 간격을 앱에 사용하는 것이 좋습니다.</p>
<h3>네트워크 연결 끊김</h3>
<p>사용자에게 연결이 끊겼음을 알려주고 등록 후 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 이벤트를 기다리십시오.</p>
<h3>새 네트워크 사용 가능성</h3>
<p>장치가 여러 네트워크에 연결되는 경우가 있습니다. 예를 들어 사용자가 집 밖에서 모바일 광대역에 연결하고 Windows 8 메시지 앱을 사용하여 친구들과 채팅한 후 집에 돌아와서 무제한 네트워크에 연결하는 경우가 있습니다. Windows 8의 기본 정책은 네트워크 요금제보다는 무제한 네트워크로, 느린 네트워크보다는 빠른 네트워크로 설정하는 것입니다. 그러나 앱에 의해 설정된 기존 연결은 새 네트워크로 자동 전환되지 않습니다. 새 네트워크로 전환할지 여부는 앱만 결정할 수 있으므로 앱이 관여해야 합니다.</p>
<p>예를 들어 동영상 스트림이 거의 완료된 경우 새 네트워크로 전환하는 것은 부적절합니다. 그러나 현재 네트워크가 패킷을 삭제하고 있거나, 너무 느리거나, 스트림이 완료될 때까지 추가 시간이 필요할 경우 새 네트워크로 전환하는 것이 가장 좋습니다.</p>
<p>네트워크 전환이 앱 시나리오에 적절한 것으로 판단되면 새 네트워크가 탐지되었을 때 다음 지침을 따르십시오.</p>
<ol>
<li>기존 네트워크와 새 네트워크의 네트워크 요금을 확인합니다. 새 네트워크로 전환하는 것이 적절할 경우 방금 말한 지침에 따라 새 네트워크 연결을 설정하고 네트워크 작업을 다시 시도하십시오. Windows는 자동으로 네트워크 요금제보다 무제한 네트워크를, 느린 네트워크보다 빠른 네트워크를 우선적으로 선택합니다.</li>
<li>새 네트워크 연결이 완료되면 앱의 새 네트워크 연결을 사용하고 기존의 연결이 있을 경우 이전 네트워크에 대한 원래 네트워크 작업을 취소하십시오.</li>
</ol>
<h3>네트워크 비용 변경</h3>
<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a>이 <strong>Fixed</strong>이고 사용량이 데이터 한도에 도달했거나 초과한 경우 네트워크 요금이 변경될 수 있습니다. <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a>이 <strong>Variable</strong>이 되거나 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.connectioncost.roaming.aspx" target="_blank">Roaming</a>이 true로 변경될 경우에도 네트워크 요금이 변경될 수 있습니다. 이러한 경우 이전 팁의 지침을 따라 앱 동작을 조정하십시오.</p>
<p>다음은 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 이벤트를 사용하여 다양한 네트워크 상태 변경에도 원활한 앱 경험을 제공하는 코드 예입니다. 두 예는 모두 <em>registeredNetworkStatusNotification</em>이라는 전역 부울 변수를 사용하며, 이 변수는 처음에 <strong>false</strong>로 설정되어 있습니다.</p>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #008000;">// Register for NetworkStatusChanged notifications, and display new </span><br /><span style="color: #008000;">// Internet ConnectionProfile info upon network status change.</span><br /><span style="color: #0000ff;">function</span> registerForNetworkStatusChange() {<br />    <span style="color: #0000ff;">try</span> {<br /><br />        <span style="color: #008000;">// Register for network status change notifications.</span><br />        <span style="color: #0000ff;">if</span> (!registeredNetworkStatusNotification) {<br />            <span style="color: #0000ff;">var</span> networkInfo.addEventListener(<span style="color: #006080;">"networkstatuschanged"</span>, onNetworkStatusChange);<br />            registeredNetworkStatusNotification = <span style="color: #0000ff;">true</span>;<br />        }<br />    }<br />    <span style="color: #0000ff;">catch</span> (e) {<br />        print(<span style="color: #006080;">"An unexpected exception occurred: "</span> + e.name + <span style="color: #006080;">": "</span> + e.message);<br />    }<br />}<br /><br /><span style="color: #008000;">// Event handler for NetworkStatusChanged event</span><br /><span style="color: #0000ff;">function</span> onNetworkStatusChange(sender) {<br />    <span style="color: #0000ff;">try</span> {<br />        <span style="color: #008000;">// Get the ConnectionProfile that is currently used to connect </span><br />        <span style="color: #008000;">// to the Internet.</span><br />        <span style="color: #0000ff;">var</span> internetProfile = networkInfo.getInternetConnectionProfile();<br />        <span style="color: #0000ff;">if</span> (internetProfile === <span style="color: #0000ff;">null</span>) {<br />            print(<span style="color: #006080;">"Not connected to Internet\n\r"</span>);<br />        }<br />        <span style="color: #0000ff;">else</span> {<br />            internetProfileInfo += getConnectionProfileInfo(internetProfile) + <span style="color: #006080;">"\n\r"</span>;<br />            print(internetProfileInfo);<br />        }<br />        internetProfileInfo = <span style="color: #006080;">""</span>;<br />    }<br />    <span style="color: #0000ff;">catch</span> (e) {<br />        print(<span style="color: #006080;">"An unexpected exception occurred: "</span> + e.name + <span style="color: #006080;">": "</span> + e.message);<br />    }<br />}</pre>
</div>
</blockquote>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #008000;">// Register for NetworkStatusChanged notifications, and display new </span><br /><span style="color: #008000;">// Internet ConnectionProfile info upon network status change.</span><br /><span style="color: #0000ff;">void</span> NetworkStatusChange()<br />{<br />    <span style="color: #008000;">// Register for network status change notifications.</span><br />    <span style="color: #0000ff;">try</span><br />    {<br />        var networkStatusCallback = <span style="color: #0000ff;">new</span> NetworkStatusChangedEventHandler(OnNetworkStatusChange);<br />        <span style="color: #0000ff;">if</span> (!registeredNetworkStatusNotification)<br />        {<br />            NetworkInformation.NetworkStatusChanged += networkStatusCallback;<br />            registeredNetworkStatusNotification = <span style="color: #0000ff;">true</span>;<br />        }<br />    }<br />    <span style="color: #0000ff;">catch</span> (Exception ex)<br />    {<br />        rootPage.NotifyUser(<span style="color: #006080;">"Unexpected exception occurred: "</span> + ex.ToString(), NotifyType.ErrorMessage);<br />    }<br />}<br /><br /><span style="color: #008000;">// Event handler for NetworkStatusChanged event</span><br />async <span style="color: #0000ff;">void</span> OnNetworkStatusChange(<span style="color: #0000ff;">object</span> sender)<br />{<br />    <span style="color: #0000ff;">try</span><br />    {<br />        <span style="color: #008000;">// Get the ConnectionProfile that is currently used to connect </span><br />        <span style="color: #008000;">// to the Internet </span><br />        ConnectionProfile InternetConnectionProfile = NetworkInformation.GetInternetConnectionProfile();<br /><br />        <span style="color: #0000ff;">if</span> (InternetConnectionProfile == <span style="color: #0000ff;">null</span>)<br />        {<br />            await _cd.RunAsync(CoreDispatcherPriority.Normal, () =&gt;<br />            {<br />                rootPage.NotifyUser(<span style="color: #006080;">"Not connected to Internet\n"</span>, NotifyType.StatusMessage);<br />            });<br />        }<br />        <span style="color: #0000ff;">else</span><br />        {<br />            connectionProfileInfo = GetConnectionProfile(InternetConnectionProfile);<br />            await _cd.RunAsync(CoreDispatcherPriority.Normal, () =&gt;<br />            {<br />                rootPage.NotifyUser(connectionProfileInfo, NotifyType.StatusMessage);<br />            });<br />        }<br />        internetProfileInfo = <span style="color: #006080;">""</span>;<br />    }<br />    <span style="color: #0000ff;">catch</span> (Exception ex)<br />    {<br />        rootPage.NotifyUser(<span style="color: #006080;">"Unexpected exception occurred: "</span> + ex.ToString(), NotifyType.ErrorMessage);<br />    }<br />}</pre>
</div>
</blockquote>
<p><a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 이벤트에 대해 자세히 알아보려면 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/hh700376.aspx" target="_blank">빠른 시작: 연결 이벤트 및 가용성 변경 관리</a>와 <a href="http://code.msdn.microsoft.com/windowsapps/Network-Information-Sample-63aaa201" target="_blank">네트워크 정보 샘플</a>을 참조하십시오.</p>
<h2>유연성을 위한 콘텐츠 캐시</h2>
<p>콘텐츠를 디스크에 캐시하면 앱이 빠르고 유연하게 동작합니다. 예를 들어 RSS 피드 판독기 앱은 이전 세션에서 디스크에 캐시된 피드를 즉시 표시할 수 있습니다. 최신 피드가 공개되자 마자 앱의 콘텐츠가 업데이트됩니다. 캐싱은 앱을 시작하는 즉시 사용자가 콘텐츠를 읽고 앱에서 새 콘텐츠를 가져오도록 해줍니다.</p>
<p>Windows 8에서는 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.storage.aspx" target="_blank">Windows.Storage</a><strong></strong> 네임스페이스에 <a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.storage.applicationdata.aspx" target="_blank">ApplicationData</a><strong></strong> 클래스를 제공합니다. 이 클래스는 앱 데이터 저장소에 대한 액세스를 제공합니다. 이 데이터 저장소는 장치의 로컬에 있거나, 여러 장치 간에 로밍되거나, 임시로 저장된 파일 및 설정으로 구성되어 있습니다.</p>
<p>파일은 일반 파일 형식의 대용량 데이터 집합, 데이터베이스 또는 데이터를 저장하는 데 적합합니다. 파일은 Roaming, Local 또는 Temporary 폴더에 있을 수 있습니다. 자세한 의미는 다음과 같습니다.</p>
<ul>
<li>로밍 파일은 사용자가 연결된 계정을 사용하여 로그인한 여러 시스템 및 장치 간에 동기화됩니다. 파일 로밍은 즉시 이루어지지 않습니다. 시스템에서 여러 요인을 평가하여 데이터를 보낼 시기를 결정합니다. 로밍 데이터 사용량은 할당량(<a href="http://msdn.microsoft.com/ko-kr/library/windows/apps/windows.storage.applicationdata.roamingstoragequota.aspx" target="_blank">RoamingStorageQuota</a><strong></strong> 속성을 통해 사용 가능) 미만으로 유지하십시오. 할당량을 초과할 경우 데이터 로밍이 일시 중단됩니다. 앱이 파일에 데이터를 쓰고 있을 때는 파일을 로밍할 수 없으므로 더 이상 필요 없는 앱의 파일 개체를 닫으십시오.</li>
<li>로컬 파일은 시스템 간에 동기화되지 않습니다. 파일이 처음으로 작성된 시스템에 남아 있습니다.</li>
<li>임시 파일은 사용되지 않을 때 삭제될 수 있습니다. 시스템에서는 임시 파일을 삭제할 시기 또는 삭제할지 여부를 결정할 때 사용 가능한 디스크 용량이나 파일 저장 기간 같은 요소를 고려합니다.</li>
</ul>
<p>다음은 디스크에 콘텐츠를 캐시하는 코드 예입니다. 서버 응답을 캐시하면 앱을 종료했다가 다시 시작하는 즉시 사용자에게 콘텐츠가 표시됩니다. 이러한 예에는 편의상 앱 데이터 저장소에 설정을 기록하는 방법이나 로밍 이벤트에 대응하는 방법 등은 나와 있지 않습니다. 자세한 내용은 <a href="http://code.msdn.microsoft.com/windowsapps/ApplicationData-sample-fb043eb2" target="_blank">응용 프로그램 데이터 샘플</a>을 참조하십시오.</p>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> roamingFolder = Windows.Storage.ApplicationData.current.roamingFolder;<br /><span style="color: #0000ff;">var</span> filename = <span style="color: #006080;">"serverResponse.txt"</span>;<br /><br /><span style="color: #0000ff;">function</span> cacheResponse(strResponse) {<br />    roamingFolder.createFileAsync(filename, Windows.Storage.CreationCollisionOption.replaceExisting)<br />        .done(<span style="color: #0000ff;">function</span> (file) {<br />            <span style="color: #0000ff;">return</span> Windows.Storage.FileIO.writeTextAsync(file, strResponse);<br />        });<br />}<br /><br /><span style="color: #0000ff;">function</span> getCachedResponse() {<br />    roamingFolder.getFileAsync(filename)<br />        .then(<span style="color: #0000ff;">function</span> (file) {<br />            <span style="color: #0000ff;">return</span> Windows.Storage.FileIO.readTextAsync(file);<br />        }).done(<span style="color: #0000ff;">function</span> (response) {<br />            print(response);<br />        }, <span style="color: #0000ff;">function</span> () {<br />            <span style="color: #008000;">// getFileAsync or readTextAsync failed. </span><br />            <span style="color: #008000;">// No cached response.</span><br />        });<br />}</pre>
</div>
</blockquote>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;">MainPage rootPage = MainPage.Current;<br />StorageFolder roamingFolder = <span style="color: #0000ff;">null</span>;<br /><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">string</span> filename = <span style="color: #006080;">"serverResponse.txt"</span>;<br /><br />async <span style="color: #0000ff;">void</span> cacheResponse(<span style="color: #0000ff;">string</span> strResponse)<br />{<br />    StorageFile file = await roamingFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);<br />    await FileIO.WriteTextAsync(file, strResponse);<br />}<br /><br />async <span style="color: #0000ff;">void</span> getCachedResponse()<br />{<br />    <span style="color: #0000ff;">try</span><br />    {<br />        StorageFile file = await roamingFolder.GetFileAsync(filename);<br />        <span style="color: #0000ff;">string</span> response = await FileIO.ReadTextAsync(file);<br />    }<br />    <span style="color: #0000ff;">catch</span> (Exception)<br />    {<br />        <span style="color: #008000;">// getFileAsync or readTextAsync failed.</span><br />        <span style="color: #008000;">// No cached response.</span><br />    }<br />}</pre>
</div>
</blockquote>
<p>앱 데이터 저장소에 대한 자세한 내용을 알아보려면 <a href="http://blogs.msdn.com/b/windowsappdev_ko/archive/2012/07/25/roaming.aspx" target="_blank">앱 데이터 로밍</a> 게시물을 참조하고 <a href="http://code.msdn.microsoft.com/windowsapps/ApplicationData-sample-fb043eb2" target="_blank">응용 프로그램 데이터 샘플</a>을 사용해 보십시오.</p>
<h2>결론</h2>
<p>Windows 스토어 앱을 계획할 때는 이 글의 지침에 따라 유용하고 번거롭지 않으며 연결된 환경을 제공하십시오. 이러한 팁은 개발 프로세스를 단축할 뿐만 아니라 앱의 유연성을 유지하고 사용자 신뢰도를 높여줄 수 있습니다.</p>
<p>- Windows 프로그램 관리자 II, Suhail Khalid</p>
<p>도움을 주신 분: Steven Baker와 Peter Smith</p>
<!-- Title: “연결된 Windows 스토어 앱 만들기” Tags: “Windows 스토어 앱, HttpClient, 연결된 앱, XMLHTTPRequest, IXHR2, XML HTTP Request 2, WinJS.xhr” --></div>]]></content:encoded>
    </item>
    <item>
      <title>//build/ 2013에 지금 등록하십시오!</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_ko/build-2013</link>
      <pubDate>Fri, 05 Apr 2013 10:33:25 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_ko/2013/04/05/build-2013/</guid>
      <description><![CDATA[지난 주, Steve Guggenheimer가 //build/ 2013 개최에 대해 알려 드렸습니다. 이 컨퍼런스는 오는 6월 26일~28일, 샌프란시스코 Moscone...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"><p>지난 주, Steve Guggenheimer가 //build/ 2013 개최에 대해 <a href="http://blogs.technet.com/b/microsoft_blog/archive/2013/03/26/announcing-build-2013.aspx" target="_blank">알려 드렸습니다</a>. 이 컨퍼런스는 오는 6월 26일~28일, 샌프란시스코 Moscone Center에서 개최됩니다. //build/는 Windows 스토어 앱 개발에 대한 자세한 정보를 얻을 수 있는 좋은 기회입니다. 이 컨퍼런스에서는 유용한 프레젠테이션, 데모, 정보 세션 등이 제공됩니다. 대부분의 Microsoft 엔지니어링 팀원이 컨퍼런스에 직접 참여하여 이러한 데모를 제공하고 질문에 답할 예정입니다. Windows 스토어 앱 개발에 대한 가장 유용한 최신 정보를 얻을 수 있는 이 절호의 기회를 놓치지 마십시오. </p>  <p>4월 2일<sup></sup> 화요일부터 등록이 시작되었습니다. <a href="http://www.buildwindows.com" target="_blank">www.buildwindows.com</a>에서 컨퍼런스에 대한 모든 세부 정보를 확인하고 바로 등록할 수 있습니다. </p>  <p>우리 모두 컨퍼런스에서 만나길 바랍니다!</p> <!-- Title: “//build/ 2013에 지금 등록하십시오!” Tags: “build” --></div>]]></content:encoded>
    </item>
  </channel>
</rss>