[SpringBoot] 데이터 엑셀 다운로드 (feat. 어노테이션만들기,
안녕하세요 ~~
좀 지난 개발 이야기지만 끄적여보겠습니다 (다시 사용할 일이 생겨서..)
사내 근태프로그램을 만드는 중에 개인의 월 근태 기록을 엑셀로 다운로드 받아야하는 일이 생겼습니다.
저는 컴퓨터공학 전공을 하긴 했지만 학교다닐땐 진짜 못했걸랑요
시험을 위해서만 공부해왔던 저라...?
그래서 자바와 스프링부트를 혼자 독학했어요...
암튼 제 사담이었고요...
여기저기 구글링을 하며
이 기능에 제일 적합하면서 최대한 유연성있는 코드를 짜보자 해서 해당 코드를 만들게 됐어요
근태기록이라 함은
정해진 근무 스케줄이 있고 (저희는 유연근로제는 하지 않음) 정해진 식사시간, 연장근로시작, 휴일근로시간 등등 대체근무 등 엄청 많잖아요?
이를 에스원데이터와 연동하고 전자결재와 연동하기 전까지의 엑셀 데이터를 추출하는 작업을 했습니다.
전재결재 연동 후에도 엑셀 데이터 추출가능합니다 ^^
우선 ..
엑셀데이터를 출력하기 위해서는 정해진 틀이 필요하기 때문에 제가 손수 엑셀로 이 폼으로 나오면 좋겠다를 디자인 했어여
(저희는 기획자가 없어서 제가 혼자 다 해야함니다...)
파일은 yyyy년 MM월 이름.xlsx 으로 받아져야하고
아래의 디자인으로 파일이 받아져야합니다.,

매주 52시간 근로의 법이 있기 때문에 매주 총 근로시간도 계산해줘야하구요
실제 출근시각과 퇴근시각은 정산하는 시각과 다를 수 있기때문에 모든걸 고려해줘야합니다
아무튼... 저는 이 디자인 그리는 것도 정말 힘들었네요..
노란색 cell의 경우 출퇴근시각이 찍혀있지 않으면 노란색으로 표기했습니다!
근무일자, 근무요일~~~ 연장근무합계까지
cell backgroud가 정해져있는데 이는 어노테이션을 생성해줘서 dto에 어노테이션을 붙여 알아서 읽어오게끔했어요
아래 코드를 봐주세욧
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelColumn {
String headerName() default "";
}
해당 어노테이션을 생성한 후에,
@Data
@Builder
@Getter @Setter
public class WorkHistoryDTO extends BaseTimeEntity {
private int id;
@ExcelColumn(headerName = "근무일자")
private String work_date;
}
위의 예시처럼 어노테이션을 지정하게 되면
어노테이션을 붙여놓은 컬럼만 읽어와 자동으로 cell value를 추가하게 됩니다
for (int i = 0; i < excelHeaderList.size(); i++) {
cell = row.createCell(i);
cell.setCellValue(excelHeaderList.get(i));
cell.setCellStyle(headerStyle);
}
요런식으로 하면 value를 추가하고 셀에 스타일을 추가하게 됩니다!
private Class<?> getClass(List<?> data) {
// List가 비어있지 않다면 List가 가지고 있는 모든 DTO는 같은 필드를 가지고 있으므로,
// 맨 마지막 DTO만 빼서 클래스 정보를 반환한다.
if(!CollectionUtils.isEmpty(data)) {
return data.get(data.size()-1).getClass();
} else {
throw new IllegalStateException("조회된 리스트가 비어 있습니다. 확인 후 다시 진행해주시기 바랍니다.");
}
}
private List<String> getHeaderName(Class<?> type) {
// 스트림으로 엑셀 헤더 이름들을 리스트로 반환
// 1. 매개변수로 전달된 클래스의 필드들을 배열로 받아, 스트림을 생성
// 2. @ExcelColumn 애너테이션이 붙은 필드만 수집
// 3. @ExcelColumn 애너테이션이 붙은 필드에서 애너테이션의 값을 매핑
// 4. LinkedList로 반환
List<String> excelHeaderNameList = Arrays.stream(type.getDeclaredFields())
.filter(s -> s.isAnnotationPresent(ExcelColumn.class))
.map(s -> s.getAnnotation(ExcelColumn.class).headerName())
.collect(Collectors.toCollection(LinkedList::new));
// 헤더의 이름을 담은 List가 비어있을 경우, 헤더 이름이 지정되지 않은 것이므로, 예외를 발생시킨다.
if(CollectionUtils.isEmpty(excelHeaderNameList)) {
throw new IllegalStateException("헤더 이름이 없습니다.");
}
return excelHeaderNameList;
}
해당 코드를 참고 하여 엑셀을 생성하는데 도움이 되셨으면 합니다~
물론 다음의 나에게두 ^^
감사합니닷
더 좋은 코드가 있으면 알려주세용