강의 자료를 대충 살펴보면 알겠지만,
뜬구름 같은 트렌드 혹은 패러다임을 "얘기"하고 있어서,
구체적인 기술이 중요한 3일짜리 속성 과정에 적합하지 않은 것 같아 자진 사퇴(?)하고, 발표자료를 공개(?)한다.
내가 발표했던 첫날 3시간은 @ibare님이 좀 더 실무적이고 유익한 내용으로 강의하실 듯~ ^^
아래에, xui.js가 제공하는 모든(!) 함수를 간략하게 설명합니다. 개발자 분들의 이해를 돕기 위해 대응하는 jquery와 dojo의 함수도 같이 소개 했습니다.
기본 함수
extend(object)
프로토타입 상속
[ jquery.extend(), dojo.extend() ]
find(selector, context)
css 셀렉터에 부합하는 노드 선택.
[ jquery.find(), dojo.query() ]
set(array)
명시적으로 노트 선택.
reduce(elements, index)
중복 제거
[ jquery.unique() ]
has(selector)
css 셀렉터에 부합 여부 확인
[ jquery.has(), dojo.query() ]
filter(fn)
노드 필터링(제외).
[ jquery.filter(), dojo.filter() ]
not(selector)
css 셀렉터에 부합하지 않는지 확인.
[ jquery와 dojo의 not() css selector ]
each(fn)
노드 순회
[ jquery.each(), dojo.forEach() ]
DOM 조작 함수
html(location, html)
HTML 컨텐츠 조작.
location: inner,outer,top,bottom,remove,before,after 중의 하나(생략가능; 생략하면 inner). 동일한 이름을 가진 단축함수(inner(html) 등) 사용 가능.
[ jquery.html(), dojo.place() ]
attr(attribute, value):
HTML 속성 조작.
value를 생략하면 get.
[ jquery.attr(), dojo.attr() ]
이벤트 함수
on(type, fn)
이벤트 핸들러 등록.
type: click, load, touchstart, touchmove, touchend, touchcancel, gesturestart, gesturechange, gestureend, orientationchange 등. 동일한 이름을 가진 단축함수(예: click() 등) 사용 가능.
[ jquery.bind(), dojo.connect() ]
un(type, fn)
이벤트 핸들러 등록 해제.
fn을 생략하면 해당 이벤트에 등록된 모든 이벤트 핸들러 등록 해제.
[ jquery.unbind(), dojo.disconnect() ]
fire(type, data)
가상의 이벤트 발생.
data: 이벤트 핸들러로 전달되는 event 객체의 data 속성으로 전달(생략 가능).
[ jquery.trigger() ]
화면 효과 함수
tween(properties, callback)
css 속성 인비트윈(inbetween) 에니메이션.
properties: 에니메이트할 css 속성-값 pairs과 duration, after, easing 등의 추가 속성들 또는 속성들의 배열.
[ jquery.animateProperty(), dojo.animateProperty() 등 ]
CSS 스타일 함수
setStyle(property, value)
css 속성 설정.
[ jquery.css(), dojo.style() ]
getStyle(property, callback)
css (계산된)속성 얻기.
[ jquery.css(), dojo.style() ]
addClass(className)
css 클래스 추가.
[ jquery.addClass(), dojo.addClass() ]
hasClass(className)
css 클래스 확인.
[ 참고: jquery.hasClass(), dojo.hasClass() ]
removeClass(className)
css 클래스 제거.
[ jquery.removeClass(), dojo.removeClass() ]
css(properties)
css 속성 일괄 설정.
[ jquery.css(), dojo.style() ]
XHR(AJAX) 함수
xhr(loation, url, options|fn)
AJAX 요청.
location: 결과(responseText)를 반영할 노드(생략 가능).
options: method, async, data, callback 등의 추가 속성 지정(생략 하거나 callback 함수만 지정 가능).
[ jquery.ajax(), ajax.load(), ajax.get(), ajax.post(), dojo.xhr(), dojo.xhrGet(), dojo.xhrPost() 등 ]
결론
xui.js는 jquery나 dojo 등의 기존 자바스크립트 라이브러리에서 널리 사용되지 않는 기능들과 다양한 데스크탑용 브라우져 지원을 위한 부가적인 코드를 제거하고, 자주 사용되는 함수들과 모바일 환경에서 꼭 필요한 브라우져만 지원하도록 최적화된 자바스크립트 라이브러리 입니다. 장단점을 요약하면 다음과 같습니다:
PROS: 크기, 성능, 구성, 소스 코드 품질
CONS: UI 툴킷 부재. 데스크탑 브라우져 호환성 부재. 문서 / 사용자 커뮤니티 부재.
직접 UI 툴킷을 만들 게 아니라면, 별도의 UI 툴킷을 사용해야 하므로 작은 크기로 인한 장점이 오히려 단점이 됩니다. JQueryMobile을 쓰려면 결국 jQuery가, Sench Touch를 쓰려면 결국 ExtJS가, Dijit(Dojo Widget)을 쓰려면 결국 dojo가 필요하게 되는데, 말 그대로 배보다 배꼽이 더 큰 상황이 됩니다. 또한, 데스크탑 브라우져 호환성이 부족하므로 xui.js는 모바일 웹에만 적용하고, 데스크탑 웹을 위해 별도의 자바스크립트 라이브러리를 사용해야 하므로 2중의 학습/개발이 필요하므로 장점보다는 단점이 커 보입니다.
여러 측면을 고려할 때, 새로운 모바일 웹 UI 툴킷을 자체 개발하거나, 새로운 범용 자바스크립트 라이브러리를 자체 개발한다면 좋은 기반(혹은 참고)가 될 수 있겠지만, 기존의 범용 자바스크립트 라이브러리(jquery, prototype, dojo 등...)의 대체품으로는 부적합 -_-;
지난 포스트에서는 CORS에 대해서 (아직 지원하지 않는 브라우져가 많다고 지레 짐작하고) 용어만 언급하고 넘어갔다.
최근에 올라온 "Methods of communication"이라는 글에 걸린 링크를 통해 지금 당장이라도 쓸 수 있음을 확인하고, 몇 자 적어보려고 한다.
Ajax에는 Same Origin Policy라는 원칙이 있다. 말 그대로, 현재 브라우져에 보여지고 있는 HTML을 내려준 웹서버(Origin)에게만 Ajax 요청을 보낼 수 있다.
MS가 XMLHttpRequest를 처음 만들 때만 해도 이런 제약은 당연한 것처럼 보였지만, 지금에 와서는 OpenAPI를 통한 매시업(Mashup)이 활성화되는 데 가장 큰 장애물이 되었다. 매시업이 아니더라도 여러 개의 도메인을 사용해야 하는 대규모 사이트를 개발할 때도 골치거리였다. Same Origin Policy를 우회하는 방법으로 JSONP, IFRAME IO, CrossDomain Proxy 등이 고안되었지만, 보안성이 취약하다거나, 동기 호출이 안되거나, 주고 받는 데이터 형식이 제한되거나, 직관적이지 못하거나(dirty hack), ... 등의 문제점 때문에 표준화되기엔 무리가 있었다.
(중략) 한 참 뒤에야 W3C는 (MS의 IE가 제공하는 방식을 수용하여) 크로스도메인간에도 Ajax요청을 주고 받을 수 있는 방법을 표준화 했는데, 그것이 바로 CORS다.
CORS를 한 마디로 요약하면, "요청을 받은 웹서버가 허락하면 크로스도메인이라도 Ajax로 통신할 수 있다"라는 정책이다. 기술적으로는 크로스도메인에 위치한 웹서버가 응답에 적절한 Access-Control-Allow-류의 헤더를 보냄으로써 크로스도메인 Ajax를 허용 수 있다.
말이 뺑뺑도는 느낌인데, 예를 들어 보자(코드를 줄이기 위해 jQuery를 사용했지만 XMLHttpRequest를 직접 사용해도 마찬가지다). Ajax 요청을 보내는 one.html을 내려 준 a.com이 오리진 웹서버다. 이 요청을 받는 b.com이 크로스도메인 웹서버다. a.com에서 b.com으로... 그래서 크로스-도메인이다.
보다시피, Ajax 클라이언트 측에는 추가적으로 할 일이 없고, 서버 측에서 할 일도 많지 않다. 웹서버(b.com)이 응답에 Access-Control-Allow-Origin헤더를 통해 모든 Origin(*)에서 오는 요청을 허락했으므로 다른 도메인(요청을 보낸 one.html을 내려 준 a.com)과 Ajax로 통신을 할 수 있다. 플래시에 익숙한 개발자라면 crossdomain.xml 을 떠오를 것이다. 맞다. 사실상 똑같다.
CORS는 FF 3.5+, 사파리 4+, 크롬 등의 대부분의 현대적인 브라우져에서 지원한다. IE의 경우엔 IE8부터 지원하는데, XMLHttpRequest대신 XDomainRequest객체를 사용해야 한다. 즉, 거의 다 된다! IE6,7만 무시하면... oTL
CORS 표준에 따르면 Origin외에도 HTTP 인증 여부, HTTP 메소드(GET, POST, ...), 특정 헤더 존재 유무 등의 다양한 기준으로 접근을 허용(preflight request)할 수 있는데, IE8은 이 스펙을 지원하지 않는다. -_-; CORS가 IE의 비표준 확장에 근거해서 만들어졌다는 점을 생각해보면.. 개그도 이런 개그가... -_-; IE9은... 아직 확인해보지 못했다.
클라이언트 대신 서버 측에서 뭔가를 해야 한다는 것은 장점인 동시에 단점인데, 기존의 수많은 OpenAPI들을 활용하고 싶어도 제공자들이 CORS를 적용하기 전까지는 무용지물... -_- OpenAPI를 제공하고 있거나, 제공할 계획이라면 JSONP 뿐 아니라 CORS도 고려해야 할 듯...
처음 언급된지 십년이 다 된 "Web as a Platform"이 실감나는 요즘이다. 팀 버너스 리가 원하던 원치않던... 웹은 점점 플랫폼으로 진화(혹은 변신)하고 있다. 별것 아닌 CORS도 이런 관점에서 보면 조금은 다르게 보일지도...
앱스프레소에서는 개발의 편의를 위해 확장 API를 제공하는데, 네트웍 관련 API들은 ax.ext.net에 모여있습니다.
이 글에서는 예제를 통해 크로스도메인 AJAX 요청을 지원하는 앱스프레소 확장 API들에 대해 알아보겠습니다.
중요: 첨부된 예제의 src/apis 폴더 아래에 여러개의 js 파일들이 들어있는데, 이는 기존에 배포된 Appspresso SDK의 버그로 인해 사용할 수 없었던 확장 API를 사용할 수 있도록 임시로 제공되는 것이며, 이후에 배포될 Appspresso SDK에서는 이 js 파일들이 없어도 확장 API를 사용할 수 있습니다.
예제 웹앱 프로젝트
AxExtNet.zip 파일을 Appspresso SDK의 Workspace 디렉토리에 압축을 풀고, File/Import/Existing Projects into Workspace로 이클립스 프로젝트로 추가하세요.
src/index.html에 이 글에서 설명할 ax.ext.net.get/post/curl API를 사용한 예제 코드가 모두 들어있습니다.
첫번째 인자로 지정한 URL로 GET 요청을 보냅니다. 요청이 성공하면(서버의 응답 코드와 무관하게, 요청이 서버로 보냈고 응답을 받았으면) success 콜백함수가 호출되고, 실패하면 error 콜백함수(생략가능)가 호출됩니다. success 콜백 함수로 전달되는 인자의 data, status, headers 등의 속성을 이용해서 응답의 세부 결과를 확인할 수 있습니다.
ax.ext.net에는 이 글에서 설명한 것 외에도 업로드/다운로드(진행 상황을 확인까지), 메일 보내기 등의 기능을 제공합니다. 이 확장 API들을 활용하기 위해서는deviceapis.filesystem를 함께 사용해야 하는데, 다음 기회에 별도의 예제와 함께 설명하겠습니다.
/*
막간을 이용해서 CSS 삽질~~ just for fun~~
나름 삽질한다고 했는데... 해서 만들었는데... 아무도 안쳐다보는 것 같아서... ㅠㅠ
결과 동영상을 짤방으로~~(웅장한 사운드 주의!!!!)
*/
웹킷 CSS 애니메이션으로 스타워즈 오프닝 크롤 구현하기
CSS3에는 자바스크립트의 도움없이 애니메이션 효과를 구현할 수 있는 방법이 추가되었다. 대표적인 것인 transition과 animation이다. transition은 간단한 전환 효과를 구현할 때 주로 사용되고, animation은 플래시와 유사한 키프레임 기반 애니메이션을 만들 때 사용된다.
21세기, 머나 먼 은하계 저 멀리... IE6가 세계에서 두번째로 많은 대한민국의에서 HTML5와 CSS3의 멋진 기능들이 그림의 떡일 뿐이라고 생각하던 시절이 있었지만...
우리에겐 모바일 웹이 있다! 스마트폰, 태블릿등의 모바일 단말의 브라우져는 거의 대부분이 웹킷 기반이다. 그리고 그 숫자는 기존의 PC 숫자보다 훠~ㄹ씬 많다. 적어도 모바일 웹에서만큼은 마음놓고 HTML5와 CSS3를 써도 된다!!고 말하고 싶지만... -_- 구글의 크롬은 아직 3차원 변형을 지원하지 않는다. OTL
각설하고, 이 글에서는 웹킷의 CSS 애니메이션, 정확히는 -webkit-animation과 -webkit-keyframes, -webkit-transform, -webkit-perspective 등을 활용해서 스타워즈의 오프닝 크롤을 구현해 보려고 한다.
백문이 불여일견! 소스부터 보자! 먼저, 롱~ 타임 어고 파 파~ㄹ어웨이 어쩌고 하는 부분이다.
@-webkit-keyframes starwars_galaxy { from { opacity:1; } 50% { opacity:1; } to { opacity:0; } }
-webkit-animation-name:<애니메이션_식별자> 은, 노드가 표시될때 지정한 애니메이션을 수행하라는 의미다. 구체적인 애니메이션 내용은 @-webkit-keyframes <애니메이션_식별자> { … } 형식으로 정의한다. 애니메이션에서 핵심이 되는 프레임은 from(혹은 0%)에서 to (혹은 100%)까지 순차적으로 애니메이션을 수행되며, 중간 과정은 (플래시의 in-between과 비슷한 원리로) 자동으로 만들어진다. -webkit-animation-duration:<재생시간> 은 지정한 애니메이션 전체를 재생하는 시간이다. -webkit-animation-timing-function:<타이밍함수> 은 시간이 점점 빠르게 혹은 느리게 가는 등의 효과를 지정한다. -webkit-animation-delay:<지연시간> 은 애니메이션이 시작되기 전까지의 지연 시간을 지정한다. 위의 예는, 시작하면(from) 화면에 #galaxy 레이어의 표시(opacity:1)하고, 1.5초(50%)이후 부터 투명도를 조절하기 시작해서(opacity:1) 마지막(to)에는 화면에서 사라지는(opacity:0) 애니메이션이다.
다음으로, STAR WARS라는 아웃라인 로고가 익스트림 클로즈업에서 빠르게 멀어지는 장면이다.
#title { position:absolute; display:block; left:-400%; top:30%; width:900%; height:100%; color:transparent; font-size:10em; -webkit-text-stroke-width:0.05em; -webkit-text-stroke-color:#ff3;/*XXX: this will not animated! :'( */ text-align:center; white-space:nowrap; overflow:hidden; opacity:0; -webkit-animation-name:starwars_title; -webkit-animation-duration:7s; -webkit-animation-timing-function:ease-in-out; -webkit-animation-delay:3s; }
@-webkit-keyframes starwars_title { from { font-size:1000em; opacity:1; } 90% { font-size:0; opacity:1; } to { font-size:0; opacity:0; } }
#title > p { font-family:'Orbitron',sans-serif; font-weight:900; }
좀 더 복잡해보이지만 기본적으로는 동일하다. 타이밍 함수를 easy-in-out으로 지정해서 애니메이션의 시작과 끝은 빠르고 중간은 천친히 진행되도록 지정한 것, -webkit-text-storke 으로 텍스트의 윤곽선을 표시하는 것, 웹 폰트를 활용하는 것 정도가 특이한 부분이다.
@-webkit-keyframes starwars_crawl { from { top:50%; opacity:1; } to { opacity:1; top:-100%; } }
-webkit-perspective:<투영된_상단_너비> 는 2차원 평면을 원근법에 따른 3차원으로 투영할 때 상단의 너비를 지정한다. 하단의 너비는 이에 맞게 늘어나거나 줄어든다. -webkit-perspective-origin:<소실점_가로_위치> <소실점_세로_위치> 은 3차원 투영의 기준이 기준이 되는 위치(소실점)를 2차원 평면을 기준으로 지정한다. perspective는 머리 속으로 상상하기가 쉽지 않으므로 임시로 background/border 등을 설정해서 투영 전과 투영 후에 영역을 확인하는 것이 편하다. 위의 예는, 가로 60% 세로 60% 너비의 2차원 평면을 상단 가운데(50% 0%)가 다섯글자 너비(5em)인 3차원 평면(하단은 이에 맞춰 늘어나므로 밑변이 넓은 사다리꼴이 만들어진다)을 만든다. 애니메이션 자체는 단순하다. 10초동안 기다렸다가 40초에 걸쳐서 상단 좌표(top)을 50%(화면 아래에서) 위치에서 -100% 위치(화면 위로 텍스트들이 다 사라질 때까지)로 바꾸는 애니메이션이다. 이 과정이 3차원으로 변형된 평면 위에서 애니메이션되면 꽤 그럴 듯하게 보인다.
백견이 불여일RUN! 결과를 “눈"과 “귀”로 확인하자! 안타깝지만, 데스크탑용 사파리에서만 제대로 확인할 수 있다. 꽤 웅장한 사운드가 나오니 조용한 곳에선 주의 요망!!
아이폰이나 아이패드에서도 되어야겠지만, 마지막 자막이 올라가는 부분이 의도한대로 동작하지 않았는데, 화면을 톡톡 탭하면 정상적으로 자막이 올라가는 걸로 봐선 버그인 듯... 이 부분에 대해 명쾌한 해결책을 알려주시는 분에게 보라매 공원 반경 1km이내에서 별다방급 커피 1회 빌붙기 허용권을 증정하겠다. -_-;
사파리 깔기 싫다는 분들을 위해서 유투브 동영상도 준비했다 -.-v 역시나, 꽤 웅장한 사운드가 나오니 조용한 곳에선 주의 요망!!
이 글에서는 웹킷이 제공하는 CSS 애니메이션을 이용해서 스타워즈의 오프닝 크롤을 구현해보았다. 이미지와 자바스크립트를 조금 쓰면 훨씬 더 그럴듯한 결과를 만들 수도 있겠지만, 웹앱과 CSS의 가능성을 보여준다는 의미에서 의도적으로 사용하지 않았다.
“하이브리드 모바일 웹앱”이라는 생소한 용어를 떠들고 다니다 보니, “네이티브로 하면 훨씬 더 쉽게 빠르게 만들 수 있는데 왜 웹으로 할려구 그러지?” 라는 얘기를 많이 듣는다. 반은 맞고 반은 틀린 얘기다. 웹으로 모든 걸 다할 순 없다. 그러나 우리가 일상적으로 접하는 앱의 90%는 웹으로도 충분히 만들 수 있다. 그리고 대부분의 경우에는 더 쉽다. 설령 조금 더 어렵다고 하더라도 그렇게 만들어두면 (아직은 좀 먼 얘기지만) 웹브라우져만 있는 환경이라면 어디에서라도 접근할 수 있다.
페이지 컨트롤(UIPageControl; Carousel; 일명 회전목마 컨트롤)은 아이폰이나 안드로이드폰을 쓰면 가장 먼저 접하게되는 UI 컨트롤이다. 일명 홈 스크린이라고 불리는 화면에서 가로 또는 세로로 플리킹(flicking)하면 이전/다음 페이지로 이동하는 그 컨트롤이다. 아이폰의 경우에는 화면 하단에 하얗고 까만 작은 동그라미가 있고, 안드로이드는 화면 상단 좌우에(폰에 따라 조금씩다르다) 작은 동그라미가 있어서, 총 몇 페이지 중에서 몇 번째 페이지를 보고 있는 지를 알려준다.
이 글에서 설명하는 방법을 안드로이드용 푸딩얼굴인식 앱을 만들면서 활용했는데, 데모 동영상에서 35초와 1분 10초 근처에 나오는 화면이 이 컨트롤을 활용한 것이다.
이 글에서는 iScroll 라이브러리를 활용하여 페이지 컨트롤을 구현하고, 덤으로 아이폰과 유사한 인디케이터 까지 구현해 보았다.
모바일 단말은 화면이 작기 때문에 이런 유형의 컨트롤(탭/Carousel/...)들이 꽤 다양하고, 또 유용하지만, 직접 만들자면 쉽지 않다. 그렇다고 이 컨트롤 하나 때문에 Sencha나 jQueryMobile 같은 덩치 큰 프레임웍을 사용하는 것이 부담스럽다면, iScroll 라이브러리는 한 번 쯤 살펴볼 가치가 있다.
아이폰/아이패드/아이팟의 모바일 사파리와 안드로이드의 모바일 크롬 등은 모두 터치기반 모바일 웹킷을 사용하는 브라우져들이다. 이 브라우져들은 버튼 등을 눌렀다(touchstart) 떼도(touchup) 즉시 반응(click)하지 않는데, 그 이유는 연속되는 터치 동작(touchstart-touchmove-touchend)들이 제스쳐(swipe, long click, …)인지 여부를 확인하기 위해 최대 300ms의 지연시간이 생기기 때문이다. 모바일 웹 사이트를 만드는 경우라면 이 정도의 지연시간은 크게 문제가 되지않지만, 상대적으로 신속한 반응을 요구하는 “웹앱"이라면 얘기가 달라진다.
해결책은 간단하다: 1. 손가락으로 무언가를 누르면(touchstart) 2. 웹킷의 기본 동작(300ms 지연)을 못하게 막고(preventDefault) 3. 움직임(touchmove) 없이 4. 손가락을 떼면(touchend) 5. 클릭(click)으로 간주한다.
touchstart/touchend을 mousedown/mouseup으로 바꿔놓고 보면 HTML5 이전에 drag & drop을 처리하기 위해서 사용하는 방식과 유사하다.
다시 말하지만, 위의 페이지는 터치기반의 모바일 웹킷, 즉 모바일 사파리나 모바일 크롬으로 봐야 효과가 있다.
일단 눈에 보이는 Slow Button과 Fast Button 각각을 평소대로 눌러보자. 클릭을 감지하는데 걸린 시간이 표시되는데, Fast 쪽이 조금 빠르긴 하지만 큰 차이는 없어 보인다. 이번에는 평소보다 좀 더 손끝에 감각을 모아서 “톡톡 두드린다는 느낌”으로 각 버튼을 눌러보자. Slow Button의 경우엔 아예 반응을 하지 않거나 확대(아이폰등에서는 더블탭이 자동 확대/축소)기능이 동작된다. Fast Button은 그런 동작을 하지 않는다.
이 글에서는 touchstart-touchmove-touchend 이벤트를 활용하여 버튼의 반응속도를 개선하는 방법을 알아보았다. 이 세가지 이벤트의 활용법을 잘 익혀두면 다양한 유형의 터치 입력을 처리하는데 많은 도움이 될 것이다.
이런 쓰잘데기 없는 동영상을 굳이 만들어 올리는 이유는 "웹 앱"(WebApp; HTML5App)이 그렇게 거창한 것도 아니고, 어려운 것도 아니고, 멀리 있는 남의 나라 이야기도 아니라는 것을 보여주기 위해서다.
PhoneGap, Titanium, QuickConnect 같은 거창한(?) 제품을 동원하지 않더라도 JQueryMobile, Jo, Wink, Sencha Touch 같은 UI 툴킷과 HTML5 canvas 태그 그리고 HTML5 JS API들(WebStorage, WebSQLDatabase, WebWorker, ...), 그리고 W3C의 DAP(Geolocation, ...)를 사용하면 웬만한 앱은 만들 수 있다.
풍경 1. 아이폰이 국내에 출시된지 1년도 안됐는데... 아... 아이폰 없던 시절이 어땠는지 기억조차 가물가물... 먹고 살려니 아이폰 개발 공부는 해야겠는데, 망할 놈의 옵씨... 옵씨는 그렇다 치고, 코어 파운데이션, 코어 그래픽스, 코어 애니메이션, 뭔 코어가 이렇게 많냐? 핵분열도 아니고... OTL
풍경 2. 없는 살림에 거금 10만원 들여 아이폰 앱 개발자 등록해서 1년 동안 앱 3개 겨우 올렸는데... 안드로이드가 대세? 열라 안드로이드 공부해서 앱 좀 올려 볼려니... 안드로이드 마켓은 뭐고 티스토어는 뭐고 올레마켓은 또 뭐냐? 그까이꺼 대충~ 눈감고 넘어가려니... 블랙베리? 심비안? 팜프리? 윈폰7? 바다? OTL
풍경 3. 아래아한글 새 버전 나온 줄 알았던 넷스케이프와의 첫만남, 문자열과 한판 승부를 벌였던 CGI 시대, ASP, PHP, JSP... OOO 서버 페이지 시대, 유행따라 삼만리 AJAX 시대... 어느새 HTML만으로 어플리케이션을 만드는 시대??? 먹고살려니 안할 순 없고... OTL
풍경 4. 스마트폰(그게 뭔데? 먹는거냐?)용 앱을 만들긴 만들어야 겠는데.. 새로운 언어 배우기도 귀찮고, API는 낯설고, 당장 할 줄 아는 건 웹... 그래서 웹 기술만으로 앱을 만들 수 없을까... phonegap은 또 뭐고 titanium은 또 뭐고, WAC은 또 뭐냐? OTL
Palm WebOS, Safari(iOS,Desktop,Dashboard), Chrome(Android,Desktop) 등 webkit 기반 브라우져만 지원.
경량
자바스크립트 41K(최소화된 버전) + UI CSS/리소스(176K)
PhoneGap 호환(?)
PhoneGap과 호환되지 않는 자바스크립트 라이브러리는 ”없음”.
주요 기능
UI
최근 모바일 단말 UI의 de-facto인 iOS의 UI을 염두에 둔 UI 툴킷. 하나의 화면(joScreen)은 상단의 헤더(joTitle, joToolbar)와 푸터(joFooter, joToolbar)로 구성. 화면과 화면 사이를 앞뒤(위아래)로 이동하는 네비게이션 레이아웃(joStackController, joStack). 기본적인 HTML 폼의 입력 필드에 대응하는 위젯들(joControl, ...), 페이지 내부 팝업(joPopup) 등의 필수적인 위젯 제공. CSS를 통한 테마/스킨 지원. 캘린더, 챠트 등의 고급 위젯 부족.
joContainer
joCard
joFlexcol
joFlexrow
joFooter
joGroup
joPopup
joScreen
joScroller
joStack
joStackScroller
joTitle
joToolbar
joControl
joButton
joCheckBox
joExpando
joHTML
joInput
joDateTime
joPasswordInput
joTextarea
joLabel
joList
joMenu
joTabBar
joTable
joKnob
joSlider
joDivider
joShim
Data
HTML5의 WebSQLDatabase(Webkit 기반 브라우져에는 대부분 제공됨)를 통한 로컬(클라이언트측) 데이터베이스 접근 제공. 추후에 PhoneGap과 연계하여 로컬 파일을 통한 데이터 접근을 제공할 계획으로 보임(joFile, joFileDataSource) 원격(서버측) 데이터 접근 부재.
joDatabase
joDataSource
joFileDataSource (x)
joSQLiteDataSource
joFile (x)
Utility
기본적인(필수적인) 유틸리티 API 제공. 추후에 PhoneGap과 연계하여 모바일에 특화된 기능을 제공할 계획으로 보임.
weak typing은 아주 편리한 언어적 특성이지만, 잘못쓰면 - 특히 strong typing(C, 자바 등의 대부분의 주류 언어)에 익숙한 개발자들에게 - 치명적인 독이 될 수 있다. underfined, null, NaN 그리고 ""(빈문자열) 간의 차이는 상당히 오묘하므로, 가장 확실한 방법은 확신이 없다면 명시적인 형변환을 수행하는 것이다.
nested block scope
function test() {
var foo = -1;
for (var foo = 0; foo < 10; foo++) { … }
alert(foo);
}
위의 코드에서 두번째 var 선언문은 아무 효과가 없다. 따라서, -1이 아니라 10이 출력된다. 별것 아닌것 처럼 보이지만, 문제가 되면 굉장히 찾기 어려운 버그다. jslint를 사용하면 경고를 해주는 데, 가장 확실한 방법은 모든 변수 선언을 함수 앞에 모아두는 것이다(예전 C 스타일).
function overloading
function test(foo) {
alert('one arg');
}
function test(foo, bar) {
alert('two args');
}
function test() {
for (var i = 0; i < arguments.length; i++) {
alert(arguments[i]);
}
}
위의 코드에서 앞에 두개의 함수 정의는 무시되고, 마지막의 세번째 함수 정의만 유효하다. 자바스크립트에서는 arguments를 사용하여 가변 파라메터를 처리할 수 있다. 그러나, 더 좋은 방법은 이름있는 파라메터를 사용하는 것이다. 즉, 객체 하나에 파라메터의 이름과 값을 함께 전달 하는 것인데, 요즘 많이 쓰이는 대부분의 자바스크립트 라이브러리들이 모두 이 방식을 선호한다.
"string" is not array-of-chars
var str = "hello";
alert(str.charAt(2));
alert(str[2]);
위의 코드에서 앞의 alert은 출력되지만, 뒤의 alert은 에러다. 잘된다고? IE에서는 해보시길...
자바 개발자들에겐 익숙할텐데, C/C++ 개발자들은 좀 귀찮을 듯... ^^;
비슷한 예로, NodeList(예: document.getElementsByName() 등의 리턴 형식)도 배열이 아니다.
뭐가 이상하냐고? 자바에서 똑같은 짓을 해보자. -.-;;;; 자바에선 Integer.parseInt()에 radix를 지정하지 않으면 10을 지정한 것으로 처리하지만, 자바스크립트에선 알아서 하라는 뜻으로 해석한다.
var sum = parseInt(num1.value) + parseInt(num2.value);
alert(sum);
위의 코드는 의도했던 안했던, 8진수와 16진수 계산기 기능도 갖고 있다. ^^; 이런 사소한 문제가 찾아내기 힘든 버그를 만든다. jslint를 사용하면 경고를 해주는데, 가장 확실한 방법은 parseInt()를 호출할 때 무조건 두번째 파라메터(radix)를 지정하는 것이다.
자바스크립트의 this는 함수가 "선언된 위치"가 아니라, 함수가 "호출 문맥(invocation context)호출된 위치"에 따라 결정된다.
위의 코드에서, 사용자가 버튼을 누르면 button.onclick에 연결된 콜백 함수를 호출하는데, 그 시점의 this는 함수가 선언된 위치인 test 객체가 아니라, 콜백 함수를 호출하는 "버튼"(DOM 노드)이다. 그래서 엉뚱한 값(버튼 태그의 name 속성 값)이 출력되는 것이다.
먼저, 주최측에서 WebSphere sMash라는 솔루션을 소개했다. 그러나, 생뚱맞은 REST에 대한 질문 답변에 시간을 다 써버리고 sMash는 맛도 제대로 못봤다. (발표하시느라 고생하신 분께는 죄송하지만)오늘 데모만 놓고 보면 그냥 Yahoo! Pipes 설치형 버전이라는 느낌... -.-; REST로 딴지거신 분...은... 다음에 비슷한 행사나 세미나에선 좀 참아주셨으면... 발표자도 나름대로 계획이 있다는...
이어서, 페차쿠차 형식의 발표들이 이어졌다. 숨을 헐떡이는 발표자들...을 보면서 걱정이 되기 시작했다.(대산님 발표 여러번 봤지만 이렇게 힘들어하시는 건 처음 봄 :D) 아이팟 터치 타이머 켜놓고 한 번 연습하긴했는데.. 제비뽑기로 순서를 정하다보니... 나는 끝에서 두번째... 혹유랑 소곤거리느라 긴장도 다 풀리고, 별 생각없이 30초*15장을 넘기고 내려왔다. 듣는 사람은 어땠는지 모르겠음=3=333 짧은 시간에도 불구하고 좋은 내용을 핵심만 꼭꼭집어 발표하시는 분들... 대단하심 @..@)b
계속해서, 개발자들의 수다가 이어져야 하는데... -.-? 예전에는 몇가지 주제를 걸어놓으면 원하는 그룹에 끼어서 그 주제에 대해서 토론하는 방식이었는데... 이번에는 앞 순서의 발표자들을 앞에 앉혀놓고 질문 답변 시간... 쉽게 말해 "미수다" 스타일-.-;;; 나는 형식이 바뀐 줄도 모르고, "개발자의 커리어 패스 관리, 로드맵, 창업" 등에 대한 다양한 얘기들이 오고 가는 동안, "너무 옆길로 새는거 아닌가~"하면서 입 꾹 닫고, 진행자(우일님)만 바라보고 있었다는... 끝날 때쯤 되서야 상황을 파악하고, 딸랑 한마디~ 했더니... 행사 종료... 본의 아니게 클로징 멘트를 -.-;;;;;
행사가 끝나고, 발표자들은 기념품(8G USB 메모리! 막대기 아님!) 주최측 + 발표자들 + 꼽사리들은 근처 고급(!) 반점에 가서 맥주 or 고량주를 한 잔씩하고, 자장면 or 짬뽕 or 볶음밥을 먹고~(이게 오늘 나의 첫 끼니였다ㅠ.ㅠ) 후다닥~ 집으로~ 고고씽~
아무튼, 이런 류의 작은 행사들이 KOEX에서 하는 행사 보다 훨씬 재미도 있고~ 유익하다는 사실을 재확인했다.
오타가 잔뜩 있는 문장을 입력한 다음, 툴바의 오른쪽 끝에서 세번째 있는 필름롤 아이콘을 클릭하면 스펠 체커가 틀린 단어를 빨갛게 표시해 준다. 이 빨간 단어를 클릭하면 추천 목록이 나오고, 추천 목록에서 맞는 단어를 클릭하면 해당 단어를 선택한 단어로 대치한다. 웹 기반 스펠 체커 API는 익숙한 환경이라 금방 만들었는데, 다음 오픈 에디터의 구조에 익숙하지 않아서(오후에 코딩해야하는 줄 모르고, 오전 세미나를 대충 들어서 ㅠ.ㅠ) UI가 엉망이다. UI를 전반적으로 손을 많이 봐야 할 듯...
그날 만든 다음 오픈 에디터 예제에, 간단한 API 사용 예제와 자바독 문서, 관련 링크 등을 추가해서 급하게 프로젝트 홈페이지도 만들었다.
덧: 지금은 개인적으로 호스팅하고 있는 열라 후진 서버(셀2.66G/1.5G)에 올려놓았는데, 좀 쓸만한 서버를 확보할 수 있으면 올려두고, 여기저기 웹 프로젝트에서 써 먹을 수 있을 듯...한데, 혹시 도와주실 분 계신가요? ^^;
"Chrome Frame"이라는 이름의 IE 플러그인(Active-X)이 그것인데, 기술적으로는 Firefox의 IE Tab 확장과 비슷하지만, IE Tab은 사용자가 명시적으로 IE로 보겠다고 해야만 활성화되지만, Chrome Frame은 색다른(?) 접근 방식을 제안한다(물론, 두가지 방식 모두 Chrome Frame이 깔려있을때만 동작한다):
1. (사용자가) URL 앞에 "cf:"를 붙인다. 예를 들면 http://acid3.acidtests.org/ 하면 IE가 X같은 반응을 보이지만, cf:http://acid3.acidtests.org/ 하면 잘 된다.
2. (개발자가) HTML 페이지에 <meta http-equiv="X-UA-Compatible" content="chrome=1" /> 메타 태그를 달아놓으면 해당 페이지를 보는데 크롬을 사용한다.
첫번째 방식은 GUI 메뉴를 URL로 옮겨놓았을 뿐, 결과만 확인하고 넘어가자:
Chrome Frame 없이 IE8로 Acid3 테스트에 도전!
IE8을 가장한 Chrome으로 Acid3 테스트에 도전!
두번째 방식을 주목해보자. 이 방식을 사용하면 Chrome Frame 플러그인만 깔려있다면 자동적으로 활성화 된다. 그렇다면 Chrome Frame 플러그인만 자동으로(강제로) 깔 수 있으면... -.-ㅋ 뭔가 구린 냄새가 솔~솔~ IE로 인터넷 뱅킹이나 쇼핑몰 결제할 때만 되면 난리 법석을 떨던 ActiveX의 *랄 발광을 역이용(!)할 수 있다. 금상첨화(혹은 설상가상) 우리나라 사용자들은 ActiveX 설치할 때 "보안 경고"가 뜨면 무조건 "예"라고 해야 한다고 배워서 실천하고 있으니~ =..=
CFInstall.check() 함수는 기본적으로 지정한 노드(여기서는 placeholder)를 설치 페이지를 표시하는 iframe 태그로 바꾸서 설치를 유도하고, 설치가 끝나면 destination 페이지로 이동한다(자세한 설명은 공식 개발자 문서를 참조하시라~).
이런식의 스크립트를 웹 서비스의 진입부에 적당히 끼워넣어 두면... "부끄러운" 대한민국의 웹(한국 웹의 불편한 진실 참조)이 우리에게 강요했던 방식 그대로~~~ 크롬을 쓰도록 강요할 수 있다 -.-ㅋ
그런데, 구글은 이런걸 왜 만들었을까? 무엇보다도 구글이 만든 대다수의 초첨단(?) 웹 어플리케이션들을 돌리기엔 IE(6,7은 말할것도 없고 8도 도토리 기재기)의 렌더링 / 자바스크립트 엔진이 너무 구리기 때문일 것이다.
Chrome Frame이 널리 보급되기엔 넘어야 할 산이 많다. 첫째, 사용자 경험이 너무 거칠다. 플래시의 경우처럼 매끈하게(뭔가 설치한다는 느낌을 주지않고) 설치할 필요가 있다. 설치 유도 스크립트(CFInstall.js)를 많이 다듬어야 할 듯... 둘째, 다음으로 77M를 넘어서는 설치 용량이다. 사용자의 컴퓨터에 구글 크롬이 설치되어 있다면 이를 그대로 활용할 수 있을 것이다. 아마 조만간 그렇게 되겠지? 셋째, 아직은 버그가 많다. 첫술에 배부를 순 없겠지...
하지만, 개인적으로 prototype이나 jQuery 보다는 dojo를 선호하기 때문에(대부분의 경우엔 별 차이가 없지만, 마이너리티 체질의 똥고집이랄까...) dojo로 Lightbox를 만들기로 했는데... 웬걸 -.-; dojox.image.Lightbox라는 녀석이 이미 있더라. 그냥 쓸까 하다가... 이 녀석이 dijit(dojo의 위젯 시스템; jQuery UI 쯤에 해당하는 모듈이다)에 의존하는 관계로 버림받고 있길래 간단히 하나 맹그러봤다.
솔직히... 만만하게 보고 시작했는데... 생각보다는 많이 복잡하더라. -,.-;;;
이름은 Lightbox와 비슷하면서도 dojo로 만들었다는 느낌이 들도록 Delightbox라고 지었다 ^^;
웹의 탄생이전 부터 이어져오는 흐름을 정리한다는데 의미를 두고 보면 의외로 볼만할지도...*^^*
현재까지 Web3.0에 대한 논의는 시맨틱 웹, 플랫폼으로써의 웹, 그리고 유비쿼터스 세상으로의 관문으로서의 웹, 세가지 흐름으로 볼 수 있다. 물론 세가지 논의가 서로 간에 밀접한 관련이 있어서 무 자르듯 자를 수는 없겠지만...
IMHO, 시맨틱 웹은 내가 이 바닥에서 먹고 살 동안은 "The Dream of Web"으로 남을 것 같다. 유비쿼터스 웹는 돈독이 오른 가전 업체들의 말장난 단계를 벗어나려면 좀 더 시간이 필요할 듯... 가장 현실적인 것은 (구글이 밀고 있는) 어플리케이션 플랫폼으로서의 웹인 것 같은데, 이건 웹3.0이기보다는 웹2.5 정도가 적당할 듯.
덧. 1.2 릴리즈 소식을 듣고 다운받으러 갔다가 무심결에 맨 위에 있는 링크를 눌렀더니... dojo.js -.-;;;;
prototype과 jquery 덕분에(?) 크기에 대한 압박이 심했나 보다. 아무튼 prototype.js나 jquery.js 대용이라면 dojo.js 파일 하나면 충분하고... dijit을 포함한 dojo 모든 것을 맛보려면 역시 풀버전~ ^^; 그 전에 눈요기가 필요하다면 Dojo Feature Explorer~ =3=333
확실히 jQuery 가 좋은 것 같은데... 하지만...
나는 dojo 빠~=3=3=333
덧1. 이번 글을 번역할 때 애먹은 단어는 carousel이다. 회전 목마... -.-;;;; 저게 뭘로 봐서 회전 목마냐고...
궁여지책으로 회전식 슬라이드쇼라고 번역했지만, 찝찝하다.
편집자께서 알아서 고쳐주시겠지 했는데... 그대로다.
뾰족한 대안이 없었나 보다. 좋은 단어 추천 요망~.~/
덧2. 연재 1~2부를 번역할 때 overhaul을 어떻게 번역할 지를 고민하다가, 그냥 한글로 "오버홀"이라고 해다.
이번에는 편집자께서 제안해주신 "분해 수리"로 바꿨는데, 오늘 올라온 기사에는 "전면 개편"이라고 바뀌었다.
흠... 그런데... 뭔가... 좀...
이 연재에서 거듭 강조하는 핵심 중의 하나는 "전면" 개편이 아니라 "점진적" 개편이라는...
웃거나 말거나...=3=3=33
dojo.declare(className, superClass, { members... }) : 주어진 이름을 가진 클래스를 정의한다. 클래스 이름은 문자열로, 수퍼클래스는 클래스 객체로 지정해야 한다. 수퍼클래스가 없으면 null 또는 [ ] (빈 배열)을 지정하면 된다. 하나 이상의 수퍼클래스(정확히는 하나의 수퍼클래스와 여러 개의 믹스인을 배열로 지정할 수도 있다.
위의 예는 Point라는 이름의 클래스를 정의한다. 이 클래스는 수퍼클래스가 없으며, 하나의 생성자(constructor)와 x, y 두 개의 속성(멤버 변수)을 가지며, moveTo, equals 세 개의 메소드(멤버함수)를 가진다. 키워드는 declare(선언)이지만, 실제로 하는 일은 선언이 아니라 정의(define)다. 오히려 dojo.provide가 선언에 가깝다. 이 글의 주제에서 벗어나는 얘기니 궁금하면 구글신께 물어보시라... 나중에 시간나면 따로 쓰기로 하자~
이 클래스를 사용하는 예를 보면:
var p1 = new Point(1, 2); var p2 = new Point(); p2.moveTo(3, 4); alert(p1.equals(p2)); // false
위의 예는 Rect라는 이름의 클래스를 정의한다. 이 클래스는 Point를 상속받으며(x, y 두 개의 속성과 moveTo, equals 두 개의 메소드), 추가로 w, h 두 개의 속성과 getRight, getBottom, resize 세개의 메소드를 정의하며, equals를 오버라이딩(재정의)하고 있다. equals에서 this.inherited(arguments)라는 생소한 구문이 보이는데 부모클래스에서 정의한 메소드를 호출하는 것이다. 자바로치면 super(rect)와 비슷하다고 보면 되겠다. 생성자에서 super를 호출하지 않는다는 점도 주의해야한다. 아무말 안해도 무조건 super를 호출한다. 자바스크립트는 함수 오버로딩(중복정의)을 지원하지 않기 때문에 생성자를 하나밖에 가질 수 없다.
객체 관련 유틸리티 함수들이 여러가지 있지만, 개인적으로 한 번이라도 써 본적이 있는 녀석들만 간단히 알아보면:
dojo.delegate(obj, { members...}) : 주어진 객체를 상속받은 객체를 만든다. dojo.declare와 달리, 클래스가 아니라 인스턴스 객체를 만든다. 간단하게 몇 가지 속성이나 메소드만 재정의한 객체를 만들때 편리하다.
dojo.clone(obj) : 주어진 객체의 deep-copy 객체를 만든다.
dojo는 하나 이상의 클래스를 하나의 .js 파일에 넣어두고, 이를 동적으로 로딩하는 기능을 제공하는데, dijit 설명할 때 자세히 알아보기로 하고... 여기서는 dojo 관련 소스들을 보다가 자주 보게될 함수 몇가지만 알아보자:
dojo.require(className) : 현재 모듈(나중에 설명한다)에서 주어진 이름의 클래스를 참조(사용)한다. 자바의 import와 비슷하지만, 자바와 달리 와일드카드(*)를 사용할 수 없다(dojo 0.4.x 쓰시던 분들도 주의!). 일반적으로 dojo.require('dijit.form.TextBox')라고 하면 dojo디렉토리/dijit/form/TextBox.js를 로딩한다고 이해해도 무방하다.
dojo.provide(className) : 주어진 이름의 클래스를 선언한다. 즉, 현재 모듈(나중에 설명한다)에서 주어진 이름의 클래스를 정의할 것임을 미리 알린다. 실제로는 빈 객체를 정의하기 때문에 네임스페이스 선언을 위해서 사용한다.
위의 두 함수는 dojo custom build에서도 매우 중요한 역할은 한다.
오늘은 여기까지~
다음에는 아기다리고기다리던 dijit(dojo widget)이다.
덧. 자바스크립트로 진지한 코딩을 해 본 개발자라면 IE의 trailing comma error의 공포를 알고 있을 것이다. dojo.delcare도 조심해서 쓰지 않으면 좌절을 맛보게 된다. 조만간 동의하실 것이라개인적으로 즐겨쓰는 방법은: