플래시 대체 컨텐츠와 noscript 요소

adobe사의 플래시는 웹 페이지에 강력한 멀티미디어 효과를 불어넣을 수 있게 해주는 강력한 도구입니다. 하지만 기본적으로 “시각”적인 인지를 바탕으로 하기 때문에 비시각적인 환경에서는 접근성을 떨어뜨리는 원인이 되기도 합니다.

물론, 플래시 자체적으로 접근성 향상을 위한 대체 텍스트를 제공하기는 합니다. 하지만 Window-EyesJAWS 같은 일부 스크린 리더만이 이런 대체 텍스트를 읽을 수 있고, 플래시 플레이어 9가 지원하는 Microsoft Active Accessibility(MSAA) 기술은 윈도우즈 환경에 국한된 기술이기 때문에 윈도우즈 운영체제가 아닌 타 플랫폼에서는 제한적일 수 밖에 없다는 한계를 갖고 있습니다.

게다가 모든 사용자가 플래시 플러그인이 동작하는 환경에서 웹 페이지를 이용하지는 않습니다. 그렇기 때문에 페이지 네비게이션처럼 접근성이 중요한 컨텐츠에 대해서는 웹 페이지 제작자가 다양한 환경을 고려해서 최대한으로 대체 컨텐츠를 제공하는 것이 올바른 방법이라고 생각합니다. 그럼 어떤 방법이 있는지 알아볼까요?

예외적인 환경

대부분의 사용자는 일반적인 환경, 다시 말해서

  1. 브라우저가 자바스크립트를 지원하며,
  2. 브라우저가 지원하는 플래시 플러그인이 설치되어 있고,
  3. 브라우저가 CSS를 지원하는

환경에서 이 글을 보실 겁니다. 하지만 예외적인 환경에서 웹 서핑을 하는 분도 분명히 있겠지요. 각각의 환경에 따라서 대체 컨텐츠를 제공하는 방법이 달라집니다.

자바스크립트가 동작하지 않을 경우

자바스크립트와 플래시는 밀접한 관계를 갖고 있습니다. 특히 플래시에서 링크를 직접 처리하지 않고 자바스크립트 함수를 호출해서 페이지를 이동시키는 방법도 유지보수의 편의성 때문에 많이 사용되고 있는데 이런 경우에는 자바스크립트가 없으면 페이지 이동 자체가 불가능하게 됩니다.

또한, 마이크로소프트와 이올라스사와의 특허 분쟁 때문에(2007년 8월에 양사가 합의했지만) 외부 자바스크립트를 이용해서 플래시를 삽입하는 것이 일반화된 후로는 자바스크립트의 도움이 없으면 플래시 컨텐츠 자체를 볼 수 없는 경우도 많아졌습니다.

noscript 요소

이렇게 자바스크립트를 사용할 수 없는 환경을 고려해서 W3Cnoscript 요소를 HTML 권고안에 포함시켰습니다. HTML 4.01의 noscript관련 권고안 내용은 다음과 같습니다.

noscript 요소는 스크립트가 실행되지 않는 환경에서의 대체 컨텐츠를 제공한다.”

“스크립트를 이해하는 사용자 에이전트는 스크립트가 실행되지 않도록 설정되었거나 script 요소에 지정된 스크립트 언어를 지원하지 않을 경우에 noscript 요소 안의 컨텐츠를 렌더링하고, 사용자 측(client-side) 스크립트를 지원하지 않는 사용자 에이전트는 항상 noscript 요소를 렌더링해야 한다.”

따라서, 자바스크립트로 플래시를 삽입할 경우에 noscript 요소에 대체 컨텐츠를 넣으면 스크립트를 지원하지 않는 환경에서도 컨텐츠를 이용할 수 있습니다.

하지만 브라우저가 noscript 요소를 처리하는 방식에 대해서 세부적인 권고안이 없기 때문에 noscript 요소 안의 컨텐츠를 재활용할 때에는 문제가 발생하기도 합니다. 이 부분에 대해서는 잠시 후에 알아보겠습니다.

플래시 플러그인이 설치되어 있지 않은 경우

플래시 플러그인이 없으면 브라우저는 해당 컨텐츠를 인식하지 못합니다. 그래서 플래시를 삽입할 때 대체 컨텐츠를 제공해줘야 합니다. 다행히도 플래시 삽입에 사용되는 object 요소는 param 요소를 제외한 요소 내의 모든 컨텐츠를 자동으로 대체 컨텐츠로 인식합니다.

하지만 일반적으로 사용되는(adobe사가 기본적으로 제공하는) 플래시 삽입 스크립트는 objectembed 요소를 함께 이용하기 때문에 embed를 인식하는 넷스캐이프 계열 브라우저에서는 대체 컨텐츠를 인식하지 못합니다. 플래시 삽입 방법과 그에 따른 문제점들은 flash embed test suite 페이지를 참고하시기 바랍니다.

플래시 삽입 방법

대체 컨텐츠 제공을 위해서 플래시를 삽입하는 자바스크립트 함수를 약간 수정했습니다. 두 개의 object 요소를 인터넷 익스플로러만 인식하는 조건부 주석(conditional comment)과 함께 넣는 방법입니다.

function embedFlash(id, url, width, height, altText, flashVars, wmode) {
	if (!flashVars) flashVars = '';
	if (!wmode) wmode = 'window';
	if (!altText) altText = '';

	var str = '' +
	'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0" width="' + width + '" height="' + height + '" id="' + id + '">' +
		'<param name="movie" value="' + url + '" />' +
		'<param name="wmode" value="' + wmode + '" />' +
		'<param name="FlashVars" value="' + flashVars + '" />' +
		'<!--[if !IE]>-->' +
		'<object type="application/x-shockwave-flash" data="' + url + '" width="' + width + '" height="' + height + '" name="' + id + '">' +
			'<param name="wmode" value="' + wmode + '" />' +
			'<param name="FlashVars" value="' + flashVars + '" />' +
		'<!--<![endif]-->' +
			'<div class="alt-content alt-' + id + '">' + altText + '</div>' +
		'<!--[if !IE]>-->' +
		'</object>' +
		'<!--<![endif]-->' +
	'</object>';
	document.write(str);
}

† 2008년 3월 28일 업데이트: IE가 인식하는 외부 object에는 id를 주고, 내부 object에는 name을 준 이유는 자바스크립트로 플래시 무비를 직접 제어할 때 두 개의 object에 동일한 id를 부여하면 문제를 일으킬 수도 있기 때문입니다. object에 각각 idname을 준 다음 먼저 document.getElementsByName DOM Level2 메소드로 내부 object를 찾고, object를 찾지 못했을 경우에는 다시 document.getElementById 메소드로 외부 object를 찾는 것이 안전한 방법이라고 하네요. Eionet의 Embedding Flash content에 자세한 설명과 자바스크립트 예제가 있으니 참고하시기 바랍니다.

이제 embedFlash 함수를 호출할 때 대체 컨텐츠를 문자열로 지정할 수 있습니다. 예를 한 번 들어보겠습니다.

embedFlash('navigation', 800, 80, '<a href="alt_content.html">대체 링크</a>');

그러면 object 요소 안에 다음과 같은 형식으로 대체 컨텐츠가 들어갑니다.

<div class="alt-content alt-navigation">
	<a href="alt_content.html">대체 링크</a>
</div>

두 개의 class가 지정된 div 요소를 앞서 설명한 noscript 요소 안에 넣으면 플래시 플러그인이 없거나 자바스크립트가 실행되지 않아도 동일한 대체 컨텐츠를 제공할 수 있고 CSS를 이용해서 스타일을 적용시킬 수도 있습니다.

효율적인 방법을 찾아서

자바스크립트로 플래시를 삽입할 때 대체 컨텐츠를 제공한다면 따로 문자열을 만들지 않고 이미 존재하는 noscript 요소 안의 컨텐츠를 이용할 수도 있지 않을까요? noscript 요소가 문서 구조 내에 있으면 innerHTML 같은 메소드를 이용해서 컨텐츠를 문자열로 가져올 수 있으니까요.

하지만, W3C의 권고안은 사용자 에이전트가 noscript 요소를 문서 구조 내에서 어떻게 이용하는지에 대해서는 다루지 않기 때문에 브라우저 제작사마다 처리 방식에 차이가 있습니다. 간단한 예를 들어볼까요?

<noscript id="alt-content-test">
	<p><a href="alt_content.html">대체 링크</a></p>
</noscript >

위와 같이 마크업하고 자바스크립트를 이용해서 alt-content-test 안의 컨텐츠를 가져와보겠습니다. 아래 소스를 이용해서요.

<input type="button" value="대체 컨텐츠 가져오기" onclick="alert(document.getElementById('alt-content-test').innerHTML); return false;" />

아래 버튼을 클릭하면 결과를 볼 수 있습니다.

인터넷 익스플로러 6에서는 noscript 요소 안에 있는 대체 컨텐츠를 올바른 HTML 컨텐츠로 인식하지만, 파이어폭스에서는 태그를 &lt;&gt;로 변환해서 일반 텍스트 형식으로 인식합니다. 자바스크립트 string 객체의 replace 메소드를 이용하면 일괄적으로 변환시킬 수 있지만 오페라에서는 더 큰 문제가 생깁니다. 자바스크립트가 활성화된 상태에서는 아예 noscript 요소를 인식하지 못한다는 점이지요. 다시 말해서 해당 요소가 없는 것과 마찬가지입니다.

noscript 요소 안에 div를 넣고 여기에 id를 주면 파이어폭스에서는 해당 div 요소를 인식하지 못합니다. 요소 노드가 아닌 텍스트 노드로 인식하니까요.

그래서 noscript 요소의 컨텐츠를 재사용하는 것에는 한계가 있습니다.

첫 번째 대안

noscript 요소를 이용하기 어렵다면 대신 div 요소를 만들고 여기에 대체 컨텐츠를 넣은 다음 이용하면 어떨까요? 그러기 위해서는 해당 div 요소를 자바스크립트가 실행되는 환경에서는 숨겨야 합니다. 가장 간단한 방법은 CSS의 display 속성을 이용하는 것입니다. 예를 들어서 다음과 같은 자바스크립트를 이용할 수 있습니다.

window.onload = function() {
	document.getElementById("alt-content-test").style.display = "none";
}

그러면 페이지가 로드되면서 div를 숨기게 됩니다. 하지만 문서 구조 내에 포함되기 때문에 아래 소스처럼 요소의 컨텐츠를 재사용할 수 있습니다.

function embedFlash(id, url, width, height, flashVars, wmode) {
	if (!flashVars) flashVars = '';
	if (!wmode) wmode = 'window';
	var altText = document.getElementById("alt-content-test").innerHTML;
	...
}

CSS가 지원되지 않을 경우

그러면 앞서 열거했던 예외 상황 중 마지막 경우가 문제가 됩니다. 즉, CSS가 지원되지 않으면 요소가 숨겨지지 않기 때문에 자바스크립트가 실행되는 환경에서도 대체 컨텐츠가 표현됩니다.

또한, 파이어폭스(2, 3 베타)는 플래시를 object로 삽입하면 [보기]->[페이지 스타일]->[스타일 없음]을 선택할 경우에 플래시 컨텐츠를 보여주지 못합니다. 이 버그 관련 정보를 파이어폭스 버그 데이타베이스에서 찾아봤는데 결과가 없었고, A List Apart의 댓글에서만 정보를 찾을 수 있었습니다. 또한, 브라우저 자체 메뉴가 아닌 Web Developer ToolbarCSS 선택 메뉴를 이용하면 문제 없이 표현됩니다.

두 번째 대안

간단하고 쉬운 방법이 있는데 바로 swfobject 스크립트를 이용하는 것입니다.

swfobject는 div 같은 일반적인 요소에 대체 컨텐츠를 넣고, 페이지가 로드될 때 DOM 스크립트의 removeChild 메소드로 해당 요소를 문서 구조 내에서 제거합니다. 따라서 CSS가 지원되지 않는 상황에서도 문제 없이 동작합니다.

하지만, noscript를 쓰지 않는 이런 방식에서는 플래시 플러그인이 없는 상태에서 자동으로 플러그인을 설치해도 플래시 컨텐츠가 즉시 보여지지 않습니다. 페이지를 새로 읽기 전까지는요.

이런 문제와 1.5버전의 사소한 문제 때문에 그동안 사용하지 않았는데 글을 쓰면서 검색해보니 최근에 2.0 버전이 발표되었더군요. 프로젝트 페이지도 구글 코드로 옮겨졌습니다. 새 버전의 향상된 기능에 대해서는 swfobject 2.0 문서를 참고하시기 바랍니다.

또한, CSS의 display 속성 대신 removeChild 메소드를 이용하는 방법에 관해서는 오페라 개발자 포럼의 noscript 대체 방법에 예제와 설명이 있습니다.

세 번째 대안

여러 방법을 살펴보면서 그래도 noscript 요소를 이용하는 것이 낫다고 판단했습니다. 자바스크립트로 어떤 조작을 하지 않아도 되고, 기본적으로 브라우저가 지원하는 대체 컨텐츠 제공 방법이니까요. 물론 일부 구형 브라우저에서는 호환성 문제가 있다는 보고도 있지만요.

그래서 서버 사이드 스크립트 언어(PHP)로 대체 컨텐츠를 변수화한 다음 object 요소와 noscript 요소에 동일하게 넣는 방법을 사용했습니다. 문서의 파일 크기가 약간 늘어나긴 했지만 큰 영향을 줄 정도는 아니었습니다.

참고로 자바스크립트에서 문자열의 최대 길이는 명확하게 정의되어 있지 않습니다. 다만, 일반적으로 문제없이 사용할 만큼 충분히 길다고 하네요.

마치며

플래시를 삽입하는 방식은 다양합니다. embed를 쓸 수도 있고, 표준 방식인 object를 사용할 수도 있습니다. 방법이 많다는 것은 그만큼 문제점도 많다는 뜻으로 해석할 수 있을겁니다.

개인적으로 플래시를 자바스크립트 이용 없이 마크업으로 직접 넣는 것이 좋다고 생각하지만, 이올라스사와의 특허 문제로 아직까지는 실무에 적용하기가 어렵네요. swfobject 2.0 버전에서는 DOM 노드를 조작하는 기존 방식과 함께 “static” 퍼블리싱이라는 새로운 방식을 도입했는데 플래시는 표준 마크업으로 삽입하고, 자바스크립트로는 플래시 플러그인 버전 확인 같은 부가 기능만을 처리하는 방식입니다.

어떤 방식이든 대체 컨텐츠를 제공하는 것은 접근성 측면에서 무척 중요합니다. 예를 들어서 서두에서 예로 들었던 네비게이션을 플래시로 제공하는 사이트는 대체 컨텐츠가 없으면 플래시 플레이어를 지원하지 않는 아이팟 터치 같은 일부 사용자 에이전트에서는 정상적인 이용이 불가능합니다.

웹의 영역은 PC 모니터를 넘어서 다양한 플랫폼으로 확장되고 있습니다. 이런 다양한 환경에서 사용자가 불편없이 컨텐츠를 이용하기 위해서는 접근성에 대한 고려가 필수적이라 생각합니다. 저도 일정이 부족하고, 기획 단계에서 접근성에 대한 고려가 없다는 핑계로 이런 부분을 게을리 했는데 이제부터는 조금씩 바꿔가려고 합니다. 접근성에 관해서는 기술보다 정성이 더 중요하다는 생각이 드네요.

댓글 9개가 달렸습니다. 태그: , , , , , ,

  1. 호이♡ | 2008-03-25 21:35

    위스턴님 블로그에 처음 글을 남겨봅니다 ^^

    좋은 글 잘 읽었습니다
    저도 플래시 오브젝트의 접근성을 어떻게 하면 높일수 있을까에 대해 고민을 많이 했었는데 미처 생각치 못했던 방식들을 알 수가 있었네요~

    저는 일단 자바스크립트만 비사용하고 플래시는 사용하는 사용자에게 자바스크립트를 사용안하는 댓가로 플래시 사용의 선택권까지 빼앗을 수 있다는 점을 우려해서 신현석님의 object 사용관련 포스트에 나온 IE에서만 스크립트를 이용해서 한번더 찍기-_- 방식을 사용하고 있었는데 최근 request 테스트를 해보니 IE에서 중복 request 문제를 발견하고 혼란에 쌓여있습니다..;;

    말씀하신대로 방법이 많은 많큼 문제점도 많아서 어느하나를 딱 사용하기가 힘들군요… 일단은 이 문제로 전부 JS로 바꾼 지금 프로젝트를 위스턴님 포스트를 참고하여 바꿔야겠군요 후~~~

    p.s 코멘트에 링크거는데 좀 해맸네요 미투데이 방식과 동일하군요 ^^;

  2. wystan | 2008-03-26 10:09

    반가워요~ 호이님. ^^

    말씀하신 것처럼 사용자의 플래시 사용 선택권을 존중해서 HTML에 직접 플래시를 넣는 것이 더 나은 방법입니다. 그런데 플래시 자체 링크를 자바스크립트로 처리하는 경우가 많다는 점이 문제더라고요. 플래시가 보여도 링크 이동이 안될 수 있으니까요. 상황에 따라서 적절히 선택해야 할 것 같습니다.

    그리고 IE에서의 중복 request 문제는 IEWatch가 conditional comment를 인식하지 못해서 그럴 수도 있습니다. 테스트 페이지를 만들어서 아파치 서버에 올리고 IE로 접속한 다음 access.log를 확인해봤는데 swf 파일에 대한 http request는 한 번만 이루어졌습니다. 이 부분에 관해서는 조금 더 테스트를 해봐야 할 것 같네요.

  3. 정찬명 | 2008-03-28 13:27

    훌륭한 참고자료를 남겨주셔서 감사합니다. Flash 삽입작업 생길 때 다시 와서 한번 더 꼼꼼히 확인해 봐야 겠네요. 좋은 하루 되세요!

  4. wystan | 2008-03-28 17:44

    나중에 꼼꼼히 확인해본다고 하셔서 미루고 있던 보충 설명을 급히 넣었습니다. ^^;
    처음 글 쓸 때에는 플래시 삽입하는 자바스크립트 소스에 id만 넣었었는데 며칠 전에 확인하고 내부 objectname으로 참조할 수 있도록 수정했거든요. 관련해서 내용 업데이트를 해야지 하면서 미루고 있었네요.

    부족한 글을 좋게 봐주셔서 고맙습니다. 정찬명님도 좋은 하루 보내세요~ ^^

  5. 호이♡ | 2008-04-02 17:49

    위스턴님이 로그를 보시고 말씀해주셔서 다시한번 확인해보니 IE에서 이올라스 문제로 인해 innerHTML로 한번 더 찍어주는 부분에서 발생한 request였습니다. 제가 요걸 생각못했군요 ㅠㅠ innerHTML 해주는 함수를 onload 형식으로 변경하면 두번째 request는 캐시로 처리하여 그나마 부담을 줄이겠지만 문서가 로딩되기 전까진 테두리가 생긴다는 점이 문제군요
    그리고 저희 같은 경우는 링크를 xml로 관리하기 때문에 굳이 JS로 빼지 않으려고 했던 것인데 생각해보니 xml을 지원 못하는 경우도 있겠네요. 이런 경우는 어떻게 대체해 줄지도 문제가 되겠다는 생각이 들었습니다
    MS에서 이올라스 패치를 배포하고 있던데 일단 이게 빨리 퍼져야겠네요 ^^

  6. 꼬세 | 2008-04-05 04:55

    브라우져별 DOM싸움에 새우등터지는 꼴이라고 해야하나요??
    하나가 되면 하나가 막혀버리는 저도 이문제에 대해서는 골치를 앓고 있었는데 좋은 참고글이 될꺼 같네요..
    좋은 정보 잘 얻고 갑니다. ^^

  7. wystan | 2008-04-06 21:03

    링크를 xml로 관리하더라도 플래시 플러그인만 지원되면 문제가 없지 않을까요?
    자바스크립트를 사용하지 않고 마크업으로 직접 플래시를 넣었다면 플래시 플러그인 유무에 대해서만 대체 컨텐츠를 제공하면 되지 않을까 생각합니다.

    그리고 말씀하신 것처럼 이올라스 패치만 제대로 제공되면 incluce 이용 같은 대안을 생각해볼 수 있으니 훨씬 나을 것 같네요.

  8. wystan | 2008-04-06 21:27

    브라우져별 DOM싸움은 웹 제작자에게 항상 시련을 주지만… 그래도 갈수록 상황이 좋아지는 것 같아요.
    ECMA 스크립트 표준과 DOM 표준 지원을 강화하는 방향으로 개발이 진행되고 있으니까요.
    물론, 사용자와 브라우저 제작사의 요구를 만족시킬 수 있도록 표준 자체도 계속 발전해야겠지만요.

  9. 최동호 | 2009-02-10 20:04

    안녕하세요.
    플래시 object embed 정말 힘들었는데 wystan님 글덕분에 정말 도움이 많이 되었습니다.^^
    아그런데 혹시 wystan 님이나 다른분들중에 플래시 링크 xml과 연동시에
    오페라 파이어폭스 무한 데이터 로딩 이 가끔식 나타나더군요 혹시 이런문제 겪으신분 계시나요?

댓글이 닫혔습니다.