8/12 개발일기
지난 주엔 개발 일기에 손도 대지 못했다. 포켓몬고 UX 글도 이어서 번역해야지, 하고 마음은 먹었으나.. 한 줄도 하지 못했다. 날씨가 너무 덥다보니, 의욕이 좀처럼 나지 못했던 게 가장 큰 이유. 어쨌든, 다시 또 한 주를 공백으로 날리긴 모해서- 연휴가 낀 금요일, 마침 사무실이 한산한 틈을 타 지난 주에 했던 내용 하나를 적어보려고 한다.
일단 요구 사항은 간단하다.
-----------------------
치마 | 플레어스커트
치마 | A라인 스커트
바지 | 스키니진
바지 | 숏팬츠
------------------------
지금 이렇게 출력하고 있는 테이블을, 아래와 같이 첫번째 행을 병합하여 보여달라는 것
-----------------------
치 | 플레어스커트
마 | A라인 스커트
-----------------------
바 | 스키니진
지 | 숏팬츠
------------------------
HTML로 표현하면 이렇게 될것이다.
<table>
<tr>
<td rowspan="2">치마</td>
<td>플레어 스커트</td>
</tr>
<tr>
<td>A라인 스커트</td>
</tr>
</table>
그런데 보통 이런 데이터들은 서버에서 받아서 foreach 로 처리해주기 십상이므로, 저렇게 병합을 사후에 처리해야 하는 경우 서버 코드로 계산하려면 처음부터 같은 item의 갯수를 별도 계산해서 넘겨주어야 한다. (치마는 2개, 바지는 3개 이런식으로) 그것도 방법이지만, 나는 다 출력한 이후에 병합하는 방식을 택했다. 우리 DB 구조가 원체 복잡하다보니, Count 하는 데에도 Query 시간이 상당 부분 소요되었기 때문이다.
그래서 생각한 로직은 이렇다.
(1) 병합 기준(치마, 바지..)이 되는 아이템 내용을 담는 변수를 만든다.(var mergeItem)
병합할 item들의 갯수를 세는 count 용 변수를 만든다. (var mergeCount)
rowspan 속성을 선언해줄 tr의 index를 저장하는 변수를 만든다. (var mergeRowNum)
(2) tr 을 반복문으로 돌리면서, 해당 라인의 첫번째 데이터가 이 변수와 같은 지 다른 지를 체크한다.
→ 같으면, mergeCount 에 1을 더해주고, 현재 mergeRowNum 에 저장되어 있는 행에
rowspan 속성을 mergeCount 로 업데이트해준다.
→ 다르면, mergeItem 의 내용을 현재 아이템으로 바꿔주고, mergeRowNum 에 현재 행 번호를 넣는다.
말로 하니 좀 복잡한데 이걸 코드로 만들면 간단하다.
var mergeItem= "";
var mergeCount= 0;
var mergeRowNum= 0;
$('tr','table').each(function(row){
if(row > 2 ){ //헤더 제외
var thisTr = $(this);
var item = $(':first-child',thisTr).html();
if(mergeItem != item ) {
mergeCount= 1;
mergeItem = item ;
mergeRowNum= Number(row); //숫자 형태로 저장
}else{
mergeCount= Number(mergeCount) + 1;
$("tr:eq("+mergeRowNum+") > td:first-child").attr("rowspan",mergeCount);
$('td:first-child',thisTr).remove(); //병합했으므로 해당 행의 첫번째 td element 는 삭제한다
}
}
})
java로도 foreach 를 사용해 데이터를 출력하는데, 출력된 이후 또 다시 jQuery 로 each 를 돌리면 성능이 느려질 거라고 여겨질 수도 있다. 그러나 이 경우에는 DB에서 Count 하는 쿼리 성능이 매우 떨어져서 (Table 조인 갯수가 많은 데이터였다) jQuery 반복문으로 처리하는 게 훨씬 더 빨랐던 데다, 검색 조건이 많아 계속해서 데이터가 바뀌었기 때문에 화면에서 동적으로 처리해주는 게 효율적이었다.
그리하여 동적 병합 테이블 완성 :) 따란-