SlideShare uma empresa Scribd logo
1 de 10
jQuery를 이용하여 웹 위젯(Web Widget)을 만드는 방법
                                원제 : How to build a web widget (using   jQuery)

                             http://alexmarandon.com/articles/web_widget_jquery/

                              Published on 15 June 2010, updated on 23 May 2012

                                                           번역 : SYS4U 김형석




소개 (Introduction)

London’s Design Museum에 관련된 작업을 진행하면서 몇 가지 웹 위젯을 작성하였는데, 그 과정
에서 몇몇 유용한 내용들을 알게 되었다. 또, 웹 위젯에 대한 유용한 정보들은 대부분 인터넷에서
찾아볼 수 있지만, 아쉽게도 jQuery를 이용하여 웹 위젯을 만드는 상세한 내용을 접하지는 못했
었다. 그리고 그것이 이 글을 쓰기로 결심한 이유가 되었다. 이 글에서는 순수하게 웹 위젯에 대
한 기술만을 다룰 것이다. 그러므로 JavaScript와 JQuery, 그리고 웹 개발 방식에 대한 이해가 있
어야 이 글을 읽는 데 무리가 없을 것이다.

이 글이 다루고자 하는 내용은 다음과 같다. :

  -   위젯 코드가 위젯을 사용하는 상이한 웹 페이지들에서 뜻하지 않게 엉망이 되어 버리지
      않도록 하는 방법

  -   외부 CSS와 Javascript 파일을 동적으로 로딩하는 방법

  -   JSONP를 이용하여 브라우저의 single-origin policy(도메인 제한)을 회피하는 방법

아직은 위의 내용이 무슨 말인지 몰라도 걱정할 필요는 없다. 앞으로 웹 위젯을 만드는 과정에서
어떤 기술들이 사용되게 될지 상세히 다룰 테니 말이다.




웹 위젯이란 무엇인가?

웹 위젯은 웹 페이지로 전송되는 데이터를 화면에 표시하는 데에 사용되는 “웹 페이지의 한
조각(a chunk of web page)”이며 컴포넌트이다. 일반적으로 웹 위젯은 HTML, CSS,
Javascript 이 섞여 있는 다소 복잡한 모양인 경우가 많다. 웹 위젯 제작자들은 이러한
복잡성을 감추고, 그리하여 웹 위젯 사용자들이 가능한 한 손쉽게 위젯을 웹 페이지 내에
추가할 수 있기를 원한다.
위젯 사용하기 (Widget Usage)

이 글에서 다룰 웹 위젯들은 단 두 개의 HTML 태그를 이용하여 웹 페이지에 추가될 수 있도록
작성될 것이다. 그것들은 아래에서 보듯 하나의 스크립트(script) 태그와, 페이지 내에서 위젯을
추가할 곳을 명시할 컨테이너 태그(일반적으로는 div가 될 것이다)들이다.


<script src="http://example.com/widget/script.js" type="text/javascript"></script>
<div id="example-widget-container"></div>



정말 이 두 태그 만으로 웹 페이지에 웹 위젯을 추가할 수 있게 된다. 스크립트 태그에 입력되어
있는 코드는 위젯을 구성하는 여러 가지 자산(asset)들을 다운로드하고 컨테이너 안의 내용을 갱
신하게 될 것이다.




더 단순해 질 수는 없나?

기술적으로만 본다면, 위젯 코드 내에서 document.write() 코드를 이용하여 위젯이 담길
컨테이너를 명시하지 않고 사용할 수도 있다. 몇몇 위젯들이 이런 방식을 이용하고 있고,
그래서 스크립트 태그만으로 위젯을 사용할 수 있도록 코드의 양을 줄여주기는 한다.
하지만, 그다지 권장할 만한 방법이 아니라고 생각한다. 그 이유는,

- document.write()는 페이지가 완전히 로딩된 다음에는 호출될 수가 없다. 이 이야기는
브라우저가 스크립트 태그를 인식하면 곧바로 위젯 코드가 실행되어야 한다는 것을
의미한다. 보통 위젯 코드는 서버로부터 데이터를 가져오는 데에 사용되는데, 이럴 경우
페이지 로딩이 위젯 코드의 데이터 수신 작업으로 인해 잠시 중단되게 되고, 이로 인해
사용자가 페이지 로딩 시간이 오래 걸린다고 생각할 수도 있다.

- 페이지 내에서 위젯을 담을 컨테이너 태그를 따로 사용하게 되면, 스크립트 태그를
필요한 아무 곳에나 입력하여도 된다. 즉, 일반적인 스크립트 사용 권고안처럼 스크립트
태그를 페이지의 맨 아래에 둘 수도 있다.




코드 고립시키기 (Code Isolation)

어떤 페이지에서 어떤 자바스크립트가 사용될지 예측할 수 없기 때문에, 다른 자바스크립트 코드
와 동시에 사용되어도 충돌(clash)이 발생하지 않도록 해야만 한다. 이를 위해, 모든 위젯 코드를
익명 펑션(anonymous function) 내에 입력한 뒤에 이 익명 펑션을 호출하는 방식을 이용할 것이
다. 익명 펑션 내에 작성된 코드에서 사용되는 모든 변수들은 외부의 변수와는 아무런 관계 없이
사용할 수 있게 된다.

다음은 이 기법을 이용한 예제이다.
var foo = "Hello World!";
document.write("<p>Before our anonymous function foo means '" + foo + '".</p>');

(function() {
    // The following code will be enclosed within an anonymous function
    var foo = "Goodbye World!";
    document.write("<p>Inside our anonymous function foo means '" + foo + '".</p>');
})(); // We call our anonymous function immediately

document.write("<p>After our anonymous function foo means '" + foo + '".</p>');



결과는 다음과 같다.


Web Widget Tutorial
Example 1 - Code isolation
Before our anonymous function foo means 'Hello World!".
Inside our anonymous function foo means 'Goodbye World!".
After our anonymous function foo means 'Hello World!".




위에서 볼 수 있듯이, 익명 펑션 내에서 사용된 foo 변수는 전역(global) foo 변수와 충돌 없이
사용할 수 있다.




변수를 선언할 때는 항상 var 를 사용하라

외부 javascript 에 의해 위젯 내의 변수가 간섭을 받는 것을 막기 위해, 변수를 선언할
때에는 반드시 var 지시어를 사용해야 한다. 그렇지 않고 변수에 값을 할당하게 되면, 위젯
내의 변수가 아닌 외부의 변수를 변경하게 되는 것이다. 변수를 선언하고 사용할 때에는
언제나 var 지시어를 사용하는 것이 바람직한 습관이다.




자바스크립트 라이브러리 로딩(Loading Javascript Libraries)

위젯을 작성하는 데에 많은 양의 자바스크립트 코드가 필요하게 될 것이라면, 보통 jQuery와 같
은 자바스크립트 라이브러리를 사용하게 된다. 하지만, 위젯을 사용하는 페이지 내에 jQuery가
로딩되어있다는 보장이 없기 때문에, 필요한 경우 jQuery를 동적으로 로딩하도록 해야 한다.


(function() {

// Localize jQuery variable
var jQuery;

/******** jQuery 가 로딩되지 않았다면 동적으로 로드한다. *********/
if (window.jQuery === undefined) {
    var script_tag = document.createElement('script');
    script_tag.setAttribute("type","text/javascript");
    script_tag.setAttribute("src", "http://code.jquery.com/jquery-latest.min.js ");
    if (script_tag.readyState) {
       script_tag.onreadystatechange = function () { // 예전 버전 IE 를 위해
            if (this.readyState == 'complete' || this.readyState == 'loaded') {
                 scriptLoadHandler();
            }
       };
    } else { // Other browsers
       script_tag.onload = scriptLoadHandler;
    }
   (document.getElementsByTagName("head")[0] ||
    document.documentElement).appendChild(script_tag);
} else {
    // The jQuery version on the window is the one we want to use
    jQuery = window.jQuery;
    main();
}

/******** Called once jQuery has loaded ******/
function scriptLoadHandler() {
    // Restore $ and window.jQuery to their previous values and store the
    // new jQuery in our local jQuery variable
    jQuery = window.jQuery.noConflict(true);
    // Call our main function
    main();
}

/******** Our main function ********/
function main() {
    jQuery(document).ready(function($) {
         // We can use jQuery here
    });
}

})(); // We call our anonymous function immediately



먼저, jQuery를 담고 있을 지역변수 하나를 작성하였다. 그 다음, jQuery가 페이지 내에 추가되어
있는지 확인하는데, 이는 jQuery가 페이지에 이미 추가되어 있는 경우 또다시 로딩하지 않도록
하기 위함이다. jQuery 라이브러리를 로딩하기 위해, jQuery 최신 소스를 다운받을 수 있는 URL
을 이용하는 스크립트 엘리먼트를 작성한다. 그 뒤, 헤드(head) 태그가 있다면 헤드 태그에, 없다
면 문서(document)의 맨 마지막에 이 스크립트 엘리먼트를 추가(appendChild)한다.

작성하는 위젯 코드에서 jQuery 라이브러리를 사용하려면, jQuery 라이브러리가 모두 로딩되어
있는지 확인해야 한다.              여기서는 scriptLoadHandler라는 펑션을 이용하는데, 이 펑션은 스크립트
의 로딩이 완료되면 딱 1회 실행되도록 되어 있다. 대부분의 브라우저(Firefox, Safari, Opera,
Chrome 등)들에서는 단순히 script 엘리먼트의 onload 속성에 이 펑션을 할당하면 된다. 인터넷
익스플로러 9의 경우에는 약간 다른 방법을 이용할 때가 있는데, 여기서는 onreadystatechange
핸들러를 등록하여 사용하고 있다. 이 onreadystatechange 핸들러는 ready state가 complete나
loaded 상태가 되면 아래 작성해 둔 main 펑션을 호출할 것이다.


왜 IE 의 onreadystatechange 핸들러에서는 두 가지 상태를 확인하는가?

스크립트가 로딩되면, 인터넷 익스플로러는 readyState 를 complete 나 loaded 로 변경하는데,
이것은 페이지가 어떤 방식으로 로딩되었는지에 영향을 받는다. 페이지가
캐시(Cache)로부터 로딩되는 경우에는 상태가 complete 로 변경되고, 네트워크에서 다운로드
되는 경우에는 loaded 로 변경된다. 그러므로, 만약 loaded 상태에 대해서만 확인한다면,
링크를 통해 다른 페이지로 이동했다가 ‘뒤로가기’ 버튼을 이용하여 원래의 페이지로
돌아오는 경우에 정상적으로 동작하지 않게 된다.



작성된 scriptLoadHandler 펑션 내에서는 jQuery 라이브러리가 다른 자바스크립트 라이브러리나
다른 버전의 jQuery와 동시에 사용될 수 있다는 것을 고려해야 한다. 그러므로 다음 코드 한 줄
은 주의를 기울여 볼 만 하다.


jQuery = window.jQuery.noConflict(true);



이 행이 실행되기 전이라면, window.jQuery는 이 위젯 코드 내에서 지정한 jQuery를 가리키게 되
고, 이로 인해 외부에서 선언한 예전 버전의 jQuery를 덮어쓰게 될 수도 있다.
Window.jQuery.noConflict(true)를 호출함으로써 window.jQuery를 원래 페이지가 로딩될 때 지
정된 jQuery 객체로 변경하게 되고, 대신 새로 로딩한 jQuery를 리턴하여 우리의 익명 펑션 내의
jQuery 변수에 할당하게 된다. 동시에 $ 변수도 예전 것으로 되돌리는데, 이 덕분에 Prototype과
같은 또 다른 라이브러리를 아무 문제 없이 사용할 수 있게 된다.

마지막으로, scriptLoadHandler가 현재 버전의 jQuery를 이용하는 main 펑션을 호출하여 위젯 객
체를 생성하게 된다.

이제 기초공사를 마쳤으니, 가장 재미있는 부분을 다루기로 하자.




데이터 로딩하기(Loading data from our site)

지금 작성하고 있는 위젯은 단순한 HTML을 웹 페이지에 출력하는 것이 목적이기 때문에, XML
이나 JSON과 같은 중계 데이터 형식이 필요하지 않다. 그저 서버로부터 HTML 데이터를 수신한
뒤, 이 HTML을 이용하여 위젯의 내용을 갱신하도록 할 것이다.

지금 작성하고 있는 위젯이 뉴스의 헤드라인 목록을 보여주는 것이라고 하자. 서버는 다음과 같
은 HTML 코드를 출력할 것이다.


<ul>
  <li><a href="http://example.com/a-first news">A first news</a></li>
  <li><a href="http://example.com/another-news">Another news</a></li>
  <li><a href="http://example.com/an-interesting-news">An interesting news</a></li>
</ul>



만약 위젯을 포함하는 페이지(호스트 페이지)가 동일한 서버에서 서비스된다면, 일반적인 AJAX
호출(굳이 따지자면 이 경우에는 AHAH - Asynchronous HTML And HTTP)만으로 HTML 데이
터를 얻어 DOM을 갱신한 수 있다. 하지만 일반적으로 웹 위젯은 웹 위젯이 사용되는 페이지와
데이터를 얻어오는 페이지가 다를 수 있고, 이 경우 브라우저가 다른 도메인으로의 AJAX 요청을
허용하지 않게 된다. 이러한 보안 규제를 Same Origin Policy라고 한다.

다행히, 이 규제를 우회할 방법이 존재한다. 브라우저는 스크립트 태그가 다른 도메인의 스크립트
를 포함(include)하는 것을 허용한다. 결국 이 방법을 이용한 JSONP라는 간단한 기법으로 데이
터를 얻어올 수 있다. JSONP의 기본 개념은, 펑션 속에 감싸져(wrapped) 있는 JSON Data를 가
져오는 script 태그를 생성하여 사용하는 것이다. 만약 다음과 같은 script 태그를 작성하였다면 :


<script type="text/javascript" src="http://example.com/widget/data.js"></script>



http://example.com/widget/data.js 로부터 전송되는 내용은 아래와 같이 보일 것이다.


our_callback( {"foo": 42, "bar": 23 } )



그러면 아래와 같이 our_callback 펑션을 선언하고 JSON 데이터를 파라미터로 받도록 한다.


function our_callback(json_data) {
    // do stuff with json_data
}



다행인 점은, jQuery가 JSONP를 내부적으로 지원한다는 것이다. jQuery는 우리를 위해 스크립트
태그와 콜백펑션(Callback Function)을 생성하고, 이 콜백펑션의 이름을 파라미터로 전송하는 일
도 해 준다. 여기서 우리가 관심을 갖는 유일한 데이터는 위젯 컨테이너에 추가할 HTML 조각이
고, 서버 측에서는 다음과 같이 콜백펑션과 JSON 데이터를 이용하여 데이터를 전송하게 될 것이
다.


callback_name_generated_by_jquery( {"html": "<ul><li>(...)</li></ul>" } )
Ruby on Rails를 이용하여 서버에서 JSONP를 작성하는 예제는 다음과 같다.


# Store HTML in a variable rather than returning in to the browser
html = render_to_string
# Build a JSON object containing our HTML
json = {"html" => html}.to_json
# Get the name of the JSONP callback created by jQuery
callback = params[:callback]
# Wrap the JSON object with a call to the JSONP callback
jsonp = callback + "(" + json + ")"
# Send result to the browser
render :text => jsonp, :content_type => "text/javascript"



클라이언트 코드는 일반적인 AJAX 호출을 통해 JSON 데이터를 받는 것과 유사하다.


var widget_url = "http://example.com/wiget_data.js?callback=?"
$.getJSON(widget_url, function(data) {
  $('#example-widget-container').html(data.html);
});




AJAX 요청을 전송할 때, 반드시 callback=? 과 같은 파라미터 문자열을 URL 에 포함하여야
한다. 그렇지 않으면 서버가 콜백펑션의 이름을 받을 수 없게 된다.



이제 서버에서 HTML을 수신할 수 있게 되었고, 그것을 웹 페이지의 위젯 컨테이너에 삽입할 수
있게 되었다. 이제 남은 마지막 작업은 지금까지 만든 위젯에 스타일을 추가하는 것이 될 것이다.




CSS 로드(Loading CSS)

스타일시트를 로딩하기 위해 다음과 같이 javascript를 이용하여 link 태그를 생성하면 된다.


var css_link = $("<link>", {
    rel: "stylesheet",
    type: "text/css",
    href: "style.css"
});
css_link.appendTo('head');
통합하기(Putting things together)

지금까지 웹 위젯을 작성하기 위해 조금씩 다른 내용들을 보아 왔다. 이제 이것들을 하나로 합쳐
서 간단한 예제를 만들 차례이다.                    이 모든 내용을 통합한 Javascript 코드는 다음과 같다.


(function() {

// Localize jQuery variable
var jQuery;

/******** Load jQuery if not present *********/
if (window.jQuery === undefined || window.jQuery.fn.jquery !== '1.4.2') {
    var script_tag = document.createElement('script');
    script_tag.setAttribute("type","text/javascript");
    script_tag.setAttribute("src",
         "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js");
    if (script_tag.readyState) {
       script_tag.onreadystatechange = function () { // For old versions of IE
             if (this.readyState == 'complete' || this.readyState == 'loaded') {
                  scriptLoadHandler();
            }
       };
    } else {
       script_tag.onload = scriptLoadHandler;
    }
    // Try to find the head, otherwise default to the documentElement
    (document.getElementsByTagName("head")[0] ||
document.documentElement).appendChild(script_tag);
} else {
    // The jQuery version on the window is the one we want to use
    jQuery = window.jQuery;
    main();
}

/******** Called once jQuery has loaded ******/
function scriptLoadHandler() {
    // Restore $ and window.jQuery to their previous values and store the
    // new jQuery in our local jQuery variable
    jQuery = window.jQuery.noConflict(true);
    // Call our main function
    main();
}

/******** Our main function ********/
function main() {
    jQuery(document).ready(function($) {
         /******* Load CSS *******/
         var css_link = $("<link>", {
             rel: "stylesheet",
             type: "text/css",
href: "style.css"
         });
         css_link.appendTo('head');

       /******* Load HTML *******/
       var jsonp_url = "http://al.smeuh.org/cgi-bin/webwidget_tutorial.py?callback=?";
       $.getJSON(jsonp_url, function(data) {
         $('#example-widget-container').html("This data comes from another server: " +
data.html);
       });
   });
}

})(); // We call our anonymous function immediately



그리고 이 위젯과 통신하기 위한 서버 코드는 다음과 같다.(Python을 이용하여 작성된 CGI 스크
립트이다.)


#!/usr/bin/python

import cgi

params = cgi.FieldStorage()

print "Content-Type: text/javascriptn"

if not 'callback' in params:
     print "ERROR: you must pass a callback parameter"
else:
     jsonp = "%s ( {'html': '<strong>Hello World!</strong>' } )"
     print jsonp % params['callback'].value



물론, 실제 사용될 웹 위젯은, 클라이언트 측과 서버 측 모두에서, 훨씬 더 복잡할 것이다. 하지
만, 웹 위젯에 대한 기본적인 내용들에 대해서 이해했으리라 믿는다.




호스트 페이지 내의 에러 처리(Dealing with errors on host pages)

지금까지 작성한 코드는 보통 정상적으로 동작할 것이지만, Shyam Subramanyan이 보고한 바 대
로, main 펑션 내에서 jQuery(document).ready를 이용하는 데에 문제가 발생할 수도 있다. 이것
은 호스트 페이지에서 jQuery를 로딩한 상태에서 jQuery(document).ready 가로채(hook) 호출한
경우 발생한다. 이렇게 호스트 페이지 내의 jQuery 가로채기(jQuery hook)로 인해 오류가 발생한
경우에는 위젯 내의 main 펑션은 실행되지 않게 된다.
Shyam이 제시한 해결책은, 호스트 페이지가 위젯을 호출할 준비가 되어 있는지를 확인하는 코드
를 추가하는 것이다. 전체 document가 준비가 되기를 기다리는 대신에, 관련된 DOM 엘리먼트나
위젯이 사용하는 자바스크립트 오브젝트들이 준비되었는지 확인하는 것인데, 이 코드는 다음과
같다. (Listly라는 사이트에서 이용되고 있는 코드이다.)


Listly.listlyReady = function () {
  // Check for presence of required DOM elements or other JS your widget depends on
  if (Listly.jQuery.listlybox && ListlyAuth) {
     window.clearInterval(Listly._readyInterval);
     // Make stuff here
  }
};

// This our new main function
function main () {
   Listly._readyInterval = window.setInterval(Listly.listlyReady, 500);
}



Shyam에 의하면, 이 코드는 Listly 사이트에서 정상적으로 동작했고, 호스트 페이지가 무겁거나
오류가 있는 경우에 위젯이 훨씬 더 빨리 로딩된다고 하였다.




결론(Conclusion)

매우 많은 사람들이 플러그인들(plugins)을 로딩하는 방법에 대해 물어보았지만, 아쉽게도 나는
그에 대한 완벽한 대답을 알고 있지는 못하다. 개인적으로는 익명 펑션 내부에 스크립트 코드와
함께 작은 플러그인 소스코드를 추가함으로써 이 문제를 해결하긴 했지만, 이런 방법은 cross-
domain에 대한 고려를 하지 않은 플러그인(plugin)으로 인해 호스트 페이지의 처리가 방해
(interfere) 받을 수 있음도 고려해야 한다.

자주 질문되는 또 다른 내용은, 위젯을 설정 가능하게(configurable) 만드는 방법에 대한 것이다.
코드 내 주석에서 제안한 것과 같이, 마크업(markup)을 최소화하기 위해, 위젯을 호출하는 URL
에 쿼리 스트링(query string)을 추가하고, 스크립트 태그에 ID를 추가하면 된다. 이렇게 함으로
써, 스크립트 내에서 자기 자신의 스크립트 태그에 접근할 수 있고, 스크립트 태그의 URL에 포함
되어 있는 쿼리 스트링의 정보를 분석하여 자동으로 설정할 수 있게 된다. 혹은 좀 더 직접적인
방법으로, 작성된 위젯의 임베드(embed) 코드에 보이지 않는 마크업(invisible markup)을 추가할
수도 있다. SNS에서 제공하는 공유 버튼들이 이 방식을 이용한다.




(후략)

Mais conteúdo relacionado

Mais procurados

자바스크립트의 또다른 발전, Backbone.js
자바스크립트의 또다른 발전, Backbone.js자바스크립트의 또다른 발전, Backbone.js
자바스크립트의 또다른 발전, Backbone.jsJinKwon Lee
 
Facebook은 React를 왜 만들었을까?
Facebook은 React를 왜 만들었을까? Facebook은 React를 왜 만들었을까?
Facebook은 React를 왜 만들었을까? Kim Hunmin
 
[111015/아꿈사] HTML5를 여행하는 비(非) 웹 개발자를 위한 안내서 - 1부 웹소켓.
[111015/아꿈사] HTML5를 여행하는 비(非) 웹 개발자를 위한 안내서 - 1부 웹소켓.[111015/아꿈사] HTML5를 여행하는 비(非) 웹 개발자를 위한 안내서 - 1부 웹소켓.
[111015/아꿈사] HTML5를 여행하는 비(非) 웹 개발자를 위한 안내서 - 1부 웹소켓.sung ki choi
 
Data-binding AngularJS
Data-binding AngularJSData-binding AngularJS
Data-binding AngularJSEunYoung Kim
 
자바 웹 개발 시작하기 (10주차 : ㅌㅗㅇ ㅎㅏ ㄹㅏ)

자바 웹 개발 시작하기 (10주차 : ㅌㅗㅇ ㅎㅏ ㄹㅏ)
자바 웹 개발 시작하기 (10주차 : ㅌㅗㅇ ㅎㅏ ㄹㅏ)

자바 웹 개발 시작하기 (10주차 : ㅌㅗㅇ ㅎㅏ ㄹㅏ)
DK Lee
 
Polymer따라잡기
Polymer따라잡기Polymer따라잡기
Polymer따라잡기Han Jung Hyun
 
Angularjs 도입 선택 가이드
Angularjs 도입 선택 가이드Angularjs 도입 선택 가이드
Angularjs 도입 선택 가이드NAVER D2
 
React 튜토리얼 2차시
React 튜토리얼 2차시React 튜토리얼 2차시
React 튜토리얼 2차시태현 김
 
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기XpressEngine
 
처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2성일 한
 
가볍게 살펴보는 AngularJS
가볍게 살펴보는 AngularJS가볍게 살펴보는 AngularJS
가볍게 살펴보는 AngularJSJae Sung Park
 
Django in Production
Django in ProductionDjango in Production
Django in ProductionHyun-woo Park
 
Single-page Application
Single-page ApplicationSingle-page Application
Single-page ApplicationSangmin Yoon
 
다시보는 Angular js
다시보는 Angular js다시보는 Angular js
다시보는 Angular jsJeado Ko
 
ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는Taegon Kim
 
AngularJS 2, version 1 and ReactJS
AngularJS 2, version 1 and ReactJSAngularJS 2, version 1 and ReactJS
AngularJS 2, version 1 and ReactJSKenneth Ceyer
 
9주 dom & event advanced 실습
9주  dom & event advanced 실습9주  dom & event advanced 실습
9주 dom & event advanced 실습지수 윤
 
Meteor를 통해서 개발하는 웹어플리케이션 서비스
Meteor를 통해서 개발하는 웹어플리케이션 서비스Meteor를 통해서 개발하는 웹어플리케이션 서비스
Meteor를 통해서 개발하는 웹어플리케이션 서비스WebFrameworks
 

Mais procurados (20)

자바스크립트의 또다른 발전, Backbone.js
자바스크립트의 또다른 발전, Backbone.js자바스크립트의 또다른 발전, Backbone.js
자바스크립트의 또다른 발전, Backbone.js
 
Facebook은 React를 왜 만들었을까?
Facebook은 React를 왜 만들었을까? Facebook은 React를 왜 만들었을까?
Facebook은 React를 왜 만들었을까?
 
[111015/아꿈사] HTML5를 여행하는 비(非) 웹 개발자를 위한 안내서 - 1부 웹소켓.
[111015/아꿈사] HTML5를 여행하는 비(非) 웹 개발자를 위한 안내서 - 1부 웹소켓.[111015/아꿈사] HTML5를 여행하는 비(非) 웹 개발자를 위한 안내서 - 1부 웹소켓.
[111015/아꿈사] HTML5를 여행하는 비(非) 웹 개발자를 위한 안내서 - 1부 웹소켓.
 
Handlebars
HandlebarsHandlebars
Handlebars
 
Data-binding AngularJS
Data-binding AngularJSData-binding AngularJS
Data-binding AngularJS
 
자바 웹 개발 시작하기 (10주차 : ㅌㅗㅇ ㅎㅏ ㄹㅏ)

자바 웹 개발 시작하기 (10주차 : ㅌㅗㅇ ㅎㅏ ㄹㅏ)
자바 웹 개발 시작하기 (10주차 : ㅌㅗㅇ ㅎㅏ ㄹㅏ)

자바 웹 개발 시작하기 (10주차 : ㅌㅗㅇ ㅎㅏ ㄹㅏ)

 
Polymer따라잡기
Polymer따라잡기Polymer따라잡기
Polymer따라잡기
 
Angularjs 도입 선택 가이드
Angularjs 도입 선택 가이드Angularjs 도입 선택 가이드
Angularjs 도입 선택 가이드
 
React 튜토리얼 2차시
React 튜토리얼 2차시React 튜토리얼 2차시
React 튜토리얼 2차시
 
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
 
처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2
 
가볍게 살펴보는 AngularJS
가볍게 살펴보는 AngularJS가볍게 살펴보는 AngularJS
가볍게 살펴보는 AngularJS
 
Django in Production
Django in ProductionDjango in Production
Django in Production
 
Meteor2015 codelab
Meteor2015 codelab Meteor2015 codelab
Meteor2015 codelab
 
Single-page Application
Single-page ApplicationSingle-page Application
Single-page Application
 
다시보는 Angular js
다시보는 Angular js다시보는 Angular js
다시보는 Angular js
 
ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는
 
AngularJS 2, version 1 and ReactJS
AngularJS 2, version 1 and ReactJSAngularJS 2, version 1 and ReactJS
AngularJS 2, version 1 and ReactJS
 
9주 dom & event advanced 실습
9주  dom & event advanced 실습9주  dom & event advanced 실습
9주 dom & event advanced 실습
 
Meteor를 통해서 개발하는 웹어플리케이션 서비스
Meteor를 통해서 개발하는 웹어플리케이션 서비스Meteor를 통해서 개발하는 웹어플리케이션 서비스
Meteor를 통해서 개발하는 웹어플리케이션 서비스
 

Destaque

iOS Human Interface Guidlines #3_SYS4U
iOS Human Interface Guidlines #3_SYS4UiOS Human Interface Guidlines #3_SYS4U
iOS Human Interface Guidlines #3_SYS4Usys4u
 
From Java code to Java heap_SYS4U I&C
From Java code to Java heap_SYS4U I&CFrom Java code to Java heap_SYS4U I&C
From Java code to Java heap_SYS4U I&Csys4u
 
JavaScript Profiling With The Chrome Developer Tools_SYS4U I&C
JavaScript Profiling With The Chrome Developer Tools_SYS4U I&CJavaScript Profiling With The Chrome Developer Tools_SYS4U I&C
JavaScript Profiling With The Chrome Developer Tools_SYS4U I&Csys4u
 
iOS Human Interface Guidlines #5_SYS4U
iOS Human Interface Guidlines #5_SYS4UiOS Human Interface Guidlines #5_SYS4U
iOS Human Interface Guidlines #5_SYS4Usys4u
 
iOS Human Interface Guidlines #7_SYS4U
iOS Human Interface Guidlines #7_SYS4UiOS Human Interface Guidlines #7_SYS4U
iOS Human Interface Guidlines #7_SYS4Usys4u
 
iOS Human Interface Guidlines #11_SYS4U
iOS Human Interface Guidlines #11_SYS4UiOS Human Interface Guidlines #11_SYS4U
iOS Human Interface Guidlines #11_SYS4Usys4u
 
iOS_Human_Interface_Guidlines_#4_SYS4U
iOS_Human_Interface_Guidlines_#4_SYS4UiOS_Human_Interface_Guidlines_#4_SYS4U
iOS_Human_Interface_Guidlines_#4_SYS4Usys4u
 
iOS Human Interface Guidlines #10_SYS4U
iOS Human Interface Guidlines #10_SYS4UiOS Human Interface Guidlines #10_SYS4U
iOS Human Interface Guidlines #10_SYS4Usys4u
 
iOS Human Interface Guidlines #12_SYS4U
iOS Human Interface Guidlines #12_SYS4UiOS Human Interface Guidlines #12_SYS4U
iOS Human Interface Guidlines #12_SYS4Usys4u
 
iOS Human_Interface_Guidlines_#2_SYS4U
iOS Human_Interface_Guidlines_#2_SYS4UiOS Human_Interface_Guidlines_#2_SYS4U
iOS Human_Interface_Guidlines_#2_SYS4Usys4u
 
Introduction to Fork Join Framework_SYS4U I&C
Introduction to Fork Join Framework_SYS4U I&CIntroduction to Fork Join Framework_SYS4U I&C
Introduction to Fork Join Framework_SYS4U I&Csys4u
 
iOS Human Interface Guidlines #8_SYS4U
iOS Human Interface Guidlines #8_SYS4UiOS Human Interface Guidlines #8_SYS4U
iOS Human Interface Guidlines #8_SYS4Usys4u
 
Observer Design Pattern in Java_SYS4U
Observer Design Pattern in Java_SYS4UObserver Design Pattern in Java_SYS4U
Observer Design Pattern in Java_SYS4Usys4u
 
30_eCommerce_sites_using_html5_SYS4U
30_eCommerce_sites_using_html5_SYS4U30_eCommerce_sites_using_html5_SYS4U
30_eCommerce_sites_using_html5_SYS4Usys4u
 
Implementing_AOP_in_Spring_SYS4U
Implementing_AOP_in_Spring_SYS4UImplementing_AOP_in_Spring_SYS4U
Implementing_AOP_in_Spring_SYS4Usys4u
 
Aspect Oriented Programming_SYS4U I&C
Aspect Oriented Programming_SYS4U I&CAspect Oriented Programming_SYS4U I&C
Aspect Oriented Programming_SYS4U I&Csys4u
 
2013 UX Design Trend Report Part 3_SYS4U I&C
2013 UX Design Trend Report Part 3_SYS4U I&C2013 UX Design Trend Report Part 3_SYS4U I&C
2013 UX Design Trend Report Part 3_SYS4U I&Csys4u
 
Java reflection & introspection_SYS4U I&C
Java reflection & introspection_SYS4U I&CJava reflection & introspection_SYS4U I&C
Java reflection & introspection_SYS4U I&Csys4u
 
iOS Human Interface Guidlines #14_SYS4U
iOS Human Interface Guidlines #14_SYS4UiOS Human Interface Guidlines #14_SYS4U
iOS Human Interface Guidlines #14_SYS4Usys4u
 
JavaEE6 Tutorial - Java Message Service_sys4u
JavaEE6 Tutorial - Java Message Service_sys4uJavaEE6 Tutorial - Java Message Service_sys4u
JavaEE6 Tutorial - Java Message Service_sys4usys4u
 

Destaque (20)

iOS Human Interface Guidlines #3_SYS4U
iOS Human Interface Guidlines #3_SYS4UiOS Human Interface Guidlines #3_SYS4U
iOS Human Interface Guidlines #3_SYS4U
 
From Java code to Java heap_SYS4U I&C
From Java code to Java heap_SYS4U I&CFrom Java code to Java heap_SYS4U I&C
From Java code to Java heap_SYS4U I&C
 
JavaScript Profiling With The Chrome Developer Tools_SYS4U I&C
JavaScript Profiling With The Chrome Developer Tools_SYS4U I&CJavaScript Profiling With The Chrome Developer Tools_SYS4U I&C
JavaScript Profiling With The Chrome Developer Tools_SYS4U I&C
 
iOS Human Interface Guidlines #5_SYS4U
iOS Human Interface Guidlines #5_SYS4UiOS Human Interface Guidlines #5_SYS4U
iOS Human Interface Guidlines #5_SYS4U
 
iOS Human Interface Guidlines #7_SYS4U
iOS Human Interface Guidlines #7_SYS4UiOS Human Interface Guidlines #7_SYS4U
iOS Human Interface Guidlines #7_SYS4U
 
iOS Human Interface Guidlines #11_SYS4U
iOS Human Interface Guidlines #11_SYS4UiOS Human Interface Guidlines #11_SYS4U
iOS Human Interface Guidlines #11_SYS4U
 
iOS_Human_Interface_Guidlines_#4_SYS4U
iOS_Human_Interface_Guidlines_#4_SYS4UiOS_Human_Interface_Guidlines_#4_SYS4U
iOS_Human_Interface_Guidlines_#4_SYS4U
 
iOS Human Interface Guidlines #10_SYS4U
iOS Human Interface Guidlines #10_SYS4UiOS Human Interface Guidlines #10_SYS4U
iOS Human Interface Guidlines #10_SYS4U
 
iOS Human Interface Guidlines #12_SYS4U
iOS Human Interface Guidlines #12_SYS4UiOS Human Interface Guidlines #12_SYS4U
iOS Human Interface Guidlines #12_SYS4U
 
iOS Human_Interface_Guidlines_#2_SYS4U
iOS Human_Interface_Guidlines_#2_SYS4UiOS Human_Interface_Guidlines_#2_SYS4U
iOS Human_Interface_Guidlines_#2_SYS4U
 
Introduction to Fork Join Framework_SYS4U I&C
Introduction to Fork Join Framework_SYS4U I&CIntroduction to Fork Join Framework_SYS4U I&C
Introduction to Fork Join Framework_SYS4U I&C
 
iOS Human Interface Guidlines #8_SYS4U
iOS Human Interface Guidlines #8_SYS4UiOS Human Interface Guidlines #8_SYS4U
iOS Human Interface Guidlines #8_SYS4U
 
Observer Design Pattern in Java_SYS4U
Observer Design Pattern in Java_SYS4UObserver Design Pattern in Java_SYS4U
Observer Design Pattern in Java_SYS4U
 
30_eCommerce_sites_using_html5_SYS4U
30_eCommerce_sites_using_html5_SYS4U30_eCommerce_sites_using_html5_SYS4U
30_eCommerce_sites_using_html5_SYS4U
 
Implementing_AOP_in_Spring_SYS4U
Implementing_AOP_in_Spring_SYS4UImplementing_AOP_in_Spring_SYS4U
Implementing_AOP_in_Spring_SYS4U
 
Aspect Oriented Programming_SYS4U I&C
Aspect Oriented Programming_SYS4U I&CAspect Oriented Programming_SYS4U I&C
Aspect Oriented Programming_SYS4U I&C
 
2013 UX Design Trend Report Part 3_SYS4U I&C
2013 UX Design Trend Report Part 3_SYS4U I&C2013 UX Design Trend Report Part 3_SYS4U I&C
2013 UX Design Trend Report Part 3_SYS4U I&C
 
Java reflection & introspection_SYS4U I&C
Java reflection & introspection_SYS4U I&CJava reflection & introspection_SYS4U I&C
Java reflection & introspection_SYS4U I&C
 
iOS Human Interface Guidlines #14_SYS4U
iOS Human Interface Guidlines #14_SYS4UiOS Human Interface Guidlines #14_SYS4U
iOS Human Interface Guidlines #14_SYS4U
 
JavaEE6 Tutorial - Java Message Service_sys4u
JavaEE6 Tutorial - Java Message Service_sys4uJavaEE6 Tutorial - Java Message Service_sys4u
JavaEE6 Tutorial - Java Message Service_sys4u
 

Semelhante a JQuery를 이용하여 웹 위젯 작성하기_(주)시스포유아이앤씨

알아봅시다, Polymer: Web Components & Web Animations
알아봅시다, Polymer: Web Components & Web Animations알아봅시다, Polymer: Web Components & Web Animations
알아봅시다, Polymer: Web Components & Web AnimationsChang W. Doh
 
LucideWorks Banana 소개
LucideWorks Banana 소개 LucideWorks Banana 소개
LucideWorks Banana 소개 SuHyun Jeon
 
[웹기반시스템 3조]e govframe 중간고사 제출 정리
[웹기반시스템 3조]e govframe 중간고사 제출 정리[웹기반시스템 3조]e govframe 중간고사 제출 정리
[웹기반시스템 3조]e govframe 중간고사 제출 정리구 봉
 
파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 Yong Joon Moon
 
Front end dev 2016 & beyond
Front end dev 2016 & beyondFront end dev 2016 & beyond
Front end dev 2016 & beyondJae Sung Park
 
[D2 오픈세미나]3.web view hybridapp
[D2 오픈세미나]3.web view hybridapp[D2 오픈세미나]3.web view hybridapp
[D2 오픈세미나]3.web view hybridappNAVER D2
 
Spring Boot + React + Gradle in VSCode
Spring Boot + React + Gradle in VSCodeSpring Boot + React + Gradle in VSCode
Spring Boot + React + Gradle in VSCodedpTablo
 
막하는 스터디 네 번째 만남 AngularJs (20151108)
막하는 스터디 네 번째 만남 AngularJs (20151108)막하는 스터디 네 번째 만남 AngularJs (20151108)
막하는 스터디 네 번째 만남 AngularJs (20151108)연웅 조
 
하이브리드앱 성능 극복
하이브리드앱 성능 극복하이브리드앱 성능 극복
하이브리드앱 성능 극복sung hwan Park
 
하이브리드앱 성능 극복
하이브리드앱 성능 극복하이브리드앱 성능 극복
하이브리드앱 성능 극복Mu-ik Jeon
 
프론트엔드 개발 첫걸음
프론트엔드 개발 첫걸음프론트엔드 개발 첫걸음
프론트엔드 개발 첫걸음DataUs
 
ReactJS로 시작하는 멀티플랫폼 개발하기
ReactJS로 시작하는 멀티플랫폼 개발하기ReactJS로 시작하는 멀티플랫폼 개발하기
ReactJS로 시작하는 멀티플랫폼 개발하기Taegon Kim
 
Isomorphicspring Isomorphic - spring web seminar 2015
Isomorphicspring Isomorphic - spring web seminar 2015Isomorphicspring Isomorphic - spring web seminar 2015
Isomorphicspring Isomorphic - spring web seminar 2015sung yong jung
 
JSP 빠르게 시작하기
JSP 빠르게 시작하기JSP 빠르게 시작하기
JSP 빠르게 시작하기Park JoongSoo
 
V8 add on with middleware modules
V8 add on with middleware modulesV8 add on with middleware modules
V8 add on with middleware modulesJay Kim
 

Semelhante a JQuery를 이용하여 웹 위젯 작성하기_(주)시스포유아이앤씨 (20)

4-3. jquery
4-3. jquery4-3. jquery
4-3. jquery
 
알아봅시다, Polymer: Web Components & Web Animations
알아봅시다, Polymer: Web Components & Web Animations알아봅시다, Polymer: Web Components & Web Animations
알아봅시다, Polymer: Web Components & Web Animations
 
LucideWorks Banana 소개
LucideWorks Banana 소개 LucideWorks Banana 소개
LucideWorks Banana 소개
 
[웹기반시스템 3조]e govframe 중간고사 제출 정리
[웹기반시스템 3조]e govframe 중간고사 제출 정리[웹기반시스템 3조]e govframe 중간고사 제출 정리
[웹기반시스템 3조]e govframe 중간고사 제출 정리
 
파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 파이썬 플라스크 이해하기
파이썬 플라스크 이해하기
 
Hacosa jquery 1th
Hacosa jquery 1thHacosa jquery 1th
Hacosa jquery 1th
 
Front end dev 2016 & beyond
Front end dev 2016 & beyondFront end dev 2016 & beyond
Front end dev 2016 & beyond
 
[D2 오픈세미나]3.web view hybridapp
[D2 오픈세미나]3.web view hybridapp[D2 오픈세미나]3.web view hybridapp
[D2 오픈세미나]3.web view hybridapp
 
Spring Boot + React + Gradle in VSCode
Spring Boot + React + Gradle in VSCodeSpring Boot + React + Gradle in VSCode
Spring Boot + React + Gradle in VSCode
 
막하는 스터디 네 번째 만남 AngularJs (20151108)
막하는 스터디 네 번째 만남 AngularJs (20151108)막하는 스터디 네 번째 만남 AngularJs (20151108)
막하는 스터디 네 번째 만남 AngularJs (20151108)
 
하이브리드앱 성능 극복
하이브리드앱 성능 극복하이브리드앱 성능 극복
하이브리드앱 성능 극복
 
하이브리드앱 성능 극복
하이브리드앱 성능 극복하이브리드앱 성능 극복
하이브리드앱 성능 극복
 
7. html5 api
7. html5 api7. html5 api
7. html5 api
 
프론트엔드 개발 첫걸음
프론트엔드 개발 첫걸음프론트엔드 개발 첫걸음
프론트엔드 개발 첫걸음
 
요즘웹개발
요즘웹개발요즘웹개발
요즘웹개발
 
ReactJS로 시작하는 멀티플랫폼 개발하기
ReactJS로 시작하는 멀티플랫폼 개발하기ReactJS로 시작하는 멀티플랫폼 개발하기
ReactJS로 시작하는 멀티플랫폼 개발하기
 
4-2. ajax
4-2. ajax4-2. ajax
4-2. ajax
 
Isomorphicspring Isomorphic - spring web seminar 2015
Isomorphicspring Isomorphic - spring web seminar 2015Isomorphicspring Isomorphic - spring web seminar 2015
Isomorphicspring Isomorphic - spring web seminar 2015
 
JSP 빠르게 시작하기
JSP 빠르게 시작하기JSP 빠르게 시작하기
JSP 빠르게 시작하기
 
V8 add on with middleware modules
V8 add on with middleware modulesV8 add on with middleware modules
V8 add on with middleware modules
 

Mais de sys4u

About Color_SYS4U
About Color_SYS4UAbout Color_SYS4U
About Color_SYS4Usys4u
 
Html5_SYS4U
Html5_SYS4UHtml5_SYS4U
Html5_SYS4Usys4u
 
Web Accessibility_SYS4U
Web Accessibility_SYS4UWeb Accessibility_SYS4U
Web Accessibility_SYS4Usys4u
 
iOS Human Interface Guidlines #15_SYS4U
iOS Human Interface Guidlines #15_SYS4UiOS Human Interface Guidlines #15_SYS4U
iOS Human Interface Guidlines #15_SYS4Usys4u
 
iOS Human Interface Guidlines #13_SYS4U
iOS Human Interface Guidlines #13_SYS4UiOS Human Interface Guidlines #13_SYS4U
iOS Human Interface Guidlines #13_SYS4Usys4u
 
UX Layout Design_SYS4U
UX Layout Design_SYS4UUX Layout Design_SYS4U
UX Layout Design_SYS4Usys4u
 
Advanced SWOT Analysis of e-commerce_SYS4U
Advanced SWOT Analysis of e-commerce_SYS4UAdvanced SWOT Analysis of e-commerce_SYS4U
Advanced SWOT Analysis of e-commerce_SYS4Usys4u
 
Proxy_design_pattern_in_Java_SYS4U
Proxy_design_pattern_in_Java_SYS4UProxy_design_pattern_in_Java_SYS4U
Proxy_design_pattern_in_Java_SYS4Usys4u
 
Java_Concurrency_Programming_SYS4U
Java_Concurrency_Programming_SYS4UJava_Concurrency_Programming_SYS4U
Java_Concurrency_Programming_SYS4Usys4u
 
iOS Human_Interface_Guidlines_#1_SYS4U
iOS Human_Interface_Guidlines_#1_SYS4UiOS Human_Interface_Guidlines_#1_SYS4U
iOS Human_Interface_Guidlines_#1_SYS4Usys4u
 
UIX UNIT_Several UI Teminologies Easy To Miss_SYS4U I&C
UIX UNIT_Several UI Teminologies Easy To Miss_SYS4U I&CUIX UNIT_Several UI Teminologies Easy To Miss_SYS4U I&C
UIX UNIT_Several UI Teminologies Easy To Miss_SYS4U I&Csys4u
 
Promotions_2nd_SYS4U I&C
Promotions_2nd_SYS4U I&CPromotions_2nd_SYS4U I&C
Promotions_2nd_SYS4U I&Csys4u
 

Mais de sys4u (12)

About Color_SYS4U
About Color_SYS4UAbout Color_SYS4U
About Color_SYS4U
 
Html5_SYS4U
Html5_SYS4UHtml5_SYS4U
Html5_SYS4U
 
Web Accessibility_SYS4U
Web Accessibility_SYS4UWeb Accessibility_SYS4U
Web Accessibility_SYS4U
 
iOS Human Interface Guidlines #15_SYS4U
iOS Human Interface Guidlines #15_SYS4UiOS Human Interface Guidlines #15_SYS4U
iOS Human Interface Guidlines #15_SYS4U
 
iOS Human Interface Guidlines #13_SYS4U
iOS Human Interface Guidlines #13_SYS4UiOS Human Interface Guidlines #13_SYS4U
iOS Human Interface Guidlines #13_SYS4U
 
UX Layout Design_SYS4U
UX Layout Design_SYS4UUX Layout Design_SYS4U
UX Layout Design_SYS4U
 
Advanced SWOT Analysis of e-commerce_SYS4U
Advanced SWOT Analysis of e-commerce_SYS4UAdvanced SWOT Analysis of e-commerce_SYS4U
Advanced SWOT Analysis of e-commerce_SYS4U
 
Proxy_design_pattern_in_Java_SYS4U
Proxy_design_pattern_in_Java_SYS4UProxy_design_pattern_in_Java_SYS4U
Proxy_design_pattern_in_Java_SYS4U
 
Java_Concurrency_Programming_SYS4U
Java_Concurrency_Programming_SYS4UJava_Concurrency_Programming_SYS4U
Java_Concurrency_Programming_SYS4U
 
iOS Human_Interface_Guidlines_#1_SYS4U
iOS Human_Interface_Guidlines_#1_SYS4UiOS Human_Interface_Guidlines_#1_SYS4U
iOS Human_Interface_Guidlines_#1_SYS4U
 
UIX UNIT_Several UI Teminologies Easy To Miss_SYS4U I&C
UIX UNIT_Several UI Teminologies Easy To Miss_SYS4U I&CUIX UNIT_Several UI Teminologies Easy To Miss_SYS4U I&C
UIX UNIT_Several UI Teminologies Easy To Miss_SYS4U I&C
 
Promotions_2nd_SYS4U I&C
Promotions_2nd_SYS4U I&CPromotions_2nd_SYS4U I&C
Promotions_2nd_SYS4U I&C
 

JQuery를 이용하여 웹 위젯 작성하기_(주)시스포유아이앤씨

  • 1. jQuery를 이용하여 웹 위젯(Web Widget)을 만드는 방법 원제 : How to build a web widget (using jQuery) http://alexmarandon.com/articles/web_widget_jquery/ Published on 15 June 2010, updated on 23 May 2012 번역 : SYS4U 김형석 소개 (Introduction) London’s Design Museum에 관련된 작업을 진행하면서 몇 가지 웹 위젯을 작성하였는데, 그 과정 에서 몇몇 유용한 내용들을 알게 되었다. 또, 웹 위젯에 대한 유용한 정보들은 대부분 인터넷에서 찾아볼 수 있지만, 아쉽게도 jQuery를 이용하여 웹 위젯을 만드는 상세한 내용을 접하지는 못했 었다. 그리고 그것이 이 글을 쓰기로 결심한 이유가 되었다. 이 글에서는 순수하게 웹 위젯에 대 한 기술만을 다룰 것이다. 그러므로 JavaScript와 JQuery, 그리고 웹 개발 방식에 대한 이해가 있 어야 이 글을 읽는 데 무리가 없을 것이다. 이 글이 다루고자 하는 내용은 다음과 같다. : - 위젯 코드가 위젯을 사용하는 상이한 웹 페이지들에서 뜻하지 않게 엉망이 되어 버리지 않도록 하는 방법 - 외부 CSS와 Javascript 파일을 동적으로 로딩하는 방법 - JSONP를 이용하여 브라우저의 single-origin policy(도메인 제한)을 회피하는 방법 아직은 위의 내용이 무슨 말인지 몰라도 걱정할 필요는 없다. 앞으로 웹 위젯을 만드는 과정에서 어떤 기술들이 사용되게 될지 상세히 다룰 테니 말이다. 웹 위젯이란 무엇인가? 웹 위젯은 웹 페이지로 전송되는 데이터를 화면에 표시하는 데에 사용되는 “웹 페이지의 한 조각(a chunk of web page)”이며 컴포넌트이다. 일반적으로 웹 위젯은 HTML, CSS, Javascript 이 섞여 있는 다소 복잡한 모양인 경우가 많다. 웹 위젯 제작자들은 이러한 복잡성을 감추고, 그리하여 웹 위젯 사용자들이 가능한 한 손쉽게 위젯을 웹 페이지 내에 추가할 수 있기를 원한다.
  • 2. 위젯 사용하기 (Widget Usage) 이 글에서 다룰 웹 위젯들은 단 두 개의 HTML 태그를 이용하여 웹 페이지에 추가될 수 있도록 작성될 것이다. 그것들은 아래에서 보듯 하나의 스크립트(script) 태그와, 페이지 내에서 위젯을 추가할 곳을 명시할 컨테이너 태그(일반적으로는 div가 될 것이다)들이다. <script src="http://example.com/widget/script.js" type="text/javascript"></script> <div id="example-widget-container"></div> 정말 이 두 태그 만으로 웹 페이지에 웹 위젯을 추가할 수 있게 된다. 스크립트 태그에 입력되어 있는 코드는 위젯을 구성하는 여러 가지 자산(asset)들을 다운로드하고 컨테이너 안의 내용을 갱 신하게 될 것이다. 더 단순해 질 수는 없나? 기술적으로만 본다면, 위젯 코드 내에서 document.write() 코드를 이용하여 위젯이 담길 컨테이너를 명시하지 않고 사용할 수도 있다. 몇몇 위젯들이 이런 방식을 이용하고 있고, 그래서 스크립트 태그만으로 위젯을 사용할 수 있도록 코드의 양을 줄여주기는 한다. 하지만, 그다지 권장할 만한 방법이 아니라고 생각한다. 그 이유는, - document.write()는 페이지가 완전히 로딩된 다음에는 호출될 수가 없다. 이 이야기는 브라우저가 스크립트 태그를 인식하면 곧바로 위젯 코드가 실행되어야 한다는 것을 의미한다. 보통 위젯 코드는 서버로부터 데이터를 가져오는 데에 사용되는데, 이럴 경우 페이지 로딩이 위젯 코드의 데이터 수신 작업으로 인해 잠시 중단되게 되고, 이로 인해 사용자가 페이지 로딩 시간이 오래 걸린다고 생각할 수도 있다. - 페이지 내에서 위젯을 담을 컨테이너 태그를 따로 사용하게 되면, 스크립트 태그를 필요한 아무 곳에나 입력하여도 된다. 즉, 일반적인 스크립트 사용 권고안처럼 스크립트 태그를 페이지의 맨 아래에 둘 수도 있다. 코드 고립시키기 (Code Isolation) 어떤 페이지에서 어떤 자바스크립트가 사용될지 예측할 수 없기 때문에, 다른 자바스크립트 코드 와 동시에 사용되어도 충돌(clash)이 발생하지 않도록 해야만 한다. 이를 위해, 모든 위젯 코드를 익명 펑션(anonymous function) 내에 입력한 뒤에 이 익명 펑션을 호출하는 방식을 이용할 것이 다. 익명 펑션 내에 작성된 코드에서 사용되는 모든 변수들은 외부의 변수와는 아무런 관계 없이 사용할 수 있게 된다. 다음은 이 기법을 이용한 예제이다.
  • 3. var foo = "Hello World!"; document.write("<p>Before our anonymous function foo means '" + foo + '".</p>'); (function() { // The following code will be enclosed within an anonymous function var foo = "Goodbye World!"; document.write("<p>Inside our anonymous function foo means '" + foo + '".</p>'); })(); // We call our anonymous function immediately document.write("<p>After our anonymous function foo means '" + foo + '".</p>'); 결과는 다음과 같다. Web Widget Tutorial Example 1 - Code isolation Before our anonymous function foo means 'Hello World!". Inside our anonymous function foo means 'Goodbye World!". After our anonymous function foo means 'Hello World!". 위에서 볼 수 있듯이, 익명 펑션 내에서 사용된 foo 변수는 전역(global) foo 변수와 충돌 없이 사용할 수 있다. 변수를 선언할 때는 항상 var 를 사용하라 외부 javascript 에 의해 위젯 내의 변수가 간섭을 받는 것을 막기 위해, 변수를 선언할 때에는 반드시 var 지시어를 사용해야 한다. 그렇지 않고 변수에 값을 할당하게 되면, 위젯 내의 변수가 아닌 외부의 변수를 변경하게 되는 것이다. 변수를 선언하고 사용할 때에는 언제나 var 지시어를 사용하는 것이 바람직한 습관이다. 자바스크립트 라이브러리 로딩(Loading Javascript Libraries) 위젯을 작성하는 데에 많은 양의 자바스크립트 코드가 필요하게 될 것이라면, 보통 jQuery와 같 은 자바스크립트 라이브러리를 사용하게 된다. 하지만, 위젯을 사용하는 페이지 내에 jQuery가 로딩되어있다는 보장이 없기 때문에, 필요한 경우 jQuery를 동적으로 로딩하도록 해야 한다. (function() { // Localize jQuery variable
  • 4. var jQuery; /******** jQuery 가 로딩되지 않았다면 동적으로 로드한다. *********/ if (window.jQuery === undefined) { var script_tag = document.createElement('script'); script_tag.setAttribute("type","text/javascript"); script_tag.setAttribute("src", "http://code.jquery.com/jquery-latest.min.js "); if (script_tag.readyState) { script_tag.onreadystatechange = function () { // 예전 버전 IE 를 위해 if (this.readyState == 'complete' || this.readyState == 'loaded') { scriptLoadHandler(); } }; } else { // Other browsers script_tag.onload = scriptLoadHandler; } (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag); } else { // The jQuery version on the window is the one we want to use jQuery = window.jQuery; main(); } /******** Called once jQuery has loaded ******/ function scriptLoadHandler() { // Restore $ and window.jQuery to their previous values and store the // new jQuery in our local jQuery variable jQuery = window.jQuery.noConflict(true); // Call our main function main(); } /******** Our main function ********/ function main() { jQuery(document).ready(function($) { // We can use jQuery here }); } })(); // We call our anonymous function immediately 먼저, jQuery를 담고 있을 지역변수 하나를 작성하였다. 그 다음, jQuery가 페이지 내에 추가되어 있는지 확인하는데, 이는 jQuery가 페이지에 이미 추가되어 있는 경우 또다시 로딩하지 않도록 하기 위함이다. jQuery 라이브러리를 로딩하기 위해, jQuery 최신 소스를 다운받을 수 있는 URL 을 이용하는 스크립트 엘리먼트를 작성한다. 그 뒤, 헤드(head) 태그가 있다면 헤드 태그에, 없다 면 문서(document)의 맨 마지막에 이 스크립트 엘리먼트를 추가(appendChild)한다. 작성하는 위젯 코드에서 jQuery 라이브러리를 사용하려면, jQuery 라이브러리가 모두 로딩되어 있는지 확인해야 한다. 여기서는 scriptLoadHandler라는 펑션을 이용하는데, 이 펑션은 스크립트
  • 5. 의 로딩이 완료되면 딱 1회 실행되도록 되어 있다. 대부분의 브라우저(Firefox, Safari, Opera, Chrome 등)들에서는 단순히 script 엘리먼트의 onload 속성에 이 펑션을 할당하면 된다. 인터넷 익스플로러 9의 경우에는 약간 다른 방법을 이용할 때가 있는데, 여기서는 onreadystatechange 핸들러를 등록하여 사용하고 있다. 이 onreadystatechange 핸들러는 ready state가 complete나 loaded 상태가 되면 아래 작성해 둔 main 펑션을 호출할 것이다. 왜 IE 의 onreadystatechange 핸들러에서는 두 가지 상태를 확인하는가? 스크립트가 로딩되면, 인터넷 익스플로러는 readyState 를 complete 나 loaded 로 변경하는데, 이것은 페이지가 어떤 방식으로 로딩되었는지에 영향을 받는다. 페이지가 캐시(Cache)로부터 로딩되는 경우에는 상태가 complete 로 변경되고, 네트워크에서 다운로드 되는 경우에는 loaded 로 변경된다. 그러므로, 만약 loaded 상태에 대해서만 확인한다면, 링크를 통해 다른 페이지로 이동했다가 ‘뒤로가기’ 버튼을 이용하여 원래의 페이지로 돌아오는 경우에 정상적으로 동작하지 않게 된다. 작성된 scriptLoadHandler 펑션 내에서는 jQuery 라이브러리가 다른 자바스크립트 라이브러리나 다른 버전의 jQuery와 동시에 사용될 수 있다는 것을 고려해야 한다. 그러므로 다음 코드 한 줄 은 주의를 기울여 볼 만 하다. jQuery = window.jQuery.noConflict(true); 이 행이 실행되기 전이라면, window.jQuery는 이 위젯 코드 내에서 지정한 jQuery를 가리키게 되 고, 이로 인해 외부에서 선언한 예전 버전의 jQuery를 덮어쓰게 될 수도 있다. Window.jQuery.noConflict(true)를 호출함으로써 window.jQuery를 원래 페이지가 로딩될 때 지 정된 jQuery 객체로 변경하게 되고, 대신 새로 로딩한 jQuery를 리턴하여 우리의 익명 펑션 내의 jQuery 변수에 할당하게 된다. 동시에 $ 변수도 예전 것으로 되돌리는데, 이 덕분에 Prototype과 같은 또 다른 라이브러리를 아무 문제 없이 사용할 수 있게 된다. 마지막으로, scriptLoadHandler가 현재 버전의 jQuery를 이용하는 main 펑션을 호출하여 위젯 객 체를 생성하게 된다. 이제 기초공사를 마쳤으니, 가장 재미있는 부분을 다루기로 하자. 데이터 로딩하기(Loading data from our site) 지금 작성하고 있는 위젯은 단순한 HTML을 웹 페이지에 출력하는 것이 목적이기 때문에, XML 이나 JSON과 같은 중계 데이터 형식이 필요하지 않다. 그저 서버로부터 HTML 데이터를 수신한 뒤, 이 HTML을 이용하여 위젯의 내용을 갱신하도록 할 것이다. 지금 작성하고 있는 위젯이 뉴스의 헤드라인 목록을 보여주는 것이라고 하자. 서버는 다음과 같
  • 6. 은 HTML 코드를 출력할 것이다. <ul> <li><a href="http://example.com/a-first news">A first news</a></li> <li><a href="http://example.com/another-news">Another news</a></li> <li><a href="http://example.com/an-interesting-news">An interesting news</a></li> </ul> 만약 위젯을 포함하는 페이지(호스트 페이지)가 동일한 서버에서 서비스된다면, 일반적인 AJAX 호출(굳이 따지자면 이 경우에는 AHAH - Asynchronous HTML And HTTP)만으로 HTML 데이 터를 얻어 DOM을 갱신한 수 있다. 하지만 일반적으로 웹 위젯은 웹 위젯이 사용되는 페이지와 데이터를 얻어오는 페이지가 다를 수 있고, 이 경우 브라우저가 다른 도메인으로의 AJAX 요청을 허용하지 않게 된다. 이러한 보안 규제를 Same Origin Policy라고 한다. 다행히, 이 규제를 우회할 방법이 존재한다. 브라우저는 스크립트 태그가 다른 도메인의 스크립트 를 포함(include)하는 것을 허용한다. 결국 이 방법을 이용한 JSONP라는 간단한 기법으로 데이 터를 얻어올 수 있다. JSONP의 기본 개념은, 펑션 속에 감싸져(wrapped) 있는 JSON Data를 가 져오는 script 태그를 생성하여 사용하는 것이다. 만약 다음과 같은 script 태그를 작성하였다면 : <script type="text/javascript" src="http://example.com/widget/data.js"></script> http://example.com/widget/data.js 로부터 전송되는 내용은 아래와 같이 보일 것이다. our_callback( {"foo": 42, "bar": 23 } ) 그러면 아래와 같이 our_callback 펑션을 선언하고 JSON 데이터를 파라미터로 받도록 한다. function our_callback(json_data) { // do stuff with json_data } 다행인 점은, jQuery가 JSONP를 내부적으로 지원한다는 것이다. jQuery는 우리를 위해 스크립트 태그와 콜백펑션(Callback Function)을 생성하고, 이 콜백펑션의 이름을 파라미터로 전송하는 일 도 해 준다. 여기서 우리가 관심을 갖는 유일한 데이터는 위젯 컨테이너에 추가할 HTML 조각이 고, 서버 측에서는 다음과 같이 콜백펑션과 JSON 데이터를 이용하여 데이터를 전송하게 될 것이 다. callback_name_generated_by_jquery( {"html": "<ul><li>(...)</li></ul>" } )
  • 7. Ruby on Rails를 이용하여 서버에서 JSONP를 작성하는 예제는 다음과 같다. # Store HTML in a variable rather than returning in to the browser html = render_to_string # Build a JSON object containing our HTML json = {"html" => html}.to_json # Get the name of the JSONP callback created by jQuery callback = params[:callback] # Wrap the JSON object with a call to the JSONP callback jsonp = callback + "(" + json + ")" # Send result to the browser render :text => jsonp, :content_type => "text/javascript" 클라이언트 코드는 일반적인 AJAX 호출을 통해 JSON 데이터를 받는 것과 유사하다. var widget_url = "http://example.com/wiget_data.js?callback=?" $.getJSON(widget_url, function(data) { $('#example-widget-container').html(data.html); }); AJAX 요청을 전송할 때, 반드시 callback=? 과 같은 파라미터 문자열을 URL 에 포함하여야 한다. 그렇지 않으면 서버가 콜백펑션의 이름을 받을 수 없게 된다. 이제 서버에서 HTML을 수신할 수 있게 되었고, 그것을 웹 페이지의 위젯 컨테이너에 삽입할 수 있게 되었다. 이제 남은 마지막 작업은 지금까지 만든 위젯에 스타일을 추가하는 것이 될 것이다. CSS 로드(Loading CSS) 스타일시트를 로딩하기 위해 다음과 같이 javascript를 이용하여 link 태그를 생성하면 된다. var css_link = $("<link>", { rel: "stylesheet", type: "text/css", href: "style.css" }); css_link.appendTo('head');
  • 8. 통합하기(Putting things together) 지금까지 웹 위젯을 작성하기 위해 조금씩 다른 내용들을 보아 왔다. 이제 이것들을 하나로 합쳐 서 간단한 예제를 만들 차례이다. 이 모든 내용을 통합한 Javascript 코드는 다음과 같다. (function() { // Localize jQuery variable var jQuery; /******** Load jQuery if not present *********/ if (window.jQuery === undefined || window.jQuery.fn.jquery !== '1.4.2') { var script_tag = document.createElement('script'); script_tag.setAttribute("type","text/javascript"); script_tag.setAttribute("src", "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"); if (script_tag.readyState) { script_tag.onreadystatechange = function () { // For old versions of IE if (this.readyState == 'complete' || this.readyState == 'loaded') { scriptLoadHandler(); } }; } else { script_tag.onload = scriptLoadHandler; } // Try to find the head, otherwise default to the documentElement (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag); } else { // The jQuery version on the window is the one we want to use jQuery = window.jQuery; main(); } /******** Called once jQuery has loaded ******/ function scriptLoadHandler() { // Restore $ and window.jQuery to their previous values and store the // new jQuery in our local jQuery variable jQuery = window.jQuery.noConflict(true); // Call our main function main(); } /******** Our main function ********/ function main() { jQuery(document).ready(function($) { /******* Load CSS *******/ var css_link = $("<link>", { rel: "stylesheet", type: "text/css",
  • 9. href: "style.css" }); css_link.appendTo('head'); /******* Load HTML *******/ var jsonp_url = "http://al.smeuh.org/cgi-bin/webwidget_tutorial.py?callback=?"; $.getJSON(jsonp_url, function(data) { $('#example-widget-container').html("This data comes from another server: " + data.html); }); }); } })(); // We call our anonymous function immediately 그리고 이 위젯과 통신하기 위한 서버 코드는 다음과 같다.(Python을 이용하여 작성된 CGI 스크 립트이다.) #!/usr/bin/python import cgi params = cgi.FieldStorage() print "Content-Type: text/javascriptn" if not 'callback' in params: print "ERROR: you must pass a callback parameter" else: jsonp = "%s ( {'html': '<strong>Hello World!</strong>' } )" print jsonp % params['callback'].value 물론, 실제 사용될 웹 위젯은, 클라이언트 측과 서버 측 모두에서, 훨씬 더 복잡할 것이다. 하지 만, 웹 위젯에 대한 기본적인 내용들에 대해서 이해했으리라 믿는다. 호스트 페이지 내의 에러 처리(Dealing with errors on host pages) 지금까지 작성한 코드는 보통 정상적으로 동작할 것이지만, Shyam Subramanyan이 보고한 바 대 로, main 펑션 내에서 jQuery(document).ready를 이용하는 데에 문제가 발생할 수도 있다. 이것 은 호스트 페이지에서 jQuery를 로딩한 상태에서 jQuery(document).ready 가로채(hook) 호출한 경우 발생한다. 이렇게 호스트 페이지 내의 jQuery 가로채기(jQuery hook)로 인해 오류가 발생한 경우에는 위젯 내의 main 펑션은 실행되지 않게 된다.
  • 10. Shyam이 제시한 해결책은, 호스트 페이지가 위젯을 호출할 준비가 되어 있는지를 확인하는 코드 를 추가하는 것이다. 전체 document가 준비가 되기를 기다리는 대신에, 관련된 DOM 엘리먼트나 위젯이 사용하는 자바스크립트 오브젝트들이 준비되었는지 확인하는 것인데, 이 코드는 다음과 같다. (Listly라는 사이트에서 이용되고 있는 코드이다.) Listly.listlyReady = function () { // Check for presence of required DOM elements or other JS your widget depends on if (Listly.jQuery.listlybox && ListlyAuth) { window.clearInterval(Listly._readyInterval); // Make stuff here } }; // This our new main function function main () { Listly._readyInterval = window.setInterval(Listly.listlyReady, 500); } Shyam에 의하면, 이 코드는 Listly 사이트에서 정상적으로 동작했고, 호스트 페이지가 무겁거나 오류가 있는 경우에 위젯이 훨씬 더 빨리 로딩된다고 하였다. 결론(Conclusion) 매우 많은 사람들이 플러그인들(plugins)을 로딩하는 방법에 대해 물어보았지만, 아쉽게도 나는 그에 대한 완벽한 대답을 알고 있지는 못하다. 개인적으로는 익명 펑션 내부에 스크립트 코드와 함께 작은 플러그인 소스코드를 추가함으로써 이 문제를 해결하긴 했지만, 이런 방법은 cross- domain에 대한 고려를 하지 않은 플러그인(plugin)으로 인해 호스트 페이지의 처리가 방해 (interfere) 받을 수 있음도 고려해야 한다. 자주 질문되는 또 다른 내용은, 위젯을 설정 가능하게(configurable) 만드는 방법에 대한 것이다. 코드 내 주석에서 제안한 것과 같이, 마크업(markup)을 최소화하기 위해, 위젯을 호출하는 URL 에 쿼리 스트링(query string)을 추가하고, 스크립트 태그에 ID를 추가하면 된다. 이렇게 함으로 써, 스크립트 내에서 자기 자신의 스크립트 태그에 접근할 수 있고, 스크립트 태그의 URL에 포함 되어 있는 쿼리 스트링의 정보를 분석하여 자동으로 설정할 수 있게 된다. 혹은 좀 더 직접적인 방법으로, 작성된 위젯의 임베드(embed) 코드에 보이지 않는 마크업(invisible markup)을 추가할 수도 있다. SNS에서 제공하는 공유 버튼들이 이 방식을 이용한다. (후략)