[Mlog] 조회 시 LazyInitialization 오류 해결

노의빈·2023년 11월 15일
0

Mlog

목록 보기
12/13
post-thumbnail

📌 현재 상황

현재 Mlog의 서비스 코드 중 아래와 같이 작성된 코드가 존재한다.

    /**
     * 포스트 목록
     * */
    @Transactional(readOnly = true)
    public List<PostDto.ListDto> getPostList() {
        return postRepository.findAllByVisibleIsTrueOrderByIdDesc()
                .stream().map(Post::toListDto)
                .toList();
    }

처음에 이 코드를 작성할 때 @Transactional(readOnly = true) 어노테이션을 작성하지 않았다.
하지만 Post와 연관 관계인 PostSeries를 Lazy Loading을 하려는데 이미 세션이 사라져 LazyInitialization 오류가 발생하였다.

🤔 어떻게 해결하지?

OSIV 설정은 false로 되어있었고, 해결 방법은 아래 세가지가 있었다.

  1. 지연 로딩이 아닌 즉시 로딩으로 바꾸기
  2. 조회 쿼리에 @Transactional(readOnly = true) 어노테이션을 붙여 읽기 전용 트랜잭션을 설정하는 것
  3. OSIV 설정을 true로 하여 영속성 컨텍스트를 View 까지 늘려주는 것

이 코드를 작성할 때 나는 2번 방법을 선택하였다. 이유는 알고 있던 방법이 2번 뿐이라 그랬었다.

하지만, 코드를 하나하나 이유를 따져가며 다시 확인해보니 이 코드를 왜 이렇게 작성하였을까에 대한 의문이 생겼다.
왜? 라는 질문을 던졌을 때 대답할 수 없었기 때문에 이 방법이 좋은 방법인지에 대한 근거도, 확신도 없었다.

😎 하나하나 알아보자

지연 로딩 -> 즉시 로딩

지연 로딩을 즉시 로딩을 하게 되면, 어차피 날릴 쿼리를 바로 날리게 된다.
생각해보면 1번 방법과 2번 방법의 차이가 크게 없다고 생각한다.
결론적으로 두 방법 모두 아래처럼 쿼리를 발생시키기 때문이다.

Hibernate: # 1개
    select
        p1_0.id,
        p1_0.content,
        p1_0.post_series,
        p1_0.preview_content,
        p1_0.thumbnail,
        p1_0.title,
        p1_0.visible,
        p1_0.writing_time 
    from
        post p1_0 
    where
        p1_0.visible 
    order by
        p1_0.id desc
Hibernate: # 2개
    select
        p1_0.id,
        p1_0.series 
    from
        post_series p1_0 
    where
        p1_0.id=?
Hibernate: # 3개
    select
        p1_0.id,
        p1_0.series 
    from
        post_series p1_0 
    where
        p1_0.id=?
Hibernate: # 4개
    select
        p1_0.id,
        p1_0.series 
    from
        post_series p1_0 
    where
        p1_0.id=?
Hibernate: # 5개
    select
        p1_0.id,
        p1_0.series 
    from
        post_series p1_0 
    where
        p1_0.id=?

@Transactional(readOnly = true) 설정

이 방법은 지연 로딩으로 인한 두번째 쿼리를 어느 시점에 날리느냐에 대한 차이만 존재한다.
Post를 조회 후 DTO로 변환하는 과정에서 PostSeries의 시리즈명을 조회하는데에서만 차이가 존재하고 즉시 로딩처럼 두개의 쿼리가 발생하게 된다.
추가적으로 조회 쿼리를 명시적으로 표시할 수 있는 이점도 존재한다.

OSIV 설정을 true로

이 방법은 개인적으로 굳이? 싶었던 방법이다.
Controller단까지 트랜잭션을 할 필요가 전혀 없다고 생각하였기 때문이다.

✅ 결론

결론적으로 세가지 방법 중 가독성을 높여줄 수 있는 2번 방법이 괜찮다고 생각하였다.

하지만 진짜 문제는 따로 있었다.
바로 N + 1 문제다.
N + 1 문제는 추후에 다시 다뤄보도록 하겠다.

profile
백엔드 공부 중입니다.

0개의 댓글