Spring boot : v2.5.5
Spring Security를 이용한 JWT 인증 로직의 Filter
에서 데이터베이스에 접근하여 유저의 정보를 가져오는 로직이 있는데, 유저 정보를 가져오면서 에러가 발생하였고, 이로 인해 이후 로직에서 사용자의 정보를 이용하지 못하여 에러가 발생하였다.
// Member.java
@Entity
public class Member implements UserDetails {
// ...
@Enumerated(EnumType.SPRING)
@ElementCollection
private List<Role> role = Collections.sigletonList(Role.USER);
// ...
}
문제가 발생한 부분의 코드는 아래와 같다.
// ...
Member tMember = memberService.findMemberById(memberId); // <- 문제가 발생한 부분
// ...
Spring Data Jpa에서 @ElementCollection
은 기본적으로 fetch 전략이 Lazy이다. 즉, Entity Manager에 의해 실제로 사용할 때 로딩이 되어 작동한다는 뜻이다. 하지만, 필요한 시점이 로딩이 되지 않으면서 계속해서 오류가 발생하였다.
VO인 유저의 기본 정보는 가져오지만, Enum클래스로 관리하는 사용자의 권한리스트를 불러오지 못하는 상황에 의해서 에러가 발생하였다. @ElementCollection
의 기본 fetch 전략인 지연 로딩 전략의 문제라 생각하고 문제를 해결하고자 하였다.
Fetch 전략을 EAGER로 바꾸어 즉시 로딩도 해보고, @Transactional
어노테이션을 이용하여 메서드가 종료되기 전까지 Entity Manager가 계속해서 관리하도록 해보았다.
위에서 시도한 두가지 방법 모두 오류가 발생하였다. 짧은 생각으로는, 같은 로직에서 Redis에 접근해 데이터를 조회하는 부분이 있었는데 Redis에 접근하는 코드와 또 다른 DB에 접근하는 코드가 한개의 Transaction에서 발생하여 그런거 같다는 의심만 갖고있다.
그래도 원인은 지연 로딩이 문제라는것을 인지하고 있었기 때문에 지연 로딩을 해결할 수 있는 여러가지 방법을 사용했다.
여러 방법 중 해결한 방법은 @EntityGraph
를 이용한 즉시 로딩이였다. JPA에서 기본 제공하는 findById()
메서드를 오버라이딩 하여 @EntityGraph
어노테이션을 적용, 즉시 로딩 하도록 하였다.
public interface MemberRepository extends JpaRepository<Member, Long> {
// 지연로딩에서 즉시로딩으로 바꾸고 싶은 필드의 이름
@EntityGraph(attributePaths = {"role"})
@Override
Optional<Member> findById(Long id);
}