Rev. 2.73

 

다음에서도 지도 API를 오픈했습니다. 경품 이벤트도 하는군요. 응모도 해볼 겸해서 지난번 "네이버 맵 API 사용해 보기"에서 만들었던 클래스를 이용하여 추가로 만들어 보았습니다. 이 클래스를 사용하면 다음과 네이버의 지도를 모두 다룰수 있습니다. 다음 예제를 보시죠.

Examples:

  new Maps('my-maps', {
    type: 'daum', // naver or daum 
    key: '1176f6b78db03f01382ee44afe2f82b98b8b899d', // API Key
    point: {x: 498637, y: 1116620, z: 3}, // coordinates and zoom level

    framesrc: 'robots.txt', // for validate url

    zoomwheel: true, // add whell zoom controll(default true)
    zoomslide: true, // add zoom controll(default true)
    minimap: true, // add index map controll(default true)

    // callback for customize
    callback: function(map, win) {
      win.DEvent.addListener(map, "move", function() { 
        var center = map.getCenter(); 
        debug.info("latitude : " + center.getLat() + "  longitude: " + center.getLng());
      });
    }
  });

type, key, point는 필수 옵션이며, 각각 API종류, API키, 좌표 및 줌레벨 값을 입력합니다. framesrc에 값을 넣게 되면, iframe을 사용해서 구동하게 됩니다. 이것은 특정 API에서 발생하는 Doctype으로 인한 문제 또는 스크립트 충돌 등을 해결하기 위한 것입니다. iframe 사용시 주의할 점은 임의의 파일을 호출할 수 있는 경로를 기입해야 합니다. 'about:blank'와 같이 지정되어버리면 location.hostname을 호출할 수 없어 맵 API의 URL 인증이 무효가 되어버리기 때문입니다.

이번 작업에서 발생한 가장 큰 문제는 maps.js가 들고있는 유틸리티 함수들이었습니다. 지도 API 시작하기 예제문서를 보면 DIV 요소에 지도를 추가하는 예제들이 있습니다. 이런 경우 루비온레일즈나 Prototype 라이브러리를 사용하는 사이트들에서는 충돌을 일으킬 가능성이 매우 높습니다. Prototype의 코드들을 상당수 오버라이딩 해버리기 때문이지요. 결국 다음 지도 API역시 iframe으로 처리해야 했습니다. 전역스러운(?) 네임스페이스의 소비를 개선하거나 충돌이 일어날만한 코드들은 우회하는 추가작업이 필요해 보입니다.

다음 지도 API의 장점은 구글맵의 좌표계인 WGS84, 네이버 지도의 좌표계인 KTM 등 자바스크립트 기반 좌표계변환기를 사용할 수 있어 여러 서비스간 호환성이 좋다는 점과, SVG를 사용하여 매우 빠른 사선 그리기와 마킹작업이 가능한 것을 들수 있겠습니다. 다음 번에는 구글과 야후 맵도 통합하고, API간의 좌표 변환기와, 마킹 컨트롤러 등을 추가해 보겠습니다. 요것 가지고 노는 재미가 점점 쏠쏠해 지는군요.

Downloads:

* original - maps.js (40.1kb)
* minified - maps.js (29.2kb)

Updates:

* 2008.11.18 IE에서 프레임이 생성되지 않던 문제를 수정하였습니다.
* 2008.11.19 KTM/WGS84간 좌표계 변환기를 추가하여 두 지도가 하나의 위치를 공유할 수 있습니다.
* 2008.11.20 WGS84에서 KTM 좌표계로 변환시 오차를 야매로(?) 수정하였습니다.
* 2008.12.01 구글맵 API가 추가되었습니다.
* 2008.12.02 구글맵과 네이버, 다음 지도간 줌레벨 변환기가 추가되었습니다.
* 2009.02.04 구글맵 역시 아이프레임을 사용하지 않고도 로드 할 수 있습니다.
* 2009.02.04 구글지도에 한글 서비스를 반영하였습니다.
* 2009.02.05 한국지역 외 상세 지도가 나오지 않는 현상을 수정하였습니다.
* 2009.02.08 다음지도의 기본 좌표계를 'congnamul'로 변경하였습니다.
* 2009.02.08 좌표계 변환기에 'congnamul' 변환식이 추가되었습니다.
* 2009.02.10 구글맵 로드시 GClientGeocoder(지역 찾기)클래스를 map 인스턴스에 포함합니다.
* 2009.02.10 길찾기와 관련된 Maps.Route 클래스가 추가되었습니다.
* 2009.02.11 구글어스의 헬퍼 클래스인 Maps.Earth가 추가되었습니다.

Comments

지난 웹앱스콘 2008에 참관했다가 네이버 API 홍보 부스에서 얻은 책자가 눈에 밟혀 실험삼아 네이버 맵 API를 사용해 보았습니다. 제길... 초장부터 삽질입니다. IE가 아닌 브라우저에서 Doctype선언이 XHTML 1.1이면 정상작동하지 않습니다. 이러한 경우 아이프레임으로 처리하고 아래와 같은 사용법으로 Prototype기반 클래스를 호출하여 말끔하게 지도를 추가할 수 있도록 하였습니다. 이제 조금은 가지고 놀만 하네요.

Examples:

  new Maps('map-container', {
    type: 'naver',
    key: '6c253133aa1ff2d4882051b33b86da1f', // API Key
    point: {x: 311326, y: 547126, z: 3}, // coordinates and zoom level

    zoomwheel: true, // add whell zoom controll(default true)
    zoomslide: true, // add zoom controll(default true)
    minimap: true, // add index map controll(default true)

    // callback for customize
    callback: function(map, win) {
      win.NEvent.addListener(map, 'mouseup', function(x, y){
        if (window.console) console.log(x, y);
      });
    }
  });

Downloads:

* original - nmap.js (4.42kb)
* minified - nmap.js (3.16kb)

Updates:

* 2008.11.11 컨트롤러 옵션이 반영되지 않던 문제를 수정하였습니다.
* 2008.11.12 사파리, 구글크롬 브라우저에서 초기화 되지 않던 문제를 수정하였습니다.
* 2009.03.22 Doctype 문제가 해결되어 아이프레임을 사용하지 않습니다.
* 2009.03.22 maps.js를 사용하도록 변경하였습니다.

Comments

jQuery는 겸손한(unobtrusive) 자바스크립트를 위한 자바스크립트 라이브러리다. jQuery는 태생적으로 BDD(Behavior driven development) 방법론을 지향하고 CSS 셀렉터를 사용하여 HTML 문서를 훓는 방법(traversing)에 기반한다. 반면에 Prototype은 태생적으로 CDD(Class driven development)를 지향하고 쉽게 자바스크립트 개발할 수 있도록 해준다. Prototype 라이브러리는 Ruby on Rails에서 아주 잘 지원되고 많은 헬퍼 함수들을 가지고 있다.

- 내가 Prototype에서 jQuery로 옮긴 이유 중에서...

jQuery.js에서 $()를 호출할때마다 거대한 jQuery객체를 만들어내지는 않는다. jQuery 객체들은 prototype-based-inheritance를 통해서 jQuery를 상속받은 클론들이다. 이런 클론들은 delegation pointer를 가지고 원본(prototype)의 property와 method들을 참조하기 때문에 무겁지 않다.

- Prototype vs jQuery 중에서...

Prototype기반으로 만들어진 프로젝트를 jQuery로 옮기고 있습니다. jQuery로 만들어진 것을 Prototype용으로 컨버전(conversion) 하는 작업은 간간히 해 왔지만 이런 경우는 처음이네요. 위와 같은 이유들도 있겠지만 조금 더 보태자면 야속하게도 약 9개월 동안 이렇다 할 업데이트가 이루어지지 않는 것과 지금은 jQuery가 대세이기 때문이기도 합니다. 요즘 배포되는 자바스크립트 플러그인들을 보면 대부분이 jQuery기반이라 것을 눈여겨 보지 않아도 알 수 있을 정도입니다. Prototype에서 jQuery로 컨버전하는 동안 주요한 변경내역을 기록해 보았습니다. 참고로 처음에 위치한 코드가 Prototype이고 아래쪽에 위치한 것이 jQuery의 코드입니다.

DOM selector

$('element'); //=> DOM element object
$('#element'); //=> jQuery(Array-like Object) object

$('element'); //=> DOM element object
$('#element').get(0); //=> DOM element object

$('element1','element2','element3'); //=> Array
$('#element1,#element2,#element3'); //=> jQuery object

element.down('div.item'); //=> DOM element object
element.children('div.item'); //=> jQuery object

element.up('div.contents'); //=> DOM element object
element.parent('div.contents'); //=> jQuery object

element.up('div.container'); //=> DOM element object
element.parents('div.container:first'); //=> jQuery object

element.next('div.item'); //=> DOM element object
element.next('div.item'); //=> jQuery object

element.previous('div.item'); //=> DOM element object
element.prev('div.item'); //=> jQuery object

element.select('div.item'); //=> Array
element.find('div.item'); //=> jQuery object

$('element').select('input[type="button"]'); //=> Array
$('#element input[type=button]'); //=> jQuery object

$$('#element a[href=#]')[0]; //=> DOM element object
$('#element a[href=#]')[0]; //=> DOM element object

element.select('input.cancel', 'input.submit', 'a.close'); //=> Array
element.find('input.cance,linput.submit,a.close'); //=> jQuery object

element.nextSiblings('div.item'); //=> Array
element.nextAll('div.item'); //=> jQuery object

element.previousSiblings('div.item'); //=> Array
element.prevAll('div.item'); //=> jQuery object

초반에 가장 이해하기 힘든것이 DOM 선택자였는데, '$'가 마치 오퍼레이터(operator) 처럼 사용되는 것은 비슷합니다. Prototype의 '$'는 보통 document.getElementById의 대역으로 사용되며, Element.prototype의 메서드들을 상속시키는 역할인 반면 jQuery의 '$'는 CSS 선택자와 동일한 룰을 가지고 있으며, Prototype의 select 메서드를 혼용하거나 $$유틸리티를 사용하는 효과가 발생합니다. 단순히 선택 방법만 틀린것이 아니라 처리방법 역시 다릅니다. jQuery의 '$'선택자에 의해 선택된 DOM 요소들은 배열화된 객체(Array-like Object)로 관리되는데 이것을 보통 'jQuery 객체'라고 부르더군요. jQuery에서 제공하는 여러 체인 메서드들은 jQuery 객체화한 다음에야 비로소 혼용될 수 있습니다. 만약, Prototype과 마찬가지로 DOM 요소를 반환하게 하려면 'jQuery 객체에 get(0)' 또는 '[0]'을 사용해야 합니다. 이 것은 더이상 체인 메서드를 사용할 수 없는 상태가 되기 때문에 '$'를 다시 호출하는 등 사용성 및 가독성을 떨어트릴 수 있으므로 가급적이면 변수에 할당하여 사용하거나 체인 메서드로 모두 처리할 것을 권장하고 있습니다.

Prototype의 'down'과 'up'에 해당하는 'children'과 'parent'메서드는 서로 차이를 보입니다. Prototype의 경우 자식/부모 노드들을 순차적으로 검색하여 발견되는 첫번째 노드를 반환하는 반면, jQuery는 첫번째 자식/부모 노드중에서 일치하는 것만을 찾아 반환합니다. 때문에 이를 대체하기 위해서는 때때로 'find'와 'parents'메서드를 혼용해야 할 필요가 있습니다.

Event

var expand = function(event) {
  event.element(); //=> DOM element object
  event.target; //=> DOM element object

  event.pointerX(); //=> absolute horizontal position for a mouse event
  event.clientX; //=> absolute vertical position for a mouse event

  event.pointerY(); //=> absolute horizontal position for a mouse event
  event.clientY; //=> absolute vertical position for a mouse event

  event.stop(); // stop bubbling
  event.stopPropagation(); // stop bubbling
};

element.observe('mouseup', expand); //=> DOM element object
element.bind('mouseup', expand); //=> jQuery object

document.observe('dom:loaded', expand); //=> document object
$(document).ready(expand); //=> jQuery object

element.stopObserving('mouseup', expand); //=> DOM element object
element.unbind('mouseup', expand); //=> jQuery object

element.observe('click', expand); //=> DOM element object
element.click(expand); //=>  jQuery object

Prototype에 익숙한 사용자라면 bind가 눈에 띨 것입니다. jQuery에서 bind는 Prototype의 observe와 동일한 메서드로 볼 수 있습니다. 이 밖에도 jQuery는 편이성을 위해 jQuery 객체에 체인되는 이벤트 메서드들(keypress, mouseover, dblclick 등)을 제공하고 있습니다.

Element

element.remove(); //=> removed DOM element object
element.remove();  //=> removed jQuery object

var img = new Element('img',{src:'/images/sample.gif',id:'my-img',alt:'img'}); //=> DOM element object
var img = $('<img src="/images/sample.gif" id="my-img" alt="img">'); //=> jQuery object

var el= new Element('a',{href:'http://firejune.com',className:'link'}).update('ClickMe!'); //=> DOM element object
var el = $('<a href="http://firejune.com" class="link">').html('ClickMe!'); //=> jQuery object

element.update('<p>hello!</p>'); //=> DOM element object
element.html('<p>hello!</p>'); //=> jQuery object

element.insert({top:img}); //=> DOM element object
element.prepend(img); //=> jQuery object

element.insert({bottom:'<p>hello!</p>'}); //=> DOM element object
element.append('<p>hello!</p>'); //=> jQuery object

element.insert({before:el}); //=> DOM element object
element.before(el); //=> jQuery object

element.insert({after:'<p>hello!</p>'}); //=> DOM element object
element.after('<p>hello!</p>'); //=> jQuery object

DOM 요소를 파싱할 때 주로 다루게 되는 메서드들입니다. jQuery에서는 DOM 요소의 선택자로 사용되는 '$'를 그대로 재사용하여 엘리먼트를 생성하는 모습은 매우 인상적입니다.

Style

element.setStyle({width: '100px'});  //=> DOM element object
element.css({width: 100}); //=> jQuery object

element.getStyle('width'); //=> '100px'
element.css('width'); //=> '100px'

element.getWidth(); //=> 100
element.width(); //=> 100

element.getHeight(); //=> 100
element.height(); //=> 100

element.hide(); //=> DOM element object
element.hide(); //=> jQuery object

element.show(); //=> DOM element object
element.show(); //=> jQuery object

element.addClassName('active'); //=> DOM element object
element.addClass('active'); //=> jQuery object

element.removeClassName('active'); //=> DOM element object
element.removeClass('active'); //=> jQuery object

element.hasClassName('active'); //=> DOM element object
element.hasClass('active'); //=> jQuery object

element.toggleClassName('active'); //=> DOM element object
element.toggleClass('active'); //=> jQuery object

엘리먼트 스타일과 관련해서는 서로 비슷한 양상을 보이지만, jQuery의 경우 'css'메서드를 통해서 스타일을 부여하는 것과 값을 읽어내는 것이 동시에 이루어지고 있다는 점에서 차이가 있습니다.

Enumerable

elements.each((function(element, index) {
  //=> DOM element object, index
});
elements.each(function(index, element) {
  //=> DOM element object, index
});

elements.invoke('remove');
elements.remove();

배열 객체를 다루는데에는 두말할 나위 없이 jQeury가 10점 만점에 10점입니다. 단, Prototype과 달리 jQuery에서 each를 사용할 때 주의할 점은 index와 반환요소의 순서가 뒤바뀐 것 입니다.

Ajax

var form = $(formElement);

new Ajax.Request(form.action, {
  method: form.method, paramitors: form.serialize(),
  onSuccess: function(transport) {
    //=> transport object(Text or JSON or XML)
  },
  onFailure: function(transport) {},
  onComplete: function(transport) {}
});

$.ajax({
  url: form.get(0).action, type: form.get(0).method, data: form.serialize(),
  success: function(transport) {
    //=> transport string or json object or xml node
  },
  error: function(transport) {},
  complete: function(transport) {}
});

응답해더의 'content-type'에 따라 결과물을 자동으로 분석하여 반환하는 Ajax 로직은 서로 닮았습니다만, jQuery의 경우는 응답결과에 따라 자료형이 변경되어 넘어오는 반면, Prototype은 transport객체에 모든 상황이 반영되어 넘어옵니다.

Effect

element.fade({duration: 0.5}); // => instance of effect
element.fadeOut(500); //=> jQuery object

element.appear({duration: 0.5}); // => instance of effect
element.fadeIn(500); //=> jQuery object

element.slideUp({duration: 0.5, afterFinish: callback}); // => instance of effect
element.slideUp(500, callback); //=> jQuery object

element.slideDown({duration: 0.5, afterFinish: callback}); // => instance of effect
element.slideDown(500, callback); //=> jQuery object


element.morph({
  style: 'width: 100px; height: 100px; opacity: 0.5;',
  duration: 0.5,
  transition: Effect.Transitions.linear,
  afterFinish: callback
}); // => instance of effect

element.animate(
  {width: 100, height: 100, opacity: 0.5}, // style properties
  500, // duration
  'linear', // easing
  callback
); //=> jQuery object

jQuery는 Scriptaculous의 effect.js를 포함해야 지만 가능한 몇몇 애니메이션 효과를 추가작업 없이 사용할 수 있습니다. 애니메이션 코어를 내장하고 있기 때문입니다. 1차원적인 계산에서 그치는 Scriptaculous의 transition과는 달리 jQuery의 easing은 p, n, firstNum, diff 총 4개의 인자로 구성되는 계산식은 매우 독창적인 변화를 만들어 낼 수 있는 구조를 가지고 있습니다. 그리고 jQuery에는 Scriptaculous를 대신하는 ui.js가 있습니다. 다수의 UI 컴포넌트와 효과들로 구성되어 있으며, 메서드 단위로 파일이 나뉘어 있습니다.

Browser detection

Prototype.Browser.IE; //=> true or false
$.browser.msie; //=> true or undefined

Prototype.Browser.Webkit; //=> true or false
$.browser.safari; //=> true or undefined


Prototype.Browser.Gecko; //=> true or false
$.browser.mozilla; //=> true or undefined

Prototype.Browser.Opera; //=> true or false
$.browser.opera; //=> true or undefined

jQuery쪽이 더욱 간결해 보이네요.

jQuery를 사용할 때 가장 만족스러운 점은 싱글 혹은 멀티플 노드의 개념을 떨쳐 버림으로서 찾아오는 강력한 체인 메서드의 연결입니다. 익숙해 지기만하면 상당히 빠른 속도로 코딩을 작렬(?)할 수 있을 것으로 기대되지만, 역시 Prototype의 Class와 bind(Function.prototype)를 혼용한 OO 클래스 지향 코딩을 구사할 수 없다는 것이 무척이나 아쉬운 점으로 남습니다. 그래서, Prototype의 쓸만한 코드들을 훔쳐 쓰려고 마음먹고 있죠.

* 이 문서는 지속적으로 업데이트 될 예정입니다.

Comments