웹 페이지의 첫 로딩 속도를 줄여주는 여러 방법 중, 서버로의 요청 횟수를 최소화 하는 것은 웹 애플리케이션의 최적화 요소 중에서도 중요한 덕목이자 실제 적용하기에도 아주 손쉬운 방법이다.
보통 웹 페이지에서 치장을 목적으로 사용되는 백그라운드 이미지들은 많이 사용될수록, 자동으로 그 요청 횟수도 늘어나기 마련인데, 이를 줄이고자 하는 목적으로 쓰이는 기법으로 CSS Sprites 기법이 있다.

간단하게, 아이콘이나 버튼과 같은 반복돼서 표시되지 않는 그림들처럼 독립된 여러 그림을 하나의 그림으로 합쳐놓고 CSS의 background-position을 보일 요소에 따라 바꾸어서 표시하는 기법인데, 이렇게 하면 서버로의 요청 횟수를 줄여주면서 사이트 로딩 속도를 줄여주고 더불어서 내려받는 이미지의 크기까지 줄여줄 수 있는 부수적 효과를 얻을 수 있다.

표딱지의 배경 그림으로 사용된 CSS Sprites 이미지 이곳의 블로그에서도 오른쪽 옆구리 아래에 붙어있는 표딱지 그림들의 경우, 개별 이미지를 사용해서 웹 페이지에 표시하려면 모두 10번의 서버 요청이 필요한데, CSS Sprites 기법을 써서 왼쪽에 보이는 바와 같은 하나의 그림으로 모든 단추의 배경 그림들을 표시할 수 있었다.

실제 구현 방법은 아주 간단해서, unordered li들로 구성된 메뉴들의 각 li에 특정 id 값을 지정해 주고 a 태그 속에 있는 text node를 <span> 태그로 감싸고 난 후, 여기에 background image의 좌표값을 표시될 해당 위치에 맞게 지정해 주면 모든 작업이 끝난다.
한 가지 주의가 필요했던 것은 text 때문에 배경 그림이 일부 가려지는 것을 막기 위해, <span>에 적용한 CSS의 padding-left 값을 li의 너비만큼 주어서, 결과적으로 글자를 화면 바깥으로 밀어내어 표시되지 못하도록 하였다. 이렇게 해서, 마지막 CSS 적용 결과는 다음과 같다.

/* sidebar badges */
div#sidebar div#badges ul li {
  list-style-type: none;
  list-style-image: none;
  background-image: none;
  width: 80px;
  height: 15px;
  margin-bottom: 4px;
  overflow: hidden;
}
 
div#badges span {
  display: block;
  background-image: url(images/buttons-bg.png);
  background-repeat: no-repeat;
  padding-left: 80px;
  cursor: pointer;
}
 
div#badges #atom span {background-position: 0 0;}
div#badges #vxhtml span {background-position: 0 -15px;}
div#badges #vcss span {background-position: 0 -30px;}
div#badges #wcag span {background-position: 0 -45px;}
div#badges #uni span {background-position: 0 -60px;}
div#badges #cc span {background-position: 0 -75px;}
div#badges #mac span {background-position: 0 -90px;}
div#badges #safari span {background-position: 0 -105px;}
div#badges #firefox span {background-position: 0 -120px;}
div#badges #ichat span {background-position: 0 -135px;}

결과적으로, 서버 요청 횟수를 10번에서 하나로 줄였을 뿐만 아니라, 이미지의 전체 크기도 거의 1/5이나 줄어든 일거양득의 효과를 보여준다. 🙂

이렇게 눈에 띄는 장점이 있기 때문에, 여러 웹 페이지에서는 CSS Sprites 기법을 많이 사용하고 있는데, 일일이 그래픽 프로그램에서 여러 개의 그림을 하나의 이미지로 합치고 CSS 적용을 위한 그림의 좌표값을 얻기란 번거로울 수가 있다. 그래서, 이런 작업을 자동화해주는 도구들이 개발돼서 한둘씩 생겨나고 있는데, 대표적으로 Website Performance | CSS Sprite Generator가 있다. 이곳에서는 이미지들을 하나로 묶은 압축 파일(zip)을 올려놓으면 약간의 설정만으로 Sprite 이미지와 함께 CSS 적용 rule까지 한꺼번에 얻을 수 있어서 편하다.

아무쪼록, 이러한 기법이 널리 알려지고 많이 쓰였으면 한다.

  • IE에서는 event listeners를 붙이거나 없앨 때, addEventListener/removeEventListener 대신에, attachEvent/detachEvent를 쓴다.
  • IE에서는 event 이름의 형식이, event가 아닌, onevent 형식을 쓴다.
  • IE에서는 event object를 해당 listener의 한 전달 변수(argument)로 돌려주지 않아서, 대신 global event 변수를 써서 접근해야 한다.
  • event 발생시 기본적으로 주어진 action이 실행되는 것을 막으려면 preventDefault method를 쓰는 것이 정석이지만, IE에서는 event object의 returnValue 속성 값을 false로 지정해 주어야 한다.
  • IE는 event 전달 과정(propagation) 중에서 보통 맨 처럼 진행되는 capture phase를 지원하지 않는다.
  • 다른 객체들로의 event 전달 과정을 멈추려면, stopPropagation method를 쓰는 대신에, event object의 cancelBubble 속성 값을 true로 지정해 줘야 한다.
  • IE에서는 event listeners를 method가 아닌 독립된 function으로 불러와서, event를 일으킨 target element를 알아볼 때 간단한 this 키워드를 쓸 수가 없고, 대신에 여러 단계의 상당히 복잡한 과정을 거처야만 얻을 수 있다.

    if (typeof element.addEventListener != "undefined") {
      element.addEventListener("event", eventListener, false);
    } else if (typeof element.attachEvent != "undefined") {
      var thisListener = function() {
        var event = window.event;
        if (Function.prototype.call) {
          eventListener.call(element, event);
        } else {
          target._currentListener = eventListener;
          target._currentListener(event);
          target._currentListener = null;
        }
      };
      element.attachEvent("onevent", thisListener);
    }
  • IE에서는 어느 한 element의 event listener가 DOM에 속해있는 또 다른 node의 reference를 포함하고 있을 경우, 사용자가 다른 페이지로 이동하더라도 해당 listener와 함께 관련된 DOM node들이 메모리에서 지워지지 않고 상주하게 된다. (IE 6의 memory leak 현상은 근래에 있었던 보안 패치에도 불구하고 여전한 듯.)

프휴~ 🙁

얼마있으면 Leopard 발표와 더불어 함께 공개될 WebKit 엔진에 기초한 Safari 3를 앞두고, Apple에서는 웹 표준에 기반한 웹 개발을 도모하기 위해 더 엄격해진 코딩 기술을 요구하게 될 예정이며, 이를 위해 몇 가지 웹 표준화 대비 지침 사항들을 공개하였다.

이미 잘 알려져 있다시피, 의도하지 않은 렌더링 오류를 피하려면 올바른 Doctype의 사용을 권장하고 있으며 CSS에서 색깔 지정시 hex 값은 항상 hash(#)로 시작할 것을 요구하고 있다. 또 특정 요소에 접근할 때는 그 요소의 id 값을 사용해서, document.getElementById('myInput')처럼 쓸 것이며, document.myForm.myInput과 같은 축약 용법은 name 속성이 지정되어 있을 때만 사용할 수 있다.

이 밖에도, 어떤 요소의 속성 값을 얻을 때 다음과 같은 축약 용법 대신에,

var target = document.getElementById('someID');
var targetTitle = target.title;

표준에 정의되어 있는 method인 getAttribute, setAttribute을 쓰라고 하는데, getAttribute method의 경우 실제 다른 브라우저들에서는 돌려주는 값이 상황에 따라 저마다 다르기 때문에, 어차피 이에 대한 대비도 필요할 것이다.

그리고 그림의 크기 지정과 관련해서, 보통 지정된 그림의 크기가 실제 그림 크기와 다를 경우 이미지의 크기는 자동적으로 조절되어 표시되는데 , 만약 이 차이가 한 쪽에서만 발생할 경우 어떤 브라우저에서는 실제 그림의 측면 비율을 무시하고 지정된 한 쪽 면의 크기만 조절되어 표시되는데, Safari 3에서는 CSS 규약에 나와있는 대로 가로 세로 측면 비율에 따라 양쪽 면의 크기가 함께 자동 조절되어 표시된다. 여기서 의도되지 않은 상황을 피하려면, 양쪽 가로 세로의 크기를 함께 지정해 주면 되겠다.

결국, 웹 표준 코딩의 중요성은 앞으로도 더욱 부각될 것이지만, 더욱 성가시게 된 것은 바로 IE 6 잔당들이지.

아래는 CSS를 적용하면서, 꼭 한두 번씩은 개발자의 골치를 썩게 하였을 잘 알려진 IE 6의 버그들을 모아보았다.

Doubled Float-Margin Bug

요넘은 모든 float된 요소들에서 나타나는데, 만약 float된 방향과 같은 쪽에 margin 값을 주어서(float:left된 박스에 margin-left 값을 준 경우) 이 요소의 margin이 float된 박스를 포함하고 있는 부모 요소와 맞부딪칠 경우, 그 너비가 두 배로 늘어나는 현상. (같이 float된 요소들 중에 첫 번째 요소에서만 나타난다.)

해결책으로는, float된 박스에 display:inline을 선언해 주면 된다.
좀 더 자세한 설명은 IE Doubled Float-Margin Bug – CSS fixes and workarounds 참고.

Three-Pixel Jog

이놈은 서로 연결되어 있는 float된 요소들 사이에서 나타나는 놈으로, 잘 드러나지 않을 수도 있다. 이것은 float된 놈과 다음에 있는 inline 요소들 사이에 3 pixels 값의 공간을 추가해 버리는 버그인데, inline 요소들이 block 요소에 둘러싸여 있어도 발생한다.

다행히 쉬운 해결책으로, float된 박스와 옆에 붙어있는 박스의 각기 서로의 이웃한 margin 값을 -3px 빼주면 된다. (물론 다른 브라우저들에 영향을 미치지 않으려면 IE hacks 만을 위한 css 파일에 따로 지정해 놓아야 한다.)
추가 참고 글 – IE6 Three Pixel Gap

Absolute Positioning in a Relative Container

일반적으로, position:absolute으로 위치한 요소가 기본 static 위치가 아닌 다르게 지정된 위치(position:relative 혹은 position:absolute)를 가지는 부모 안에 포함되어 있을 경우, 브라우저 창의 왼쪽 구석이 아닌, 부모 요소의 위치를 기준으로 한 상대적 위치 값을 가지게 되는데, IE 6에서는 부모 요소에 height 값이 안 주어져 있을 경우에는 그냥 무조건 페이지 모서리를 기준으로 위치가 정해져 버린다.

해결책은, absolute으로 위치한 요소를 감싸고 있는 부모 요소에 height:1% 혹은 zoom:1 값을 지정해 주면 된다. 이렇게 하면, absolute하게 위치한 박스의 위치가 올바르게 적용되고, 또 부모 요소가 자식 요소들을 모두 둘러쌀 수 있도록 늘어나게 된다.
참고 글 – Absolutely Buggy II

Whitespace Bug

주로 ul로 메뉴를 작성할 때 나타나는 벌레로, 메뉴 사이에 의도하지 않은 빈 공백이 생기는 버그.

예를 들어 특정 너비의 세로 메뉴를 아래와 같이 짰을 경우,

<ul>
  <li><a href="page1.html">Link 1</a></li>
  <li><a href="page2.html">Link 2</a></li>
  <li><a href="page3.html">Link 3</a></li>
  <li><a href="page4.html">Link 4</a></li>
  <li><a href="page5.html">Link 5</a></li>
</ul>

IE 6에서는 코드의 li 사이에 있는 빈 공백을 페이지에도 그대로 표시해 버린다.

물론, 해결 방법으로 코드에 있는 li 사이의 공백들을 없애주면 되지만, 코드 스타일을 흐트러뜨리지 않으려면, 이젠 눈에 익은 다음과 같은 꼼수를 써주면 된다.

ul li a { height: 1%; }

더는 종잡을 수 없는 벌레들이 남기고 간 오물들로 당황하는 날이 오지 말기를 바라지만, 당장은 상황에 따라 제대로 대처할 수 있는 해결책들을 숙지해 놓는 수 밖에 없을 것이다. 😕

jQuery 1.2 발표후 보고된 자질한 버그들을 잽싸게 잡으면서 jQuery 1.2.1이 배포되고 있다.
벌레 잡기와 함께 바뀐 주요 변경 내용은, 새로 추가되었던 상대적 애니메이션 효과의 API가 기존 애니메이션 스타일과 충돌을 일으키는 문제를 해결하면서 상대적 애니메니션 효과의 문법이 아래처럼 약간 바뀌었다.

$(...).animate({ height: "+=50px", width: "-=20%", fontSize: "+=2em" });

위에서, +=는 현재 위치에서 더하고, -=는 현재의 위치에서 값을 빼라는 의미로 사용.

그리고 대체 용법을 제공하면서 사라졌던 .eq() method가 다시 추가되었다. 여기에는 많은 플러그인들이 그 동안 .eq() method를 많이 써왔다는 점과, 기존에 이것을 대체하려고 했던 .slice()가 이 경우 자연스런 해법이 아닌 것 같아서 삭제가 재고되었단다.

1.2 발표 후, 연이어 급히 벌레 잡는 발표로 불안한 인상을 주기도 하지만 그 만큼 더 안정화 되었다고 믿고 싶다.

한편, 얼마 전에 예고되었듯이 jQuery UI가 공식 페이지와 함께 공개되기 직전인가보다.
아직 공식 발표 소식은 없지만, 잠깐 데모를 살펴봤는데 Safari에서는 몇몇 동작이 제대로 동작하지 않는 등, 아직 개발이 활발히 진행중인 상태라 실제 적용에는 아직 무리가 있는 듯 하지만, 앞으로 분명 UI 개발자들에게 다양한 선택의 폭을 넓혀주리라 믿는다.

jQuery UI demo window
데모 어플리케이션 중에, JavaScript Speed Test 어플리케이션을 Safari에서 열어보면 여지없이 Safari가 뻗어버려서 주의가 요구됨. 😳