Skip to content

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

Open
Jihyun3478 wants to merge 61 commits into
woowacourse:jihyun3478from
Jihyun3478:jpa
Open

[๐ŸŒฑ JPA ๋ฐฉํƒˆ์ถœ ์˜ˆ์•ฝ ๋Œ€๊ธฐ] ๋กœ์ง€(์ด์ง€ํ˜„) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค.#591
Jihyun3478 wants to merge 61 commits into
woowacourse:jihyun3478from
Jihyun3478:jpa

Conversation

@Jihyun3478

@Jihyun3478 Jihyun3478 commented Jun 18, 2026

Copy link
Copy Markdown

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

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

์–ด๋–ค ๋ถ€๋ถ„์— ์ง‘์ค‘ํ•˜์—ฌ ๋ฆฌ๋ทฐํ•ด์•ผ ํ• ๊นŒ์š”?

0๋‹จ๊ณ„: ์‹œ์ž‘ ๊ตฌ์กฐ ํŒŒ์•…

๊ธฐ์กด ์ฝ”๋“œ ๊ตฌ์กฐ๋Š” Controller โ†’ Service โ†’ Dao โ†’ Domain์ด์—ˆ๊ณ , Service๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋งก๊ณ  Dao๋Š” SQL ์‹คํ–‰๊ณผ ๊ฐ์ฒด ๋งคํ•‘๋งŒ ์ฑ…์ž„์กŒ์Šต๋‹ˆ๋‹ค. Controller์—์„œ Service๋กœ Dto๋ฅผ ๋„˜๊ธฐ๊ธฐ ์ „์— Command๋ฅผ ํ™œ์šฉํ•ด Service๊ฐ€ DTO ๊ณ„์ธต์— ์˜์กดํ•˜์ง€ ์•Š๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

JPA ์ „ํ™˜ ๋ฒ”์œ„ ํŒ๋‹จ

์œ ์ง€: Controller ๊ณ„์ธต, Service ๊ณ„์ธต
๊ต์ฒด: Repository(Dao) ๊ณ„์ธต ์ „์ฒด

Repository์— ์กด์žฌํ•˜๋Š” ROW_MAPPER, baseSelectSql() ๊ฐ™์€ ํ•„๋“œยท๋ฉ”์„œ๋“œ๊ฐ€ JPA์˜ ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋ชจ๋‘ ๋ถˆํ•„์š”ํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.


1๋‹จ๊ณ„: JPA ์ „ํ™˜

๋ฐฉํƒˆ์ถœ ๋ฏธ์…˜ INSERT (JdbcTemplate)

INSERT INTO reservation (NAME, DATE, TIME_ID, THEME_ID)
VALUES ('๋กœ์ง€', 2026-06-18, 5, 3);
JPA ์ „ํ™˜ ํ›„ INSERT (Hibernate)

insert 
into
    reservation
    (name, date, theme_id, time_id, id) 
values
    (?, ?, ?, ?, default)
  • ๋™์ผํ•œ ์ : 4๊ฐœ์˜ ์ปฌ๋Ÿผ์„ ์‚ฝ์ž…ํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  • ๋‹ค๋ฅธ ์ : JPA๋Š” id ์ปฌ๋Ÿผ์„ ๋ช…์‹œํ•˜๋˜ default ํ‚ค์›Œ๋“œ๋กœ ์œ„์ž„ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ?๋กœ ๋ฐ”์ธ๋”ฉ๋ฉ๋‹ˆ๋‹ค.

findById(reservationId).getTime().getStartAt() ๋ฐœํ–‰ SQL

select r1_0.id, r1_0.name, r1_0.date,
       t1_0.id, t1_0.description, t1_0.name, t1_0.thumbnail,
       t2_0.id, t2_0.start_at
from reservation r1_0
left join theme t1_0 on t1_0.id = r1_0.theme_id
left join reservation_time t2_0 on t2_0.id = r1_0.time_id
where r1_0.id = ?

@manytoone์˜ ๊ธฐ๋ณธ fetch ์ „๋žต์ด EAGER์ด๊ธฐ ๋•Œ๋ฌธ์— findById ํ•œ ๋ฒˆ์— ์—ฐ๊ด€๋œ Theme, ReservationTime๊นŒ์ง€ LEFT JOIN์œผ๋กœ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๊ด€์ฐฐ: ์“ฐ๊ธฐ ์ง€์—ฐ + flush ์ˆœ์„œ / ๋ฐœ๊ฒฌํ•œ ๋ฒ„๊ทธ์™€ ์›์ธ

์˜ˆ์•ฝ ์‚ญ์ œ ํ›„ ๋Œ€๊ธฐ์ž๋ฅผ ์˜ˆ์•ฝ์œผ๋กœ ์Šน๊ฒฉํ•˜๋Š” ์ฝ”๋“œ์—์„œ ConstraintViolationException์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

// deleteById ํ˜ธ์ถœ
// save ํ˜ธ์ถœ (๊ฐ™์€ ์Šฌ๋กฏ unique constraint)
โ†’ ConstraintViolationException ๋ฐœ์ƒ
  • ์˜ˆ์ธก: deleteById๋ฅผ ๋จผ์ € ํ˜ธ์ถœํ–ˆ์œผ๋‹ˆ DELETE๊ฐ€ ๋จผ์ € ๋‚˜๊ฐ€๊ณ  INSERT๊ฐ€ ๋‚˜๊ฐˆ ๊ฒƒ์ด๋‹ค.
  • ์‹ค์ œ: Hibernate์˜ action queue๋Š” INSERT๋ฅผ DELETE๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ์›์ธ: flush ์‹œ์ ๊นŒ์ง€ ์“ฐ๊ธฐ ์ง€์—ฐ์ด ์Œ“์ด๋ฉฐ, ์‹คํ–‰ ์ˆœ์„œ๋Š” ํ˜ธ์ถœ ์ˆœ์„œ๊ฐ€ ์•„๋‹Œ Hibernate ๋‚ด๋ถ€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.
  • ํ•ด๊ฒฐ: deleteById ์งํ›„ reservationRepository.flush()๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ•ด DELETE๋ฅผ ๋จผ์ € DB์— ๋ฐ˜์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

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

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

JPQL์ด ๋จผ์ € ๋– ์˜ฌ๋ž์Šต๋‹ˆ๋‹ค. ์ธ์ฆ/์ธ๊ฐ€ ์ฝ”๋“œ๊ฐ€ ์—†์–ด "์–ด๋–ป๊ฒŒ ๋‚ด ์˜ˆ์•ฝ์„ ๊ตฌ๋ถ„ํ•˜๋Š”๊ฐ€"๋ฅผ ์ฟผ๋ฆฌ ๊ด€์ ์œผ๋กœ ๋จผ์ € ๊ณ ๋ฏผํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ๋Š” ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ฟผ๋ฆฌ๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. name์œผ๋กœ ํ•„ํ„ฐ๋งํ•˜๋Š” ์กฐ๊ฑด ์ž์ฒด๋Š” ๋ฉ”์„œ๋“œ ์ด๋ฆ„์œผ๋กœ ํ‘œํ˜„ ๊ฐ€๋Šฅํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์˜ˆ์•ฝ๊ณผ ๋Œ€๊ธฐ๋ฅผ ํ•ฉ์‚ฐยท์ •๋ ฌํ•˜๋Š” ๊ณผ์ •์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ์ด์–ด์—์„œ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ฒฐ์ •์˜ ํ•œ๊ณ„

  • ํ•œ๊ณ„ 1 (์ฟผ๋ฆฌ ํšŸ์ˆ˜): ์˜ˆ์•ฝ ์กฐํšŒ 1๋ฒˆ + ๋Œ€๊ธฐ ์กฐํšŒ 1๋ฒˆ, ๋Œ€๊ธฐ์ž ์ˆœ๋ฒˆ ๊ณ„์‚ฐ N๋ฒˆ โ†’ ์ด 2+N๋ฒˆ์˜ ์ฟผ๋ฆฌ
  • ํ•œ๊ณ„ 2 (์ •๋ ฌ ์œ„์น˜): ์ •๋ ฌ์„ DB๊ฐ€ ์•„๋‹Œ Java์—์„œ ์ˆ˜ํ–‰ โ†’ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ์ „์ฒด๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ฆฐ ๋’ค ์ •๋ ฌ
  • ํ•œ๊ณ„ 3 (ํ‘œํ˜„ ๋ถˆ๊ฐ€): Reservation๊ณผ ReservationWaiting์„ UNIONํ•˜๊ฑฐ๋‚˜ ํ•จ๊ป˜ ์กฐํšŒํ•˜๋Š” ๊ฒƒ์€ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ฟผ๋ฆฌ๋กœ ํ‘œํ˜„ ๋ถˆ๊ฐ€

Member ๋„๋ฉ”์ธ ์ถ”๊ฐ€

2๋‹จ๊ณ„์—์„œ name ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋‚ด ์˜ˆ์•ฝ์„ ์กฐํšŒํ•˜๋˜ ๋ฐฉ์‹์˜ ํ•œ๊ณ„๋ฅผ ๋А๊ปด, Member ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  Reservation, ReservationWaiting ๋ชจ๋‘ String name โ†’ @manytoone Member member๋กœ ๊ต์ฒดํ–ˆ์Šต๋‹ˆ๋‹ค. ๋•๋ถ„์— GET /reservations-mine?name=๋กœ์ง€ ๋Œ€์‹  GET /reservations-mine?memberId=1๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

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

  • ๊ฒฐ์ • 1: ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ฟผ๋ฆฌ๋ฅผ ์“ธ ๊ฒƒ์ธ๊ฐ€, JPQL์„ ์“ธ ๊ฒƒ์ธ๊ฐ€

    • ๋‹จ์ˆœ ํ•„ํ„ฐ๋ง(findByMember_Id)์€ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์œผ๋กœ ์ถฉ๋ถ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์˜ˆ์•ฝ๊ณผ ๋Œ€๊ธฐ๋ฅผ ํ•ฉ์ณ ์ •๋ ฌํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ฟผ๋ฆฌ์˜ ํ•œ๊ณ„์— ๋ถ€๋”ชํ˜”์Šต๋‹ˆ๋‹ค. JPQL๋กœ ์‹œ์ž‘ํ–ˆ๋‹ค๋ฉด ์ด ๋ฒฝ์„ ๋” ์ผ์ฐ ๋„˜์—ˆ์„์ง€๋„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค.
  • ๊ฒฐ์ • 2: Member ๋„๋ฉ”์ธ์„ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ธ๊ฐ€

    • ์ด๋ฒˆ ๋ฏธ์…˜์€ ์ธ์ฆ/์ธ๊ฐ€ ํ•™์Šต์ด ๋ชฉ์ ์ด ์•„๋‹ˆ๋ผ JPA ํ•™์Šต์ด ๋ชฉ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฒ˜์Œ์—๋Š” Member ์—†์ด ์ง„ํ–‰ํ•˜๋ ค ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ String name์œผ๋กœ๋Š” ์ง„์งœ "๋‚ด ์˜ˆ์•ฝ"์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†์–ด ๊ฒฐ๊ตญ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€ํ•˜์ž Reservation, ReservationWaiting, ๊ด€๋ จ Repository, Service, Controller, ํ…Œ์ŠคํŠธ ์ „์ฒด๋ฅผ ๋‹ค ๊ฑด๋“œ๋ ค์•ผ ํ–ˆ๊ณ , ๋ณ€๊ฒฝ ๋ฒ”์œ„๊ฐ€ ์˜ˆ์ƒ๋ณด๋‹ค ํ›จ์”ฌ ๋„“์—ˆ์Šต๋‹ˆ๋‹ค.

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

ConstraintViolationException์„ ์ฒ˜์Œ ๋งˆ์ฃผํ–ˆ์„ ๋•Œ์ž…๋‹ˆ๋‹ค. deleteById๋ฅผ ๋จผ์ € ํ˜ธ์ถœํ–ˆ๋Š”๋ฐ ์™œ DELETE๋ณด๋‹ค INSERT๊ฐ€ ๋จผ์ € ๋‚˜๊ฐ€๋Š”์ง€ ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉฐ Hibernate์˜ action queue๊ฐ€ INSERT๋ฅผ ๋จผ์ € ์ฒ˜๋ฆฌํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋๊ณ , flush ํƒ€์ด๋ฐ์ด ํ˜ธ์ถœ ์ˆœ์„œ์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์ฒ˜์Œ์œผ๋กœ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ "์ด๋ก "์ด ์•„๋‹Œ "๋ฒ„๊ทธ"๋กœ ์ฒ˜์Œ ๋งŒ๋‚œ ์ˆœ๊ฐ„์ด์—ˆ์Šต๋‹ˆ๋‹ค.


๋ฏธ์™„ ์‚ฌํ•ญ

  • 3๋‹จ๊ณ„ (JPQLยทํŽ˜์น˜ ์ „๋žต): ๋ฏธ์ง„์ž… ์ƒํƒœ
  • N+1 ๋ฌธ์ œ ํƒ์ƒ‰: 2๋‹จ๊ณ„์˜ 2+N๋ฒˆ ์ฟผ๋ฆฌ ๋ฌธ์ œ๋ฅผ fetch join ๋˜๋Š” @batchsize๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์„ ์‹œ๋„ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค
  • 4๋‹จ๊ณ„: ๋ฏธ์ง„์ž… ์ƒํƒœ

simhokyung and others added 30 commits June 4, 2026 16:11
# Conflicts:
#	src/main/resources/data.sql
#	src/main/resources/schema.sql
#	src/main/resources/static/admin-reservation.html
#	src/main/resources/static/admin-theme.html
#	src/main/resources/static/admin-time.html
#	src/main/resources/static/index.html
#	src/main/resources/static/my-reservations.html
#	src/main/resources/static/reservation.html
#	src/test/java/roomescape/DatabaseInitializer.java
- ReservationTimeControllerTest
- ThemeControllerTest
- ReservationWaitingControllerTest
- ReservationWaitingServiceTest
- ReservationWaitingDaoTest
Jihyun3478 added 30 commits June 4, 2026 16:11
- ๋น„๊ด€์  ๋ฝ ์ ์šฉ -> ๊ฐ™์€ ์Šฌ๋กฏ์— ๋Œ€ํ•œ ๋Œ€๊ธฐ ์‹ ์ฒญ์ด ์ง๋ ฌํ™”๋˜๋ฏ€๋กœ ์ˆœ๋ฒˆ ์—ญ์ „ ๋ฐฉ์ง€
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.

2 participants