LazyInitializationException 과 SecurityContextHolder

시은·2024년 2월 18일
0
post-thumbnail

LazyInitializationException

현재 로그인한 유저의 유저 객체를 getLoginUser() 로 꺼내서 쓰려고 했는데…

public class AuthUtil {
    private final TokenUtil tokenUtil;
    private final UserRepository userRepository;

    public Long getLoginUserIndex() {
        User user = getLoginUser();
        Long userId = user.getId();
        return userId;
    }

    public User getLoginUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        return (User) principal;
    }
}

LazyInitializationException 이 발생 했습니다. 그래서 아래와 같이 새로운 코드를 작성해주니 해결 되었는데요… 🤔 도대체 왜 그랬던 걸까요?

public User getLoginUserForLazy() {
    Long id = getLoginUserIndex();
    User user = userRepository.findById(id).get();
    return user;
}

Hibernate 에서의 지연 로딩과 세션

  • 지연 로딩은 엔티티 또는 엔티티 컬렉션이 데이터베이스에서 즉시 로드 되지 않고 처음 요청 시 로드 되는 전략입니다.
  • 세션은 본질적으로 엔티티가 저장되고 관리되는 컨텍스트 또는 버퍼입니다. 세션이 닫힌 후에 해당 세션 내의 초기화(로드)되지 않은 엔티티의 지연 로드 속성에 접근 하려고 하면 LazyInitializationException 이 발생합니다.

지연로딩과 세션에 대해 기본적인 사항을 알았다면 다시 로직을 찬찬히 살펴보겠습니다.

🔎 현재 로그인한 유저의 정보를 컨텍스트 홀더에서 꺼냄 → 이를 서비스 로직에서 사용하려 함 → LazyInitializationException 발생

대략 이런 플로우 인데요. 왜 그랬을까요?

세션의 범위에 대해 파악하기

해답은 이곳에 있습니다. 세션은 요청의 시작에서 열리며, 요청이 끝날 때 닫힙니다. 서비스 로직 에서 getLoginUser() 메서드를 사용해 사용자 정보를 가져오지만, 이 정보는 이전 Hibernate 세션 에서 로드 된 것입니다. 이후 Hibernate 세션은 종료되었기 때문에 연관된 엔티티에 대한 지연 로딩을 시도할 때 LazyInitializationException 이 발생합니다. 즉 가져온 객체를 사용하여 연관된 데이터를 가져오려 할 때 세션이 이미 닫혀 있기 때문에 필요한 데이터를 가져올 수 없습니다. 이를 위해서는 해당 객체를 다시 초기화 해줘야 합니다.

public User getLoginUserForLazy() {
    Long id = getLoginUserIndex();
    User user = userRepository.findById(id).get();
    return user;
}

위와 같은 메서드를 사용한다면 userRepository.findById(id).get()을 사용하여 데이터베이스에서 User 엔터티를 다시 가져옵니다. 때문에 LazyInitializationException 문제를 회피할 수 있습니다. 이로 인해 지연 로드된 속성에 접근이 가능합니다.

profile
창의력 대장이 되기 위한 여정

0개의 댓글