Search
📑

POI 라이브러리 좀 편하게 써보자 (POI, java excel download)

시작

개발 이유

엑셀 파일을 많이 사용해야 하는 일이 있어서 POI 라이브러리를 접하고 애용했다. 하지만… 생각보다 불편한 점을 느꼈다… 예를 들자면 아래와 같은 엑셀을 만들기 위해서는 좀 긴 코드를 만들어야 했다. 몇 줄만 만들면 될 때는 사실 어렵지 않은 작업이라 크게 신경 쓰이진 않았다. 그런데 데이터 헤더 부분(데이터 필드 이름)의 길이도 달라지고 데이터의 크기도 달라지고 심지어 어떤 시트에서는 수식도 넣어줘야 했다. 그리고 가장 머리 아픈 것은… 스타일…셀에 스타일을 넣어야 할 일이 생길 때부터 어마어마한 길이의 코드를 경험하게 되었다.

기존 POI

기존 코드들을 살펴 보며 무엇이 문제였는지 차근차근 살펴보자. 첫 번째로 [기존 스타일 적용 코드] 를 살펴보자. 필드 이름에 관련한 스타일을 적용했을 경우 이 정도 길이의 스타일 관련 코드가 덕지덕지 붙는다. 이게 스타일이 하나만 존재할 때는 그래도 봐 줄만한데… 만약 적용하는 코드가 좀 많아지면 상상도 하기 싫어질 정도로 많아진다. 두 번째로 [기존 셀을 가져오기 위한 코드]를 살펴보자. 언뜻 보면 문제가 없어 보인다. 근데 사용하는 입장에서는 이만한 고생을 하게 만든 코드가 없다. 왜냐하면 create()get()이 나누어져 있어서 이미 있는 Row, Cell에 create()를 하면 기존 작업한 Row, Cell이 덮어쓰기 되어버리고, 아직 만들어지지 않은 Row, Cell을 get()하면 NullPointerExeception이 발생한다. 둘 다 예외로 처리해주면 좋았을텐데 하나는 일단 정상적으로 작동은 되고 하나는 예외가 발생해버리는 것이다. 그리고 마지막으로 위의 두 가지가 적용된 코드는 엄청나게 많은 양의 코드를 만들었다. 그로 인하여 유지,보수 하기가 상당히 까다롭게 변해가고 있었고 만들기도 상당히 귀찮아졌다.
기존 스타일 적용 코드
기존 셀을 가져오기 위한 코드

바꾸고자 했던 방향성

기존 POI를 사용하면서 느낀 것을 좀 쉽게 바꾸는게 가장 큰 목표였다. 그래서 세분화된 개선 목표를 잡고 갈 필요가 있었다. 기존 POI 라이브러리에서 불편했던 점을 개선해보자
1.
필드명을 자동으로 삽입해주게 만들어 보자.
2.
데이터를 자동으로 삽입해주게 만들어 보자.
3.
스타일 적용에 재사용성을 높여보자.
4.
수식을 적용할 수 있는 부분을 개선해보자.
5.
수식을 적용할 수 있는 부분을 재사용할 수 있도록 만들어보자.
위의 세분화된 개선 목표를 잡고 크게 목표를 이루기 위한 방법을 좀 정해봤다. 처음에는 상속을 이용하여 만들면 어떨까? 라는 생각을 했다. 실제로 만드신 분도 계셨다. 하지만 [백기선님의 라이브스터디]를 하면서 “어노테이션 을 활용하는게 어떨까?” 라는 생각을 했다. 그래서 기존 불편했던 점들을 해결하는 방식에 어노테이션을 활용해보기로 했다. 거기에 추가적으로 Stream API를 적극 활용해보기로 했다(이 부분은 내가 모던 자바를 공부만 했지 활용은 한 적이 없어서 한번 추가 해봤다).
1.
기존 POI 라이브러리에서 불편했던 점을 개선해보자
a.
“@" 어노테이션이 사용 가능한 형태로 만들어보자
2.
java Stream API를 적극 활용하여 만들자

참고

처음에는 내 코드를 기반으로 만들려고 생각을 했는데 나와 비슷한 생각을 하며 만드신 분들이 있었다. 그래서 최대한 좋은 부분을 함께 적용 시키기 위해서 참고했다. 완전히 다르다고 말할 수는 없어서 미리 참고한 자료를 표시한다. 아래에 링크를 함께 첨부했다. 두 분다 한국 개발자이시고 많은 부분 코드에 도움이 되었다. 또한 실제로 Maven배포와 jitpack배포를 해주신 분들이라서 더 좋은 코드라고 생각한다.

만들어보자

목표는 정해졌으니 어떤 것을 하나의 카테고리로 묶을지 고민했다. 위의 참고에서 많이 힌트를 얻어 아래와 같은 코드를 이용할 수 있도록 하는 것을 목표로 했다. 처음 만들어 보는 것이라 어떻게 시작해야 할지 모르겠어서 참고도 보고, 내가 궁극적으로 어떻게 사용하면 좋을지 대해서 생각해봤다. 최종적으로는 이러한 dto를 만들어 사용할 수 있도록 하는 것을 구현 목표로 잡았다.
@ExcelFormula(formulaClass = AverageFormula.class) // 수식이 들어갈 수 있는 부분 @ExcelStyle(headerStyleClass = DefaultHeaderStyle.class, bodyStyleClass = DefaultBodyStyle.class) // 대상이 sheet인 style public class ExcelDto { @ExcelColumn(headerName = "연번코드") // column의 필드명에 해당되는 메타데이터 private String indexCode; @ExcelStyle(headerStyleClass = BackgroundBlue.class, bodyStyleClass = DefaultHeaderStyle.class) // 대상이 column인 style @ExcelColumn(headerName = "나이") private int age; @ExcelStyle(format = "yyyy-mm-dd") @ExcelColumn(headerName = "생일") private LocalDate birthDay; }
Java
복사

완성 코드

최종 구현 목표에 따라 구현된 최종본이다. 만들면서 어떤 방식으로 생각했고 어떻게 만들었는지 모두 적고 싶었지만 완성 이후에 글을 작성하는 것이라 어려움이 좀 있었다. 그래서 최종 코드와 함께 설명과 겪었던 것, 생각했던 것 등을 적었다.

resource 패키지

excel_file 패키지

excel_file/sheet 패키지

excel_file/formula 패키지

excel_file/Style 패키지

코드가 완성까지 과정

최종 코드 동작

최종적으로

최종적으로 내가 목표했던 목표치는 이룰 수 있었던 것 같다. 1. 데이터, 필드명 자동삽입 2. 스타일 삽입 자동, 재사용 가능 3. 수식 입력 개선 및 재사용 가능 이렇게 정했던 목표 외에도 어노테이션을 사용할 수 있도록 했고, 최대한 자바 Stream API를 사용했던 것 같다. 하지만 Stream API를 사용만 한 것이고 잘사용했는지에 대해서는 아직 의문이 있다.

내가 생각한 Stream을 사용할 때 좋았던 점

ExcelResourceFactory
ReflectionUtils
ExcelSheetHelper

부족했던 점

적극적으로 사용만 하려고 했지 아쉽게도 잘 활용했는가에 대해서는 의문이 많이 든다. 예를 들자면 간단하게 끝날 수 있는 것도 비싼 Stream을 활용했다고 생각되기 때문이다. 이유는 Stream 자체가 불필요한 연산을 줄일 수 있는데 내 코드에서 그런 코드가 있는지 찾기가 어렵기 때문이다. 그렇다면 꼭 사용할 필요 있었을까? 라는 의문이 많이 든다. 또한 ExcelStyleResourceFactory부분의 configure(…)부분은 너무 어렵게 만들었다는 생각이 든다. 이런 점에서 잘못된 사용이 가독성을 해치고 장점을 저해한다는 것을 느낄 수 있었다. 간단하게 끝낼 수 있는 것도 많이 돌아간 것 같다. 또한 성능 측정과 같은 부분을 좀 넣고 싶었는데 배움이 아직 많이 부족하다는 것을 알았다. 이 부분은 공부 이후 다시 한번 추가해야겠다.

여정

아래는 이 모듈을 개발하기 위한 과정에서 내가 필기한 것들이다. 아이패드 용량 부족으로 인하여 더 이상 저장해놓을 수 없어서 여기에 올려놓고 다음에 다운받아 볼려고 올려놓은 자료이다.
엑셀 다운로드 모듈 개발기.pdf
9526.5KB

기타

필자와 비슷한 기능을 찾고 있거나 POI 라이브러리 필자가 만든 기능 안에서 사용하고 싶다면 jitpack을 통하여 추가할 수 있다. 이번이 처음 오픈소스를 만들어보겠다고 마음먹고 시작했는데… 너무 부족한 것이 많았던 것 같다. 하지만 이후에도 만들어보겠다고 생각된다면 부족한 점을 채워 다시 한번 만들어봐야겠다.