이번에 프로젝트 투입이 되면서 처음 접하게 된 언어가 있다. 바로 Adobe사의 Flex란 언어이다.
그동안 자바, JSP로만 웹 개발만 해오다 새로운 걸 접하니 생소하고 두렵기도 하고 해서 예제로 배우는 Adobe FLEX란 책을 구매하여 공부하는 중이다.
그런데 구글 날씨 API를 이용한 날씨 정보 유틸은 이 책에 제일 뒤쪽 부록으로 간략하게 설명이 되어 있는 AIR란 걸 이용하여 만들었다.
에어(AIR)란 Adobe사에서 아폴로(Apollo)란 이름으로 진행했던 프로젝트의 정식 명칭이다.
에어는 윈도우 데스크탑 버전의 RIA 어플리케이션을 작동하게 하기 위한 운영체제 호환, 디바이스 호환이 가능한 런타임이다.
기존의 FLEX가 웹브라우저에서 동작하는 어플리이케이션이고 로컬 자원에 접근하기가 힘든 반면
에어는 웹브라우저가 아닌 윈도우에서 실행이 되고 로컬 자원에 접근할 수 있다는 장점이 있다.
에어는 윈도우 데스크탑 버전의 RIA 어플리케이션을 작동하게 하기 위한 운영체제 호환, 디바이스 호환이 가능한 런타임이다.
기존의 FLEX가 웹브라우저에서 동작하는 어플리이케이션이고 로컬 자원에 접근하기가 힘든 반면
에어는 웹브라우저가 아닌 윈도우에서 실행이 되고 로컬 자원에 접근할 수 있다는 장점이 있다.
왜냐하면 실행되는 환경이 다를 뿐 개발 방식은 똑같기 때문이다. 설명은 여기까지 하고 구글 날씨 API를 이용하여 개발한 소스를 첨부한다.
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="selectCity()" fontFamily="Arial" fontSize="11" width="100%" height="100%" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.utils.ArrayUtil;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.rpc.http.HTTPService;
private function selectCity():void {
var curCity:String = cbCity.selectedItem.data;
var svHTTP:HTTPService = new HTTPService();
svHTTP.url = "http://www.google.co.kr/ig/api?weather=" + curCity;
svHTTP.addEventListener(ResultEvent.RESULT, resultHandler);
svHTTP.addEventListener(FaultEvent.FAULT, faultHandler);
svHTTP.send();
}
public function resultHandler(event:ResultEvent):void {
var resultAC:ArrayCollection = (event.result.xml_api_reply.weather is ArrayCollection) ?
event.result.xml_api_reply.weather as ArrayCollection :
new ArrayCollection(ArrayUtil.toArray(event.result.xml_api_reply.weather));
if(resultAC.getItemAt(0).problem_cause != null) {
Alert.show(resultAC.getItemAt(0).problem_cause.data);
curInfo.text = "";
curIcon.load(null);
} else {
// 현재 날씨 정보
currentWeatherInfo(resultAC.getItemAt(0));
// 오늘 날씨 정보
todayDispWeatherInfo(resultAC.getItemAt(0));
// 다음 날씨 정보
next1DispWeatherInfo(resultAC.getItemAt(0));
// 이틀 째 날씨 정보
next2DispWeatherInfo(resultAC.getItemAt(0));
// 삼일째 날씨 정보 Display
next3DispWeatherInfo(resultAC.getItemAt(0));
}
}
// 현재 날씨 정보 Display
private function currentWeatherInfo(params:Object):void {
var txtValue:String = "";
txtValue = txtValue + "현재 : " + params.current_conditions.condition.data;
txtValue = txtValue + ", " + params.current_conditions.temp_c.data + "℃";
txtValue = txtValue + ", " + params.current_conditions.humidity.data;
txtValue = txtValue + ", " + params.current_conditions.wind_condition.data;
curInfo.text = txtValue;
curIcon.load("http://www.google.co.kr/ig" + params.current_conditions.icon.data);
}
// 오늘 날씨 정보 Display
private function todayDispWeatherInfo(params:Object):void {
lbToday.text = params.forecast_conditions[0].day_of_week.data;
todayIcon.load("http://www.google.co.kr/ig" + params.forecast_conditions[0].icon.data);
todayIcon.toolTip = params.forecast_conditions[0].condition.data;
lbTodayTemp.text = params.forecast_conditions[0].low.data + "℃ | " + params.forecast_conditions[0].high.data + "℃";
}
// 다음 날씨 정보 Display
private function next1DispWeatherInfo(params:Object):void {
lbNext1.text = params.forecast_conditions[1].day_of_week.data;
next1Icon.load("http://www.google.co.kr/ig" + params.forecast_conditions[1].icon.data);
next1Icon.toolTip = params.forecast_conditions[1].condition.data;
lbNext1Temp.text = params.forecast_conditions[1].low.data + "℃ | " + params.forecast_conditions[1].high.data + "℃";
}
// 이틀째 날씨 정보 Display
private function next2DispWeatherInfo(params:Object):void {
lbNext2.text = params.forecast_conditions[2].day_of_week.data;
next2Icon.load("http://www.google.co.kr/ig" + params.forecast_conditions[2].icon.data);
next2Icon.toolTip = params.forecast_conditions[2].condition.data;
lbNext2Temp.text = params.forecast_conditions[2].low.data + "℃ | " + params.forecast_conditions[2].high.data + "℃";
}
// 삼일째 날씨 정보 Display
private function next3DispWeatherInfo(params:Object):void {
lbNext3.text = params.forecast_conditions[3].day_of_week.data;
next3Icon.load("http://www.google.co.kr/ig" + params.forecast_conditions[3].icon.data);
next3Icon.toolTip = params.forecast_conditions[3].condition.data;
lbNext3Temp.text = params.forecast_conditions[3].low.data + "℃ | " + params.forecast_conditions[3].high.data + "℃";
}
public function faultHandler(event:FaultEvent):void {
Alert.show(event.fault.message);
}
]]>
</mx:Script>
<mx:Model id="cityData">
<Cities>
<City label="서울" data="Seoul"/>
<City label="대구" data="Daegu"/>
<City label="부산" data="Busan"/>
<City label="광주" data="GwangJu"/>
<City label="대전" data="Daejeon"/>
<City label="전주" data="Jeonju"/>
<City label="원주" data="Wonju"/>
<City label="청주" data="Chungju"/>
<City label="포항" data="Pohang"/>
<City label="성남" data="Songnam"/>
<City label="수원" data="Suwon"/>
<City label="상주" data="Sangju"/>
<City label="김해" data="Gimhae"/>
<City label="인천" data="Incheon"/>
<City label="울산" data="Ulsan"/>
<City label="창원" data="Changwon"/>
<City label="천안" data="Cheonan"/>
<City label="안양" data="Anyang"/>
<City label="안산" data="Ansan"/>
<City label="춘천" data="Chuncheon"/>
<City label="제주" data="Jeju"/>
<City label="여수" data="Yeosu"/>
<City label="안동" data="Andong"/>
<City label="양산" data="Yangsan"/>
<City label="태안" data="Taean"/>
<City label="진주" data="Jinju"/>
<City label="진해" data="Chinhae"/>
<City label="의정부" data="Uijongbu"/>
<City label="평택" data="Pyongtaek"/>
<City label="오산" data="Osan"/>
<City label="순천" data="Sunchon"/>
<City label="당진" data="Dangjin"/>
<City label="마산" data="Masan"/>
<City label="군산" data="Gunsan"/>
<City label="목포" data="Mokpo"/>
<City label="동해" data="Tonghae"/>
<City label="구미" data="Gumi"/>
<City label="삼척" data="Samchok"/>
</Cities>
</mx:Model>
<mx:ArrayCollection id="myAC" source="{ArrayUtil.toArray(cityData.City)}"/>
<mx:ComboBox x="10" y="10" width="81" enabled="true" id="cbCity" dataProvider="{myAC}" change="selectCity()"></mx:ComboBox>
<mx:Button x="99" y="11" label="Refresh Weather Info" id="btnRefresh" enabled="true" click="selectCity()"/>
<mx:TextInput x="10" y="42" width="283" id="curInfo" editable="false" enabled="true"/>
<mx:VBox x="10" y="85" height="107" width="67">
<mx:Label width="100%" enabled="true" textAlign="center" id="lbToday"/>
<mx:Image id="todayIcon" verticalAlign="middle" horizontalAlign="center" width="66"/>
<mx:Label width="100%" textAlign="center" id="lbTodayTemp"/>
</mx:VBox>
<mx:Image x="301" y="34" id="curIcon"/>
<mx:VBox x="85" y="85" height="107" width="67">
<mx:Label id="lbNext1" enabled="true" width="100%" textAlign="center"/>
<mx:Image id="next1Icon" width="66" horizontalAlign="center" verticalAlign="middle"/>
<mx:Label id="lbNext1Temp" width="100%" textAlign="center"/>
</mx:VBox>
<mx:VBox height="107" width="67" x="160" y="85">
<mx:Label id="lbNext2" enabled="true" width="100%" textAlign="center"/>
<mx:Image id="next2Icon" width="66" horizontalAlign="center" verticalAlign="middle"/>
<mx:Label id="lbNext2Temp" width="100%" textAlign="center"/>
</mx:VBox>
<mx:VBox height="107" width="67" x="235" y="85">
<mx:Label id="lbNext3" enabled="true" width="100%" textAlign="center"/>
<mx:Image id="next3Icon" width="66" horizontalAlign="center" verticalAlign="middle"/>
<mx:Label id="lbNext3Temp" width="100%" textAlign="center"/>
</mx:VBox>
</mx:WindowedApplication>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="selectCity()" fontFamily="Arial" fontSize="11" width="100%" height="100%" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.utils.ArrayUtil;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.rpc.http.HTTPService;
private function selectCity():void {
var curCity:String = cbCity.selectedItem.data;
var svHTTP:HTTPService = new HTTPService();
svHTTP.url = "http://www.google.co.kr/ig/api?weather=" + curCity;
svHTTP.addEventListener(ResultEvent.RESULT, resultHandler);
svHTTP.addEventListener(FaultEvent.FAULT, faultHandler);
svHTTP.send();
}
public function resultHandler(event:ResultEvent):void {
var resultAC:ArrayCollection = (event.result.xml_api_reply.weather is ArrayCollection) ?
event.result.xml_api_reply.weather as ArrayCollection :
new ArrayCollection(ArrayUtil.toArray(event.result.xml_api_reply.weather));
if(resultAC.getItemAt(0).problem_cause != null) {
Alert.show(resultAC.getItemAt(0).problem_cause.data);
curInfo.text = "";
curIcon.load(null);
} else {
// 현재 날씨 정보
currentWeatherInfo(resultAC.getItemAt(0));
// 오늘 날씨 정보
todayDispWeatherInfo(resultAC.getItemAt(0));
// 다음 날씨 정보
next1DispWeatherInfo(resultAC.getItemAt(0));
// 이틀 째 날씨 정보
next2DispWeatherInfo(resultAC.getItemAt(0));
// 삼일째 날씨 정보 Display
next3DispWeatherInfo(resultAC.getItemAt(0));
}
}
// 현재 날씨 정보 Display
private function currentWeatherInfo(params:Object):void {
var txtValue:String = "";
txtValue = txtValue + "현재 : " + params.current_conditions.condition.data;
txtValue = txtValue + ", " + params.current_conditions.temp_c.data + "℃";
txtValue = txtValue + ", " + params.current_conditions.humidity.data;
txtValue = txtValue + ", " + params.current_conditions.wind_condition.data;
curInfo.text = txtValue;
curIcon.load("http://www.google.co.kr/ig" + params.current_conditions.icon.data);
}
// 오늘 날씨 정보 Display
private function todayDispWeatherInfo(params:Object):void {
lbToday.text = params.forecast_conditions[0].day_of_week.data;
todayIcon.load("http://www.google.co.kr/ig" + params.forecast_conditions[0].icon.data);
todayIcon.toolTip = params.forecast_conditions[0].condition.data;
lbTodayTemp.text = params.forecast_conditions[0].low.data + "℃ | " + params.forecast_conditions[0].high.data + "℃";
}
// 다음 날씨 정보 Display
private function next1DispWeatherInfo(params:Object):void {
lbNext1.text = params.forecast_conditions[1].day_of_week.data;
next1Icon.load("http://www.google.co.kr/ig" + params.forecast_conditions[1].icon.data);
next1Icon.toolTip = params.forecast_conditions[1].condition.data;
lbNext1Temp.text = params.forecast_conditions[1].low.data + "℃ | " + params.forecast_conditions[1].high.data + "℃";
}
// 이틀째 날씨 정보 Display
private function next2DispWeatherInfo(params:Object):void {
lbNext2.text = params.forecast_conditions[2].day_of_week.data;
next2Icon.load("http://www.google.co.kr/ig" + params.forecast_conditions[2].icon.data);
next2Icon.toolTip = params.forecast_conditions[2].condition.data;
lbNext2Temp.text = params.forecast_conditions[2].low.data + "℃ | " + params.forecast_conditions[2].high.data + "℃";
}
// 삼일째 날씨 정보 Display
private function next3DispWeatherInfo(params:Object):void {
lbNext3.text = params.forecast_conditions[3].day_of_week.data;
next3Icon.load("http://www.google.co.kr/ig" + params.forecast_conditions[3].icon.data);
next3Icon.toolTip = params.forecast_conditions[3].condition.data;
lbNext3Temp.text = params.forecast_conditions[3].low.data + "℃ | " + params.forecast_conditions[3].high.data + "℃";
}
public function faultHandler(event:FaultEvent):void {
Alert.show(event.fault.message);
}
]]>
</mx:Script>
<mx:Model id="cityData">
<Cities>
<City label="서울" data="Seoul"/>
<City label="대구" data="Daegu"/>
<City label="부산" data="Busan"/>
<City label="광주" data="GwangJu"/>
<City label="대전" data="Daejeon"/>
<City label="전주" data="Jeonju"/>
<City label="원주" data="Wonju"/>
<City label="청주" data="Chungju"/>
<City label="포항" data="Pohang"/>
<City label="성남" data="Songnam"/>
<City label="수원" data="Suwon"/>
<City label="상주" data="Sangju"/>
<City label="김해" data="Gimhae"/>
<City label="인천" data="Incheon"/>
<City label="울산" data="Ulsan"/>
<City label="창원" data="Changwon"/>
<City label="천안" data="Cheonan"/>
<City label="안양" data="Anyang"/>
<City label="안산" data="Ansan"/>
<City label="춘천" data="Chuncheon"/>
<City label="제주" data="Jeju"/>
<City label="여수" data="Yeosu"/>
<City label="안동" data="Andong"/>
<City label="양산" data="Yangsan"/>
<City label="태안" data="Taean"/>
<City label="진주" data="Jinju"/>
<City label="진해" data="Chinhae"/>
<City label="의정부" data="Uijongbu"/>
<City label="평택" data="Pyongtaek"/>
<City label="오산" data="Osan"/>
<City label="순천" data="Sunchon"/>
<City label="당진" data="Dangjin"/>
<City label="마산" data="Masan"/>
<City label="군산" data="Gunsan"/>
<City label="목포" data="Mokpo"/>
<City label="동해" data="Tonghae"/>
<City label="구미" data="Gumi"/>
<City label="삼척" data="Samchok"/>
</Cities>
</mx:Model>
<mx:ArrayCollection id="myAC" source="{ArrayUtil.toArray(cityData.City)}"/>
<mx:ComboBox x="10" y="10" width="81" enabled="true" id="cbCity" dataProvider="{myAC}" change="selectCity()"></mx:ComboBox>
<mx:Button x="99" y="11" label="Refresh Weather Info" id="btnRefresh" enabled="true" click="selectCity()"/>
<mx:TextInput x="10" y="42" width="283" id="curInfo" editable="false" enabled="true"/>
<mx:VBox x="10" y="85" height="107" width="67">
<mx:Label width="100%" enabled="true" textAlign="center" id="lbToday"/>
<mx:Image id="todayIcon" verticalAlign="middle" horizontalAlign="center" width="66"/>
<mx:Label width="100%" textAlign="center" id="lbTodayTemp"/>
</mx:VBox>
<mx:Image x="301" y="34" id="curIcon"/>
<mx:VBox x="85" y="85" height="107" width="67">
<mx:Label id="lbNext1" enabled="true" width="100%" textAlign="center"/>
<mx:Image id="next1Icon" width="66" horizontalAlign="center" verticalAlign="middle"/>
<mx:Label id="lbNext1Temp" width="100%" textAlign="center"/>
</mx:VBox>
<mx:VBox height="107" width="67" x="160" y="85">
<mx:Label id="lbNext2" enabled="true" width="100%" textAlign="center"/>
<mx:Image id="next2Icon" width="66" horizontalAlign="center" verticalAlign="middle"/>
<mx:Label id="lbNext2Temp" width="100%" textAlign="center"/>
</mx:VBox>
<mx:VBox height="107" width="67" x="235" y="85">
<mx:Label id="lbNext3" enabled="true" width="100%" textAlign="center"/>
<mx:Image id="next3Icon" width="66" horizontalAlign="center" verticalAlign="middle"/>
<mx:Label id="lbNext3Temp" width="100%" textAlign="center"/>
</mx:VBox>
</mx:WindowedApplication>
위 소스로 만든 어플리케이션의 실행화면은 다음 그림을 참고하기 바란다.
그림을 보면 콤보박스에서 조회하고자 하는 도시를 선택을 하면 현재의 날씨 정보를 텍스트박스에 보여주고 오른편에 이미지로 나타내준다.
그 아래에는 오늘 날씨 정보와 3일치의 날씨 정보를 나타내준다.
날씨 정보에 대한 소스는 이전 글(구글 날씨 API용 국내 주요 시/군 코드 정리)를 참고하기 바란다.
실제로 저 허접한 유틸을 돌려보고 싶은 사람이 있다면 다음 첨부파일을 다운받아 설치하면 간단하게 사용이 가능하다.
설치하는 건 다른 일반 윈도우 어플리케이션과 비슷하다.
다만 테스트용 인증서로 배포버전을 만든 거라 경고문이 뜨는 데 악성프로그램이 아니니 안심하고 설치하면 된다.
마음에 안들면 제어판->프로그램 추가/제거에서 JuneUtil이란 놈을 찾아 과감히 삭제하시길.
본인의 컴퓨터에 Adobe AIR Runtime이 설치되어 있다면 첨부파일로만 설치하여 사용이 가능하고 설치안되어 있는 분들은 Adobe AIR사이트에서 다운받아 설치하면 된다.
용량이 12MB로 큰 용량이 아니니 부담없이 설치해도 될 듯 하다.
에어 런타임 다운받기
※ 혹시나 본문의 소스보기가 힘들고 첨부파일을 설치하신 분은 해당 프로그램을 실행 후 마우스 오른쪽 버튼을 누르면 View Source란 메뉴를 통해서도 쉽게 소스를 볼 수가 있으니 참고바랍니다.