성능 개선 가이드

코드 분석

전역 변수

의도치 않은 전역 변수 선언이 있는지 확인하십시오.

var 선언 없이 변수를 생성할 경우, 전역 변수가 생성됩니다. 함수 내에서만 필요한 변수가 전역으로 선언될 경우, 함수 종료 후에도 참조가 가능하여 메모리 누수가 발생할 수 있습니다.

$r_title(수정 전)
scwin.getDay = function(pYmd, format) {
    try {
        if (pYmd.length > 8) {
            pYmd = pYmd.substring(0, 8);
        }
        if (typeof format !== "undefined" && format !== "" && WebSquare.util.getBoolean(format)) {
            date = WebSquare.date.getDay(pYmd); 
			//date가 var 선언이 생략되어 있어 전역변수로 사용됨
        } else {
            …
        }
        return date;
    } catch (e) {
    }
}
$r_title(수정 후)
scwin.getDay = function(pYmd, format) {
    try {
        var date = -1;  
		//var를 생략하지 않고 date가 가질 수 없는 값을 초기값으로 설정한뒤,
		// case에 따른 수식결과를 저장한뒤 리턴한다.
        if (pYmd.length > 8) {
            pYmd = pYmd.substring(0, 8);
        }
        if (typeof format !== "undefined" && format !== "" && WebSquare.util.getBoolean(format)) {
            date = WebSquare.date.getDay(pYmd);
        } else {
            …
        }
        return date;
    } catch (e) {
    }
}

객체

객체에 적접한 API 사용 여부 및 수식 형태를 확인하십시오. API나 수식을 올바른 형태로 사용하지 않을 경우 에러가 발생하거나 잘못된 값이 반환될 수 있습니다.

API

$r_title(수정 전 (1))
scwin.isDate = function(sDate) {
    try {
        if (sDate.length == 8) {
            sDate = sDate+”000000”;
        }
        var flag = true;
        ……
        return flag;
    } catch (e) {
    }
}
$r_title(수정 후 (1))
scwin.isDate = function(sDate) {
    var flag = true;
    try {
        if (sDate.length == 8) {
            sDate = sDate+”000000”;
        }
        var flag = true;
        ……
        return flag;
    } catch (e) {
        flag = false;   //sDate가 undefined인 경우 false 반환
    }
    return flag;
}
$r_title(수정 전 (2))
scwin.addDateSimpleisDate = function(sDate) {
    try {
        var date = “”;
        var isDate = scwin.isDate(pYmd);    //8자리 이상의 string여부 체크
        if (!isDate) return;
        cYmd = pYmd.substring(0, 8);
        var dTime = (pYmd+””).substring(8);
        var isSet = Number(offset); //isDate에서 숫자인지 체크하지는 않음           
        if (isDate && typeof isSet == “number”) 
			date = WebSquare.date.dateAdd(cYmd, isSet);
        if (typeof format != “undefined” && format == “yyyyMMdd”) {
            date = date;
        } else {
            date = date + “” + dTime;
        }
        return date;
    } catch (e) {
    }
}
$r_title(수정 후 (2))
scwin.addDateSimpleisDate = function(sDate) {
    try {
        var date = “”;
        var isDate = scwin.isDate(pYmd);
        if (!isDate) return;
        var cYmd = pYmd.substring(0, 8);
        var dTime = pYmd.substring(8);
         
        var isSet = Number(offset);
        if (!isNaN(isSet)) date = WebSquare.date.dateAdd(cYmd, isSet);  
		// Number 변환시 NaN가 아닌 경우 dateAdd 수행
        if (typeof format == “undefined” || format != “yyyyMMdd”)  
			date = date + dTime;
        return date
    } catch (e) {
    }
}

수식

객체 형태에 적합한 수식을 사용했는지 확인하십시오.

$r_title(수정 전)
scwin.addYmd = function (pYmd, year, month, day) {
    try{
        var dY = (pYmd+"").substring(0, 4);
        var dM = (pYmd+"").substring(4, 6);
        var dD = (pYmd+"").substring(6, 8);
        var dTime = (pYmd+"").substring(8);
        if (year == "" || typeof year == "undefined")
            year = 0;
        if (month == "" || typeof month == "undefined")
            month = 0;
        if (day == "" || typeof day == "undefined")
            day = 0;
        var date = new Date();
        date.setFullYear((dY*1) + year);
        date.setMonth((dM*1) + month-1);
        date.setDate((dD*1) + day);
$r_title(수정 후)
scwin.addYmd = function (pYmd, year, month, day) {
    try{
        var dY = (pYmd+"").substring(0, 4);
        var dM = (pYmd+"").substring(4, 6);
        var dD = (pYmd+"").substring(6, 8);
        var dTime = (pYmd+"").substring(8);
        year = Number(year);
        month = Number(month);
        day = Number(day);
        if (isNaN(year)) year = 0;  // 리턴값이 숫자가 아닌 경우 0으로 변경
        if (isNaN(month)) month = 0;
        if (isNaN(day)) day = 0;
        var cDate = $p.parseDate(dY+dM+dD);
        cDate.setFullYear(cDate.getFullYear() + year);  //일자 더하기 계산
        cDate.setMonth(cDate.getMonth() + month);
        cDate.setDate(cDate.getDate() + day);

객체 생성

공통 소스의 경우, 사용하는 객체의 Lifecycle을 확인하여 재사용하거나 혹은 사용하지 않을 경우 삭제하십시오.

수정 전

수정 후

스크립트 수행 시간 최소화

공통에서 자주 호출되는 함수는 최소한의 자원 사용으로 결과를 반환할 수 있도록 작성하십시오.

$r_title(수정 전)
//일자를 계산된 월의 말일자로 변
if (Number(dD) >= 30) {  //일자가 29일 경우 2월인 경우 윤년 체크필요
    var chkMonth1 = [2];
    var chkMonth2 = [4,6,9,11];
    if (chkMonth1.indexOf(dM) > -1) {
        if (scwin.isLeafYear(dY+”01”+”01”)) {   
						//윤년체크를 위해 8자리 일자로 다시 생성
            dD = “29”;
        } else {
            dD = “28”;
        }
    } else if (chkMonth2.indexOf(dM) > -1) { //31인 경우 30일로 변경
        if (Number(dD) > 30) {
            dD = “30”;
        }
    }
}
$r_title(수정 후)
if (Number(dD) >= 29) {
    if (dM == 2) {
        var dY = cDate.getFullYear();
        var isLeafYear = ((dY % 400 == 0) || ((dY % 4 == 0) && (dY % 100 != 0))) ? true : false;
        if (isLeafYear) {
            dD = "29";
        } else {
            dD = "28";
        }
    } else if (Number(dD) > 30 && (dM == 4 || dm == 6 || dm == 9 || dM == 11)) {
        dD = "30”;
    }
}

가독성

코드 분량과 코드 실행에 필요한 자원을 고려하여 최대한 가독성이 높은 형태로 코드를 작성하십시오.

$r_title(수정 전)
//년월일 또는 년,월,일 또는 년,월,일,시,분,초에 해당하는 파라미터를 조합하여 일자 스트링을 반환한다.
scwin.dateTime = function() {
  try{
    var len = arguments.length;
    var isDate = true;
    if (len > 1) {
        var year = "";
        var month = "";
        var day = "";
        var hour = "";
        var minute = "";
        var second = "";
        if ( len > 3) {
              year = arguments[0] + "";
              month =  arguments[1] + "";
              day = arguments[2] + "";
              hour = arguments[3]+"";
              minute = arguments[4] + "";
              second =arguments[5] + "";
        } else {
              year = arguments[0] + "";
              month =  arguments[1] + "";
              day = arguments[2] + "";
              hour = "0";
              minute = "0";
              second = "0";
        }
      if (month.length < 2) month = "0" + month;
      if (day.length < 2) day = "0" + day;
        if (hour.length < 2) hour = "0" + hour;
      if (minute.length < 2) minute = "0" + minute;
      if (second.length < 2) second = "0" + second;
      date = year + month + day + hour + minute + second;
      isDate = scwin.isDate(date);
    } else {
        if (typeof arguments[0] == "string") {
          date = arguments[0];
          var yy = date.substring(0, 4);
          var MM = date.substring(4, 6);
          var dd = date.substring(6, 8);
          var hh = date.substring(8, 10);
          var mm = date.substring(10, 12);
          var ss = date.substring(12);
          if (MM.length < 2) MM = "01";
          if (dd.length < 2) dd = "01";
          if (hh.length < 2) hh = "00";
          if (mm.length < 2) mm = "00";
          if (ss.length < 2) ss = "00";
          date = yy + MM + dd + hh + mm + ss;
          isDate = scwin.isDate(date);
        } else {
        }
    }
    if(!isDate) {
      date = "00000101";
    }
    return date;
  } catch (e) {
    scwin.trace("[scwin.dateTime] Exception :: ", e);
  }
};
$r_title(수정 후)
scwin.dateTime = function() {
    try{
        var len = arguments.length;
        var yy, MM, dd, hh, mm, ss;
        if (len == 1) {
            var pYmd = arguments[0]+"";
            yy = scwin.lpad(pYmd.substring(0, 4), "0", 4);
            MM = scwin.lpad(pYmd.substring(4, 6), "0", 2);
            dd = scwin.lpad(pYmd.substring(6, 8), "0", 2);
            hh = scwin.lpad(pYmd.substring(8, 10), "0", 2);
            mm = scwin.lpad(pYmd.substring(10, 12), "0", 2);
            ss = scwin.lpad(pYmd.substring(12), "0", 2);
        } else {
            yy = (typeof arguments[0] == "undefined") ? "0000" : scwin.lpad(arguments[0]+"", "0", 4);
            MM = (typeof arguments[1] == "undefined") ? "01" : scwin.lpad(arguments[1]+"", "0", 2);
            dd = (typeof arguments[2] == "undefined") ? "01" : scwin.lpad(arguments[2]+"", "0", 2);
            hh = (typeof arguments[3] == "undefined") ? "00" : scwin.lpad(arguments[3]+"", "0", 2);
            mm = (typeof arguments[4] == "undefined") ? "00" : scwin.lpad(arguments[4]+"", "0", 2);
            ss = (typeof arguments[5] == "undefined") ? "00" : scwin.lpad(arguments[5]+"", "0", 2);
        }
        if (MM == "00") MM = "01";
        if (dd == "00") dd = "01";
        var date = yy + MM + dd + hh + mm + ss;
        var isDate = scwin.isDate(date);
        if(!isDate) {
            date = "00000101";
        }
        return date;
    } catch (e) {
        scwin.trace("[scwin.dateTime] Exception :: ", e);
    }
};

데이터 형태에 적합한 수식 사용

데이터 형태에 적합한 수식을 사용해야 합니다.

$r_title(수정 전)
scwin.getMonthTerm = function (fromDate, toDate) {
    try{
        var iMonth = 0;
        var iYear = 0;
        var rMonth = "";
        if (!scwin.isDate(fromDate) || !scwin.isDate(toDate)) {
            return ;
        }
        if (parseInt(fromDate) <= parseInt(toDate)) {   //date를 숫자로 바꾸어 계산 - 정확하지 않음
            iYear = parseInt(toDate.substr(0, 4)) - parseInt(fromDate.substr(0,4));
            iMonth = parseInt(toDate.substr(4, 2), 10) - parseInt(fromDate.substr(4,2), 10);
            rMonth = (12 * iYear) + iMonth;
        }
$r_title(수정 후)
scwin.getMonthTerm = function (fromDate, toDate) {
    try{
        var iMonth = 0;
        var iYear = 0;
        var rMonth = 0;
        if (!scwin.isDate(fromDate) || !scwin.isDate(toDate))  return;
        if ($p.dateDiff(fromDate.substring(0,8), toDate.substring(0,8)) >= 0) 
			{
            iYear = parseInt(toDate.substr(0, 4)) - parseInt(fromDate.substr(0,4));
            iMonth = parseInt(toDate.substr(4, 2), 10) - parseInt(fromDate.substr(4,2), 10);
            rMonth = (12 * iYear) + iMonth;
        } else {
            return “”;
        }

변수 선언 및 참조 방식

선언되는 변수의 사용성에 따라 선언 위치와 사용 방법을 결정하십시오.

수정 전

수정 후

수정 내용은 아래와 같습니다.

  1. CodeTable은 입력값 검증을 위해 참조되는 입력가능한 문자의 집합 스트링이므로 top에서만 선언. (메모리 절약)

  2. IFrame이 참조하는 API는 top의 String을 반환하는 API로 생성. (반환값은 slice(0)을 통해 복사된 string.)

  3. 컬럼 값에 허용 가능한 문자들을 IFrame 내의 API를 이용하여 하나의 String으로 생성.

  4. 컬럼 값에서 하나의 char 씩 가져와서 가능한 문자 String에 존재하는지 확인.

팝업 파라미터 전달

String이 아닌 Object 형식의 리턴 값은 사용하지 마십시오. 브라우저에 따라 메모리 누수가 발생할 수 있습니다.

수정 전

수정 후

실행 시간 최소화

공통 소스는 최대한 빠른 시간에 결과 값을 반환할 수 있어야 합니다.

$r_title(수정 전)
var windodwObj = {
    pageUrl : "/" + Fprefix + "/xml/" + Formid +".xml",
    menuCd : Formid,
    screenNm : Formid,
    menuNm : FormName || Formid,
    pageSize : pageSize
};
if (window.top.dlt_MenuList.getMatchedJSON("menuCd", Formid).length > 0) {
  windodwObj.pageUrl = window.top.dlt_MenuList.getMatchedJSON("menuCd", Formid)[0].pageUrl || "" ;
  windodwObj.menuCd =  window.top.dlt_MenuList.getMatchedJSON("menuCd", Formid)[0].menuCd || "" ;
  windodwObj.menuNm = window.top.dlt_MenuList.getMatchedJSON("menuCd", Formid)[0].menuNm || "" ;
  windodwObj.screenNm = window.top.dlt_MenuList.getMatchedJSON("menuCd", Formid)[0].screenNm || "" ;
  windodwObj.parentMenu = window.top.dlt_MenuList.getMatchedJSON("menuCd", Formid)[0].parentMenu || "";
} else if (window.top.dlt_MenuList.getMatchedJSON("screenNm", FormName).length > 0) {
  windodwObj.pageUrl = window.top.dlt_MenuList.getMatchedJSON("screenNm", FormName)[0].pageUrl || "";
  windodwObj.menuCd =  window.top.dlt_MenuList.getMatchedJSON("screenNm", FormName)[0].menuCd || "";
  windodwObj.menuNm = window.top.dlt_MenuList.getMatchedJSON("screenNm", FormName)[0].menuNm || "";
  windodwObj.screenNm = window.top.dlt_MenuList.getMatchedJSON("screenNm", FormName)[0].screenNm || "";
  windodwObj.parentMenu = window.top.dlt_MenuList.getMatchedJSON("screenNm", FormName)[0].parentMenu || "";
}
if( GLV.G_DEVFG == "L" && (windodwObj.screenNm == "ZCOMSMKF00010" || windodwObj.screenNm == "ZCLMSVOC21410" || windodwObj.screenNm == "ZCLMSVOC11410") ) {
  strFunction = "scwin.frmload_only_arg_call_" + Formid;
}
var openAction = "selectWindow";
var selectedTab = ( window.top.mainTab.getSelectedTabIndex() +1).zf(2);  
var windowMDI = window.top.scwin.object("mdi"+selectedTab);
//이미 오픈되어 있으면 창호출 안함.
if (windowMDI && windowMDI.getWindow(windodwObj.screenNm)) {     
  return scwin._reloadNew(windodwObj.screenNm, strArg, strFunction);
}
$r_title(수정 후)
ar reloadCheck = false;  //이미 오픈되어 있으면 창호출 안함.
if (windowMDI) {
  if (typeof FormName != "undefined" && FormName != "") {
    if (windowMDI.getWindow(FormName)) {
        reloadCheck = true;
        Formid = windowMDI.getWindow(FormName).windowId; //window에 해당하는 id를 반환하는 api 사용
    }
  } else if (typeof Formid != "undefined" && Formid != "") {
    if (windowMDI.getWindowByWindowId(Formid)) reloadCheck = true;
  }
  if(reloadCheck)  return scwin._reloadNew(Formid, strArg, strFunction);
}
var windodwObj = {
    pageUrl : "/" + Fprefix + "/xml/" + Formid +".xml",
    menuCd : Formid,
    screenNm : Formid,
    menuNm : FormName || Formid,
    pageSize : pageSize
};
var menuDataList = window.top.dlt_MenuList.getMatchedJSON("menuCd", Formid);
if (menuDataList.length <= 0) menuDataList = window.top.dlt_MenuList.getMatchedJSON("screenNm", FormName);
if (menuDataList.length > 0) {
  windodwObj.pageUrl = menuDataList[0].pageUrl || "" ;
  windodwObj.menuCd =  menuDataList[0].menuCd || "" ;
  windodwObj.menuNm = menuDataList[0].menuNm || "" ;
  windodwObj.screenNm = menuDataList[0].screenNm || "" ;
  windodwObj.parentMenu = menuDataList[0].parentMenu || "";
}

DataCollection 관련 API의 반복 수행

DataList나 DatdaMap의 경우, 데이터 변경이 발생할 때마다 이벤트도 함께 발생합니다. 다른 컴포넌트에 영향을 주는 이벤트일 경우, 한번에 데이터를 변경할 것을 권장합니다.

$r_title(수정 전)
scwin.addBodyLink = function(ds_obj){
  var lv_ARow = ds_obj.insertRow();
  ds_obj.setCellData(lv_ARow, "link_nm", "주민등록증 진위확인/잠김해제");
  ds_obj.setCellData(lv_ARow, "link_url", "http://www.egov.go.kr/main?a=AA090UserJuminIsCertApp");
  ds_obj.setCellData(lv_ARow, "add_gubun", 1);   
   
  lv_ARow = ds_obj.insertRow();
  ds_obj.setCellData(lv_ARow, "link_nm", "운전면허증 진위여부 조회");
  ds_obj.setCellData(lv_ARow, "link_url", "http://dls.koroad.or.kr/jsp/ool/olq/OL_LcnsTruthYnRtvV.jsp");
  ds_obj.setCellData(lv_ARow, "add_gubun", 1);       
   
  lv_ARow = ds_obj.insertRow();
  ds_obj.setCellData(lv_ARow, "link_nm", "외국인등록증 진위 확인");
  ds_obj.setCellData(lv_ARow, "link_url", "http://www.hikorea.go.kr/pt/FrnRegIdChkPopupR_kr.pt");
  ds_obj.setCellData(lv_ARow, "add_gubun", 1); 
  ……
$r_title(수정 후)

scwin.addBodyLink = function(ds_obj){
	var linkData = {{"link_nm”:"주민등록증 진위확인/잠김해제”, 
				"link_url”:"http://www.egov.go.kr/main?a=AArtApp”, 	
				”add_gubun”:”1”},
				{"link_nm”:"운전면허증 진위여부 조회”, 
				"link_url”:"http://dls.koroad.or.kr/jsp/oolRtvV.jsp”, 
				”add_gubun”:”1”},		
				  ……
	};
	ds_obj.setJSON(linkData, true);

주요 확인 사항

기본

주요 확인 항목

대분류

소분류

이슈를 제기한 부서


업무 환경

  • 내부 업무

  • 콜센타 시스템 등 시스템 집중 사용자가 있는 환경

  • 대고객 업무

현재 개발/상태

  • 개발

  • 테스트

  • 운영

시스템 환경

  • 웹스퀘어 버전

  • 웹스퀘어 스튜디오 버전 (빌드일)

  • 사용자 OS, 브라우저

  • PC 사양

  • 네트워크

구체적인 문제

속도

주요 확인 항목

대분류

소분류

초기 화면 로딩 속도 이슈인가?


초기화 이후 조회 속도 이슈인가?


화면의 조작이 느린 상황인가?


기타 상황

  • 메모리 누수 의심

  • 네트워크 속도 의심

  • 구형 브라우저 이슈

  • 모바일 이슈

  • SSL 관련 이슈

WEB서버와 WAS서버 종류 및 버전은?


메모리

주요 확인 항목

대분류

소분류

브라우저가 종료되는 현상이 발생하는가?


메모리가 얼마까지 증가하는가?


메모리 릴리즈 여부

  • 창 새로고침

  • 창 닫기

  • 브라우저 닫기

  • OS 재기동

각종 환경

  • 콜센터 상담직원 처럼 하루종일 동일한 화면을 사용하는가?

  • MDI 사용 여부

  • 발생 브라우저

  • 창간 객체 전달 여부

  • ActiveX나 플러그인 사용 여부

  • 문서보안/PC보안 모듈 사용 여부

백화 현상

주요 확인 항목

대분류

소분류

백화 현상이 발생했을 때 화면은 조작 가능한가?


화면이 조작 가능한 경우

  • 메모리와 CPU 점유율 확인

  • 개발자 도구를 이용하여 화면의 객체 확인

  • 다른 탭을 닫았을 경우 동작 확인

화면이 조작 가능하지 않은 경우

  • 메모리와 CPU 점유율 확인

  • 시스템 오류 메시지 확인

각종 환경

  • 콜센터 상담직원 처럼 하루종일 동일한 화면을 사용하는가?

  • MDI 사용여부

  • 발생 브라우저

  • 창간 객체 전달 여부

  • ActiveX나 플러그인 사용 여부

  • 문서보안/PC보안 모듈 사용 여부

메모리 관리

메모리와 성능을 고려한 개발은 프로젝트의 성공 여부를 결정하는 요소 중 하나입니다. 특히, 특히 공통 소스는 모든 업무 화면에 영향을 줄 수 있기 때문에 메모리 및 성능에 영향이 없도록 최대한 간결하게 작성하심시오.

메모리 사용량이 증가는 메모리 부족을 의미하며 이는 곧 스크립트 수행 속도 저하로 이어집니다. 브라우저에 HTML 태그가 느리게 렌더링되어 브라우저의 백화 현상이 발생하거나 심할 경우 브라우저가 정상 동작하지 않습니다.

메모리 관리는 브라우저 사용성 및 개발 프로그램의 성능의 핵심입니다.

메모리 누수 방지

WebSquare 공개 API 사용

jQuery의 제한적 사용

jQuery Component Search 유형별 성능 테스트 결과 (검색방식에 따른 속도 테스트) (출처보기)

최상위 scope에 함수 및 변수 선언 제한

$r_title(비권장 사용 예제)
var GlobalValue = 1;

function doInit() {
	//Something
}
$r_title(권장 사용 예제)

scvar.GlobalValue = 1;

scwin.doInit = function() {
	//Something
}

Submission

업무화면에서 Submission 생성

업무화면에서 통신 객체(Submission)를 생성하여 필요한 통신 객체를 수행하는 것을 권장합니다.

Submission 생성

업무 화면에서 필요한 통신 객체를 미리 생성해 놓는 것은 소스 가독성이나 Workflow 연계를 위해서도 필요한 중요한 작업입니다.

동적으로 Submission을 생성하는 경우 동일한 id의 Submission이 존재하는지 확인하십시오. 동일한 ID를 사용할 경우 기존 Submission의 ID를 수정하거나 삭제하십시오.

$r_title(비권장 사용 예제)
common.executeSubmission = function(options) {
	
	// ...

	var submissionObj = {
  		“id” : options.id,
 		 …
	}

	$p.createSubmission(submissionObj);
}
$r_title(권장 사용 예제)
common.executeSubmission = function(options) {
	
	// ...

	var submissionObj = {
		“id” : options.id,
		…
		}

	if ($p.getSubmission(submissionObj.id) != null) {
		$p.deleteSubmission(submissionObj.id);
	}

	$p.createSubmission(submissionObj);
}

Async 통신 권장 (Sync 모드 경고)

Submission 실행시 비동기(Asynchronous) 모드의 통신을 권장합니다.

Async 모드 권장

config.xml 파일에 <warningSyn> 항목이 "true"로 설정된 경우, 동기(synchronous) 모드의 Submission에 대해 경고 메시지가 표시됩니다.

$r_title(사용 예)
<WebSquare>
	<notRecommended>
        <warningSync value="true"/>
    </notRecommended>
</WebSquare>

동영상


동기 통신 모드 경고 (비동기 통신 권장) - config.xml

Studio에서 sync 모드를 선택할 때 표시되는 경고 메시지

sync 모드 사용 시 실행 화면에 표시되는 경고 메시지

$p.ajax

응답 객체의 직접 할당 금지

WebSquare는 Submission을 이용한 통신을 권장합니다. 그러나 사용자가 직접 통신 관련 설정을 해야하는 경우를 위해 $p.ajax API를 제공합니다.

아래 에제의 경우, 사용자가 직접 제어할 수 있도록 응답 결과 객체를 반환하고 있습니다. 그러나 업무 화면의 콜백 함수로 그대로 넘기기 때문에 업무 화면에서 변수 등에 직접 할당할 경우 통신 완료 후 응답 객체가 메모리에서 릴리즈되지 않을 수 있습니다.

$r_title(비권장 사용 예)
$p.ajax({
   action : action,
   mode : mode,
   mediatype : mediatype,
   method : method,
   processMsg : processMsg,
   requestData : reqData,
   requestHeader : requestHeader,
   beforeAjax : function(e) {
   },
   success : function(e) {
       var resBody = e.responseBody;
       if (callbackFnc != '') {
           eval(callbackFnc + '(resBody)');
       }
   },
   error : function(e) {
       return _requestErrCallback(e);
   }
});
$r_title(권장 사용 예)
$p.ajax({
   action : action,
   mode : mode,
   mediatype : mediatype,
   method : method,
   processMsg : processMsg,
   requestData : reqData,
   requestHeader : requestHeader,
   beforeAjax : function(e) {
   },
   success : function(e) {
      if (callbackFnc != '') {
        window[callbackFnc](JSON.parse(e.responseText));
      }
   },
   error : function(e) {
       return _requestErrCallback(e);
   }
});

Closure 내부에서 객체 참조

Closure는 전역이 아닌 지역 변수 선언이 가능하여 공통 모듈에서 많이 사용합니다. 단, Closure 안에서 Object 형태의 외부 객체를 직접 참조하지 않도록 주의하십시오. 메모리에서 릴리즈 되지 않을 수 있습니다.

특히 Closure 함수를 비동기 형태로 호출할 경우, 객체를 직접 다른 변수에 할당하면 참조 관계가 발생합니다. 이 경우, 함수 실행이 완료되더라도 메모리에서 제거되지 않을 수 있습니다.

아래 에제에서 test2test와 직접 참조 관계가 이루어져 test 값 변경에 따라 test2 값이 변경됩니다. 즉 test2와 참조 관계가 발생하여 test 사용이 끝난 뒤에도 메모리에서 제거되지 않습니다.

$r_title(직접 참조 관계 예)
//CASE1

var test = {a: "abc", b: "bbc"};
scwin.functionTest = function(value) {
  scwin.test2 = value;
  console.log(value);
	// 결과 : {a: "abc", b: "bbc"}
  console.log(scwin.test2);
	// 결과 : {a: "abc", b: "bbc"}

  for (var item in value) { value[item] = null; }
  console.log(value);
	// 결과 : {a: null, b: null}
  console.log(scwin.test2);
	// 결과 : {a: null, b: null}
};

scwin.functionTest(test);
console.log(test);
	// 결과 : {a: null, b: null}

//CASE2

var test = {a: "abc", b: "bbc"};
scwin.functionTest = function(value) {
	scwin.test2 = value;
	console.log(value);
		// 결과 : {a: "abc", b: "bbc"}
	console.log(scwin.test2);
		// 결과 : {a: "abc", b: "bbc"}
};
scwin.functionTest(test);
for (var item in test) { test[item] = null; }
console.log(test);
	// 결과 : {a: null, b: null}
console.log(scwin.test2);
	// 결과 : {a: null, b: null}

따라서 일반적으로 String 타입으로 파라미터를 전달하나, 직접 Object형식으로 파라미터를 전달해야만 하는 경우 반드시 초기화를 수행하여 호출된 함수에서 전역변수로 재사용되는 것을 방지하십시오.

$r_title(비권장 사용 예)
$p.ajax({
  
	// ...
  
	success :	function(e) {
					resBody = e.responseBody;
					if (callbackFnc != '') {
						eval(callbackFnc + '(resBody, reqAjaxOptions)');
              		}
            	}
  });
}

scwin.callback1 = function(resBody, reqAjaxOptions) {
	if (reqAjaxOptions.mode) {

	// ...

	}
  scwin.orgResData = resBody;
}
$r_title(권장 사용 예)
$p.ajax({

	//...

	 success :	function(e) {
					resBody = e.responseBody;
					if (callbackFnc != '') {
						eval(callbackFnc + '(resBody, reqAjaxOptions)');
						for(var item in resBody){
						resBody[item] = null;
						}
						for(var item in reqAjaxOptions){
							reqAjaxOptions[item] = null;
						}
					}
            }
  });
}

scwin.callback1 = function(resBody, reqAjaxOptions) {
	if (reqAjaxOptions.mode) {

	// ...

	}
	
	scwin.orgResData = JSON.parse(JSON.stringify(resBody));
}

DataCollection

생성

DataCollection은 개발도구에서 생성하거나 API를 통해서 생성할 수 있습니다. 단, 업무화면에서는 유지보수를 위해 개발도구에서 DataCollection 선언 한 뒤 스크립트에서 사용하는 것을 권장합니다.

DataCollection을 API를 이용하여 생성할 때 동일한 id가 이미 존재하는 경우 삭제 후 생성하십시오.

$r_title(비권장 사용 예)
common.searchCondition = function(targetCollectionId) {
	var option = {};
	option.id = targetCollectionId;
	option.type = "dataList";
	option.option = {"baseNode": "list", "repeatNode": "map"}; 
	option.columnInfo = [{"id":"title", "name": "title", "dataType":"text"},
		{"id":"value", "name": "value", "dataType":"text"}];
	$p.data.create(option);
	
	//...

}
$r_title(권장 사용 예)
common.searchCondition = function(targetCollectionId) {
	$p.data.remove(targetCollectionId)
	var option = {};
	option.id = targetCollectionId;
	option.type = "dataList";
	option.option = {"baseNode": "list", "repeatNode": "map"}; 
	option.columnInfo = [{"id":"title", "name": "title", "dataType":"text"},
		{"id":"value", "name": "value", "dataType":"text"}];
	$p.data.create(option);

	// ...

}

초기화

DataList는 removeAll, DataMap은 reset API를 이용하여 등록된 데이터를 삭제하십시오.

$r_title(권장 사용 예)
common.clearData = function(collectionObj) {
	switch (collectionObj.initializeType) {
	case "dataList":
		collectionObj.removeAll();
	case "dataMap":
		collectionObj.reset();
	}
}

변수명

일반적인 네이밍 규칙에 어긋난 변수명 역시 메모리 누스를 유발합니다. 일부 브라우저(Internet Explorer)에서 변수명이 숫자로 이루어진 경우 메모리 누수 현상이 발생하는 경우가 있습니다. 일반적인 변수명 사용 규칙에 따라 영문으로 시작하는 변수명을 사용하십시오.

WebSquare에서 제공하는 컴포넌트는 id를 지정해야 합니다. 단, 이벤트 등록이나 스크립트에서 호출할 필요가 없는 경우 id를 생략할 수 있습니다.

$r_title(비권장 사용 예)
<w2:anchor class="todolist_refresh" id="1" outerDiv="true">
$r_title(권장 사용 예)
<w2:anchor class="todolist_refresh" id="anchor01" outerDiv="true">

// 혹은 id 삭제 가능

<w2:anchor class="todolist_refresh" outerDiv="true">

팝업

팝업의 Lifecycle

타 Frame으로 객체 전달

$r_title(비권장 사용 예)
//Child

comf.onPopupClose = function(retValue) {

//...

comf.onPopupClose(retValue);
}
comf.onPopupClose = function(retValue) {
	opener.comf._onPopupClose(retValue);
}

//Parent
comf._onPopupClose = function(retValue) {
	scwin.callUserCallback(retValue);
}
$r_title(권장 사용 예)
//Child

scwin.onPopupClose = function() {

  // ...

comf.onPopupClose(retValue);
}
comf.onPopupClose = function(retValue) {
	var str = JSON.stringify(retValue);
	opener.comf._onPopupClose(str);
}


//Parent
comf._onPopupClose = function(str) {
	var retValue = JSON.parse(str);
	scwin.callUserCallback(retValue);
}

객체 제어

JavaScript는 느슨한 타입(loosely typed) 언어, 혹은 동적(dynamic) 언어입니다. 즉, 변수 타입을 미리 선언할 필요가 없고 프로그램이 처리되는 과정에서 자동으로 파악 변환되기도 합니다. 공통함수가 객체 타입에 따른 대응을 못하고 에러가 발생하는 경우 스크립트가 중지되고 브라우저에 따라 성능 및 메모리에 영향을 받습니다.

$r_title(비권장 사용 예)
scwin.addDateSimpleisDate = function(sDate) {
	try {
		var date = “”;
		var isDate = scwin.isDate(pYmd);  //8자리 이상의 string여부 체크
		if (!isDate) return;
		cYmd = pYmd.substring(0, 8);
		var dTime = (pYmd+””).substring(8);
		var isSet = Number(offset); //isDate에서 숫자인지 체크하지는 않음      
		if (isDate && typeof isSet == “number”)  date = $p.dateAdd(cYmd, isSet);
		if (typeof format != “undefined” && format == “yyyyMMdd”) {
			date = date;
		} else {
			date = date + “” + dTime;
		}
		return date;
	} catch (e) { }
}
$r_title(권장 사용 예)
scwin.addDateSimpleisDate = function(sDate) {
	try {
		var date = "";
		var isDate = scwin.isDate(pYmd);
		if (!isDate) return;
		var cYmd = pYmd.substring(0, 8);
		var dTime = pYmd.substring(8);
		var isSet = Number(offset);
		if (!isNaN(isSet)) date = $p.dateAdd(cYmd, isSet);  
		// Number 변환시 NaN가 아닌 경우 dateAdd 수행
		if (typeof format == “undefined” || format != “yyyyMMdd”)  
		date = date + dTime;
		return date
	} catch (e) { }
}

전역 변수 선언

JavasSript는 느슨한 타입(loosely typed) 언어로 변수 선언을 위해 사용하는 var 문자를 누락해도 스크립트는 정상적 수행됩니다. 단, 이 경우 해당 변수는 전역에 선언되어, 함수 종료 후에도 계속 참조가 가능하고 자동 릴리즈가 되지 않습니다. SPA 방식으로 이동하는 화면인 경우 이는 메모리 누수 현상으로 이어집니다.

$r_title(비권장 사용 예)
scwin.getDay = function(pYmd, format) {
	try {
		if (pYmd.length > 8) {
			pYmd = pYmd.substring(0, 8);
	}
	if (typeof format !== "undefined" && format !== "" && 
	WebSquare.util.getBoolean(format)) {
		date = WebSquare.date.getDay(pYmd);	
		//date가 var 선언이 생략되어 있어 전역변수로 사용됨
    } else {
      // …
    }
    return date;
  } catch (e) {
  }
}
$r_title(권장 사용 예)
scwin.getDay = function(pYmd, format) {
	try {
    var date = -1;  
	//var를 생략하지 않고 
	// date가 가질 수 없는 값을 초기값으로 설정한뒤 
	// case에 따른 수식결과를 저장한뒤 리턴한다.
    if (pYmd.length > 8) {
		pYmd = pYmd.substring(0, 8);
    }
    if (typeof format !== "undefined" && format !== "" && 
	WebSquare.util.getBoolean(format)) {
		date = WebSquare.date.getDay(pYmd);
    } else {
       // …
    }
    return date;
  } catch (e) {
  }
}

공통 코드 조회

각 업무 페이지의 SelectBox나 CheckBox와 같은 공통 코드(혹은 코드성 데이터)가 존재합니다. 이 같은 공통 코드는 업무 페이지의 데이터를 불러오기 전에 먼저 조회해야 합니다. 비동기(asynchronous) 통신을 사용할 경우 공통 코드 조회가 완료되기 전에 업무 페이지의 데이터가 먼저 로드될 수 있습니다. 이 경우 SelectBox, CheckBox와 같은 공통 코드가 제대로 표현되지 않습니다.

아래의 방법을 권장합니다.

웹사이트 성능 최적화

WebSquare5로 렌더링된 화면은 대부분의 작업을 브라우저에 위임합니다. 즉, XML 파일을 HTML Tag 로 변환하여 브라우저가 변환된 HTML을 해석하도록 위임합니다.

아래와 같은 최적화 작업을 수행하여 웹사이트 성능을 최적화하십시오.

Expires 헤더 추가

정적인 자원(stylesheet, image, script 등)의 비효율적인 처리는 성능 저하로 연결됩니다.

해당 자원들 헤더에 Expires 정보를 추가하여 캐시 처리를 하십시오.

Gzip 압축

외부 파일 및 Response 데이터를 압축하여 성능을 높일 수 있습니다.

대부분의 브라우저가 Gzip 압축 전송을 지원하므로 Gzip 압축을 권장합니다.

기타 주의할 내용

WFrame

IFrame 수가 지나치게 많은 경우 메모리 점유율이 올라갑니다. IFrame 대신 WFrame을 사용을 권장합니다.

TabControl

TabControl 컴포넌트는 frameMode 속성값으로 iframewframe을 제공하며, 해당 속성값에 따라 컨텐츠를 렌더링합니다. 단, iframe 을 설정할 경우 탭 수에 비례하여 브라우저의 메모리 점유가 증가합니다. 따라서 메인 층에서 사용하는 경우에만 속성값을 iframe으로 지정하길 권장합니다. src 속성은 컨텐츠에 웹스퀘어5 컴포넌트를 직접 구성하지 않고 content 영역을 XML 파일을 불러 렌더링합니다.

다음은 잘못 사용한 코드 예입니다.

$r_title(잘못된 frameMode 설정 (예))

<w2:tabControl confirmTrueAction="exist" useTabKeyOnly="true" id="tabControl1" useConfirmMessage="false" confirmFalseAction="new"
    alwaysDraw="false">
    <w2:tabs id="tabs1" style="width:70px;height:30px" label="" disabled="false"></w2:tabs>
    <w2:tabs id="tabs2" style="width:70px;height:30px" label="" disabled="false"></w2:tabs>
    <w2:tabs id="tabs3" style="width:70px;height:30px" label="" disabled="false"></w2:tabs>
    <w2:tabs id="tabs4" style="width:70px;height:30px" label="" disabled="false"></w2:tabs>
    <!-- 올바른 예 -->
    <w2:content id="content1" alwaysDraw="false" frameMode="wframe" src="/tabChild_001.xml"></w2:content>
    <!-- 잘못된 예. 브라우저 리소스를 많이 사용하게 될 iframe 를 사용 -->
    <w2:content id="content2" alwaysDraw="false" frameMode="iframe" src="/tabChild_002.xml"></w2:content>
    <!-- 잘못된 예. frameMode를 사용하지 않음. wframe 를 사용하여 해당 컴포넌트에 src 사용. -->
    <w2:content id="content3" alwaysDraw="false">
        <w2:wframe src="/tabChild_003.xml"></w2:wframe>
    </w2:content>
    <!-- 잘못된 예. frameMode 를 사용하지 않음. iframe 를 사용 해당 컴포넌트에 src 사용. -->
    <w2:content id="content4" alwaysDraw="false">
        <w2:iframe id="" src="/tabChild_004.xml" spa="false"></w2:iframe>
    </w2:content>
</w2:tabControl>

동기 요청 최소화

대부분의 프로젝트에서는 현재 시간 (서버 시간), 사용자 정보, 또는 사용자 권한을 조회하기 위해 동기 요청을 사용합니다. 한 화면에서 동기 요청을 여러 번 호출하는 것은 성능 저하로 이어질 수 있습니다.

하나의 요청으로 처리 가능한 함수를 구현하십시오.

성능 관리

초기 화면 네트워크 튜닝

초기 화면은 시스템 관점에서는 리소스 다운로드와 실행 및 그 외 준비로 시간이 많이 소요되는 단계이지만, 사용자 입장에서는 시스템의 진입 단계로 빠른 응답속도가 제공되어야 하는 중요한 화면입니다. 따라서 네트워크를 통해 리소스가 수집되는 단계를 이해하여 리소스 로딩에 대한 튜닝 작업이 반드시 필요합니다.

리소스 로딩

Network 패널의 주어진 입력 항목에 대한 전체 타이밍 정보를 확인할 수 있습니다.

위와 같은 정보는 아래의 console 명령으로 추출할 수 있습니다.

performance.getEntriesByType('resource').filter(item => item.name.includes("config.xml"))

각 단계 별 설명

단계

설명

Queuing

  • 요청이 큐에 저장될 경우, 다음을 의미합니다.

    • 랜더링 엔진이 요청을 연기한 것으로 이는 이 요청이 중요 리소스 (예:스크립트/스타일)보다 우선순위가


낮다고 간주됨. 주로 이미지 파일에서 자주 발생.

  • N/A

    • 곧 사용 가능한 TCP 소켓을 기다리도록 보류되었음.

    • 브라우저가 HTTP 1에서 각 출처마다 6개의 TCP 연결만 허용하기 때문에 요청이 보류됨.

    • 디스크 캐시 항목을 만드는데 걸린 시간. (대부분의 경우 매우 빠름.)

Stalled/Blocking

  • 요청을 전송할 수 있게 되기까지 기다리는 데 소비한 시간. 발생 원인은 위의 Queueing 원인 중 하나가 될 수 있음.

  • 또한 이 시간은 프록시 협상에 소비한 시간도 모두 포함.

Proxy Negotiation

  • 프록시 서버 연결과 협상하는데 소비한 시간.

DNS Lookup

  • DNS 검색을 수행하는데 소비한 시간.

  • 페이지의 새 도메인마다 DNS

검색을 수행하는데 한번의 전체 왕복이 필요함.

Initial Connection/Connecting

  • 연결을 설정하는 데 소요된 시간.

  • TCP 핸드세이크/재시도와 SSL 협상 등이 포함됨.

SSL

  • SSL 핸드세이크를 완료하는데 소요된 시간.

Request Sent/Sending

  • 네트워크 요청을 발급하는데 걸린 시간.

  • 일반적으로 밀리초의 극히 짧은 시간.

Waiting (TTFB)

  • 최초 응답 대기 시간. TTFB(Time To

FirstByte)라고도 함.

  • (서버까지 왕복에 소요된 시간) + (서버 응답을 기다린 시간)

Content Download/Downloading

  • 응답 데이타 수신에 소요된 시간.

네트워크 문제 진단

Queue에 저장되거나 지연된 시리즈

느린 TTFB

느린 TTFB는 긴 대기 시간 즉 녹색이 많이 나타나는 경우입니다. 이 시간은 200ms 미만이 좋습니다. 이 시간이 길다면, 그 원인은 아래와 같습니다.

  1. 클라이언트와 서버 사이의 네트워크 상태가 잘못된 경우

  2. 서버 애플리케이션의 응답이 느린 경우

처리 용량 확보

해결 방안

해결 방안

해결 방안

설명

네트워크 요청 횟수 튜닝

  • js: 공통으로 제공되는 js 파일이 여러개로 분리된 경우, 하나의 파일로 합쳐서 호출. minify하여 js의 용량을 줄이는 것도 좋은 방안임.

  • CSS Sprites: 아이콘등 여러 이미지를 개별로 요청하지 않고 한 이미지 파일로 통합한 후 Background로 Position을 잡아주어 한 이미지만 로딩하면 트래픽 절약이 가능.

브라우저 기본 폰트 사용

  • 웹폰트를 사용하는 경우 웹폰트 리소스 다운로드를 위한 트래픽과 폰트 사용을 위한 약간의 지연이 브라우저에서 발생 가능. 반드시 사용해야 한다면, 용량이 적은 웹폰트를 권장.

시스템 사용을 위한 외부 프레임워크 연계

  • 시스템 사용 시 외부 프레임워크은 초기 화면이 아닌 필요한 시점에 로드하는 것을 권장. 로딩 부하를 줄일 수 있음.

    • (예) 보안, 인증, 레포트

단일 frame 사용

  • 화면 구성 프레임(frameset, iframe)은 하나로 처리하는 것을 권장.

  • 각 프레임은 각각의 윈도우 객체를 소유하고 웹스퀘어 엔진이나 공통 JS와 같은 리소스를 요청.

jQuery 셀렉터 사용

id 셀렉터

사용 예 (1)

Object에 지정된 id를 이용하여 검색하는 것이 가장 빠른 속도를 보장합니다.

$r_title(예제 1)
<xf:group class="grp_shbox" id="">
	<w2:anchor id="btn_shbox " class="btn_shbox_hide" outerDiv="false" 
		style="" ev:onclick="scwin.btn_shbox_hide_onclick">
		<xf:label><![CDATA[검색영역 숨기기]]></xf:label>
	</w2:anchor>
</xf:group>
Anchor 컴포넌트 접근 방법

".grp_shbox. btn_shbox_hide”

“.btn_shbox_hide”

“#btn_shbox”

“a.btn_shbox_hide”

가장 빨리 접근하는 방법: #btn_shbox

사용 예 (2)

아래와 같이 컴포넌트에 id 속성이 없는 경우에는 가장 가까운 상위 컴포넌트의 id 속성으로부터 자식 셀렉터를 이용하여 “#link_area .link_text”로 지정하는 방법이 더 빠릅니다.

$r_title(예제 2)
<xf:group id="link_area" class="btn_shbox" tagName="ul">
	<xf:group class="" tagName="li">
		<w2:anchor class="link_text" outerDiv="false" style="">
			<xf:label><![CDATA[고객센터]]></xf:label>
		</w2:anchor>
	</xf:group>
</xf:group>

태그 셀렉터

클래스 셀렉터는 jquery 셀렉터 중 검색 시간이 가장 많이 소요됩니다. 태그 명을 사용하거나 클래스로 셀렉터를 지정해야 하는 경우, 태그 명 추가를 권장합니다.

$r_title(예제)
<xf:group id="link_area" class="btn_shbox" tagName="ul">
	<xf:group class="" tagName="li">
		<w2:anchor class="link_text" outerDiv="false" style="">
			<xf:label><![CDATA[고객센터]]></xf:label>
		</w2:anchor>
	</xf:group>
</xf:group>

위의 예제에서 “#link_area .link_text”보다 “#link_area a”가 더 빠릅니다. 따라서 “#link_area a.link_text”와 같이 태그 명을 추가하는 것이 좋습니다.

메서드 체인

동일한 Object에 여러 개의 작업을 실행하는 경우 검색된 결과에 여러 개의 메소드를 붙여서 순차적으로 호출하는 “메서드 체인”을 사용하십시오. 태그를 다시 검색하는 과정을 생략할 수 있어 상당한 속도 개선을 기대할 수 있습니다.

$r_title(예제)
$("#link_area").css("color", "red").slideUp(2000).slideDown(2000);

셀렉터 캐싱

메서드 체인을 이용할 수 없는 경우, 셀렉터 검색 결과를 캐싱(변수에 저장 후 재사용)하여 재검색을 생략하고 그 결과 처리 속도를 올릴 수 있습니다. 아래는 검색 결과를 “linkText”에 저장하여 사용하는 예제입니다.

$r_title(예제)
var linkText = $("#link_area a.link_text");
linkText.css("...");
	...중략...
linkText.toggle("...");
	...중략...
linkText.click("...");

셀렉터 범위 지정

아래 예제와 같이 셀렉터의 검색 범위를 지정하여 검색 속도를 개선할 수도 있습니다.

$r_title(예제 (1))
$("#link_area li a.link_text").css("...");
	...중략...
$("#link_area li span").toggle("...");
	...중략...
$("#link_area li a").click("...");

아래는 공통 셀렉터인 $("#link_area li")를 변수 “linkText”에 저장하여 셀렉터 뒤에 검색대상인 linkText를 지정하여 변수의 범위 내에서 검색하는 예제입니다. HTML 전체에서 검색하는 보통의 지정 방법과 비교하여 검색 속도를 개선할 수 있습니다.

$r_title(예제 (2))
var linkText = $("#link_area li");
$("a.link_text", linkText).css("...");
	...중략...
$("span", linkText).toggle("...");
	...중략...
$("a", linkText).click("...");

재귀 호출과 반복문 튜닝

재귀 호출형 함수는 호출할 때 마다 스택 메모리가 생성되는며, 이는 재귀 조건이 달성된 후에야 제거됩니다. 그 결과 메모리가 계속 쌓이는 문제가 있습니다. 재귀로 호출된 함수가 많은 경우 스택 메모리 한계로 인해 스택오버플로우(stack overflow) 및 브라우저 멈춤현상이 발생합니다. 일반적인 반복문은 스택에 쌓이지 않지만 일정 시간 후에는 스크립트 수행 속도가 점점 떨어지고 스크립트 타임아웃(script timeout) 현상이 발생할 수 있습니다. 특히 모바일 브라우저는 5초 이내에 스크립트 타임아웃이 발생하기 때문에 무거운 로직을 반복문으로 처리하는 것은 자제해야 합니다.

DataCollection 제어

반복적인 DataCollection 변경

DataCollection의 데이터 변경은 이벤트 발생으로 이어지며, 이는 곧 (ref로 연결된 Component 등) 관련된 객체들에 대해서도 추가 작업이 필요함을 의미합니다. 따라서 셀단위로 데이터를 반복 설정하는 것보다 데이터를 한번에 설정할 것을 권장합니다.

$r_title(비권장 사용 예)
scwin.setBasicData = function() {
	
	// ...

	dataCollection1.setCellData("name","WebSquare");
	dataCollection1.setCellData("address","서울시 구로구");
	dataCollection1.setCellData("telephone","01011112222");
}
$r_title(일반 사용 예)
scwin.setBasicData = function() {
	var basicData = {};
	basicData["name"] = "WebSquare";
	basicData["address"] = "서울시 구로구";
	basicData["telephone"] = "01011112222";
	
	dataCollection1.setJSON(basicData);
	}
$r_title(권장 사용 예)
scwin.setBasicData = function() {
	var basicData =	{"name":"WebSquare",
					"address":"서울시 구로구",
					"telephone":"01011112222"}
	dataCollection1.setJSON(basicData);
}

여러행 추가시 Array를 이용하여 처리

여러 행을 추가하는 경우 Array를 만들어 Data를 추가한 후 한번에 DataCollection에 추가하는 것이 가능합니다.

$r_title(비권장 사용 예)
scwin.addBodyLink = function(ds_obj){
	var lv_ARow = ds_obj.insertRow();
	ds_obj.setCellData(lv_ARow, "link_nm", "주민등록증 진위확인/잠김해제");
	ds_obj.setCellData(lv_ARow, "link_url", 
	"http://www.egov.go.kr/main?a=AA090UserJuminIsCertApp");
	ds_obj.setCellData(lv_ARow, "add_gubun", 1);    
  
	lv_ARow = ds_obj.insertRow();
	ds_obj.setCellData(lv_ARow, "link_nm", "운전면허증 진위여부 조회");
	ds_obj.setCellData(lv_ARow, "link_url", 
	"http://dls.koroad.or.kr/jsp/ool/olq/OL_LcnsTruthYnRtvV.jsp");
	ds_obj.setCellData(lv_ARow, "add_gubun", 1);        
  
	lv_ARow = ds_obj.insertRow(); 
	ds_obj.setCellData(lv_ARow, "link_nm", "외국인등록증 진위 확인");
	ds_obj.setCellData(lv_ARow, "link_url", 
	"http://www.hikorea.go.kr/pt/FrnRegIdChkPopupR_kr.pt");
	ds_obj.setCellData(lv_ARow, "add_gubun", 1);  

	// ...

	ds_obj.setJSON(linkData, true);
$r_title(권장 사용 예)
scwin.addBodyLink = function(ds_obj){
	var linkData = 
		{{"link_nm”:"주민등록증 진위확인/잠김해제”, 
		"link_url”:"http://www.egov.go.kr/main?a=AA090UserJuminIsCertApp”, 
		”add_gubun”:”1”},
		{"link_nm”:"운전면허증 진위여부 조회”, 
		"link_url”:"http://dls.koroad.or.kr/jsp//OL_LcnsTruthYnRtvV.jsp”, 
		”add_gubun”:”1”},
		// ...
};
ds_obj.setJSON(linkData, true);

반복문 내에 사용시 이벤트 중지 후 처리

반복문 내에서 건당 작업이 필요한 경우, DataCollection 변경 시 발생하는 이벤트를 중지하고 변경이 완료된 후 이벤트가 발생하도록 처리하십시오.

$r_title(사용 예)
//broadcast 비활성화에 의해 dataList를 변경해도 연동된 컴포넌트 UI는 갱신되지 않음.
$p.data.setBroadcast(false); 
	for(var i = 0; i < 10; i++){
		dataList1.insertRow(i);
	};   
	for(var i = 0; i < 10; i++){
		dataList2.insertRow(i);
	}; 

//dataList1, dataList2의 최종데이터로 연동된 컴포넌트 ui가 갱신된다.
$p.data.setBroadcast(true);

GridView 튜닝

전체 렌더링 소요 시간 (통신 + 렌더링)

메모리의 사용량 역시 셀이 많을수록 증가폭이 늘어납니다.

메모리 사용량

아래는 1초 내 메모리 증가분은 100M를 기준으로 허용하는 것입니다. 아래 테이블에 따라, 약 400,000 Cell을 기준으로 제한하는 것을 권장합니다.

권장 렌더링 소요 시간

성능 개선을 위한 추가 기능

SPA

WebSquare 엔진, 공통 CSS, 공통 JavaScript 등 여러 화면에서 공통으로 사용되는 모듈과 화면 소스의 Lifecycle을 분리하여 화면 로딩 및 전환 속도를 높이는 기능입니다. WindowContainer 및 TabControl는 WebSquare 엔진, 공통 CSS, 공통 JavaScript 등 여러 화면에서 공통으로 사용되는 모듈을 미리 로딩하여 화면 로딩 및 전환 속도를 높일 수 있습니다.

WFrame

IFrame은 분리된 전역메모리 영역(window)을 사용하여 개발은 용이하지만 메모리 및 성능이 떨어질 수 있습니다. 이를 보완하기 위해 WebSquare5 SP2는 WFrame을 제공합니다. 개발 생산성 향상과 메모리 및 성능 향상을 위해 WFrame들은 전역메모리 영역(window)을 공유하지만 각 WFrame은 분리된 지역변수를 참조합니다. (The internal link is invalid. 참조.)

성능 저하 원인 및 해결 방안

본 단락은 성능 저하가 발생할 경우 그 원인 및 해결 방안을 분류하여 정리한 것입니다.

웹 서버 설정

JSON, JS, XML, CSS 다운로드 속도

원인

해결 방안

  • gzip 설정이 빠져 있는 경우 주로 발생. (모바일 환경에서는 속도에 더 큰 영향을 줌.)

  • 웹서버 설정 변경. (gzip 설정 추가.)

정적인 자원에 대한 Caching 설정 오류

원인

해결 방안

  • JS 및 이미지 파일에 대한 캐싱 오류.

  • 웹서버 설정 변경

    • etag 설정 추가

    • lastmodified date 설정 추가

SSL Handshake

원인

해결 방안

  • Keepalive가 설정되지 않은 경우 모든 요청시 SSL Handshake가 수행되어 속도 저하.

  • 웹서버 설정에 변경

    • SSL Keep Alive 설정 추가

WebSquare 설정

Cache 설정 및 압축 설정

원인

해결 방안

  • 웹스퀘어 엔진 압축 미적용 으로 인한 성능 저하

  • 웹스퀘어 XML 파일 캐싱 미적용 으로 인한 성능 저하

  • <engineCache compression="true" ... /> 설정 적용

  • <environment ... cache="cache" ... /> 설정 적용

engineType 설정

원인

해결 방안

  • 개발용 웹스퀘어 엔진(engineType="5") 사용

  • 웹스퀘어 설정 파일에서 운영환경에 적절한 웹스퀘어 엔진(engineType="6")을 사용 JS크기를 최소화 하여 성능 향상을 할 수 있음.

  • engineType 참조.

$r_title(WebSquare engineType 설정 (예))
<!-- 파일명 : /{websquare_home}/config/websquare.xml -->
<!-- 웹스퀘어 엔진의 난독화 및 압축 수준을 설정하는데 사용 개발환경에는 5, 운영환경에는 6을 권장 -->
<engineType value="6"/

<engineCache compression="true" ... />

<engineCache compression="false" ... />

통신 최적화

화면 프리징

원인

해결 방안

  • synchronous 방식의 HTTP Request 사용시 화면 프리징이 발생

  • 업무 프로그램 튜닝

HTTP 오류 발생 리소스

원인

해결 방안

  • 404, 500과 같은 HTTP 오류가 발생하면 화면 처리 속도가 저하됨

  • 원인 : 오류 처리 및 리소스가 캐싱

  • 업무 프로그램 수정

HTTP 1.1 Max Connection 초과시

원인

해결 방안

  • 브라우저별 동일한 도메인으로 맺을수 있는 최대 Connection 수 를 초과

  • 업무 프로그램 튜닝

브라우저 별 동일한 도메인으로 맺을수 있는 최대 Connection 수

브라우저

호스트 당 최대 병렬 연결 개수

IE 6 and 7

2

IE 8

6

IE 9

6

IE 10

8

Firefox 2

2

Firefox 4 ~ 17

6

Opera 9.63

4

Opera 10

8

Opera 11 & 12

6

Chrome 1 & 2

6

Chrome 3

4

Chrome 4 ~ 23

6

Safari 3 & 4

4

브라우저 별 동일한 도메인으로 맺을수 있는 최대 Connection 수

동시에 다수의 HTTP 요청

원인

해결 방안

  • 단일 처리용 서비스를 동시 처리하도록 업무를 변경하면서 발생. (최대 100건 이상의 HTTP 요청이 발생.)

  • 업무 프로그램 튜닝

다수의 JS 파일 요청

원인

해결 방안

  • 공통 JS파일을 지나치게 세분화 하여 발생

  • JS 파일 통합 및 난독화 기능을 형상 관리에 추가

외부 자바스크립트 파일에 대한 HTTP 요청시 성능 부담이 증가합니다.

크기가 25kb인 파일 4개보다 100kb 파일 하나를 더 빨리 내려받기 때문에 외부 자바스크립트 파일 개수를 최소화할 것을 권장합니다.

데이터 튜닝

XML 방식의 대량 데이터 사용

원인

해결 방안

  • IE에서 MSXML Parser가 없어서 발생

  • MSXML Parser 설치

원인

해결 방안

  • XML 파서의 속도 저하 및 메모리 과다 사용으로 발생

  • JSON 방식으로 변경

리소스 중복 로딩

CSS, JS 중복 로딩 오류

원인

해결 방안

  • 동일한 CSS, JS파일을 중복 로딩하여 속도 저하 발생

  • 중복 CSS, JS 로딩 여부 검토

화면 튜닝

TabControl이 여러 Tab을 포함

TabControl 컴포넌트는 여러 화면을 탭으로 구성할 때 사용하는 컴포넌트입니다.

각 탭에 표시하는 화면의 복잡도(컴포넌트의 수)를 바탕으로 브라우저가 그려야 하는 모든 컴포넌트를 고려해야 합니다.

원인

해결 방안

  • 숨겨진 Tab 영역까지 한번에 Rendering

  • 업무 프로그램 튜닝

alwaysDraw 속성은 아래와 같이 TabControl 컴포넌트가 포함하는 모든 탭의 컴포넌트의 렌더링 여부를 결정합니다.

alwaysDraw

alwaysDraw="true"

alwaysDraw="false" (권장)

  • TabControl 컴포넌트가 포함하는 모든 탭의 컴포넌트를 초기 화면 로딩 시 렌더링.

  • 탭 및 컴포넌트 개수에 따라 브라우저의 렌더링 부하가 증가.

  • 초기 로딩 시, 첫번 째 탭의 컴포넌트만 렌더링.

  • 사용자 이벤트 혹은 스크립트 이벤트로 각 탭이 선택되는 시점에 해당 탭의 화면을 렌더링.

alwaysDraw="false" 설정을 사용할 경우, 초기 랜더링 후 첫 번째 탭을 제외한 나머지 탭의 컴포넌트는 객체화가 되지 않습니다.

화면 로딩 속도

원인

해결 방안

  • 메뉴 클릭 또는 CTI inbound call시 화면 전환 속도

  • 공통 및 업무 프로그램 튜닝

외부 컴포넌트 수

원인

해결 방안

  • RD, Web Editor, Chart 등 리소스를 많이 사용하는 외부 컴포넌트를 다수 사용하는 경우

  • 측정 후 외부 컴포넌트 사용을 적절하게 제한

그리드 엑셀 업/다운로드

원인

해결 방안

  • 대용량 엑셀 업/다운로드로 인한 서버 메모리 부족

  • 측정 후 결과에 따른 적절한 메모리 사용량 계산

그리드 컬럼 개수

원인

해결 방안

  • 그리드 컬럼이 100개를 초과 하는 경우

  • 환경에 맞게 적절한 수로 제한

그리드 대량 데이터

원인

해결 방안

  • 10만셀 이상의 데이터를 사용하는 경우

  • 환경에 맞게 적절한 수로 제한

IE 하위 호환성

원인

해결 방안

  • IE 브라우저 하위 호환성을 유지하기 위하여 meta-tag로 구형 브라우저 설정 추가

  • 하위 브라우저만 지원하는 외부 모듈 수정

성능을 고려하지 않은 개발

원인

해결 방안

  • 동시 처리가 가능한 API대신 단일 처리 API를 반복하여 호출하는 경우

  • 업무 프로그램 튜닝

IFrame 개수

원인

해결 방안

  • 지나치게 많은 IFrame 사용

  • IFrame 사용을 적절하게 제한

WFrame 개수

원인

해결 방안

  • WFrame을 너무 많이 분할 해서 로딩 성능 저하

  • WFrame 사용을 적절하게 제한

화면당 컴포넌트 수

원인

해결 방안

  • 화면에 너무 많은 컴포넌트를 사용하는 경우 성능 저하

  • 측정 후 컴포넌트 개수를 적절하게 제한

IE 브라우저에서의 렌더링 속도

속도 저하 원인

IE 브라우저에서 JavaScript 기반 파서(xmlsax.js, xmlw3cdom.js)를 사용할 경우, ActiveX 기반 내장 XML 파서와 비교하여 렌더링 속도가 최대 30배 저하됩니다.

IE 브라우저의 렌더링 속도 저하 원인

(1) JavaScript 기반 파서를 사용하도록 config.xml 파일에 설정된 경우

(2) IE 브라우저의 ActiveX 기반 내장 XML 파서를 사용하지만, 실행 시 오류가 발생한 경우

확인 방법

현재 사용 중인 XML 파서는 WebSquare 로그를 통해 확인할 수 있습니다.

IE 브라우저 화면에서 Ctrl키 + 마우스 우클릭 후 로그보기를 선택하십시오.

WebSquare 로그 보기

권장 – IE11 브라우저 내장 XML 파서 사용

비권장 – JavaScript 기반 파서 사용

해결 방법

config.xml

config.xml 파일에서 useActiveXObject 설정을 확인합니다.

아래와 같이 useActiveXObject="false"인 경우, IE 브라우저는 JavaScript 기반 파서(xmlsax.js, xmlw3cdom.js)를 사용합니다.

$r_title(config.xml 설정 (외부 JS XML 파서를 사용))
<WebSquare>
   <xhr useActiveXObject="false" useResponseXML="true"/> 
</WebSquare>

useActiveXObject 설정을 true로 변경하거나 다음과 같이 해당 설정을 주석으로 처리하십시오.

$r_title(config.xml 설정 (useActiveXObject 설정 주석 처리))
<!-- <xhr useActiveXObject="true" useResponseXML="true"/> -->

클라이언트

JavaScript 기반 파서(xmlsax.js, xmlw3cdom.js) 사용 설정이 config.xml 파일에 없어도, 일부 PC에서 렌더링 속도가 느릴 수 있습니다.

이는 주로 IE 브라우저의 ActiveX 기반 내장 XML 파서에 문제가 있는 경우입니다.

위의 비권장인 경우와 동일한 로그가 확인될 경우 IE 브라우저를 초기화하거나 재설치하십시오.

WebSquare 로그 분석

로그 조회

화면에서 Ctrl + 마우스 오른쪽 버튼을 클릭 후, [로그보기]를 선택합니다.

[Ctrl + 마우스 우클릭]

웹스퀘어 로그 확인 창

$r_title(일반적인 로그 (예))
06:03:00.590 00000 000000 BootLoader] WebSquare Runtime Engine Version : ${version}_${java_version}

06:03:00.591 00001 000001 BootLoader]     Copyright 2009  INSWAVE Systems Co., Ltd. All Rights Reserved.

06:03:00.591 00000 000001 BootLoader] [STEP1][START] Loading Runtime Engine.

06:03:03.635 03044 003045 websquare] import core css : http://demo:8000/100003/BR_MAINB_websquare50/websquare/skin/stylesheet_ext.css

06:03:03.659 00024 003069 websquare] import user css(early_xml) : /dws/cm/css/all.css

06:03:03.659 00000 003069 websquare] import user css(early_xml) : /dws/cm/css/contents.css

06:03:03.659 00000 003069 websquare] import user css(early_xml) : /dws/cm/css/mkt.css

06:03:03.660 00001 003070 BootLoader] [STEP2][ENGINE] Runtime Engine Load complete. Time:3101ms

06:03:03.979 00319 003389 websquare] w2xDocumentRoot[]

06:03:03.979 00000 003389 websquare] w2xHome        [/dws/]

06:03:03.979 00000 003389 websquare] w2xPath        [/dws/mkt1001m01_5.xml]

06:03:03.979 00000 003389 websquare] w2xTheme       [null]

06:03:04.502 00523 003912 BootLoader] [STEP3][RESOURCE] Resource(XML, Skin, CSS, JS) Load Complete. Time:842ms

06:03:05.838 01336 005248 BootLoader] [STEP4][CREATE] WebSquare Object Creation Complete. Time:1336ms

06:03:05.839 00001 005249 websquare] [WebSquare.util.js]script loading[script_ordered_async]

06:03:06.716 00877 006126 websquare] [WebSquare.util.js]script loading[script_ordered_async]

06:03:06.719 00003 006129 BootLoader] controlCollection.toHTML - before

06:03:06.760 00041 006170 BootLoader] controlCollection.toHTML - after

06:03:06.764 00004 006174 BootLoader] [STEP5][RENDER] HTML Rendering Complete. Time:926ms

06:03:06.865 00101 006275 BootLoader] [STEP6][ADJUST] Pre Event Activation. Time:101ms

06:03:06.865 00000 006275 BootLoader] Asynchronous call process

06:03:07.772 00907 007182 BootLoader] [STEP7][EVENT] Event Activation. Time:907ms

06:03:07.821 00049 007231 websquare] WebSquare.layer.showProcessMessage [processing..]

06:03:07.824 00003 007234 websquare] /mkt/mkt1/mkt1001m01/selectBascInfo.do

06:03:07.826 00002 007236 websquare] /session/getSessionData.json

06:03:07.831 00005 007241 websquare] /session/getSessionData.json

06:03:07.832 00001 007242 websquare] /dws/view/com/data/dc_movePageMenuList.json

06:03:07.832 00000 007242 websquare] /memorydata/selectCustMenu.json

06:03:07.833 00001 007243 websquare] /dws/view/com/data/dc_menuList1.json

06:03:07.834 00001 007244 websquare] /dws/view/com/data/dc_menuList2.json

06:03:07.834 00000 007244 websquare] /dws/view/com/data/dc_menuList3.json

06:03:07.835 00001 007245 websquare] /dws/view/com/data/dc_menuList4.json

06:03:07.836 00001 007246 websquare] /session/getSessionData.json

06:03:07.839 00003 007249 BootLoader] [STEP8][EXECUTE] JS load and Global Script Execution Completed. Time:67ms

06:03:07.839 00000 007249 BootLoader] [STEP SUMMARY] Initial procedures completed. Duration : 7280, 시간 요약 : 3101 842 1336 926 101 907 67 7280

06:03:07.857 00018 007267 websquare] ajax complete. Status[200] async[true] url[/session/getSessionData.json]

06:03:07.859 00002 007269 websquare] ajax complete. Status[200] async[true] url[/mkt/mkt1/mkt1001m01/selectBascInfo.do]

06:03:07.995 00136 007405 websquare] ajax complete. Status[200] async[true] url[/session/getSessionData.json]

06:03:07.996 00001 007406 websquare] ajax complete. Status[200] async[true] url[/memorydata/selectCustMenu.json]

06:03:08.006 00010 007416 websquare] ajax complete. Status[200] async[true] url[/dws/view/com/data/dc_movePageMenuList.json]

06:03:08.009 00003 007419 websquare] ajax complete. Status[200] async[true] url[/dws/view/com/data/dc_menuList1.json]

06:03:08.026 00017 007436 websquare] ajax complete. Status[200] async[true] url[/dws/view/com/data/dc_menuList2.json]

06:03:08.038 00012 007448 websquare] ajax complete. Status[200] async[true] url[/dws/view/com/data/dc_menuList3.json]

06:03:08.044 00006 007454 websquare] ajax complete. Status[200] async[true] url[/session/getSessionData.json]

06:03:08.045 00001 007455 websquare] ajax complete. Status[200] async[true] url[/dws/view/com/data/dc_menuList4.json]

06:03:13.804 05759 013214 websquare] testFun.hogaList - 1

06:03:13.806 00002 013216 websquare] convertTypeB - 2 [{"o_FluctAmt21":"-18236","o_RemOfferVol21":"2","hoga":"2","o_RemOfferVol21_Color":"1","hoga_Color":"1","gubun":"5"},{"o_FluctAmt21":"-39898","o_RemOfferVol21":"2","hoga":"2","o_RemOfferVol21_Color":"1","hoga_Color":"1","gubun":"5"},{"o_FluctAmt21":"-80343","o_RemOfferVol21":"2","hoga":"2","o_RemOfferVol21_Color":"1","hoga_Color":"1","gubun":"3"},{"o_FluctAmt21":"-133879","o_RemOfferVol21":"2","hoga":"2","o_RemOfferVol21_Color":"1","hoga_Color":"1","gubun":"3"},{"o_FluctAmt21":"-91962","o_RemOfferVol21":"2","hoga":"2","o_RemOfferVol21_Color":"1","hoga_Color":"1","gubun":"3"},{"o_FluctAmt22":"-39607","hoga":"2","o_RemBidVol22":"2","o_RemBidVol22_Color":"4","hoga_Color":"1","gubun":"3"},{"o_FluctAmt22":"-8555","hoga":"2","o_RemBidVol22":"2","o_RemBidVol22_Color":"4","hoga_Color":"1","gubun":"3"},{"o_FluctAmt22":"-7913","hoga":"2","o_RemBidVol22":"2","o_RemBidVol22_Color":"4","hoga_Color":"1","gubun":"3"},{"o_FluctAmt22":"-27099","hoga":"2","o_RemBidVol22":"2","o_RemBidVol22_Color":"4","hoga_Color":"1","gubun":"5"},{"o_FluctAmt22":"-24134","hoga":"2","o_RemBidVol22":"2","o_RemBidVol22_Color":"4","hoga_Color":"1","gubun":"5"}]

06:03:13.838 00032 013248 websquare] convertTypeB - 3

06:03:13.842 00004 013252 websquare] testFun.hogaList - 2

06:03:13.843 00001 013253 websquare] 39  1 starthogaList fps : 25.6

로그 구조

$p.log()함수를 이용한 로그는 다음과 같은 형태로 생성됩니다.

$r_title(로그 형태)
시간 이전로그출력후경과시간 첫로그출력후경과시간 로그출력기] 로그메시지

06:03:13.838 00032 013248 websquare] convertTypeB 라는 로그는 아래와 같이 해석할 수 있습니다.

로그 해석

No.

항목

설명

1

로그생성시간

06:03:13.838

2

이전로그출력 후 경과시간

32ms

3

첫로그출력 후 경과시간

13.248s

4

로그출력기

websquare

5

메시지

convertTypeB

Step 로그

$r_title(Step 로그 (예))
06:03:00.591 00000 000001 BootLoader] [STEP1][START] Loading Runtime Engine.

06:03:03.660 00001 003070 BootLoader] [STEP2][ENGINE] Runtime Engine Load complete. Time:3101ms

06:03:04.502 00523 003912 BootLoader] [STEP3][RESOURCE] Resource(XML, Skin, CSS, JS) Load Complete. Time:842ms

06:03:05.838 01336 005248 BootLoader] [STEP4][CREATE] WebSquare Object Creation Complete. Time:1336ms

06:03:06.764 00004 006174 BootLoader] [STEP5][RENDER] HTML Rendering Complete. Time:926ms

06:03:06.865 00101 006275 BootLoader] [STEP6][ADJUST] Pre Event Activation. Time:101ms

06:03:07.772 00907 007182 BootLoader] [STEP7][EVENT] Event Activation. Time:907ms

06:03:07.839 00003 007249 BootLoader] [STEP8][EXECUTE] JS load and Global Script Execution Completed. Time:67ms

06:03:07.839 00000 007249 BootLoader] [STEP SUMMARY] Initial procedures completed. Duration : 7280, 시간 요약 : 3101 842 1336 926 101 907 67 7280

오류 로그

$r_title(오류 로그 (예))
19:27:23.626 00000 007026 websquare] 작업 도중 오류가 발생하였습니다.

Exception Occured at element id [tradingGridView2]

* message     : Cannot read property 'getRealRowIndex' of undefined

* stack

   TypeError: Cannot read property 'getRealRowIndex' of undefined

   at WebSquare.uiplugin.gridViewApiController.getCellStyleClassName (eval at <anonymous> (http://demo:8000/100003/BR_MAINB_websquare50/websquare/bootloader.js:267:28), <anonymous>:3931:35)

   at WebSquare.uiplugin.styleController.setCellStyle (eval at <anonymous> (http://demo:8000/100003/BR_MAINB_websquare50/websquare/bootloader.js:267:28), <anonymous>:395:32)

   at Object.PageFunc.setCurrentBorder (<anonymous>:613:50)

   at Object.PageFunc.convertTypeB (<anonymous>:852:26)

   at Object.PageFunc.Realmkt1001m01_applyRTSData (<anonymous>:1003:34)

   at Object.testFun.hogaList (<anonymous>:1701:30)

   at Object.testFun.starthogaList (<anonymous>:1733:29)

   at Object.testFun.testStart (<anonymous>:1713:29)

   at <anonymous>:1:9

* caller

   at WebSquare.uiplugin.gridViewApiController.prototype.getCellStyleClassName

   at WebSquare.uiplugin.styleController.prototype.setCellStyle

   at function ()

   at function (data)

   at function (data)

   at lOfferVol_

   at function ()

   at function ()



<<Internal JavaScript Source Block [/dws/mkt1001m01_9.xml][0] Start>>

[/dws/mkt1001m01_9.xml:0001]

[/dws/mkt1001m01_9.xml:0002]

[/dws/mkt1001m01_9.xml:0003]            

[/dws/mkt1001m01_9.xml:0004]            // page function object

[/dws/mkt1001m01_9.xml:0005]            FFFF = 0;

[/dws/mkt1001m01_9.xml:0006]            var PageFunc  = $.extend({}, PageFunc );

[/dws/mkt1001m01_9.xml:0007]


… 생략 ...


[/dws/mkt1001m01_9.xml:0611]                            } else {

[/dws/mkt1001m01_9.xml:0612]                                tradingGridView.setCellStyle (i-5 , 2 , 'border', '');

[/dws/mkt1001m01_9.xml:0613]                                tradingGridView2.setCellStyle(i   , 2 , 'border', '');

[/dws/mkt1001m01_9.xml:0614]                                

[/dws/mkt1001m01_9.xml:0615]                                if( i == 11) {

[/dws/mkt1001m01_9.xml:0616]                                    tradingGridView.setCellStyle (5  , 2 , 'border-bottom', '');

[/dws/mkt1001m01_9.xml:0617]                                    tradingGridView2.setCellStyle(10 , 2 , 'border-bottom', '');

[/dws/mkt1001m01_9.xml:0618]                                }

[/dws/mkt1001m01_9.xml:0619]                            }

점검 목록

성능 관련 문제가 발생할 경우 아래 내용을 확인하십시오.

일반

JavaScript

번호

확인 사항

설명

1

JavasScript 함수 작성 시 지역 변수를 적절하게 사용했는가?

함수 내에서 변수 선언시 var 선언을 생략하면 전역변수로 생성되어 메모리 과다 점유 현상 발생.

2

JavasScript 로직 내에 오류가 발생하는가?

JavaScript 내 오류 발생시 화면 렌더링 속도 저하.

스크립트 수행이 중지되어 함수가 정상적으로 종료되지 않는 경우가 있는지 확인.

3

XML 파일 내의 업무 로직 처리를 위한 JavasScript 분량이 적절한가?

하나의 XML 파일 내의 JavasScript 양이 많을 경우 렌더링 속도 저하.


  • 권장:

    • 공통으로 사용되는 함수 등은 xml 파일 별로 넣지 않고 별도의 .js 파일로 분리하여 관리.

4

offsetHeight() 등 상대적으로 느린 기능의 과도한 사용하고 있지 않은가?

상대적으로 느린 기능의 과도한 사용을 지양.

5

반복적으로 발생하는 이벤트를 적절하게 처리하고 있는가?

onresize, onscroll 등의 일부 이벤트는 반복적으로 수십번 이상 이벤트 핸들러를 호출하므로 적절한 처리가 필요함.

6

부적절한 반복문은 없는가?

  • 권장:

    • 반복과 관련이 없는 변수 등은 외부로 이동.

    • 반복문 내부에서 break, continue 문을 적절히 사용.

7

변수 및 함수가 SPA 방식에 맞게 선언되어 있는가?

올바르지 않은 변수 및 함수 선언은 메모리 누수를 초래 수 있음.


  • 잘못된 변수 선언 예시

    • var status = “S”;

    • rowCount = dataList1.getRowCount();

    • function updateData() { …

통신

번호

확인 사항

설명

1

sync 및 async 통신 방식이 적절히 사용되었는가?

sync 방식은 완료시까지 스크립트 정지 현상이 발생하므로 async 방식을 권장.

2

통신 관련 공통 함수가 객체 대신 string을 사용했는가?

callback 함수에 객체 형태로 전달하면 메모리 누수 발생.


  • 예시

    • IE8 브라우저 다운현상이 발생할 수 있음

3

통신 객체의 동적 생성시 중복 문제는 없는가?

동일한 ID로 중복하여 생성할 경우 메모리 누수 발생.

4

통신 함수의 응답 객체 사용에 문제가 없는가?

통신 함수에서 사용자 함수 호출 시 응답 결과가 다른 곳과 참조 관계가 없는지 확인.

네트워크

번호

확인 사항

설명

1

Keep-Alive 관련 설정을 확인했는가?

Keep-Alive 기능의 활성화/비활성가 무조건 성능에 긍적적인 영향을 주지는 않음.

반드시 해당 프로젝트의 여건에 따라서 서버 관리자와 협의 후 적용 여부를 결정할 것을 권장.

2

텍스트 형식(HTML, XML, CSS, JavaScript, JSON 등)의 자원을 gzip으로 압축해서 전송하고 있는가?

텍스트 형식의 자원을 gzip 압축 전송할 경우, 전송 데이터의 크기를 줄여 네트워크 속도를 개선할 수 있음.

CSS

번호

확인 사항

설명

1

웹 폰트 사용시 부모 페이지 외의 자식 페이지에서 웹 폰트를 중복 호출하지 않는가?

중복 호출 시 IE8 등에서 백화 현상 또는 웹 폰트 미적용 현상 발생.

2

IE8을 지원해야 하는 프로젝트에서 웹 폰트를 사용하는가?

IE8의 경우 웹 폰트를 내려 받기 위한 브라우저 처리 속도가 현저하게 느려지기 때문에 웹 폰트 사용을 권장하지 않음.


  • 예시

    • ie8 + iframe + 웹 폰트 사용시 백화 현상이 발생할 수 있음

3

무거운 웹 폰트를 사용하는가?

IE에서 웹 폰트를 사용할 경우 브라우저 메모리 사용량이 크게 증가함.

특히 IE에서 웹 폰트를 다수의 IFrame 및 Window Popup과 병행하여 사용할 경우, 메모리의 과도한 사용과 누수가 발생하여 속도 저하, 백화 현상, 브라우저 비정상 종료 현상으로 이어질 수 있음.


  • 권장:

    • 무거운 웹 폰트의 사용은 지양할 것을 권장.

4

border-radius 속성을 과도하게 사용하지는 않는가/

과도한 border-radius 속성의 사용은 구형 스마트폰 기기에서 문제를 발생시킬 수 있음.

jQuery

번호

확인 사항

설명

1

jQuery 1.7.2를 사용하는가?

해당 버전 사용시 메모리 누수 발생

2

jQuery 사용 시 $... 기능을 사용하고 있는가?

WebSquare에서 제공하는 최적화된 $p.$... 기능 사용을 권장.

메모리 누수

번호

확인 사항

설명

1

메모리 누수를 야기할 수 있는 잠재적 요인들을 확인했는가?

대표적인 메모리 누수 요인을 확인.

  • Closure 내부 개체 참조로 인한 메모리 누수.

  • Top 영역에 무분별하게 데이터를 계속 저장하여 발생하는 메모리 누수.

  • 부적절한 jQuery 사용으로 개체가 해제되지 발생하는 메모리 누수.

2

JavaScript Exception으로 인한 메모리 누수는 없는가?

WebSquare 로그를 조회하여 JavaScript Exception이 발생하는 코드가 존재할 경우, 로직 보완 및 예외 처리가 필요함.

Exception이 발생할 경우 화면 로딩 속도가 저하됨.

3

팝업으로 인자 전달 시에 객체 대신 string 을 사용했는가?

객체 형태로 전달시 메모리 누수 발생.

4

데이터 객체의 동적 생성시 중복 문제는 없는가?

동일한 ID로 중복하여 생성할 경우 누수 발생.

5

변수 및 함수가 SPA 방식에 맞게 선언되어 있는가?

올바르지 않은 변수 및 함수 선언은 메모리 누수를 초래 수 있음.


  • 잘못된 변수 선언 예시.

    • var status = “S”;

    • rowCount = dataList1.getRowCount();

    • function updateData() { …

6

통신 관련 공통 함수가 객체 대신 string을 사용했는가?

callback 함수에 객체 형태로 전달하면 메모리 누수 발생.


  • 예시

    • IE8 브라우저 다운현상이 발생할 수 있음

7

통신 객체의 동적 생성시 중복 문제는 없는가?

동일한 ID로 중복하여 생성할 경우 메모리 누수 발생.

8

jQuery 1.7.2를 사용하는가?

해당 버전 사용시 메모리 누수 발생

9

IFrame이 야기할 수 있는 메모리 누수 가능성을 확인했는가?

대표적인 메모리 누수 원인을 확인.

  • IFrame 사이에서 개체 참조 시

  • IFrame 사이에서 데이터 전달 시

  • IFrame 사이에서 함수 호출 시 인자로 개체를 전달하는 경우

  • 다른 IFrame 영역의 DOM에 동적으로 이벤트를 생성할 경우

10

데이터 객체의 동적 생성시 중복 문제는 없는가?

동일한 ID로 중복하여 생성할 경우 메모리 누수 발생.

11

외부 모듈 연계 시 해당 외부 모듈의 메모리 누수 여부를 확인했는가?

브라우저 전체 메모리에 영향을 미침.

12

Fasoo DRM 모듈을 사용하는가?

Fasoo DRM Client for LIG NoNX Wix, FSW Client 류의 제품들이 메모리 사용시 팝업을 열고 닫을 때 메모리 사용량이 증가할 수 있음.

13

외부 라이브러리를 점검했는가?

  • Import된 외부 라이브러리의 메모리 점유량 확인.

  • IFame, Window Popup, SPA 방식이 메모리 누수를 야기하는지 확인.

기타

번호

확인 사항

설명

1

공통코드, 공통 메시지 등 업무 공통 데이터가 시스템 로딩 초기에 일괄적으로 메모리에 적재되는가?

소규모 프로젝트에서는 이런 방식의 데이터 적재 패턴이 별다른 문제를 야기하지 않을 수도 있음.

단, 중대형 프로젝트 이상에서는 심각한 문제의 원인이 될 수 있으므로 설계자와 협의하여 업무에 적합한 업무 공통 데이터 로딩 방식을 적용해야 함.

2

SSL 환경에서 멀티업로드(플래시 모드)를 사용하는가?

업로드 성능이 저하될 수 있음.


  • 권장:

    • html5_transparent 모드를 권장. (IE 10 이상만 사용 가능)

3

캐시가 적절히 사용되고 있는지 확인했는가?

(웹스퀘어 환경설정, 웹 서버 만료일 설정, 브라우저의 캐시 설정 등)

웹스퀘어 config.xml 내의 캐시 관련 설정(웹서버 만료일, 브라우저 캐시 설정 등 관련)이 웹페이지 속도에 큰 영향을 미침.

개발/운영 환경에 따라 웹스퀘어 config.xml 내의 캐시 관련 설정이 적용되어 있는지와 서버 설정을 확인.


  • 예시

    • 웹스퀘어 환경설정, 웹 서버 만료일 설정, 브라우저의 캐시 설정 등

4

(갤럭시노트3, 갤럭시탭10.1과 같은) 특정 모바일 장비를 사용하는가?

해당 장비에서는 자체 xml parser가 내장되어 있지 않음.

config.xml에서 <userAgentPattern XPathParser="Android 4.3[\w\W]+N900|Android 4.3[\w\W]+E300|Android 4.4"/>을 추가.

/websquare/externalJS/xpath/xpath.js를 로딩하여 사용 시 성능저하 발생.


  • 권장:

    • 근본적인 처리 방안 없음으로 가급적 해당 기기를 사용하지 않을 것.

5

MDI(WindowContainer) 사용시 화면 중복 오픈 금지 및 오픈 창 개수 제한이 있는가?

MDI 개별 Window가 지나치게 많아지게 될 경우 메모리 과다로 인해 화면 조작이 불가.

컴포넌트

일반

번호

확인 사항

설명

1

컴포넌트 및 함수의 네이밍은 문제가 없는가?

가이드된 네이밍 규칙을 준수하는지 확인.

Generator

번호

확인 사항

설명

1

대량 건을 반복해서 출력하고 있는가?

렌더링 되는 DOM 개수가 많아지면 그만큼 성능 저하가 발생.

실제로 화면에 출력되는 영역만 렌더링하고, 출력되지 않는 영역은 아예 DOM 자체가 생성되지 않도록 처리하는 것을 권장.


  • 사례

    • WFrame이 포함된 컴포넌트를 Generator로 500건 이상 그렸을 때 초기 로딩 속도 10초 이상 소요됨. WFrame을 500개 그리는 대신 1개만 사용하여, 사용자가 클릭할 때마다 wframe.setSrc() 함수를 호출하는 구조로 변경하여 초기 로딩 속도를 3초까지 줄일 수 있었음.

GridView

번호

확인 사항

설명

1

GridView의 각 셀에 서로 다른 서식을 적용하고 있는가?

GridView의 formattersetCellColor(), setCellBackgroundColor() 함수 등을 빈번하게 실행할수록 화면 렌더링 속도 및 스크롤 속도가 느려질 수 있음.


  • 권장:

    • 서버에서 서식을 처리하여 내려주는 것을 권장.

    • 한행의 셀 표현 방식을 변경하기 위해 formatter류가 많이 사용된 경우 랜더링 속도에 영향을 줄 수 있으므로, 행이 많은 경우는 서버에서 서식을 적용하는 것을 권장.

2

setCellStyle() 함수를 남용하고 있지 않은가?

셀 단위로 스타일을 적용하기 보다는, 행 단위 혹은 컬럼 단위로 스타일을 적용할 것을 권장.

3

드릴다운 기능을 사용할 경우 컬럼 혹은 데이터가 너무 많지는 않은가?

컬럼이나 데이터가 너무 많은 경우 기하급수적인 성능 저하가 발생할 수 있음.

4

visibleRowNum 속성값이 너무 크게 설정되어 있지 않은가?

visibleRowNum 속성값이 "all" 혹은 "100" 이상으로 설정하고 GridView와 바인딩된 DataList가 많은 데이터를 포함할 경우, GridView에 모든 데이터가 렌더링되어 CPU 및 메모리 과다 사용이 발생할 수 있고, 그 결과 속도 저하, 백화 현상, 브라우저 비정상 종료가 초래될 수 있음.


  • 권장:

    • visibleRowNum 속성은 업무 화면에서 스크롤이 생기지 않는 수준에서 설정하거나 "100" 미만으로 설정할 것을 권장.

5

customerFormatter 혹은 typeGetter 속성값으로 지정된 함수가 DataList 혹은 UI를 조작하는 함수를 호출하고 있는가?

customFormatter 혹은 typeGetter가 GridView와 바인딩 된 DataList를 조작할 경우, GridView가 갱신되면서 customFormattertypeGetter가 다시 호출되어 무한 루프에 빠질 수 있음.

GridView UI 조작 API 중에서도 GridView를 갱신하여 customFormattertypeGetter를 다시 호출하도록 유도하는 API가 있으므로 가급적 이를 피해야 함.


  • 피해야 할 대표적인 API

    • DataList

      • setCellData()

    • GridView

      • setCellReadOnly()

      • setRowVisible()

6

렌더링 되는 컬럼의 갯수(<td> 태그)가 과도하게 많지는 않은가?

렌더링 되는 <td> 태그의 수는 "컬럼 수 * 현재 화면에 표시된 행의 수"와 동일.

메모리 사용량 및 렌더링 시간은 <td> 태그의 수에 비례함.

senseReader=”true” 또는 visibleRowNum=”all” 속성 설정을 사용할 경우 특히 주의해야 함.

IFrame

번호

확인 사항

설명

1

전체 Layout 화면 구성 시 IFrame 개수가 지나치게 많이 사용되지는 않았는가?

IFrame 사용 개수가 많아지면 화면 렌더링 속도 저하 및 메모리 과다 점유 현상 발생 가능.


  • 권장:

    • 가능한 IFrame을 WFrame로 변환해서 처리할 것을 권장.

2

IFrame이 야기할 수 있는 메모리 누수 가능성을 확인했는가?

대표적인 메모리 누수 원인을 확인.

  • IFrame 사이에서 개체 참조 시

  • IFrame 사이에서 데이터 전달 시

  • IFrame 사이에서 함수 호출 시 인자로 개체를 전달하는 경우

  • 다른 IFrame 영역의 DOM에 동적으로 이벤트를 생성할 경우

Submission

번호

확인 사항

설명

1

sync 통신을 무분별하게 사용하고 있지 않은가?

sync 방식으로 서비스를 요청할 경우, 데이터 응답이 반환될 때까지 브라우저가 다른 작업을 수행할 수 없기 때에 사용자 경험에 영향을 줌. 특히, 서버 응답이 지연되거나 데이터 양이 큰 경우, 사용자 경험을 크게 손상시킴.

TabControl

번호

확인 사항

설명

1

TabControl 사용 시 다수의 탭을 사용하는가?

다수의 탭 중 닫혀진 탭 내부 컨텐츠를 렌더링 하는 방법이 alwaysDraw="true"인 경우, 최초 로딩 시 다수의 탭이 있을수록 화면 로딩 속도가 저하.


  • 권장:

    • alwaysDraw="false"를 사용하여 탭이 선택될 때 로딩하는 방식 권장.

2

TabControl 내부 Content 항목의 화면 구성 시 별도의 xml 파일 연결을 확인했는가?

TabControl 내부 Content 항목이 IFrame을 포함하여 IFrame이 중복으로 사용되는 현상 발생.



  • 권장:

    • TabControl 내의 content 속성 중 src 에서 별도 xml 파일을 선택하도록 해야함.

3

alwaysDraw="true" 속성 설정을 사용하고 있는가?

alwaysDraw="true" 사용 지양.

팝업

번호

확인 사항

설명

1

Window 팝업을 alert이나 IFrame형태의 팝업으로 처리했는가?

2단 팝업 등에서 메모리 미반환 현상 발생.

2

팝업(프레임)간 Object를 전달하고 있는가?

타 프레임에 Primitives형(예: String)이 아닌 Object를 직접 전달하고 있는지 확인.

3

팝업으로 인자 전달 시에 객체 대신 string 을 사용했는가?

객체 형태로 전달시 메모리 누수 발생.

데이터

번호

확인 사항

설명

1

대용량 Excel 다운로드가 있는가?

클라이언트 환경에서 데이터를 수집하고 서버로 보내는 경우 시간이 오래 소요될 수 있음.


  • 권장:

    • 서버에서 데이터를 직접 처리하도록 Provider를 사용할 것을 권장.

2

DataCollection의 Data를 처리하기 위한 API들이 효율적으로 사용되었는가?

Data 처리를 비효율적으로 API를 반복하여 사용하는 경우 성능 저하 발생


  • 예시

    • 1개의 행 변경하기 위해 setRowJson()을 사용하지 않고 각 셀에 대해 setCellData()를 반복적으로 사용

3

대량 Data 처리시 Data 구조체를 적절하게 사용했는가?

array > json > xml 순으로 속도 처리가 빠름

4

데이터 객체의 동적 생성시 중복 문제는 없는가?

동일한 ID로 중복하여 생성할 경우 메모리 누수 발생.

5

대량의 데이터를 적재하고 있는 DataList의 데이터 값을 읽거나 수정할 때, 적절한 코드를 사용하는가?

값을 수정할 경우, DataList에 바인딩 되어 있는 GridView나 LinkedDataList가 많을 수록 영향도가 증가함.


  • 권장:

    • setCellData(), getCellData(), getAllJSON() 등의 호출 패턴 검토가 필요함.

    • setBroadcast() 함수 활용.

외부 연동

번호

확인 사항

설명

1

외부 모듈 연계시 document mode(DocType)의 지원 범위가 표준을 준수하는가?

외부모듈 지원 시 DocType이 비표준 모드인 경우, 브라우저 하위 버전으로 설정되어 렌더링 속도에 영향을 미침.

2

외부 모듈 연계 시 해당 외부 모듈의 메모리 누수 여부를 확인했는가?

브라우저 전체 메모리에 영향을 미침.

3

외부 모듈(bootstrap 사용시)의 버전 별 메모리 해제 여부를 확인했는가?

bootstrap-2.1.1.min.js 해당버전이 메모리 해제가 되지 않는 문제 발생.

4

Fasoo DRM 모듈을 사용하는가?

Fasoo DRM Client for LIG NoNX Wix, FSW Client 류의 제품들이 메모리 사용시 팝업을 열고 닫을 때 메모리 사용량이 증가할 수 있음.

5

외부 라이브러리를 점검했는가?

  • Import된 외부 라이브러리의 메모리 점유량 확인.

  • IFame, Window Popup, SPA 방식이 메모리 누수를 야기하는지 확인.