Ajax 속도 개선 - Ajax sogdo gaeseon

현재 만드는 메일 솔루션을 Full ajax 형 어플리케이션으로 개발 하다보니 생긴 큰 화두 중에 하나는 바로

속도 이다.

리스트를 80개 기준으로 데이터를 가져와 뿌려줄때 의 HTML 랜더링의 속도가 관건 이었다.

처음시도는 jQuery 의 load 함수를 통해 페이지 자체를 동적으로 가져오게 하는것이었다.
이방법은 Ajax 처럼 보이나 실제 내부적으로 보면 일반 HTTP 방식과 별 다른 차이가 없다.
그래서 별다른 이슈가 없을것이라고 예상하고 작업한 결과.....

1. 시련의 시작.

이건 윙미... 80개의 리스트를 뿌려주는 데 5~10 초 정도의 시간이 걸린다.

이때에는 jQuery 를 이용한 이벤트 Attach 나 기타 유틸을 만들어 each 를 사용해 해당 엘리먼트의 렌더링을 하도록 하였다. 그러나 여기에 어마어마한 속도 로딩 차이가 발생 했던것.

실제 HTML 이 내려오는 속도는 빠르나 효과와 드래그앤드랍 DHTML 적용등을 위해 하는 후 처리 등을 모두 jQuery 를 사용하여 처리하였는데, 이것이 발목을 붙잡게 되었다..

그리하여.. 내린 결론은 전면보수~~!!!!

일딴 그때 당시 네이버의 새로운 메일 서비스가 오픈되었고 속도의 비교대상이 네이버 메일이 되었다.
네이버메일은 80개의 리스트 출력에 3초이상이 걸리지 않았다...(털석...ㅜ_ㅜ)

결론은 네이버 API 분석....

분석결과 네이버는 HTML 템플릿을 만들어놓고, 여기에 내용을 채워 넣은뒤 innerHTML 로 해당 리스트를 갱신하는 방법 이었다.. 여기서 또 털썩...

속도의 정점은 innerHTML 이었던 것이다~!!! 조사해본 결과 innerHTML 이 제일빠른 방법이고 dom 생성은 좀더 복잡한 방식으로 별로 추천 되지 않는 방식이었다. 이런 윙미 그럼 ajax 라이브러리들은 뭘하고 있던게냐~!!!!

그리고 하나더 발목을 잡는것이 IE 였다.

유독 IE6 IE7에서 랜더링 속도가 더욱느리게 나타났었다.. 역시 IE는 짱나..

파이어 폭스와 기타 크롬이나 사파리등에서는 속도 문제가 젼혀 발생하지 않았는데 말이다...

고객들은 IE를 아직도 대부분 사용하니 어쩔수 없는 선택.

2. 고난의 날들

그래서 전면적으로 구조를 개선 하였다. 리스트 데이터는 json형태의 리스트로 받고 이를 하나하나 HTML 템플릿을 사용하여 내용을 채워놓고 innerHTML 형태로 삽입.

그리고 기존 jQuery 이벤트를 이용하여 효과 삽입 결론은..

아주쬐끔 빨라졌다 ㅜ_ㅜ innerHTML 의 효과가 jQuery 의 이벤트 적용 덕분에 반감된셈...

그래서 jQuery로 했던 모든 이벤트들을 html 코드안으로 넣고 다시 재작업.. ㅜ_ㅠ;;

다시 조금 개선된 속도를 보여주나 기대 치에는 못미침...

여기서 얻은 결론은 HTML 양 이었다.
과도하게 태그안에 스타일과 이벤트 코드를 삽입 하는 바람에 HTML 양이 증가하고 양이 증가한 HTML을 innerHTML 로 삽입하면 렌더링 속도는 현저히떨어지는 것이었다.

다시 작업.....

3. 고지가 보인다.

이번에는 이벤트를 모조리빼고 HTML 양이 적은 형태로 변경.

와우~!! 2배정도의 속도향상이 일어났다~!!

그러나 80개기준의 3초내외까지는 이르지 못했다....

여기서 한가지 발목을 잡는 것이 이미지 였다.

이미지 태그를 사용하여 아이콘을 표시하는 부분이 있는데 이것이 리스트 로우마다 뿌려지게되어 태그의 양이 증가한다고 생각 했다. 그래서 또 네이버 소스분석...

내린 결론 스타일로 빼자~!!!!

4. 정상에 올라보니....

그리하여 이미지를 백그라운드 스타일로 정의 하고 다시 작업...
근데 왠지 차이가 없어 보인다... 그리고 속도느린 PC에서는 이미지가 로딩되는데 시간이 오래 걸린다...ㅜ_ㅜ
아 이제 방법이 없는가 하고 체념하는 순간~!!

네이버의 구조를 보니 이미지를 통으로 로딩하여 좌표를 주어 백그라운드로 처리한것을 발견~!!

이미지 재작업을 한후 이런 방식으로 아이콘 처리.

다시 테스트.....

와우~!!!! 드뎌 내가 바라는 속도가 나오게 된것이다~!!!!!!!

5. 눈물젖은 ajax....

현재 ajax 현 어플리케이션이 우후죽순으로 쏟아져나오는 상황에서 가장 민감하게 대두된 것이 속도 문제이다.
저사양 PC나 IE 의 한계때문에 ajax 형 어플리 케이션을 만드는데 속도가 가장 발목을 잡는 화두로 나타나고 있다~

내가 제안하는 방법은.

1. 최적화된 HTML 양으로 innerHTML 을 사용하여 컨덴츠를 갱신하라.
2. 이벤트는 되도록 후처리 작업에 넣고 태그에 삽입하는 것은 피하라.
3. 이미지는 무조건 스타일로 빼고 가급적 하나씩의 로딩은 피하라. 작은이미지 맵을 만들어 한번에 로딩하고 이를 백그라운드 좌표를 사용하여 표현하라

이번 경험을하면서 ajax형 어플레케션이 모두 좋은 것은 아니고 이를 잘 표현하고 구현하기위해서는 고려 해야할 것들이 정말 많다는 것을 새삼 느끼게 되었다.

이글을 누가 볼지는 모르겠으나 나와 같은 고민하는 사름들에게 조금이나마 도움이 됬으면 한다.

오늘도 더 빠른 속도를 위해 달려본다~!

�ҽ��� ��ϴ�.
���߿� ������ �ٽɸ� �����Ͽ����ϴ�.
�θ������� �Դϴ�.
�ڽ��������� �˾����� �ּҷ� ���� ����ڸ� üũ�ڽ��� üũ�Ͽ� �߰���ư�� ������ �θ��������� �ش��ڰ� �߰��Ǹ� �̶� ������ ���������� Ajax �� �����ɴϴ�.
����: 1000�� �߰� �ϴµ� 1���� �Ѱ� �ɸ��ϴ�.
�׷��� �ڽ��������� ���������˰� ���� �Ϸ絿�� �װ͸� �����ߴµ�
���ú��� Ajax �� ����� ��Ź���� ���������ϴ�.
�� ��������� �ٲٸ�  �θ��������� ����Ÿ�� �������� ���ϳ׿�.
������� �ð��� ������������ ���� �ɸ��ϴ�. ( 1000 �� �������µ� ���� 1��...)
�׷��� Ajax �������� ������ �������� �Ķ���Ͱ��� �ְ� �޾ƿ��� ������ 1�� �̸��ε�
��𿡼� �ð��� �� ��� �Դ°ɱ��?

�� ������ �ٽ��� ����κп��� �ð��� ���� �ɸ��°�? �Դϴ�.
�亯 �ֽźе� �̸� ���� �մϴ�.

<script>

function window.onload(){
paintTargetList();
}

/************************************************************************
// ����� �߰� ����
***********************************************************************/
var g_arrTargetData = new Array();

function appendSaveTargetList(s_code, user_id, target_no, g_s_code, g_user_id, group_code){

var newValue  = s_code + '<?=SEP_COL?>' + user_id + '<?=SEP_COL?>' + target_no;
var newCompareValue = newValue;

newValue += '<?=SEP_COL?>';
newValue += g_s_code + '<?=SEP_COL?>' + g_user_id + '<?=SEP_COL?>' + group_code;

var frm = document.forms['form'];
var oldValue = frm.Target_List.value;
var arrRows = oldValue.split('<?=SEP_ROW?>');
var isExists = false;
for(var i = 0; i < arrRows.length; i++){
isExists = false;
var arrOldValue = arrRows[i].split('<?=SEP_COL?>');
var value = arrOldValue[0] + '<?=SEP_COL?>' + arrOldValue[1] + '<?=SEP_COL?>' + arrOldValue[2];
// �񱳴� ���� 3��(���������)�θ� �Ѵ�.
if(value == newCompareValue){
isExists = true;
break;
}
}
if(!isExists){
if(oldValue != '') oldValue += '|';
oldValue += newValue;
}
frm.Target_List.value = oldValue;
}

function addTarget(newTargetList){

if(newTargetList == '') return;

var frm = document.forms['form'];
var targetListObj = frm.Target_List;
var oldTargetList = targetListObj.value;

// ajax�� ������ ��������
// array => arrNewTargetList[]
// 0. $G_S_Code
// 1. $G_User_ID
// 2. $Group_Code
// 3. $S_Code
// 4. $User_ID
// 5. $Target_No
// 6. $GroupOrTarget = 0 �׷�, 1 �����

var arrNewTargetList = newTargetList.split('<?=SEP_ROW?>');
//alert(arrNewTargetList); //������¿�: 1^admin^76^1^admin^1242,1^chdan^0^1^admin^56 <--�޸������� ��� �ݺ� �߰��� ��Ʈ��

for(var i = 0; i < arrNewTargetList.length; i++){
var value = arrNewTargetList[i].split('<?=SEP_COL?>');
var g_s_code      = value[0];
var g_user_id    = value[1];
var group_code    = value[2];
var s_code        = value[3];
var user_id      = value[4];
var target_no    = value[5];
var groupOrTarget = value[6];

if(value.length > 7){
var tmp = '' + SEP_COL;
tmp += '' + SEP_COL;
tmp += '' + SEP_COL;
tmp += s_code + SEP_COL;
tmp += user_id + SEP_COL;
tmp += target_no;

var tmpVal = '';
tmpVal += g_s_code + SEP_COL;
tmpVal += g_user_id + SEP_COL;
tmpVal += group_code + SEP_COL;
tmpVal += value[13] + SEP_COL;
tmpVal += '' + SEP_COL;
tmpVal += s_code + SEP_COL;
tmpVal += user_id + SEP_COL;
tmpVal += '' + SEP_COL;
tmpVal += target_no + SEP_COL;
tmpVal += value[14] + SEP_COL;
tmpVal += value[7] + SEP_COL;
tmpVal += value[8] + SEP_COL;
tmpVal += value[10] + SEP_COL;
tmpVal += value[11] + SEP_COL;
tmpVal += value[12] + SEP_COL; // TEL_NO 3
tmpVal += '' + SEP_COL; // TEL_TYPE1
tmpVal += '' + SEP_COL; // TEL_TYPE2
tmpVal += '' + SEP_COL; // TEL_TYPE3
tmpVal += '' + SEP_COL; // SMS_NO
tmpVal += '' + SEP_COL; // ACP_NO
tmpVal += '' + SEP_COL; // email
tmpVal += '' + SEP_COL; // remark
tmpVal += '' + SEP_COL; // info 1
tmpVal += '' + SEP_COL; // info 2
tmpVal += '' + SEP_COL; // info 3
tmpVal += '' + SEP_COL; // info 4
tmpVal += '' + SEP_COL; // info 5
tmpVal += '' + SEP_COL; // info 6
tmpVal += '' + SEP_COL; // info 7
tmpVal += '' + SEP_COL; // info 8
tmpVal += '' + SEP_COL; // info 9
tmpVal += '' + SEP_COL; // info 10
tmpVal += '' + SEP_COL; // sms_locate
tmpVal += '' + SEP_COL; // acp_locate
tmpVal += '' + SEP_COL; // update_scode
tmpVal += '' + SEP_COL; // update_userid
tmpVal += '' + SEP_COL; // update_date
tmpVal += value[9] + SEP_COL; // level_name
tmpVal += '0' + SEP_COL; // g_order
tmpVal += '' + SEP_COL; // division
g_arrTargetData[tmp] = tmpVal;
}

// �׷쿡�� ����� ��� ��������
if(groupOrTarget == 0){
var targetList = getTargetData(g_s_code, g_user_id, group_code, '', '', '');
if(targetList == '') continue;
var arrTargetList = targetList.split('<?=SEP_ROW?>');
for(var k = 0; k < arrTargetList.length; k++){
var arrValue = arrTargetList[k].split('<?=SEP_COL?>');;
var val_s_code    = arrValue[5];
var val_user_id  = arrValue[6];
var val_target_no = arrValue[8];
appendSaveTargetList(val_s_code, val_user_id, val_target_no, g_s_code, g_user_id, group_code);
}
}else{
appendSaveTargetList(s_code, user_id, target_no, g_s_code, g_user_id, group_code);
}
}

paintTargetList();

}

function getTargetData(g_s_code, g_user_id, group_code, s_code, user_id, target_no){

var tmp = g_s_code + '<?=SEP_COL?>';
tmp += g_user_id + '<?=SEP_COL?>';
tmp += group_code + '<?=SEP_COL?>';
tmp += s_code + '<?=SEP_COL?>';
tmp += user_id + '<?=SEP_COL?>';
tmp += target_no;
//alert(tmp);

if(g_arrTargetData[tmp] == null || g_arrTargetData[tmp] == 'undefined') g_arrTargetData[tmp] = '';
if(g_arrTargetData[tmp] != ""){ return g_arrTargetData[tmp]; }

var url  = '../loadAjax/load_target.php';
var qStr  = 'G_S_Code=' + g_s_code + '&G_User_ID=' + g_user_id + '&Group_Code=' + group_code;
qStr += '&S_Code=' + s_code + '&User_ID=' + user_id + '&Target_No=' + target_no;

url += ('?' + qStr);

alert(url); //����: ../loadAjax/load_target.php?G_S_Code=&G_User_ID=&Group_Code=&S_Code=1&User_ID=admin&Target_No=1242
    //����: ../loadAjax/load_target.php?G_S_Code=&G_User_ID=&Group_Code=&S_Code=1&User_ID=admin&Target_No=56
// ������ �����غ��� ��� 1000�� ��½� 0.7 �� �̸� ����

var xmlHttp = createRequest();
var result = '';

xmlHttp.onreadystatechange = function(){
if(xmlHttp.readyState == 4){
result = xmlHttp.responseText;
}
};

  xmlHttp.open("GET", url, false) ; // false : ����� ���

xmlHttp.send(null);

if(result != ''){
g_arrTargetData[tmp] = result;
}

return result;

}

</script>

<!--����� ����Ʈ ��ºκ� -->
<tbody id="tbodyTargetList">
</tbody>

����� �߰� �˾���ư : <img src="<?=$img_path?>btn_target_add_s.gif" style="cursor:hand" onclick="javascript:go_popup(1);">

�˾����� ��Ʈ�����·� ������ | �� �޾ƿ�(SEP_COL) ����: 1^admin^76^1^admin^1242| .....