사내에서 Excel Export/Import 기능을 구현 하던 중, try-with-resources를 남용하다 아래와 같은 에러를 만났다.
"Cannot write data, document seems to have been closed already"
try-with-resources이 무엇인지, webflux환경에서 reactive하게 개발할 때 주의할 점에 대해 알아보도록 하자.
try-with-resources?
1. java7 이전의 try-catch-finally
java7 이전에는 Closable인터페이스를 구현하고 있는 자원들은 사용이후에 반드시 close 메소드를 호출 해줘야 했다.
예를 들어 아래와 같은 코드처럼 말이다.
//...
SXSSFWorkbook workbook = new SXSSFWorkbook();
try{
//create sheet, set datas, write workbook ...
} catch{
} finally{
if(workbook != null) workbook.close();
}
//...
코드가 복잡해지고 nested try-catch가 발생하게 되어 가독성 및 디버깅에 문제가 있었다.
2. try-with-resources
이러한 문제점을 해결하기 위해 나온 것이 try-with-resources라는 문법이 java7부터 추가 되었다.
//...
try(SXSSFWorkbook workbook = new SXSSFWorkbook()){
//do something.
//no need to close.
} catch{
}
이런 방식으로 try문을 생성하게 되면, 해당 자원을 사용하고 시스템이 자동으로 close()메소드를 호출 해줌으로써 개발자가 더 이상 누락 없이 누락했는지 고민 할 필요도 없고, error 트랙킹도 훨씬 쉬워졌으며 가독성도 훨씬 증가하게 되었다.
Reactive 환경에서의 문제점
try-catch-fianlly 보다 try-with-resources가 훨씬 좋다는 것은 이미 잘 알려져 있다. 그런데, reactive 환경에서 try-with-resources를 사용하다보면 문제점이 생긴다.
auto-close는 코드가 try문의 끝부분에 도달했을 때, 자동적으로 close()를 호출하는 방식으로 작동된다.
하지만, reactive환경에서 각종 비동기 작업들이 체이닝으로 돌아가게 된다.
이런 경우 아직 해당 resources를 사용해야 함에도 불구하고, try-with-resources가 close()를 호출해버리는 문제가 생긴다.
내가 직면했던 문제도 바로 이것이었다.
2. 해결방법
이러한 문제점 때문에, Reactor에서는 usingWhen 이라는 연산자를 제공해주고 있다. (Reactor 공식사이트, 스택오버플로우 참고)
public static <T,D> Mono<T> usingWhen(Publisher<D> resourceSupplier,
Function<? super D,? extends Mono<? extends T>> resourceClosure,
Function<? super D,? extends Publisher<?>> asyncCleanup)
어떤 인자들을 받는지 살펴보면 아래와 같다.
1. resourceSupplier: Publisher를 통해 생성되는 사용할 Resource이다.
예를 들어, Mono.fromCallable(() -> new SXSSFWorkbook)가 될 수 있을 것이다.
2. resourceClosure: resourceSupplier으로부터 공급된 resource를 이용하는 factory, 함수라고 생각하면 편하다.
리소스를 이용한 필요한 태스크들을 여기서 처리한다.
3. asyncCleanup: resource가 끝날때 실행되는 함수이다.(onComplete, onError, onCancle 모두 작동한다.)
여기에서 resource.close()를 호출해주면 된다.
'java,springboot' 카테고리의 다른 글
[성능개선]KMP알고리즘 활용하여 50% 성능개선 해보기 (1) | 2024.03.11 |
---|---|
[Springboot] Reactive Redis 총 정리(config, generic, test) (1) | 2024.02.06 |
브라우저에서 RTSP프로토콜 스트리밍 하기 (1) | 2023.10.30 |
[R2DBC] Batch Insert 성능 테스트 및 개선 (17배 향상) (0) | 2023.09.05 |
[Solved] Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor 에러 해결 (0) | 2023.06.13 |