서버 개발

[서버 개발] @Transactional 과 LazyInitializationException

딸기토끼0623 2024. 5. 29. 20:10

0. 코드

https://github.com/Kids-of-StrawberryRabbit/NewsFeed/blob/dev-0.0.0/src/main/kotlin/com/noreabang/strawberryrabbit/domain/feed/service/FeedService.kt#L42

1. 에러 상황

피드에 딸린 댓글 목록을 가져오는 과정에서 해당 에러가 발생했다.

1-1. FeedService.kt

fun getFeedResponseById(id: Long): FeedDetailResponse {
    val feed = feedRepository.findByIdOrNull(id) ?: throw ModelNotFoundException("Feed", id)
    return feed.toDetailResponse()
}

1-2. Feed.kt (엔티티)

fun Feed.toDetailResponse(): FeedDetailResponse {
    return FeedDetailResponse(
        title = this.title,
        content = this.content,
        music = this.music!!.toResponse(),
        member = this.member!!.toResponse(),
        createAt = this.createdAt,
        feedLikes = this.feedLikes.map { it.toResponse() },
        comments = this.comments.map { it.toResponse() }
    )
}

1-3. Comment.kt (엔티티)

fun Comment.toResponse(): CommentResponse {
    return CommentResponse(
        id = id!!,
        content = content,
        createdAt = createdAt,
    )
}

2. 이유

검색해보니 다음과 같은 상황으로 정리해 볼 수 있었다.

1. Feed 정보를 조회하기 위해 JPA에서 DB 조회 세션시작한다.
2. FeedService가 종료되면 JPA에서 해당 DB 조회 세션종료한다.
3. Comment를 불러와야하기 때문에 DB 접근 시도가 발생하게 된다.
4. 살아있는 DB 조회 세션이 없어서 LazyInitializationException 발생!

3. 해결책

아래와 같이 서비스 전체를 @Transactional로 감싸주고, ReadOnly인 함수들에만 @Transactional(readOnly = true) 를 붙여준다.

@Service
@Transactional
class FeedService(

...

	@Transactional(readOnly = true)
	fun getFeedResponseById(id: Long): FeedDetailResponse {
        val feed = feedRepository.findByIdOrNull(id) ?: throw ModelNotFoundException("Feed", id)
        return feed.toDetailResponse()
    }
 
...
 
}