could not initialize proxy - no session 이슈 해결

qpwoeiru·2025년 1월 19일
1
post-thumbnail

우리 서비스에서 매일 정각마다 수행하는 스케줄러가 있는데, 아래같은 에러가 발생했다.

20250119T000002 scheduling-1 ERROR o.s.s.s.TaskUtils$LoggingErrorHandler Unexpected error occurred in scheduled task
org.hibernate.LazyInitializationException: could not initialize proxy [com.gamzabat.algohub.feature.user.domain.User#8] - no Session
...

원인

로그를 보면 scheduled task에서 error가 발생했음을 알 수 있다. 그 중, User 엔티티의 proxy를 initialize 할 수 없다는 내용이다.

LazyInitializationException

기본적으로 지연로딩은 영속성 컨텍스트에서 객체를 접근할 수 있어야 한다는 조건이 있다. LazyInitializationException 은 지연 로딩으로 엔티티를 조회할 때, 해당 엔티티가 포함된 영속성 컨텍스트가 닫혀있거나 접근할 수 없는 상태일 경우 발생한다.

현재 우리 프로젝트에서는 OSIV 설정을 안 했으므로 스프링의 기본 영속성 컨텍스트 범위를 따른다. 즉, 영속성 컨텍스트가 유지되는 범위는 @Transactional 적용 범위가 된다.


Exception 발생 flow

기존에 있던 스케줄러 메소드는 @Transactional이 없었다.

@Scheduled(cron = "0 0 0 * * ?", zone = "Asia/Seoul")
public void dailyProblemScheduler() {
    ...
    notifyProblemStartsToday(now);
    ...
}
    
private void notifyProblemStartsToday(LocalDate now) {
    List<Problem> problems = problemRepository.findAllByStartDate(now);
    for (Problem problem : problems) {
        notificationService.sendNotificationToMembers(
            ...
            groupMemberRepository.findAllByStudyGroup(problem.getStudyGroup()),
            ...
        );
    }
}
...

즉, 위 코드에서problemsgroupMemberRepository.findAllByStudyGroup() 으로 찾은 데이터들은 영속성 컨텍스트에 포함되지 않는 것이다.

이 상태로 sendNotificationToMembers() 메소드를 호출하면서, 파라미터로는 영속성 컨텍스트에 포함 안 된 members를 넘겨준다. 그래서 sendNotificationToMembers() 메소드의 로직 중, 아래 코드에서 에러가 발생한다.

if (setting.isAllNotifications() && isSettingOn(setting, category))
    users.add(member.getUser().getEmail());

member.getUser().getEmail() 로 User 데이터를 지연 조회 하려는데, member 객체가 현재 영속성 컨텍스트에 포함되지 않으니 LazyInitializationException 가 발생하는 것이다.


해결 방법

LazyInitializationException를 해결할 수 있는 방법으로는 OSIV를 사용해 영속성 컨텍스트 범위를 열어주거나, 지연로딩 대신 EAGER를 사용하는 방법도 있었다. 하지만 이번 케이스에서는 영속성 컨텍스트의 범위가 문제 되는 것이었다.

그래서 기존에 영속성 컨텍스트에 포함되지 않던 members가 포함될 수 있게 수정하고자 했다. 그 방법으로 스케줄러 메소드 자체에 @Trnasactional을 붙여서 영속성 컨텍스트에 포함되도록 만들어 해결했다.

@Transactional
@Scheduled(cron = "0 0 0 * * ?", zone = "Asia/Seoul")
public void dailyProblemScheduler() {
   ...
}

참고
https://strong-park.tistory.com/entry/orghibernatelazyinitializationexception-could-not-initialize-proxy-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0
https://stackoverflow.com/questions/345705/hibernate-lazyinitializationexception-could-not-initialize-proxy

0개의 댓글

관련 채용 정보