Skip to content

fix: createWorkbook이 호출자의 Workbook을 닫고 반환하는 문제와 출력 스트림 누수 수정 - #306

Open
z3rotig4r wants to merge 1 commit into
eGovFramework:mainfrom
z3rotig4r:fix/excel-create-workbook-stream-leak
Open

fix: createWorkbook이 호출자의 Workbook을 닫고 반환하는 문제와 출력 스트림 누수 수정#306
z3rotig4r wants to merge 1 commit into
eGovFramework:mainfrom
z3rotig4r:fix/excel-create-workbook-stream-leak

Conversation

@z3rotig4r

Copy link
Copy Markdown
Contributor

변경 이유

EgovExcelServiceImpl.createWorkbook(Workbook, String)은 전달받은 Workbook을 파일로 저장한 뒤 그 Workbook을 반환합니다. 그런데 반환 직전 finally에서 wb.close()를 호출합니다.

} finally {
    LOGGER.debug("ExcelServiceImpl loadExcelObject End ");
    if (wb != null) wb.close();
    if (fileOut != null) fileOut.close();
}
return wb;

닫힌 Workbook을 반환하므로 호출자가 반환값으로 할 수 있는 일이 없습니다. XSSF는 write() 호출 시 Cannot write data, document seems to have been closed already로 실패하고, 시트 추가·셀 수정 후 재저장하는 사용 방식도 성립하지 않습니다. 반환 타입이 void가 아니라 Workbook인 이상 반환값을 쓸 수 있어야 하는데 현재는 어떤 경우에도 쓸 수 없습니다.

닫는 주체가 잘못된 문제이기도 합니다. Workbook을 만든 쪽은 호출자입니다. 이 메서드는 저장만 위임받았는데 호출자가 만든 자원의 수명을 임의로 끝냅니다. 같은 클래스의 loadWorkbook 계열이 Workbook을 반환하고 닫지 않는 것과도 어긋납니다.

두 번째로 자원 누수가 있습니다. finally에서 wb.close()가 먼저 실행되므로 여기서 예외가 나면 다음 줄의 fileOut.close()가 실행되지 않고 FileOutputStream이 열린 채 남습니다. 파일 핸들이 반납되지 않아 Windows에서는 해당 파일이 잠기고, 반복 호출 시 핸들이 누적됩니다.

변경 내용

  • FileOutputStream을 try-with-resources로 감쌌습니다. Workbook 쪽에서 무슨 일이 생기든 스트림은 닫힙니다.
  • finallywb.close()를 제거했습니다. Workbook의 수명은 이를 생성한 호출자가 관리합니다.
  • 인터페이스와 구현 양쪽 javadoc에 닫는 책임이 호출자에게 있다는 점을 명시했습니다.
  • 테스트 3건을 추가했습니다. XSSF·HSSF 각각에 대해 반환된 Workbook이 같은 인스턴스이고 이어서 쓰기가 가능한지 확인하고, close()가 실패하는 Workbook 대역으로 FileOutputStream이 닫혔는지 확인합니다.

테스트 방법

mvn -B -pl Foundation/org.egovframe.rte.fdl.excel -am test

org.egovframe.rte.fdl.excel 모듈 테스트가 모두 통과합니다(9개 클래스 34건, 실패·오류 0건).

회귀 여부는 본문 수정만 되돌리고 테스트만 적용해 확인했습니다. 이 상태에서 신규 3건 중 2건이 실패합니다.

  • testCreateWorkbookReturnsWritableXssfCannot write data, document seems to have been closed already
  • testOutputStreamClosedWhenWorkbookCloseFailswb.close() 예외가 BaseRuntimeException으로 전파되고 스트림은 열린 채 남음
  • testCreateWorkbookReturnsWritableHssf는 수정 전에도 통과합니다. HSSF는 close() 이후에도 메모리상의 워크북 구조가 남아 쓰기가 성립하기 때문이며, 포맷에 따라 증상이 다르게 나타난다는 점을 남겨 두려고 함께 두었습니다.

영향 범위

  • 수정 파일은 EgovExcelServiceImpl.java(본문 4줄)와 EgovExcelService.java(javadoc 1줄), 신규 테스트 1개입니다.
  • 실행환경 안에 이 메서드의 호출처는 없습니다. 반환값을 무시하고 저장 용도로만 쓰던 애플리케이션 코드는 Workbook을 직접 닫아야 합니다. 다만 기존에는 닫힌 Workbook이 반환됐으므로 반환값에 의존하던 코드는 있을 수 없습니다.
  • 공개 API 시그니처, 설정, 의존성 변경은 없습니다.

EgovExcelServiceImpl.createWorkbook(Workbook, String)이 파라미터로 받은
Workbook을 finally에서 닫은 뒤 그대로 반환한다. XSSFWorkbook.close()는
OPCPackage를 닫으므로 반환된 워크북으로 다시 write()하면
"Cannot write data, document seems to have been closed already"로 실패한다.
파라미터로 전달된 자원은 호출자 소유이므로 wb.close() 호출을 제거하고,
반환 계약(호출자가 이어서 사용·재저장 가능, 닫는 책임은 호출자)을 javadoc에 명시한다.
로딩 메서드 5개는 앞서 같은 패턴을 제거했으므로 정책이 일치한다.

또한 finally에서 wb.close()가 IOException을 던지면 다음 줄의
fileOut.close()가 실행되지 않아 FileOutputStream이 누수된다.
FileOutputStream을 try-with-resources로 감싸 항상 닫히도록 한다.

회귀 방지 테스트 3건 추가(EgovExcelCreateWorkbookContractTest).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant