Skip to content

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

Open
dahxxn wants to merge 5 commits into
woowacourse:dahxxnfrom
dahxxn:jpa
Open

[๐ŸŒฑ JPA ๋ฐฉํƒˆ์ถœ ์˜ˆ์•ฝ ๋Œ€๊ธฐ] ํ•œ๋‹ค ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค.#580
dahxxn wants to merge 5 commits into
woowacourse:dahxxnfrom
dahxxn:jpa

Conversation

@dahxxn

@dahxxn dahxxn commented Jun 18, 2026

Copy link
Copy Markdown

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

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

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

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

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

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

1๋‹จ๊ณ„: JPA ๋งคํ•‘ ์ „ํ™˜

  • Theme, ReservationTime, Reservation, ReservationWaiting ์—”ํ‹ฐํ‹ฐ ์ „ํ™˜
  • Reservation, ReservationWaiting ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘
  • Spring Data JPA Repository ์ „ํ™˜
  • ๊ธฐ์กด JDBC Repository ์ œ๊ฑฐ

2๋‹จ๊ณ„: ๋‚ด ์˜ˆ์•ฝ ๋ชฉ๋ก ์กฐํšŒ

  • ์ด๋ฆ„์„ ๊ธฐ์ค€์œผ๋กœ ๋ณธ์ธ ์˜ˆ์•ฝ ๋ชฉ๋ก ์กฐํšŒ

3๋‹จ๊ณ„: ์˜ˆ์•ฝ ๋Œ€๊ธฐ

  • POST /waitings ์˜ˆ์•ฝ ๋Œ€๊ธฐ ์š”์ฒญ
  • DELETE /waitings/{id} ์˜ˆ์•ฝ ๋Œ€๊ธฐ ์ทจ์†Œ
  • ์˜ˆ์•ฝ๊ณผ ์˜ˆ์•ฝ ๋Œ€๊ธฐ ํ†ตํ•ฉ ์กฐํšŒ
  • ์ค‘๋ณต ์˜ˆ์•ฝ ๋ฐ ์ค‘๋ณต ๋Œ€๊ธฐ ๋ฐฉ์ง€
  • ๋Œ€๊ธฐ ์ˆœ๋ฒˆ ํ‘œ์‹œ
  • fetch join ๋˜๋Š” @EntityGraph ์ ์šฉ

4๋‹จ๊ณ„: ์˜ˆ์•ฝ ๋Œ€๊ธฐ ๊ด€๋ฆฌ

  • ์˜ˆ์•ฝ ์ทจ์†Œ ์‹œ ์ฒซ ๋ฒˆ์งธ ๋Œ€๊ธฐ ์ž๋™ ์Šน์ธ
  • ๊ด€๋ฆฌ์ž ์˜ˆ์•ฝ ์ทจ์†Œ ์‹œ ์ฒซ ๋ฒˆ์งธ ๋Œ€๊ธฐ ์ž๋™ ์Šน์ธ
  • ์ž๋™ ์Šน์ธ ์‹คํŒจ ์‹œ ์ „์ฒด ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
  • ์–ด๋“œ๋ฏผ ์˜ˆ์•ฝ ๋Œ€๊ธฐ ๋ชฉ๋ก ์กฐํšŒ
  • ์–ด๋“œ๋ฏผ ์˜ˆ์•ฝ ๋Œ€๊ธฐ ์ทจ์†Œ
  • ๋™์‹œ ์Šน์ธ์— ๋Œ€ํ•œ ๋ช…์‹œ์ ์ธ ๋™์‹œ์„ฑ ์ œ์–ด

๊ด€์ฐฐํ•œ SQL

์—”ํ‹ฐํ‹ฐ ๊ธฐ๋ฐ˜ DDL ์ƒ์„ฑ

create table reservation (
    date DATE not null,
    id bigint generated by default as identity,
    theme_id bigint not null,
    time_id bigint not null,
    name varchar(255) not null,
    primary key (id)
)
create table theme (
    id bigint generated by default as identity,
    description varchar(255),
    name varchar(255) not null,
    thumbnail TEXT,
    primary key (id)
)

๊ธฐ์กด์—๋Š” schema.sql์ด ์Šคํ‚ค๋งˆ์˜ ๊ธฐ์ค€์ด์—ˆ์ง€๋งŒ, JPA ์ „ํ™˜ ํ›„์—๋Š” ์—”ํ‹ฐํ‹ฐ์˜ nullable, ๊ธธ์ด, ํƒ€์ž… ๋“ฑ์˜ ๋งคํ•‘ ์ •๋ณด๊ฐ€ Hibernate ์ƒ์„ฑ DDL์— ๋ฐ˜์˜๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

LAZY ์—ฐ๊ด€๊ด€๊ณ„ ์กฐํšŒ

Reservation์„ ์กฐํšŒํ–ˆ์„ ๋•Œ:

select
    r.id,
    r.date,
    r.name,
    r.theme_id,
    r.time_id
from reservation r
where r.id = ?

์ดํ›„ ๋‹ค์Œ ์ฝ”๋“œ๋กœ ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ์˜ ์‹ค์ œ ๊ฐ’์— ์ ‘๊ทผํ–ˆ์Šต๋‹ˆ๋‹ค.
reservation.getTime().getStartAt();
๊ทธ๋•Œ ReservationTime์„ ์กฐํšŒํ•˜๋Š” SQL์ด ์ถ”๊ฐ€๋กœ ์‹คํ–‰๋์Šต๋‹ˆ๋‹ค.

select
    rt.id,
    rt.start_at
from reservation_time rt
where rt.id = ?

@ManyToOne(fetch = LAZY)๋กœ ์ธํ•ด ์ตœ์ดˆ ์˜ˆ์•ฝ ์กฐํšŒ์—์„œ๋Š” ์™ธ๋ž˜ ํ‚ค๋งŒ ์กฐํšŒํ•˜๊ณ ,
์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ์˜ ์‹ค์ œ ํ•„๋“œ์— ์ ‘๊ทผํ•  ๋•Œ ์ถ”๊ฐ€ SELECT๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ง์„ค์ธ ๊ฒฐ์ •

๋‹จ๋ฐฉํ–ฅ๊ณผ ์–‘๋ฐฉํ–ฅ

Theme์ด๋‚˜ ReservationTime์—์„œ ์˜ˆ์•ฝ ๋ชฉ๋ก์„ ํƒ์ƒ‰ํ•  ๊ฐ€๋Šฅ์„ฑ์„ ๊ณ ๋ คํ•ด ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์„ ๊ณ ๋ฏผํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ํ˜„์žฌ ๊ธฐ๋Šฅ์—๋Š” ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์—์„œ ์˜ˆ์•ฝ ๋ชฉ๋ก์œผ๋กœ ์ด๋™ํ•ด์•ผ ํ•˜๋Š” ์š”๊ตฌ๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋ถˆํ•„์š”ํ•œ ์ปฌ๋ ‰์…˜ ๊ด€๋ฆฌ์™€ ์—ฐ๊ด€๊ด€๊ณ„ ๋™๊ธฐํ™” ์ฑ…์ž„์„ ๋งŒ๋“ค์ง€ ์•Š๊ธฐ ์œ„ํ•ด Reservation, ReservationWaiting์—์„œ๋งŒ ์ฐธ์กฐํ•˜๋Š” ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

cascade์™€ orphanRemoval

์˜ˆ์•ฝ์ด ์‚ญ์ œ๋˜๋”๋ผ๋„ Theme์ด๋‚˜ ReservationTime์€ ํ•จ๊ป˜ ์‚ญ์ œ๋˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์„œ๋กœ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ๋…๋ฆฝ์ ์ด๋ฏ€๋กœ cascade์™€ orphanRemoval์„ ์ ์šฉํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ฟผ๋ฆฌ์™€ JPQL

๋‹จ์ˆœํ•œ ๋™๋“ฑ ์กฐ๊ฑด์€ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์œผ๋กœ ์˜๋„๋ฅผ ์ถฉ๋ถ„ํžˆ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด ํŒŒ์ƒ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
findByName(...)
existsByTime_Id(...)
existsByTheme_Id(...)
๋ฐ˜๋ฉด ๋Œ€๊ธฐ ์ˆœ๋ฒˆ ๊ณ„์‚ฐ์ด๋‚˜ ์ธ๊ธฐ ํ…Œ๋งˆ ์ง‘๊ณ„์ฒ˜๋Ÿผ ์ง‘๊ณ„ยท์ •๋ ฌยทDTO ํ”„๋กœ์ ์…˜์ด ํ•„์š”ํ•œ ์กฐํšŒ๋Š” ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด ์ง€๋‚˜์น˜๊ฒŒ ๊ธธ์–ด์ง€๊ฑฐ๋‚˜ ํ‘œํ˜„ ์ž์ฒด๊ฐ€ ์–ด๋ ค์›Œ JPQL์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ”๋“ค๋ฆฐ ํ•œ ์žฅ๋ฉด

์ฒ˜์Œ์—๋Š” ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ EAGER๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด N+1 ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ EAGER๋Š” ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฆ‰์‹œ ๋กœ๋”ฉํ•œ๋‹ค๋Š” ๊ฒƒ๋งŒ ๋ณด์žฅํ•˜๋ฉฐ, ํ•˜๋‚˜์˜ JOIN SQL๋กœ ์กฐํšŒํ•˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜คํžˆ๋ ค ์—ฐ๊ด€ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š” ์—†๋Š” ์กฐํšŒ์—์„œ๋„ ํ•ญ์ƒ ๋กœ๋”ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ์—”ํ‹ฐํ‹ฐ์˜ ๊ธฐ๋ณธ ๊ด€๊ณ„๋Š” LAZY๋กœ ์œ ์ง€ํ•˜๊ณ , ์—ฐ๊ด€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ํŠน์ • ์กฐํšŒ์—์„œ fetch join์ด๋‚˜ @EntityGraph๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉํ–ฅ์ด ์ ์ ˆํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ๊ฒฐ์ •์˜ ํ•œ๊ณ„

์ž๋™ ์Šน์ธ ๋กœ์ง์„ ํ•˜๋‚˜์˜ @transactional ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ ์•ˆ์— ๋‘์–ด ๋‹จ์ผ ์š”์ฒญ ์•ˆ์—์„œ์˜ ์›์ž์„ฑ์€ ํ™•๋ณดํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ๊ฐ™์€ ์Šฌ๋กฏ์—์„œ ์—ฌ๋Ÿฌ ์ทจ์†Œ ๋˜๋Š” ์Šน๊ฒฉ ์š”์ฒญ์ด ๋™์‹œ์— ์‹คํ–‰๋  ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•œ ๋ช…์‹œ์ ์ธ ๋ฝ์€ ์ ์šฉํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ UNIQUE ์ œ์•ฝ์กฐ๊ฑด์ด ์ตœ์ข… ๋ฌด๊ฒฐ์„ฑ ์•ˆ์ „์žฅ์น˜ ์—ญํ• ์„ ํ•˜์ง€๋งŒ, ์ถฉ๋Œ ๋ฐœ์ƒ ์‹œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๊ณผ ์žฌ์‹œ๋„ ์ •์ฑ…๊นŒ์ง€ ์ถฉ๋ถ„ํžˆ ๋‹ค๋ฃจ์ง€๋Š” ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ˜„์žฌ ๊ฒฐ์ •์—์„œ ๊ฐ€์žฅ ์•ฝํ•œ ๋ถ€๋ถ„์€ ๋™์‹œ์„ฑ ์ œ์–ด์ž…๋‹ˆ๋‹ค.

dahxxn added 5 commits June 18, 2026 17:15
- ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์—†๋Š” ๋„๋ฉ”์ธ์ธ Theme, ReservationTime์— ๋Œ€ํ•ด์„œ JPA ์ „ํ™˜
- ๊ด€๋ จ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ผ๊ด„ ์ˆ˜์ • ๋ฐ ์ด์ „ JDBC ๊ธฐ๋ฐ˜ ์ฝ”๋“œ ์ œ๊ฑฐ
- ์„ค์ • ํŒŒ์ผ ์ˆ˜์ • (build.gradle, application.properties)
- ์˜ˆ์•ฝ ์ผ์ • ๋ณ€๊ฒฝ ์‹œ save ํ˜ธ์ถœ ๋Œ€์‹  ์˜์† ์—”ํ‹ฐํ‹ฐ ๋ณ€๊ฒฝ
- ๊ธฐ์กด ์˜ˆ์•ฝ ์Šฌ๋กฏ์„ ๋ณด์กดํ•˜์—ฌ ๋Œ€๊ธฐ ์ž๋™ ์Šน๊ฒฉ ์ฒ˜๋ฆฌ
- flush๋ฅผ ํ†ตํ•œ SQL ์‹คํ–‰ ์ˆœ์„œ ๋ฐ ์ค‘๋ณต ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ์  ์ œ์–ด
- ๊ด€๋ จ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ˆ˜์ •
- ์˜ˆ์•ฝ ๋ฐ ์˜ˆ์•ฝ ๋Œ€๊ธฐ ๋ชฉ๋ก ์กฐํšŒ์— EntityGraph ์ ์šฉ
- Theme๊ณผ ReservationTime์„ JOIN์œผ๋กœ ํ•จ๊ป˜ ์กฐํšŒ
- DTO ๋ณ€ํ™˜ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋˜ ์ถ”๊ฐ€ SELECT ์ œ๊ฑฐ
- ์—”ํ‹ฐํ‹ฐ์˜ ๊ธฐ๋ณธ ์—ฐ๊ด€๊ด€๊ณ„๋Š” LAZY๋กœ ์œ ์ง€
- ์˜ˆ์•ฝ ์ทจ์†Œ์™€ ๋Œ€๊ธฐ ์ž๋™ ์Šน๊ฒฉ์˜ ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ ๊ฒ€์ฆ
- ๋™์‹œ ์ทจ์†Œ ์š”์ฒญ์˜ ์ถฉ๋Œ์„ 409 ๋น„์ฆˆ๋‹ˆ์Šค ์˜ˆ์™ธ๋กœ ๋ณ€ํ™˜
- ์˜ˆ์•ฝ ๋ฐ ์˜ˆ์•ฝ ๋Œ€๊ธฐ ์ค‘๋ณต ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ UNIQUE ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€
- ์ž๋™ ์Šน๊ฒฉ ๊ณผ์ •์˜ SQL ๋ฐ flush ์ˆœ์„œ ๊ฒ€์ฆ
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