현재 로그인한 유저의 유저 객체를 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;
}
지연로딩과 세션에 대해 기본적인 사항을 알았다면 다시 로직을 찬찬히 살펴보겠습니다.
🔎 현재 로그인한 유저의 정보를 컨텍스트 홀더에서 꺼냄 → 이를 서비스 로직에서 사용하려 함 → 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 문제를 회피할 수 있습니다. 이로 인해 지연 로드된 속성에 접근이 가능합니다.