본문 바로가기
프로그래밍/JavaScript

[자바스크립트] JSON 데이터를 이용한 컨텐츠 노출 스킨 변경 - #3

by zoo10 2010. 12. 22.

이번 포스팅에서는 실제로 데이터를 만들고 실제로 구동할 수 있는 함수들을 만들어 보도록 하겠습니다. 실제로 데이터들을 접근하고 원하는 위치에 데이터를 노출시키는 작업이 되겠습니다.

들어가기에 앞서 이번 스크립트는 아주 협소한 코드로 짜여 질것이며, 단순히 현재 화면을 구현하고 테스트하기 위한 소스가 될것입니다. 마크업과 JSON 데이터를 변형시키겠습니다. 마크업에 대해서는 아래 코드와 함께 설명을 드리겠습니다만 그림도 같이 들어갈 수 있겠네요. JSON 데이터는 실제로 나올 데이터를 예제로 만들어서 사용하겠습니다. 보시면 이해가 빠르실것 같습니다.

먼저 JSON 데이터를 보도록 하겠습니다.(너무 길어서 토글로 처리합니다.)
[열기]

데이터가 꾸려지면서 내용이 좀 길어졌습니다. 별거는 없습니다만 이미지 경로는 제 환경에 맞게 잡혀 있으니 그 부분 유의하시면 되겠습니다. 그리고 가정하길 한 콘텐츠 당 이미지는 4개씩 존재한다고 하겠습니다. 한미일의 대표적인 만화들을 골라봤습니다. 뭐야 저게 대표는 아니다라고 하신다면 제가 기억한 만화라고 쿨럭;;;. 개인적으로 우리나라의 대표적인 만화가 너무 오래된 것들 뿐이라 좀 씁쓸합니다. 저 만화들을 대신할 뚜렷한 작품이 없다는 것이겠지요. 하핫..

자, 가상의 데이터를 준비했습니다. 물론 실제에서는 DB에 꺼내온 값들로 구성을 할 수 있겠지요. 그리고 지면 상의 문제로 인해 엔터키를 많이 사용했습니다. 그리고 줄거리는 잘라냈습니다. 실제 소스는 좀더 깁니다.

드디어 리스트를 구성해 보겠습니다. 리스트를 구성할 마크업을 수정해야 합니다. 여기서 부터는 말로 설명하기가 좀 애매합니다. 어렵지는 않으나 애매한 그 어떤 표현. 흠, 고민스럽습니만 일단 말로 풀어보겠습니다. 먼저 마크업을 보시죠.

<div class="tbl_lst" id="list">
	<table cellspacing="0" class="prt_tbl">
		<caption>기본부품 리스트</caption>
		<colgroup>
		<col width="100"><col><col width="100"><col width="100">
		<col width="80"><col width="20">
		</colgroup>
		<tbody>
		<tr>
		<td><replace></td>
		<td><replace></td>
		<td><replace></td>
		<td><replace></td>
		<td><replace></td>
		<td>▼</td>
		</tr>
		</tbody>
	</table>
</div><!--//<div class="tbl_lst">-->

일단 리스트보기의 태그를 보면 내용이 많이 줄었습니다. 비교하시려면 아래 포스팅에서 확인하세요.(포스팅에서 바로 확인이 안되네요. 아래 포스팅의 첨부파일을 참고하세요.)
2010/12/20 - [WEB/javascript] - [자바스크립트] JSON 데이터를 이용한 컨텐츠 노출 스킨 변경 - #2
반복되던 tr 요소를 하나만 남기고 다 제거했고, td 안에 replace 라는 마크업이 들어 있습니다. 이 replace 요소가 핵심이 되는데요. 바로 치환자가 되겠습니다.

치환자란 어떤 다른 요소로 바뀔수 있는 요소 정도라고 정의하겠습니다. 그러니까 이 replace 란 태그는 제가 임의로 만든 태그이고 공식적인 HTML 태그가 아닙니다. 이 태그의 쓰임새는 아래 스크립트에서 확인해 보겠습니다. 먼저 JSON 데이터의 기본 설정을 읽겠습니다.

/********************************************
기본 설정값 읽기
*********************************************/
var jName = datas.config.jsonName;
var bodyName = datas.config.dtBody;

기본 설정값을 읽어서 변수에 담습니다. 변수에는 string 데이터가 들어가 있습니다. alert(typeof jName)이라고 찍어보시면 확인하실 수 있습니다. 이제 리스트 보기에 데이터를 뿌리는 스크립트를 보겠습니다. 위에서 말씀드린 replace 의 위치를 잘 보시기 바랍니다.

/********************************************
리스트보기 임시 구현
*********************************************/
var l = document.getElementById("list");
var tbody = l.getElementsByTagName("TBODY");
var tr = l.getElementsByTagName("TR");

for (n=0; n<datas.data.length; n++){
	var el = l.getElementsByTagName("replace");
	var new_tr = tr[n].cloneNode(true);
	
	if(n < datas.data.length-1) tbody[0].appendChild(new_tr);
	
	for (i=datas.listConfig.dtName.length-1; i>=0; i--){
		var dtName = datas.listConfig.dtName[i];
		var replaceData = eval(jName+"."+bodyName+"["+n+"]."+dtName);
		el[i].parentNode.innerHTML = replaceData;
	}
}

HTML 에서 원하는 요소를 가져오는 방법중에 getElementById 와 getElementsByTagName 그리고 getElementsByName 등이 있습니다. 좀더 자세한 정보를 원하시면 제가 정말 많이 참고하는 사이트인 W3C School 에서 확인해 보셔도 좋겠습니다. 영문 사이트이지만 그리 어렵지 않고 많은 정보가 있습니다. 위의 함수의 내용을 보시려면 HTML DOM 에서 확인하실 수 있습니다.
http://www.w3schools.com/jsref/dom_obj_document.asp

위 스크립트에서 9, 10라인 그리고 16라인이 핵심입니다. 소스를 분리해서 좀 더 자세히 설명해 보겠습니다.

/********************************************
리스트보기 임시 구현
*********************************************/
var l = document.getElementById("list");
var tbody = l.getElementsByTagName("TBODY");
var tr = l.getElementsByTagName("TR");

4라인은 list라는 아이디를 가진 요소를 담습니다. 즉, <div class="tbl_lst" id="list"> 이 부분입니다.
5라인은 list 영역에 있는 태그 중 TBODY 를 찾아서 tbody라는 변수에 담습니다. tbody 변수는 배열입니다.
6라인은 tbody 영역에 있는 태그 중 TR을 찾아서 tr 변수에 담습니다. tr 변수는 배열입니다.

for (n=0; n<datas.data.length; n++){

8라인은 실제 데이터의 수만큼 Loop를 합니다. 즉 만화 제목만큼 돌린다고 생각하시면 편하실 듯 합니다.

	var el = l.getElementsByTagName("replace");
	var new_tr = tr[n].cloneNode(true);

9라인은 list라는 영역안에 있는 replace라는 태그들을 모두 찾아내어 el이란 변수에 담습니다. el은 배열입니다.
10라인은 n번째 tr을 복사해서 new_tr에 담습니다. 이 부분은 리스트 형태일 때는 중요한 부분입니다. 그림을 통해 설명을 드리겠습니다.

위 그림은 tr이 복제되는 과정에 대해서 설명을 한 내용입니다. 그런데 왜 tr을 복제해야 하는가? 그에 대한 설명은 지금부터 드리겠습니다. 우리는 계속된 스크립트에서 replace라는 치환자를 실제 데이터로 바꿀것입니다. 그런데 replace를 데이터를 치환하면 replace라는 태그는 모두 사라집니다. 그렇게 되면 9라인의 l.getElementsByTagName("replace") 라는 구문은 어떤 데이터도 찾지 못하게 됩니다. 그러면 더이상 실제 데이터를 집어넣을 공간이 없어지게 되죠. 그래서 원본 스킨중에서 반복이 되야 하는 부분을 복사해 뒀다가 다음 리스트를 위해 삽입해 놓아야 하는겁니다.

    if(n < datas.data.length-1) tbody[0].appendChild(new_tr); 

12라인에서 복사해둔 tr을 appendChild를 통해서 새로 삽입합니다. 그리고 마지막 데이터 다음에는 삽입할 필요가 없기 때문에 조건문을 달았습니다. 이해가 되시는지 모르겠네요. 이 로직은 다른 스킨인 썸네일보기 에서도 동일하게 적용됩니다. 혹시나 잘 모르시겠다면 10, 12번째 라인을 주석처리 하신 후 테스트해 보시면 될것 같습니다. 바로 스크립트 오류 메시지를 보실 수 있습니다.(사실 이 부분에서 많이 고민했습니다. 반복할 수 있는 방법이 딱히 떠오르지 않았었는데 객체를 복사할 수 있는 함수가 있었던게 기억이 나서 찾아보고는 처리할 수 있게 되었습니다. 외우지는 못해도 찾아쓸수 있는것. 이게 코딩의 핵심인 것 같습니다.)

	for (i=datas.listConfig.dtName.length-1; i>=0; i--){

14라인의 for Loop는 감소 반복(?)을 하고 있습니다. 이 부분은 SELECT 태그에 Option 값 동적 추가/삭제 시에 사용했던 로직을 떠올리시면 됩니다. SELECT태그는 요소를 삭제할 때 제일 큰 값(인덱스 기준으로)부터 삭제합니다. 보통은 이렇게 하죠. 그리고, JSON데이터 중에서 listConfig 요소에 담긴 dtName의 수만큼 Loop를 하고 있습니다. dtName에는 data 요소의 Key(변수명으로 봐도 무방합니다.)값들이 문자열(string)으로 들어 있습니다. 우리는 이 dtName의 요소 순서대로 스킨에 실 데이터를 매핑하는 것으로 약속합니다. 새끼손가락~~

사실 이 부분은 저도 아직 좀 의문이기 합니다. 이미 담아놓은 replace 태그의 집합인 el 값을 참조하지 않고 화면의 replace를 그때그때 찾아서 간다는 얘기인데요. 이렇게 코딩을 했지만 좀 의문스럽습니다. 그러니까 el[0] ~ el[6] 까지 있었다면 el[0]에 해당하는 replace 가 실 데이터로 치환되면 치환되기 이전의 el[1]값이 el[0]으로 바뀌게 되버립니다. 아~ 말이 좀 이상하네요. ㅎㅎㅎ; 실제로 i-- 하지 않고 i++ 로직으로 for Loop를 사용하면 1, 3, 5 번째 td에 데이터가 들어갑니다. 실제로 테스트해 보시면 더욱 명확하실 겁니다. 어찌됬든 우리는 뒤에서 부터 데이터를 채웁니다.

		var dtName = datas.listConfig.dtName[i];

15라인은 listConfig 요소 중 dtName을 꺼내옵니다. 데이터는 큰 인덱스 i 값에 해당하는 순서로 가져옵니다. 즉, itIssueDt 문자열부터 담기게 됩니다.

		var replaceData = eval(jName+"."+bodyName+"["+n+"]."+dtName);

16라인은 문자열을 객체화해서 실제 데이터를 꺼내는 부분입니다. jName, bodyName, dtName 안에 들어있는 데이터는 모두 단순한 문자열일 뿐입니다. 우리가 만약 마루밑 아리에티의 개봉일 2010.09 에 접근하려면 datas.data[0].itIssueDt 라고 표현해야 합니다. 그런데 우리는 이 부분을 문자열로 모두 가지고 있습니다.(동적으로 만들기 위해서 사용한 방법입니다.) 아래 표를 참고하면 각 변수에 담겨 있는 데이터와 데이터 타입을 알 수 있습니다. 또한 eval 함수를 사용해서 해당 문구를 객체화하여 실제 마루 및 아리에티의 개봉일에 접근할 수 있게 됩니다.

변수명 데이터 타입 eval 함수 사용 결과
jName datas string eval(jName+"."+bodyName+"["+n+"]."+dtName); 객체화 됨
bodyName data string
dtName itIssueDt string

이렇게 얻은 replaceData에는 실제 값에 접근할 수 있는 JSON의 노드 경로를 얻을 수 있게 됩니다.

		el[i].parentNode.innerHTML = replaceData;

17라인은 실제 데이터를 치환하는 부분입니다. 좀 특이한것은 parentNode라는 부분인데. el[i]의 부모노드 즉, <replace>태그의 부모태그인 tr 태그를 찾아서 그 태그에서 innerHTML 을 하고 있습니다. 이 부분으로 <replace> 태그는 replaceData 바로 '2010.09'로 치환되게 됩니다..

드디어 이번 포스트가 마무리 되네요. 휴 좀 긴 포스트 읽으시느라 수고많으셨습니다. 물론 상세보기와 썸네일보기의 스크립트가 남아있지만 이 부분은 코드를 보여드리는 걸로 대체 하겠습니다. 별로 다른게 없어서요. 아~ 이미지 뿌리는 부분이 조금 다릅니다만 그 부분은 다음 포스팅에 같이 언급하도록 하겠습니다. 그리고 첨부파일을 다운로드 하실 수도 있습니다. 다시한번 긴글 수고하셨습니다.

/********************************************
상세보기 임시 구현
*********************************************/
var d = document.getElementById("detail");
var curItemIndex = 0;
var el = d.getElementsByTagName("replace");

for (i=datas.detailConfig.dtName.length-1; i >= 0; i--){
	var dtName = datas.detailConfig.dtName[i];
	var replaceData = eval(jName+"."+bodyName+"["+curItemIndex+"]."+dtName);
	if(el[i].getAttribute("TYPE")=="image") {
		el[i].parentNode.innerHTML = "<IMG src='"+replaceData[0]+"'>";
		continue;
	}
	
	el[i].parentNode.innerHTML = replaceData;
}


/********************************************
썸네일보기 임시 구현
*********************************************/
var t = document.getElementById("thumbnail");
var ul = t.getElementsByTagName("UL");
var li = ul[0].getElementsByTagName("LI");

for (n=0; n<datas.data.length; n++){
	var el = ul[0].getElementsByTagName("replace");
	var newNode = li[n].cloneNode(true);
	
	if(n < datas.data.length-1) ul[0].appendChild(newNode);
	
	for (i=datas.thumbConfig.dtName.length-1; i>=0; i--){
		var dtName = datas.thumbConfig.dtName[i];
		var replaceData = eval(jName+"."+bodyName+"["+n+"]."+dtName);
		
		if(el[i].getAttribute("TYPE")=="image") {
			el[i].parentNode.innerHTML = "<img src='"+replaceData[0]+"'>";
			continue;
		}
		el[i].parentNode.innerHTML = replaceData;
	}
}