๊ณต๋ถ€/TIL

[20250212] JPA n+1 ๋ฌธ์ œ

dvlpsy๐ŸŒผ 2025. 2. 12. 20:07
๋ฐ˜์‘ํ˜•

SPRING๊ณผ JPA๋ฅผ ์‚ฌ์šฉํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ ์ „ ํŒ€์›๋“ค๊ณผ N+1 ๋ฌธ์ œ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐ๋ฅผ ๋‚˜๋ˆด๋‹ค.

๋“ค์–ด๋Š” ๋ดค์ง€๋งŒ ์•„์ง ๋งˆ์ฃผ์นœ์  ์—†์—ˆ๊ธฐ์— ๋ฏธ๋ฆฌ ๊ณต๋ถ€ํ•ด ๋ณด๊ณ ์ž ํ•œ๋‹ค.

์˜ค๋Š˜์€ ํ• ์ผ์ด ๋งŽ์•˜๊ธฐ์— chatGPT์—๊ฒŒ ๋ฌผ์–ด๋ด์„œ ์งง๊ณ  ๋น ๋ฅด๊ฒŒ ์ดํ•ดํ•ด๋ณธ๋‹ค.

 

JPA์—์„œ N+1 ๋ฌธ์ œ ์›์ธ๊ณผ ํ•ด๊ฒฐ๋ฐฉ๋ฒ• ์•Œ๋ ค์ค˜

 

JPA N+1 ๋ฌธ์ œ ์›์ธ ๋ฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

JPA (Java Persistence API)์—์„œ N+1 ๋ฌธ์ œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์„ฑ๋Šฅ ๋ฌธ์ œ์˜ ์ผ์ข…์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋Š” ์ฃผ๋กœ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•จ๊ป˜ ์กฐํšŒํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋ฉฐ, ์˜ˆ์ƒ๋ณด๋‹ค ๋งŽ์€ ์ˆ˜์˜ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜์–ด ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

N+1 ๋ฌธ์ œ์˜ ์›์ธ

N+1 ๋ฌธ์ œ๋Š” ์ฃผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  1. 1:N ๊ด€๊ณ„์˜ ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ: ์˜ˆ๋ฅผ ๋“ค์–ด, Author ์—”ํ‹ฐํ‹ฐ์™€ Book ์—”ํ‹ฐํ‹ฐ๊ฐ€ 1:N ๊ด€๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด…์‹œ๋‹ค. Author ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ, ๊ฐ Author์— ๋Œ€ํ•œ Book ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•จ๊ป˜ ์กฐํšŒํ•˜๋ ค๊ณ  ํ•˜๋ฉด N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์ง€์—ฐ ๋กœ๋”ฉ (Lazy Loading): JPA๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์—ฐ ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋  ๋•Œ๊นŒ์ง€ ๋กœ๋”ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ 1:N ๊ด€๊ณ„์—์„œ Author ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•œ ํ›„ ๊ฐ Author์˜ Book ์—”ํ‹ฐํ‹ฐ์— ์ ‘๊ทผํ•  ๋•Œ๋งˆ๋‹ค ์ถ”๊ฐ€์ ์ธ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

N+1 ๋ฌธ์ œ์˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

1. Fetch Join

Fetch Join์€ JPQL (Java Persistence Query Language)์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ, ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•จ๊ป˜ ์กฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. Fetch Join์„ ์‚ฌ์šฉํ•˜๋ฉด N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ํ•œ ๋ฒˆ์˜ ์ฟผ๋ฆฌ๋กœ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. @EntityGraph

@EntityGraph ์–ด๋…ธํ…Œ์ด์…˜์€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ํ•จ๊ป˜ ๋กœ๋”ฉํ•  ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. @EntityGraph๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Fetch Join๊ณผ ์œ ์‚ฌํ•œ ํšจ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. Batch Size

@BatchSize ์–ด๋…ธํ…Œ์ด์…˜์€ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋กœ๋”ฉํ•  ๊ฐœ์ˆ˜๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. @BatchSize๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด N+1 ๋ฌธ์ œ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ, ์ฟผ๋ฆฌ ์‹คํ–‰ ํšŸ์ˆ˜๋ฅผ ์ค„์—ฌ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4. CascadeType.ALL

CascadeType.ALL์€ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•œ ๋ชจ๋“  ์ž‘์—…์„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ์—๋„ ์ „ํŒŒํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. CascadeType.ALL์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ๋กœ๋”ฉ๋˜๋ฏ€๋กœ N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ CascadeType.ALL์€ ์˜ˆ์ƒ์น˜ ์•Š์€ ๋ถ€์ž‘์šฉ์„ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜ ์‚ฌํ•ญ

  • N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ƒํ™ฉ์— ๋งž๋Š” ์ ์ ˆํ•œ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Fetch Join์€ ์นดํ…Œ์‹œ์•ˆ ๊ณฑ (Cartesian Product)์„ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • @EntityGraph๋Š” JPA 2.1 ์ด์ƒ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

N+1 ๋ฌธ์ œ๋Š” JPA์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ํ”ํ•œ ์„ฑ๋Šฅ ๋ฌธ์ œ์ด์ง€๋งŒ, ์ ์ ˆํ•œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ํšจ๊ณผ์ ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Fetch Join, @EntityGraph, @BatchSize ๋“ฑ์˜ ๋ฐฉ๋ฒ•์„ ํ™œ์šฉํ•˜์—ฌ N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค์„ธ์š”.

 

์•ž์œผ๋กœ ํ”„๋กœ์ ํŠธ์—์„œ ์œ„์˜ ๋ฌธ์ œ๋ฅผ ๋งˆ์ฃผ์ณค์„๋•Œ, ์›ํ™œํžˆ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.