Skip to content

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

Open
boyekim wants to merge 6 commits into
woowacourse:boyekimfrom
boyekim:step3
Open

[๐ŸŒฑ JPA ๋ฐฉํƒˆ์ถœ ์˜ˆ์•ฝ ๋Œ€๊ธฐ] ๋ณด์˜ˆ ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค.#571
boyekim wants to merge 6 commits into
woowacourse:boyekimfrom
boyekim:step3

Conversation

@boyekim

@boyekim boyekim commented Jun 18, 2026

Copy link
Copy Markdown

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

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

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

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

  • JdbcTemplate ๊ธฐ๋ฐ˜ ์ €์žฅ์†Œ๋ฅผ Spring Data JPA Repository๋กœ ์ „ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • Reservation -> ReservationSlot -> Theme/Date/Time ๊ตฌ์กฐ๋กœ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • Theme + Date + Time ์กฐํ•ฉ์€ ReservationSlot์œผ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ  ์œ ๋‹ˆํฌ ์ œ์•ฝ์„ ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

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

  • GET /reservations-mine ๋กœ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ฟผ๋ฆฌ ๋Œ€์‹  JPQL(@Query)์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์กฐ๊ฑด, ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ ์กฐ์ธ, ๋‚ ์งœ/์‹œ๊ฐ„ ์ •๋ ฌ, ๋Œ€๊ธฐ๋ฒˆํ˜ธ ๊ณ„์‚ฐ์ด ํ•จ๊ป˜ ํ•„์š”ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  • ์„œ๋น„์Šค์—์„œ ๋Œ€๊ธฐ๋ฒˆํ˜ธ๋ฅผ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๊ณ , JPQL ์ƒ์„ฑ์ž ํ‘œํ˜„์‹์œผ๋กœ ReservationWithWaitingNumber๋ฅผ ๋ฐ”๋กœ ์กฐํšŒํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

3๋‹จ๊ณ„: ์˜ˆ์•ฝ ๋Œ€๊ธฐ ์กฐํšŒ์™€ N+1 ๊ด€์ฐฐ

  • ๋ณ„๋„ Waiting ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ , ReservationStatus.WAITING์œผ๋กœ ์˜ˆ์•ฝ ๋Œ€๊ธฐ๋ฅผ ํ‘œํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • Reservation์ด ReservationSlot, User๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ReservationSlot์ด Theme, Date, Time์„ ์ฐธ์กฐํ•˜๋„๋ก ๋ชจ๋ธ๋งํ–ˆ์Šต๋‹ˆ๋‹ค.
  • DTO ๋ณ€ํ™˜ ์‹œ reservation.getReservationSlot().getTheme()์ฒ˜๋Ÿผ ์—ฐ๊ด€ ๊ฐ์ฒด๋ฅผ ํƒ€๊ณ  ๋“ค์–ด๊ฐ€๋ฉด N+1์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹จ์ˆœํžˆ fetch = EAGER๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์€ โ€œ์–ธ์ œ ๋กœ๋”ฉํ• ์ง€โ€๋งŒ ์ •ํ•  ๋ฟ, ํ•ญ์ƒ ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค๋Š” ๋ณด์žฅ์€ ์—†๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์กฐํšŒ API์—์„œ๋Š” ํ•„์š”ํ•œ ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ๋ฅผ JPQL ์กฐ์ธ์œผ๋กœ ๋ช…์‹œํ•˜๊ณ , ๋Œ€๊ธฐ ์ˆœ๋ฒˆ์€ JPQL ์ƒ์„ฑ์ž ํ‘œํ˜„์‹๊ณผ ์ƒ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ๋กœ ํ•จ๊ป˜ ์กฐํšŒํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋กœ์จ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋Œ€๊ธฐ ๋ชฉ๋ก์„ ๋‹ค์‹œ ์ˆœํšŒํ•˜์ง€ ์•Š๊ณ , ์กฐํšŒ ์ฟผ๋ฆฌ์—์„œ waitingNumber๊นŒ์ง€ ํ•จ๊ป˜ ๊ฐ€์ ธ์˜ค๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

4๋‹จ๊ณ„: ์–ด๋“œ๋ฏผ ๋Œ€๊ธฐ ๊ด€๋ฆฌ ๋ฐ ์ž๋™ ์Šน์ธ

  • GET /admin/waitings, DELETE /admin/waitings/{id}๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ˆ˜๋™ ์Šน์ธ ๋Œ€์‹  ์ž๋™ ์Šน์ธ์„ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ™•์ • ์˜ˆ์•ฝ ์ทจ์†Œ์™€ ์ฒซ ๋ฒˆ์งธ ๋Œ€๊ธฐ ์˜ˆ์•ฝ ์Šน๊ฒฉ์„ ํ•˜๋‚˜์˜ @Transactional ์•ˆ์—์„œ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ž๋™ ์Šน์ธ ํ๋ฆ„์—์„œ๋Š” ReservationSlot์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , ๋‘ Reservation์˜ ์ƒํƒœ๋งŒ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

๋ฐœํ–‰ SQL ๋ฐœ์ทŒ

๋‚ด ์˜ˆ์•ฝ ๋ชฉ๋ก ์กฐํšŒ์—์„œ ๋Œ€๊ธฐ๋ฒˆํ˜ธ๋Š” ์ƒ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ๋กœ ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค.

select
    r.id,
    r.reservation_slot_id,
    r.user_id,
    r.status,
    r.created_at,
    r.updated_at,
    (
        select count(r2.id) + 1
        from reservation r2
        where r2.reservation_slot_id = r.reservation_slot_id
          and r2.status = ?
          and (
              r2.updated_at < r.updated_at
              or (
                  r2.updated_at = r.updated_at
                  and r2.id < r.id
              )
          )
    ) as waiting_number
from reservation r
join users u on u.id = r.user_id
join reservation_slot rs on rs.id = r.reservation_slot_id
join reservation_date rd on rd.id = rs.date_id
join reservation_time rt on rt.id = rs.time_id
join theme t on t.id = rs.theme_id
where u.name = ?
order by rd.date desc, rt.start_at desc, r.id;

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

๋Œ€๊ธฐ๋ฒˆํ˜ธ ๊ณ„์‚ฐ ์œ„์น˜

์ฒ˜์Œ์—๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•œ ๋’ค ์„œ๋น„์Šค์—์„œ ๋Œ€๊ธฐ๋ฒˆํ˜ธ๋ฅผ ๊ณ„์‚ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ๋Œ€๊ธฐ๋ฒˆํ˜ธ๋Š” ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋„๋ฉ”์ธ ๊ทœ์น™์ด๋ผ๊ธฐ๋ณด๋‹ค ์กฐํšŒ ํ™”๋ฉด์— ํ•„์š”ํ•œ ํ‘œ์‹œ ๊ฐ’์— ๊ฐ€๊นŒ์› ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์„œ๋น„์Šค์— ๊ณ„์‚ฐ ๋กœ์ง์„ ๋‘์ง€ ์•Š๊ณ , JPQL ์ƒ์„ฑ์ž ํ‘œํ˜„์‹์œผ๋กœ ReservationWithWaitingNumber๋ฅผ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.
์„œ๋น„์Šค๋Š” ์กฐํšŒ ๊ฒฐ๊ณผ๋ฅผ ์‘๋‹ต์œผ๋กœ ๊ฐ์‹ธ๋Š” ์—ญํ• ๋งŒ ํ•˜๋„๋ก ๋‹จ์ˆœํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

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

์ฒ˜์Œ์—๋Š” JDBC์—์„œ ํ•˜๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ๋Œ€๊ธฐ๋ฒˆํ˜ธ๋‚˜ ์ธ๊ธฐ ํ…Œ๋งˆ count๋ฅผ ์ฟผ๋ฆฌ์—์„œ ๋ฐ”๋กœ DTO๋กœ ๋ฝ‘์œผ๋ ค ํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ JPA๋กœ ์˜ฎ๊ธฐ๋ฉด์„œ select new ...Dto(...)๋ฅผ ์“ฐ๋ฉด DTO์˜ ์ „์ฒด ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ๋ฅผ JPQL ๋ฌธ์ž์—ด ์•ˆ์— ์ ์–ด์•ผ ํ–ˆ๊ณ , ์ƒ์„ฑ์ž ์ธ์ž ์ˆœ์„œ๊นŒ์ง€ ์ฟผ๋ฆฌ์™€ ๊ฐ•ํ•˜๊ฒŒ ๋ฌถ์˜€์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ํ•œ๋•Œ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•œ ๋’ค ์„œ๋น„์Šค์—์„œ ๋Œ€๊ธฐ๋ฒˆํ˜ธ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐ”๊ฟจ์Šต๋‹ˆ๋‹ค.
์ด ๋ฐฉ์‹์€ ์ฟผ๋ฆฌ๊ฐ€ ๋‹จ์ˆœํ•ด์ง€๊ณ  ๋„๋ฉ”์ธ ํ๋ฆ„์€ ์ฝ๊ธฐ ์‰ฌ์› ์ง€๋งŒ, ๋Œ€๊ธฐ๋ฒˆํ˜ธ๊ฐ€ ์‹ค์ œ ์ƒํƒœ ๋ณ€๊ฒฝ ๊ทœ์น™์ด๋ผ๊ธฐ๋ณด๋‹ค ์กฐํšŒ ํ™”๋ฉด์— ํ•„์š”ํ•œ ํ‘œ์‹œ ๊ฐ’์— ๊ฐ€๊นŒ์šด๋ฐ๋„ ์„œ๋น„์Šค๊ฐ€ ๊ณ„์‚ฐ ๋กœ์ง์„ ๋งŽ์ด ๋“ค๊ณ  ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ตญ ๋Œ€๊ธฐ๋ฒˆํ˜ธ๋Š” ์กฐํšŒ ์ „์šฉ ๊ฐ’์ด๋ผ๊ณ  ํŒ๋‹จํ•ด JPQL ์ƒ์„ฑ์ž ํ‘œํ˜„์‹์œผ๋กœ ๋‹ค์‹œ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค.
์„œ๋น„์Šค๋Š” ReservationWithWaitingNumber๋ฅผ ๋ฐ›์•„ ์‘๋‹ต์œผ๋กœ ๊ฐ์‹ธ๋Š” ์—ญํ• ๋งŒ ํ•˜๋„๋ก ์ค„์˜€๊ณ , Repository ์ฟผ๋ฆฌ์—์„œ ๊ฐ™์€ ์Šฌ๋กฏ์˜ ์•ž์„  WAITING ์˜ˆ์•ฝ ์ˆ˜๋ฅผ ์„ธ์–ด waitingNumber๋ฅผ ํ•จ๊ป˜ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

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