굉장히 좋은 자바스크립트 강좌가 있어 소개합니다. 영문이라는 한계가 있지만 “굉장히”라는 수식어가 모자랄 만큼 좋은 글입니다. 이렇게 좋은 글이 많이 알려지지 않은 건 글 제목이 자바스크립트가 아닌 “ECMAScript”여서가 아닐까 하는데요. ECMAScript는 자바스크립트의 기반이 되는 스크립트 언어로 ECMA International에서 표준화한 언어입니다.
소개하려는 글은 자바스크립트 프로그래머이자 ECMAScript 이론가라고 자신을 소개하고 있는 Dmitry A. Soshnikov가 블로그를 통해서 발표한 글입니다. 현재까지 발표한 글은 자바스크립트의 핵심을 한 페이지로 요약한 “JavaScript, The Core”와 ECMA-262의 세 번째 버전(edition)을 상세하게 설명하는 “ECMA-262-3 in detail”, 그리고 최신 버전을 다루는 “ECMA-262-5 in detail”, 마지막으로 네 개의 노트(note)로 구성되어 있습니다.
참고로 ECMA-262-3은 IE와 Adobe Flash 등에서 폭넓게 지원하는 버전이고, ECMA-262-5는 Google Chrome, Mozilla Firefox 등이 지원하고 있는 ECMAScript의 공식적인 최신 버전입니다.
자바스크립트를 깊이 이해하려면 스코프(scope)나 클로저(closure) 같은 핵심 개념을 이해해야 하는데 이런 개념은 직관적으로 이해하기가 쉽지 않았습니다. 지금껏 여러 번 시도해봐도 명확하지 않았는데 이 글을 보면서 비로서 이해를 할 수 있었습니다.
다시 강조하지만 정말 굉장한 글입니다. 하지만 영문이라는 한계가 있는데요. 글 중 일부는 러시아어를 기본으로(Dmitry 가 직접 쓴) 독일어, 아랍어, 중국어, 일본어 등으로 번역이 되어 있습니다. 여기에 한글 번역을 추가하려고 우선 “JavaScript, The Core” 번역을 시작했는데 생각보다 진도가 느리네요. 좋은 글을 번역해서 널리 알리는 데 많은 분들이 동참하셨으면 좋겠습니다.
adobe사의 플래시는 웹 페이지에 강력한 멀티미디어 효과를 불어넣을 수 있게 해주는 강력한 도구입니다. 하지만 기본적으로 “시각”적인 인지를 바탕으로 하기 때문에 비시각적인 환경에서는 접근성을 떨어뜨리는 원인이 되기도 합니다.
물론, 플래시 자체적으로 접근성 향상을 위한 대체 텍스트를 제공하기는 합니다. 하지만 Window-Eyes나 JAWS 같은 일부 스크린 리더만이 이런 대체 텍스트를 읽을 수 있고, 플래시 플레이어 9가 지원하는 Microsoft Active Accessibility(MSAA) 기술은 윈도우즈 환경에 국한된 기술이기 때문에 윈도우즈 운영체제가 아닌 타 플랫폼에서는 제한적일 수 밖에 없다는 한계를 갖고 있습니다.
게다가 모든 사용자가 플래시 플러그인이 동작하는 환경에서 웹 페이지를 이용하지는 않습니다. 그렇기 때문에 페이지 네비게이션처럼 접근성이 중요한 컨텐츠에 대해서는 웹 페이지 제작자가 다양한 환경을 고려해서 최대한으로 대체 컨텐츠를 제공하는 것이 올바른 방법이라고 생각합니다. 그럼 어떤 방법이 있는지 알아볼까요?
예외적인 환경
대부분의 사용자는 일반적인 환경, 다시 말해서
브라우저가 자바스크립트를 지원하며,
브라우저가 지원하는 플래시 플러그인이 설치되어 있고,
브라우저가 CSS를 지원하는
환경에서 이 글을 보실 겁니다. 하지만 예외적인 환경에서 웹 서핑을 하는 분도 분명히 있겠지요. 각각의 환경에 따라서 대체 컨텐츠를 제공하는 방법이 달라집니다.
자바스크립트가 동작하지 않을 경우
자바스크립트와 플래시는 밀접한 관계를 갖고 있습니다. 특히 플래시에서 링크를 직접 처리하지 않고 자바스크립트 함수를 호출해서 페이지를 이동시키는 방법도 유지보수의 편의성 때문에 많이 사용되고 있는데 이런 경우에는 자바스크립트가 없으면 페이지 이동 자체가 불가능하게 됩니다.
또한, 마이크로소프트와 이올라스사와의 특허 분쟁 때문에(2007년 8월에 양사가 합의했지만) 외부 자바스크립트를 이용해서 플래시를 삽입하는 것이 일반화된 후로는 자바스크립트의 도움이 없으면 플래시 컨텐츠 자체를 볼 수 없는 경우도 많아졌습니다.
noscript 요소
이렇게 자바스크립트를 사용할 수 없는 환경을 고려해서 W3C는 noscript 요소를 HTML 권고안에 포함시켰습니다. HTML 4.01의 noscript관련 권고안 내용은 다음과 같습니다.
“noscript 요소는 스크립트가 실행되지 않는 환경에서의 대체 컨텐츠를 제공한다.”
“스크립트를 이해하는 사용자 에이전트는 스크립트가 실행되지 않도록 설정되었거나 script 요소에 지정된 스크립트 언어를 지원하지 않을 경우에 noscript 요소 안의 컨텐츠를 렌더링하고, 사용자 측(client-side) 스크립트를 지원하지 않는 사용자 에이전트는 항상 noscript 요소를 렌더링해야 한다.”
따라서, 자바스크립트로 플래시를 삽입할 경우에 noscript 요소에 대체 컨텐츠를 넣으면 스크립트를 지원하지 않는 환경에서도 컨텐츠를 이용할 수 있습니다.
하지만 브라우저가 noscript 요소를 처리하는 방식에 대해서 세부적인 권고안이 없기 때문에 noscript 요소 안의 컨텐츠를 재활용할 때에는 문제가 발생하기도 합니다. 이 부분에 대해서는 잠시 후에 알아보겠습니다.
플래시 플러그인이 설치되어 있지 않은 경우
플래시 플러그인이 없으면 브라우저는 해당 컨텐츠를 인식하지 못합니다. 그래서 플래시를 삽입할 때 대체 컨텐츠를 제공해줘야 합니다. 다행히도 플래시 삽입에 사용되는 object 요소는 param 요소를 제외한 요소 내의 모든 컨텐츠를 자동으로 대체 컨텐츠로 인식합니다.
하지만 일반적으로 사용되는(adobe사가 기본적으로 제공하는) 플래시 삽입 스크립트는 object와 embed 요소를 함께 이용하기 때문에 embed를 인식하는 넷스캐이프 계열 브라우저에서는 대체 컨텐츠를 인식하지 못합니다. 플래시 삽입 방법과 그에 따른 문제점들은 flash embed test suite 페이지를 참고하시기 바랍니다.
플래시 삽입 방법
대체 컨텐츠 제공을 위해서 플래시를 삽입하는 자바스크립트 함수를 약간 수정했습니다. 두 개의 object 요소를 인터넷 익스플로러만 인식하는 조건부 주석(conditional comment)과 함께 넣는 방법입니다.
† 2008년 3월 28일 업데이트: IE가 인식하는 외부 object에는 id를 주고, 내부 object에는 name을 준 이유는 자바스크립트로 플래시 무비를 직접 제어할 때 두 개의 object에 동일한 id를 부여하면 문제를 일으킬 수도 있기 때문입니다. object에 각각 id와 name을 준 다음 먼저 document.getElementsByNameDOM Level2 메소드로 내부 object를 찾고, object를 찾지 못했을 경우에는 다시 document.getElementById 메소드로 외부 object를 찾는 것이 안전한 방법이라고 하네요. Eionet의 Embedding Flash content에 자세한 설명과 자바스크립트 예제가 있으니 참고하시기 바랍니다.
이제 embedFlash 함수를 호출할 때 대체 컨텐츠를 문자열로 지정할 수 있습니다. 예를 한 번 들어보겠습니다.
두 개의 class가 지정된 div 요소를 앞서 설명한 noscript 요소 안에 넣으면 플래시 플러그인이 없거나 자바스크립트가 실행되지 않아도 동일한 대체 컨텐츠를 제공할 수 있고 CSS를 이용해서 스타일을 적용시킬 수도 있습니다.
효율적인 방법을 찾아서
자바스크립트로 플래시를 삽입할 때 대체 컨텐츠를 제공한다면 따로 문자열을 만들지 않고 이미 존재하는 noscript 요소 안의 컨텐츠를 이용할 수도 있지 않을까요? noscript 요소가 문서 구조 내에 있으면 innerHTML 같은 메소드를 이용해서 컨텐츠를 문자열로 가져올 수 있으니까요.
하지만, W3C의 권고안은 사용자 에이전트가 noscript 요소를 문서 구조 내에서 어떻게 이용하는지에 대해서는 다루지 않기 때문에 브라우저 제작사마다 처리 방식에 차이가 있습니다. 간단한 예를 들어볼까요?
인터넷 익스플로러 6에서는 noscript 요소 안에 있는 대체 컨텐츠를 올바른 HTML 컨텐츠로 인식하지만, 파이어폭스에서는 태그를 <와 >로 변환해서 일반 텍스트 형식으로 인식합니다. 자바스크립트 string 객체의 replace 메소드를 이용하면 일괄적으로 변환시킬 수 있지만 오페라에서는 더 큰 문제가 생깁니다. 자바스크립트가 활성화된 상태에서는 아예 noscript 요소를 인식하지 못한다는 점이지요. 다시 말해서 해당 요소가 없는 것과 마찬가지입니다.
† noscript 요소 안에 div를 넣고 여기에 id를 주면 파이어폭스에서는 해당 div 요소를 인식하지 못합니다. 요소 노드가 아닌 텍스트 노드로 인식하니까요.
그래서 noscript 요소의 컨텐츠를 재사용하는 것에는 한계가 있습니다.
첫 번째 대안
noscript 요소를 이용하기 어렵다면 대신 div 요소를 만들고 여기에 대체 컨텐츠를 넣은 다음 이용하면 어떨까요? 그러기 위해서는 해당 div 요소를 자바스크립트가 실행되는 환경에서는 숨겨야 합니다. 가장 간단한 방법은 CSS의 display 속성을 이용하는 것입니다. 예를 들어서 다음과 같은 자바스크립트를 이용할 수 있습니다.
그러면 페이지가 로드되면서 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 Toolbar의 CSS 선택 메뉴를 이용하면 문제 없이 표현됩니다.
여러 방법을 살펴보면서 그래도 noscript 요소를 이용하는 것이 낫다고 판단했습니다. 자바스크립트로 어떤 조작을 하지 않아도 되고, 기본적으로 브라우저가 지원하는 대체 컨텐츠 제공 방법이니까요. 물론 일부 구형 브라우저에서는 호환성 문제가 있다는 보고도 있지만요.
그래서 서버 사이드 스크립트 언어(PHP)로 대체 컨텐츠를 변수화한 다음 object 요소와 noscript 요소에 동일하게 넣는 방법을 사용했습니다. 문서의 파일 크기가 약간 늘어나긴 했지만 큰 영향을 줄 정도는 아니었습니다.
참고로 자바스크립트에서 문자열의 최대 길이는 명확하게 정의되어 있지 않습니다. 다만, 일반적으로 문제없이 사용할 만큼 충분히 길다고 하네요.
마치며
플래시를 삽입하는 방식은 다양합니다. embed를 쓸 수도 있고, 표준 방식인 object를 사용할 수도 있습니다. 방법이 많다는 것은 그만큼 문제점도 많다는 뜻으로 해석할 수 있을겁니다.
개인적으로 플래시를 자바스크립트 이용 없이 마크업으로 직접 넣는 것이 좋다고 생각하지만, 이올라스사와의 특허 문제로 아직까지는 실무에 적용하기가 어렵네요. swfobject 2.0 버전에서는 DOM 노드를 조작하는 기존 방식과 함께 “static” 퍼블리싱이라는 새로운 방식을 도입했는데 플래시는 표준 마크업으로 삽입하고, 자바스크립트로는 플래시 플러그인 버전 확인 같은 부가 기능만을 처리하는 방식입니다.
어떤 방식이든 대체 컨텐츠를 제공하는 것은 접근성 측면에서 무척 중요합니다. 예를 들어서 서두에서 예로 들었던 네비게이션을 플래시로 제공하는 사이트는 대체 컨텐츠가 없으면 플래시 플레이어를 지원하지 않는 아이팟 터치 같은 일부 사용자 에이전트에서는 정상적인 이용이 불가능합니다.
웹의 영역은 PC 모니터를 넘어서 다양한 플랫폼으로 확장되고 있습니다. 이런 다양한 환경에서 사용자가 불편없이 컨텐츠를 이용하기 위해서는 접근성에 대한 고려가 필수적이라 생각합니다. 저도 일정이 부족하고, 기획 단계에서 접근성에 대한 고려가 없다는 핑계로 이런 부분을 게을리 했는데 이제부터는 조금씩 바꿔가려고 합니다. 접근성에 관해서는 기술보다 정성이 더 중요하다는 생각이 드네요.
scrollObj는 자바스크립트의 문서 객체 모델(DOM)을 이용해서 이미지나 텍스트에 스크롤 효과를 주는 오브젝트(객체)입니다. 비슷한 효과를 내는 함수나 오브젝트들이 자바스크립트 상의 소스를 document.write 메소드로 화면상에 출력하는 것과 달리 HTML 소스를 그대로 이용하므로 자바스크립트를 사용할 수 없는 환경에서도 스크롤될 내용을 확인할 수 있고, 문서의 소스도 간단해지는 장점을 갖습니다.
scrollObj는 이미지나 텍스트가 들어 있는 리스트(ul, ol)의 절대 위치(absolute position) 값을 바꾸는 것으로 스크롤 효과를 만듭니다. 즉, 리스트 요소 자체를 이동시키는 것이지요. 그러면서 리스트에 포함된 특정한 항목 요소(li)를 기준으로 하는 이동 거리를 계산합니다. 이 특정 요소의 너비(또는 높이)와 이동 거리가 같아질 때 리스트 항목의 순서를 바꾸어서 스크롤이 계속 이어지도록 하는 것이지요. 설명을 위해 간단한 예제를 만들어봤습니다.
11111
22222222222
3333
444444444444444
scrollObj 선언과 함게 먼저 리스트 요소의 순서가 바뀝니다. 예제에서 리스트의 마지막 요소였던 “444444444444444” 항목이 첫 번째 요소가 된 것처럼이요. 이 과정에서 앞으로 옮겨진 요소들의 너비(또는 높이)만큼 리스트의 절대 위치가 이동됩니다. 따라서 실제로는 리스트의 첫 번째 항목이 “11111” 요소인 것처럼 보이게 되지요.
“시작/정지” 버튼을 누르면 스크롤이 시작됩니다. “방향 전환” 버튼으로 스크롤 방향을 바꿀 수도 있고요. 리스트를 감싸는 div에 overflow: hidden; 속성을 주지 않았기 때문에 실제 요소의 이동을 눈으로 확인할 수 있습니다.
앞서 리스트의 순서가 바뀌는 시점을 특정 요소의 크기만큼 위치가 이동했을 때라고 했는데, 스크롤 방향에 따라서 기준이 되는 요소가 다르다는 점에 주의해야 합니다. 위 예제에서 일반적인 좌측 이동일 경우에는 리스트의 두 번째 요소가 기준이고, 오른쪽으로 이동할 때에는 첫 번째 요소가 기준이 되거든요. 이렇게 기준 요소를 달리 정한 것은 리스트 순서가 바뀔 때 정지 효과를 넣기 위해서입니다.
scrollObj 사용법
scrollObj는 오브젝트만 선언하면 자동으로 스크롤이 시작됩니다. 추가적인 전역 변수 선언은 필요하지 않고요. 선언 시 지정할 수 있는 인수는 많지만 대부분 생략 가능합니다. 먼저 scrollObj가 어떻게 선언되어야 하는지 실제 자바스크립트 소스를 적어보겠습니다.
하지만 이렇게 자유롭게 변경할 수 없는 인수들은 오브젝트 선언시 그 값을 정확히 지정해야 합니다. 예를 들어서 스크롤 거리를 2px로 지정하려면 다음과 같이 오브젝트가 선언되어야 합니다.
so1 = new scrollObj("scrollTest", false, 0, 2);
주의 사항
리스트 항목 요소들 사이에 마진(margin) 값이 존재할 경우에는 오브젝트 선언 시 이동 방향의 양쪽 마진을 합한 값을 sumMargin 인수에 넣어주여야 합니다. 만약 isVertical이 true이고(수직 스크롤) margin이 있을 경우에는 마진이 합쳐지는 현상(collapsing-margins)도 고려해야 합니다. 예를 들어서 아래와 같은 스타일이 적용될 경우에 sumMargin 값은 20이 아니라 10이 되어야 합니다.
#scrollTest li { margin: 10px 0; }
오브젝트 선언 시 사용되는 startNum 인수는 실제 보여질 화면 영역 왼쪽에 존재할 리스트 항목 요소의 수를 결정합니다. 예를 들어서 startNum이 기본값인 2라면 앞서 들었던 예처럼 리스트의 첫 번째 요소(“11111”)가 실제로는 두 번째 요소로 바뀐 상태에서 스크롤이 시작됩니다. 이 부분은 특히 역방향 스크롤에서 리스트 요소의 수가 적고 각각의 항목들 크기가 많이 차이날 때 중요한 역할을 합니다. 가급적이면 리스트 항목 요소들의 수를 충분히 확보한 상태에서 스크롤 효과를 주는 것이 좋습니다.
적용 예
scrollObj 데모 페이지에서 실제로 적용된 예를 보실 수 있습니다. 페이지 중앙에 있는 수평 텍스트 스크롤, 우측의 수직 텍스트 스크롤, 하단의 수평 이미지 스크롤이 모두 scrollObj로 구현되었습니다.
기존에 포스팅한 DOM 스크립트: 이미지 스크롤러를 다양한 용도로 사용할 수 있도록 수정하면서 내용이 많이 바뀌었습니다. 가장 큰 변화로는 기존 함수 기반의 소스를 오브젝트 기반으로 바꾼 것인데 그렇게 해서 전역 변수를 추가적으로 선언해야 하는 불편함을 없앴습니다. 범용성도 높이고요.
기존 소스에 비해서 오류 처리 부분이라든가 주석 처리가 많이 나아졌지만 충분한 테스트를 거치지 않았기 때문에 알 수 없는 문제가 있을 수도 있습니다. 혹시 이런 문제를 발견하시면 꼭 알려주세요.