1. 들어가며
프로젝트를 진행하면서 사용자가 화면에서 조회한 데이터를 Excel로 다운로드하는 기능을 개발하게 되었습니다. 단순히 데이터를 내보내는 것이 아니라, 사용자가 화면에서 보는 형태와 최대한 동일한 결과를 Excel에서도 제공하는 것이 요구사항이었습니다.
예를 들어 상태 값에 따라 글자 색상이 변경되거나, 특정 행이 강조 표시되거나, 헤더에 별도의 배경색이 적용되는 경우 등 브라우저상의 시각적 요소가 Excel에서도 유지되어야 했습니다.
처음에는 ag-Grid에서 제공하는 exportDataAsExcel() 메서드만 호출하면 화면과 동일한 결과가 생성될 것이라고 생각했습니다. 하지만 실제로는 브라우저의 렌더링 방식과 Excel Export의 동작 방식이 서로 달라 스타일이 유지되지 않았습니다. 이를 해결하기 위해 겪은 시행착오와 해결 프로세스를 공유하고자 합니다. 본 글은 ag-Grid v28.0.2 환경을 기준으로 작성했으며, 버전에 따라 일부 동작이나 지원 기능은 달라질 수 있습니다.
2. 화면과 Excel의 데이터가 달랐던 이유: `valueFormatter` 미적용
[문제 상황]
일부 컬럼은 DB에서 가져온 원본 값 대신 사용자의 가독성을 위해 `valueFormatter`를 이용해 변환된 텍스트를 화면에 표시하고 있었습니다.
이 상태로 엑셀 출력을 실행하자, 화면에는 '승인'으로 보이는 데이터가 Excel 파일 내에서는 원본 데이터인 'Y'로 출력되는 현상이 발생했습니다.
[해결 방법]
ag-Grid의 Excel Export 옵션 중 `processCellCallback`을 활용하여, 엑셀 파일에 셀 값을 찍기 전에 valueFormatter가 적용된 최종 텍스트를 반환하도록 값을 가공하는 콜백 로직을 추가했습니다.
이 처리를 통해 내부 데이터 키값 대신 사용자가 화면에서 보는 직관적인 텍스트 그대로 엑셀에 저장할 수 있었습니다.
3. CSS가 Excel에 그대로 적용되지 않는 이유
[문제 상황]
브라우저에서 특정 조건일 때 셀의 글자 색상을 빨간색으로 바꾸기 위해 아래와 같이 `cellClass` 설정을 사용했습니다.
화면에서는 CSS 스크립트(`.text-red { color: red; }`)에 의해 정상 작동했으나, 다운로드된 Excel 파일에서는 아무런 스타일도 적용되지 않았습니다.
[원인 분석]
ag-Grid의 Excel Export 엔진은 브라우저의 렌더링된 CSS 호스트 환경을 읽지 못합니다. 대신 엑셀 고유의 XML 스타일 포맷으로 변환해주는 내부 객체인 `excelStyles`배열을 참조하여 스타일을 빌드하는 구조였습니다.
즉, 화면에서 사용하는 CSS만으로는 Excel 스타일이 자동으로 반영되지 않았습니다. 이 문제를 해결하기 위해 excelStyles와 셀 클래스 간의 매핑 방식을 적용했으며, 그 과정은 다음 섹션에서 설명합니다.
4. excelStyles와 cellClass, cellClassRules의 연동 구조
[해결 방법]
문제를 해결하기 위해 Grid 내부에 공통으로 사용할 `excelStyles`설정을 선언해 주었습니다. 핵심은 CSS 클래스명과 `excelStyles`의 id를 1:1로 매핑하는 것입니다.
[알게 된 점]
버전 28.0.2에서는 `cellClass`함수를 통해 리턴된 클래스명이든 `cellClassRules`에 의해 만족하여 활성화된 클래스명이든 최종적으로 셀에 부여된 클래스 문자열이 excelStyles의 id와 일치하면, 엑셀 엔진이 이를 자동으로 인식합니다. 덕분에 폰트 색상, 굵기 등의 속성이 정상적으로 인코딩되며, 화면 CSS 구조와 엑셀 스타일 구조를 일관성 있게 결합할 수 있었습니다.
5. Header 스타일 적용 과정과 특별한 고유 ID (header)
[문제 상황]
헤더(Header) 영역 스타일링 과정에서도 예상하지 못한 문제를 만났습니다. 처음에는 앞서 성공했던 일반 Cell 방식과 동일하게 커스텀 스타일 ID를 정의하고 매핑하면 될 것이라 생각했습니다.
화면 CSS는 정상 작동했으나, Excel을 다운로드해 보니 헤더에 아무런 스타일이 적용되지 않았습니다. 처음에는 단순한 오타나 설정 실수인 줄 알고 여러 차례 코드를 검증하고 테스트를 반복했으나 결과는 같았습니다.
[원인 분석 및 해결]
공식 문서에서 ag-Grid의 Excel Export 엔진에서 헤더 영역은 매우 특별하게 처리된다는 점을 발견했습니다. 헤더는 개발자가 임의로 지정한 커스텀 ID(`blue-header`)를 인식하지 않고, ag-Grid가 내부적으로 예약해 둔 고유 ID인 `header`라는 명칭을 강제 구조화하여 사용하고 있었습니다.
이에 따라 excelStyles 설정을 임의의 ID 대신 공식 스펙에 맞추어 아래와 같이 수정했습니다.
[알게 된 점]
수정 후 헤더 스타일이 Excel 파일에 정상적으로 반영되는 것을 확인할 수 있었습니다. 이 과정을 통해 일반 Cell 영역과 Header 영역은 내부 파싱 엔진 자체가 완전히 다른 메커니즘으로 동작함을 이해하게 되었습니다. 헤더 스타일을 제어할 때는 임의의 클래스 바인딩보다 프레임워크가 지정한 내장 규칙(`id: "header"`)을 따르는 것이 핵심이었습니다.
단, 위 내용은 ag-Grid v28.0.2 기준이며, v29 이상에서는 Excel Export 내부 엔진 구조가 일부 변경되었을 수 있으므로 공식 문서를 함께 확인하시길 권장합니다.
6. 마무리
이번 경험을 통해 단순히 기능을 구현하는 것을 넘어, 라이브러리의 동작 방식을 이해하고 프로젝트 상황에 맞는 해결책을 선택하는 과정의 중요성을 다시 한 번 느낄 수 있었습니다.
출처 및 참고 자료
* ag-Grid 공식 문서 – Excel Export: https://www.ag-grid.com/react-data-grid/excel-export/
* ag-Grid 공식 문서 – Excel Export Styles: https://www.ag-grid.com/react-data-grid/excel-export-styles/
* ag-Grid 공식 문서 – Cell Styles (`cellClass`, `cellClassRules`): https://www.ag-grid.com/react-data-grid/cell-styles/
JJ