일전에 Flex 4 CSS에 대한 글을 올렸습니다. Flex 4의 CSS는 Flex 3까지의 CSS의 한계를 극복하도록 만들어졌죠. 그런데 이 CSS가 기능이 확장된 계기가 있었습니다. 갑자기 궁금해지지 않나요? Flex 개발에 관련된 Adobe 개발자들이 머리를 짜서 하자고 했을까요? 아니면 어떤 요청이 있었을까요? 답은 Adobe Bug reporting 시스템에 있었습니다.

 

Adobe에서는 Adobe Bug Reporting 시스템을 운영하고 있습니다. 들어가보면 알겠지만 Flex, BlazeDS, Flash Player, ActionScript Compiler 주제로 개설되어 있고 개발자들의 요구사항 및 버그를 이곳에서 전부 받고 같은 요구사항이 있는 사람들이 투표하는 방식으로 진행되어 투표수가 많으면 Adobe에서 우선순위를 가려서 개발착수에 들어가지요.

Adobe Bug 리포팅 시스템 첫화면

 

 

Flex 4의 고급 CSS도 이런 과정을 통해 탄생된 겁니다.

 

https://bugs.adobe.com/jira/browse/SDK-14385

 

Flex CSS 지원해달라는 요구사항 페이지

 

Jacob Wright라는 사람이 리포팅을 했고 투표수가 75입니다. 그만큼 CSS의 기능이 더욱 확장되었으면 좋겠다는 사람의 수가 많은 거지요. 투표수가 많으니 Adobe 측에서도 무시할 수 없게 된 것이고 이번 Flex 4에서 지원하게 된 것입니다.

 

 

 

버그 리포팅에 참여하자.

 

그럼 우리도 참여할 수 있을까요?

때론 그럴겁니다 영어 못하기 때문에 참여 못한다고....

하지만 꼭 그렇지 않습니다. 자주 들어가 내용을 살펴보고 투표로써 작은 관심을 가져주는 것만으로도 충분합니다. 개발자중에서도 영어 잘하는 사람이 이런 글을 올립니다. 그럼 그 사람의 요청이 있으면 가서 투표해주면 되는 겁니다. 절대 어려운 일이 아닙니다. 아래에 버그 리포팅에서 추천(투표)의 중요성에 대해 알 수 있을 겁니다.

 

[열이아빠]플렉스 버그 리포팅에서 추천의 중요성

 

Adobe Flex/AIR 관련되어 한글문제가 꽤 있습니다. 이 한글문제를 해결하기 위해서도 이 버그 리포트 시스템을 이용하면 됩니다. 이미 Flash Platform 한글문제 공동대응팀이 생겼고 지속적으로 이 문제를 해결하기 위해 다양한 각도로 일하고 있습니다. 대응팀 총괄을 맡고 있는 이희덕씨 블로그에 관련 글이 많습니다.

 

[희희덕덕]한글문제 이슈 관련글

 

아래글은 열이아빠님이 쓰신 플렉스 버그 검색하는 방법입니다..

 

[열이아빠]플렉스 버그 검색해보기

 

투표에 참여해서 Flex/AIR/Flash 한글문제를 해결하는 방법입니다.

 

[희희덕덕]여러분의 참여로 한글 문제를 함께 해결해 봅시다!

 

 

앞으로 Flash Platform 한글문제 공동대응팀은 관련 자료를 종합하고 관리할 것입니다.

 

 

정리하며

 

여러분도 Adobe RIA 기술에 대한 불만 또는 원하는 사항들이 있을겁니다. 앞에서 설명드린데로 잘 정리해서 Adobe Bug Reporting 시스템에 올리시거나 투표를 적극적으로 참여함으로써 성취할 수 있는 겁니다. 한국 개발자들은 유독 영어 울렁증이 있다고들 호소하는데(저를 비롯) 사실 핑계라고 생각합니다. 필요하면 공부하면 되고요. 이런 일들은 꼭 영어를 잘해서 하는 일도 아니거든요. 한국 Adobe RIA 기술에 관련된 시장은 일본에 비해 거의 8배 정도 부족합니다.  그리고 적극성도 일본에 비해 많이 뒤쳐지며 나오는 컨텐츠의 창의력도 훨씬 뒤지는 편입니다. 왜 유독 일본어만 Flex Livedocs가 있을까요? Adobe는 시장성이 없는 한국에 한글문서 작성 인력을 투자하기에는 그들도 아깝다는 생각을 하는겁니다. 물론 Adobe는 그런 마음을 가지면 안되는 것이지만 또한 적극적이지 못한 우리도 반성해야될 일이라고 생각합니다. 우리가 안으로만 숨지않고 적극적으로 활동하면 Adobe에서 한국시장을 무시 못하게 될겁니다. 그런 날이 오길 반드시 바랍니다.

 

글쓴이 : 지돌스타(http://blog.jidolstar.com/549)

Flash Player가 Cross OS, Cross Browser가 된다고 한다지만 유독 한글 입력 문제에 있어서 만큼은 제대로 되지 않는 경우가 있다. 가령 MS Windows 환경에서 Explorer외에 Firefox, Safari, Chrome, Opera 등에서 SWF를 브라우저에 Embed할 때 wmode를 transparent로 지정하면 TextField에 한글입력이 되지 않는다.(2009년 3월 25일 현재)

 

본인은 스타플(http://starpl.com)의 별지도에 댓글 달기 기능을 추가하면서 사용자의 OS, Browser를 판별하여 한글입력이 되지 않는 Browser의 경우 "*한글 입력이 되지 않아요!"라는 문구를 넣어야 했다.

 

 

위처럼 IE 경우엔 한글입력이 잘되므로 문구가 필요없다.

 

 

그러나 FireFox의 경우에는 한글입력이 되지 않아서 문구가 들어간다. 저 문구를 누르면 문제에 대한 안내페이지로 이동하게 되어 있다.

 

불행히도 ActionScript 3.0 라이브러리에서는 사용자가 어떤 브라우져를 이용하는지 알 수 있는 API를 제공하지 않고 있다. 그럴만도 한 것이 SWF는 브라우저에 Embed되어 작동되는 것 외에도 Flash Player 자체에서도 동작하기 때문일 것이다. (참고로 flash.system.Capabilities의 os 속성을 이용해 운영체제의 상세정보는 얻어올 수 있다.)

 

다행히도 사용자의 Browser정보를 알 수 있는 방법은 있다. 그것은 JavaScript 를 이용하는 것이다. JavaScript를 이용해만 쉽게 Browser 종류, 버전, OS 종류등을 얻어올 수 있다.

 

관련정보를 검색해본 끝에 Browser 종류, 버전, OS 종류를 범용적으로 사용할 수 있는 Javascript 코드를 발견했다. 아래 링크를 참고하자.

 

Browser detect : http://www.quirksmode.org/js/detect.html

 

사용자는 Javascript코드내에서 단지 아래처럼 사용하면 그만이다.

 

  • Browser name: BrowserDetect.browser
  • Browser version: BrowserDetect.version
  • OS name: BrowserDetect.OS

 

너무 편하다. 그럼 이것을 어떻게 ActionScript 3.0 에서 사용할 수 있다는 것인가? 결과적으로 flash.external.ExternalInterface를 이용하면 된다. ExternalInterface의 call()메소드를 이용해 JavaScript 코드를 호출하면 된다. 여기서 두가지 선택을 하게 된다.

 

  1. 위의 BrowserDetect를 js 파일로 만들어 HTML에 포함해서 ActionScript 3.0에서 ExternalInterface로 호출
  2. 위의 BrowserDetect를 커스터마이징한 JavaScript 코드를 ActionScript 3.0에 삽입해서 ExternalInterface로 호출

 

첫번째 방법은 ExternalInterface를 학습한 사람이라면 쉽게 접근할 수 있을 것이다. 하지만 AS3 코드와  JavaScript 코드가 둘로 나뉘어 관리하기가 힘들어지는 단점이 있다. 그래서 하나의 AS3에서 관리할 수 있는  두번째 방법으로 문제를 해결하고자 한다.

 

package com.starpl.utils
{
	import flash.external.ExternalInterface;

	/**
	 * Browser detect
	 * @author Yongho Ji
	 * @since 2009.03.24
	 * @see http://www.quirksmode.org/js/detect.html
	 */
	public class BrowserDetectUtil
	{
		/**
		 * Here we define javascript functions which will be inserted into the DOM
		 */
		private static const DATA_OS:String =
				"var dataOS = [" +
					"{" +
						"string: navigator.platform," +
						"subString: \"Win\"," +
						"identity: \"Windows\"" +
					"}," +
					"{" +
						"string: navigator.platform," +
						"subString: \"Mac\"," +
						"identity: \"Mac\"" +
					"}," +
					"{" +
						"string: navigator.userAgent," +
						"subString: \"iPhone\"," +
						"identity: \"iPhone/iPod\"" +
					"}," +
					"{" +
						"string: navigator.platform," +
						"subString: \"Linux\"," +
						"identity: \"Linux\"" +
					"}" +
				"];";

		private static const DATA_BROWSER:String =
				"var dataBrowser = [" +
						"{" +
							"string: navigator.userAgent," +
							"subString: \"Chrome\"," +
							"identity: \"Chrome\"" +
						"}," +
						"{" +
							"string: navigator.userAgent," +
							"subString: \"OmniWeb\"," +
							"versionSearch: \"OmniWeb/\"," +
							"identity: \"OmniWeb\"" +
						"}," +
						"{" +
							"string: navigator.vendor," +
							"subString: \"Apple\"," +
							"identity: \"Safari\"," +
							"versionSearch: \"Version\"" +
						"}," +
						"{" +
							"prop: window.opera," +
							"identity: \"Opera\"" +
						"}," +
						"{" +
							"string: navigator.vendor," +
							"subString: \"iCab\"," +
							"identity: \"iCab\"" +
						"}," +
						"{" +
							"string: navigator.vendor," +
							"subString: \"KDE\"," +
							"identity: \"Konqueror\"" +
						"}," +
						"{" +
							"string: navigator.userAgent," +
							"subString: \"Firefox\"," +
							"identity: \"Firefox\"" +
						"}," +
						"{" +
							"string: navigator.vendor," +
							"subString: \"Camino\"," +
							"identity: \"Camino\"" +
						"}," +
						"{" +
							"string: navigator.userAgent," +
							"subString: \"Netscape\"," +
							"identity: \"Netscape\"" +
						"}," +
						"{" +
							"string: navigator.userAgent," +
							"subString: \"MSIE\"," +
							"identity: \"Explorer\"," +
							"versionSearch: \"MSIE\"" +
						"}," +
						"{" +
							"string: navigator.userAgent," +
							"subString: \"Gecko\"," +
							"identity: \"Mozilla\"," +
							"versionSearch: \"rv\"" +
						"}," +
						"{" +
							"string: navigator.userAgent," +
							"subString: \"Mozilla\"," +
							"identity: \"Netscape\"," +
							"versionSearch: \"Mozilla\"" +
						"}" +
					"];";		

		private static const FUNCTION_SEARCH_BROWSER:String =
			"document.insertScript = function ()" +
			"{"	+
				"if (document.searchBrowser==null)" +
				"{" +
					"searchBrowser = function ()" +
					"{" +
						DATA_BROWSER +
						"var browser = null;" +
						"for (var i=0;i<dataBrowser.length;i++)	" +
						"{" +
							"var dataString = dataBrowser[i].string;" +
							"var dataProp = dataBrowser[i].prop;" +
							"if (dataString) " +
							"{" +
								"if (dataString.indexOf(dataBrowser[i].subString) != -1)" +
								"{" +
									"browser = dataBrowser[i].identity;" +
									"break;" +
								"}" +
							"}" +
							"else if (dataProp)" +
							"{" +
								"browser = dataBrowser[i].identity;" +
								"break;" +
							"}" +
						"}" +
						"if( browser == null )" +
						"{" +
							"browser = \"An unknown browser\";" +
						"}" +
						"return browser;" +
					"}" +
				"}" +
			"}";

		private static const FUNCTION_SEARCH_BROWSER_VERSION:String =
			"document.insertScript = function ()" +
			"{"	+
				"if (document.searchBrowserVersion==null)" +
				"{" +
					"searchBrowserVersion = function () " +
					"{" +
						DATA_BROWSER +
						"var browser;" +
						"for (var i=0;i<dataBrowser.length;i++)	" +
						"{" +
							"var browserString = dataBrowser[i].string;" +
							"var browserProp = dataBrowser[i].prop;" +
							"if (browserString) " +
							"{" +
								"if (browserString.indexOf(dataBrowser[i].subString) != -1)" +
								"{" +
									"browser = dataBrowser[i];" +
									"break;" +
								"}" +
							"}" +
							"else if (browserProp)" +
							"{" +
								"browser = dataBrowser[i];" +
								"break;" +
							"}" +
						"}" +
						"var versionSearchString = browser.versionSearch || browser.identity;" +
						"var index;" +
						"var version;" +
						"version = navigator.appVersion;" +
						"index = version.indexOf(versionSearchString);" +
						"if (index != -1) " +
						"{" +
							"return parseFloat(version.substring(index+versionSearchString.length+1));" +
						"}" +
						"version = navigator.userAgent;" +
						"index = version.indexOf(versionSearchString);" +
						"if (index != -1) " +
						"{" +
							"return parseFloat(version.substring(index+versionSearchString.length+1));" +
						"}" +
						"return \"an unknown version\";" +
					"}"+
				"}"+
			"}";	

		private static const FUNCTION_SEARCH_OS:String =
			"document.insertScript = function ()" +
			"{" +
				"if (document.searchOS==null)" +
				"{" +
					"searchOS = function ()" +
					"{" +
						DATA_OS +
						"var os = null;" +
						"for (var i=0;i<dataOS.length;i++)	" +
						"{" +
							"var dataString = dataOS[i].string;" +
							"var dataProp = dataOS[i].prop;" +
							"if (dataString) " +
							"{" +
								"if (dataString.indexOf(dataOS[i].subString) != -1)" +
								"{" +
									"os = dataOS[i].identity;" +
									"break;" +
								"}" +
							"}" +
							"else if (dataProp)" +
							"{" +
								"os = dataOS[i].identity;" +
								"break;" +
							"}" +
						"}" +
						"if( os == null )" +
						"{" +
							"os = \"An unknown OS\";" +
						"}" +
						"return os;" +
					"}"+
				"}"+
			"}";	

		private static var initFlag:Boolean = false;
		private static var _browserVersion:String = null;
		private static var _browser:String = null;
		private static var _os:String = null;

		private static function insertScript():void
		{
			if( initFlag ) return;
			if (! ExternalInterface.available)
			{
			    throw new Error("ExternalInterface is not available in this container. Internet Explorer ActiveX, Firefox, Mozilla 1.7.5 and greater, or other browsers that support NPRuntime are required.");
			}

			initFlag = true;
			ExternalInterface.call( FUNCTION_SEARCH_BROWSER );
			ExternalInterface.call( FUNCTION_SEARCH_BROWSER_VERSION );
			ExternalInterface.call( FUNCTION_SEARCH_OS );
		}

		/**
		 * browser name
		 */
		public static function get browser():String
		{
			if( _browser ) return _browser;
			insertScript();
			_browser = ExternalInterface.call( "searchBrowser" );
			return _browser;
		}

		/**
		 * browser version
		 */
		public static function get browserVersion():String
		{
			if( _browserVersion ) return _browserVersion;
			insertScript();
			_browserVersion = ExternalInterface.call( "searchBrowserVersion" );
			return _browserVersion;
		}

		/**
		 * OS name
		 */
		public static function get OS():String
		{
			if( _os ) return _os;
			insertScript();
			_os = ExternalInterface.call( "searchOS" );
			return _os;
		}

	}
}

 

 

사용법은 매우 간단하다.

 

  • Browser name: BrowserDetectUtil.browser
  • Browser version: BrowserDetectUtil.version
  • OS name: BrowserDetectUtil.OS

 

ExternalInterface를 활용하면 매우 무궁무진하게 확장이 가능해진다.

 

단, ExternalInterface를 사용할 수 있는 조건을 잘 고려해야한다. 첫번째로 ExternalInterface를 지원하는 Flash Player 버전을 확인한다. 둘째로 SWF를 Embed할때 allowScriptAccess속성과 allowNetworking 속성을 체크한다. 두번째의 경우 본인 블로그에 있는 “위젯도 마음대로 못다는 네이버 블로그” 글을 보면 되겠다.

 

참고글

 

+ Recent posts