반응형

Brian J. Dillard, VP, Ajax 개발, Pathfinder Development

옮긴이: 장동수 dwkorea@kr.ibm.com

2008 년 9 월 23 일

Ajax 기술이 대규모 상용 웹 응용 프로그램의 외관을 바꾸고 있습니다. 그러나 대다수의 소규모 웹 사이트는 모든 사용자 인터페이스를 하룻밤 사이에 재구축할 만한 자원이 없습니다. 새로운 기능은 실세계 인터페이스 문제를 해결하고 사용자 경험을 향상시켜야 가치를 증명할 수 있습니다. 이 연재 기사는 오픈 소스 클라이언트측 라이브러리를 사용하여 사용자 인터페이스를 점진적으로 현대화하는 방법을 설명합니다. 이번 회에는 느리고, 복잡하고, 짜증스런 제품 상세 정보 페이지를 DHTML과 Ajax를 사용하여 빠르고, 우아하게 바꿔 보겠습니다. 점진적 개선(progressive enhancement)의 원칙을 따르면, 모든 웹 브라우저에서 사이트에 접근할 수 있습니다.

기사 소개

이 기사는 웹 1.0 쇼핑 사이트를 Ajax 기술을 사용하여 단계적으로 개선한다. 개선 전과 후의 예제 응용 프로그램 소스를 받을 수 있고(다운로드), 필자의 웹 서버에서 두 버전이 실제로 동작하는 모습을 확인할 수 있다. Ajax 기술과 모범 사례 외에도, Ajax가 점진적 개선의 원칙과 사용성, 사용자 경험 디자인(UxD: user experience design)을 통해 사용자 경험을 향상시키는 방법을 알게 될 것이다.

이 기사는 HTML과 CSS에 익숙하고, 자바스크립트와 Ajax 프로그래밍에 대해 기본적인 지식이 있다는 전제 하에 썼다. 예제 응용 프로그램은 클라이언트측 코드만 사용하여 구축했지만, 사용된 기술은 어떤 서버측 프레임워크에도 적용할 수 있다. 예제 사이트를 실행하려면, localhost에서 동작하는 기본적인 웹 서버가 필요하지만, 소스 코드를 따라가면서 필자의 웹 서버에서 실행되는 예제 사이트를 통해 결과를 확인해도 된다.




위로


1부와 2부 복습

연재의 Part 1Part 2에 서는 Customize Me Now라는 예제 응용 프로그램을 통해 웹 1.0 버전을 Ajax를 사용한 웹 2.0 버전으로 개선하는 작업을 시작하면서, 비지니스와 사용성 관점에서 그 이유를 설명했다. jQuery 자바스크립트 프레임워크와 몇 가지 플러그인을 포함한 오픈 소스 도구를 설치하는 방법도 알아보았다. 이 라이브러리들을 사용하여 Customize Me Now의 팝업과 사이트를 벗어나는 링크, 내비게이션 상의 샛길을 모달 대화상자, 툴팁, 라이트박스로 대체함으로써 사용자 흐름을 정리했다. 점진적 개선의 원칙을 따랐기 때문에 자바스크립트를 사용할 수 없는 환경에서는 개선된 웹 2.0 응용 프로그램을 웹 1.0 방식으로 사용할 수 있었다.

3부의 목표

이 번 회에는, 관리하기 힘든 제품 상세 페이지를 탭 인터페이스를 사용하여 다듬어 볼 것이다. 제품 이미지들도 회전식 슬라이드쇼를 사용해 표시할 것이다. 간단한 동적 HTML(DHTML) 기술도 사용해 보고, 좀 더 복잡한 Ajax 코드도 사용해 볼 것이다. 어느 쪽을 사용하든, 점진적 개선의 원칙에 따라 자바스크립트를 사용할 수 없더라도 페이지에 접근할 수 있도록 만들 것이다. 이를 위해 회전식 슬라이드쇼를 위한 jCarousel과 탭을 위한 jQuery UI Tabs라는 jQuery 플러그인 두 개가 더 필요하다.

이 기사의 개념을 이해하려면 Ajax 적용 전의 예제 사이트를 조금 고친 Customize Me Now 1.1을 참조하라. 1.1을 조금식 고쳐나가면서 Customize Me Now 2.1을 만들어 보자.

두 종류의 제품 상세 정보: 단일 페이지와 다중 페이지

제 품 상세 페이지는 전자 상거래 웹 사이트에서 굉장히 복잡한 부분 중 하나다. 단순한 설명과 기술 명세부터 사용자 리뷰에 이르기까지 각 제품에 대해 많은 정보를 축적하고 있다. 물론 제품에 대한 다양한 이미지도 필요하다. 사용자 경험 측면에서 고객에게 구매 결정을 위한 충분한 정보를 보여주면서도 너무 많은 정보 때문에 혼란을 주지않는 것이 중요한 과제다.

Customize Me Now 1.0은 단일 페이지에 적합한 제품 상세 페이지를 갖고 있었기 때문에 Ajax를 통한 개선을 적용하기 쉬웠다. Customize Me Now 2.0에서는 원래 페이지를 jQuery와 Thickbox를 사용한 모달 대화상자로 대체하여, 사용자가 검색에서 구매까지 따라가게 될 "행복한 길"을 정리할 수 있었다.

이제 요구사항이 바뀌었다. Customize Me Now 1.1은 1.0보다 훨씬 더 자세한 제품 정보를 제공한다. 여기에는 여러 개의 긴 텍스트 블록과 큰 사진이 포함된다(이미지는 유명한 웹 2.0 사진 공유 서비스인 플리커에서 발췌했다). Ajax가 도입되기 전에는 이런 대량의 정보를 표시하는 방법은 두 가지가 있었다. 스크롤이 필요한 긴 페이지(그림 1그림 2)를 사용하거나 여러 개로 쪼개진 작은 페이지들을 사용하는 방법(그림 3그림 4)이다.

Customize Me Now 1.1을 브라우저를 통해 보면, 제품 상세 페이지의 버전 A와 B를 Customize Me Now 1.0의 이전 버전과 비교해 볼 수 있다. 세 가지 버전을 볼 수 있는 링크가 전역 헤더와 푸터에 들어 있다. 그림에서 알 수 있듯이, 사용성 측면에서 버전 A와 B는 이전 버전보다 더 골치 아프다. 확실한 것은 제품 상세를 보여주는 이 새 버전에는 Thickbox 모달 대화상자가 그다지 이상적인 방법이 아니라는 점이다.


그림 1. 제품 상세 정보 페이지 버전 A: 단일 페이지, 텍스트 내용
제품 상세 정보 페이지 버전 A: 단일 페이지, 텍스트


그림 2. 제품 상세 정보 페이지 버전 A: 단일 페이지, 이미지
제품 상세 정보 페이지 버전 A: 단일 페이지, 이미지

버 전 A는 사용자뿐 아니라 브라우저와 서버에게도 과도한 부담을 준다. 사용자는 과도한 정보(화면에 보이지 않는 정보의 분량을 알고 있다면)에 부담을 갖게 되고, 브라우저와 서버는 네트워크를 타고 전송되는 대량의 정보에 부담을 갖게 된다. 단지 사진 여섯 장만이라면 광대역 네트워크에서는 빠르게 불러올 수 있겠지만, 사진이 16장 또는 60장이거나 사용자 리뷰가 150개라면 문제가 심각해진다. 사용자가 느린 네트워크를 사용한다면 어떻게 될까? 제품에 대한 모든 가용한 정보를 한 번에 불러오면 성능은 심각하게 떨어지고, 사용자는 엄청난 정보를 파악해야 하느라 괴롭다.


그림 3. 제품 상세 정보 페이지 버전 B: 다중 페이지, 텍스트 내용
제품 상세 정보 페이지 버전 B: 다중 페이지, 텍스트 내용


그림 4. 제품 상세 정보 페이지 버전 B: 다중 페이지, 이미지
제품 상세 정보 페이지 버전 B: 다중 페이지, 이미지

버 전 B는 사용자에게 각 페이지마다 소량의 정보만 표시함으로써 사용자의 부담을 덜어주려고 한다. 그러나 사용자가 추가 정보를 보려고 할 때마다 링크를 클릭하고 새 페이지가 로드되길 기다려야 한다. 더구나, 각 버전 B의 각 하위 페이지는 혼란스러운 수십 개의 링크를 포함하고 있다. 정보는 정리되었지만 내비게이션은 더 어지러워졌다.

단일 페이지 버전 전면 개편

버 전 A를 회전식 슬라이드쇼(image carousel)와 탭 인터페이스를 사용하여 개선하는 작업은 모든 것이 한 페이지에 있기 때문에 별 어려움이 없다. Ajax는 필요 없고, 구닥다리 DHTML만 있으면 된다. 이 접근법의 장점은 점진적 개선이라는 것이다. 서버는 여전히 스크롤이 필요한 긴 페이지를 브라우저로 전송하지만, 자바스크립트가 이를 좀 더 현대적인 사용자 인터페이스로 바꿔준다. 자바스크립트를 사용할 수 없는 브라우저에서는 원래 페이지가 그대로 보일 것이다. 물론, 이 방법은 단일 페이지의 대역폭 문제에는 도움이 되지 않는다.

오픈 소스 도구 다운로드와 설치

Ajax로 페이지를 뜯어 고치기 위해, jQuery 최신 버전을 다운로드하자(참고자료). 연재의 Part 1과 Part 2를 계속 봤다면 jQuery 1.2.1이 이미 설치되어 있을 것이다. 글을 쓰는 시점에서, 현재 버전은 몇 가지 버그가 수정된 1.2.3이다.

플러그인도 두 개 다운로드해야 한다(참고자료). jQuery UI Tabs는 융통성 있는 사용자 인터페이스 위젯과 컴포넌트 집합인 jQuery UI의 일부다. jQuery UI Tabs는 ul 태그를 탭 인터페이스로 바꿔주며, 각 탭의 내용을 내장하거나 Ajax로 바꿀 수 있다. 반면, jCarousel은 이미지 여러 개를 슬라이드쇼로 바꿔주는 독립형 플러그인이다. jQuery UI 탭과 함께 사용하면 이 슬라이드쇼를 내장하거나 Ajax로 바꿀 수 있다.

이 컴포넌트들을 다운로드했으면 응용 프로그램의 적절한 디렉터리 구조에 넣어두자. 각 다운로드에 포함된 예제 코드와 추가 파일들을 지워도 무방하다. 각 자바스크립트 라이브러리의 압축된 버전은 실 서비스에 적합하지만, 전체 소스 코드를 남겨두면 각 컴포넌트를 분석하고 이해하는 데 도움이 된다.

  • jQuery: 라이브러리 자체의 압축된 버전만 있으면 된다.
  • jQuery UI Tabs: 라이브러리의 압축된 버전과 딸린 CSS 파일, 그리고 이미지 파일 두 개(loading.gif와 tab.png)를 남겨 두어야 한다. tab.png는 배경색이 흰색인 사이트에 맞춰져 있으므로, 필요하다면 어도비 포토샵이나 여타 이미지 편집 프로그램을 사용하여 Customize Me Now의 배경색과 어울리도록 둥근 모서리를 수정해야 한다.
  • jCarousel: 라이브러리의 압축된 버전과 딸린 CSS 파일, 그리고 포함된 세 개의 스킨 중의 하나는 남겨두어야 한다. jCarousel의 스킨은 CSS 파일 한 개와 모양을 꾸미는 데 필요한 이미지 파일들로 구성된다. Customize Me Now의 경우, Tango 스킨을 "tango-modified"라는 이름으로 바꾸어 사용하고 있다. 기본 Tango 테마를 조금 고칠 것이다.

파일 위치를 정했으면, detailA.html의 앞쪽에 포함시키자. 결과가 Listing 1이다.


Listing 1. jQuery와 플러그인 포함시키기
                

<!--jquery assets-->
<script type="text/javascript"
src="../js/jquery-1.2.3.minjs"></script>

<!--jquery.ui.tabs assets-->
<script type="text/javascript"
src="./ui.tabs/ui.tabs.pack.js"></script>
<link rel="stylesheet" href="../ui.tabs/ui.tabs.css"
type="text/css" media="print, projection, screen">

<!--jcarousel assets-->
<script type="text/javascript"
src="../jcarousel/lib/jquery.jcarousel.pack.js"></script>
<link rel="stylesheet" type="text/css"
href="../jcarousel/lib/jquery.jcarousel.css" />
<link rel="stylesheet" type="text/css"
href="../jcarousel/skins/tango-modified/skin.css" />


DHTML로 회전식 슬라이드쇼 만들기

DHTML 로 회전식 슬라이스쇼를 만들려면 먼저 jCarousel의 외양을 수정해야 한다. jCarousel은 수평 또는 수직으로 표시할 수 있으며, 각 스타일시트는 두 가지 형태에 대한 규칙을 포함하고 있다. 수평 슬라이드쇼만 사용한다면 수직 슬라이드쇼와 연관된 모든 CSS 스타일을 지워도 무방하다. 덧붙여서 각 요소의 너비, 높이, 패딩, 마진 등도 수정해야 한다. 기본적으로, jCarousel은 썸네일 이미지 세 개를 표시하는 슬라이드쇼를 만든다. 이미지가 훨씬 더 크다면(500 픽셀 이상) 한 번에 이미지 하나만 표시할 수도 있다. 수정된 skin.css가 Listing 2에 나와 있다.


Listing 2. 슬라이드쇼를 위한 CSS 코드
                
..jcarousel-skin-tango.jcarousel-container {
-moz-border-radius: 10px;
background: #F0F6F9;
border: 1px solid #346F97;
}

..jcarousel-skin-tango.jcarousel-container-horizontal {
width: 502px;
padding: 20px 125px !important;
}

.jcarousel-skin-tango .jcarousel-clip-horizontal {
width: 502px;
height: 410px;
}

..jcarousel-skin-tango .jcarousel-item {
width: 502px;
height: 410px;
}

..jcarousel-skin-tango .jcarousel-item-horizontal {
margin-right: 125px;
}

..jcarousel-skin-tango .jcarousel-item-placeholder {
background: #fff;
color: #000;
}

/**
* Horizontal Buttons
*/
..jcarousel-skin-tango .jcarousel-next-horizontal {
position: absolute;
top: 43px;
right: 5px;
width: 32px;
height: 32px;
cursor: pointer;
background: transparent url(next-horizontal.png) no-repeat 0 0;
}

..jcarousel-skin-tango .jcarousel-next-horizontal:hover {
background-position: -32px 0;
}

..jcarousel-skin-tango .jcarousel-next-horizontal:active {
background-position: -64px 0;
}

..jcarousel-skin-tango .jcarousel-next-disabled-horizontal,
..jcarousel-skin-tango .jcarousel-next-disabled-horizontal:hover,
..jcarousel-skin-tango .jcarousel-next-disabled-horizontal:active {
cursor: default;
background-position: -96px 0;
}

..jcarousel-skin-tango .jcarousel-prev-horizontal {
position: absolute;
top: 43px;
left: 5px;
width: 32px;
height: 32px;
cursor: pointer;
background: transparent url(prev-horizontal.png) no-repeat 0 0;
}

..jcarousel-skin-tango .jcarousel-prev-horizontal:hover {
background-position: -32px 0;
}

..jcarousel-skin-tango .jcarousel-prev-horizontal:active {
background-position: -64px 0;
}

..jcarousel-skin-tango .jcarousel-prev-disabled-horizontal,
..jcarousel-skin-tango .jcarousel-prev-disabled-horizontal:hover,
..jcarousel-skin-tango .jcarousel-prev-disabled-horizontal:active {
cursor: default;
background-position: -96px 0;
}

계속해서, detailA.html 파일의 마크업도 조금 수정해야 한다. 원래 버전에는 이미지들이 ul 태그로 구조화되어 있는데, 이것을 div 태그로 둘러싸야 한다. 둘러싼 div 태그의 class 속성을 jcarousel-skin-tango로 지정해야 수정된 Tango 스킨의 스타일 규칙들이 슬라이드쇼에 적용된다. 그리고 divid 속성을 imageCarousel로 지정해야 jQuery가 DOM(Document Object Model)을 파싱하여 변형시켜 준다. 수정된 HTML이 Listing 3이다.


Listing 3. 슬라이드쇼를 위한 HTML 코드
                
<div id="imageCarousel" class="jcarousel-skin-tango">
<ul class="productImages">
<li>
<img class="product" alt="product photo"
width="500" height="375" src="../img/pizza1.jpg" />
Photo credit: <a target="_blank"
href="http://www.flickr.com/photos/kankan/">Kanko*</a>,
Flickr, Creative Commons Attribution License
</li>
<!--additional <li> items, images and photo credits here-->
</ul>
</div>

마지막으로, 슬라이드쇼를 만드는 자바스크립트 코드를 작성해야 한다. jCarousel이 대부분 알아서 처리하므로 몇 줄만 작성하면 된다. HTML 문서의 헤더 안쪽에서 자바스크립트와 CSS를 포함시키는 문장 아래에 Listing 4의 스크립트 블록을 추가하자.


Listing 4. 슬라이드쇼를 위한 자바스크립트 코드
                
<script type="text/javascript">
$(document).ready(function() {

$('#imageCarousel').jcarousel({
scroll: 1
});

});
</script>

자바스크립트 코드 자체는 간단하지만 많은 일을 수행한다. jQuery의 ready 이벤트 핸들러를 사용하여, 페이지의 DOM이 완전히 로드된 후에 추가적인 작업을 수행한다. jQuery의 셀렉터 기능을($ 함수) 사용하여, id 속성이 imageCarousel인 DOM 노드를 찾는다. DOM 노드의 jcarousel 메서드를 호출하여, 지정한 노드 안쪽의 모든 노드를 회전식 슬라이드쇼로 변형시킨다. jcarousel 메서드는 인자가 한 개인데, 설정 매개변수들을 해시로 지정한다. 대부분의 jCarousel의 기본 설정은 그대로 쓰고, 한 번에 이미지 한 개만 표시하도록 scroll을 1로 지정했다.

이것으로 회전식 슬라이드쇼를 완성했다.

DHTML로 탭 만들기

이번에는 페이지에서 각 섹션의 내용을 DHTML 탭으로 만들어보자. 먼저, 각 섹션의 내용을 유일한 id 속성과 class 속성이 tabContentdiv 태그로 둘러싸야 한다. 각 섹션의 내용은 Listing 5와 비슷하다. 이렇게 하면 jQuery UI Tabs가 탭으로 변형시킬 부분을 알 수 있다.


Listing 5. 다섯 개의 섹션을 가진 HTML(detailA.html)
                
<div class="tabContent" id="introduction">

<h2>Introduction</h2>

<!--paragraphs of text content here-->

</div>

<div class="tabContent" id="moreDetails">

<h2>More Details</h2>

<!--paragraphs of text content here-->

</div>

<div class="tabContent" id="userReviews">

<h2>User Reviews</h2>

<!--paragraphs of text content here-->

</div>

<div class="tabContent" id="techSpecs">

<h2>Technical Specifications</h2>

<!--paragraphs of text content here-->

</div>

<div class="tabContent" id="productImages">

<h2>Product Images</h2>

<div id="imageCarousel" class="jcarousel-skin-tango">
<ul class="productImages">
<!--<li> items from your image slideshow here-->
</ul>
</div>

</div>

다음으로 HTML 문서의 맨 앞에, 첫 번째 섹션 바로 앞에 순서 없는 링크 목록을 추가해야 한다. jQuery UI Tabs가 이 마크업을 탭 인터페이스로 변형시킨다. href 속성이 문서 내부의 앵커처럼 보이지만, 실제로는 앞에서 추가한 각 섹션을 둘러싼 div 태그의 id 속성과 일치해야 한다. 수정한 결과가 Listing 6이다.


Listing 6. 추가 HTML 내용(detailA.html)
                
<ul class="navTabs">
<li><a href="#introduction"><span>Introduction</span></a></li>
<li><a href="#moreDetails"><span>More Details</span></a></li>
<li><a href="#userReviews"><span>User Reviews</span></a></li>
<li><a href="#techSpecs"><span>Technical Specifications</span></a></li>
<li><a href="#productImages"><span>Product Images</span></a></li>
</ul>

다음으로, 자바스크립트를 사용할 수 있을 때와 그렇지 않을 때 사용할 스타일을 만들어야 한다. 자바스크립트를 사용할 수 있으면, 새로운 탭 인터페이스를 보게 되므로 예쁘게 꾸밀 필요가 있다. 패딩과 보더를 좀 주고, 각 탭 섹션 안쪽의 h2 태그를 표시하지 않도록 하자. 이 태그는 새로운 인터페이스에서는 탭의 레이블과 중복되므로 불필요하다. 이 스타일들은 전역 스타일시트에 추가해야 한다.

자바스크립트를 사용할 수 없으면 탭 인터페이스를 볼 수 없다. 이 경우에는 문서에서 앞서 만든 순서없는 목록을 숨기고, 섹션의 내용에 추가한 보더와 패딩을 제거하고, h2 태그도 표시해야 한다. noscript 태그를 사용하면 쉽게 할 수 있다. 자바스크립트를 사용할 수 없는 브라우저에서 전역 스타일시트를 재정의하려면, CSS 규칙들을 noscript 태그 안쪽에 추가해야 한다. 이 스타일들은 자바스크립트를 사용할 수 없을 경우에만 활성화된다.

최종적으로 브라우저의 기능에 맞춰 보이는(좀 다르게 보이긴 하겠지만) 두 가지 스타일이 만들어졌다. 이렇게 만들어진 두 가지 CSS 스타일이 Listing 7과 Listing 8이다.


Listing 7. 추가적인 CSS 스타일(customizemenow.css)
                
#CMN .tabContent {
padding: 14px;
border: 1px solid #97a5b0;
}
#CMN .tabContent h2{
display: none;
}


Listing 8. 추가적인 스타일(detailA.html의 noscript 블록)
                
<noscript>
<style type="text/css">
#CMN .tabContent {
padding: 0;
border: 0;
}
#CMN .tabContent h2 {
display: block;
}
#CMN ul.nav {
display: none;
}
</style>
</noscript>

마지막으로, 앞에서 추가한 스크립트 블록(Listing 4) 뒤에 DHTML 탭을 만드는 자바스크립트를 추가하자(Listing 9).


Listing 9. 커스텀 자바스크립트(detailA.html)
                
$(document).ready(function() {

/*earlier jCarousel code goes first*/

/*create tabs from an unordered list using jquery.ui.tabs*/
$('ul.navTabs').tabs(
{ fx: { height: 'toggle', opacity: 'toggle' } }
);

});

jCarousel과 마찬가지로 jQuery UI Tabs를 위한 자바스크립트 코드는 간단하다. jQuery의 셀렉터 기능을 사용하여 순서 없는 링크 목록의 DOM 노드를 찾는다. tabs 메서드는 목록 노드 안쪽에 있는 ul 태그와 자식 li 태그들을 탭 인터페이스로 변형시키고, 목록 안쪽의 링크에 대응하는 div 노드를 찾는다. 이 div들은 사용자가 대응하는 노드를 클릭하기 전까지는 숨겨져 있다. jcarousel 메서드와 마찬가지로, 초기화 매개변수 해시를 통해 tabs 메서드의 동작을 조절할 수 있다. 여기서는 멋진 시각 효과를 추가했다. 탭 인터페이스를 먼저 만들고 jCarousel을 만들면 회전형 슬라이드쇼를 위한 CSS 속성과 혼동되어 문제를 유발할 수 있다.

단일 페이지 버전 검토하기

완성된 Customize Me Now 2.1의 제품 상세 페이지 버전 A에서 DHTML 탭(그림 5)과 회전식 슬라이드쇼(그림 6)를 확인할 수 있다. 브라우저로 페이지를 열어보면 페이드-아웃이나 윈도-셰이드 효과 같은 시각 효과를 볼 수 있다.


그림 5. 개선된 제품 상세 페이지: 탭 인터페이스
개선된 제품 상세 페이지: 탭 인터페이스


그림 6. 개선된 제품 상세 페이지: 회전형 슬라이드쇼
개선된 제품 상세 페이지: 회전형 슬라이드쇼

Customize Me Now 2.1 버전의 제품 상세 페이지 버전 A는 Customize Me Now 1.1 버전의 같은 페이지에 비해 몇 가지 장점이 있다. 정보의 복잡성과 분량을 감추어 사용자에게 덜 위압적이다. 탭 내비게이션 메타포를 통해 정보를 사용자가 요약할 수 있을 정도로 나누었다. 브라우저의 자바스크립트를 중지시키고 페이지를 다시 열면, 1.1 버전과 거의 같은 페이지를 볼 수 있다. 안타깝지만, 이 버전은 단일 페이지의 대역폭 문제를 해결하지 못한다. 사용자는 어쩌면 볼 기회도 없을 엄청난 양의 텍스트와 그림을 모두 다운로드한다. 다음에 이 문제에 대한 해결책을 알아보자.

다중 페이지 버전 전면 개편

지 금까지 제품 상세 페이지의 단일 페이지 버전에 탭과 회전식 슬라이드쇼를 추가해 보았다. 이제 비슷한 변형을 다중 페이지 버전에 적용해보자. Ajax를 사용하여 탭과 슬라이드쇼의 내용을 동적으로 가져오기 때문에 코드는 좀 더 복잡하다. 그 덕분에 페이지를 더 빨리 불러오고 대역폭도 절약된다. 추가 내용은 필요할 때만 동적으로 가져온다.

Ajax로 슬라이드쇼 만들기

제 품 상세 페이지의 다중 페이지 버전에 회전식 슬라이드 쇼를 추가하기 위해, 먼저 새로운 JSON(JavaScript Object Notation: 자바스크립트 객체 표기법) 문서를 만들어야 한다. JSON은 XML과 비슷하지만 더 가벼운 데이터 전송 형식이다. jQuery나 여타 Ajax 프레임워크를 사용하면 JSON 문서를 자바스크립트 객체로 안전하고 신속하게 변환할 수 있으므로, Ajax를 위한 최적의 형식이다. JSON 문서는 마크업을 포함하지 않으며, 자바스크립트 코드가 슬라이드쇼로 변형할 이미지의 URL과 메타데이터만 포함하고 있다. 만들어진 문서를 detailB5-fragment.html 파일에 저장하자(Listing 10).


Listing 10. JSON 데이터(detailB5-fragment.html)
                
{"items": [
{
"url": "pizza1.jpg",
"width": "500",
"height": "375",
"creditURL": "kankan",
"creditLabel": "Kanko*"
},
{
"url": "pizza2.jpg",
"width": "500",
"height": "374",
"creditURL": "lenore-m",
"creditLabel": "L. Marie"
},
{
"url": "pizza3.jpg",
"width": "500",
"height": "375",
"creditURL": "roadhunter",
"creditLabel": "Topato"
},
{
"url": "pizza4.jpg",
"width": "500",
"height": "369",
"creditURL": "sgt_spanky",
"creditLabel": "Kevitivity"
},
{
"url": "pizza5.jpg",
"width": "500",
"height": "368",
"creditURL": "fooey",
"creditLabel": "fo챕철횧oooey"
},
{
"url": "pizza6.jpg",
"width": "500",
"height": "334",
"creditURL": "pancakejess",
"creditLabel": "jsLander"
}
]}

다음으로, jQuery, jQuery UI Tabs, jCarousel 파일을 앞서 detailA.html에(Listing 1) 했던 것과 같은 방식으로 detailB1.html의 헤더에 추가하자. 그런 다음, 회전식 슬라이드쇼가 놓일 자리를 detailB1.html의 본문에 추가하자. jCarousel은 지금 추가한 빈 순서없는 목록을 이미지로 채울 것이다(Listing 11).


Listing 11. 자바스크립트 코드(detailB1.html)
                
<div class="tabContent" id="productImages">

<h2>Product Images</h2>

<div id="imageCarousel" class="jcarousel-skin-tango">
<ul class="productImages">
</ul>
</div>

</div>

마지막으로, 자바스크립트 코드를 HTML 문서의 헤더에 추가해야 한다(Listing 12).


Listing 12. 자바스크립트 코드(detailB1.html)
                
<script type="text/javascript">

window.alert = function() {
return;
};

$(document).ready(function() {

/*
create an image slideshow from a JS array of URLs using
jcarousel
*/
var itemLoadCallback = function(carousel, state) {
if (state != 'init') {
return;
}
jQuery.getJSON("detailB5-fragment.html", function(data){
itemAddCallback(carousel, carousel.first,
carousel.last, data.items);
});
};

var itemAddCallback = function(carousel, first, last, data) {
for (i = 0, j = data.length; i < j; i++) {
carousel.add(i, getItemHTML(data[i]));
}
carousel.size(data.length);
};

var getItemHTML = function(d) {
return '<img class="product" alt="product photo" width="' +
d.width + '" height="' +
d.height + '" src="../img/' +
d.url + '" />Photo credit: <a target="_blank"' +
' href="http://www.flickr.com/photos/' +
d.creditURL + 's/">' +
d.creditLabel + '</a>, Flickr, ' +
'Create Commons Attribution License'
;
};

jQuery('#imageCarousel').jcarousel({
itemLoadCallback: itemLoadCallback,
scroll: 1
});

jQuery('ul.productImages').css("width","3012px");

});
</script>

보는 바와 같이 자바스크립트가 앞에서 만든 것들보다 더 복잡하다. 포함된 함수들은 다음과 같다.

  • itemLoadCallback: 앞서 만든 JSON 문서에서 Ajax를 사용하여 데이터를 읽어 itemAddCallback 함수에 전달한다.
  • itemAddCallBack: itemLoadCallback 함수로 읽어온 JSON 데이터를 파싱하여 슬라이드쇼에 이미지들을 추가한다.
  • getItemHTML: JSON 데이터를 DOM에 추가할 jCarousel을 위한 마크업을 만든다.

jcarousel 메서드를 호출할 때 itemLoadCallback을 인자로 전달하면, jcarousel은 DOM에 이미 존재하는 마크업 대신 Ajax를 통해 동적으로 가져온 데이터를 슬라이드쇼로 만든다. itemLoadCallback과 도우미 함수들은 각각의 역할을 수행하지만, 두 가지 문제점이 있다.

첫 번째 문제는, jCarousel은 동적으로 로드한 데이터의 CSS 속성을 제대로 계산하지 못하기 때문에, 슬라이드쇼의 몇몇 이미지가 보이지 않는다. 디버깅을 통해 jCarousel이 이미지를 포함하고 있는 ul 태그의 너비를 잘못 계산한다는 사실이 밝혀졌다. 시간을 들여 디버깅을 하고 jCarousel을 확장하여 버그를 수정할 수도 있겠지만, 단순무식한(brute-force) 방법이 더 빠르다. 슬라이드쇼가 만들어진 직후, 각각의 너비를 jQuery의 css 메서드를 사용하여 제품 상세 페이지 버전 A에서 정적으로 생성했던 슬라이드쇼의 너비와 일치하도록 바꾸면 된다.

두 번째 문제도 첫 번째 문제와 관련이 있다. jCarousel이 DOM 노드의 너비를 제대로 계산하지 못하기 때문에, 브라우저 창의 크기를 바꿀 때마다 자바스크립트 경고 창이 뜬다. 즉 좋은 사용자 경험이 아니다. 이것을 막으려면, 자바스크립트의 내장 메서드인 window.alert를 더미 함수로 대체하면 된다.

Ajax로 탭 만들기

jQuery UI Tabs를 다중 페이지 버전의 제품 상세 페이지에 적용하기 위해, 정적인 내용과 Ajax를 통해 동적으로 가져온 내용을 섞어 사용한다. detailB1.html은 이전에 detailB2.html, detailB3.html, detailB3.html, detailB4.html에 포함된 모든 내용을 포함하는 래퍼(wrapper) 역할을 한다. 그러나 Ajax를 통해 이 파일들의 내용을 detailB1.html에 가져올 때, 각 페이지가 완전한 HTML이라는 점을 고려해야 한다. 실제로는 각 페이지마다 div 태그 한 개만 필요하다. 이 문제를 피하려면, 각 페이지를 새 파일 이름으로 복사하고 필요없는 마크업을 제거하면 된다. 그렇게 만들어진 파일이 detailB2-fragment.html, detailB3-fragment.html, detailB4-fragment.html이다(Listing 13). 실무에서는 서버측의 템플릿 엔진을 사용해 완전한 HTML과 조각 HTML을 모두 만들 수 있다. 예를 들어, 루비 온 레일즈에서는 같은 내용에 대한 요청이 Ajax인지 아닌지에 따라 다른 래퍼를 적용할 수 있다. 여기서는, 클라이언트측 기술만 사용하다 보니, 파일을 분리하여 같은 효과를 흉내낸 것이다.


Listing 13. HTML 조각 파일의 마크업
                
<div class="tabContent" id="moreDetails">

<h2>More Details</h2>

<!--paragraphs of text content here-->

</div>

다음으로, detailB1.html 파일의 마크업을 조금 수정해야 한다. Customize Me Now 1.1에서는 사용자가 페이지 사이를 쉽게 오갈 수 있도록 2차 내비게이션 메뉴를 포함하고 있었지만(Listing 14), 조금만 수정하면 jQuery UI Tabs를 사용하여 탭 인터페이스로 변형할 수 있다. 수정된 버전이 Listing 15이다.


Listing 14. detailB1.html의 두 번째 내비게이션의 원래 마크업
                
<ul class="nav">
<li><a href="detailB1.html">Introduction</a></li>
<li><a href="detailB2.html">More Details</a></li>
<li><a href="detailB3.html">User Reviews</a></li>
<li><a href="detailB4.html">Technical Specifications</a></li>
<li class="last"><a href="detailB5a.html">Photos</a></li>
</ul>


Listing 15. Ajax 탭을 위한 detailB1.html의 마크업
                
<ul class="nav">
<li><a href="detailB1.html"><span>Introduction</span></a></li>
<li><a href="detailB2.html"><span>More Details</span></a></li>
<li><a href="detailB3.html"><span>User Reviews</span></a></li>
<li><a href="detailB4.html"><span>Technical Specifications</span></a></li>
<li class="last"><a href="detailB5a.html"><span>Product Images</span></a></li>
</ul>

다음으로, 슬라이쇼 관련 코드 앞에 자바스크립트 코드를 추가해야 한다(Listing 16).


Listing 16. Ajajx 탭을 만들기 위한 스크립트 블록
                
$(document).ready(function() {

/*transform urls for tabs with inline content*/
$('ul.nav > li:first > a').attr("href", "#introduction");
$('ul.nav > li:last > a').attr("href", "#productImages");

/*transform urls for tabs with ajax content*/
$('ul.nav > li:not(:first):not(:last) > a').each(function (i) {
var el = $(this);
el.attr("href", el.attr("href").replace(".html",
"-fragment.html"));
});

/*earlier jCarousel code goes here*/

/*
replace ul classname of "nav" with "navTabs" to
reset styling to a blank state
*/
$('ul.nav').attr({"class":"navTabs"});

/*create tabs from an unordered list using jquery.ui.tabs*/
$('ul.navTabs').tabs(
{ fx: { height: 'toggle', opacity: 'toggle' } }
);

});

Ajax 탭을 위한 자바스크립트 코드는 앞에서 만든 DHTML 버전보다 더 복잡하지만, 이것은 점진적 개선을 지원하기 위함이다. 2차 내비게이션 메뉴의 마크업(Listing 15) 을 변경할 때 링크 URL은 바꾸지 않았다. 이렇게 하면, 자바스크립트를 사용할 수 없는 환경에서도 각 링크를 통해 모든 내용에 접근할 수 있다. 그러나 Ajax 탭을 만들려면 이 링크들을 jQuery UI Tabs가 지원하는 형식으로 바꿔야 한다. 소개와 제품 사진 탭의 경우 정적인 내용을 포함하고 있으므로 href 속성이 #wrapperDivIDAttribute와 같은 형식이어야 하며, 다른 세 개의 탭의 경우 Ajax를 통해 내용을 가져와야 하므로 href 속성이 앞에서 만든 HTML 조각 파일을 가리켜야 한다. jQuery를 사용하면 이 링크들을 쉽게 바꿀 수 있다.

jQuery를 사용하면 2차 내비게이션 메뉴의 class 속성도 쉽게 바꿀 수 있다. 메뉴를 구성하는 ul 태그는 여러 가지 스타일을 사용하는데, li 태그를 탭으로 변형하면, 원래 스타일이 취소된다. 이를 해결하기 위해 jQuery를 사용하여 ul 태그의 class 속성을 nav에서 navTabs로 바꾸었다. 이제, jQuery의 셀렉터 기능($ 함수)을 사용하여 class 속성이 바뀐 ul 태그를 찾아 tabs 메서드를 적용하면 된다.

대충 끝났지만, CSS와 관련해 몇 가지 작업이 더 남았다. 먼저, 이 페이지의 Ajax 버전에만 적용할 스타일을 모두 재정의해야 한다. 앞에서 했던 것과 비슷한 방식으로 noscript 태그를 사용하는데, 이번에는 회전식 슬라이드쇼로 변형될 마크업을 숨기기 위해 스타일을 추가로 하나 더 정의한다. 이렇게 만들어진 noscript 스타일 블록이 Listing 17이다.


Listing 17. detailB1.html의 noscript 블록에 정의된 스타일
                
<noscript>
<style type="text/css">
#CMN .tabContent {
padding: 0;
border: 0;
}
#CMN .tabContent h2 {
display: block;
}
#CMN #productImages {
display: none;
}
</style>
</noscript>

마지막으로, jQuery UI Tabs가 Ajax를 통해 가져온 내용을 탭 인터페이스에 끼워넣을 때 발생하는 골치거리를 해결해야 한다. 만들어진 페이지를 지금 웹 브라우저로 보면 탭과 탭 안쪽은 내용 사이에 이중 경계선을 볼 수 있다. jQuery UI Tabs가 각 탭 안쪽 내용을 둘러싼 div 태그에 상단 경계선을 추가하는데, HTML 조각에도 이미 경계선이 추가되어 있기 때문에 이중 경계선을 보게 된다. 이를 해결하려면 HTML 조각의 div 태그에서 상단 경계선을 제거하는 CSS 클래스를 추가해야 한다. 이렇게 만들어진 것이 Listing 18과 19다.


Listing 18. 이중 경계선 문제를 해결하기 위한 CSS 선언
                
#CMN .tabContent.noTop {
border-top: 0;
}


Listing 19. HTML 조각 파일에 적용된 CSS 클래스
                
<div class="tabContent noTop" id="moreDetails">

<h2>More Details</h2>

<!--paragraphs of text content here-->

</div>

다중 페이지 버전 검토하기

제품 상세 페이지 버전 B의 Customize Me Now 2.1 버전을 웹 브라우저로 보면 버전 A(그림 5, 그림 6) 와 외관, 동작이 비슷함을 알 수 있다. 그러나 구현은 버전 B가 훨씬 더 복잡하다. 사용자가 탭을 클릭하기 전에는 해당 내용을 가져오지 않으므로 대역폭이 절약되지만, 이미지에 대해서는 대역폭을 더 절약하지 못했다. 여기서 작성한 Ajax 코드가 동작하는 방식이 jCarousel 슬라이드쇼의 모든 이미지가 미리 로드되어 있다고 가정하고 만들어져 있기 때문이다. 그러나 페이지 적재 시간은 개선했다. 브라우저는 페이지의 나머지가 모두 표시되기 전에는 서버에 이 이미지들을 요청하지 않기 때문에, 느린 네트워크 환경에서 사용자 경험을 상당히 개선했다.

제품 사진 탭을 선택하기 전에는 이미지를 로딩하지 않도록 만들 수도 있다. 이렇게 하면 이미지를 보고 싶어하지 않는 사용자는 대역폭을 낭비하지 않게 된다. 자바스크립트 코드가 더 복잡해지겠지만, 제품 사진이 많다면, 연습삼아 해볼 만하다.

제품 상세 페이지의 버전 A와 버전 B의 차이는 자바스크립트를 껐을 때 드러난다. 버전 A는 스크롤이 필요한 단일 페이지로 보이겠지만 버전 B는 여러 개의 페이지로 동작한다.

결론

연 재 3회에서는 점진적 개선의 원칙을 지키면서도 현대적이고, 매력적이고, 사용성이 높은 Ajax 인터페이스를 만들기 위해 노력했다. 또한 더 강력한 jQuery의 기능과 다양한 플러그인도 배웠다. 이 기사에서 배운 기술들을 사용하여 독자의 사이트를 개선해보자. 예를 들어, jQuery Cookie 플러그인을 사용하면 사용자가 마지막으로 선택했던 탭을 기억했다가 사용자가 돌아왔을 때 그 탭을 표시할 수 있다. 탭 인터페이스를 구매 확인 페이지에 적용할 수도 있다. 가능성은 무궁무진하다.

실 무에서는 제품 상세 페이지를 세 가지 다른 버전으로 만들기 위해 시간을 낭비하지는 않을 것이다. 시간도 너무 많이 걸리고, 서로 다른 인터페이스가 섞여 사용자의 혼란을 유발한다. 이러한 것을 개의치 않는다면, 이 기사는 jQuery 같은 오픈 소스 도구의 강력한 기능과 융통성 덕분에 Ajax, 점진적 개선, 사용자 중심 설계가 얼마나 쉬운지 보여준다.





위로


다운로드 하십시오

설명 이름 크기 다운로드 방식
개선 전 예제 소스 코드 wa-aj-overhaul3OnePointOne.zip 797KB HTTP
개선 후 예제 소스 코드 wa-aj-overhaul3TwoPointOne.zip 889KB HTTP
다운로드 방식에 대한 정보

더 많은 다운로드

Notes

  1. 필자의 웹 사이트에서 개선 전의 데모 응용 프로그램을 볼 수 있다.
  2. 필자의 웹 사이트에서 개선 후의 데모 응용 프로그램을 볼 수 있다.
  3. 필자의 웹 사이트에서 연재 기사에서 소개된 모든 버전의 데모 응용 프로그램을 볼 수 있다.


참고자료

교육
  • JSON에 대해 더 배워보자.

  • Learning jQuery 웹 사이트를 통해 jQuery 커뮤니티에 참여하고 튜토리얼과 포럼을 확인하자.

  • jQuery 문서 웹 사이트를 통해 jQuery API를 배울 수 있다.

  • "jQuery로 Ajax 개발을 단순화하기"(Jesse Skinner, 한국 developerWorks, 2007년 9월) 기사를 통해 Ajax를 더 심도있게 익힐 수 있다.

  • Learning jQuery(Packt Publishing, 2007년 7월)를 통해 jQuery를 배워보자.

  • jQuery in Action(Manning Publication Co., 2008년 2월)을 통해 jQuery에 대해 추가로 도움을 받을 수 있다.

  • jQuery Reference Guide(Packt Publishing, 2007년 7월)를 통해 jQuery에 대한 일반적인 참고자료들을 더 볼 수 있다.

  • Brian Dillard의 블로그 Agile Ajax를 통해 jQuery와 기타 UI 관련 주제들을 확인하자.

  • Ajax 응용 프로그램의 보안에 대한 모범 사례들을 Billy Hoffman과 Bryan Sullivan의 책 Ajax Security(Addison Wesley Professional, 2007년 12월)에서 볼 수 있다.

  • 기술 서점에서 이 주제 또는 기타 기술적인 주제에 대한 책들을 찾아보자.


제품 및 기술 얻기
  • jQuery 웹 사이트에서 jQuery에 대한 모든 정보와 추가 플러그인을 찾을 수 있다. 글을 쓰는 시점에서 현재 버전은 1.2.3이다.

  • jQuery UI Tabs: jQuery 플러그인을 사용하면 내장되어 있거나 Ajax로 가져온 내용을 탭 인터페이스로 만들 수 있다. 이 플러그인은 입맛대로 조절할 수 있는 위젯과 사용자 인터페이스 컴포넌트의 모음인 jQuery UI의 일부다.

  • jCarousel 플러그인을 사용하면 미리 로드되어 있거나 Ajax로 가져온 이미지들을 회전형 슬라이드쇼로 만들 수 있다.

  • jQuery Cookie 플러그인을 사용하면 jQuery UI Tabs에서 최근에 사용한 탭을 기억하여 사용자가 다음에 방문할 때 그 탭을 표시할 수 있다.


토론


필자소개

Brian Dillard

Brian J. Dillard는 12년 동안 웹 개발자로 일하면서 Orbitz Worldwide, Reflect True Custom Beauty, Archipelago LLC, United Airlines 같은 다양한 회사를 위해 풍부한 사용자 인터페이스를 구축했다. 현재 시카코에 위치한 Pathfinder Development에서 RIA 전도사로 일하면서, 다양한 고객을 위한 리치 인터넷 애플리케이션을 구축하고, 오픈 소스 프로젝트에 참여하고, Agile Ajax 블로그에 기여하고 있다. 수천 개 웹 사이트에서 실무에 사용되는 Ajax 방문기록 및 북마크 라이브러리인 Really Simple History 프로젝트를 이끌고 있다.

Posted by 1010