SheetJS 테이블 내용 엑셀 다운로드, 스타일 적용 방법
my code archive
반응형

회사에서 엑셀 다운로드 개발 업무를 맡게 됐는데 기존 소스 로직은

페이징 처리된 부분은 1페이지만 다운이 되서 전체 row를 추출하기 위해

SheetJS 라이브러리를 사용하게 됐다.

결론적으론... 이 라이브러리를 사용 안 하기로 했지만 덕분에 스터디를 했기 때문에 기록해 본다.

 

SheetJS 란?

Javascript 클라이언트단에서 엑셀 다운로드 구현 시 사용되는 라이브러리

https://docs.sheetjs.com/

 

SheetJS Community Edition | SheetJS Community Edition

SheetJS Community Edition Documentation

docs.sheetjs.com

 

1. SheetJS , FileSaver 스크립트 추가

<!-- SheetJS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.14.3/xlsx.full.min.js"></script>
<!-- FileSaver saveAs 이용 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"></script>
<!-- 엑셀 cell style 적용 -->
<script src="https://cdn.jsdelivr.net/npm/xlsx-js-style@1.2.0/dist/xlsx.min.js"></script>

 

2. 테이블 내용 JSON 으로 가져오기

SheetJS에서 테이블 내용을 가져오는 함수는 json_to_sheet, table_to_sheet 등 다양한데 나처럼 페이징 상관없이 전체 row를 가져올 때엔 JSON으로 가져와야 한다. 

회사에선 DataTable을 사용 중이라 $("#테이블 ID).DataTable(); 이렇게 값을 가져왔다.

function tableToJson(tableId) { 
    var transactions = [];
    
    var table = $('#'+tableId).DataTable(); 
    var data = table.rows().data(); 
    
    for (var i = 0; i < data.length; i++) { 
    	for (var j = 0; j < data[i].length; j++) {
    		/* 회사 코드라 생략 */
    	}
    	
    	transactions.push(data[i]); 
    }
    
    return transactions;
}

 

3. 커스텀 헤더 만들기

JSON은 key, value 형식으로 이루어져 있는데 위의 소스대로 하면 key가 그냥 1, 2, 3 ... 이렇게 들어가서

테이블 상의 제일 첫 번째 줄(<th> 태그)에 해당하는 부분이 key값이 되도록 커스텀 헤더를 만들어주었다.

function tableHeaderToJson(tableId) { 
    var headers = [];
    var arrOfArr = [];
    $("thead").find("th").each(function () {
    	headers.push($(this).text().trim());
    });
    
    $("#"+tableId).find("tr").each(function () {
    	
    	$("#"+tableId).find("td").each(function (i) {
    		var td = $(this);
    		var tdVal = '';
        	tdVal = td.eq(i).html();
    	});
    });
    
    arrOfArr[0]=headers;
    return arrOfArr;
}

SheetJS에서 커스텀 헤더를 만들기 위해선 Array Of Array가 필요해서 위와 같이 구현함.

참고 링크 : https://stackoverflow.com/questions/52215676/sheetjs-json-to-sheet-renaming-headers

 

SheetJS json_to_sheet renaming headers

I have an array of objects. By default, sheet_to_json uses object keys as headers. However, I need to rename those headers without explicitly modifying my array of objects. Is this possible?

stackoverflow.com

 

4. SheetJS 테이블 내용 Excel 다운로드

function downloadExcel(table, headName) {
	// table 데이터를 json 형식으로 받아옴.
	var jsonData = tableToJson(table);
	// json key 값을 컬럼명으로 넣기 위해 table header값을 받아옴.
	var headerArr = tableHeaderToJson(table);
	// workbook 생성
	var work_book = XLSX.utils.book_new(); 
	
	// 시트 만들기 (모든 페이지를 엑셀에 담기 위해 json_to_sheet 사용)
	//var work_sheet = XLSX.utils.table_to_sheet("#"+table);
	var work_sheet = XLSX.utils.json_to_sheet(jsonData);
	// 엑셀 헤더 값을 컬럼명으로 rename
	XLSX.utils.sheet_add_aoa(work_sheet, headerArr);
	
	XLSX.utils.sheet_add_json(work_sheet, jsonData, { origin: 'A2', skipHeader: true });

	// workbook에 새로 만든 워크시트에 이름을 주고 붙인다.
	XLSX.utils.book_append_sheet(work_book, work_sheet, headName);
	// 엑셀 파일 만들기
	var work_book_down = XLSX.write(work_book, {bookType:'xlsx',  type: 'binary'});
	// 엑셀 파일 내보내기 
	saveAs(new Blob([s2ab(work_book_down)],{type:"application/octet-stream"}), headName+'.xls');
}

function s2ab(s) { 
    var buf = new ArrayBuffer(s.length); //convert s to arrayBuffer
    var view = new Uint8Array(buf);  //create uint8array as viewer
    for (var i=0; i<s.length; i++) view[i] = s.charCodeAt(i) & 0xFF; //convert to octet
    return buf;    
}

대략적인 로직은 위와 같다. Workbook을 생성하고 Sheet를 생성하고 이름을 붙이고 다운로드...

설명은 주석 달아두었음.

 

5. 엑셀 헤더 cell style 적용

위와 같이 했을 때 전체 페이지에 해당하는 모든 row 엑셀로 추출하기 성공! 그런데 다음 과제는...

헤더에 색상을 넣는 style을 적용하는 거였다.

// Make first row bold
	for(let i = 0; i < headerArr[0].length; i++) {
	    const cell = work_sheet[XLSX.utils.encode_cell({r: 0, c: i})];
	    // Create new style if cell doesnt have a style yet
	    if(!cell.s) {cell.s = {};}
	    if(!cell.s.font) {cell.s.font = {};}
	    // Set bold
	    //cell.s.font.bold = true;
	    cell.s = {
	    	font: {
	    		bold: true
	    	},
	    	fill: {
	    		patternType: "solid",
	    		fgColor: { rgb: "b1b1b1" }
	    	}
	    }
	}

 

6. cell 길이 자동 적용

글자수가 길 때에는 엑셀 cell 길이가 동적으로 맞춰지도록 적용함.

let objectMaxLength = []; 
	jsonData.map(arr => {
	  Object.keys(arr).map(key => {
	    let value = arr[key] === null ? '' : arr[key]

	    if (typeof value === 'number') {
	      return objectMaxLength[key] = 10
	    }

	    objectMaxLength[key] = objectMaxLength[key] >= value.length ? objectMaxLength[key]  : value.length + 5
	  })
	})
    let worksheetCols = objectMaxLength.map(width => {
	  return {
	    width
	  }
	});
    
    work_sheet["!cols"] = worksheetCols;

 

7. 전체 소스

<table id="resultTbl" class="table">
</table>

<button type="button" class=common-button" id="excelBtn">엑셀 다운로드</button><!-- 엑셀 -->

$('#excelBtn').click(function() {
    var headName = '';
    headName += setFileName(selectedMenuName);	//메뉴명을 파일명으로 지정
    downloadExcel('resultTbl',headName);	
});
function downloadExcel(table, headName) {
	// table 데이터를 json 형식으로 받아옴.
	var jsonData = tableToJson(table);
	// json key 값을 컬럼명으로 넣기 위해 table header값을 받아옴.
	var headerArr = tableHeaderToJson(table);
	// workbook 생성
	var work_book = XLSX.utils.book_new(); 
	
	let objectMaxLength = []; 
	jsonData.map(arr => {
	  Object.keys(arr).map(key => {
	    let value = arr[key] === null ? '' : arr[key]

	    if (typeof value === 'number') {
	      return objectMaxLength[key] = 10
	    }

	    objectMaxLength[key] = objectMaxLength[key] >= value.length ? objectMaxLength[key]  : value.length + 5
	  })
	})
    let worksheetCols = objectMaxLength.map(width => {
	  return {
	    width
	  }
	})
	// 시트 만들기 (모든 페이지를 엑셀에 담기 위해 json_to_sheet 사용)
	//var work_sheet = XLSX.utils.table_to_sheet("#"+table);
	var work_sheet = XLSX.utils.json_to_sheet(jsonData);
	
	sheet2arr(work_sheet);
	
	work_sheet["!cols"] = worksheetCols;
	// 엑셀 헤더 값을 컬럼명으로 rename
	XLSX.utils.sheet_add_aoa(work_sheet, headerArr);
	
	XLSX.utils.sheet_add_json(work_sheet, jsonData, { origin: 'A2', skipHeader: true });
	
	
	
	// Make first row bold
	for(let i = 0; i < headerArr[0].length; i++) {
	    const cell = work_sheet[XLSX.utils.encode_cell({r: 0, c: i})];
	    // Create new style if cell doesnt have a style yet
	    if(!cell.s) {cell.s = {};}
	    if(!cell.s.font) {cell.s.font = {};}
	    // Set bold
	    //cell.s.font.bold = true;
	    cell.s = {
	    	font: {
	    		bold: true
	    	},
	    	fill: {
	    		patternType: "solid",
	    		fgColor: { rgb: "b1b1b1" }
	    	}
	    }
	}
	
	// workbook에 새로 만든 워크시트에 이름을 주고 붙인다.
	XLSX.utils.book_append_sheet(work_book, work_sheet, headName);
	// 엑셀 파일 만들기
	var work_book_down = XLSX.write(work_book, {bookType:'xlsx',  type: 'binary'});
	// 엑셀 파일 내보내기 
	saveAs(new Blob([s2ab(work_book_down)],{type:"application/octet-stream"}), headName+'.xls');
}

function s2ab(s) { 
    var buf = new ArrayBuffer(s.length); //convert s to arrayBuffer
    var view = new Uint8Array(buf);  //create uint8array as viewer
    for (var i=0; i<s.length; i++) view[i] = s.charCodeAt(i) & 0xFF; //convert to octet
    return buf;    
}	

function tableHeaderToJson(tableId) { 
    var headers = [];
    var arrOfArr = [];
    $("thead").find("th").each(function () {
    	headers.push($(this).text().trim());
    });
    
    $("#"+tableId).find("tr").each(function () {
    	
    	$("#"+tableId).find("td").each(function (i) {
    		var td = $(this);
    		var tdVal = '';
        	tdVal = td.eq(i).html();
    	});
    	
    });
    
    arrOfArr[0]=headers;
    return arrOfArr;
} 

function tableToJson(tableId) { 
    var transactions = [];
    
    var table = $('#'+tableId).DataTable(); 
    var data = table.rows().data(); 
    
    for (var i = 0; i < data.length; i++) { 
    	for (var j = 0; j < data[i].length; j++) {
    		/* 회사 소스라 생략 */
    	}
    	
    	transactions.push(data[i]); 
    }
    
    return transactions;
}

 

8. merge cell

맨위에서 언급했던 결론적으로 SheetJS 라이브러리를 사용하지 않기로 한 이유는 merge cell 때문이었다.

테이블에서 rowspan, colspan 처리된 cell은 노가다(?)로 코드를 작성해야 해서 번거롭기 때문...

SheetJS에서 merge cell 처리하는 방법은 아래 링크 참고!

 

https://stackoverflow.com/questions/53516403/sheetjs-xlsx-how-to-write-merged-cells

 

sheetjs xlsx, How to write merged cells?

I need to create a xlsx with merged cells using sheetjs. data: [ { "id": "nick", "items": [ { "name": "ball" }, { "name": "phone" } ] }, ...

stackoverflow.com

https://juni-official.tistory.com/243

 

sheet.js 셀 스타일 및 병합(merge) 방법

sheet.js 셀 스타일 및 셀 병합 sheet.js 말고 excel.js 라이브러리를 사용한다면 셀 스타일과 병합하는 과정이 간단합니다. 그리고 github에 사용 방법도 자세히 나와있는 편이라 쉽게 찾아보며 엑셀 기

juni-official.tistory.com

 

반응형
profile

my code archive

@얼레벌레 개발자👩‍💻

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

반응형