임베디드 시스템 소프트웨어

Similar documents
다른 JSP 페이지호출 forward() 메서드 - 하나의 JSP 페이지실행이끝나고다른 JSP 페이지를호출할때사용한다. 예 ) <% RequestDispatcher dispatcher = request.getrequestdispatcher(" 실행할페이지.jsp");

PowerPoint Template

Microsoft PowerPoint - 11주차_Android_GoogleMap.ppt [호환 모드]

13 주차 - MDI, Exception, WebBrowser, RichTextBox, AlarmProgram 대림대학 년도 1 학기홍명덕

Microsoft PowerPoint UI-Event.Notification(1.5h).pptx

PowerPoint Presentation


Microsoft PowerPoint - CSharp-10-예외처리

제11장 프로세스와 쓰레드

H3250_Wi-Fi_E.book

PowerPoint Presentation

var answer = confirm(" 확인이나취소를누르세요."); // 확인창은사용자의의사를묻는데사용합니다. if(answer == true){ document.write(" 확인을눌렀습니다."); else { document.write(" 취소를눌렀습니다.");

UI TASK & KEY EVENT

PowerPoint Presentation

쉽게 풀어쓴 C 프로그래밍

학습목표 함수프로시저, 서브프로시저의의미를안다. 매개변수전달방식을학습한다. 함수를이용한프로그래밍한다. 2

슬라이드 1

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

슬라이드 1

API - Notification 메크로를통하여어느특정상황이되었을때 SolidWorks 및보낸경로를통하여알림메시지를보낼수있습니다. 이번기술자료에서는메크로에서이벤트처리기를통하여진행할예정이며, 메크로에서작업을수행하는데유용할것입니다. 알림이벤트핸들러는응용프로그램구현하는데있어

UI TASK & KEY EVENT

제8장 자바 GUI 프로그래밍 II

PowerPoint 프레젠테이션

JAVA PROGRAMMING 실습 08.다형성

Microsoft PowerPoint - web-part03-ch20-XMLHttpRequest기본.pptx

[ 그림 8-1] XML 을이용한옵션메뉴설정방법 <menu> <item 항목ID" android:title=" 항목제목 "/> </menu> public boolean oncreateoptionsmenu(menu menu) { getme

Microsoft PowerPoint - C++ 5 .pptx

Microsoft PowerPoint - java1-lab5-ImageProcessorTestOOP.pptx

C++ Programming

Microsoft PowerPoint - 07-Data Manipulation.pptx

Microsoft PowerPoint - Java7.pptx

제이쿼리 (JQuery) 정의 자바스크립트함수를쉽게사용하기위해만든자바스크립트라이브러리. 웹페이지를즉석에서변경하는기능에특화된자바스크립트라이브러리. 사용법 $( 제이쿼리객체 ) 혹은 $( 엘리먼트 ) 참고 ) $() 이기호를제이쿼리래퍼라고한다. 즉, 제이쿼리를호출하는기호

혼자서일을다하는 JSP. 이젠일을 Servlet 과나눠서한다. JSP와서블릿의표현적인차이 - JSP는 <html> 내에서자바를사용할수있는수단을제공한다. - 서블릿은자바내에서 <html> 을작성할수있는수단을제공한다. - JSP나서블릿으로만웹페이지를작성하면자바와다양한코드가

DLL(Dynamic Linked Library)

Javascript

. 스레드 (Thread) 란? 스레드를설명하기전에이글에서언급되는용어들에대하여알아보도록하겠습니다. - 응용프로그램 ( Application ) 사용자에게특정서비스를제공할목적으로구현된응용프로그램을말합니다. - 컴포넌트 ( component ) 어플리케이션을구성하는기능별요

PowerPoint Presentation

쉽게 풀어쓴 C 프로그래밍

Visual Basic 반복문

PowerPoint Presentation

Chapter #01 Subject

SBR-100S User Manual

윈도우시스템프로그래밍

임베디드 시스템 소프트웨어

adfasdfasfdasfasfadf

JVM 메모리구조

학습목표 메뉴를추가하는방법을이해하고실습할수있다. 프로그램의기본설정 (settings) 을정의하는방법을알고실습할수있다. 대화상자를여는방법을알고실습할수있다. 로그메시지로디버깅하는방법을이해한다. 디버거로디버깅하는방법을이해한다.

( )부록

MVVM 패턴의 이해

(Microsoft PowerPoint - 07\300\345.ppt [\310\243\310\257 \270\360\265\345])

q 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2

Microsoft Word - src.doc

<4D F736F F F696E74202D20C1A63038C0E520C5ACB7A1BDBABFCD20B0B4C3BC4928B0ADC0C729205BC8A3C8AF20B8F0B5E55D>

Microsoft PowerPoint Android-SDK설치.HelloAndroid(1.0h).pptx

Analytics > Log & Crash Search > Unity ios SDK [Deprecated] Log & Crash Unity ios SDK. TOAST SDK. Log & Crash Unity SDK Log & Crash Search. Log & Cras

gnu-lee-oop-kor-lec06-3-chap7

슬라이드 1

gnu-lee-oop-kor-lec10-1-chap10

JAVA PROGRAMMING 실습 05. 객체의 활용

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

9 차시고급위젯다루기 1 학습목표 날짜 / 시간과관련된위젯을배운다. 웹뷰를사용하여간단한웹브라우저기능을구현한다. 매니패스트파일의설정법을배운다. 2 확인해볼까? 3 날짜 / 시간위젯 1) 활동하기 활동개요

17장 클래스와 메소드

금오공대 컴퓨터공학전공 강의자료

JAVA PROGRAMMING 실습 09. 예외처리

ThisJava ..

Design Issues

제 1장 C#의 개요

PowerPoint 프레젠테이션

<4D F736F F F696E74202D20B5A5C0CCC5CDBAA3C0CCBDBA5F3130C1D6C2F75F32C2F7BDC32E >

Microsoft PowerPoint - web-part02-ch15-문서객체조작.pptx

Microsoft PowerPoint - 04-UDP Programming.ppt

윈도우즈프로그래밍(1)

슬라이드 1

API 매뉴얼

PowerPoint Presentation

Microsoft Word - windows server 2003 수동설치_non pro support_.doc

Microsoft PowerPoint 자바-기본문법(Ch2).pptx

쉽게 풀어쓴 C 프로그래밍

iii. Design Tab 을 Click 하여 WindowBuilder 가자동으로생성한 GUI 프로그래밍환경을확인한다.

5장. JSP와 Servlet 프로그래밍을 위한 기본 문법(완성-0421).hwp

어댑터뷰

오버라이딩 (Overriding)

SIGIL 완벽입문

임베디드 시스템 소프트웨어

예제 2) Test.java class A intvar= 10; void method() class B extends A intvar= 20; 1"); void method() 2"); void method1() public class Test 3"); args) A

설계란 무엇인가?

게시판 스팸 실시간 차단 시스템

C# Programming Guide - Types

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

<4D F736F F F696E74202D20C1A63034B0AD202D20C7C1B7B9C0D3B8AEBDBAB3CABFCD20B9ABB9F6C6DBC0D4B7C2>

슬라이드 1

윈도우시스템프로그래밍

JUNIT 실습및발표

Microsoft PowerPoint - additional01.ppt [호환 모드]

유니티 변수-함수.key

2 Application Name: Day10_yhg <LinearLayout android:layout_weight="3" > /> an

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

..,. Job Flow,. PC,.., (Drag & Drop),.,. PC,, Windows PC Mac,.,.,. NAS(Network Attached Storage),,,., Amazon Web Services*.,, (redundancy), SSL.,. * A

Microsoft PowerPoint - 09-CE-5-윈도우 핸들

Transcription:

10 주차강대기

위치서비스를이용하는애플리케이션만들기 가속도센서 웹브라우저

위치서비스를이용하는애플리케이션만들기 프로젝트생성및화면디자인 위치서비스이용을위한환경설정 위치서비스이용을위한변수및이벤트핸들러추가 StatusChanged 이벤트핸들러구현 PositionChanged 이벤트핸들러구현 위치서비스중지코드추가

Global Positioning System (GPS) GPS 소스가어떤것인가에따라해상도가다르지만, 기본적으로 10~25m 정도의오차를가짐 내비게이션시스템은자체알고리즘으로이를보정함 그외에도 Differential GPS 등과같은다양한구조개선이있어왔음 2000 년 5 월이전에는, 미국의군사적보안을이유로, 민간용은일부러 100m 오차를가지게했었음 (Selective Availability) 한국에서위치정보를제공하는사업자에게는국가기관이위치정보내역을요구할수있음 그외의위치정보를얻기위한소스 (source) 로는 무선전화모듈 인근휴대폰기지국 WiFi 핫스팟

윈도우폰은 Assisted GPS (A-GPS) 를사용함 GPS, 와이파이, 무선전화모듈을조합해서사용 단말기의전력소비를최소화하고, 적절한정확도를가진위치정보를제공하기위해, 반드시는아니지만대략다음과같은전략을구사함 위치정보의정확도가높지않아도될경우 정확도가떨어지지만전력소비가상대적으로적은와이파이와무선전화모듈사용 높은정확도가필요한경우 GPS 모듈사용 전반적으로각모듈의상태와운영체제가사용가능한조합모듈을고려 해서사용함 위치정보의정확도에대한설정은애플리케이션에서하고, 이를바탕으로운영체제에게위치서비스요청 질문 ipod touch 에는 GPS 가있는가? ipad Wifi 버전에는 GPS 가있는가? Wi-Fi Positioning System 이란무엇인가?

위치서비스를사용하는애플리케이션은위치정보의정확성과전력소비량사이의균형을유지하는것이중요함높은정확도의위치정보가필요하기전까지는낮은정확도의위치정보를요청함위치정보가더이상필요하지않을시에는위치서비스의사용을중지시킴위치정보서비스사용시두가지고려사항 MovementThreshold 속성값 어느정도의최소거리 ( 미터단위 ) 로위치가변경될것인지를인지하는것에대한임계값 위치가변경되었음을알리는이벤트를최소어느정도거리가변경되었을때발생시킬것인가를정함 Microsoft는이값을적어도 20m 이상설정할것을권장함 이렇게하면, 노이즈로발생되는쓰레기값의영향을덜받고, 전력소비도낮추는데도움이됨 유효하지않은위치정보의처리방안 여러개의모듈로높은신뢰성의위치정보를제공하지만, 언제든지유효하지않은위치정보를줄수있음

프로젝트생성 화면디자인 비하인드코드 ( 코드비하인드, 코드숨김 ) 에서건드려야할 UI 개체들의 Name 속성값을적절히변경함 버튼의 Click 이벤트에대한이벤트핸들러구성

<!--ContentPanel - 여기에추가내용을배치합니다.--> <Grid x:name="contentpanel" Grid.Row="1" Margin="12,0,12,0"> <TextBlock Height="30" HorizontalAlignment="Left" Margin="45,38,0,0" Name="textBlock1" Text=" 상태 " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="45,74,0,0" Name="Status_TextBlock" Text=" 위치서비스꺼짐 " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="45,110,0,0" Name="textBlock2" Text=" 위도 " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="45,146,0,0" Name="Latitude_TextBlock" Text="0.000" VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="45,182,0,0" Name="textBlock3" Text=" 경도 " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="45,218,0,0" Name="Longitude_TextBlock" Text="0.000" VerticalAlignment="Top" /> <Button Content=" 위치서비스시작 " Height="72" HorizontalAlignment="Left" Margin="88,282,0,0" Name="StartLocationService_Button" VerticalAlignment="Top" Width="255" Click="OnStartLocationService" /> <Button Content=" 위치서비스중지 " Height="72" HorizontalAlignment="Left" Margin="88,0,0,175" Name="StopLocationService_Button" VerticalAlignment="Bottom" Width="255" Click="OnStopLocationService" /> </Grid>

위치서비스에관련된클래스들의사용을위해서는 System.Device.dll 파일을참조할수있어야함 참조추가 (Add Reference) 를통해, 레퍼런스파일을추가함 System.Device.Location 네임스페이스 ( 이름공간 ) 을비하인드코드에추가함 using System.Device.Location;

위치서비스는 GeoCoordinateWatcher 개체에의해접근할수있음 GeoCoordinateWatcher 변수추가 위치서비스를시작하고끝내는두개의버튼에대한핸들러추가 위치서비스시작 (OnStartLocationService) GeoCoordinateWatcher 변수초기화 기본은낮은정확도 특정한정확도 ( 예를들어높은정확도 ) 를요구하는경우 GeoPositionAccuracy 개체의열거값을패러미터로하는생성자를사용하여초기화함 MovementThreshold 속성설정 위치서비스중지 (OnStopLocationService) GeoCoordinateWatcher 클래스의이벤트에대한핸들러추가 StatusChanged, PositionChanged

using System.Device.Location; namespace PhoneApp1 public partial class MainPage : PhoneApplicationPage private GeoCoordinateWatcher geocoordinatewatcher; // 생성자 public MainPage() InitializeComponent(); private void OnStartLocationService(object sender, RoutedEventArgs e) this.geocoordinatewatcher = new GeoCoordinateWatcher(); this.geocoordinatewatcher.movementthreshold = 20;

GeoCoordinateWatcher 클래스로위치서비스를이용하기위해두개의이벤트핸들러를구성함 StatusChanged 와 PositionChanged StatusChanged 위치서비스의상태가변경되면발생함 PositionChanged 위치서비스의 MovementThreshold 속성값을기반으로위치가변경되었음을인지하면발생함

private void OnStartLocationService(object sender, RoutedEventArgs e) this.geocoordinatewatcher = new GeoCoordinateWatcher(); this.geocoordinatewatcher.movementthreshold = 20; this.geocoordinatewatcher.statuschanged += new EventHandler<GeoPositionStatusChangedEventArgs>(geoCoordinateWatcher_StatusChanged); this.geocoordinatewatcher.positionchanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoCoordinateWatcher_Positi onchanged); this.geocoordinatewatcher.start(); void geocoordinatewatcher_positionchanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) throw new NotImplementedException(); void geocoordinatewatcher_statuschanged(object sender, GeoPositionStatusChangedEventArgs e) throw new NotImplementedException();

EventHandler<GeoPositionStatusChangedEventArgs>(geoC oordinatewatcher_statuschanged) 이벤트핸들러가다양한이벤트아규먼트 (EventArgs) 들을처리할수있다는것을알수있음 GeoPositionStatusChangedEventArgs 를내부데이터타입으로하는 이벤트핸들러를구성함 EventHandler<GeoPositionChangedEventArgs<GeoCoordin ate>>(geocoordinatewatcher_positionchanged) GeoCoordinate 을내부데이터로하는 GeoPositionChangedEventArgs 를내부데이터로하는이벤트핸들러를구성함 이코드들을이해하기위해서는 C# 의제네릭을제대로알아야함 C# 텍스트북을보는것이제일좋으며, 관련링크중몇개는다음과같음 http://msdn.microsoft.com/kokr/library/512aeb7t(v=vs.80).aspx http://msdn.microsoft.com/kokr/library/ms379564(v=vs.80).aspx

위치서비스시작 위한버튼을눌렀을때, 위치서비스를시작하도록비동기화방식의 Start 메소드를호출 대부분의함수호출은동기화방식 느린 I/O 가동반되는경우, 비동기화방식의메소드가제공됨 파일 I/O, 멀티미디어파일재생, 네트워크 I/O, 위치서비스연결, 등등 따라서, Start 메소드는위치정보의수신가능여부와상관없이바로리턴됨 동기화방식의메소드가필요할경우, TryStart 메소드를사용함 ( 타임아웃값도설정할수있음 )

private void OnStartLocationService(object sender, RoutedEventArgs e) this.geocoordinatewatcher = new GeoCoordinateWatcher(); this.geocoordinatewatcher.movementthreshold = 20; this.geocoordinatewatcher.statuschanged += new EventHandler<GeoPositionStatusChangedEventArgs>(geoCoordinateWatcher_StatusChanged); this.geocoordinatewatcher.positionchanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoCoordinateWatcher_PositionChanged); this.geocoordinatewatcher.trystart(true, TimeSpan.FromMilliseconds(60000)); if (GeoPositionStatus.Ready == this.geocoordinatewatcher.status) // 위치데이터를사용가능함 else // 연결타임아웃. 위치데이터사용불가능 void geocoordinatewatcher_statuschanged(object sender, GeoPositionStatusChangedEventArgs e) throw new NotImplementedException(); void geocoordinatewatcher_positionchanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) throw new NotImplementedException();

위치서비스를담당하는 GeoCoordinateWatcher 의변경사항을메인쓰레드에서받은후, UI 쓰레드를통해화면의내용을변경하도록하는경우 메인쓰레드에서직접 UI 의업데이트를시도하는경우, UI 쓰레드와충돌하여, invalid cross-thread access exception 이발생함. 또한 synchronized 를통해메인쓰레드에서직접업데이트를하려하면메인쓰레드의다른작업들은기다려야하는블록현상이일어남 UI 쓰레드가특정코드를실행하게하려면, System.Windows.Deployment 라는네임스페이스에포함된 Dispatcher 클래스의 BeginInvoke 메소드를사용함 이때특정코드를넣기위해람다식을사용함. 다시말하지만, System.Windows.Deployment 라는네임스페이스에포함된 Dispatcher 클래스의 BeginInvoke 메소드를사용하지않고직접업데이트를시도하는경우, invalid cross-thread access exception 이발생함. 또한 위치서비스시작 버튼과 위치서비스중지 버튼을활성화 / 비활성화하는부분도추가함

void geocoordinatewatcher_statuschanged(object sender, GeoPositionStatusChangedEventArgs e) Deployment.Current.Dispatcher.BeginInvoke(() => geocoordinatewatcherstatuschanged(e)); private object geocoordinatewatcherstatuschanged(geopositionstat uschangedeventargs e) throw new NotImplementedException();

Deployment.Current.Dispatcher.BeginInvoke(() => geocoordinatewatcherstatuschanged(e)); 이한줄의코드를이해하려면 C# 의람다식 (Lambda Expression) 과델리게이트 (Delegate, 대리자, 위임자 ) 을이해하고있어야함 역시완벽한이해를위해서는기본적으로 C# 의텍스트북을정독해야함 참고 URL 들 람다식 http://msdn.microsoft.com/ko-kr/library/bb397687.aspx 델리게이트 ( 대리자 ) http://msdn.microsoft.com/kokr/library/ms173171(v=vs.80).aspx

실제상태체크는 geocoordinatewatcherstatuschanged 메소드에서이루어짐상태는 GeoPositionStatusChangedEventArgs 개체로반환되는패러미터인 e 의 Status 속성값을체크해서알수있음 Disabled 위치서비스가비활성화되어있거나지원불가능한상태 Ready 위치서비스가동작하고위치정보를수신하고있는상태 NoData 위치서비스가초기화되고있는상태 Initializing 위치서비스가동작하고있지만, 위치정보는수신 하지못하는상태 이상태에맞추어 Status_TextBlock 값을바꾸어주고, 또한각상황에맞게위치서비스에대한버튼들을활성화또는비활성화시킴

void geocoordinatewatcher_statuschanged(object sender, GeoPositionStatusChangedEventArgs e) Deployment.Current.Dispatcher.BeginInvoke(() => geocoordinatewatcherstatuschanged(e)); private void geocoordinatewatcherstatuschanged(geopositionstatuschangedeventargs e) switch (e.status) case GeoPositionStatus.Disabled: this.status_textblock.text = " 위치서비스가사용불가능하거나, 지원되지않음 "; break; case GeoPositionStatus.Initializing: this.status_textblock.text = " 위치서비스초기화 "; this.startlocationservice_button.isenabled = false; break; case GeoPositionStatus.NoData: this.status_textblock.text = " 위치데이터사용불가능 "; this.stoplocationservice_button.isenabled = true; break; case GeoPositionStatus.Ready: this.status_textblock.text = " 위치데이터를받고있음 "; this.stoplocationservice_button.isenabled = true; break;

앞의 StatusChanged 와비슷한방법으로 UI 쓰레드를생성하여이벤트를처리함 GeoPositionChangedEventArgs<GeoCoordinat e> 개체패러미터를통해받게되는위치정보를 Latitude_TextBlock 과 Longitude_TextBlock 에설정해줌 Latitude 위도, Longitude - 경도

void geocoordinatewatcher_positionchanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) Deployment.Current.Dispatcher.BeginInvoke(() => geocoordinatewatcherpositionchanged(e)); private void geocoordinatewatcherpositionchanged(geopositionchangedeven targs<geocoordinate> e) this.latitude_textblock.text = e.position.location.latitude.tostring("0.000"); this.longitude_textblock.text = e.position.location.longitude.tostring("0.000");

Stop 메소드로위치서비스중지 Status_TextBlock 설정 Latitude_TextBlock 설정 Longitude_TextBlock 설정 위치서비스시작버튼활성화 this.startlocationservice_button.isenabled = true; 위치서비스중지버튼비활성화 this.stoplocationservice_button.isenabled = false;

private void OnStopLocationService(object sender, RoutedEventArgs e) this.geocoordinatewatcher.stop(); this.status_textblock.text = " 위치서비스꺼짐 "; this.latitude_textblock.text = "0.000"; this.longitude_textblock.text = "0.000"; this.startlocationservice_button.isenabled = true; this.stoplocationservice_button.isenabled = false;

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using System.Device.Location; namespace PhoneApp1 public partial class MainPage : PhoneApplicationPage private GeoCoordinateWatcher geocoordinatewatcher; // 생성자 public MainPage() InitializeComponent(); private void OnStartLocationService(object sender, RoutedEventArgs e) this.geocoordinatewatcher = new GeoCoordinateWatcher(); this.geocoordinatewatcher.movementthreshold = 20; this.geocoordinatewatcher.statuschanged += new EventHandler<GeoPositionStatusChangedEventArgs>(geoCoordinateWatcher_StatusChanged); this.geocoordinatewatcher.positionchanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoCoordinateWatcher_PositionChanged); this.geocoordinatewatcher.start();

void geocoordinatewatcher_statuschanged(object sender, GeoPositionStatusChangedEventArgs e) Deployment.Current.Dispatcher.BeginInvoke(() => geocoordinatewatcherstatuschanged(e)); private void geocoordinatewatcherstatuschanged(geopositionstatuschangedeventargs e) switch (e.status) case GeoPositionStatus.Disabled: this.status_textblock.text = " 위치서비스가사용불가능하거나, 지원되지않음 "; break; case GeoPositionStatus.Initializing: this.status_textblock.text = " 위치서비스초기화 "; this.startlocationservice_button.isenabled = false; break; case GeoPositionStatus.NoData: this.status_textblock.text = " 위치데이터사용불가능 "; this.stoplocationservice_button.isenabled = true; break; case GeoPositionStatus.Ready: this.status_textblock.text = " 위치데이터를받고있음 "; this.stoplocationservice_button.isenabled = true; break;

void geocoordinatewatcher_positionchanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) Deployment.Current.Dispatcher.BeginInvoke(() => geocoordinatewatcherpositionchanged(e)); private void geocoordinatewatcherpositionchanged(geopositionchangedeventargs<geocoordinate> e) this.latitude_textblock.text = e.position.location.latitude.tostring("0.000"); this.longitude_textblock.text = e.position.location.longitude.tostring("0.000"); private void OnStopLocationService(object sender, RoutedEventArgs e) this.geocoordinatewatcher.stop(); this.status_textblock.text = " 위치서비스꺼짐 "; this.latitude_textblock.text = "0.000"; this.longitude_textblock.text = "0.000"; this.startlocationservice_button.isenabled = true; this.stoplocationservice_button.isenabled = false;

윈도우폰 SDK 7.0 GPS 센서를테스트할방법이기본적으로지원되지않으므로, 3 rd party Window Phone GPS Emulator 사용함 예 : http://windowsteamblog.com/windows_phone/b/wpdev/ar chive/2011/01/28/windows-phone-gps-emulator.aspx 윈도우폰 SDK 7.1 기본적으로가속도센서, 위치센서를위한 ( 그다지좋지않은 ) 지도서비스, 스크린샷기능이추가됨 동서대학교의 ( 위도, 경도 ) = (35.1462115, 129.0084867) maps.naver.com 이나 maps.google.com 에서쉽게알아낼수있음

LocationManager 개체를 getsystemservice() 를통해서얻음 private LocationManager mgr mgr = (LocationManager) getsystemservice(location_service) 안드로이드에뮬레이터는기본적으로가짜 (Fake) GPS 제공자를사용함 Dalvik Debug Monitor Service (DDMS) 를이용하는이클립스의에뮬레이터컨트롤에서위도와경도입력 창 > 보기뷰 > 기타 > 안드로이드 > 에뮬레이터컨트롤 Window > Show View > Other > Android > Emulator Control 안드로이드에뮬레이터컨솔 (telnet 127.1 5554) 에뮬레이터컨트롤에서 Google Earth 에서출력된 KML 파일입력 DDMS 를이용하는외부프로그램사용

앞의슬라이드에서소개된 3 rd party Window Phone GPS Emulator 를사용하여 GPS 를에뮬레이트해보자. 앞에서처럼단순히위도와경도를출력하는게아니라, Window Phone 에뮬레이터에지도정보를같이표시할수있다. 이를위해서는어떤방법들이있는가? 보다구체적으로, 빙맵, Microsoft Research Maps (MSR Maps) Service 또는구글맵이나심지어는네이버맵, 다음맵, 야후맵을이용하는방법을조사및연구해보자. 서버에가입한사용자들의닉네임과현재위치를기록하고, 자신이있는위치에서일정한반경에있는사람들의닉네임을출력하는프로그램을설계해보자.

앞의코드들을완전히이해하려면, C# 의다음특징들을이해해야한다. 이를위해이들에대해조사해보고예제를실행해보자. 제네릭 (Generic) 델리게이트 ( 대리자, Delegate) 람다식 (Lambda Expression) 무명메소드 (Anonymous Method) 앞의 StatusChanged 이벤트핸들러코드는새로운 UI 쓰레드를생성한후, 델리게이트로람다식을사용하여다른메소드를호출한경우이다. 이를다음과같은경우로바꿔어보라. 새로운 UI 쓰레드를생성하지않고해당메소드에서바로사용한경우 새로운 UI 쓰레드를생성하지않고해당메소드에서다른메소드를호출한경우 새로운 UI 쓰레드를생성하지않고해당메소드에서델리게이트로무명메소드를호출한경우 새로운 UI 쓰레드를생성하지않고해당메소드에서델리게이트로람다식을호출한경우 ( 다른메소드를사용한경우와그렇지않은경우 ) 새로운 UI 쓰레드를생성하고, 델리게이트로다른메소드를호출한경우 새로운 UI 쓰레드를생성해서, 델리게이트로무명메소드를호출한경우 새로운 UI 쓰레드를생성해서, 델리게이트로람다식을사용한경우 ( 다른메소드호출안함 )

가속도센서 가속도센서를이용하는애플리케이션만들기 프로젝트생성및화면디자인 가속도센서를이용하기위한환경설정 가속도센서를이용하기위한변수및이벤트핸들러추가 가속도센서의시작 / 중지버튼에대한이벤트핸들러구현 ReadingChanged 이벤트핸들러구현

가속도센서 물체의가속도, 진동, 충격등의동적힘을측정하는센서 단말기를기울거나흔들때, 어느정도기울어졌는지와흔드는속도를인식할수있음

윈도우폰애플리케이션프로젝트생성 이제어떻게가속도센서값을받아오는지를알아보도록함 이를위해우선비하인드코드에서읽어들이는 UI 개체에대해서는이름을적절히변경함

<Grid x:name="contentpanel" Grid.Row="1" Margin="12,0,12,0"> <TextBlock Height="30" HorizontalAlignment="Left" Margin="21,19,0,0" Name="textBlock1" Text=" 상태 : " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="21,55,0,0" Name="textBlock3" Text="X : " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="22,91,0,0" Name="textBlock5" Text="Y : " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="22,127,0,0" Name="textBlock7" Text="Z : " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="84,19,0,0" Name="Status_TextBlock" Text=" 가속도센서정지 " VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="60,55,0,0" Name="xAxis_TextBlock" Text="0.00" VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="60,91,0,0" Name="yAxis_TextBlock" Text="0.00" VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="60,127,0,0" Name="zAxis_TextBlock" Text="0.00" VerticalAlignment="Top" /> <Button Content=" 가속도센서시작 " Height="72" HorizontalAlignment="Left" Margin="22,198,0,0" Name="Start_Button" VerticalAlignment="Top" Width="416" Click="OnStartAccelerometer" /> <Button Content=" 가속도센서정지 " Height="72" HorizontalAlignment="Left" Margin="22,276,0,0" Name="Stop_Button" VerticalAlignment="Top" Width="416" Click="OnStopAccelerometer" /> </Grid>

가속도센서에관련된클래스는 Microsoft.Devices.Sensors.dll 파일을참조하도록해야함 참조추가 (Add Reference) 를통해해당파일을추가함 Microsoft.Devices.Sensors 네임스페이스 (Name Space, 이름공간 ) 추가 using Microsoft.Devices.Sensors;

가속도센서는 Accelerometer 개체를통해접근하므로, 이를위한변수를선언함 가속도센서를위한이벤트핸들러 윈도우폰 SDK 7.0 ReadingChanged 이벤트 윈도우폰 SDK 7.1 CurrentValueChanged 이벤트 ReadingChanged 이벤트는더이상사용안함 (deprecated 중요도가떨어져더이상사용되지않고앞으로는사라지게될 이라는의미임 )

using Microsoft.Devices.Sensors; public partial class MainPage : PhoneApplicationPage private Accelerometer accelerometer;

// 생성자 public MainPage() InitializeComponent(); this.accelerometer = new Accelerometer(); this.accelerometer.currentvaluechanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading> >(accelerometer_currentvaluechanged); void accelerometer_currentvaluechanged(object sender, SensorReadingEventArgs<AccelerometerReading> e) throw new NotImplementedException();

// 생성자 public MainPage() InitializeComponent(); this.accelerometer = new Accelerometer(); this.accelerometer.readingchanged += new EventHandler<AccelerometerReadingEventArgs>(accelero meter_readingchanged); void accelerometer_readingchanged(object sender, AccelerometerReadingEventArgs e) throw new NotImplementedException();

가속도센서의시작및중지버튼에대한이벤트핸들러구현 가속도센서시작 OnStartAccelerometer 가속도센서중지 - OnStopAccelerometer

private void OnStartAccelerometer(object sender, RoutedEventArgs e) this.accelerometer.start(); this.status_textblock.text = " 가속도센서시작 "; this.start_button.isenabled = false; this.stop_button.isenabled = true;

private void OnStopAccelerometer(object sender, RoutedEventArgs e) this.accelerometer.stop(); this.status_textblock.text = " 가속도센서정지 "; this.xaxis_textblock.text = "0.00"; this.yaxis_textblock.text = "0.00"; this.zaxis_textblock.text = "0.00"; this.start_button.isenabled = true; this.stop_button.isenabled = false;

CurrentValueChanged 이벤트핸들러는두개의패러미터를가짐 object sender SensorReadingEventArgs<AccelerometerReading> e 이두개의패러미터들중두번째인 SensorReadingEventArgs<AccelerometerReading > 개체인 e 의 e.sensorreading.acceleration 은 Microsoft.Xna.Framework.Vector3 형식을사용함 이를위해 Microsoft.Xna.Framework 참조추가 또한 bstopped 라는상태를나타내기위한멤버변수추가

this.accelerometer.state 는 SensorState 개체임 SensorState 값 Disabled 센서를사용하지않도록설정됨, 비활성화된상태 Initializing 센서가사용가능한상태이며, 동작하여초기화중 NoData 센서가데이터를가져올수없음, 센서가동작하고있으나, 센서정보를얻을수없음 NoPermissions 호출한프로그램에게센서의데이터에액세스할수있는권한이없음 NotSupported 센서하드웨어를사용할수없음 Ready 센서가사용가능한상태이며, 센서정보를읽어데이터를처리중

void accelerometer_currentvaluechanged(object sender, SensorReadingEventArgs<AccelerometerReading> e) Deployment.Current.Dispatcher.BeginInvoke(()=>accelerometerCurrentValueChan ged(e)); private void accelerometercurrentvaluechanged(sensorreadingeventargs<accelerometerreadi ng> e) if (bstopped) return; this.status_textblock.text = this.accelerometer.state.tostring(); this.xaxis_textblock.text = e.sensorreading.acceleration.x.tostring("0.00 ); this.yaxis_textblock.text = e.sensorreading.acceleration.y.tostring("0.00 ); this.zaxis_textblock.text = e.sensorreading.acceleration.z.tostring("0.00 );

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using Microsoft.Devices.Sensors; namespace PhoneApp2 public partial class MainPage : PhoneApplicationPage private Accelerometer accelerometer; private bool bstopped; // 생성자 public MainPage() InitializeComponent(); this.accelerometer = new Accelerometer(); this.accelerometer.currentvaluechanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(accelerometer_CurrentValueChanged); this.bstopped = true;

private void OnStartAccelerometer(object sender, RoutedEventArgs e) this.accelerometer.start(); this.status_textblock.text = " 가속도센서시작 "; this.start_button.isenabled = false; this.stop_button.isenabled = true; this.bstopped = false; private void OnStopAccelerometer(object sender, RoutedEventArgs e) this.bstopped = true; this.accelerometer.stop(); this.status_textblock.text = " 가속도센서정지 "; this.xaxis_textblock.text = "0.00"; this.yaxis_textblock.text = "0.00"; this.zaxis_textblock.text = "0.00"; this.start_button.isenabled = true; this.stop_button.isenabled = false;

void accelerometer_currentvaluechanged(object sender, SensorReadingEventArgs<AccelerometerReading> e) Deployment.Current.Dispatcher.BeginInvoke(()=>accelerometerCurrentValueChanged(e)); private void accelerometercurrentvaluechanged(sensorreadingeventargs<accelerometerreading> e) if (bstopped) return; this.status_textblock.text = this.accelerometer.state.tostring(); this.xaxis_textblock.text = e.sensorreading.acceleration.x.tostring("0.00 ); this.yaxis_textblock.text = e.sensorreading.acceleration.y.tostring("0.00 ); this.zaxis_textblock.text = e.sensorreading.acceleration.z.tostring("0.00 );

ReadingChanged 이벤트핸들어의패러미터인 AccelerometerReadingEventArgs 개체의 X,Y,Z 속성값을읽어들여출력함

void accelerometer_readingchanged(object sender, AccelerometerReadingEventArgs e) Deployment.Current.Dispatcher.BeginInvoke(() => accelerometerreadingchanged(e)); private void accelerometerreadingchanged(accelerometerreadingeventargs e) if (bstopped) return; this.status_textblock.text = this.accelerometer.state.tostring(); this.xaxis_textblock.text = e.x.tostring("0.00"); this.yaxis_textblock.text = e.y.tostring("0.00"); this.zaxis_textblock.text = e.z.tostring("0.00");

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using Microsoft.Devices.Sensors; namespace PhoneApp3 public partial class MainPage : PhoneApplicationPage private Accelerometer accelerometer; private bool bstopped; // 생성자 public MainPage() InitializeComponent(); this.accelerometer = new Accelerometer(); this.accelerometer.readingchanged += new EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged); this.bstopped = true;

private void OnStartAccelerometer(object sender, RoutedEventArgs e) this.accelerometer.start(); this.status_textblock.text = " 가속도센서시작 "; this.start_button.isenabled = false; this.stop_button.isenabled = true; this.bstopped = false; private void OnStopAccelerometer(object sender, RoutedEventArgs e) this.bstopped = true; this.accelerometer.stop(); this.status_textblock.text = " 가속도센서정지 "; this.xaxis_textblock.text = "0.00"; this.yaxis_textblock.text = "0.00"; this.zaxis_textblock.text = "0.00"; this.start_button.isenabled = true; this.stop_button.isenabled = false;

void accelerometer_readingchanged(object sender, AccelerometerReadingEventArgs e) Deployment.Current.Dispatcher.BeginInvoke(() => accelerometerreadingchanged(e)); private void accelerometerreadingchanged(accelerometerreadingeventargs e) if (bstopped) return; this.status_textblock.text = this.accelerometer.state.tostring(); this.xaxis_textblock.text = e.x.tostring("0.00"); this.yaxis_textblock.text = e.y.tostring("0.00"); this.zaxis_textblock.text = e.z.tostring("0.00");

SensorManager 개체를사용하며, getsystemservice() 메서드를사용해서얻음 private SensorManager mgr; mgr = (SensorManager) getsystemservice(sensor_service); 에뮬레이터의경우, www.openintents.org 에서대체센서 API를제공하는데, 이는코드구글프로젝트의일부임 OpenIntents Sensor Simulator http://code.google.com/p/openintents/wiki/sensorsimulat or www.openintents.org 의 Sensor Simulator 를다운받아에뮬레이터와연결하면, 시뮬레이터에서는가상폰의이미지를보여주고, 마우스로움직이게해주며, 그움직임을에뮬레이터에있는안드로이드프로그램에넘김

가속도센서를이용하여, 공이굴러가는효과를내는프로그램을만들어보자. 프로그램을실행하면화면에공이하나있고, 윈도우폰을기울이면그방향으로공이굴러가도록한다. 이와비슷한아이폰용게임으로는 Labyrinth 가있음 휴대폰을바닥이평평한지를검사할때사용하는수준기 ( 수평기, Spirit level, Bubble level) 로사용할수있는프로그램을작성해보자. 이러한아이폰용프로그램으로는 ihandy 가있음

웹브라우저 WebBrowser 컨트롤 WebBrowser 컨트롤의특징 WebBrowser 컨트롤을이용한애플리케이션만들기 프로젝트생성및화면디자인 버튼컨트롤에대한이벤트핸들러추가 WebBrowser 컨트롤에대한이벤트핸들러추가 Back 버튼에대한핸들링추가

웹브라우저를활용하는두가지방법 인터넷익스플로러를실행 런처의 WebBrowserTask 네비게이션에서이미다루었음 WebBrowser 컨트롤을사용하는방법

인터넷익스플로러과비교할때, WebBrowser 컨트롤의차이점 애플리케이션에서탐색한웹페이지의컨텐츠에대한캐시부분을고려해야함 탐색한웹페이지의히스토리및컨텐츠에대한부분을컨트롤에서관리해주지않음 탐색되는 URL 주소와보안자물쇠아이콘을컨트롤내에서볼수있게제공되지않음 HTML 페이지의탐색기능이제공되지않음 애플리케이션에서인터넷익스플로러와쿠키를공유할수없음 스크립트가실행되지않도록기본적으로설정되어있으며, 스크립트가실행되기위해서는 IsScriptEnabled 속성값을설정해야함 이속성이설정되면다음페이지가로드되는시점부터스크립트가실행됨 HtmlBrush 가포함되어있지않음 ActiveX 컨트롤이허용되지않음

프로젝트생성및화면디자인 URL 주소를입력받는 TextBox 컨트롤과입력받은주소로탐색시키는 Button 컨트롤의 Name 속성값을변경시킴 여기서는귀찮아서안했음

<TextBox Height="72" HorizontalAlignment="Left" Margin="7,15,0,0" Name="textBox1" Text="about:blank" VerticalAlignment="Top" Width="360" /> <Button Content="Go" Height="72" HorizontalAlignment="Left" Margin="362,15,0,0" Name="button1" VerticalAlignment="Top" Width="88" Click="button1_Click" /> <phone:webbrowser HorizontalAlignment="Left" Margin="12,93,0,0" Name="webBrowser1" VerticalAlignment="Top" Height="508" Width="438" />

버튼컨트롤에대해이벤트핸들러추가 WebBrowser 컨트롤을활용해서 TextBox 컨트롤을통해입력된 URL 주소로탐색하는코드 탐색을위해서는 Navigate 메소드를사용함 URL 주소는 Uri 개체로만들어서패러미터로넘김

private void button1_click(object sender, RoutedEventArgs e) this.webbrowser1.navigate(new Uri(this.textBox1.Text, UriKind.Absolute));

TextBox 컨트롤을통해입력된 URL 주소는확인할수있으나, 해당웹페이지의링크를통해서다른웹페이지로이동하게되면, 이에대한 URL 주소는현재상태에서는확인할수없음 WebBrowser 컨트롤에서는탐색되는 URL 주소에대해컨트롤내부에서보여줄방법이제공되지않음 리디렉션되는 URL 주소나링크된 URL 주소를보기위해서는 WebBrowser 컨트롤의이벤트를활용한별도의코드를작성해야함 Navigating 이벤트 - URL 주소의리디렉션과같이탐색중일때발생함 패러미터는 NavigatingEventArgs Navigated 이벤트 탐색이완료된후에발생함 패러미터는 System.Windows.Navigation.NavigationEventArgs

// 생성자 public MainPage() InitializeComponent(); this.webbrowser1.navigating += new EventHandler<NavigatingEventArgs>(webBrowser1_Navigating); this.webbrowser1.navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(webBrowser1_Navigated); void webbrowser1_navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) throw new NotImplementedException(); void webbrowser1_navigating(object sender, NavigatingEventArgs e) throw new NotImplementedException();

void webbrowser1_navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) this.textbox1.text = e.uri.tostring(); void webbrowser1_navigating(object sender, NavigatingEventArgs e) this.textbox1.text = e.uri.tostring();

이전페이지로돌아가는기능은아직구현되지않았음 Back 버튼에대한핸들링으로구현 URL 주소를기억하기위한 string 배열변수 URL 주소의개수를 50 개미만으로설정 현재탐색된 URL 주소를가리키기위한인덱수변수 - nforwardindex

private string[] strhistory; private int nforwardindex; // 생성자 public MainPage() InitializeComponent(); this.webbrowser1.navigating += new EventHandler<NavigatingEventArgs>(webBrowser1_Naviga ting); this.webbrowser1.navigated += new EventHandler<System.Windows.Navigation.NavigationEven targs>(webbrowser1_navigated); this.strhistory = new string[50]; this.nforwardindex = -1;

void webbrowser1_navigated(object sender, System.Windows.Navigation.NavigationEventA rgs e) this.textbox1.text = e.uri.tostring(); this.strhistory[++this.nforwardindex] = e.uri.tostring();

OnBackKeyPress 메소드를오버라이드함 nforwardindex 를체크 이전주소가있으면이전페이지를로드해서가고 (e.cancel 을 true 로 ) 아니면애플리케이션을종료시킴 (e.cancel 을 false 로해서운영체제에서핸들하도록함 )

protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e) if (0 < this.nforwardindex) this.webbrowser1.navigate(new Uri(this.strHistory[-- this.nforwardindex], UriKind.Absolute)); e.cancel = true; else e.cancel = false; base.onbackkeypress(e);

여기까지만하면, Back 버튼을눌러도같은 URL 주소를계속탐색하는문제가발생함 그이유는 Back 버튼에대한이벤트핸들러에서 Navigate 메소드를호출하면 Navigated 이벤트핸들러가다시호출되면서계속같은 URL 주소가저장되기때문임 즉, Navigated 이벤트핸들러에서는 Back 버튼으로인해발생하는탐색의경우, 해당 URL 주소를저장하지않도록처리해야함 이를위해서는 Back 버튼에의한 Navigate 메소드호출인지를구별하기위한변수하나를추가함 private bool bnavigatedbyback;

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; namespace PhoneApp4 public partial class MainPage : PhoneApplicationPage private string[] strhistory; private int nforwardindex; private bool bnavigatedbyback; // 생성자 public MainPage() InitializeComponent(); this.webbrowser1.navigating += new EventHandler<NavigatingEventArgs>(webBrowser1_Navigating); this.webbrowser1.navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(webBrowser1_Navigated); this.strhistory = new string[50]; this.nforwardindex = -1; this.bnavigatedbyback = false;

void webbrowser1_navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) this.textbox1.text = e.uri.tostring(); if (this.bnavigatedbyback) this.bnavigatedbyback = false; else this.strhistory[++this.nforwardindex] = e.uri.tostring(); void webbrowser1_navigating(object sender, NavigatingEventArgs e) this.textbox1.text = e.uri.tostring();

private void button1_click(object sender, RoutedEventArgs e) this.webbrowser1.navigate(new Uri(this.textBox1.Text, UriKind.Absolute)); protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e) if (0 < this.nforwardindex) this.bnavigatedbyback = true; this.webbrowser1.navigate(new Uri(this.strHistory[--this.nForwardIndex], UriKind.Absolute)); e.cancel = true; else e.cancel = false; base.onbackkeypress(e);

앞의예제코드의문제점중하나는 50 개의주소만을히스토리로저장할수있다는것이다. 웹서핑중 50 개이상의페이지를탐색한경우, nforwardindex 값은 50보다크거나같게되고이는배열의범위값을넘어서는예외 (exception) 를발생하게된다. 이를해결한코드를작성하라. 앞의예제에서는과거의 URL로 Back 버튼을눌러서돌아가다가, 처음페이지에서 Back 버튼을누르면, 어플리케이션이자동으로종료한다. 이때, 자동으로종료하지않고, 어플리케이션이끝낼것인지를물어보도록하는코드를작성하라. 앞의예제에는주소들만가지고있다가새롭게로드하는방법이다. WebBrowser 개체의 SaveToString 메소드와 NavigateToString 메소드를사용하면해당웹컨텐츠를스트링으로바꾸거나웹페이지에스트링을집어넣을 (inject) 수있다. 이를이용해서웹컨텐츠도저장할수있도록예제를확장해보자.