이번 장에서는 XPLATFORM을 이용하여 웹접근성을 구현시 고려해야 할 사항들에 대해 알아봅니다.
웹접근 구현 가능버전
2013년4월1일 기준 XPLATFORM Runtime(9.1, 9.2) 버전을 사용하여 구현이 가능합니다.
웹접근성의 개요
웹접근성(Accessibility)이란 장애인, 고령자등이 웹 사이트에서 제공하는 정보에 비장애인과 동등하게 접근하고 이해할 수 있도록 보장하는 것입니다. 장애의 종류에는 시각장애, 색각장애, 약시장애, 광과민성장애, 청각장애, 지체장애, 지적장애, 언어장애등이 있습니다. 또한,이러한 장애이외에도 운전이나 작업에 따른 눈, 귀등을 사용할 수 없는 경우도 포함됩니다.
웹접근성은 HTML과 관련된 접근성만을 의미하며 국가에서 제시한 기준이 있지만 RIA에 대한 접근성은 아직 국가기준이 없습니다. 이 문서의 목차는 웹접근성 국가기준인 "한국정보문화진흥원 품질마크 인증 심사기준"(부록참조)중 13개 항목을 중심으로 작성 되었으며 내용은 XPLATFORM에 대해서만 간결히 정리하였습니다. 국가에서 정한 웹접근성 기준은 크게 아래와 같습니다.
1) 인식의 용이성 글로 표현할 수 없는 콘텐츠를 제외하고 장애 유형에 관계없이 모든 사용자가 콘텐츠를 인지할 수 있도록 제공해야 한다. 2) 운용의 용이성 콘텐츠에 포함된 모든 구성 요소들은 장애 유형과 관계없이 모든 사용자가 사용할 수 있어야 한다. 3) 이해의 용이성 모든 콘텐츠는 가능한 한 그 내용과 사용 방법을 모든 사용자가 이해하기 쉽도록 구성해야 한다. 4) 기술적 진보성 현재 개발된 보조기술로는 접근이 어렵거나 불가능한 웹 콘텐츠 는 가용한 보조기술을 이용하여 접근할 수 있도록 대체 콘텐츠를 함께 제공해야 한다.
XPLATFORM은 V9.1부터 MSAA를 이용한 접근성을 지원하며 아래 그림은 XPLATFORM에서 접근성이 처리되는 방식입니다. MSAA(Microsoft Active Accessibility)란 Microsoft에서 제공하는 접근성 기능입니다. 아래 그림에서 TTS(text to speech)프로그램은 화면에 보이는 글자를 읽어주는 프로그램이며 센스리더,드림보이스등이 있습니다. TTS프로그램은 장애인이 별도로 구매 해야 사용할 수 있습니다.
※ 단, XPLATFORM이 글자를 읽는 것이 아니라 MSAA에 내용을 전달만 할 뿐이고 실제로는 TTS프로그램이 내용을 읽는다는 점에 주의하십시오.따라서, 화면조작과 읽는 시점이 차이가 날 수 있습니다. 예를 들어, 버튼에 mouseover시 내용을 읽지 않는다면 그것은 XPLATFORM의 버그가 아니고 단지 읽는 시점이 차이가 나서 읽는 것을 건너 뛴 경우일 수 있다는 뜻입니다.
사전준비사항
소프트웨어 준비
웹접근성을 구현하기 위해서는 TTS프로그램이 있어야 합니다. 국내의 경우 대부분의 사용자 분들이 센스리더 제품을 사용하고 있으므로 해당 제품을 이용하는 것이 가장 정확한 확인작업이 가능할 것으로 봅니다.
포커스 이동방법 정의
웹접근성을 구현시 가장 중요한 것은 시각장애인 분들의 경우 마우스를 사용하지 않으며,
모든 컴포넌트 이동 및 값 입력을 키보드 만을 이용하여 제어를 합니다.
따라서 각 컴포넌트 별 이동순서를 명확히 기술해 주어야 합니다.
즉 TabOrder가 설정이 중요합니다.
(공통 개발영역에서 구현방법이나, 각 개별 페이지에서 처리해야 하는 부분에 대한 명확한 개발방법 합의과정이 필요합니다.)
화면 이동 흐름정의
화면의 이동방법에 대한 정의가 필요합니다. 예를 들어 - 좌->우로 - 위에서 아래로 - Tab형태의 화면의 경우 선택한 Tab에서 하위 컴포넌트로 이동 등
화면 유형고려시 UX관점에서 접근필요
외부 컴포넌트에 대한 웹접근성 지원여부 확인
XPLATFORM이외의 3Party제품이 있는 경우 해당 제품에서 웹접근성 구현이 가능한지 확인필요 예) 차트, 레포트 등
외부 컴포넌트 사용시 웹접근성을 제공하지 않는 컴포넌트에 대해서는 키보드 포커스가 이동을 하면 안되며, 반드시 해당 컴포넌트를 대체할 수 있는 대체텍스트가 제공되어야 한다.
외부모듈의 경우 tabOrder가 필요하지 않는 경우에는 Focus 이동을 막기 위해
Div안에 위치시키는 것이 tabOrder를 제어하기 편리합니다.
데이타의 명확화
장애인을 위한 가장 기본저인 사항은 모든 데이타를 읽어 줄 수 있는 형태로 표현이 가능해야 한다는 것입니다. 예를 들어 그리드의 BackGround Image를 표현했다면 해당 Cell에 포커스 이동시 BackGround Image에 대한 대체텍스트가 제공되어야 합니다.
웹접근성 구현방법
IE브라우저에 XPLATFORM AX를 임베드하여 구현하는 방법을 대상으로 설명합니다.
붉은색은 키보드의 TabOrder흐름을 의미합니다. 예) IE브라우저로 오픈되면 기본 로그이미지를 시작으로 Tab이 이동되게 됩니다. 이때 키보드를 이용하여 TabKey로 이동시 1번에 도착하게 되면 3번으로 가지 않고 XPLATFORM AX영역으로 이동합니다. 또한 XPLATFORM AX 마지막 컴포넌트에서 TabKey를 누르게 되면 3번으로 이동하게 되고 키보드 Shift+ Tab을 누르게 되면 역으로 이동됩니다.
HTML 작업
웹접근성을 구현하기 위해서는 웹페이지에 XPLATFORM을 임베드시켜 표현을 해야 합니다. 이 때 HTML상에서 작업되어야 할 사항을 대상으로 정리합니다.
js파일 (웹페이지 : XPInstaller322.js)
// Engine function CreateXPInstlr(strID,ErrFunc,SetFunc,NotifyFunc,W,H) { if (checkBrowser.browser != "Explorer") { CreateXPInstlrPlugin(strID,ErrFunc,SetFunc,NotifyFunc,W,H); return; } document.write("<OBJECT id='" + strID + "' CLASSID='CLSID:FCB889AC-D683-47a0-B04C-8FC42E257E5B'"); document.write(" WIDTH='"+W+"' HEIGHT='"+H+"' CODEBASE='" +Server_Path+ "XP_Install/XPLATFORM9.1_SetupEngine.cab#VERSION=" + XPLATFORM_CAB_VER + "' onfocus=fn_XPFocus() onError='" + ErrFunc + "()'>"); document.write("</OBJECT>"); } function fn_XPFocus() { XPlatformCtrl.callscript("gfn_setFocus()"); }
usernotify 스크립트 추가
<script type="text/javascript" FOR="XPlatformCtrl" EVENT="usernotify(nNotifyID,strMsg)"> if(nNotifyID == 902) { window.focus(); } else if(nNotifyID == 901) { document.getElementById("그림에서 3번ID").focus(); } else if(nNotifyID == 903) { document.getElementById("그림에서 1번ID").focus(); } </script> //902 (센스리더를 Ctrl+Shift+F12 Disable 실행하기 위해 사용) //901 (XPLATFORM에서 IE로 이동하기 위해 사용) //903 (IE에서 XPLATFORM으로 이동하기 위해 사용)
IE브라우저 3번 위치에서 뒤로가기(Shift + Tab)시 발생하는 이벤트
function fn_keydown() { var eventobj=window.event? event : e if(eventobj.shiftKey && eventobj.keyCode == 9) { var XPlatformCtrlObj = document.getElementById("XPlatformCtrl"); XPlatformCtrl.callscript("gfn_backKeySet()"); } } //<body>영역 <input type="image" id="quickmenu" href="#quick-list" src="http://www.miplatform.co.kr/images/quick/btn_quick.gif" alt="3번 바로가기" onclick="selectLinks('quick-list');return false;" onkeydown="fn_keydown();"/>
IE브라우저 1번 위치의 스크립트 실행방법
function setup_install(strUrl) { window.location.href = strUrl; } //<body>영역 <input type="image" id="installmenu" src="http://www.miplatform.co.kr/images/menu.gif" alt="컴포넌트 설치 안내" onclick="setup_install('http://www.miplatform.co.kr/');"/> // 위의 스크립트와 연관된 사항입니다.
input type이 image로 설정이 되어야 자바스크립트에서 해당 위치로 setfocus시 정동적으로 이동이 가능합니다.
UI(XPLATFORM) 공통사항
extCommon API준비
XPLATFORM을 IE에 임베드하여 구혀나는 경우 IE브라우저에서 Tab으로 이동시 ActiveX 안으로 커서 이동이 불가능합니다. 이때 TTS프로그램(센스리더)의 단축 키보드 기능 Ctrl + Shift + F12를 눌러주게 되면 ActiveX 안으로 커서 이동이 가능합니다 이 방법을 사용자가 임으로 할 수 없기 때문에 이를 제어할 수 있도록 extCommonAPI에 해당 기능을 추가하였습니다.
소스 예) var extComapi = new ExtCommon(); extComapi.set_SensReader_VCursorDisable(); // Ctrl + Shift + F12
Timer를 위한 Global변수 선언
GV_TIMESET=100;
해당 변수를 Global변수로 선언하는 이유는 Timer설정값의 유연성을 제공.
ADL속성 추가
enableaccessibility 속성
XPLATFORM에서 웹접근성을 지원하기 위해서는 accessibility기능을사용해야 합니다.
UxStudio의 ADL속성 중 enableaccessibility : true설정
ADL script
gfn_setFocus : HTML의 XPLATFORM Control에 setFocus가 들어온 경우 발생
Script) function gfn_setFocus() { if(application.getActiveForm() != null) { application.getActiveForm().setFocus(); } }
gfn_backKeySet : HTML의 Quick Menu에서 뒤로가기(Shift + Tab)시 들어온 이벤트를 폼에 전달
Script) function gfn_backKeySet() { application.getActiveForm().fn_backKeySet(); } //fn_backKeySet()은 ActiveForm에 정의되어 있어야 합니다.
테마(CSS)
포커스가 가능한 컴폰너트에 대한 focusborder 적용
그리드 및 버튼에 포커스 위치시 시각화 작업 (focusborder적용)
Button, Button:mouseover, Button:pushed, Button:focused, Button:selected, Button:disabled { background : #ffffff; border : 1 solid #bcbcbc; bordertype : round 2 2; focusborder : 1 dotted #808080; pusheddrawoffset : 0 1; image : ; imagealign : left middle; align : center middle; font : Dotum,9; color : #545454; padding : 0 0 1 0; }
현재 포커스에 대한 명확한 확인을 위해 실선모양의 테두리가 표현되어야 합니다. 이는 모드 컴포넌트에 대해 적용이 되어야 합니다.
테두리 선은 고객사마다 다를 수 있으므로 구현시 사전 표현방법에 대해 논의가 필요합니다.
Image 작업(고려사항 위주)
시각장애인을 위한 패턴처리
1) 웹접근성에 맞는 이미지 처리 (시각장애에 대한 패턴처리) 2) BackGroundImage에 대한 패턴처리 3) 고려사항 : 색약장애인의 경우 색상을 구별하기 어렵습니다. 즉 이미지의 구별을 색상(빨강, 파랑, 노랑)등으로 표현을 하게 되면 색약 장애인분들의 경우 색상구별이 되지 않습니다. 즉 하나의 색상으로 보여지므로 색상구별이 아닌 이미지 패턴을 사용하여 구현을 해야 합니다.
위의 이미지를 보게 되면 핑크와, 연두색을 이용하여 Max값와 Min값을 표현한다고 가정했을 경우 색약 장애인분들은 색상 구별이 되지 않습니다 따라서 오른쪽과 같이 사선모양과 사각형형태로 이미지를 표현해 주어야 색상이 아닌 이미지 패턴을 가지고 구별이 가능합니다.
컴포넌트별 작업사항
Form
웹접근성 구현을 위해 기본추가 컴포넌트
1) DataSet추가
컬럼정보 | 사용내용 | 비고 |
---|---|---|
tabOrderColumn | TabOrder컴포넌트명 | |
delChk | 삭제여부 |
XPLATFORM 화면의 tabOrder이동방법은 ds_taborder에 정의된 데이터를 이용하여 순서가 지정됩니다. 따라서 로직구현에 따라 tabOrder가 변경되어야 하는 경우에는 ds_taborder에 값을 변경해 주면 된다.
DataSet을 만들어 TabOrder 순서를 관리한 이유는 개발된 화면의 수정등이 일어날 경우 TabOrder를 매번 맞추어 주어야 하는 번거러움을 없애기 위해 설정하였으며, 최기 한번정도만 DataSet에 관리해 주면 유연하게 대응이 가능할 것으로 봄 (이 방법은 고객사 개발에 따라 변경이 가능한 부분이니 참고용으로 사용하세요.)
위의 TabOrder설정을 반영해 주기 위해서는 공통스크립트 중 gf_SetCompTabStop(allobj, objDs)를 실행하여 DataSet정보를 세팅할 수 있으며, 해당 스크립트 실행은 폼이 모두 load된 시점이나, TabOrder가 재정의되어야 하는 이벤트 부분에 넣어주면 됩니다.
2) btnXPStart : 버튼
화면을 빠져나가기 위해 필요한 컴포넌트로 onsetfocus 이벤트에서 다음과 같은 로직이 구현된다 XPLATFORM->IE(뒤로가기 컴포넌트 설치버튼으로 이동)
function btnXPStart_onsetfocus(obj:Button, e:SetFocusEventInfo) { if(e.oldcomponent.name != 'mainframe') { 이동하고자 하는 컴폰넌트.setFocus(); setTimer(903,GV_TIMESET); } else { 이동하고자 하는 컴폰넌트.setFocus(); } }
3) btnXPEnd : 버튼
화면을 빠져나가기 위해 필요한 컴포넌트로 onsetfocus 이벤트에서 다음과 같은 로직이 구현된다. XPLATFORM->IE이동 (퀵메뉴 버튼으로 이동)
function btnXPEnd_onsetfocus(obj:Button, e:SetFocusEventInfo) { setTimer(901,GV_TIMESET); }
4) sta_text : Statics
그리드에 포커스가 위치하는 경우 테이블 정보를 읽어주기 위해 필요한 컴포넌트로 Grid의 onsetfocus에서 사용된다.
사용 예) function Grid00_onsetfocus(obj:Grid, e:SetFocusEventInfo) { if( (eval(obj.binddataset).rowposition == 0) && (obj.getCellPos() == 0)) { var str_temp = "XXX 테이블 " + obj.getCellText(-1,0) + " " + obj.getCellText(0, 0); sta_text.text = str_temp; sta_text.style.accessibility.apply(); } }
공통소스
/* 동적으로 생성한 taborder정보를 삭제한다. */ function gf_taborder_del() { var dsCnt = ds_taborder.getRowCount()-1; for(var i=dsCnt;i>0;i--) { if(ds_taborder.getColumn(i,"delChk") == '1') { ds_taborder.deleteRow(i); } } } /* 폼에서 사용하는 컴포넌트에 대한 Taborder를 재 설정한다. */ function gf_SetCompTabStop(allobj, objDs) { var arrRetComp = gf_GetCompFullName(allobj); for (var i=0; i<arrRetComp.length; i++) { eval(arrRetComp[i]).tabstop = false; eval(arrRetComp[i]).taborder = 1000 + i; } for (var i=0; i<objDs.getRowCount(); i++) { eval(objDs.getColumn(i,"tabOrderColumn")).tabstop = true; eval(objDs.getColumn(i,"tabOrderColumn")).taborder = i; } } /********************************************************************************************* ** function name : gf_GetCompFullName() ** function description : Form에 존재하는 Component Full Name 조회 (Recusive 함수임) ** argument : obj찾을 대상 obj ** strUpperComp 상위 Component ** return Type : ** ex : gf_GetCompFullName(this, "") *********************************************************************************************/ function gf_GetCompFullName(obj, strUpperComp) { if (strUpperComp == null || strUpperComp == undefined) strUpperComp = ""; var arrRet = new Array(); for (var i=0; i<obj.components.length; i++) { if (obj.components[i] instanceof Div || obj.components[i] instanceof PopupDiv || obj.components[i] instanceof Tabpage) { var compName = obj.components[i].name; if (strUpperComp.length > 0) compName = strUpperComp + "." + compName; var tmpArrRet = gf_GetCompFullName(obj.components[i], compName); if (tmpArrRet.length > 0) { arrRet = arrRet.concat(tmpArrRet); } //Composite Component 자체도 목록으로 볼 경우 arrRet.push(compName); } else if (obj.components[i] instanceof Tab) { var tabObj = obj.components[i]; var tabCompName = tabObj.name; if (strUpperComp.length > 0) tabCompName = strUpperComp + "." + tabCompName; for (var t=0; t<tabObj.components.length; t++) { var compName = tabCompName + "." + tabObj.components[t].name; var tmpArrRet = gf_GetCompFullName(tabObj.components[t], compName); if (tmpArrRet.length > 0) { arrRet = arrRet.concat(tmpArrRet); } //Composite Component 자체도 목록으로 볼 경우 arrRet.push(compName); } //Composite Component 자체도 목록으로 볼 경우 arrRet.push(tabCompName); } else { var compName = obj.components[i].name; if (strUpperComp.length > 0) compName = strUpperComp + "." + compName; arrRet.push(compName); } } return arrRet; }
화면에는 DataSet으로 관리되지 않는 컴폰너트의 경우 TabOrder를 삭제할 수 없기 때문에
상단 스크립트로 동잘할 DataSet에 정의되지 않는 경우 1000이상으로 TabOrder를 변경해 주어
TabOrder로 이동되지 않게 처리됨
function gf_SetCompTabStop(allobj, objDs)
{
var arrRetComp = gf_GetCompFullName(allobj);
for (var i=0; i<arrRetComp.length; i++) {
eval(arrRetComp[i]).tabstop = false;
eval(arrRetComp[i]).taborder = 1000 + i; <---------- 해당 내용
}
폼의 timer 설정
function Form_ontimer(obj:Form, e:TimerEventInfo) { killTimer(e.timerid); if(e.timerid == 902) { var extComapi = new ExtCommon(); extComapi.set_SensReader_VCursorDisable(); // Ctrl + Shift + F12 포커스를 가지는 컴포넌트.setFocus(); application.userNotify(902, "ie"); } else if(e.timerid == 901) { 포커스를 가지는 컴포넌트.setFocus(); application.userNotify(901, "out"); } else if(e.timerid == 903) { application.userNotify(903, "back"); } }
902 (센스리더를 Ctrl+Shift+F12 Disable 실행하기 위해 사용)
901 (XPLATFORM에서 IE로 이동하기 위해 사용)
903 (IE에서 XPLATFORM으로 이동하기 위해 사용)
폼의 onload이벤트 설정
var fLoadchk=false; //폼변수로 설정 // 아래 내용은 폼의 onLoad 및 데이터 조회 후 설정 /* 첫 포커스에 위치시키는 작업 */ if(fLoadchk==true) { setTimer(902, GV_TIMESET); } else { }
TabOrder 이동을 위한 기타 스크립트
IE->XPLATFORM으로 뒤로가기를 통해 이동해 온 경우 그리드 포커스시 그리드 정보를 읽어주는 기능 (IE브라우저에 포커스가 있는 상태에서 XPLATFROM AX로 포커스를 받는 경우 그리드가 아닌 다른 컴포넌트는 정상적으로 TTS가 읽어지나 그리드는 읽어지지 않아 스크립트를 통해 이동시켜줌)
function fn_backKeySet() { eval(ds_taborder.getColumn(ds_taborder.getRowCount()-2,"tabOrderColumn")).setFocus(); if( eval(ds_taborder.getColumn(ds_taborder.getRowCount()-2,"tabOrderColumn")) instanceof Grid) { var eventHandleName = eval(ds_taborder.getColumn(ds_taborder.getRowCount()-2,"tabOrderColumn")).onsetfocus.getHandler(0); if(eventHandleName <> null) eventHandleName(eval(ds_taborder.getColumn(ds_taborder.getRowCount()-2,"tabOrderColumn"))); } }
TabPage -> 버튼형태 변경
Tab컴포넌트는 웹접근성 구현시에는 사용을 하지 않는다. 만약 Tab컴포넌트 유형이 필요한 경우에는 이미지 버튼을 이용하여 Tab형태로 구현합니다. Tab컴포넌트의 경우 화면 변경이 일어나는 경우 유연성이 부족합니다.
Grid
그리드 컴폰너트를 사용하여 웹접근성 구현시 필요한 사항을 정리합니다.
Grid 컬럼 고정
Grid의 Cell값을 읽어주는 기준값이 있을 경우 반드시 Column을 Fix시켜 분다.
위의 Grid에서 기준 컬럼이 지표컬럼이므로 Col0의 bind를 left로 설정해 줍니다.
Fix된 컬럼이 있는 경우 TTS에서 읽어줄때 Fix컬럼의 데이타를 읽어주고 해당 Cell의 정보를 읽어줍니다.
그리드의 경우 마지막 열 Head컬럼에 '마지막열 확인기능 추가'
시각장애인의 경우 그리드의 마지막 Cell(열)임을 인지할 수 있도록 그리드의 마지막 Head의 accessibility에 '컬럼명과 마지막열'을 표현해 줍니다.
Grid Focus 이동방법 설정
Grid의 데이터 표현방법의 이동이 Row단위로 이동되는 부분을 Cell단위로 이동할 수 있도록 변경합니다
일반 웹접근성 사용자 분들은 Row단위로 표현시 상단의 Title정보를 읽지 않습니다.
따라서 Cell단위로 표현을 하는 것이 사용자의 인식이 용이합니다.
Select에 대한 색상변경 필요 (선택영역 표현) - 테마 CSS변경
Grid>#body { background : #ffffff; gradation : ; .. 중략... cellmargin :2 0 0 2; selectgradation : ; selectborder : 2 solid #888888;
그리드 이미지가 있는 경우 대체텍스트 제공
function fn_status(chkValue) { var fla_idx = "관심"; if (chkValue > 0 && chkValue <= 25) { fla_idx = "관심"; } else if (chkValue >= 25 && chkValue < 50) { fla_idx = "주의"; } else if (chkValue >= 50 && chkValue < 75) { fla_idx = "경보"; } else if (chkValue >= 75 && chkValue <= 100) { fla_idx = "심각"; } return fla_idx; }
Grid Focus시 Accessibility 대체표현을 위한 static사용
그리드에 Focus가 위치하는 경우 해당 그리드에 대해서는 'XXX 테이블' 정보와 첫번째 셀정보를 읽어 주어야 한다. 이때 자동으로 읽어 주기 이해서는 static 컴포넌트를 이용하여 추가작업이 필요합니다.
function Grid00_onsetfocus(obj:Grid, e:SetFocusEventInfo) { if( (eval(obj.binddataset).rowposition == 0) && (obj.getCellPos() == 0)) { var str_temp = "XXX 테이블 " + obj.getCellText(-1,0) + " " + obj.getCellText(0, 0); sta_text.text = str_temp; sta_text.style.accessibility.apply(); } }
그리드에 테이블 정보라고 읽어주는 부분은 그리드 첫번째 Row와 첫번째 Cell에 위치하는 경우
한번만 읽어주면 됩니다.
그리드 SumRow 표현방법
그리드의 Head 및 SumRow에는 포커스가 가지 않음 Head의 경우 Row에 값이 존재하는 경우 센스리더가 데이터를 포함해 함께 읽어줄 수 있으나, SumRow는 읽어주지 않으므로 이벤트를 발생시킬 수 있는 작업패턴이 필요합니다. 따라서 해당 SumRow를 데이터 형태로 만들어 사용합니다.
그리드 기본 Summary기능은 사용할 수 없으며,
그리드 마지막 Row 또는 0번째 Row를 삽입하여 Summary에 대한 정보를 만들어 표현하시기 바랍니다.
Grid Cell EditType 처리시 readonly 설정
Grid의 Cell은 Edit가 가능한 경우에만 포커스가 이동합니다. 그러나 입력을 하지 않아도 값을 읽어주어야 하는 경우에는 edittype을 readonly로 설정해 주면 해당 포커스 이동이 가능합니다.