Java/라이브러리 활용

[Java] Spring Boot 환경에서 Excel 활용하기 -2 : Apache POI 기반 엑셀 스타일, 필터, 유효성 적용

adjh54 2025. 2. 25. 18:24
728x170
해당 글에서는 Spring Boot 환경에서 Excel을 활용하기 위해 Apache POI를 기반으로 엑셀 스타일, 필터, 유효성을 적용하는 방법에 대해 알아봅니다.


 
 

💡 [참고] Spring Boot Apache POI 에 대해 상세히 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
주제링크
Spring Boot 환경에서 Excel 활용하기 -1 : Apache POI 기반 업로드, 읽어오기, 다운로드https://adjh54.tistory.com/664
Spring Boot 환경에서 Excel 활용하기 -2 : Apache POI 기반 엑셀 스타일, 필터, 유효성 적용 예시https://adjh54.tistory.com/668
Apache POI 활용 방법을 담은 예제 Repositoryhttps://github.com/adjh54ir/blog-codes/tree/main/spring-boot-excel-poi

 
 

1) Apache POI(Poor Obfuscation Implementation)


💡 Apache POI(Poor Obfuscation Implementation)

- Java 프로그래밍 언어를 사용하여 Microsoft Office 파일 형식을 읽고 쓸 수 있게 해주는 오픈소스 라이브러리입니다. 주로 Excel, Word, PowerPoint 등의 파일을 처리하는 데 사용됩니다.
기능설명
다양한 Office 파일 형식 지원.xls, .xlsx, .doc, .docx 등 Microsoft Office 파일 형식을 폭넓게 지원
플랫폼 독립성순수 Java로 작성되어 모든 플랫폼에서 동일하게 동작
성능과 안정성최적화된 성능과 안정적인 파일 처리 기능 제공
커뮤니티 지원활발한 개발자 커뮤니티와 지속적인 업데이트 제공

 

1. 주요 기능


기능설명
Excel 파일 업로드 및 데이터 읽기사용자가 업로드한 Excel 파일의 내용을 읽어 시스템에서 활용할 수 있도록 처리하는 기능
새로운 Excel 파일 생성 및 데이터 쓰기시스템에서 새로운 Excel 파일을 생성하고 데이터를 작성하여 저장하는 기능
템플릿 기반 Excel 파일 생성미리 정의된 템플릿을 기반으로 데이터를 채워 Excel 파일을 생성하는 기능
대용량 Excel 파일 처리SXSSF를 활용하여 대용량 데이터를 효율적으로 처리하는 기능
Excel 파일 다운로드생성된 Excel 파일을 사용자가 다운로드할 수 있도록 제공하는 기능

 
 

2. Excel 확장자 종류


유형설명
HSSF (Horrible SpreadSheet Format)Excel 97-2003 (.xls) 파일 형식을 처리하기 위한 컴포넌트
XSSF (XML SpreadSheet Format)Excel 2007 이상 버전의 (.xlsx) 파일 형식을 처리하기 위한 컴포넌트
SXSSF (Streaming XML SpreadSheet Format)대용량 Excel 파일을 메모리 효율적으로 처리하기 위한 스트리밍 방식의 컴포넌트
CSV (Comma-Separated Values)단순한 텍스트 기반의 데이터 형식으로, 쉼표(,)로 구분된 값들을 저장하는 형식. Apache POI는 OpenCSV 라이브러리와 함께 사용하여 CSV 파일도 처리 가능

 
 

2) 주요 클래스


 

1. CellStyle


💡 CellStyle

- 셀의 서식을 지정하는 클래스로, 글꼴, 배경색, 테두리, 정렬 등 셀의 모든 시각적 속성을 설정할 수 있는 클래스입니다.
// 워크북과 시트 생성
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("테스트");

// 헤더 스타일 생성
CellStyle headerStyle = workbook.createCellStyle();

// 배경색 설정
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

// 테두리 설정
headerStyle.setBorderTop(BorderStyle.THIN);
headerStyle.setBorderBottom(BorderStyle.THIN);
headerStyle.setBorderLeft(BorderStyle.THIN);
headerStyle.setBorderRight(BorderStyle.THIN);

// 정렬 설정
headerStyle.setAlignment(HorizontalAlignment.CENTER);

// 헤더 행 생성 및 스타일 적용
Row headerRow = sheet.createRow(0);
Cell cell = headerRow.createCell(0);
cell.setCellValue("테스트 헤더");
cell.setCellStyle(headerStyle);

CellStyle (POI API Documentation)

cloneStyleFrom void cloneStyleFrom(CellStyle source) Clones all the style information from another CellStyle, onto this one. This CellStyle will then have all the same properties as the source, but the two may be edited independently. Any stylings on thi

poi.apache.org

 
 

2. DataValidationHelper


💡 DataValidationHelper

- 데이터 유효성 검사 규칙을 생성하는 도우미 클래스입니다.
- sheet.getDataValidationHelper()를 통해 얻을 수 있으며, 다양한 유효성 검사 제약 조건을 만드는 데 사용됩니다.
// DataValidationHelper 생성
DataValidationHelper dvHelper = sheet.getDataValidationHelper();

// 유효성 검사를 적용할 셀 범위 지정 (1행~10행, A열)
CellRangeAddressList addressList = new CellRangeAddressList(1, 10, 0, 0);

// 드롭다운 목록 옵션 설정
String[] options = {"옵션1", "옵션2", "옵션3"};
DataValidationConstraint constraint = dvHelper.createExplicitListConstraint(options);

// 유효성 검사 생성 및 적용
DataValidation validation = dvHelper.createValidation(constraint, addressList);
validation.setShowErrorBox(true);
sheet.addValidationData(validation);

DataValidationHelper (POI API Documentation)

poi.apache.org

 

3. CellRangeAddressList


💡 CellRangeAddressList

- 유효성 검사를 적용할 셀 범위를 지정하는 클래스입니다. 시작 행, 끝 행, 시작 열, 끝 열을 매개변수로 받아 범위를 정의합니다.
// 예시: 1행부터 10행까지, A열(0)부터 C열(2)까지 범위 지정
CellRangeAddressList addressList = new CellRangeAddressList(1, 10, 0, 2);

// 예시: 특정 셀 하나만 지정 (3행 B열)
CellRangeAddressList singleCellList = new CellRangeAddressList(3, 3, 1, 1);

// 예시: 불연속적인 범위 지정
CellRangeAddressList multipleRanges = new CellRangeAddressList();
multipleRanges.addCellRangeAddress(new CellRangeAddress(1, 5, 0, 0));  // A2:A6
multipleRanges.addCellRangeAddress(new CellRangeAddress(8, 12, 0, 0)); // A9:A13

CellRangeAddressList (POI API Documentation)

CellRangeAddressList(int firstRow, int lastRow, int firstCol, int lastCol) Convenience constructor for creating a CellRangeAddressList with a single CellRangeAddress.

poi.apache.org

 

4. DataValidation


💡 DataValidation

- 최종적으로 생성된 유효성 검사 규칙을 나타내는 클래스입니다. 오류 메시지, 입력 메시지 등을 설정하고 시트에 적용할 수 있습니다
// DataValidationHelper 생성
DataValidationHelper validationHelper = sheet.getDataValidationHelper();

// 유효성 검사를 적용할 셀 범위 설정 (A2:A10)
CellRangeAddressList addressList = new CellRangeAddressList(1, 10, 0, 0);

// 드롭다운 목록으로 사용할 데이터 설정
String[] allowedValues = {"Option 1", "Option 2", "Option 3"};
DataValidationConstraint constraint = validationHelper.createExplicitListConstraint(allowedValues);

// DataValidation 생성 및 설정
DataValidation validation = validationHelper.createValidation(constraint, addressList);

// 오류 메시지 설정
validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
validation.setShowErrorBox(true);
validation.setErrorTitle("입력 오류");
validation.setError("목록에서 값을 선택해주세요.");

// 입력 메시지 설정
validation.setShowPromptBox(true);
validation.setPromptTitle("선택 안내");
validation.setPrompt("드롭다운 목록에서 항목을 선택하세요.");

// 시트에 유효성 검사 추가
sheet.addValidationData(validation);

DataValidation (POI API Documentation)

createErrorBox void createErrorBox(java.lang.String title, java.lang.String text) Sets the title and text for the error box . Error box is displayed when the user enters an invalid value int o a cell which belongs to this validation object . In order fo

poi.apache.org

 
 

5. DataFormat


💡 DataFormat

- 셀의 데이터 형식을 지정하는 클래스입니다.
- 날짜, 시간, 숫자, 통화 등 다양한 형식을 설정할 수 있으며 workbook.createDataFormat()으로 생성합니다.
// DataFormat 예시
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("테스트");
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);

// 셀 스타일 생성
CellStyle style = workbook.createCellStyle();

// DataFormat 생성 및 적용
DataFormat format = workbook.createDataFormat();

// 날짜 형식 설정
style.setDataFormat(format.getFormat("yyyy-mm-dd"));
cell.setCellStyle(style);
cell.setCellValue(new Date());

// 통화 형식 설정
CellStyle currencyStyle = workbook.createCellStyle();
currencyStyle.setDataFormat(format.getFormat("#,##0원"));
Cell currencyCell = row.createCell(1);
currencyCell.setCellStyle(currencyStyle);
currencyCell.setCellValue(15000);

// 소수점 형식 설정
CellStyle decimalStyle = workbook.createCellStyle();
decimalStyle.setDataFormat(format.getFormat("#,##0.00"));
Cell decimalCell = row.createCell(2);
decimalCell.setCellStyle(decimalStyle);
decimalCell.setCellValue(123.456);

DataFormat (POI API Documentation)

getFormat short getFormat(java.lang.String format) get the format index that matches the given format string. Creates a new format if one is not found. Aliases text to the proper format. Parameters: format - string matching a built in format Returns: ind

poi.apache.org

 
 

3) 엑셀 스타일 적용


 

1. Header 스타일 적용 : border, 헤더 높이/너비 지정


💡 Header 스타일 적용 : border, 헤더 높이/너비 지정

- Apache POI를 사용하여 엑셀 파일의 헤더 스타일을 설정하는 Java 코드입니다

1. 새로운 엑셀 워크북과 시트를 생성하고 "사용자 등록 정보"라는 이름을 부여하였습니다.

2. 헤더 행의 높이를 600으로 설정하였습니다.

3. 4개의 컬럼 헤더를 설정 ("사용자 번호", "사용자 이름", "사용자 관계", "사용자 연락처")하였습니다.

4. 헤더 스타일에 적용하였습니다.
- 회색 배경색 적용하였습니다.
- 상하좌우 테두리 적용하였습니다.
- 수직/수평 가운데 정렬하였습니다.

- 마지막으로 각 열의 너비를 자동으로 조정하고, 기본 너비에서 1.5배로 여유 공간을 추가했습니다. 
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("사용자 등록 정보");

// 엑셀 시트 생성
Row headerRow = sheet.createRow(0);

// 헤더 행 높이 지정
headerRow.setHeight((short) 600);

// 헤더 구성
String[] headers = {"사용자 번호", "사용자 이름", "사용자 관계", "사용자 연락처(예: 010-0000-0000)"};

// 테두리와 배경색을 모두 포함하는 헤더 스타일 생성
CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headerStyle.setBorderTop(BorderStyle.THIN);
headerStyle.setBorderBottom(BorderStyle.THIN);
headerStyle.setBorderLeft(BorderStyle.THIN);
headerStyle.setBorderRight(BorderStyle.THIN);
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 수직 가운데 정렬
headerStyle.setAlignment(HorizontalAlignment.CENTER); // 수평 가운데 정렬

// 헤더 값 및 스타일 적용
for (int i = 0; i < headers.length; i++) {
    Cell cell = headerRow.createCell(i);
    cell.setCellValue(headers[i]);
    cell.setCellStyle(headerStyle);
}
// 열 너비 자동 조정
for (int i = 0; i < headers.length; i++) {
    sheet.autoSizeColumn(i);
    // 기본 너비에 약간의 여유 공간 추가
    int currentWidth = sheet.getColumnWidth(i);
    sheet.setColumnWidth(i, (int) (currentWidth * 1.5));
}

 
 

💡 Header에 스타일이 적용이 되었습니다.

 
 

2. 나머지 영역 스타일 적용 : border


💡 나머지 영역 스타일 적용 : border

- Apache POI를 사용하여 엑셀 시트의 나머지 영역에 테두리 스타일을 적용하는 코드입니다.

1. 테두리 스타일 생성
- CellStyle을 생성하여 상/하/좌/우에 얇은(THIN) 테두리를 설정합니다

2. 모든 셀에 테두리 적용하였습니다.
- 50행까지 순회하면서 각 셀에 테두리를 적용하였습니다.
- 빈 행이나 셀이 있다면 새로 생성하였습니다.
- 헤더행(첫 번째 행)을 제외한 모든 셀에 테두리 스타일을 적용하였습니다.
  // 나머지 셀들을 위한 테두리 스타일
  CellStyle borderStyle = workbook.createCellStyle();
  borderStyle.setBorderTop(BorderStyle.THIN);
  borderStyle.setBorderBottom(BorderStyle.THIN);
  borderStyle.setBorderLeft(BorderStyle.THIN);
  borderStyle.setBorderRight(BorderStyle.THIN);

  // 모든 셀에 테두리 적용
  for (int row = 0; row <= 50; row++) {
      Row currentRow = sheet.getRow(row);
      if (currentRow == null) {
          currentRow = sheet.createRow(row);
      }
      for (int col = 0; col < headers.length; col++) {
          Cell cell = currentRow.getCell(col);
          if (cell == null) {
              cell = currentRow.createCell(col);
          }
          if (row != 0) {  // 첫 번째 행(헤더)가 아닌 경우에만 borderStyle 적용
              cell.setCellStyle(borderStyle);
          }
      }
  }

 

💡 나머지 영역에 대해서 border가 적용이 되었습니다.

 
 

4) 엑셀 입력 필터 적용


 

1. 필터 선택 적용


💡 필터 선택 적용

- Excel 파일에서 데이터 유효성 검사(validation)를 설정하는 부분입니다.

1. 사용자 관계 필드에 드롭다운 선택 목록을 추가하였습니다.
2. 1행부터 50행까지, 3번째 열(인덱스 2)에 유효성 검사를 적용하였습니다.
3. 선택 가능한 옵션으로 "본인", "모", "부", "조모", "조부"를 설정하였습니다.
4. 잘못된 값이 입력될 경우 오류 메시지를 표시하도록 설정하였습니다.

- 이렇게 설정하면 해당 셀에서는 지정된 다섯 가지 옵션 중에서만 선택이 가능하며, 다른 값은 입력할 수 없게 됩니다.
DataValidationHelper dvHelper = sheet.getDataValidationHelper();

// 사용자 관계 선택 목록 설정
CellRangeAddressList relationAddressList = new CellRangeAddressList(1, 50, 2, 2);
String[] relationships = {"본인", "모", "부", "조모", "조부"};
DataValidationConstraint relationConstraint = dvHelper.createExplicitListConstraint(relationships);
DataValidation relationValidation = dvHelper.createValidation(relationConstraint, relationAddressList);
relationValidation.setShowErrorBox(true);
sheet.addValidationData(relationValidation);

 

💡 필터를 적용하여 selecbox가 출력이 된 모습

 

💡 유효성 검증 실패 시 메시지 출력

 
 

2. formatting 적용 방법 : 핸드폰 번호


💡 formatting 적용 방법 : 핸드폰 번호

- 선택하신 코드는 Apache POI를 사용하여 엑셀 파일의 전화번호 형식을 설정하는 코드입니다.

1. 전화번호 형식 설정 (000-0000-0000 패턴) 하였습니다.
2. 50개 행에 대해 전화번호 스타일 적용하였습니다.
3. 모든 셀에 테두리 스타일 적용 (상/하/좌/우) 하였습니다.

- 이 코드를 적용하면 숫자로 입력된 전화번호가 자동으로 000-0000-0000 형식으로 변환되어 표시됩니다.
// 사용자 연락처 셀 형식 설정
CellStyle phoneStyle = workbook.createCellStyle();
DataFormat phoneFormat = workbook.createDataFormat();
phoneStyle.setDataFormat(phoneFormat.getFormat("000-0000-0000")); // 전화번호 형식 적용

// 사용자 연락처 열에 형식 적용
for (int row = 1; row <= 50; row++) {
    Cell phoneCell = sheet.getRow(row).getCell(3);
    phoneCell.setCellStyle(phoneStyle);
}

// 테두리 설정 추가
phoneStyle.setBorderTop(BorderStyle.THIN);
phoneStyle.setBorderBottom(BorderStyle.THIN);
phoneStyle.setBorderLeft(BorderStyle.THIN);
phoneStyle.setBorderRight(BorderStyle.THIN);

 

💡 아래와 같이 숫자로 입력을 한 경우

 

💡 아래와 같이 formatting이 적용이 됩니다.

 
 

3. 전화번호 형식 유효성 검증


💡 전화번호 형식 유효성 검증

- 선택하신 코드는 Apache POI를 사용하여 엑셀 파일의 전화번호 형식과 유효성 검증을 설정하는 부분입니다.

1. 전화번호 형식 설정 (000-0000-0000 패턴) 하였습니다.
2. 테두리 스타일 적용(상/하/좌/우) 하였습니다.
3. 유효성 검증
- 전화번호 길이가 13자리(하이픈 포함)인지 확인
- 잘못된 형식 입력 시 오류 메시지 표시

- 유효성 검사 실패 시 데이터 입력 차단이 코드가 적용되면 사용자는 올바른 전화번호 형식으로만 데이터를 입력할 수 있으며, 잘못된 형식 입력 시 오류 메시지가 표시됩니다.
// 사용자 연락처 셀 형식 설정
CellStyle phoneStyle = workbook.createCellStyle();
DataFormat phoneFormat = workbook.createDataFormat();
phoneStyle.setDataFormat(phoneFormat.getFormat("000-0000-0000")); // 전화번호 형식 적용

// 테두리 설정 추가
phoneStyle.setBorderTop(BorderStyle.THIN);
phoneStyle.setBorderBottom(BorderStyle.THIN);
phoneStyle.setBorderLeft(BorderStyle.THIN);
phoneStyle.setBorderRight(BorderStyle.THIN);

/*
* 4. 사용자 연락처 유효성 검증 추가
*/
CellRangeAddressList phoneAddressList = new CellRangeAddressList(1, 50, 3, 3);
DataValidationConstraint phoneConstraint = dvHelper.createTextLengthConstraint(
    DataValidationConstraint.OperatorType.EQUAL,
    "13", // 000-0000-0000 형식의 길이
    null
);
DataValidation phoneValidation = dvHelper.createValidation(phoneConstraint, phoneAddressList);
phoneValidation.setShowErrorBox(true);
phoneValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
phoneValidation.createErrorBox("입력 오류", "올바른 전화번호 형식(000-0000-0000)으로 입력해주세요.");
sheet.addValidationData(phoneValidation);

// 사용자 연락처 열에 형식 적용
for (int row = 1; row <= 50; row++) {
Cell phoneCell = sheet.getRow(row).getCell(3);
phoneCell.setCellStyle(phoneStyle);
}

 

💡 잘못된 형식으로 입력을 한 경우

 
 

💡 아래와 같이 오류 메시지가 출력이 됩니다.

 
 

4. 숫자만 입력 허용


💡 숫자만 입력 허용

- Excel 파일에서 숫자 입력에 대한 유효성 검증을 설정하는 부분입니다

1. 1~50행의, 첫 번째 열(0번 인덱스)에 대해 숫자 입력 제한을 설정하였습니다.
2. 허용되는 숫자 범위는 0에서 99999999까지 지정하였습니다.
3. 숫자가 아닌 값을 입력하면 "입력 오류" 메시지가 표시되며 "숫자만 입력이 가능합니다"라는 안내문이 출력됩니다.

- 이렇게 설정하면 지정된 셀 범위에 숫자만 입력할 수 있으며, 다른 형식의 데이터는 입력이 제한됩니다.
DataValidationHelper dvHelper = sheet.getDataValidationHelper();

// 숫자만 입력 가능하도록 유효성 검증 추가
CellRangeAddressList addressList = new CellRangeAddressList(1, 50, 0, 0);
DataValidationConstraint constraint = dvHelper.createNumericConstraint(
        DataValidationConstraint.ValidationType.INTEGER,
        DataValidationConstraint.OperatorType.BETWEEN,
        "0",
        "99999999"
);
DataValidation validation = dvHelper.createValidation(constraint, addressList);
validation.setShowErrorBox(true);
validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
validation.createErrorBox("입력 오류", "숫자만 입력이 가능합니다.");
sheet.addValidationData(validation);

 

 💡 정상적으로 숫자를 입력한 경우

 

💡 숫자외에 입력을 한 경우

 
 
 

5. 앞자리 0 허용


💡 앞자리 0 허용

- Apache POI를 사용하여 엑셀 파일에서 앞자리 0을 허용하는 설정을 구현하였습니다.

- CellStyle과 DataFormat을 생성하여 "0000" 형식을 설정합니다. 이는 4자리 숫자를 표시하며 앞자리에 0이 올 수 있도록 합니다.
- 셀에 테두리 스타일을 적용합니다 (상/하/좌/우)
- 1~50행의 첫 번째 열(0번 인덱스)에 이 스타일을 적용합니다

- 이 방식으로 표시되는 것은 형식적인 것이며, 실제 값은 숫자 그대로 유지된다는 점에 주의해야 합니다
 /*
   * 1. "사용자 번호" 유효성 검증
   */
  CellStyle attendanceStyle = workbook.createCellStyle();
  DataFormat attendanceFormat = workbook.createDataFormat();
  attendanceStyle.setDataFormat(attendanceFormat.getFormat("0000"));      // 0 허용 추가

  // 테두리 설정 추가
  attendanceStyle.setBorderTop(BorderStyle.THIN);
  attendanceStyle.setBorderBottom(BorderStyle.THIN);
  attendanceStyle.setBorderLeft(BorderStyle.THIN);
  attendanceStyle.setBorderRight(BorderStyle.THIN);

  // 사용자 번호 열에 형식 적용
  for (int row = 1; row <= 50; row++) {
      Cell attendanceCell = sheet.getRow(row).getCell(0);
      attendanceCell.setCellStyle(attendanceStyle);
  }

 

💡 아래와 같이 111을 입력하였을 때

 
 

 💡 아래와 같이 0111로 입력이 됩니다. 그러나 사실상 값은 111이기에 후 처리가 필요합니다.

 
 

💡 “0101”로 입력하였을 때 앞자리에 0이 입력이 됨을 확인하였습니다.

 
 

5. 숫자 + 앞자리 0 허용


💡 숫자 + 앞자리 0 허용

1. 앞자리 0 허용 설정
- "0000" 형식을 사용하여 4자리 숫자를 표시
- 앞자리에 0이 올 수 있도록 설정셀에 기본 테두리 스타일 적용하였습니다.

2. 숫자 유효성 검증
- 1~50행의 첫 번째 열에 대해 숫자 입력 제한 설정하였습니다.
- 0부터 99999999 사이의 숫자만 입력 가능
- 잘못된 입력 시 "숫자만 입력이 가능합니다" 오류 메시지 표시하였습니다.

- 이렇게 함으로써 사용자 번호 필드는 앞자리에 0을 포함할 수 있으면서도 숫자만 입력되도록 제한됩니다.
 /*
   * 1. "사용자 번호" 유효성 검증
   */
  CellStyle attendanceStyle = workbook.createCellStyle();
  DataFormat attendanceFormat = workbook.createDataFormat();
  attendanceStyle.setDataFormat(attendanceFormat.getFormat("0000"));      // 0 허용 추가

  // 테두리 설정 추가
  attendanceStyle.setBorderTop(BorderStyle.THIN);
  attendanceStyle.setBorderBottom(BorderStyle.THIN);
  attendanceStyle.setBorderLeft(BorderStyle.THIN);
  attendanceStyle.setBorderRight(BorderStyle.THIN);

  // 사용자 번호 열에 형식 적용
  for (int row = 1; row <= 50; row++) {
      Cell attendanceCell = sheet.getRow(row).getCell(0);
      attendanceCell.setCellStyle(attendanceStyle);
  }

  // 숫자만 입력 가능하도록 유효성 검증 추가
  CellRangeAddressList addressList = new CellRangeAddressList(1, 50, 0, 0);
  DataValidationConstraint constraint = dvHelper.createNumericConstraint(
          DataValidationConstraint.ValidationType.INTEGER,
          DataValidationConstraint.OperatorType.BETWEEN,
          "0",
          "99999999"
  );
  DataValidation validation = dvHelper.createValidation(constraint, addressList);
  validation.setShowErrorBox(true);
  validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
  validation.createErrorBox("입력 오류", "숫자만 입력이 가능합니다.");
  sheet.addValidationData(validation);

 

💡 숫자에 대한 유효성 검증을 수행합니다.

 

💡 “0030” 값에 대해서 00에 대한 허용을 수행하였습니다

 
 

5) 전체 적용 코드 확인


 

💡 전체 적용 코드 확인

- 위에와 같이 구성한 엑셀 스타일/필터에 대한 구성방법에 대한 코드를 확인해 봅니다.

 
 

1. excelDownload.html


💡 excelDownload.html

- Thmeleaf 기반의 엑셀 다운로드 함수를 동작하기 위한 화면입니다.
- 템플릿 다운로드 버튼 클릭 시 '/excel/template-download' 엔드포인트로 POST 요청을 보냅니다
- 서버로부터 받은 응답을 blob 형태로 변환하여 '사용자 등록(양식).xlsx' 파일로 다운로드합니다
- 에러 발생 시 콘솔에 오류 메시지를 출력합니다
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>User Template Download</title>
    <script th:inline="javascript">
        const downloadExcel = async () => {
            await fetch('/excel/template-download', {
                method: "POST",
                mode: 'same-origin',
                headers: {
                    'Content-Type': 'application/json',
                },
            })
                .then(response => response.blob())
                .then(blob => {
                    const url = window.URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = '사용자 등록(양식).xlsx';
                    document.body.appendChild(a);
                    a.click();
                    window.URL.revokeObjectURL(url);
                    a.remove();
                })
                .catch((error) => {
                    console.log("엑셀 다운로드 중에 오류가 발생하였습니다.", error);
                    return
                });
        }
    </script>
</head>
<body>
<div class="container">
    <h2>사용자 등록 템플릿 다운로드</h2>
    <button type="button" onclick="downloadExcel()">템플릿 다운로드</button>
</div>
</body>
</html>

 

💡 최초 화면 페이지를 여는 엔드포인트를 호출하면 아래와 같은 화면이 출력이 됩니다.

 

💡 템플릿 다운로드 버튼을 누르면 구성한 액셀이 다운로드됩니다.

 
 

2. ExcelController


 
 

엔드포인트HTTP Method설명
/excel/downloadGET엑셀 다운로드 화면을 출력
/excel/template-downloadPOST엑셀 템플릿 파일을 다운로드

 

/**
 * 엑셀 처리를 관리하는 Controller
 *
 * @author : leejonghoon
 * @fileName : ExcelController
 * @since : 2025. 2. 11.
 */
@Slf4j
@Controller
@RequiredArgsConstructor
@RequestMapping("/excel")
public class ExcelController {

    private final ExcelService excelService;

    /**
     * 엑셀 다운로드 화면 출력
     *
     * @return
     */
    @GetMapping("/download")
    public String excelDownloadPage() {
        return "/pages/excelDownload";
    }

    /**
     * 엑셀 템플릿 다운로드
     *
     * @param model
     * @return
     */
    @PostMapping("/template-download")
    public ResponseEntity<Resource> downloadExcelTemplate(Model model) {
        Resource resource = excelService.downloadExcelTemplate(model);
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=사용자 등록(양식).xlsx")
                .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
                .body(resource);
    }

}

 
 

3. ExcelService


💡 ExcelService

- Apache POI를 사용하여 사용자 등록을 위한 엑셀 템플릿을 생성하는 기능을 구현합니다.

1. 엑셀 시트 생성 및 기본 설정
- 새로운 워크북과 "사용자 등록 정보" 시트 생성
- 헤더 행 높이와 열 너비 자동 조정

2. 헤더 스타일링
- 회색 배경색 적용
- 테두리 설정수직/수평 가운데 정렬

3. 데이터 유효성 검증
- 사용자 번호: 0-99999999 사이의 숫자만 입력 가능
- 사용자 관계: 본인, 모, 부, 조모, 조부 중 선택 가능사
- 용자 연락처: 000-0000-0000 형식으로 제한

4. 셀 스타일링
- 모든 셀에 테두리 적용각 컬럼별 특수 형식 지정 (전화번호 형식 등)

5. 마지막으로 생성된 엑셀 파일을 ByteArrayResource로 변환하여 다운로드 가능한 형태로 반환합니다.
package com.blog.springbootexcelpoi;

import com.blog.springbootexcelpoi.dto.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 엑셀 처리를 위한 서비스
 *
 * @author : leejonghoon
 * @fileName : ExcelService
 * @since : 2025. 2. 11.
 */
@Slf4j
@Service
public class ExcelService {

    /**
     * 엑셀 템플릿 다운로드
     *
     * @param model
     * @return
     */
    public Resource downloadExcelTemplate(Model model) {
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("사용자 등록 정보");

        // 엑셀 시트 생성
        Row headerRow = sheet.createRow(0);

        // 헤더 행 높이 지정
        headerRow.setHeight((short) 600);

        // 헤더 구성
        String[] headers = {"사용자 번호", "사용자 이름", "사용자 관계", "사용자 연락처(예: 010-0000-0000)"};

        // 테두리와 배경색을 모두 포함하는 헤더 스타일 생성
        CellStyle headerStyle = workbook.createCellStyle();
        headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerStyle.setBorderTop(BorderStyle.THIN);
        headerStyle.setBorderBottom(BorderStyle.THIN);
        headerStyle.setBorderLeft(BorderStyle.THIN);
        headerStyle.setBorderRight(BorderStyle.THIN);
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 수직 가운데 정렬
        headerStyle.setAlignment(HorizontalAlignment.CENTER); // 수평 가운데 정렬

        // 헤더 값 및 스타일 적용
        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            cell.setCellStyle(headerStyle);
        }

        // 열 너비 자동 조정
        for (int i = 0; i < headers.length; i++) {
            sheet.autoSizeColumn(i);
            // 기본 너비에 약간의 여유 공간 추가
            int currentWidth = sheet.getColumnWidth(i);
            sheet.setColumnWidth(i, (int) (currentWidth * 1.5));
        }

        // 나머지 셀들을 위한 테두리 스타일
        CellStyle borderStyle = workbook.createCellStyle();
        borderStyle.setBorderTop(BorderStyle.THIN);
        borderStyle.setBorderBottom(BorderStyle.THIN);
        borderStyle.setBorderLeft(BorderStyle.THIN);
        borderStyle.setBorderRight(BorderStyle.THIN);

        // 모든 셀에 테두리 적용
        for (int row = 0; row <= 50; row++) {
            Row currentRow = sheet.getRow(row);
            if (currentRow == null) {
                currentRow = sheet.createRow(row);
            }
            for (int col = 0; col < headers.length; col++) {
                Cell cell = currentRow.getCell(col);
                if (cell == null) {
                    cell = currentRow.createCell(col);
                }
                if (row != 0) {  // 첫 번째 행(헤더)가 아닌 경우에만 borderStyle 적용
                    cell.setCellStyle(borderStyle);
                }
            }
        }
        DataValidationHelper dvHelper = sheet.getDataValidationHelper();

        /*
         * 1. "사용자 번호" 유효성 검증
         */
        CellStyle attendanceStyle = workbook.createCellStyle();
        DataFormat attendanceFormat = workbook.createDataFormat();
        attendanceStyle.setDataFormat(attendanceFormat.getFormat("0000"));      // 0 허용 추가

        // 테두리 설정 추가
        attendanceStyle.setBorderTop(BorderStyle.THIN);
        attendanceStyle.setBorderBottom(BorderStyle.THIN);
        attendanceStyle.setBorderLeft(BorderStyle.THIN);
        attendanceStyle.setBorderRight(BorderStyle.THIN);

        // 사용자 번호 열에 형식 적용
        for (int row = 1; row <= 50; row++) {
            Cell attendanceCell = sheet.getRow(row).getCell(0);
            attendanceCell.setCellStyle(attendanceStyle);
        }

        // 숫자만 입력 가능하도록 유효성 검증 추가
        CellRangeAddressList addressList = new CellRangeAddressList(1, 50, 0, 0);
        DataValidationConstraint constraint = dvHelper.createNumericConstraint(
                DataValidationConstraint.ValidationType.INTEGER,
                DataValidationConstraint.OperatorType.BETWEEN,
                "0",
                "99999999"
        );
        DataValidation validation = dvHelper.createValidation(constraint, addressList);
        validation.setShowErrorBox(true);
        validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
        validation.createErrorBox("입력 오류", "숫자만 입력이 가능합니다.");
        sheet.addValidationData(validation);

        /*
         * 2. "사용자 관계" 유효성 검증
         */
        CellRangeAddressList relationAddressList = new CellRangeAddressList(1, 50, 2, 2);
        String[] relationships = {"본인", "모", "부", "조모", "조부"};
        DataValidationConstraint relationConstraint = dvHelper.createExplicitListConstraint(relationships);
        DataValidation relationValidation = dvHelper.createValidation(relationConstraint, relationAddressList);
        relationValidation.setShowErrorBox(true);
        sheet.addValidationData(relationValidation);

        // 사용자 연락처 셀 형식 설정
        CellStyle phoneStyle = workbook.createCellStyle();
        DataFormat phoneFormat = workbook.createDataFormat();
        phoneStyle.setDataFormat(phoneFormat.getFormat("000-0000-0000")); // 전화번호 형식 적용

        // 테두리 설정 추가
        phoneStyle.setBorderTop(BorderStyle.THIN);
        phoneStyle.setBorderBottom(BorderStyle.THIN);
        phoneStyle.setBorderLeft(BorderStyle.THIN);
        phoneStyle.setBorderRight(BorderStyle.THIN);

        /*
         * 4. 사용자 연락처 유효성 검증 추가
         */
        CellRangeAddressList phoneAddressList = new CellRangeAddressList(1, 50, 3, 3);
        DataValidationConstraint phoneConstraint = dvHelper.createTextLengthConstraint(
                DataValidationConstraint.OperatorType.EQUAL,
                "13", // 000-0000-0000 형식의 길이
                null
        );
        DataValidation phoneValidation = dvHelper.createValidation(phoneConstraint, phoneAddressList);
        phoneValidation.setShowErrorBox(true);
        phoneValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
        phoneValidation.createErrorBox("입력 오류", "올바른 전화번호 형식(000-0000-0000)으로 입력해주세요.");
        sheet.addValidationData(phoneValidation);

        // 사용자 연락처 열에 형식 적용
        for (int row = 1; row <= 50; row++) {
            Cell phoneCell = sheet.getRow(row).getCell(3);
            phoneCell.setCellStyle(phoneStyle);
        }

        // 파일 생성
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            workbook.write(outputStream);
            workbook.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        ByteArrayResource resource = new ByteArrayResource(outputStream.toByteArray());
        model.addAttribute("downloadMessage", "다운로드가 완료되었습니다.");
        return resource;

    }

}

 
 
 
 
 
오늘도 감사합니다 😀 

 
 
 

그리드형