JS Merge , Compress
최초 접속 후 다운로드시 nexacro platform의 javascript framework을 merge 및 Compress를 하지 않을 경우 네트워크 상황에 따라 초기 로딩시간이 소요됩니다.
Merge 및 compress를 하므로 인하여 파일 사이즈를 줄일 수 있고, 요청 파일 수가 감소하므로 네트워크 비용을 감소 시킬 수 있습니다.
운영시 nexacro platform의 javascript framework은 필수적으로 하여야 하며, 업무소스 또한 가급적 권장합니다.
개발툴에서의 적용 방법
■ 개발툴 > 메뉴 > Build > Deploy > Deploy Module
■ Deploy될 디렉토리 선택 및 merge JSON , Compress JS file 체크
■ Deploy된 파일을 서버에 업로드하고 해당 경로에 맞게 초기 페이지 수정
- 런타임(start.json)
-
- HTML(index.html)
_
확인 방법
■ 패킷분석 툴(Fiddler) 또는 웹브라우즈의 개발자 도구에서 적용 후의 요청 파일 및 사이즈를 확인 합니다.
-
`
Compiled JS는 런타임에서만 해당되며 보안측면을 고려할 때는 Compiled JS를 권장.
메뉴의 Deploy Application를 선택하면 전체 프로젝트를 할 수 있으며, Deploy File을 선택하여 개별파일로 진행 가능.
제품 폴더의 nexacrogenerator.exe , nexacrocompressor.exe 파일을 이용하여 배치 형태로 작업이 가능.(Windows 계열 Machine에서만 가능)
기본 Merge시 5개 파일이 생성되나 json 파일을 수정하여 1개의 파일로 변경도 가능.이 경우 gzip처리가 되지 않으면 1개 파일의 사이즈가 커지므로 최초 접속 시 다수의 파일을 동시에 다운로드 받는 것 보다 단점을 가지고 있으나 캐쉬처리된 후는 효율적일 수 있음.
gzip 압축
gzip은 파일 압축에 쓰이는 응용 소프트웨어이다.
gzip은 GNU zip의 준말이며, 초기 유닉스 시스템에 쓰이던 압축 프로그램을 대체하기 위한 자유 소프트웨어이다. gzip은 Jean-loup Gailly와 마크 애들러가 만들었다.
버전 0.1은 1992년 10월 31일에 처음 공개되었으며, 버전 1.0이 1993년 2월에 뒤따라 나왔다.
오픈BSD의 gzip 버전은 더 오래된 압축 프로그램을 기반으로 하고 있으며, 오픈BSD 3.4에 추가되었다
최신의 대부분의 브라우저가 지원하는 압축 방식인 gzip을 사용하여 패킷 사이즈를 절감합니다.
gzip은 약 70%까지의 압축률을 보이며, 이미 압축되어 있는 image의 경우는 비효율적이며 텍스트 형식의 js 파일만 처리하도록 합니다.
압축은 서버 설정으로 가능하나 압축에 따른 서버 비용이 증가되므로 서버 성능테스트등이 병행되어야 합니다.
<Tomcat 기준 : Server.xml] Connector compressableMimeType="application/javascript,text/xml,text/html,text/javascript,application/xml ,text/css,application/x-javascript,text/json,application/json,text/plain" compression="off" URIEncoding="UTF-8" useSendfile="false" compressionMinSize="2048" connectionTimeout="20000" port="8080" ...중략 ... compression="off" 를 on으로 설정시 압축되어서 속도 향상이 가능합니다.
각 서버(WAS)별 설정방법은 해당 밴더사에 문의하시기 바랍니다.
gzip 설정 시 서버 비용이 증가되므로, 미리 gzip된 파일을 다운로드 할 수도 있습니다. 이 경우 서버측에서 지원이 가능한 지 확인이 필요하며, http://playnexacro.com/ 에서 gzip으로 검색하시어 참고하시기 바랍니다.
참고 : WebToB 미리 압축된 파일에 대한 환경설정파일 *HEADERS gzip action="AddResponse", FieldName="Content-Encoding", FieldValue = "gzip", RegExp="W.(js.gz)$" <--------- 정규식을 이용하여 처리
기본 | merge/compress | gzip(merge+) | |
---|---|---|---|
size (MB) | 7.5 | 5.5 | 0.85 |
파일갯수 | 66 | 5 | 5 |
미리 압축된 파일을 전송
gzip 파일을 미리 생성 시 서버에서 gzip으로 압축하는 비용을 줄일 수 있습니다. 다만, 파일 변경 시 변환 작업이 필요하므로 관리 팩터가 늘어 납니다.
gzip 파일 생성은 unix machine에서 재공하는 gzip 명령 또는 윈도우 utility(Window에서도 재공)를 사용하여 가능합니다. (예시 2-1)
gzip 설정 시 서버 비용이 증가되므로, 미리 gzip된 파일을 다운로드 할 수도 있습니다. 이 경우 응답헤더에 Content-Encoding type을 gzip으로 명시하여야 합니다.
응답헤더에 명시하는 방법은 서버설정 (예시 2-2)으로 하는 방법과 url filter를 이용하는 방법 (예시 2-3)가 있습니다.
[예시 2-1] [예시 2-2]
WebToB 예 -- 미리 압축된 파일에 대한 환경설정파일 *HEADERS gzip action="AddResponse", FieldName="Content-Encoding", FieldValue = "gzip", RegExp="W.(js.gz)$" <--------- 정규식을 이용하여 처리
[예시 2-3]
web.xml <filter-mapping> <filter-name>gzipFilter</filter-name> <url-pattern>*.gz</url-pattern> </filter-mapping> <filter> <filter-name>gzipFilter</filter-name> <filter-class>com.gzip_nc.gzip_nc</filter-class> </filter>
package com.gzip_nc; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class gzip_nc implements Filter{ @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) arg0; HttpServletResponse httpResponse = (HttpServletResponse) arg1; httpResponse.addHeader("Content-Encoding", "gzip"); arg2.doFilter(httpRequest, httpResponse); } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } }
- 응답 헤더에 encoding type 처리 확인
KeepAlive
KeepAlive는 HTTP1.1 스펙으로 서버와 한번 맺은 세션(Connection)을 지속할 지 여부로 전용 브라우저 및 웹브라우저는 기본적으로 request시 KeepAlive를 사용합니다.
KeepAlive를 사용한다고 요청하더라고 서버에서 사용하지 않도록 설정하면 요청시 마다 새로운 세션을 생성합니다.(예시 3-1)
이미 열려있는 세션을 활용하므로 세션오픈,종료에 따른 시간을 단축 할 수 있으나 유지할 경우 서버측 관리비용은 증가될 수 있습니다. 다만, 상당수의 웹서버는 KeepAlive를 on하여 사용 중에 있습니다.
네트워크 환경이 좋은 경우 크게 의미가 없으나, 그렇지 못할 경우 KeepAlive사용 여부를 확인 할 필요가 있습니다.
SSL 통신을 사용할 경우 커넥션시 마다 인증처리를 하므로, KeepAlive는 의미가 있습니다.
[예시 3-1] - 요청(request)시에는 Connection:keep-alive로 하나 close로 응답(response)하고 있습니다. - 이 경우는 서버 설정으로 keep-alive를 off한 상태라고 볼 수 있습니다.
런타임의 경우 xadl의 usehttpkeepalive속성으로 변경이 가능합니다. 다만 framework js등은 기본적으로 keepalive로 동작됩니다.
ETag
HTTP1.1에서 파일 캐쉬의 비교 대상은 ETag 또는 Last-modified입니다.
ETag는 Last-modified와 같이 브라우저의 캐쉬에 저장된 파일과 웹서버의 파일이 서로 일치하는 지를 식별하기 위한 방법중에 하나입니다.
ETag는 node,time,size등의 조합으로 구성하며, 서버측의 설정으로 조합의 변경이 가능합니다.
ETag를 사용하고 여러대의 웹서버를 운영 중이라면,각 서버의 동일파일의 ETag값이 다른 경우 의도치 않게 다시 다운로드 받는 현상이 발생됩니다.
여러대의 서버를 운영 중이라면 각 서버의 ETag값이 동일한 지 확인을 하여야 하며, 다를 경우 ETag 설정 조합 방법을 변경하는 등 서버 담당자의 확인이 필요합니다.
- ETag는 Header 정보에 포함되어 전송 및 수신 됩니다.
각 서버별 동일한 파일에 대하여 동일한 ETag 값을 보장 할 수 없다면 ETag를 제거하고 Last-modified만을 사용하여도 됩니다.
ETag를 사용하지 않는 윈도우 서버의 경우 최신파일에서 이전파일로 변경(원복)되는 경우 일자가 이전으로 변경되어 브라우저로 접속 시 갱신이 되지 않을 수 있으므로 주의가 필요하며 이와 같은 문제는 반대로 ETag설정으로 해결될 수 있습니다.
초기 로딩 js 파일 최적화
기본적으로 설정된 framework js중 실제 업무에서 사용하지 않는 파일은 확인 후 목록에서 제거 합니다.
일부 몇개의 기능 또는 몇개의 화면을 위하여 다수의 기능이 포함된 외부 라이브러리를 포함하고 있다면, 필요한 요소만 선별하여 추가하거나 대상 화면에서만 처리할 수 있도록 고려 하여야 합니다.
[예시 5-1] - framework library중 프로젝트에서 사용하지 않는 Component등이 포함되어 있을 수 있습니다.
소스 merge를 하게되면, 파일사이즈등은 크게 차이가 없어 몇개의 js파일은 빼는 것이 성능을 개선할 수 있는 큰 팩트는 아닐 수 있으나 이를 파싱하는 단계들을 줄이고, 작지만 요소 요소의 튜닝 팩트를 모우는 것이 필요합니다.
Cache Level
Cache Level은 개발 툴(type definition)에서 설정이 없다면 기본이 “dynamic”입니다. (예시 6-1)
개발중에는 많은 수정이 이뤄지므로 “dynamic”이 적절할 수 있으나 사용자 테스트용 서버등에는 “session”으로 설정하여 운영 전 최종모습으로 테스트할 수 있도록 합니다. 간혹, 업무화면에 따라, 시점을 고려치 않고 작업이 되어 “session”으로 변경 후 오동작 현상이 있을 수 있으므로 사전에 확인이 될 수도 있습니다.
최종 운영서버에는 가급적 화면 및 공통 함수, 이미지 영역은 “session”으로 지정하도록 합니다.
화면별 다수의 공통함수를 include하는 방식은 비권장사항이나, 화면별 공통함수를 include한다면 해당 공통함수는 성능에 많은 영향을 끼치므로 "session" cache를 사용하여야 합니다.
[예시 6-1] - 설정하지 않을 경우 cachelevel은 "dynamic"임을 인지 하셔야 합니다.
데이터 통신을 위한 서비스용 URL은 반드시 "none"으로 설정하셔야 합니다.
Static Cache는 런타임에서만 사용 가능하며, 해외와 같이 네트워크 상황이 좋지 못한 경우 유용할 수 있습니다. 다만, Static Cache는 버전 관리를 수작업으로 하기에 관리비용이 증가됩니다.
tracemode
trace(console.log등)는 디버깅을 하기 위한 좋은 수단이나 불필요하게 많은 로그를 남길 경우 성능에 영향을 줍니다.
운영서버에서는 trace를 off 하는 것을 권장합니다. (예시 7-1)
trace를 off하더라도 trace내부의 구분에 성능 저하를 유발할 수 있는 코드가 있다면 trace문 자체를 막도록 하여야 합니다. (예시 7-2)
결론적으로 테스트가 완료되면, 운영시스템에서는 trace off는 물론 불필요한 trace구문은 제거하도록 합니다.
[예시 7-1] - tracemode를 "none"으로 변경 (runtime) application.trace = function() {}; - trace 재정의예 (HTML)
[예시 7-2]
trace(this.Dataset01.rowcount); //== 10000 trace(this.Dataset01.saveXML()); // trace를 off하더라도 내부 구문은 수행됨
통신 방식
업무 화면에서 서비스를 동기,비동기를 선택하여 호출할 수 있으며 송수신되는 패킷의 형태를 변경하여 처리 할 수 있습니다.
nexacro platform에서 지원하는 데이터 통신방식은 XML, SSV, Binary(런타임전용)이 있습니다.
동기, 비동기 통신
동기(Synchronous) 통신은 통신요청 후 메인 thread가 Locking되고 응답 후 후처리를 합니다. 이로 인하여 통신 요청 후 응답이 오기까지 어플리케이션(전용브라우저,웹브라우저)는 대기상태로 빠집니다.
화면이 로딩될 때 동기통신을 사용하게 되면 서비스 응답시간이 지연될 경우 화면을 미리 그리지 못하고 대기상태로 빠지는 백화현상이 발생됩니다.
이로 인하여 화면이 로딩되는 시점, 즉 form의 oninit 또는 onload에서는 반드시 비동기통신을 사용하도록 권장합니다.
비동기통신과 동기통신이 동시에 요청되는 경우에는 동기통신의 응답결과에 영향을 받습니다.
동기통신을 사용시에는 움직이는 이미지등 대기 이미지가 보여지지 않는 현상이 발생하므로, 가급적 비동기를 권장합니다.
1초가 소요되는 서비스를 5개씩 동기로 요청하는 것보다는 5개를 비동기로 요청하는 것이 더 빠른 응답결과를 가질 수 있을 것이며, 이를 상황별로 묶어(1개~2개등으로) 비동기통신으로 요청하는 것이 더 효율적일 것입니다. (예시 8-1)
[예시 8-1] - 동기 통신일 경우 서비스 응답이 완료된 이후 다음 서비스가 호출
데이터 통신 (SSV)
nexacro platform은 XML, SSV, Binary(런타임전용) 통신을 지원합니다.
개발자 도구 또는 네트워크분석툴등으로 확인을 위해서는 XML통신이 식별이 쉬울 수 있으나, 사용자 테스트 서버 및 운영서버에서는 SSV 통신을 권장합니다.
SSV통신은 CSV포멧과 유사한 형태로 XML의 불필요하게 나열되는 태그를 제거하므로 보다 적은 네트워크 비용을 수반합니다. 또한, XML 파싱보다는 보다 빠른 성능을 기대합니다.
[예시 8-2] - SSV 응답본문 - XML 응답본문
송신은 transaction API, 수신은 서버 API인 PlatformResponse에서 통신 방식을 지정합니다. 서버 framework 구성시 request 방식에 따라 response방식을 지정하면 효율적입니다.(즉, client가 SSV이면 server도 SSV, client가 XML이면 server도 XML)
스타일 최적화
많은 이미지와 다양한 스타일을 적용하게 되면, 응답시간 및 렌더링등의 영향을 받습니다.
이미지는 손실률이 없고 높은 압축률을 보이는 png 형태를 권장합니다.
성능을 고려한다면 페이지에 구성되는 이미지는 최소화 하도록 하며, 컴퍼넌트의 스타일로 구성이 가능하다면 이로 대체하도록 합니다.
컴퍼넌트 또한 다양한 스타일로 구성할 수 있으나 이러한 스타일이 복잡할 수록 렌러링에는 영향을 받으므로 주의하여야 합니다. (예시 9-1)
[예시 9-1] - 상단 이미지는 기본 형태이나 하단 이미지는 그리드의 edittype과 padding이 적용된 경우입니다. 하단의 그리드를 HTML 형태로 만들기 위해서는 더 많은 node가 필요할 것입니다.
공통함수 구성
모든 폼에서 모든 공통함수를 include 하는 방식 보다는 필요한 부분만을 include하거나 제거하여 최적화되도록 구성합니다.
공통영역의 담당자가 구현 및 컨설팅하는 내역에 따라 시스템 성능의 상당한 영향을 끼칠 수 있는 부분입니다.
공통함수 초기 1회만 로딩
공통함수를 폼마다 include하지 않고, json에 등록하여 초기 1회만 로딩하도록 구현합니다. 즉, 업무 화면에서는 include가 없도록 합니다.
- 공통함수용 json등록
* 공통함수 예시 (CommonForm.js) //----------------------------------------------------------------------- // 아래 재정의는 2016.7월 정기 버전에 처리예정이며, 이후 버전은 제거 // platform.js override // 아래는 제품에서 검토 필요. //----------------------------------------------------------------------- var _pLoadManager = nexacro.LoadManager.prototype; _pLoadManager.on_load_datamodule = function (svcid, errstatus, message, fireerrorcode, returncode, requesturi, locationuri) { var load_Item = this.getDataItem(svcid); if (load_Item) { var callback_id = load_Item._context_callback; //------------------------------------------- // 변경 부분 //------------------------------------------- //var callback_func = this.context[callback_id]; var callback_func; if(typeof(callback_id) == "function") { callback_func = callback_id; } else { callback_func = this.context[callback_id]; } //------------------------------------------- var ret = false; if (errstatus < 0 && fireerrorcode) { load_Item.errcode = errstatus; if (fireerrorcode != "comm_cancel_byuser" || fireerrorcode != "comm_stop_transaction_byesc" || load_Item._is_cancel || !load_Item._handle || (load_Item._handle && !load_Item._handle._user_aborted && load_Item._handle._user_aborted !== undefined)) { ret = application._onHttpSystemError(this.context, true, this.context, fireerrorcode, requesturi, returncode, requesturi, locationuri); if (fireerrorcode != "comm_cancel_byuser" && fireerrorcode != "comm_stop_transaction_byesc") { ret = false; } if (ret) { return true; } } } if (fireerrorcode == "comm_cancel_byuser" || fireerrorcode == "comm_stop_transaction_byesc") { if (ret && load_Item._handle && !load_Item._handle._user_aborted && load_Item._handle._user_aborted !== undefined) { return ret; } if (load_Item._is_cancel !== undefined && !load_Item._is_cancel) { return ret; } } this.removeDataItem(svcid); this.removeTransactionItem(svcid); this.dataCnt--; if (callback_func && typeof (callback_func) == "function") { callback_func.call(this.context, svcid, errstatus, message); if (errstatus == 0) { load_Item._handle = null; } } return ret; } }; delete _pLoadManager; // 재정의 끝 //----------------------------------------------------------------------- function gfn_alertTest() { alert("test"); }; //----------------------------------------------------------------------- // transaction //----------------------------------------------------------------------- gfn_callService = function(objForm,srvId,sUrl,inData,outData,param,sCallBack,bAsync) { var srvId = srvId + "|" + sCallBack; var callBackfn = function(srvId,errCd,errMsg) { gfn_callBack(objForm,srvId,errCd,errMsg) }; objForm.transaction(srvId,"srv::" + sUrl,inData,outData,"",callBackfn,bAsync); }; //----------------------------------------------------------------------- // callback //----------------------------------------------------------------------- gfn_callBack = function(objForm,srvId,errCd,errMsg) { var arrSrvId = srvId.split(/\|/g); var callBack = objForm[arrSrvId[1]]; if(callBack) { callBack.call(objForm,arrSrvId[0],errCd,errMsg); } objForm = null; };
this.CommonLib = {}; CommonLib.gfn_callService = function(objForm,srvId,sUrl,inData,outData,param,sCallBack,bAsync) { }
//--------------------------------------------------------------------------- // 업무 화면 예시 //--------------------------------------------------------------------------- this.Button00_onclick = function(obj:Button, e:nexacro.ClickEventInfo) { var callBack = "fn_CallBack"; var srvId = "aaaa"; gfn_callService(this,srvId,"XFJspCallTest_k2.jsp","","Dataset00=inptHmfrcListDTO","",callBack,true); }
this.Button00_onclick = function(obj:Button, e:nexacro.ClickEventInfo) { var callBack = "fn_CallBack"; var srvId = "aaaa"; CommonLib.gfn_callService(this,srvId,"XFJspCallTest_k2.jsp","","Dataset00=inptHmfrcListDTO","",callBack,true); }
- prototype 추가방식 예) var pForm = nexacro.Form.prototype; pForm.gfn_transaction(...) { ... } * 업무화면 this.gfn_transaction(...
필요한 공통함수만 include
위 10.1 방식을 권장하지만 form에 include를 하는 방식을 사용하더라도 다수의 공통함수를 include하는 것 보다는 필요한 파일만 include하도록 합니다.
이 경우, 개발자가 어느 파일에 어느 기능이 있는 지 알아야 하므로 단점이 발생하지만 모든 파일을 include하는 것은 비효율적입니다.
- main js에 모든 공통함수를 include 하고 form에서 main.xjs를 include하는 경우 - 필요한 js만 include 하도록 변경
공통 코드 구현
공통코드는 일반적으로 form load시에 호출 되므로, 비동기 통신을 사용하도록 합니다.
특정 프로젝트의 경우 공통코드를 1 transaction에서 1 codeset을 가져오는 방식을 사용하는 경우가 있습니다. 비동기로 처리하더라도 다수의 공통 코드를 여러 번의 서비스로 호출하는 것은 1회의 서비스로 처리하는 것에 비하여 비용이 더 소요될 수 있으므로, 한번의 서비스로 다수의 code를 가져오도록 구현합니다.
공통코드는 쉽게 변경되지 않는 데이터로 한번 호출한 데이터는 Global Dataset으로 관리하며, 메모리상의 데이터를 재활용하여 서비스시간을 줄일 수 있도록 합니다.
this.form_onload() { var serviceId = "CODE01"; var arrCodeset = new Array(); arrCodeset[0] = "CD001"; arrCodeset[1] = "CD002"; arrCodeset[2] = "CD003"; var arrOutDs = new Array(); arrOutDs[0] = "ds_cd01"; arrOutDs[1] = "ds_cd02"; arrOutDs[2] = "ds_cd03"; commonLib.gfn_commonCode(this,serviceId,arrCodeset,arrOutDs,"fn_callBack"); } this.fn_callBack = function(srvId,errorCode,errorMsg) { if(errorCode == ..) if(srvId == "CODE01") ... }
commonLib.gfn_commonCode = function(objForm,srvId,arrCode,arrDs,sCallBack) { ... application.gds_code.set_enableevent(false); var idx=0,...... for(var i;...) { idx = application.gds_code.findRow("CODE",arrCode[i]); if(idx>-1) { application.gds_code.filter("CODE",arrCode[i]); objForm[arrDs[i]].copyData(... } else { newArrCode.push(arrCode[i]); newArrDs.push(arrDs[i]); } .... }
최초 접속페이지 대기 이미지 처리
네트워크 상황이 좋지 못하거나 PC사양이 좋지 못한 경우 최조 접속시 로딩 지연시간이 발생됩니다.
접속시간은 동일하더라도, 대기 시간동안 대기 이미지등으로 접속 중임을 인지할 수 있도록 구현합니다.
js 파일 include는 defer 옵션을 사용하여 대기이미지를 미리 보여 주는 방식으로 사용 합니다.
index.html <div id="loadingImg" style="width:100%;height:100%;POSITION:absolute;z-index:1"> <div style="position:absolute;top:50%;left:50%;margin:-83px 0 0 -110px"> <img width="400px" height="300px" src="./image/waiting.gif"/> </div> </div> function oninitframework() { nexacro._initHTMLSysEvent(window, document); nexacro._prepareManagerFrame(); application._globalvalue = "gv_val1=a"; application.load("myproj", "test.xadl.js"); } function hideImage() { document.getElementById('loadingImg').style.display="none"; }
// xadl application onload event에서 이미지 숨기기 this.application_onload = function(obj:Application, e:nexacro.LoadEventInfo) { hideImage(); }
<script type="text/javascript" src="./nexacro14lib/framework/SystemBase.js" defer></script>
화면 갯수 제한
어플리케이션은 한계 메모리가 있으며, 한계 메모리를 초과하거나 유후 메모리가 없는 경우 비정상종료가 발생되거나, 이상동작이 발생됩니다.
화면 구성에 따라 메모리 사용량을 다르겠지만 최대로 오픈할 수 있는 화면(MDI), 오픈창등의 갯수를 제한하도록 구성합니다.
최초 로딩 페이지 단순화
최초로 로딩되고 보여지는 페이지는 가급적 단순화하여 구성합니다.
메인페이지에 많은 수의 데이터셋과 링크된 페이지,많은 include등으로 구성하게 되면 이로 인한 로딩 시간이 길어 지므로, 분할처리 하도록 구성합니다.
디자인 처리(IE9)
IE9이하에서 CSS를 처리 할 때 bordertype을 round로 설정시 성능에 영향을 줄 수 있으므로 round처리는 최소화 하도록 합니다.
IE9이하에서 CSS를 처리 할 때 Alpah값을 처리할 경우 성능에 영향을 줄 수 있으므로 가급적 사용을 제한 합니다. 예) #11111111 => #111111ff