Skip to content

[๐ŸŒฑ JPA ๋ฐฉํƒˆ์ถœ ์˜ˆ์•ฝ ๋Œ€๊ธฐ] ๋ฐ€๋ž€(์กฐ๊ฐ•๋ž˜) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค.#567

Open
kangrae-jo wants to merge 7 commits into
woowacourse:kangrae-jofrom
kangrae-jo:step3
Open

[๐ŸŒฑ JPA ๋ฐฉํƒˆ์ถœ ์˜ˆ์•ฝ ๋Œ€๊ธฐ] ๋ฐ€๋ž€(์กฐ๊ฐ•๋ž˜) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค.#567
kangrae-jo wants to merge 7 commits into
woowacourse:kangrae-jofrom
kangrae-jo:step3

Conversation

@kangrae-jo

@kangrae-jo kangrae-jo commented Jun 18, 2026

Copy link
Copy Markdown

์ฒดํฌ ๋ฆฌ์ŠคํŠธ

  • ๋ฏธ์…˜์˜ ํ•„์ˆ˜ ์š”๊ตฌ์‚ฌํ•ญ์„ ๋ชจ๋‘ ๊ตฌํ˜„ํ–ˆ๋‚˜์š”?
  • Gradle test๋ฅผ ์‹คํ–‰ํ–ˆ์„ ๋•Œ, ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ํ†ต๊ณผํ–ˆ๋‚˜์š”?
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜๋‚˜์š”?

๋ฒ ์ด์Šค ์ฝ”๋“œ ์„ ํƒ ์ฒดํฌ

  • ์ด์ „ ๋ฏธ์…˜์˜ ๋‚ด ์ฝ”๋“œ์—์„œ ์‹œ์ž‘
  • ์ด์ „ ๋ฏธ์…˜์˜ ํŽ˜์–ด์˜ ์ฝ”๋“œ์—์„œ ์‹œ์ž‘

์ด๋ฒˆ ๋ฏธ์…˜์—์„œ ๊ฑด๋“œ๋ฆด ๋ฒ”์œ„

  • JPA ์ „ํ™˜์—์„œ ๋ฐ”๊ฟ€ ๋ถ€๋ถ„์€ ์˜์†์„ฑ ๊ณ„์ธต์ด๋‹ค. JdbcTemplate Repository, ์ง์ ‘ ์ž‘์„ฑํ•œ SQL, ResultSet ๋งคํ•‘, join ์กฐํšŒ ์ฝ”๋“œ๋ฅผ Entity ๋งคํ•‘๊ณผ Spring Data JPA Repository๋กœ ๋Œ€์ฒดํ•  ์˜ˆ์ •์ด๋‹ค.

  • ๋งŒ์•ฝ ๋ฉ”์„œ๋“œ์ฟผ๋ฆฌ๋กœ ๊ตฌํ˜„ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด JPQL ๋“ฑ์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • ์œ ์ง€ํ•  ๋ถ€๋ถ„์€ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™๊ณผ API ๋™์ž‘์ด๋‹ค. ์˜ˆ์•ฝ ์ƒ์„ฑ, ์กฐํšŒ, ์‚ญ์ œ ํ๋ฆ„๊ณผ Controller, Service์˜ ํฐ ์ฑ…์ž„ ๊ตฌ์กฐ, ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฒ€์ฆํ•˜๋Š” ๊ฒฐ๊ณผ๋Š” ์œ ์ง€ํ•œ๋‹ค.

  • ๋”ฐ๋ผ์„œ ์ด๋ฒˆ ์ „ํ™˜์˜ ๊ฒฝ๊ณ„๋Š” ๊ธฐ๋Šฅ ๋ณ€๊ฒฝ์ด ์•„๋‹ˆ๋ผ DB ์ ‘๊ทผ ๋ฐฉ์‹ ๋ณ€๊ฒฝ์ด๋‹ค.

๋‹จ๊ณ„๋ณ„ ๋„๋‹ฌ ์ง€์ 

1๋‹จ๊ณ„ - JPA ๊ธฐ๋ฐ˜ ๋งˆ๋ จ๊ณผ ๋™์ž‘ ์ดํ•ด

  • JDBC ์ €์žฅ์†Œ๋ฅผ Spring Data JPA๋กœ ์ „ํ™˜ํ–ˆ๋‹ค.
  • ๋„๋ฉ”์ธ์„ Entity์™€ Embeddable๋กœ ๋งคํ•‘ํ–ˆ๋‹ค.
  • Dirty Checking, 1์ฐจ ์บ์‹œ, ์“ฐ๊ธฐ ์ง€์—ฐ, flush, EAGER/LAZY ๋™์ž‘์„ ํ™•์ธํ–ˆ๋‹ค.
  • ๋ฉ”์„œ๋“œ ์ด๋ฆ„์œผ๋กœ ์ •๋ ฌ ์กฐ๊ฑด์„ ํ‘œํ˜„ํ–ˆ๋‹ค.

2๋‹จ๊ณ„ - ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ฟผ๋ฆฌ

  • findByReserver๋ฅผ ํ†ตํ•ด ๋‹จ์ˆœ ๋™๋“ฑ ์กฐ๊ฑด ์กฐํšŒ๋ฅผ ํŒŒ์ƒ ์ฟผ๋ฆฌ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค.
  • ๋‹จ์ˆœ ์กฐํšŒ๋Š” ๋ณ„๋„์˜ JPQL ์—†์ด ๋ฉ”์„œ๋“œ ์ด๋ฆ„์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Œ์„ ํ™•์ธํ–ˆ๋‹ค.

3๋‹จ๊ณ„ - ์กฐํšŒ ์ตœ์ ํ™”์™€ JPQL

  • ์ผ๋ฐ˜ ์กฐํšŒ์—์„œ ๋ฐœ์ƒํ•˜๋Š” N+1 ๋ฌธ์ œ๋ฅผ ํ™•์ธํ–ˆ๋‹ค.
  • fetch join๊ณผ EntityGraph๋ฅผ ์ ์šฉํ•ด ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ๋ฅผ SELECT ํ•œ ๋ฒˆ์œผ๋กœ ์กฐํšŒํ–ˆ๋‹ค.
  • ๋ฉ”์„œ๋“œ ์ด๋ฆ„์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๋Š” ๋Œ€๊ธฐ ์ˆœ๋ฒˆ ๊ณ„์‚ฐ์„ ์ƒ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ์™€ COUNT๋ฅผ ์‚ฌ์šฉํ•œ JPQL๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค.
  • JPQL ์ƒ์„ฑ์ž ํ‘œํ˜„์‹์œผ๋กœ ReservationWaitingWithOrder projection์„ ์ƒ์„ฑํ–ˆ๋‹ค.

4๋‹จ๊ณ„ - ์˜ˆ์•ฝ ๋Œ€๊ธฐ ๋กœ์ง์˜ JPA ๋™์ž‘ ํ™•์ธ

  • ์˜ˆ์•ฝ ์‚ญ์ œ ํ›„ ๋Œ€๊ธฐ์ž๋ฅผ ์Šน๊ฒฉํ•˜๋Š” ๊ณผ์ •์˜ SQL ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ํ™•์ธํ–ˆ๋‹ค.
  • ๊ธฐ์กด ์˜ˆ์•ฝ์„ flushํ•˜์ง€ ์•Š์œผ๋ฉด DELETE ์ „์— ์ƒˆ ์˜ˆ์•ฝ์˜ INSERT๊ฐ€ ์‹คํ–‰๋˜์–ด ์œ ์ผ์„ฑ ์ œ์•ฝ ์กฐ๊ฑด์„ ์œ„๋ฐ˜ํ–ˆ๋‹ค.
  • ๋ช…์‹œ์ ์œผ๋กœ flushํ•˜๋ฉด ๊ธฐ์กด ์˜ˆ์•ฝ์˜ DELETE ์ดํ›„ ์ƒˆ ์˜ˆ์•ฝ์˜ INSERT๊ฐ€ ์‹คํ–‰๋˜์–ด ์ •์ƒ์ ์œผ๋กœ ์ „ํ™˜๋๋‹ค.
  • flush๊ฐ€ ์˜ˆ์•ฝ ๋Œ€๊ธฐ ์ „ํ™˜ ๊ณผ์ •์˜ SQL ์‹คํ–‰ ์ˆœ์„œ์™€ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค๋Š” ์ ์„ ํ™•์ธํ–ˆ๋‹ค.

์ตœ์ข… ๋„๋‹ฌ ์ง€์ 

  • JPA๋กœ ์ €์žฅ์†Œ๋ฅผ ์ „ํ™˜ํ•˜๊ณ  ํŒŒ์ƒ ์ฟผ๋ฆฌ์™€ JPQL์„ ๊ตฌ๋ถ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • N+1 ๋ฌธ์ œ๋ฅผ ํ™•์ธํ•˜๊ณ  fetch join๊ณผ EntityGraph๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ flush๊ฐ€ ์‹ค์ œ ๋„๋ฉ”์ธ ๋กœ์ง์˜ ์„ฑ๋Šฅ๊ณผ ์ผ๊ด€์„ฑ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ํ…Œ์ŠคํŠธ๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ‚ค์›Œ๋“œ ์ •๋ฆฌ

๋ชฉ์  ํ‚ค์›Œ๋“œ 1์ค„ ์ •์˜ ๋ฏธ์…˜1์—์„œ ํ•ด๋‹นํ•˜๋Š” ์ง€์ 
ํŒจ๋Ÿฌ๋‹ค์ž„ ์ฐจ์ด ORM ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• DB๋ฅผ ๋งคํ•‘ํ•˜๊ณ  ๊ฐ์ฒด์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ SQL๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ธฐ์ˆ  JdbcTemplate์œผ๋กœ SQL์„ ์ง์ ‘ ์‹คํ–‰ํ•˜๋˜ ๋ฐฉ์‹์„ Entity์™€ Repository ์ค‘์‹ฌ์œผ๋กœ ์ „ํ™˜ํ•œ ๋ถ€๋ถ„
๊ฐ์ฒด-๊ด€๊ณ„ ์ž„ํ”ผ๋˜์Šค ๋ถˆ์ผ์น˜ ๊ฐ์ฒด ๋ชจ๋ธ๊ณผ ๊ด€๊ณ„ํ˜• DB ๋ชจ๋ธ์˜ ํ‘œํ˜„ ๋ฐฉ์‹์ด ๋‹ฌ๋ผ ์ƒ๊ธฐ๋Š” ์ฐจ์ด ๊ฐ์ฒด๋Š” ์ฐธ์กฐ๋กœ ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•˜์ง€๋งŒ DB๋Š” ์™ธ๋ž˜ํ‚ค์™€ JOIN์œผ๋กœ ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ถ€๋ถ„
์˜์†์„ฑ ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ DB์— ์ €์žฅํ•˜๊ณ  ์กฐํšŒํ•˜๋ฉฐ ์œ ์ง€ํ•˜๋Š” ๊ฐœ๋… ์ง์ ‘ ์ž‘์„ฑํ•˜๋˜ INSERT, UPDATE, DELETE๋ฅผ JPA๊ฐ€ ์—”ํ‹ฐํ‹ฐ ์ƒํƒœ์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„
ํ•ต์‹ฌ ๊ฐœ๋… ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ JPA๊ฐ€ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ์ €์žฅํ•œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ณต๊ฐ„ findById()๋กœ ์กฐํšŒํ•œ Reservation์ด ๋ณ€๊ฒฝ ๊ฐ์ง€์™€ 1์ฐจ ์บ์‹œ์˜ ๊ด€๋ฆฌ ๋Œ€์ƒ์ด ๋˜๋Š” ๋ถ€๋ถ„
1์ฐจ ์บ์‹œ ๊ฐ™์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ์บ์‹œ ๊ฐ™์€ ID๋ฅผ ๋‘ ๋ฒˆ ์กฐํšŒํ–ˆ์„ ๋•Œ SELECT๋Š” ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๊ณ  ๊ฐ™์€ ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋œ ๋ถ€๋ถ„
dirty checking ์˜์† ์ƒํƒœ ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•ด UPDATE SQL์„ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ save()๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ์—”ํ‹ฐํ‹ฐ ๋ณ€๊ฒฝ ํ›„ flush ์‹œ UPDATE๊ฐ€ ๋ฐœ์ƒํ•œ ๋ถ€๋ถ„
์“ฐ๊ธฐ ์ง€์—ฐ ๋ณ€๊ฒฝ SQL์„ ๋ชจ์•„๋‘์—ˆ๋‹ค๊ฐ€ flush ์‹œ์ ์— DB๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹ DELETE๊ฐ€ ์ฆ‰์‹œ ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  flush ์‹œ์ ๊นŒ์ง€ ์ง€์—ฐ๋œ ๋ถ€๋ถ„. ๋‹จ, IDENTITY INSERT๋Š” ์ฆ‰์‹œ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Œ
flush ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ์„ DB์— ๋™๊ธฐํ™”ํ•˜๋Š” ๋™์ž‘ ์˜ˆ์•ฝ ์‚ญ์ œ ํ›„ ๋ช…์‹œ์ ์œผ๋กœ flushํ•˜์—ฌ DELETE๋ฅผ ๋จผ์ € ์‹คํ–‰ํ•œ ๋’ค ์Šน๊ฒฉ ์˜ˆ์•ฝ์„ INSERTํ•œ ๋ถ€๋ถ„
FlushMode.AUTO ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋Š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ JPQL ์‹คํ–‰ ์ „ ์ž๋™์œผ๋กœ flushํ•˜๋Š” ๊ธฐ๋ณธ ๋ชจ๋“œ Reservation ๋ณ€๊ฒฝ ํ›„ ๊ด€๋ จ JPQL์„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ SELECT ์ „์— UPDATE๊ฐ€ ๋ฐœ์ƒํ•œ ๋ถ€๋ถ„
๋น„์˜์† ์•„์ง ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๋Š” ์ƒˆ ๊ฐ์ฒด ์ƒํƒœ new Reservation(...)์œผ๋กœ ์ƒ์„ฑํ–ˆ์ง€๋งŒ ์•„์ง ์ €์žฅํ•˜์ง€ ์•Š์€ ์ƒํƒœ
์˜์† ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ ์ƒํƒœ Repository๋กœ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์กฐํšŒํ•œ Reservation์ด ๋ณ€๊ฒฝ ๊ฐ์ง€ ๋Œ€์ƒ์ด ๋œ ์ƒํƒœ
์ค€์˜์† ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๊ด€๋ฆฌ๋ฅผ ๋ฒ—์–ด๋‚œ ์—”ํ‹ฐํ‹ฐ ์ƒํƒœ clear(), detach() ์ดํ›„ ๊ธฐ์กด ๊ฐ์ฒด๊ฐ€ ๋ณ€๊ฒฝ ๊ฐ์ง€ ๋Œ€์ƒ์—์„œ ์ œ์™ธ๋˜๋Š” ์ƒํƒœ
์‚ญ์ œ ์‚ญ์ œ ๋Œ€์ƒ์œผ๋กœ ๋“ฑ๋ก๋˜์–ด flush ์‹œ DELETE๊ฐ€ ์‹คํ–‰๋  ์—”ํ‹ฐํ‹ฐ ์ƒํƒœ deleteById() ์ดํ›„ DELETE๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „๊นŒ์ง€์˜ ์ƒํƒœ
๋งคํ•‘ @Entity ํด๋ž˜์Šค๋ฅผ JPA๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ๋กœ ์ง€์ •ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ Reservation, ReservationWaiting, Theme, ReservationTime์„ ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘ํ•œ ๋ถ€๋ถ„
@Id ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๊ธฐ๋ณธํ‚ค ํ•„๋“œ๋ฅผ ์ง€์ •ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ ๊ฐ ์—”ํ‹ฐํ‹ฐ์˜ id๋ฅผ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธํ‚ค์™€ ๋งคํ•‘ํ•œ ๋ถ€๋ถ„
@GeneratedValue ๊ธฐ๋ณธํ‚ค ๊ฐ’์„ ์ง€์ •๋œ ์ƒ์„ฑ ์ „๋žต์— ๋”ฐ๋ผ ์ƒ์„ฑํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ IDENTITY ์ „๋žต์œผ๋กœ INSERT ์‹œ DB๊ฐ€ ID๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ํ•œ ๋ถ€๋ถ„
GenerationType.IDENTITY INSERT๋ฅผ ์‹คํ–‰ํ•ด DB๊ฐ€ ์ƒ์„ฑํ•œ ๊ธฐ๋ณธํ‚ค๋ฅผ ๋ฐ›์•„์˜ค๋Š” ์ „๋žต ์Šน๊ฒฉ ์˜ˆ์•ฝ ์ €์žฅ ์‹œ ID๋ฅผ ์–ป๊ธฐ ์œ„ํ•œ INSERT๊ฐ€ ์ง€์—ฐ๋œ DELETE๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰๋œ ๋ถ€๋ถ„
@Column ํ•„๋“œ์™€ DB ์ปฌ๋Ÿผ์˜ ์ด๋ฆ„, null ํ—ˆ์šฉ ์—ฌ๋ถ€, ๊ธธ์ด ๋“ฑ์„ ์„ค์ •ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ createdAt, name, date ๋“ฑ์˜ ์ปฌ๋Ÿผ ์กฐ๊ฑด์„ ์ง€์ •ํ•œ ๋ถ€๋ถ„
@Table ํ…Œ์ด๋ธ”๋ช…๊ณผ ์œ ์ผ์„ฑ ์ œ์•ฝ ๋“ฑ ํ…Œ์ด๋ธ” ๋‹จ์œ„ ์„ค์ •์„ ์ง€์ •ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ Reservation์— ๋‚ ์งœยท์‹œ๊ฐ„ยทํ…Œ๋งˆ ์กฐํ•ฉ์˜ ์œ ์ผ์„ฑ ์ œ์•ฝ์„ ์„ค์ •ํ•œ ๋ถ€๋ถ„
๊ฐ’ ๊ฐ์ฒด ๋งคํ•‘ @Embeddable ๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ ๋‚ด๋ถ€์— ํฌํ•จ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐ’ ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ Member์™€ Slot์„ ๋…๋ฆฝ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์•„๋‹Œ ๊ฐ’ ๊ฐ์ฒด๋กœ ์ •์˜ํ•œ ๋ถ€๋ถ„
@Embedded ๊ฐ’ ๊ฐ์ฒด์˜ ํ•„๋“œ๋ฅผ ์†Œ์œ  ์—”ํ‹ฐํ‹ฐ ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ์œผ๋กœ ํŽผ์ณ ์ €์žฅํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ Reservation ํ…Œ์ด๋ธ”์— name, date, time_id, theme_id๊ฐ€ ์ €์žฅ๋˜๋Š” ๋ถ€๋ถ„
์—ฐ๊ด€๊ด€๊ณ„ @ManyToOne ์—ฌ๋Ÿฌ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ํ•˜๋‚˜์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐธ์กฐํ•˜๋Š” N:1 ๊ด€๊ณ„ ๋งคํ•‘ ์—ฌ๋Ÿฌ ์˜ˆ์•ฝ์ด ํ•˜๋‚˜์˜ ReservationTime๊ณผ Theme์„ ์ฐธ์กฐํ•˜๋Š” ๊ตฌ์กฐ
@OneToMany ํ•˜๋‚˜์˜ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์—ฌ๋Ÿฌ ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ปฌ๋ ‰์…˜์œผ๋กœ ๊ฐ€์ง€๋Š” 1:N ๊ด€๊ณ„ ๋งคํ•‘ ์ด๋ฒˆ ๋ฏธ์…˜์—์„œ๋Š” ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
@JoinColumn ์—ฐ๊ด€๊ด€๊ณ„์— ์‚ฌ์šฉ๋˜๋Š” ์™ธ๋ž˜ํ‚ค ์ปฌ๋Ÿผ์„ ์ง€์ •ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ Slot.time์„ time_id, Slot.theme์„ theme_id ์™ธ๋ž˜ํ‚ค์™€ ์—ฐ๊ฒฐํ•œ ๋ถ€๋ถ„
๋‹จ๋ฐฉํ–ฅ ํ•œ์ชฝ ๊ฐ์ฒด๋งŒ ์ƒ๋Œ€ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ด€๊ณ„ Reservation์€ Theme๊ณผ ReservationTime์„ ์•Œ์ง€๋งŒ ๋ฐ˜๋Œ€์ชฝ์€ ์˜ˆ์•ฝ ๋ชฉ๋ก์„ ๊ฐ€์ง€์ง€ ์•Š๋Š” ๊ตฌ์กฐ
์–‘๋ฐฉํ–ฅ ์–‘์ชฝ ๊ฐ์ฒด๊ฐ€ ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ด€๊ณ„ ์ด๋ฒˆ ๋ฏธ์…˜์—์„œ๋Š” ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ์™ธ๋ž˜ํ‚ค ๊ฐ’์„ ์‹ค์ œ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ฒด ์™ธ๋ž˜ํ‚ค๊ฐ€ ์žˆ๋Š” Reservation์˜ Slot.time, Slot.theme ์ชฝ
cascade ์—”ํ‹ฐํ‹ฐ์˜ ์ €์žฅยท์‚ญ์ œ ์ž‘์—…์„ ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ์—๋„ ์ „ํŒŒํ•˜๋Š” ์„ค์ • ์ด๋ฒˆ ๋ฏธ์…˜์—์„œ๋Š” ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
orphanRemoval ๋ถ€๋ชจ์™€์˜ ๊ด€๊ณ„๊ฐ€ ๋Š์–ด์ง„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•˜๋Š” ์„ค์ • ์ด๋ฒˆ ๋ฏธ์…˜์—์„œ๋Š” ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
ํŽ˜์น˜ EAGER ์กฐํšŒ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ ์ „๊นŒ์ง€ ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋กœ๋”ฉํ•˜๋Š” ์ „๋žต time, theme์ด JOIN ๋˜๋Š” ์ถ”๊ฐ€ SELECT๋กœ ์กฐํšŒ๋œ ๋ถ€๋ถ„. JOIN ์ž์ฒด๋ฅผ ๋ณด์žฅํ•˜์ง€๋Š” ์•Š์Œ
LAZY ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ์— ์‹ค์ œ๋กœ ์ ‘๊ทผํ•  ๋•Œ ๋กœ๋”ฉ์„ ์‹œ๋„ํ•˜๋Š” ์ „๋žต ์ด๋ฒˆ ๋งคํ•‘์—์„œ๋Š” ์ง์ ‘ ์ ์šฉํ•˜์ง€ ์•Š์•˜์ง€๋งŒ, ์ ‘๊ทผ ์‹œ ์ถ”๊ฐ€ SELECT๋‚˜ LazyInitializationException์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
N+1 ๋ชฉ๋ก ์กฐํšŒ 1๋ฒˆ ์ดํ›„ ๊ฐ ๊ฒฐ๊ณผ์˜ ์—ฐ๊ด€ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ SELECT๊ฐ€ ๋ฐ˜๋ณต๋˜๋Š” ๋ฌธ์ œ ์˜ˆ์•ฝ 3๊ฐœ ์กฐํšŒ ์‹œ Reservation 1๋ฒˆ, Theme 3๋ฒˆ, ReservationTime 3๋ฒˆ์œผ๋กœ ์ด 7๋ฒˆ ์กฐํšŒ๋œ ๋ถ€๋ถ„
fetch join JPQL์— JOIN FETCH๋ฅผ ๋ช…์‹œํ•ด ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ™์€ SQL๋กœ ์กฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ• ์˜ˆ์•ฝ๊ณผ ์‹œ๊ฐ„ยทํ…Œ๋งˆ๋ฅผ ํ•œ ๋ฒˆ์˜ SELECT๋กœ ์กฐํšŒํ•˜์—ฌ N+1์„ ๋ฐฉ์ง€ํ•œ ๋ถ€๋ถ„
@EntityGraph Repository ์กฐํšŒ ๋ฉ”์„œ๋“œ์— ํ•จ๊ป˜ ๋กœ๋”ฉํ•  ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐฉ์‹ slot.time, slot.theme์„ ํ•จ๊ป˜ ๋กœ๋”ฉํ•˜์—ฌ ํ•œ ๋ฒˆ์˜ SELECT๋กœ ์กฐํšŒ๋œ ๋ถ€๋ถ„
์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ์ฟผ๋ฆฌ Repository ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ๋ถ„์„ํ•ด JPQL์„ ์ž๋™ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹ findByReserver(), findFirstBySlotOrderByIdAsc()๋ฅผ ์‚ฌ์šฉํ•œ ๋ถ€๋ถ„
JPQL ํ…Œ์ด๋ธ”๊ณผ ์ปฌ๋Ÿผ์ด ์•„๋‹ˆ๋ผ ์—”ํ‹ฐํ‹ฐ์™€ ํ•„๋“œ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ์ฟผ๋ฆฌ ์–ธ์–ด ReservationWaitingWithOrder ์กฐํšŒ์™€ fetch join์„ ์—”ํ‹ฐํ‹ฐ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑํ•œ ๋ถ€๋ถ„
Native Query DB SQL์„ ๊ทธ๋Œ€๋กœ ์ž‘์„ฑํ•ด ์‹คํ–‰ํ•˜๋Š” JPA ์ฟผ๋ฆฌ ๋ฐฉ์‹ ์ธ๊ธฐ ํ…Œ๋งˆ ์กฐํšŒ์—์„œ GROUP BY, COUNT, ORDER BY, LIMIT๋ฅผ ์ง์ ‘ ํ‘œํ˜„ํ•œ ๋ถ€๋ถ„
Pageable ์กฐํšŒ ๊ฒฐ๊ณผ์˜ ํŽ˜์ด์ง€ ํฌ๊ธฐ, ๋ฒˆํ˜ธ, ์ •๋ ฌ ์กฐ๊ฑด์„ ์ „๋‹ฌํ•˜๋Š” Spring Data ๊ธฐ๋Šฅ ์˜ˆ์•ฝ ๋ชฉ๋ก ํŽ˜์ด์ง•์— ์‚ฌ์šฉํ–ˆ์œผ๋ฉฐ ์ธ๊ธฐ ํ…Œ๋งˆ ์ƒ์œ„ N๊ฐœ ์กฐํšŒ์˜ JPQL ๋Œ€์•ˆ์œผ๋กœ ๊ฒ€ํ† ํ•œ ๋ถ€๋ถ„

* chore: gradle settings for jpa

* refactor: ๋„๋ฉ”์ธ์„ ์—”ํ‹ฐํ‹ฐ๋กœ ๋ณ€ํ™˜

* refactor: JPA repository๋ฅผ ์ƒ์†ํ•˜๋„๋ก ๋ณ€๊ฒฝ

* refactor: JPA ์ €์žฅ์†Œ ํ˜ธ์ถœ ๋ฐฉ์‹ ์ •๋ฆฌ

- Spring Data JPA ๋ฉ”์„œ๋“œ์— ๋งž์ถฐ ์˜ˆ์•ฝ/๋Œ€๊ธฐ ์กฐํšŒ์™€ ์ˆ˜์ • ํ๋ฆ„ ๋ณ€๊ฒฝ

- ์˜ˆ์•ฝ ๋Œ€๊ธฐ ์ˆœ๋ฒˆ ์กฐํšŒ๋ฅผ native query ๋Œ€์‹  JPQL projection์œผ๋กœ ์ „ํ™˜

- DB ์ œ์•ฝ ์œ„๋ฐ˜์„ 409 Conflict ์‘๋‹ต์œผ๋กœ ๋งคํ•‘

* refactor: ์—”ํ‹ฐํ‹ฐ ๊ธฐ๋ฐ˜ ์Šคํ‚ค๋งˆ ์ƒ์„ฑ์œผ๋กœ ์ „ํ™˜

- ์—”ํ‹ฐํ‹ฐ์™€ ์ž„๋ฒ ๋””๋“œ ๊ฐ’์— nullable, unique, join column ์ œ์•ฝ ๋ช…์‹œ

- ์˜ˆ์•ฝ๊ณผ ๋Œ€๊ธฐ ์ค‘๋ณต ์ œ์•ฝ์กฐ๊ฑด์„ JPA ๋งคํ•‘์œผ๋กœ ์ด๋™

- schema.sql์„ ์ œ๊ฑฐํ•˜๊ณ  ํ…Œ์ŠคํŠธ ์Šคํ‚ค๋งˆ๋„ Hibernate DDL๋กœ ์ƒ์„ฑ

* test: JPA Repository ๊ธฐ์ค€ ํ…Œ์ŠคํŠธ ์ •๋ฆฌ

- JdbcRepository ํ…Œ์ŠคํŠธ๋ฅผ DataJpaTest ๊ธฐ๋ฐ˜ JPA Repository ํ…Œ์ŠคํŠธ๋กœ ์ „ํ™˜

- Spring Data JPA ๋ฉ”์„œ๋“œ๋ช…๊ณผ ์˜ˆ์™ธ ํƒ€์ž…์— ๋งž์ถฐ ๊ฒ€์ฆ ์ˆ˜์ •

- ํŽ˜์ด์ง€ ์กฐํšŒ์™€ ๋Œ€๊ธฐ ์ˆœ๋ฒˆ ์กฐํšŒ mock์„ JPA ์‹œ๊ทธ๋‹ˆ์ฒ˜์— ๋งž๊ฒŒ ๊ฐฑ์‹ 
- dirty checking: save ์—†์ด ์—”ํ‹ฐํ‹ฐ ๋ณ€๊ฒฝ ํ›„ flushํ•˜๋ฉด UPDATE๊ฐ€ ๋ฐœํ–‰๋œ๋‹ค
- 1์ฐจ ์บ์‹œ: ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜์—์„œ ๊ฐ™์€ id๋ฅผ ๋‘ ๋ฒˆ ์กฐํšŒํ•˜๋ฉด SELECT๋Š” ํ•œ ๋ฒˆ๋งŒ ๋ฐœ์ƒํ•œ๋‹ค
- ์“ฐ๊ธฐ ์ง€์—ฐ: IDENTITY ์ „๋žต์—์„œ๋Š” save ์‹œ์ ์— INSERT๊ฐ€ ๋จผ์ € ๋ฐœํ–‰๋  ์ˆ˜ ์žˆ๋‹ค
- flush ์‹œ์ : JPQL ์‹คํ–‰ ์ง์ „์— ๋ณ€๊ฒฝ ๋‚ด์šฉ์ด DB์™€ ๋™๊ธฐํ™”๋œ๋‹ค
- fetch ๊ธฐ๋ณธ๊ฐ’: @manytoone์€ ๊ธฐ๋ณธ EAGER๋ผ ์˜ˆ์•ฝ ์กฐํšŒ ์‹œ ์‹œ๊ฐ„๊ณผ ํ…Œ๋งˆ๋„ ํ•จ๊ป˜ ๋กœ๋”ฉ๋œ๋‹ค
- LazyInitializationException: ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์€ ํ”„๋ก์‹œ๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๋ฐ–์—์„œ ์ ‘๊ทผํ•˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค
@kangrae-jo kangrae-jo changed the title Step3 [๐ŸŒฑ JPA ๋ฐฉํƒˆ์ถœ ์˜ˆ์•ฝ ๋Œ€๊ธฐ] ๋ฐ€๋ž€(์กฐ๊ฐ•๋ž˜) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค. Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant