• ABOUT
  • POSTS
  • GUESTBOOK

ยฉ 2025 BlueCool12 All rights reserved.

2025.08.30ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

๐Ÿž JPA N+1 ๋ฌธ์ œ - Fetch Join & EntityGraph

[๋ฌธ์ œ ์š”์•ฝ] 

  • ์ฆ์ƒ: ๊ธ€ ๋ชฉ๋ก ํŽ˜์ด์ง€์—์„œ ๊ธ€ ์กฐํšŒ ์‹œ Category๋ฅผ ๊ฑด๋ณ„๋กœ ์ถ”๊ฐ€ ์กฐํšŒ (N+1 ๋ฌธ์ œ ๋ฐœ์ƒ) 
  • ์›์ธ: JPA ์ฟผ๋ฆฌ์—์„œ fetch join์„ ๋ช…์‹œํ•˜์ง€ ์•Š์•„ ๋ฐœ์ƒ 
  • ํ•ด๊ฒฐ: @EntityGraph(attributePaths = โ€œcategoryโ€)๋กœ ํ•œ ๋ฒˆ์— ์กฐํšŒ 

 



๊ธฐ์กด์—๋Š” ๊ธ€ ๋ชฉ๋ก์„ ์กฐํšŒ ์‹œ JpaRepository์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์˜€๋‹ค. 
 

java
@Query("""
      SELECT p FROM Post p
      WHERE p.status = :status
      ORDER BY p.createdAt DESC, p.id DESC
      """)
Slice<Post> findPublishedPosts(@Param("status") PostStatus status, Pageable pageable);


์‹ค์ œ ์‹คํ–‰๋œ ์ฟผ๋ฆฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. 
 

plaintext
Hibernate:
  select
    p1_0.id,
    p1_0.category_id,
    p1_0.content,
    -- ๋“ฑ๋“ฑ ํ•„๋“œ๋“ค
  from
    post p1_0
  where
    p1_0.status=?
  order by
    p1_0.created_at desc,
    p1_0.id desc
  fetch
    first ? rows only
Hibernate:
  select
    c1_0.id,
    c1_0.created_at,
    -- ๋“ฑ๋“ฑ ํ•„๋“œ๋“ค
  from
    category c1_0
  where
    c1_0.id=?
Hibernate:
  select
    c1_0.id,
    c1_0.created_at,
    -- ๋“ฑ๋“ฑ ํ•„๋“œ๋“ค
  from
    category c1_0
  where
    c1_0.id=?
-- ์ดํ•˜ ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐœ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต


JPA @ManyToOne์˜ ๊ธฐ๋ณธ fetch ์ „๋žต์€ EAGER์ด์ง€๋งŒ Hibernate๊ฐ€ SELECT๋กœ ๊ฐ€์ ธ์™€์„œ N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์œผ๋กœ ๋ณด์˜€๋‹ค. 
 

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 1) JPQL fetch join

์ฟผ๋ฆฌ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์กฐ์ธ ๋กœ๋”ฉ์„ ์ง€์‹œํ•œ๋‹ค. 
 

java
@Query("""
      SELECT p FROM Post p
      LEFT JOIN FETCH p.category
      WHERE p.status = :status
      ORDER BY p.createdAt DESC, p.id DESC
      """)
Slice<Post> findPublishedPosts(@Param("status") PostStatus status, Pageable pageable);

* category๊ฐ€ nullable์ด๊ธฐ ๋•Œ๋ฌธ์— LEFT JOIN์„ ์„ ํƒํ•˜์˜€๋‹ค. 
 

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 2) EntityGraph

์ฟผ๋ฆฌ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  @EntityGraph๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. 
 

java
@EntityGraph(attributePaths = { "category" })
@Query("""
      SELECT p FROM Post p
      WHERE p.status = :status
      ORDER BY p.createdAt DESC, p.id DESC
      """)
Slice<Post> findPublishedPosts(@Param("status") PostStatus status, Pageable pageable);


์œ„ ๋‘ ๊ฐœ์˜ ๋ฐฉ๋ฒ• ๋ชจ๋‘ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜์—ˆ๋‹ค. 
 

plaintext
Hibernate:
  select
    p1_0.id,
    c1_0.id,
    c1_0.created_at,
    -- ๋“ฑ๋“ฑ ํ•„๋“œ๋“ค
  from
    post p1_0
  left join
    category c1_0
      on c1_0.id=p1_0.category_id
  where
    p1_0.status=?
  order by
    p1_0.created_at desc,
    p1_0.id desc


fetch join์„ ๋ช…์‹œ์ ์œผ๋กœ @Query ์•ˆ์— ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ ์กฐ์ธ ํƒ€์ž…๊ณผ ์กฐ๊ฑด์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์žฅ์ ์ด์ง€๋งŒ ํ˜„์‹œ์ ์—์„œ๋Š” ๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ์ฟผ๋ฆฌ๊ฐ€ ๋ณต์žกํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋…์„ฑ์ด ์ข‹์€ @EntityGraph๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜์˜€๋‹ค. 
 

์ด์ „ ๊ธ€
๐Ÿ”ฃ ๋ฉ”ํƒ€๋ฌธ์ž ์ดํ•ด๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ •๊ทœํ‘œํ˜„์‹
๋‹ค์Œ ๊ธ€
๐ŸŽฎ Controlled์™€ Uncontrolled - ๋ฆฌ์•กํŠธ ํผ ์ปดํฌ๋„ŒํŠธ ์ดํ•ดํ•˜๊ธฐ
์žฅ์‹์šฉ ๋กœ๊ณ