๐ŸŒฑSpring Boot ์‹œ์ž‘ํ•˜๊ธฐ 19 - ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ํŠธ๋žœ์žญ์…˜, JPA ๋™์ž‘ ์›๋ฆฌ

๊น€์„ธํ˜„ยท2025๋…„ 10์›” 23์ผ

Spring

๋ชฉ๋ก ๋ณด๊ธฐ
19/19

๋‚ ์งœ:2025-10-23


ํ•™์Šต ๋ชฉํ‘œ

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๊ฐœ๋… ์ดํ•ด
  • ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ ํŒŒ์•…
  • @Transactional์˜ ์—ญํ• ๊ณผ ๊ธฐ๋ณธ ๊ฐœ๋…
  • JpaRepository์˜ CRUD ๋ฉ”์„œ๋“œ ์ดํ•ด
  • ๋”ํ‹ฐ์ฒดํ‚น(Dirty Checking) ๊ฐœ๋…
    -์ปค์Šคํ…€ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ์ดํ•ด

โœ… ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ (Persistence Context)

์ •์˜

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๋Š” ํ™˜๊ฒฝ์œผ๋กœ,

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์ด์—์„œ ๊ฐ์ฒด๋ฅผ ๋ณด๊ด€ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ€์ƒ์˜ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค.

  • ์‰ฝ๊ฒŒ ๋งํ•ด,
    โ€œJPA๊ฐ€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ž„์‹œ ์ €์žฅ์†Œโ€
    ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.


์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ์ฃผ์š” ์—ญํ• 

์—ญํ• ์„ค๋ช…
์—”ํ‹ฐํ‹ฐ ๋ณด๊ด€๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ€์ ธ์˜จ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ
๋ณ€๊ฒฝ ๊ฐ์ง€ (๋”ํ‹ฐ์ฒดํ‚น)์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ ์—ฌ๋ถ€๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์ž๋™์œผ๋กœ UPDATE ์ฟผ๋ฆฌ ์‹คํ–‰
๋™๊ธฐํ™” (Flush)ํŠธ๋žœ์žญ์…˜์ด ๋๋‚  ๋•Œ DB์™€ ์ƒํƒœ๋ฅผ ์ผ์น˜์‹œํ‚ด
  • ์ฆ‰, JPA๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ถ”์ ํ•˜๊ณ ,
    ๊ฐœ๋ฐœ์ž๊ฐ€ ๋”ฐ๋กœ UPDATE ๋ฌธ์„ ์“ฐ์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.

โœ… ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ

์ •์˜

  • ์—”ํ‹ฐํ‹ฐ๋Š” ์ƒ์„ฑ๋ถ€ํ„ฐ ์‚ญ์ œ๊นŒ์ง€ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๊ฒช์Šต๋‹ˆ๋‹ค.
  • ์ž…๋ฌธ ๋‹จ๊ณ„์—์„œ๋Š” ๋น„์˜์† โ†’ ์˜์† ๋‘ ๊ฐ€์ง€ ์ƒํƒœ๋งŒ ์ดํ•ดํ•˜๋ฉด ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

๋น„์˜์† (Transient)

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์•„๋ฌด๋Ÿฐ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒํƒœ
  • DB์— ์ €์žฅ๋˜์ง€ ์•Š์€ ๊ฐ์ฒด

์˜ˆ:

  • new ํ‚ค์›Œ๋“œ๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค์—ˆ์ง€๋งŒ ์•„์ง save()ํ•˜์ง€ ์•Š์€ ์ƒํƒœ

์˜์† (Persistent)

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๋Š” ์ƒํƒœ
  • JPA๊ฐ€ ํ•ด๋‹น ๊ฐ์ฒด์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ , DB์™€ ๋™๊ธฐํ™”ํ•จ

์˜ˆ์‹œ

  • save()๋ฅผ ํ†ตํ•ด DB์— ๋ฐ˜์˜๋˜์—ˆ๊ฑฐ๋‚˜, findById()๋กœ ์กฐํšŒ๋œ ์ƒํƒœ

โœ… @Transactional ๊ธฐ๋ณธ ๊ฐœ๋…

ํŠธ๋žœ์žญ์…˜์ด๋ž€?

  • ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ ์ž‘์—…์„ ํ•˜๋‚˜๋กœ ๋ฌถ์–ด ๋ชจ๋‘ ์„ฑ๊ณตํ•˜๊ฑฐ๋‚˜ ๋ชจ๋‘ ์‹คํŒจํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.
  • ์ด ์„ฑ์งˆ์„ ์›์ž์„ฑ(Atomicity) ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ํŠธ๋žœ์žญ์…˜์˜ ๊ด€๊ณ„

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„ ์•ˆ์—์„œ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

  • ์ฆ‰, ํŠธ๋žœ์žญ์…˜์ด ์žˆ์–ด์•ผ JPA๊ฐ€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ถ”์ ํ•˜๊ณ  ๋ณ€๊ฒฝ์„ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • โ€œํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ DB๋ฅผ ํ•œ ๋ฒˆ์ด๋ผ๋„ ๊ฐ”๋‹ค ์˜จ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹คโ€
    = ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ํ•ต์‹ฌ ์ •์˜


@Transactional ์–ด๋…ธํ…Œ์ด์…˜**

Spring์—์„œ๋Š” @Transactional์„ ์‚ฌ์šฉํ•ด
๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„๋กœ ๋ฌถ์Šต๋‹ˆ๋‹ค.

์ฃผ์š” ์†์„ฑ ์š”์•ฝ

์†์„ฑ์„ค๋ช…
readOnly์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜ (CUD ๋ถˆ๊ฐ€, ์„ฑ๋Šฅ ์ตœ์ ํ™”)
propagationํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ ๊ทœ์น™ ์„ค์ •
isolationํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€ ์„ค์ •
rollbackForํŠน์ • ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๊ฐ•์ œ ๋กค๋ฐฑ ์ง€์ •
  • ์ž…๋ฌธ ๋‹จ๊ณ„์—์„œ๋Š” readOnly = true๋งŒ ์•Œ์•„๋‘๋ฉด ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
  • ์กฐํšŒ ์ „์šฉ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ถˆํ•„์š”ํ•œ ์“ฐ๊ธฐ ์ž‘์—…์„ ์ค„์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

โœ… JpaRepository์™€ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ

  • Spring Data JPA์˜ ํ•ต์‹ฌ ์ธํ„ฐํŽ˜์ด์Šค์ธ JpaRepository๋Š” SQL์„ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ CRUD ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ CRUD ๋ฉ”์„œ๋“œ

๋™์ž‘๋ฉ”์„œ๋“œ์„ค๋ช…
Createsave(entity)์—”ํ‹ฐํ‹ฐ ์ €์žฅ
ReadfindById(id), findAll()๋ฐ์ดํ„ฐ ์กฐํšŒ
Update(ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์—”ํ‹ฐํ‹ฐ ๋ณ€๊ฒฝ)์ž๋™์œผ๋กœ UPDATE (๋”ํ‹ฐ์ฒดํ‚น)
Deletedelete(entity), deleteById(id)๋ฐ์ดํ„ฐ ์‚ญ์ œ
  • ์ด๋Ÿฌํ•œ ๊ธฐ๋ณธ ์ œ๊ณต ๋ฉ”์„œ๋“œ๋ฅผ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ(Query Method) ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

โœ… ๋”ํ‹ฐ์ฒดํ‚น (Dirty Checking)

์ •์˜

  • ๋”ํ‹ฐ์ฒดํ‚น์ด๋ž€, ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌ ์ค‘์ธ ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ฐ์ง€ํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜์ด ๋๋‚  ๋•Œ ์ž๋™์œผ๋กœ UPDATE ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

๋™์ž‘ ์›๋ฆฌ

1.ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋˜๋ฉด JPA๊ฐ€ ์—”ํ‹ฐํ‹ฐ์˜ ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์Šค๋ƒ…์ƒท์œผ๋กœ ์ €์žฅ
2.์—”ํ‹ฐํ‹ฐ์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์Šค๋ƒ…์ƒท๊ณผ ๋น„๊ต
3.๋ณ€๊ฒฝ์ด ๊ฐ์ง€๋˜๋ฉด ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ ์‹œ์ ์— ์ž๋™์œผ๋กœ UPDATE ์‹คํ–‰

  • ์ฆ‰, save()๋‚˜ update()๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„
    ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ํ•„๋“œ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ์ž๋™์œผ๋กœ DB์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

โœ… ์ปค์Šคํ…€ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ

  • ๊ธฐ๋ณธ CRUD ์™ธ์—๋„, ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ์œผ๋กœ ์กฐ๊ฑด ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ

๊ธฐ๋Šฅ๋ฉ”์„œ๋“œ ์˜ˆ์‹œ
์ด๋ฆ„์œผ๋กœ ์ฐพ๊ธฐfindByName(String name)
์ด๋ฉ”์ผ๊ณผ ๋‚˜์ด๋กœ ์ฐพ๊ธฐfindByEmailAndAge(String email, int age)
ํŠน์ • ์กฐ๊ฑด ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธexistsByEmail(String email)
๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜ ์„ธ๊ธฐcountByAgeGreaterThan(int age)
  • ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ๊ธฐ๋ฐ˜์œผ๋กœ Spring Data JPA๊ฐ€ ์ž๋™์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด @Query ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์ง์ ‘ ์ž‘์„ฑํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ •๋ฆฌ

๊ฐœ๋…ํ•ต์‹ฌ ํฌ์ธํŠธ
์˜์†์„ฑ ์ปจํ…์ŠคํŠธJPA๊ฐ€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ž„์‹œ ์ €์žฅ์†Œ
์—”ํ‹ฐํ‹ฐ ์ƒ๋ช…์ฃผ๊ธฐ๋น„์˜์†(๊ด€๋ฆฌ X) โ†” ์˜์†(๊ด€๋ฆฌ O)
ํŠธ๋žœ์žญ์…˜์—ฌ๋Ÿฌ ์ž‘์—…์„ ํ•˜๋‚˜๋กœ ๋ฌถ์–ด ๋ชจ๋‘ ์„ฑ๊ณต/์‹คํŒจ์‹œํ‚ค๋Š” ๋‹จ์œ„
@Transactional๋ฉ”์„œ๋“œ ๋‹จ์œ„ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ (readOnly ์˜ต์…˜์œผ๋กœ ์ตœ์ ํ™” ๊ฐ€๋Šฅ)
JpaRepositorySQL ์—†์ด CRUD ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ
๋”ํ‹ฐ์ฒดํ‚น์—”ํ‹ฐํ‹ฐ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ UPDATE
์ปค์Šคํ…€ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฉ”์„œ๋“œ ์ด๋ฆ„์œผ๋กœ ์กฐ๊ฑด ๊ฒ€์ƒ‰ ์ž๋™ ์ฟผ๋ฆฌ ์ƒ์„ฑ
profile
๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ํ•œ ๊ฑธ์Œ

0๊ฐœ์˜ ๋Œ“๊ธ€