BeanCreationException 예외로 알아보는 빈 생명주기
실행 환경: Java 17, Spring Boot 3.1.4
스프링 부트로 커맨드 라인 애플리케이션을 만들던 중, csv 관련 에러 테스트 중 예외 처리가 의도하지 않은 방향으로 흘러가는 것을 발견했다.
코드 구현
우선 아래는 애플리케이션을 실행하고 유지하는 CommandLineRunner 인터페이스를 구현한 CommandLineExecutor 클래스이며,
애플리케이션 실행 및 정책은 다음과 같이 설정하였다.
RuntimeException발생 시: warning 로그를 남기고 실행 상태 유지Exception발생 시: error 로그를 남기고 실행 종료
@Slf4j
@Component
@RequiredArgsConstructor
public class CommandLineExecutor implements CommandLineRunner {
private final ConsoleIOHandler consoleIOHandler;
private final FunctionHandler functionHandler;
private boolean isRunning = true;
@Override
public void run(String... args) {
while (isRunning) {
progress();
}
}
private void progress() {
try {
consoleIOHandler.printMenuTitle(ConsoleConstants.VOUCHER_PROGRAM_START_MESSAGE);
consoleIOHandler.printEnumString(Function.class);
String command = consoleIOHandler.getInputWithPrint();
Function.fromString(command)
.ifPresentOrElse(
function -> function.execute(functionHandler),
() -> {
throw InputException.of(InputErrorMessage.INVALID_COMMAND);
});
} catch (RuntimeException e) {
log.warn(e.getMessage());
} catch (Exception e) {
isRunning = false;
log.error(Arrays.toString(e.getStackTrace()));
}
}
}다음으로는 csv 파일을 읽고 쓰는 로직인 CsvFileHandler 클래스이며, 호출 시점 및 에러 처리는 아래와 같이 구현하였다.
에러 핸들링 테스트
애플리케이션 실행 중 에러
우선 애플리케이션 실행 중에 파일 경로에 파일 명을 수정하여 존재하지 않는 파일을 읽도록 하여 IOException이 발생하도록 했다.
의도한 대로 사용자 정의 에러가 발생하고 CommandLineExecutor에서 예외를 처리하여 정의한 메시지가 warning 로그로 남은 뒤 애플리케이션이 계속 유지됐다.
애플리케이션 초기화 중 에러
이번에는 애플리케이션 시작 전 파일명을 잘못 입력하여 애플리케이션 초기화 중에 IOException이 발생하도록 했다.
이번에는 CommandLineExecutor에서 예외를 처리하지 못하고 애플리케이션이 바로 종료되었고, 아래 로그가 출력되었다.
로그를 살펴보면 직접 정의한 FileException은 가장 마지막 라인에 존재하고, 그 위엔 BeanCreationException가 존재하여 빈 생성 중 발생한 예외로 추측 할 수 있다.
빈 관련 에러라는 것을 확인했으니 빈 생명주기를 살펴보자.
스프링 컨테이너 생성
스프링 빈 생성
의존 관계 주입
초기화 콜백
사용(실제 애플리케이션(빈) 동작 단계)
소멸 전 콜백
스프링 종료
여기서 파일을 읽어오는 단계는 @PostConstruct는 4번 초기화 콜백에 해당하며, 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출된다.
하지만 CommandLineExecutor가 동작하는 순간은 5번 사용 단계에 해당하기 때문에, 애초에 해당 에러를 처리하지 못하는 것이다.
그 흐름을 자세히 살펴보면 아래와 같다.
애플리케이션 시작 전 파일명 잘못 입력
빈 초기화 중
@PostConstruct애노테이션을 통해CsvCustomerRepository의init()메서드 호출init()메서드에서CsvFileHandler의readListFromCsv()메서드 호출CsvFileHandler내부에서IOException발생FileException으로 변환하여 throw@PostContruct애노테이션에서 발생한 빈 초기화 중 발생한 예외이기 때문에BeanCreationException으로 감싸져서 throw애플리케이션 초기화 중 발생했기 때문에
CommandLineExecutor이 동작하기 전에 예외가 발생
결론
사실 어찌보면 너무나 당연한 지식을 기반한 내용이지만, Spring의 여러 기능을 사용하게 되면서 생각하지 못한(의도하지 않은) 경로로 예외가 흘러가는 것을 확인할 수 있었다.
다시 한 번 빈 생명주기에 대해 공부할 수 있었고, 그 흐름을 이해하는 것이 중요하다는 것을 깨달았다.
만약 BeanCreationException이 발생하면, 빈이 생성되는 과정에서 문제가 있는 것이므로 빈 생명주기를 생각하면서 디버깅을 해보자.
참고
Last updated
Was this helpful?