나에게 자바스크립트를 전수해주신 분은 내가 특례로 근무했던 회사에서 "자바스크립트의 신"이라 불리시는 분이었다. 프로그래머의 세계에서도 내공이 출중하신 초고수들은 언제나 강호를 떠나 재야에 묻혀 조용히 지내신다. 해가 떠있을 때 돌아다니는 것을 꺼려했던 그 분과 회사에서 대면하는 일은 결코 쉬운 일이 아니었다. 이유인 즉, 낮에는 회사 구석의 간이 침대에서 주무시다가 저녁 7시쯤 되어야 눈을 뜨고 활동하셨기 때문이다. 점심먹으러 갈 때면 깨워서 같이 가야하나 아니면 그냥 우리만 가야하나 고민과 번뇌를 던져줬던 그 분. 지금은 한 아이의 아버지가 되었고 여러 업종에 두루 종사하시다 최근 한 곳에 정착하셨다.
그 분은 웹페이지도 훌륭하고 어렵게 작성했지만 jscript로 MFC와 똑같은 기능을 구현하는데 심혈을 기울였고 API도 거의 똑같이 구현하셨었다. jscript로 윈도우 탐색기를 비롯한 많은 어플리케이션을 개발하셨는데 당시 나로써는 정말 놀랍기만 했다.물론 느리다.-_-;;; DOM객체, 데이터바인딩 등등 jscript 많은 개념을 그 분 코드를 통해 나는 배울 수 있었다.
오늘 소개할 jscript의 비동기호출도 예전에 사용했던 코드의 개념을 그대로 담았지만 원본소스는 MicroSoft의 코드중 일부를 가져온 것임을 밝혀둡니다. 지금 내 블로그 우측프레임(woorie.net/right.php)에 이 코드가 적용되어있으니 소스보기를 해서 보시던지 아래의 "소스보기"를 눌러 소스를 보세요~!
이럴 때 아주 유용한 jscript 팁입니다.
이벤트1이 일어나고 1의 데이터로딩과 1의 데이터 출력이 일어나는 중간에 이벤트2의 호출이 일어나는 경우. 즉 1의 데이터 출력이 미처 완료되지 않은 순간에 다른 이벤트가 일어나는 경우를 처리하는데 아주 유용한 방법입니다.
방법은 다음과 같습니다.
먼저 전역의 이벤트 큐를 하나 만듭니다.
위 소스에서는 m_aoCalls이 되겠습니다.
이벤트1이 발생하면 단어리스트를 요청하고 객체를 m_aoCalls에 넣습니다. 이 때 m_aoCalls의 인덱스값은 0 이됩니다. 그리고 로딩완료 이벤트를 등록해줍니다. 로딩 완료 이벤트 인자값으로 큐의 인덱스를 넘깁니다. 나중에 로딩 이벤트가 끝나고 어떤게 자기를 호출한 것인지 찾기위해서 입니다.
위 소스에서는 다음이 되겠습니다.
objXMLDoc.onreadystatechange = Function( "fnLoadComplete( " + iCall + " );" );
이벤트2가 발생하면 이것도 큐에 넣습니다. 이것도 마찬가지로 큐에 넣어줌과 동시에 로딩 완료 이벤트를 등록하고 큐인덱스를 인자값으로 넘겨줍니다.
이렇게 큐에 넣을때마다 로딩완료이벤트를 등록해두면 완료시점에 큐를 검색해서 하나씩 꺼내다가 알아서 씁니다. 이건 누가 하는게 아니라 브라우져가 알아서 해주는 것입니다.
아..소스 설명하려고 했는데 쓸데없이 말만 길어졌군요.
소스 설명은 2편을 만들어서 해야겠습니다.
이번엔 개념만 이해하시고 천천히 소스 보세요 -_-;;
그 분은 웹페이지도 훌륭하고 어렵게 작성했지만 jscript로 MFC와 똑같은 기능을 구현하는데 심혈을 기울였고 API도 거의 똑같이 구현하셨었다. jscript로 윈도우 탐색기를 비롯한 많은 어플리케이션을 개발하셨는데 당시 나로써는 정말 놀랍기만 했다.물론 느리다.-_-;;; DOM객체, 데이터바인딩 등등 jscript 많은 개념을 그 분 코드를 통해 나는 배울 수 있었다.
오늘 소개할 jscript의 비동기호출도 예전에 사용했던 코드의 개념을 그대로 담았지만 원본소스는 MicroSoft의 코드중 일부를 가져온 것임을 밝혀둡니다. 지금 내 블로그 우측프레임(woorie.net/right.php)에 이 코드가 적용되어있으니 소스보기를 해서 보시던지 아래의 "소스보기"를 눌러 소스를 보세요~!
<script language="jscript">
var m_iMaxCalls = 64;
var m_aoCalls = new Array();
var m_iLastCall = 0;
var m_iMasterIdx = null;
var oContainer = new Array ;
function fnAllocCall()
{
var iReturn = null;
for (var i = 0; i < m_iMaxCalls && m_aoCalls[m_iLastCall] != null; i++)
{
m_iLastCall = (m_iLastCall + 1) % m_iMaxCalls;
}
if (i < m_iMaxCalls) iReturn = m_iLastCall;
return iReturn;
}
function fnDeAllocCall( iCallIdx )
{
m_aoCalls[iCallIdx] = null;
}
function CallObject( oXml , oEl, oSync, posx, posy )
{
this.oXml = oXml;
this.oEl = oEl ;
this.oSync = oSync;
this.posx = posx ;
this.posy = posy ;
}
function get_xml( strXMLSrc , oEl, oSync, posx, posy )
{
var aryXMLNodes;
var i = 0;
var strRetVal = "";
var iCall = fnAllocCall();
var objXMLDoc = new ActiveXObject("Microsoft.XMLDOM");
objXMLDoc.async = true;
m_aoCalls[iCall] = new CallObject(objXMLDoc , oEl, oSync, posx, posy);
objXMLDoc.onreadystatechange = Function( "fnLoadComplete( " + iCall + " );" );
objXMLDoc.load( strXMLSrc );
return iCall;
}
function fnLoadComplete( iIndex )
{
if( iIndex == null ) return;
var oCall = m_aoCalls[ iIndex ];
try
{
var iReadyState = oCall.oXml.readyState;
}
catch(e)
{
return;
}
if( iReadyState != 4 ) return;
if( oCall != null && oCall.oXml.xml != "" )
{
sHtml = "";
//type = oCall.oXml.documentElement.getAttribute("personalty") ;
aryXMLNodes = oCall.oXml.documentElement.selectNodes("//word[@name]");
for (i = 0; i < aryXMLNodes.length; i++)
{
sHtml += aryXMLNodes[i].getAttribute("name")+"("+aryXMLNodes[i].getAttribute("hit")+")<br/>";
}
fnDeAllocCall( iIndex );
}
else
{
//페이지 로딩이 안되었을 경우, 다시 호출하거나 그냥 넘어가거나
}
oCall.oEl.enc = oContainer.length ;
oContainer[oCall.oEl.enc] = sHtml ;
mouse_txt.innerHTML = sHtml;
info.style.left = oCall.posx + 10 ;
info.style.top = oCall.posy + 5;
info.style.display="";
return;
}
function display(src)
{
parent.display(src);
}
function onload_word(name)
{
if( event.srcElement.loaded =='F' )
{
m_iMasterIdx = get_xml("list_top.php?nickname="+name, event.srcElement, 1, event.clientX, event.clientY);
event.srcElement.loaded = 'T';
}
else
{
idx = event.srcElement.enc ;
mouse_txt.innerHTML = oContainer[idx];
info.style.left = event.clientX + 10 ;
info.style.top = event.clientY + 5;
info.style.display="";
}
}
function all_clear()
{
info.style.display="none";
}
</script>
var m_iMaxCalls = 64;
var m_aoCalls = new Array();
var m_iLastCall = 0;
var m_iMasterIdx = null;
var oContainer = new Array ;
function fnAllocCall()
{
var iReturn = null;
for (var i = 0; i < m_iMaxCalls && m_aoCalls[m_iLastCall] != null; i++)
{
m_iLastCall = (m_iLastCall + 1) % m_iMaxCalls;
}
if (i < m_iMaxCalls) iReturn = m_iLastCall;
return iReturn;
}
function fnDeAllocCall( iCallIdx )
{
m_aoCalls[iCallIdx] = null;
}
function CallObject( oXml , oEl, oSync, posx, posy )
{
this.oXml = oXml;
this.oEl = oEl ;
this.oSync = oSync;
this.posx = posx ;
this.posy = posy ;
}
function get_xml( strXMLSrc , oEl, oSync, posx, posy )
{
var aryXMLNodes;
var i = 0;
var strRetVal = "";
var iCall = fnAllocCall();
var objXMLDoc = new ActiveXObject("Microsoft.XMLDOM");
objXMLDoc.async = true;
m_aoCalls[iCall] = new CallObject(objXMLDoc , oEl, oSync, posx, posy);
objXMLDoc.onreadystatechange = Function( "fnLoadComplete( " + iCall + " );" );
objXMLDoc.load( strXMLSrc );
return iCall;
}
function fnLoadComplete( iIndex )
{
if( iIndex == null ) return;
var oCall = m_aoCalls[ iIndex ];
try
{
var iReadyState = oCall.oXml.readyState;
}
catch(e)
{
return;
}
if( iReadyState != 4 ) return;
if( oCall != null && oCall.oXml.xml != "" )
{
sHtml = "";
//type = oCall.oXml.documentElement.getAttribute("personalty") ;
aryXMLNodes = oCall.oXml.documentElement.selectNodes("//word[@name]");
for (i = 0; i < aryXMLNodes.length; i++)
{
sHtml += aryXMLNodes[i].getAttribute("name")+"("+aryXMLNodes[i].getAttribute("hit")+")<br/>";
}
fnDeAllocCall( iIndex );
}
else
{
//페이지 로딩이 안되었을 경우, 다시 호출하거나 그냥 넘어가거나
}
oCall.oEl.enc = oContainer.length ;
oContainer[oCall.oEl.enc] = sHtml ;
mouse_txt.innerHTML = sHtml;
info.style.left = oCall.posx + 10 ;
info.style.top = oCall.posy + 5;
info.style.display="";
return;
}
function display(src)
{
parent.display(src);
}
function onload_word(name)
{
if( event.srcElement.loaded =='F' )
{
m_iMasterIdx = get_xml("list_top.php?nickname="+name, event.srcElement, 1, event.clientX, event.clientY);
event.srcElement.loaded = 'T';
}
else
{
idx = event.srcElement.enc ;
mouse_txt.innerHTML = oContainer[idx];
info.style.left = event.clientX + 10 ;
info.style.top = event.clientY + 5;
info.style.display="";
}
}
function all_clear()
{
info.style.display="none";
}
</script>
<?xml version="1.0" encoding="euc-kr" ?>
<Lists Id="root" personalty="C">
<word name="사람" hit="13" />
<word name="사진" hit="11" />
<word name="문제" hit="9" />
<word name="말" hit="8" />
<word name="돈" hit="8" />
<word name="밤" hit="7" />
<word name="일" hit="6" />
<word name="아자" hit="6" />
<word name="글" hit="6" />
<word name="마음" hit="5" />
<word name="단어" hit="5" />
<word name="다들" hit="5" />
<word name="넘넘" hit="5" />
<word name="교수" hit="5" />
<word name="결과" hit="5" />
<word name="현대" hit="4" />
<word name="하루" hit="4" />
<word name="친구" hit="4" />
<word name="정보" hit="4" />
<word name="열쇠" hit="4" />
</Lists>
우선 위 소스가 제공하는 기능을 간략히 소개하겠습니다.
오른쪽을 보시면 Reply Word Analysis 라는 타이틀 아래 여러 닉네임들이 보입니다."백구씨쥔장", "howling" 등등.. 각각의 닉네임을 출력하는 html 태그는 <span>태그이며 마우스 오버 이벤트와 마우스 아웃 이벤트 취하고 있습니다.
<span loaded='F' onmouseover=onload_word('백구씨쥔장')onmouseout=all_clear() style='color:#666666;cursor:hand;'>백구씨쥔장</span>
마우스 오버 이벤트가 일어나면 span 태그는 해당 닉네임이 갖는 단어리스트를 db서버에 요청하게 되지요. 예를 들어 만약 마우스가 "백구씨쥔장"이란 닉네임 위로 올라면 마우스오버 이벤트가 일어나고 그 때, "백구씨쥔장"이 갖고 있는 단어들을 db에서 읽어와서 보여주죠~ 그리고 마우스가 "howling"이란 닉네임으로 이동된다면 다시 마우스오버 이벤트가 발생하고 이 때, "howling"이 갖는 단어들을 db에서 읽어오게 됩니다.
그런데 만약 마우스를 빠르게 움직여 "백구씨쥔장" 닉네임위를 거쳐 "howling"닉네임으로 이동시키면 어떻게 될까요?
우선 "백구씨쥔장"을 출력하는 span태그가 마우스오버 이벤트를 잡아서 db서버에 단어를 요청하겠지요..그리고 순간 "howling"을 출력하는 span태그도 마우스오버 이벤트를 잡아서 howling 닉네임이 갖는 단어리스트를 db서버에 요청할 것입니다.
이런 이벤트가 동기식으로 움직인다면 "백구씨쥔장" 단어리스트는 db에서 읽어져 보여지기도 전에 다시 "howling" 단어리스트를 요청을 처리하도록 강요받게 되고, 결국 백구씨쥔장의 단어리스트는 중간에 무시되어 없어지게 됩니다. 만약 iframe으로 db 리스트를 받아 보여주도록 코드를 작성했다면 어떤 함수가 백구씨쥔장의 단어리스트가 왜 안오나 기다리다가 스크립트 에러를 낼지도 모릅니다.
비동기 호출은 위와 같은 상황 즉,
<Lists Id="root" personalty="C">
<word name="사람" hit="13" />
<word name="사진" hit="11" />
<word name="문제" hit="9" />
<word name="말" hit="8" />
<word name="돈" hit="8" />
<word name="밤" hit="7" />
<word name="일" hit="6" />
<word name="아자" hit="6" />
<word name="글" hit="6" />
<word name="마음" hit="5" />
<word name="단어" hit="5" />
<word name="다들" hit="5" />
<word name="넘넘" hit="5" />
<word name="교수" hit="5" />
<word name="결과" hit="5" />
<word name="현대" hit="4" />
<word name="하루" hit="4" />
<word name="친구" hit="4" />
<word name="정보" hit="4" />
<word name="열쇠" hit="4" />
</Lists>
우선 위 소스가 제공하는 기능을 간략히 소개하겠습니다.
오른쪽을 보시면 Reply Word Analysis 라는 타이틀 아래 여러 닉네임들이 보입니다."백구씨쥔장", "howling" 등등.. 각각의 닉네임을 출력하는 html 태그는 <span>태그이며 마우스 오버 이벤트와 마우스 아웃 이벤트 취하고 있습니다.
<span loaded='F' onmouseover=onload_word('백구씨쥔장')onmouseout=all_clear() style='color:#666666;cursor:hand;'>백구씨쥔장</span>
마우스 오버 이벤트가 일어나면 span 태그는 해당 닉네임이 갖는 단어리스트를 db서버에 요청하게 되지요. 예를 들어 만약 마우스가 "백구씨쥔장"이란 닉네임 위로 올라면 마우스오버 이벤트가 일어나고 그 때, "백구씨쥔장"이 갖고 있는 단어들을 db에서 읽어와서 보여주죠~ 그리고 마우스가 "howling"이란 닉네임으로 이동된다면 다시 마우스오버 이벤트가 발생하고 이 때, "howling"이 갖는 단어들을 db에서 읽어오게 됩니다.
그런데 만약 마우스를 빠르게 움직여 "백구씨쥔장" 닉네임위를 거쳐 "howling"닉네임으로 이동시키면 어떻게 될까요?
우선 "백구씨쥔장"을 출력하는 span태그가 마우스오버 이벤트를 잡아서 db서버에 단어를 요청하겠지요..그리고 순간 "howling"을 출력하는 span태그도 마우스오버 이벤트를 잡아서 howling 닉네임이 갖는 단어리스트를 db서버에 요청할 것입니다.
이런 이벤트가 동기식으로 움직인다면 "백구씨쥔장" 단어리스트는 db에서 읽어져 보여지기도 전에 다시 "howling" 단어리스트를 요청을 처리하도록 강요받게 되고, 결국 백구씨쥔장의 단어리스트는 중간에 무시되어 없어지게 됩니다. 만약 iframe으로 db 리스트를 받아 보여주도록 코드를 작성했다면 어떤 함수가 백구씨쥔장의 단어리스트가 왜 안오나 기다리다가 스크립트 에러를 낼지도 모릅니다.
비동기 호출은 위와 같은 상황 즉,
이벤트호출1 -> 1의 데이터로딩 -> 1의 데이터출력
이벤트호출2 -> 2의 데이터로딩 -> 2의 데이터출력
이벤트호출3 -> 3의 데이터로딩 -> 3의 데이터출력
이벤트호출n -> n의 데이터로딩 -> n의 데이터출력
이벤트호출2 -> 2의 데이터로딩 -> 2의 데이터출력
이벤트호출3 -> 3의 데이터로딩 -> 3의 데이터출력
이벤트호출n -> n의 데이터로딩 -> n의 데이터출력
이럴 때 아주 유용한 jscript 팁입니다.
이벤트1이 일어나고 1의 데이터로딩과 1의 데이터 출력이 일어나는 중간에 이벤트2의 호출이 일어나는 경우. 즉 1의 데이터 출력이 미처 완료되지 않은 순간에 다른 이벤트가 일어나는 경우를 처리하는데 아주 유용한 방법입니다.
방법은 다음과 같습니다.
먼저 전역의 이벤트 큐를 하나 만듭니다.
위 소스에서는 m_aoCalls이 되겠습니다.
이벤트1이 발생하면 단어리스트를 요청하고 객체를 m_aoCalls에 넣습니다. 이 때 m_aoCalls의 인덱스값은 0 이됩니다. 그리고 로딩완료 이벤트를 등록해줍니다. 로딩 완료 이벤트 인자값으로 큐의 인덱스를 넘깁니다. 나중에 로딩 이벤트가 끝나고 어떤게 자기를 호출한 것인지 찾기위해서 입니다.
위 소스에서는 다음이 되겠습니다.
objXMLDoc.onreadystatechange = Function( "fnLoadComplete( " + iCall + " );" );
이벤트2가 발생하면 이것도 큐에 넣습니다. 이것도 마찬가지로 큐에 넣어줌과 동시에 로딩 완료 이벤트를 등록하고 큐인덱스를 인자값으로 넘겨줍니다.
이렇게 큐에 넣을때마다 로딩완료이벤트를 등록해두면 완료시점에 큐를 검색해서 하나씩 꺼내다가 알아서 씁니다. 이건 누가 하는게 아니라 브라우져가 알아서 해주는 것입니다.
아..소스 설명하려고 했는데 쓸데없이 말만 길어졌군요.
소스 설명은 2편을 만들어서 해야겠습니다.
이번엔 개념만 이해하시고 천천히 소스 보세요 -_-;;

