Vue.js - 동적으로 테이블 행 병합하기, 동적 rowspan(1)

Gyu·2022년 1월 25일
0

이 글은 사수 없는 주니어 프론트 개발자가 혼자서 구글링도 해보고 혼자 머리 싸매고 고민해서 겨우겨우 간신히 해결한 경험을 바탕으로 작성되었습니다.

들어가기 전

나는 JAVA 국비 6개월 과정을 수료하고, 어쩌다보니 프론트 개발자로 취업을 하게 되었다. 입사한 회사(현 직장)에서 내가 맡은 서비스는 PHP+Vue.js로 개발 되어있고, 예전에 백엔드 개발자 분들이 프론트도 같이 만들어 백엔드와 프론트의 의존도가 매우 높고 중간중간 백엔드스러운 코드들이 많았다. 그리고 회사 특성상 팀 당 프론트 개발자가 한 명이라 사수 없이 대형 프로젝트의 프론트 전반을 맡게된 나 자신. 신규 프로젝트를 하면서 동적으로 테이블 행을 병합해야하는 과제에 직면하게 되는데....

내가 구현해야할 화면

  • 내가 구현해야할 화면은 위 사진처럼 날짜 컬럼에서 같은 날짜끼리는 행이 병합되는 테이블이었다.

데이터 형식

{
  table_data: [
    { created_at: '2020-01-01', data1: '', data2: '', data3: '' },
    ...
  ]
}
  • api 호출 시 테이블에 보여줘야할 데이터의 형식은 위와 같다.

기존 코드

// template 영역
...
<table>
  <thead>
    <tr>
      <th>날짜</th>
      <th>데이터1</th>
      <th>데이터2</th>
      <th>데이터3</th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="item in tableData">
      <td class="dynamic"> {{ item.created_at }} </td>
      <td> {{ item.data }} </td>
	  <td> {{ item.data }} </td>
	  <td> {{ item.data }} </td>
    </tr>
  </tbody>
</table>
...

// script 영역
...
updated () {
  $(".dynamic").each(function () {
    var rows = $(".dynamic:contains('" + $(this).text() + "')");
    if (rows.length > 1) {
      rows.eq(0).attr("rowspan", rows.length);
      rows.not(":eq(0)").remove(); 
    } 
  });
}
...
  • 기존 프로젝트에서도 행병합을 구현한 부분이 있었다. 기존 레거시 코드에서는 위처럼 updated() 훅에 제이쿼리를 사용해 행을 병합했다.
  • 하지만 Vue.js에서는 제이쿼리 사용을 권장하고 있지않다. 나는 이 부분을 Vue스럽게 바꿔보기로 했다.

내가 개선한 코드

// template 영역
...
<table>
  <thead>
    <tr>
      <th>날짜</th>
      <th>데이터1</th>
      <th>데이터2</th>
      <th>데이터3</th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="(item, index) in tableData">
      <td
      	v-if="index == 0 || item.created_at === tableData[index-1].created_at"
      	:rowSpan="getRowSpan(tableData, item)"
      >
      	{{ item.created_at }} 
      </td>
      <td> {{ item.data }} </td>
      <td> {{ item.data }} </td>
      <td> {{ item.data }} </td>
    </tr>
  </tbody>
</table>
...

// script 영역 - methods
...
getRowSpan(arr, data) {
  let rowSpan = 0;
  
  arr.forEach(item => {
    if(item.created_at == data.created_at) rowSpan++;
  });
  
  return rowSpan;
}
...
  1. 0번째 요소의 날짜는 무조건 보여준다.
  2. 반복문 내부에서 현재 요소와 이전 요소의 날짜가 같다면 현재 요소의 날짜를 렌더링하지 않는다.
  3. tableData에서 현재 요소의 날짜와 같은 날짜를 갖고 있는 요소의 개수를 반환하는 함수를 이용해 rowSpan을 부여한다.

마치며...

나의 사수이자 선생님인 구글을 통해 짠 코드지만 저 코드도 사실 완벽하지 않다. 반복문 안에 조건문이 있어 배열의 길이 개수만큼 조건문을 연산하기 때문에, 렌더링 시 성능저하가 생길 수도 있다. 때문에 api 호출해서 응답값을 받을 때 계산을 하는 것도 좋은 방법인 것 같다.

profile
애기 프론트 엔드 개발자

0개의 댓글