JSONP를 사용한 도메인 간 통신, Part 1: JSONP와 jQuery의 결합으로 강력한 매시업 빠르게 만들기

http://www.ibm.com/developerworks/kr/library/wa-aj-jsonp1/

소개

Ajax(Asynchronous JavaScript and XML)는 흔히 Web 2.0 사이트라고 하는 새로운 세대의 웹 사이트를 선도하는 핵심 기술이다. Ajax를 사용하면 웹 애플리케이션의 표시 및 동작에 영향을 주지 않고 백그라운드에서 데이터를 검색할 수 있다. 데이터는 클라이언트 측 JavaScript에서 HTTP로 원격 서버에 연결하는 기능을 제공하는 API인 XMLHttpRequest 함수를 사용하여 검색된다. 또한 Ajax는 여러 소스의 컨텐츠를 단일 웹 애플리케이션에 통합하는 많은 매시업에서 기본 기술로 사용되고 있다.

하지만 이 방법을 사용할 경우에는 브라우저의 제한사항으로 인해 도메인 간 통신이 지원되지 않는다. 다른 도메인의 데이터를 요청하려고 하면 보안 오류가 발생한다. 물론 데이터가 있는 원격 서버를 제어하고 동일한 도메인에만 데이터를 요청한다면 이러한 보안 오류를 막을 수 있다. 하지만 웹 애플리케이션이 고유 서버에만 한정되어 있다면 무슨 의미가 있겠는가? 여러 써드파티 서버에 있는 데이터를 수집해야 하는 경우에는 어떻게 할 것인가?

동일 출처 정책 제한 이해

동일 출처 정책은 한 도메인에서 로드된 스크립트가 다른 도메인의 문서 특성을 가져오거나 조작하지 못하도록 한다. 즉, 요청된 URL의 도메인이 현재 웹 페이지의 도메인과 같아야 한다. 이는 기본적으로 브라우저가 다른 출처의 컨텐츠를 조작하는 것을 방지하기 위해 해당 컨텐츠를 분리한다는 것을 의미한다. 이 브라우저 정책은 아주 오래 전인 Netscape Navigator 2.0부터 사용되던 것이다.

비교적 간단하게 이 제한을 해결하려면, 웹 페이지에서 자신의 출처가 되는 웹 서버에 데이터를 요청하고 해당 웹 서버가 실제 써드파티 서버에 요청을 전달하는 프록시 서버 역할을 수행하면 된다. 많이 사용되고 있기는 하지만 이 기술은 확장성이 없다. 또 다른 방법으로는 프레임 요소를 사용하여 현재 웹 페이지 내에 새 영역을 만든 후 GET 요청을 사용하여 써드파티 컨텐츠를 가져오는 방법이 있다. 하지만 컨텐츠를 가져온 이후에 프레임의 컨텐츠가 동일 출처 정책 제한에 걸릴 수 있다.

이 제한을 해결할 수 있는 방법은 웹 페이지에 동적 스크립트 요소를 삽입하는 것이다. 이 스크립트 요소에는 다른 도메인의 서비스 URL을 가리키는 소스가 있기 때문에 스크립트 자체적으로 데이터를 가져올 수 있다. 이 스크립트는 로드되면서 실행된다. 이 방법은 동일 출처 정책에서 동적 스크립트 삽입이 허용되고 스크립트가 웹 페이지의 소스 도메인에서 로드된 스크립트로 간주되기 때문에 정상적으로 작동된다. 하지만 이 스크립트에서 다른 도메인의 문서를 로드하려는 시도는 실패하게 된다. 다행히 JSON(JavaScript Object Notation)을 함께 사용하면 이 기술을 향상시킬 수 있다.

JSON과 JSONP

JSON은 XML에 비해 브라우저와 서버 간의 정보 교환에 사용되는 경량 데이터 형식이다. JSON은 이름에서 알 수 있듯이 JavaScript 오브젝트의 문자열 표현이다. (이 사실을 간과하는 JavaScript 개발자들이 없기를 바란다.) 예를 들어, symbol과 price라는 두 속성을 가진 ticker 오브젝트가 있다고 가정해 보자. JavaScript에서는 다음과 같이 ticker 오브젝트를 정의할 수 있다.

var ticker = {symbol: 'IBM', price: 91.42};

다음은 동일한 정의를 JSON으로 표현한 것이다.

{symbol: 'IBM', price: 91.42}

JSON과 JSON을 데이터 교환 형식으로 사용할 수 있는 가능성에 대한 자세한 정보는 참고자료에서 볼 수 있다. Listing 1에서는 호출되었을 때 IBM 주가를 보여 주는 JavaScript 함수를 정의한다. (이 기사에서는 코드를 웹 페이지에 통합하는 방법에 대한 상세 설명을 제공하지 않는다.)


Listing 1. showPrice 함수 정의하기
function showPrice(data) {
    alert("Symbol: " + data.symbol + ", Price: " + data.price);
}
                

JSON 데이터를 매개변수로 전달하여 이 함수를 호출할 수 있다.

showPrice({symbol: 'IBM', price: 91.42}); // alerts: Symbol: IBM, Price: 91.42
                

이제 Listing 2처럼 이들 두 단계를 웹 페이지에 포함할 수 있는 준비가 완료되었다.


Listing 2. 웹 페이지에 showPrice 함수 및 매개변수 포함하기
<script type="text/javascript">
function showPrice(data) {
    alert("Symbol: " + data.symbol + ", Price: " + data.price);
}
</script>
<script type="text/javascript">showPrice({symbol: 'IBM', price: 91.42});</script>

페이지가 로드되면 그림 1과 같은 경고가 표시된다.


그림 1. IBM 티커
IBM ticker

지금까지 이 기사에서는 정적 JSON 데이터를 매개변수로 사용하여 JavaScript 함수를 호출하는 방법을 살펴보았다. 하지만 함수 호출에서 JSON 데이터를 동적으로 랩핑하여 동적 데이터와 함께 함수를 호출할 수도 있다. 이를 동적 JavaScript 삽입 기술이라고 한다. 이 기술의 작동 방법을 확인하려면 ticker.js라는 독립형 JavaScript 파일에 다음 행을 입력해야 한다.

showPrice({symbol: 'IBM', price: 91.42});

이제 웹 페이지의 스크립트를 Listing 3의 코드처럼 변경한다.


Listing 3. 동적 JavaScript 삽입 코드
<script type="text/javascript">
// This is our function to be called with JSON data
function showPrice(data) {
    alert("Symbol: " + data.symbol + ", Price: " + data.price);
}
var url = “ticker.js”; // URL of the external script
// this shows dynamic script insertion
var script = document.createElement('script');
script.setAttribute('src', url);

// load the script
document.getElementsByTagName('head')[0].appendChild(script); 
</script>
				

Listing 3의 예제에서, ticker.js에 파일에 있는 동적으로 삽입된 JavaScript 코드는 실제 JSON 데이터를 매개변수로 사용하여 showPrice() 함수를 호출한다.

앞에서 설명한 대로 동일 출처 정책은 동적 스크립트 요소의 문서 내 삽입을 허용하기 때문에 다른 도메인의 JavaScript를 동적으로 삽입하여 JSON 데이터를 전달할 수 있다. 이처럼 함수 호출에 랩핑된 JSON 데이터를 JSONP(JSON with Padding)라고 한다. 이 작업을 수행하려면 코드를 삽입하기 전에 콜백 함수가 미리 정의되어 있어야 한다. 이 예제에서는 showPrice()가 콜백 함수이다.

그러나 JSONP 서비스(또는 원격 JSON 서비스)는 사용자 지정 함수 호출에서 리턴된 JSON 데이터에 대한 랩핑을 지원하는 추가 기능을 가진 웹 서비스이다. 따라서 이 방법을 사용하려면 원격 서비스에서 콜백 함수 이름을 요청 매개변수로 받아야 한다. 그런 다음 원격 서비스에서 JSON 데이터를 매개변수로 전달하는 이 함수에 대한 호출이 생성되면 이 함수가 클라이언트 측 웹 페이지에 삽입되어 바로 실행된다.

jQuery의 JSONP 지원

jQuery 버전 1.2부터는 JSONP 호출에 대한 네이티브 지원이 제공되고 있다. JSONP 콜백을 지정한 경우 다른 도메인에 있는 JSON 데이터를 로드할 수 있으며 JSONP 콜백은 url?callback=?라는 구문을 사용하여 지정할 수 있다.

jQuery에서는 ?가 호출할 생성된 함수 이름으로 자동 변환된다. Listing 4에서는 이 코드를 보여 준다.


Listing 4. JSONP 콜백 사용하기
jQuery.getJSON(url+"&callback=?", function(data) {
    alert("Symbol: " + data.symbol + ", Price: " + data.price);
});

이를 위해 jQuery는 스크립트가 삽입될 때 호출되는 전역 함수를 창 오브젝트에 추가한다. 이 함수는 완료 후에 제거된다. jQuery는 도메인 내 호출에 대해서도 최적화 기능을 제공한다. 동일한 도메인에 대한 요청이 발생할 경우 jQuery는 해당 요청을 일반적인 Ajax 요청으로 변환한다.

JSONP 지원을 사용하는 예제 서비스

앞의 예제에서는 정적 파일(ticker.js)을 사용하여 JavaScript를 웹 페이지에 동적으로 삽입했다. JSONP 응답을 리턴하기는 하지만 URL에 콜백 함수 이름을 정의할 수 없었기 때문에 JSONP 서비스가 아니었다. 그렇다면 실제 JSONP 서비스로 변환하려면 어떻게 해야 할까? 이 기사에서는 여러 가지 방법 중에서 PHP와 Java를 사용하는 두 가지 예제를 설명한다.

먼저 서비스에서 요청 URL에 포함된 callback 매개변수를 허용한다고 가정하자. (매개변수 이름 자체는 중요하지 않지만 클라이언트와 서버에서 동일한 이름을 사용해야 한다.) 그리고 서비스에 대한 요청이 다음과 같다고 가정하자.

http://www.yourdomain.com/jsonp/ticker?symbol=IBM&callback=showPrice

여기에서 symbol은 요청된 티커 기호를 나타내는 요청 매개변수이며, callback은 웹 애플리케이션에서 사용하는 콜백 함수의 이름이다. Listing 5의 코드를 사용하여 jQuery의 JSONP 지원이 포함된 이 서비스를 호출할 수 있다.


Listing 5. 콜백 서비스 호출
jQuery.getJSON("http://www.yourdomain.com/jsonp/ticker?symbol=IBM&callback=?", 
function(data) {
    alert("Symbol: " + data.symbol + ", Price: " + data.price);
});

이 코드에서는 실제 함수 이름 대신 ? 기호를 콜백 함수 이름으로 입력했다. 이는 jQuery가 ? 기호를 인라인 함수를 호출하는 생성된 함수 이름(예: jsonp1232617941775)으로 바꾸기 때문이다. 이 기능을 활용하면 showPrice()와 같은 함수를 자유롭게 정의할 수 있다.

Listing 6에서는 PHP로 구현한 JSONP 서비스의 일부를 보여 준다.


Listing 6. PHP로 구현한 JSONP 서비스의 일부
$jsonData = getDataAsJson($_GET['symbol']);
echo $_GET['callback'] . '(' . $jsonData . ');';
// prints: jsonp1232617941775({"symbol" : "IBM", "price" : "91.42"});

Listing 7에서는 동일한 기능을 수행하는 Java™ Servlet 메소드를 보여 준다.


Listing 7. Java Servlet으로 구현한 JSONP 서비스
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
  throws ServletException, IOException {
	String jsonData = getDataAsJson(req.getParameter("symbol"));
	String output = req.getParameter("callback") + "(" + jsonData + ");";

	resp.setContentType("text/javascript");
          
	PrintWriter out = resp.getWriter();
	out.println(output);
	// prints: jsonp1232617941775({"symbol" : "IBM", "price" : "91.42"});
}

그렇다면 이제 단일 웹 페이지에 표시하기 위해 써드파티 서버의 컨텐츠를 통합하는 매시업을 빌드하려면 어떻게 해야할까? 간단하다. 써드파티 JSONP 서비스를 사용해야 한다. 이 기사에서는 이들 서비스 중 일부를 소개한다.

바로 사용할 수 있는 JSONP 서비스

이제 JSONP를 사용하는 방법을 알고 있으므로 바로 사용할 수 있는 몇몇 JSONP 웹 서비스로 애플리케이션과 매시업을 빌드해 볼 수 있다. 다음은 후속 개발 프로젝트를 위한 출발점이다. (힌트: 지정된 URL을 브라우저의 주소 필드에 붙여 넣으면 결과 JSONP 응답을 확인할 수 있다.)

Digg API: Digg의 최신 스토리

http://services.digg.com/stories/top?appkey=http%3A%2F%2Fmashup.com&type=javascript
&callback=?

Geonames API: 우편번호에 해당하는 위치 정보

http://www.geonames.org/postalCodeLookupJSON?postalcode=10504&country=US&callback=?

Flickr API: Flickr의 최신 고양이 사진

http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any
&format=json&jsoncallback=?
                

Yahoo Local Search API: 우편번호 10504 지역의 피자 검색

http://local.yahooapis.com/LocalSearchService/V3/localSearch?appid=YahooDemo&query=pizza
&zip=10504&results=2&output=json&callback=?

주의할 사항

JSONP는 매시업을 빌드하는 데 사용할 수 있는 매우 강력한 기술이기는 하지만 도메인 간 통신에 필요한 모든 요구 사항을 해결할 수 있는 방법은 아니다. 개발 리소스를 확정하기 전에 신중하게 고려해야 하는 단점이 있다. 특히 JSONP 호출에 대한 오류 처리 방법이 없다. 동적 스크립트 삽입이 작동되는 경우에는 함수가 호출되지만 그렇지 않은 경우에는 어떠한 작업도 수행되지 않는다. 즉, 오류도 발생하지 않고 작업만 실패할 뿐이다. 예를 들어, 서버의 404 오류를 받을 수 없다. 요청을 취소하거나 다시 시작할 수도 없다. 그러나 일정 시간이 경과된 후 시간 종료로 요청을 중지할 수 있다. (jQuery의 후속 버전에서는 JSONP 요청에 대한 중단 기능이 지원될 것이다.)

JSONP의 또 다른 주요 단점은 신뢰할 수 없는 서비스와 함께 사용할 경우 매우 위험할 수 있다는 것이다. JSONP 서비스가 함수 호출로 랩핑된 JSON 응답을 리턴하고 브라우저에서 이 응답을 실행하므로 호스트하는 웹 애플리케이션이 다양한 공격에 노출될 수 있다. JSONP 서비스를 사용하려면 내재된 위험 요소를 잘 알고 있어야 한다. 자세한 정보는 참고자료에서 볼 수 있다.



Posted by 홍반장水 홍반장水
TAG jQuery

jQuery로 작업하기, Part 2: 매개체로서의 JQuery: UI 프로젝트

http://www.ibm.com/developerworks/kr/library/wa-aj-jquery5/index.html

소개

이 기사가 게재된 이후 UI 프로젝트의 1.6 버전이 1.7 버전으로 변경되었다.

jQuery UI 프로젝트는 새롭고 흥미로운 jQuery 라이브러리로 내년에 빠르게 성장할 것으로 예상된다. 이 UI 라이브러리는 릴리스가 나올 때마다 개발자가 여러 가지 기능과 수정 사항을 추가하고 있기 때문에 jQuery 코어보다 훨씬 빠른 속도로 확장되고 있다. UI 패키지와 관련하여 흥미로운 점은 이 패키지가 실제로 사용자 인터페이스 관련 항목의 모음이고 이러한 항목을 3가지 주요 모듈로 분류할 수 있다는 것이다. 첫 번째는 웹 사이트에 바로 전개할 수 있도록 미리 만들어진 "스킨 교체형" 사용자 인터페이스를 가지고 있는 Widgets 모듈이고, 두 번째는 페이지 요소에 매우 쉽게 직관적으로 적용할 수 있는 Effects 모듈(예: 흔들기, 확대하기 등)이며, 세 번째는 페이지 요소에 대한 개선된 마우스 Interactions(예: 끌어서 놓기) 모듈이다. 마지막으로 UI 패키지는 미리 만들어진 위젯에 대한 테마를 사용자가 직접 작성할 수 있기 때문에 다운로드한 위젯을 사용자의 웹 사이트만을 위한 특별한 위젯으로 만들 수 있다.

jQuery UI 프로젝트의 매우 흥미로운 역사를 알면 프로젝트가 왜 이러한 방식으로 개발되었는지와 라이브러리의 세 부분이 매우 상이한 이유를 어느 정도 이해할 수 있을 것이다. 이 UI 라이브러리는 "처음"(jQuery의 관점에서 보면 수십년 전이라고도 할 수 있는 2007년말)에는 여러 다양한 플러그인의 모습으로 시작되었다. jQuery 커뮤니티의 우수한 개발자들의 노력으로 개발된 이들 플러그인은 모두 매우 유명했으며 다운로드해서 사용하던 사용자도 많았다. jQuery 커뮤니티에서는 이 유명한 플러그인들을 하나의 큰 플러그인으로 통합하여 jQuery 코어 코드의 공식 확장으로 발전시키기로 결정했다. 이러한 과정을 거쳐 2008년초에 UI 버전 1.5가 릴리스되었다. 하지만 이 통합 UI 플러그인이 릴리스된 이후 플러그인을 직접 사용해 본 개발자들로부터 좋기는 하지만 코드가 너무 어렵다는 의견(또는 불만)이 많이 나왔다. 이는 모든 플러그인의 개발자가 각기 달랐을 뿐 아니라 각 개발자의 스타일도 제각각이기 때문에 발생한 당연한 결과였다. 결국 모든 플러그인에 공통된 스타일을 적용하자는 결정이 내려졌으며 이후에 개발된 모든 1.5.x 릴리스는 이러한 노력의 산물이었다. UI 라이브러리의 역사에서 그 발자취를 확인할 수 있다.

하지만 UI 라이브러리의 1.6 버전이 릴리스될 때에는 모든 코드에 대한 리팩토링 작업이 완료되고 공통 스타일과 공통 코드가 적용된 UI 라이브러리를 보게 될 것이다. 하지만 이 릴리스에도 한 가지 단점이 있다. 그것은 바로 UI 팀에서 완벽한 1.6 버전을 릴리스하기 위해 몇 가지 위젯을 코드베이스에서 제외하기로 결정했다는 것이다. v1.5에 포함된 위젯의 수가 3개 더 많기는 하지만 v1.6을 사용하면 더 빠르고 쉽게 작업할 수 있을 것이다. 이러한 절충안이 좋은 결정이었는지에 대한 판단은 사용자에 의해 내려질 것이며 제외된 위젯의 경우 UI 라이브러리의 후속 릴리스에 다시 포함될 것으로 예상된다. (기록을 남기는 의미로 제외된 위젯을 언급하자면 autocomplete combobox, magnifier 및 spinner가 있었다.)

이제 UI 라이브러리의 역사를 모두 살펴보았으므로 이 라이브러리의 장점을 자세히 알아보자. 그러고 나면 jQuery 팀원들이 이 라이브러리에 왜 그렇게 열광하는지 그 이유를 알 수 있을 것이다. jQuery 팀의 일원인 필자 또한 이 라이브러리의 가능성에 열광하지 않을 수 없었다. AWT로 Java™ UI를 작업하던 시절을 기억할 수 있는 사람(또는 그 시절을 잊어버리기로 결정한 사람)이라면 Swing이 처음 모습을 드러내면서 완전히 새로운 위젯이 등장하고 사용자 인터페이스 작업이 실질적으로 한결 쉬워졌던 순간의 흥분을 기억할 수 있을 것이다. UI 라이브러리의 가능성은 Swing과 견주어도 결코 뒤떨어지지 않는다. 이 라이브러리는 웹 디자이너의 도구 창고에 보다 복잡한 위젯을 추가하는 동시에 위젯의 작동 방식을 표준화함으로써 웹 디자인 분야에 완전히 새로운 수준의 사용자 인터페이스를 추가할 수 있을 것이기 때문이다. 현재 UI 라이브러리에 포함된 위젯의 수는 많지 않지만 v1.6의 릴리스를 기준으로 1년 뒤면 웹 애플리케이션에 사용할 수 있는 10 - 20개의 새 위젯이 UI 라이브러리에 추가될 것이다.

UI 설치하기

UI 라이브러리는 jQuery 코어 또는 플러그인과는 다른 방식으로 다운로드 및 설치하게 된다. 네트워크 트래픽 및 연결을 최소화하기 위해 UI 팀에서는 UI 라이브러리 중에서 사용할 파트와 원하는 압축 스키마를 미리 선택하도록 사용자에게 요청하는 고유한 웹 애플리케이션을 개발했다. 그런 다음 파일을 직접 다운로드할 수 있다. 이 방법의 장점은 UI 라이브러리 중에서 사용할 파트만을 제공하면 되기 때문에 애플리케이션의 성능에 미치는 영향을 최소화할 수 있다는 점이다. 하지만 사용자 정의 파일에 처음부터 포함되지 않은 파트를 나중에 사용하기로 결정한 경우에는 전체 파일을 다시 다운로드해야 하는 단점이 있다. 세상만사 다 그렇듯이 모든 결정에는 일장일단이 있기 마련이다.

Effects

UI 라이브러리의 Effects 모듈은 페이지 요소에 적용할 수 있는 "흥미로운" 기능을 제공한다. 필자가 "흥미롭다"고 표현한 이유는 이 모듈을 전체적으로 보았을 때 여러 가지 효과를 별다른 기준 없이 무작위로 모아놓은 것처럼 보이기 때문이다. 마치 누군가가 Microsoft® PowerPoint의 모든 효과를 살펴본 후 그 중 일부를 선택해서 JavaScript 효과로 만들었다는 생각이 든다. 이들 효과가 어떻게 선정되었는지는 아마도 절대 알 수 없을 것이다. 이러한 효과는 괜찮은 것처럼 보이기는 하지만 일부 효과는 언제 어디에서 사용할 수 있을지 의문이 들기도 하기 때문에 전문 웹 애플리케이션에서 얼마나 유용할 것인지도 여전히 미지수로 남아 있다.

그렇지만 정말 사용하기 쉽다는 장점도 있다. 첫 번째 효과 세트는 페이지의 요소를 숨기거나 표시하는 데 사용할 수 있다. 이들 효과는 jQuery 라이브러리의 내장 함수인 hide()show()를 확장한 것으로 페이지의 요소를 숨기거나 표시할 때 사용할 효과의 유형을 지정할 수 있다. 그렇다면 이제 예제를 살펴보자. 그림 1에서는 플로우차트 형식으로 Puff 효과의 적용 전, 적용 중 및 적용 후의 모습을 보여 준다.


그림 1. Puff 예제
reappear라는 단어를 보여 주는 두 개의 파란색 상자와 한 개의 흰색 상자

효과가 실제로 표현되는 모습을 설명하기가 어려우므로 이 기사에서 제공하는 데모를 다운로드한 후 직접 실행해 보아야 한다. 하지만 필자는 그림 1의 정적 이미지를 제공하는 소스 코드를 보여 줄 수 있다. 이 코드를 보면 어느 정도 상상이 될 것이다.


Listing 1. Puff 효과
		   
$("#puffSample").click(function(){
   $(this).hide("puff");
});

$("#puffReappear").click(function(){
   $("#puffSample").show("puff");
   });

이 코드에는 일반적인 코드와 크게 다른 점이 없으며 매우 직관적이기 때문에 show/hide 효과의 작동 방법을 쉽게 알 수 있다. 진짜 문제는 사용자가 "이게 뭐지? 필요 없는거잖아"라는 말을 하지 않도록 이러한 효과를 적절한 방법으로 유용하게 사용할 수 있는 시기를 찾는 것이다.

Effects 모듈에 포함된 다른 효과를 살펴보자. 이러한 효과는, 적어도 이러한 효과 중 일부는 어느 정도 실용적이기 때문에 웹 애플리케이션에서 사용자 경험을 향상시키는 데 사용할 수 있는 방법을 상상할 수 있다. 다시 한번 말하지만 실제로 실행하지 않고서는 예제를 설명하기가 어려우므로 이 기사에서 제공하는 예제를 다운로드하여 직접 확인해 보기를 권장한다.

이 모듈의 효과는 페이지의 특정 요소에 대한 관심을 끌기 위한 것이며 이 관점에서 일부 효과는 효과적으로 사용할 수 있다. 이러한 효과에는 가상의 HTML 트램펄린에 떨어뜨린 것처럼 요소가 글자 그대로 통통 튀는 효과를 제공하는 "bounce", 노란색 배경이 요소에 반짝 표시되는 "highlight"(Mac과 유사함), 요소가 반복적으로 사라졌다가 표시되는 "pulsate", 요소를 50% 축소하는 "scale", 요소를 좌우로 흔드는 "shake" 및 요소를 길고 평평하게 만들어 주는 "size"가 있다. 이러한 효과는 hide/show 효과와 마찬가지로 사용 방법이 매우 간단하다.


Listing 2. Bounce 효과
		   
$("#bounce").click(function(){
   $("#loginSample").effect("bounce");
   }); 

지금까지 UI 라이브러리의 Effects 모듈을 살펴보았다. 페이지에서 매우 특별하게 보이지는 않겠지만 이러한 효과 중 일부는 페이지의 요소를 강조할 필요가 있을 때 실제로 사용하면 좋은 효과를 얻을 수 있을 것이다.

Interactions

마지막으로 필자는 화면에서 DIV를 확대하는 것보다 더 흥미로운 몇 가지 기능을 알게 되었다. Interactions은 한 요소를 다른 요소 위로 끌어왔을 때 발생할 수 있는 상황, 한 요소를 다른 요소 위에 놓았을 때 발생할 수 있는 상황 및 두 HTML 요소를 사용하여 수행할 수 있는 기타 작업에 대한 코드를 작성하는 매우 강력한 방법을 제공한다.

끌기/놓기 요소를 이용한 상호 작용은 웹 애플리케이션 설계와 관련하여 완전히 새로운 분야이다. 대부분의 사용자는 정적 페이지와 정적 페이지 요소에 익숙해 있기 때문에 끌어서 놓기 상호 작용이 지원되는 웹 애플리케이션은 거의 없는 편이다. 잘 생각해 보면 이 고유한 유형의 상호 작용을 지원하는 데스크탑 애플리케이션도 별로 없다는 것을 알 수 있다. 물론 이러한 애플리케이션이 전혀 없지는 않다. 구체적으로 한 예를 들자면 Yahoo Fantasy Sports 페이지가 있다. 끌기/놓기 모델을 거의 완벽하게 활용하고 있는 이 페이지에서는 사용자가 팀 명부를 섞고, 선발 전 플레이어의 순위를 지정하고, 플레이어를 제외 목록으로 이동하는 등의 작업을 수행할 수 있다. 아직까지 Fantasy Sports를 이용해 보지 않았다고 하더라도 이 페이지에 구현된 고유한 사용자 인터페이스 디자인을 확인하는 차원에서 이 애플리케이션을 이용해 보기를 권장한다. 필자는 이 애플리케이션이 웹에서 끌기/놓기 모델을 가장 잘 활용하고 있는 애플리케이션 중 하나라는 견해를 가지고 있다.

Interactions 모듈에는 5가지 유형의 상호 작용이 있다. 처음 3가지 상호 작용은 다음과 같다.

  • Resizable은 페이지에 있는 요소의 크기를 조정하는 데 필요한 기능을 지원한다. 이 상호 작용을 사용하면 사용자가 요소의 모양을 변경하고 그에 따라 페이지의 다른 모든 요소를 적절하게 조정할 수 있다. 이 상호 작용은 여러 개의 포틀릿을 가지고 있으면서 사용자가 자신의 웹 페이지 디자인을 사용자 정의할 수 있는 웹 애플리케이션을 만들려는 경우에 매우 유용하다. MyYahoo와 iGoogle 모두 사용자가 자신의 페이지를 사용자 정의할 수 있기는 하지만 사용자 정의할 수 있는 항목이 제한되어 있다. 즉, 미리 정해진 수의 열 내에서만 모든 컨텐츠를 사용자 정의할 수 있는 것이다. 사용자가 자신의 페이지 전체를 정의할 수는 없을까?
  • Selectable은 페이지의 요소를 그룹화한 후 사용자가 그룹 내에서 요소의 서브세트를 선택할 수 있도록 지원한다. 이 유형의 상호 작용은 사용자 정의 목록 또는 선택 위젯을 작성할 때 유용하다.
  • Sortable 인터페이스는 페이지 요소를 정렬된 순서로 "빠르게" 정렬하는 기능을 지원한다. 5개의 행으로 구성된 테이블이 있을 경우 Sortable 상호 작용을 사용하면 네 번째 행을 두 번째 행으로 끌어놓았을 때 테이블의 나머지 부분이 자동으로 정렬된다. 이러한 세 상호 작용을 사용하면 새로운 방식으로 위젯 및 웹 애플리케이션 상호 작용을 구현할 수 있으므로 좀 더 자세히 살펴보기를 권한다.

이 섹션에서는 앞으로 웹 애플리케이션의 변화를 가져올 것으로 기대되는 다음 두 가지 상호 작용에 대해 자세히 살펴볼 것이다.

  • Draggable
  • Droppable

앞으로 설명할 예제에서는 반드시 그래야 하는 것은 아니지만 이 두 상호 작용을 함께 사용한다.

이 예제 웹 애플리케이션에서는 새로운 방식의 온라인 쇼핑 방법을 보여 준다. 단추를 눌러서 상품을 장바구니에 넣는 대신 Draggable/Droppable 상호 작용을 활용하여 많은 사람들이 실제로 상품을 장바구니에 넣을 때의 "느낌을 살릴" 것이다. 즉, 사용자가 선택한 상품을 장바구니에 끌어서 놓게 된다. 상품이 장바구니에 놓여지면 장바구니에 있는 상품 수와 총금액이 자동으로 업데이트된다.


그림 2. 장바구니 예제
장바구니와 바나나, 사과 및 키위를 함께 보여 주는 그림

이제 이 상호 작용을 수행하는 매우 짧은 코드를 살펴보자. (전체 작업 예제는 이 기사의 끝에 있는 샘플 코드에 포함되어 있다.)


Listing 3. 장바구니
		   
// First, set up the shopping cart
// Give the cart an ID that we can reference in the jQuery code
// Give each span an ID so we can easily update the text in it
<img src="cart.jpg" id=cart>
<p><span id=numItems>0</span> items in your cart.
<p>Your total is $<span id=totalPrice>0</span>.

// Each product has code that looks like this.  The entire product
// is wrapped in a DIV with a class of "product" and given a
// unique ID.  It also has a span that defines the price of the object.
<p><div class=product id=ban><img src="banana.jpg" align=left> Bananas
  <p>$<span class=price>1.99</span></div><br><br>

// define each DIV that has a "product" as "draggable"
// The draggable function has many options that define how the object
// will look and feel when dragged.  There are many options, most not
// covered here, but I put a sampling in to whet your appetite.
$(".product").draggable({
    'opacity': 0.3,  // make the object semi-transparent when dragged
    'revert': "valid", // snap the object back after it's been dropped
    'delay': 200, // delay 200 ms before starting to drag it
    'distance':4, // wait till it's been dragged 4 pixels before starting
    'helper':"clone" // keep the object where it is and use a helper to show dragging
});
   
// just like the draggable has many options, the droppable has many
// options as well.  These options provide a variety of ways
// to offer reinforcement to the user about how the draggable/droppable
// relationship is to work
$("#cart").droppable({
    'accept':".product",  // define which elements will trigger a 'drop'
    'activeClass':"border",  // what class to add to the droppable while dragging
    'drop': function(e,ui){  // this function gets called when something is dropped
        // update the number of items in the cart
        $("#numItems").text(new Number($("#numItems").text())+1);
        var ID = $(ui.draggable).attr("id");
        // get the price from the object just placed in the cart
        var price = new Number($("#"+ID + " .price").text());
        // update the total price in the cart
        $("#totalPrice").text(new Number($("#totalPrice").text())+price);
    }
    });

그렇다. 이 짧은 코드만으로도 이 상호 작용을 수행할 수 있는 것이다. 이 데모를 작성하면서 필자 자신도 정말 쉽다는 생각에 놀라고 말았다. 말할 필요도 없이 이 상호 작용은 매우 쉽고 직관적으로 사용할 수 있기 때문에 이 유형의 상호 작용을 활용하는 사용자 인터페이스를 작성하는 사람들이 많아질 것이다. 이 섹션에서 언급한 다른 상호 작용들도 쉽게 사용할 수 있으며 웹 애플리케이션에서 새로운 상호 작용을 구현할 때 유용하게 활용할 수 있는 고유한 특성을 제공한다.

Widgets

마지막으로 설명할 내용은 실제 이미지를 보여 줄 수 있는 UI 코드 섹션이다. 필자가 보기에 UI 라이브러리에서 가장 흥미로운 부분은 미리 만들어진 검증된 위젯을 가지고 있는 Widgets 모듈이다. 이 모듈에 포함된 위젯은 개발자가 학수고대하던 웹 애플리케이션의 여러 기능을 제공한다. 개발자는 이러한 위젯을 통해 개발 시간을 단축할 수 있기 때문에 결과적으로 남는 시간을 활용하여 웹에서 필요한 정보를 얻고 이 기사처럼 유용한 기사도 읽을 수 있다. 이러한 위젯을 통해 얻게 된 결과를 보고 나면 경영진에서도 개발자의 노력이 매우 지대했음을 인정하고 임금을 올려 줄 마음이 들 것이다.

아래에서 보여 주는 스크린샷은 UI 홈 페이지에서 가져온 것이다.

Accordian


그림 3. Accordian
라틴 텍스트가 포함된 아코디언 양식의 위젯을 보여 주는 창

Accordian 플러그인은 목록을 그래픽 형태의 목록으로 변환하며 이 경우 사용자는 목록의 각 섹션을 이동하면서 한 번에 한 섹션만을 볼 수 있다. 일부 UI 동아리에서는 수 년 전에 Outlook이라는 프로그램에서 이 위젯이 처음 사용되었기 때문에 이 위젯을 "Outlook 막대"라고도 부른다. 이 위젯을 사용할 때 자주 범하는 실수 중 하나는 2개 이상의 섹션을 동시에 표시하려고 하는 것이다. 이러한 기능이 지원되는 위젯은 Accordian 위젯이 아니다. 이 위젯은 한 번에 한 섹션만을 표시하며 한 섹션에 다른 섹션과는 관련이 없는 수많은 정보를 담고 있을 때 효과적으로 사용할 수 있다.

이 위젯은 HTML이 올바르게 설정되어 있어야만 정상적으로 작동한다. 즉, 사용자가 Accordian 위젯의 사용을 예상하고 HTML을 설정해야 한다. 또한 이 위젯은 "단계적 기능 축소(degrade gracefully)" 이론을 고려하여 설계된 것이므로 JavaScript를 사용하지 않는 사용자나 Internet Explorer 5를 사용하는 사용자도 페이지를 올바르게 볼 수 있다. Listing 4의 예제에서는 이 위젯을 사용하려는 경우 반드시 따라야 하는 HTML의 구조를 보여 준다.


Listing 4. Accordian
		   
// The accordian relies on the UL, LI, and A tags to properly configure itself
<ul id="accordianExample" class="ui-accordian-container" style="width:400px;">
   <li>
       <a href="#">Title 1</a>
       <div>Your text would go here</div>
   </li>

   <li>
       <a href="#">Title 2</a>
       <div>More text</div>
   </li>

</ul>

// yes, it's this simple
$("#accordianExample").accordian();

Slider


그림 4. Slider
가로 슬라이더 막대를 보여 주는 창

Listing 5. Slider
		   
<div id="sliderExample" class="ui-slider-1">
<div class="ui-slider-handle"></div>
</div>

// this will set up the slider
$("#sliderExample").slider();

// this will set the maximum and minimum values of the slider
$("#sliderExample").slider({
   min: 10,
   max: 20
});

// this will get the value of the slider
$("#sliderExample").slider("value");

DatePicker


그림 5. DatePicker
2009년 1월 달력을 보여 주는 창

Listing 6. DatePicker
		   
<input type=text id=dateField>

// this will create a date picker, which pops up when the textfield gets focus
$("#dateField").datepicker();

// show the drop-down fields to let the user jump around months and years
$("#dateField").datepicker({
   changeMonth: true,
   changeYear: true
});

// show two months instead of one
$("#dateField").datepicker({
   numberOfMonths: 2
});

// because this widget has to support internationalization, it has a TON of
// functions that let you set every aspect of the calendar, to support any
// possible calendar settings.  (Although it doesn't look like the jQuery team
// felt it was necessary to support the Mayan calendar.  Guess 2012 is right
// around the corner.)
//
// This function lets you set the date format that will appear in the textfield.
$("#dateField").datepicker({
   dateFormat: 'dd/mm/yyyy'
});

// Gets the date back from the datepicker
var date = $("#dateField").datepicker("getDate");

ProgressBar


그림 6. ProgressBar
왼쪽 1/4이 파란색으로 표시된 가로 진행 표시줄을 보여 주는 이미지

Listing 7. ProgressBar
		   
<div id="progressbarExample"></div>

// make this div a progress bar
$("#progressbarExample").progressbar();

// start the progress bar
$("#progressbarExample").start();

// stop the progress bar
$("#progressbarExample").stop();

Dialog


그림 7. Dialog
라틴 텍스트가 포함된 대화 상자 텍스트 상자를 보여 주는 창

이 시리즈를 처음부터 읽었다면 "jQuery로 작업하기, Part 1: 매개체로서의 JQuery: 플러그인을 사용하여 jQuery 함수를 작성하고 확장하기"에서 설명한 BlockUI 플러그인과 Dialog 위젯의 차이점이 궁금할 것이다. 둘 다 원하는 대화 상자 기능을 수행하므로 편한 대로 선택해서 사용하면 된다. BlockUI를 사용할 경우에는 대화 상자의 모습을 구체적으로 지정할 수 있는 유연성이 제공되는 반면 이 Dialog 위젯을 사용할 경우에는 대부분의 작업이 자동으로 수행된다.


Listing 8. ProgressBar
		   
<div id="dialogExample" title="Example Dialog">Text here.  Warning!!</div>

// turn the DIV into a dialog
$("#dialogExample").dialog();

// open the dialog, then close the dialog
$("#dialogExample").dialog("open").dialog("close");

// make a dialog that the user can't drag around, is modal, and in the center of the page
$("#dialogExample").dialog({
    draggable: false,
    modal: true,
    position: "center"
    });

Tabs


그림 8. Tabs
세 개의 탭과 라틴 텍스트를 보여 주는 창

Tabs 위젯은 아마도 필자가 가장 좋아하지 않는 위젯일 것이다. 필자가 사용했을 때 이 위젯은 한번도 올바르게 작동하지 않았으며 필자는 이 특별한 위젯이 CSS에 대한 의존성이 매우 높다는 것을 알게 되었다. 그리고 이 CSS는 위젯의 올바른 작동을 위해 사용자가 제공해야 한다. 예제가 길고 복잡할 뿐만 아니라 필자가 이 위젯에 대한 편견을 가지고 있기 때문에 이 기사에서는 이 위젯에 대한 예제를 다루지 않는다. 개인적으로 필자는 별도의 DIV를 직접 만들어서 사용해도 이 위젯을 사용하는 것과 마찬가지로 쉽게 작업할 수 있다는 생각을 가지고 있다.

ThemeRoller

UI 라이브러리의 마지막 기능은 "ThemeRoller"이다. 이름이 이상하기는 해도 라이브러리의 위젯을 코드에서 사용하기로 선택한 경우에는 이 기능을 매우 유용하게 사용할 수 있다. 지금까지 설명한 위젯에는 기본 디자인이 지정되어 있다. 하지만 이 디자인은 사용자의 페이지 디자인과 일치하지 않을 확률이 높다. 따라서 이러한 위젯을 사용하기로 결정했다면 위젯의 디자인을 사이트의 디자인에 맞추고 싶을 것이다. 이러한 요구 사항을 파악한 UI 라이브러리의 제작자들은 사용자가 자신의 사이트에 맞게 CSS 파일을 수정한 후 저장할 수 있는 CSS 생성기를 지난 수 개월 동안 심혈을 기울여서 만들었다. 이렇게 생성된 CSS 파일이 모든 위젯의 디자인을 사용자 사이트의 디자인과 일치시켜 주므로 누구라도 이러한 위젯을 부담 없이 사용할 수 있을 것이다.

일반적으로 CSS 전문가가 아닌 작업자(필자처럼)가 수행하기에는 어려운 작업임에도 불구하고 ThemeRoller를 사용하면 그러한 작업에 필요한 CSS를 어렵지 않게 작성할 수 있다. 예를 들어, 위젯의 임의의 부분에 둥근 모서리를 지정할 수 있으며 그림자를 만들 수도 있다. 실제로 이 기능을 사용하면 UI 라이브러리를 사용할 때 CSS 파일의 복잡한 코딩 부분이 없어도 되기 때문에 그래픽을 잘 다루지 못하는 개발자라도 위젯을 쉽게 사용하고 수정할 수 있다. 어려운 CSS 파일 작업을 수행하고서 평범한 디자인의 위젯을 만드는 대신 ThemeRoller를 통해 비프로그래밍 방식으로 작업을 수행할 수 있다.

마지막으로 ThemeRoller에는 위젯을 꾸미는 데 사용할 수 있는 미리 작성된 여러 가지 테마가 있다. 필자와 비슷한 수준의 개발자라면 미리 작성된 테마를 보고서 자신이 직접 디자인한 테마보다도 훨씬 더 근사하다고 느낄 것이다. 사이트의 CSS를 ThemeRoller에 배치하는 대신 ThemeRoller CSS를 사이트에 배치할 수 있다. 이 방법은 전문가가 디자인한 듯한 멋진 웹 애플리케이션을 무료로 얻을 수 있는 "경제적인" 방법이다.


Posted by 홍반장水 홍반장水
TAG jQuery


About
AmCharts is a set of flash and JavaScript (HTML5) charts for your websites and Web-based products. AmCharts can extract data from simple CSV or XML files, or they can read dynamic data generated with PHP, .NET, Java, Ruby on Rails, Perl, ColdFusion, and many other programming languages.


Blog About JavaScript amCharts – Win an iPad


JavaScript / HTML5 charts

3D Donut Chart
3D Stacked Column
Bar and line
Bars or columns
Bars with gradient fills
Candlestick or OHLC
Chart with legend
Custom bullets
Date Based category axis
Vertical and horizontal guides
Multiple value axes
Pie and column
Pie chart
Rotated axis values
Smoothed line and step

Stock

Data at irregular intervals
OHLC chart
Multiple data sets
Events
Smoothed line chart
Structured products
Vertical legend
Intraday data
Stacked
Drawing trend lines

Column & Bar
Column and line chart mix
3D Stacked column chart
Controlling chart with JavaScript
Chart with gradient fills
Histogram chart
Floating chart
Images above the columns
Columns with rounded corners
Auto-resizing chart

Line & Area

Chart with scroller
Chart with data gaps
Two Y-axes chart
Logarithmic scale chart
Auto-reloading chart
Chart with reversed Y axis

Pie & Donut

2D pie chart
3D donut chart
Saving chart as image
Advanced navigation menus

Scatter & Bubble

Time plot
Chart with value indicator
Bulls eye chart
Error bars
Timeline

Radar & Polar

Stacked radar chart
100% stacked spider chart
Polar chart


Posted by 홍반장水 홍반장水

HTML5 Sencha App vs. native iPhone App side by side (HD)

sencha로 만든 앱과 네이티브앱의 비교
On the left, Sencha version of touchNOC Manager app. On the right, native iPhone App version. Most of navigation done side-by-side, except for features unique for iPhone App version (favorites re-ordering and search).

Posted by 홍반장水 홍반장水

Sencha Touch 에서 이미지 버튼 구현하기.

Image buttons in Sencha Touch?

Application JavaScript:
Code:
Ext.setup({
    onReady: function() {		
	var rootPanel = new Ext.Panel({
		fullscreen: true,					
		dockedItems:[{					
			xtype: 'toolbar',
			dock: 'top',
			items: [{
				xtype: 'button',
				cls: 'btnAction'						
			}]								
		}]			
	});
    }
});





CSS:
Code:
.btnAction {
	background: url(../action.png);
	width: 40px;
	height: 40px;
}


Posted by 홍반장水 홍반장水