Skip to content

Latest commit

ย 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
ย 
ย 

README.md

QueryDSL

DSL, Fluent API์™€ QueryDSL

1. DSL

  • Domain-Specific Language. ๋„๋ฉ”์ธ ํŠนํ™” ์–ธ์–ด.
  • ๊ฐ€์žฅ ํฐ ์ƒ์œ„ ๊ฐœ๋….
  • Java๋‚˜ Python ๊ฐ™์€ ๋ฒ”์šฉ ์–ธ์–ด์™€ ๋‹ฌ๋ฆฌ ํŠน์ • ๋ชฉ์ ์—๋งŒ ์‚ฌ์šฉ๋˜๋„๋ก ์„ค๊ณ„๋œ ์–ธ์–ด์ด๋‹ค.
  • ๋†’์€ ๊ฐ€๋…์„ฑ๊ณผ ๊ฐ„๊ฒฐํ•จ์„ ๊ฐ„๊ฒฐํ•จ์„ ๊ฐ€์ง„๋‹ค.
  • ์•„๋ž˜ 2๊ฐ€์ง€์˜ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค.
    • External DSL: ๋ณ„๋„์˜ ๋ฌธ๋ฒ•์„ ๊ฐ€์ง„ ์™„์ „ํ•œ ๋…๋ฆฝ ์–ธ์–ด (์˜ˆ: SQL, HTML, CSS, ์ •๊ทœํ‘œํ˜„์‹ ๋“ฑ๋“ฑ)
    • Internal DSL: Java๋‚˜ Kotlin ๊ฐ™์€ ๊ธฐ์กด ์–ธ์–ด์˜ ๋ฌธ๋ฒ• ๋‚ด์—์„œ, ํŠน์ • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ API๋ฅผ ํ†ตํ•ด DSL์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ๋งŒ๋“  ๊ฒƒ (์˜ˆ: QueryDSL, Gradle Groovy ๋“ฑ๋“ฑ)

2. Fluent API (Fluent Interface)

  • Internal DSL์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๋””์ž์ธ ํŒจํ„ด.
  • ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๋Œ€๋ถ€๋ถ„์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์ž๊ธฐ ์ž์‹ (this)์ด๋‚˜ ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ์‚ฌ์šฉํ•  ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
  • ์ด๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ . ์œผ๋กœ ์—ฐ๊ฒฐํ•˜์—ฌ ๋งˆ์น˜ ํ•˜๋‚˜์˜ ๋ฌธ์žฅ์ฒ˜๋Ÿผ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋™์ผํ•œ ๋‚ด์šฉ์˜ ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ณ  ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

3. QueryDSL

  • QueryDSL์€ ์œ„ ๋‘ ๊ฐ€์ง€ ๊ฐœ๋…์„ ํ•ฉ์นœ ํŠน์ • ์ž๋ฐ”/์ฝ”ํ‹€๋ฆฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ.
  • Java ์ฝ”๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ํƒ€์ž…์— ์•ˆ์ „ํ•œ(type-safe) ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” Internal DSL์ด๋‹ค.
  • ์ฆ‰, ๊ธฐ๋ณธ์ ์ธ ๋ฌธ๋ฒ•์€ ๋ชจ๋‘ JPQL์„ ๋”ฐ๋ผ๊ฐ„๋‹ค.
  • QueryDSL์€ ์™„์ „ํžˆ ์ƒˆ๋กœ์šด ์ฟผ๋ฆฌ ์–ธ์–ด๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฌธ์ž์—ด๋กœ ์ง์ ‘ ์ž‘์„ฑํ•˜๋˜ JPQL ์ฟผ๋ฆฌ๋ฌธ์„ Java ๊ฐ์ฒด์™€ ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•ด ๋Œ€์‹  ์กฐ๋ฆฝํ•ด์ฃผ๋Š” ๋„๊ตฌ์ด๋‹ค!
  • Fluent API ํŒจํ„ด(๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹)์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์ถ•ํ•œ๋‹ค.
  • QueryDSL์€ Q-Type์ด๋ผ๋Š” ์ปดํŒŒ์ผ ์‹œ์ ์— ์ƒ์„ฑ๋˜๋Š” ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, ์ž๋ฐ” ์ฝ”๋“œ(๊ฐ์ฒด์™€ ๋ฉ”์„œ๋“œ)๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  • ๋งŒ์•ฝ ์ฟผ๋ฆฌ ๋‚ด์—์„œ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฐ์ฒด๋‚˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฆ‰์‹œ ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

Fluent API๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์žฅ์ 

1. IDE์˜ ์ฝ”๋“œ ์ž๋™ ์™„์„ฑ ๊ธฐ๋Šฅ ์‚ฌ์šฉ

์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ IED๊ฐ€ ๋‹ค์Œ์— ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ๋ชฉ๋ก์„ ์ž๋™์œผ๋กœ ๋ณด์—ฌ์ค€๋‹ค.

2. ๋ฌธ๋ฒ•์ ์œผ๋กœ ์ž˜๋ชป๋œ ์ฟผ๋ฆฌ๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Œ

์ฟผ๋ฆฌ ๋ฌธ๋ฒ•์˜ ์˜ค๋ฅ˜๋ฅผ ์ปดํŒŒ์ผ ์‹œ์ ์— ์žก์•„๋‚ด์ค€๋‹ค. ๋ฌธ์ž์—ด ์ฟผ๋ฆฌ๋ฌธ(JPQL)์˜ ๊ฒฝ์šฐ ์ปดํŒŒ์ผ ์‹œ์ ์— ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๋ฅผ ์žก์ง€ ๋ชปํ•˜๊ณ  ๋Ÿฐํƒ€์ž„์ด ๋˜์–ด ํ•ด๋‹น ์ฟผ๋ฆฌ๊ฐ€ DB์— ์ „์†กํ•  ๋•Œ๊ฐ€ ๋˜์–ด์„œ์•ผ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

3. ๋„๋ฉ”์ธ ํƒ€์ž…๊ณผ ํ”„๋กœํผํ‹ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Œ

๋„๋ฉ”๋‹ ๊ฐ์ฒด ํ•„๋“œ๋ช…์˜ ์˜ค๋ฅ˜๋‚˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์˜ ์˜ค๋ฅ˜๋ฅผ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์žก์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

4. ๋„๋ฉ”์ธ ํƒ€์ž…์˜ ๋ฆฌํŒฉํ† ๋ง์„ ๋” ์ž˜ ํ•  ์ˆ˜ ์žˆ์Œ

์ฝ”๋“œ์˜ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ ์ฟผ๋ฆฌ ์ฝ”๋“œ๊นŒ์ง€ ํ•จ๊ป˜ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. IDE์˜ ๋ฆฌํŽ™ํ† ๋ฆฌ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด ํ•„๋“œ๋ช…์„ ๋ฐ”๊พธ๋ฉด ์ฟผ๋ฆฌ ์ฝ”๋“œ์˜ ํ•„๋“œ๋ช… ์—ญ์‹œ ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋œ๋‹ค.

์ฆ‰, ๋Ÿฐํƒ€์ž„์—์„œ๋‚˜ ์žก์„ ์ˆ˜ ์žˆ์—ˆ๋˜ ์˜ค๋ฅ˜๋ฅผ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์žก์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค!

๋™์  ์ฟผ๋ฆฌ

๋™์  ์ฟผ๋ฆฌ๋Š” ๋Ÿฐํƒ€์ž„์‹œ์˜ ์ž…๋ ฅ์— ๋”ฐ๋ผ WHERE / JOIN / ORDER BY ๋“ฑ์˜ ์กฐ๊ฑด์„ ๋™์ ์œผ๋กœ ๊ตฌ์„ฑํ•ด SQL์„ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹จ, ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋ฌธ์ž์—ด์— ๊ฒฐํ•ฉํ•  ์‹œ SQL ์ธ์ ์…˜์„ ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค.

Qํด๋ž˜์Šค

Qํด๋ž˜์Šค๋Š” QueryDSL์—์„œ ์—”ํ‹ฐํ‹ฐ(Entity)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ์ฟผ๋ฆฌ ์ „์šฉ ํด๋ž˜์Šค์ด๋‹ค. Qํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์ด์ ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • ํƒ€์ž… ์•ˆ์ •์„ฑ : ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์—”ํ‹ฐํ‹ฐ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•จ์œผ๋กœ ์ž˜๋ชป๋œ ํ•„๋“œ๋‚˜ ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋Ÿฐํƒ€์ž„ ์‹œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค.
  • ์ž๋™ ์™„์„ฑ : IDE๊ฐ€ ์ž๋™์™„์„ฑ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.
  • Fluent Interface ์Šคํƒ€์ผ : ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ๊ธฐ ์‰ฌ์šด ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Internal DSL ํšจ๊ณผ : ์ž๋ฐ”์˜ ์ฝ”๋“œ ์•ˆ์—์„œ ๋„๋ฉ”์ธ ํŠนํ™” ์–ธ์–ด์ฒ˜๋Ÿผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Qํด๋ž˜์Šค๋ฅผ ๊ทธ๋Œ€๋กœ ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ ค์„œ๋Š” ์•ˆ ๋œ๋‹ค!!

Qํด๋ž˜์Šค๋ฅผ ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ฆฌ๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ 

  • Qํด๋ž˜์Šค๋Š” ๊ฐ์ฒด ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ž๋ฐ” ๋ฒ„์ „๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ์ƒ์„ฑ๋œ๋‹ค.
  • ๋”ฐ๋ผ์„œ ํŒ€์›๋งˆ๋‹ค Qํด๋ž˜์Šค๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์—†๋‹ค!
  • ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊นƒํ—ˆ๋ธŒ์— ๋ฌด์‹œํ•  ํŒŒ์ผ์„ ์„ค์ •ํ•˜๋Š” .gitignore ํŒŒ์ผ์— Qํด๋ž˜์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ํด๋”(์˜ˆ: build/generated/ ๋˜๋Š” target/generated-sources/)๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ

QueryDSL์—์„œ FetchJoin ํ•˜๋Š” ๋ฒ•

  • ์•ž์„œ ์„ค๋ช…ํ–ˆ๋“ฏ์ด QueryDSL๋Š” JPQL์˜ ๋ฌธ๋ฒ•์„ ๋”ฐ๋ฅธ๋‹ค!
  • ํ•„ํ„ฐ๋ง์„ ์œ„ํ•œ join ๋ฉ”์†Œ๋“œ์™€ ์‹ค์ œ ํ…Œ์ด๋ธ”์„ ๊ฒฐํ•ฉํ•˜๊ธฐ ์œ„ํ•œ fetch join์ด ๋ณ„๊ฐœ๋กœ ์กด์žฌํ•œ๋‹ค.
  • ์ด ์ค‘ fetch join๋ฅผ ์‚ฌ์šฉํ•ด์•ผ N+1 ๋ฌธ์ œ ์—†์ด ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

DTO ๋งคํ•‘ ๋ฐฉ์‹

  • DTO ์ƒ์„ฑ์ž @QueryProjection ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
  • ์ด ๋ฐฉ์‹์€ Projections.constructor์™€ ๋‹ฌ๋ฆฌ ์ปดํŒŒ์ผ ์‹œ์ ์— DTO ์ƒ์„ฑ์ž์˜ ํƒ€์ž…์ด๋‚˜ ์ธ์ž ๊ฐœ์ˆ˜๊ฐ€ ์ž˜๋ชป๋˜๋ฉด ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์„œ ์•ˆ์ „ํ•˜๋‹ค.
  • @QueryProjection ์‚ฌ์šฉ ํ›„ Gradle/Maven์œผ๋กœ ์ปดํŒŒ์ผํ•˜๋ฉด QMemberDto ํด๋ž˜์Šค๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋œ๋‹ค.
  • @QueryProjection๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋– ํ•œ DTO๊ฐ€ ๋‹ค๋ฅธ DTO๋ฅผ ํ•„๋“œ๋กœ ๊ฐ€์ง€๋Š” ๊ตฌ์กฐ(DTO์•ˆ์˜ DTO)๋ฅผ ์ฟผ๋ฆฌ ํ•œ ๋ฒˆ์œผ๋กœ ์‰ฝ๊ฒŒ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋‹ค.
// QMember๊ฐ€ QTeamDto๋ฅผ ํ•„๋“œ๋กœ ๊ฐ€์งˆ ๋•Œ

queryFactory.select(
        new QMemberDTO(
                member.name,
                new QTeamDto(team.name, team.location)))

์ปค์Šคํ…€ ํŽ˜์ด์ง€๋„ค์ด์…˜

  • JPA์˜ Pageable ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต๊ฑฐ๋‚˜ ๋ณต์žกํ•œ join์ด ํ•„์š”ํ•  ๋•Œ Pageable ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ˆ˜๋™์œผ๋กœ ํŽ˜์ด์ง€๋„ค์ด์…˜์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.
  • ์•„๋ž˜ 2๊ฐœ์˜ ์ฟผ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
  • ๋‚ด์šฉ ์กฐํšŒ ์ฟผ๋ฆฌ: offset()๊ณผ limit()์„ ์‚ฌ์šฉ
  • ์ „์ฒด ๊ฐœ์ˆ˜ ์กฐํšŒ ์ฟผ๋ฆฌ: select(member.count())๋ฅผ ์‚ฌ์šฉ
// Predicate ๊ฐ์ฒด, Pageable ๊ฐ์ฒด๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์™”๋‹ค๊ณ  ๊ฐ€์ •

Pageable pageable = PageRequest.of(0, 10); // 0๋ฒˆ ํŽ˜์ด์ง€, 10๊ฐœ์”ฉ

// 1. ๋‚ด์šฉ ์กฐํšŒ ์ฟผ๋ฆฌ
List<Member> content = queryFactory
        .selectFrom(member)
        .orderBy(member.name.desc())
        .offset(pageable.getOffset())   // ํŽ˜์ด์ง• ๊ณผ์ •์„ ์ˆ˜๋™์œผ๋กœ ์ž‘์„ฑ
        .limit(pageable.getPageSize())
        .fetch();

// 2. ์ „์ฒด ๊ฐœ์ˆ˜ ์กฐํšŒ ์ฟผ๋ฆฌ
Long total = queryFactory
        .select(member.count())
        .from(member)
        .where(predicate) // content ์ฟผ๋ฆฌ์˜ where ์กฐ๊ฑด๊ณผ ๋™์ผํ•ด์•ผ ํ•จ
        .fetchOne();

// 3. PageImpl ๊ฐ์ฒด๋กœ ์กฐํ•ฉํ•˜์—ฌ ๋ฐ˜ํ™˜
return new PageImpl<>(content, pageable, total);
  • ์›ํ•˜๋Š” ์ฟผ๋ฆฌ๋ฌธ์— ํŽ˜์ด์ง• ์‹œ์Šคํ…œ์„ ํ•œ ๋ฒˆ ๋” ๋ง์”Œ์šฐ๋Š” ๋А๋‚Œ์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
  • PageImpl ๊ฐ์ฒด๋Š” ํŽ˜์ด์ง•์„ ํ†ตํ•ด DB์—์„œ ์ž˜๋ผ ์˜จ 10๊ฐœ์˜ ๋ฐ์ดํ„ฐ(content)์™€ ์ „์ฒด ๊ฐœ์ˆ˜๊ฐ€ 100๋งŒ ๋ช‡ ๊ฐœ์ธ์ง€๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ(count)๋ฅผ ํ•ฉ์ณ์„œ ํฌ์žฅํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
  • ์ด๋ฅผ ํ†ตํ•ด List๋งŒ์œผ๋กœ๋Š” ์•Œ ์ˆ˜ ์—†๋Š” ํŽ˜์ด์ง€์— ๊ด€ํ•œ ์ •๋ณด๋ฅผ ํ”„๋ก ํŠธ์—๊ฒŒ ๋„˜๊ธธ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

transform - groupBy

  • ์กฐํšŒ ๊ฒฐ๊ณผ๋ฅผ Map ๊ฐ™์€ ์ปฌ๋ ‰์…˜์œผ๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ, ํŠนํžˆ 1:N ๊ด€๊ณ„๋ฅผ DTO๋กœ ๋งคํ•‘ํ•  ๋•Œ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์ด๋‹ค
  • ์ผ๋ฐ˜์ ์ธ JPQL์€ SELECT ์ ˆ์—์„œ ์ปฌ๋ ‰์…˜์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์—†๋‹ค.
  • ํ‘œ์ค€ JPQL์˜ SELECT ์ ˆ์€ ์—”ํ‹ฐํ‹ฐ๋‚˜ DTO ๊ฐ™์€ ๋‹จ์ผ ๊ฐ์ฒด๋งŒ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • SELECT new TeamDto(..., List<MemberDto>)์ฒ˜๋Ÿผ DTO ์ƒ์„ฑ์ž์— ์ปฌ๋ ‰์…˜(List)์„ ์ง์ ‘ ๋„ฃ์„ ์ˆ˜ ์—†๋‹ค๋Š” ๋œป์ด๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜ QueryDSL์€ transform์œผ๋กœ ์ด๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.
  • transform์€ map์œผ๋กœ ๋ฐ˜ํ™˜๋œ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์ผ๋‹จ ๋ฉ”๋ชจ๋ฆฌ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค. ์ดํ›„ QueryDSL์ด ์ด ๋ฐ์ดํ„ฐ๋ฅผ GroupBy.groupBy(...).as(...)์— ๋งž์ถฐ ์›ํ•˜๋Š” ๊ตฌ์กฐ๋กœ ์žฌ์กฐ๋ฆฝํ•ด์ค€๋‹ค.
  • ๋‹จ, transform์€ DB์—์„œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๋‹จ ๊ฐ€์ ธ์™€์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๊ทธ๋ฃนํ•‘ํ•˜๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ˆ˜์‹ญ๋งŒ ๊ฑด ์ด์ƒ์œผ๋กœ ๋งค์šฐ ๋งŽ์„ ๊ฒฝ์šฐ ์„ฑ๋Šฅ ์ €ํ•˜์˜ ์›์ธ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

order by null

  • ์ •๋ ฌ ๋ฉ”์†Œ๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ ฌํ•˜๋ ค ํ•  ๋•Œ ํ•ด๋‹น ์ •๋ ฌ ๊ธฐ์ค€์ด NULL์ธ ๊ฐ’๋“ค์„ ๋ชจ๋“  ๊ฐ’์˜ ์ œ์ผ ์•ž์— ๋‘˜์ง€, ์ œ์ผ ๋’ค์— ์ค„ ์ง€๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
  • ๋งจ ์•ž์—์ด๋ผ๋ฉด nullsFirst(), ๋งจ ๋’ค๋ผ๋ฉด nullsLast()๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • DB๋งˆ๋‹ค NULL์˜ ๊ธฐ๋ณธ ์ •๋ ฌ ์ˆœ์„œ๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์—(ex. MySQL/H2๋Š” ๋งจ ์•ž, Oracle์€ ๋งจ ๋’ค) ์ผ๊ด€๋œ ์ •๋ ฌ์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

โœ… ๋ฏธ์…˜ ๊ธฐ๋ก

  • ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ๋ฆฌ๋ทฐ ๋ณด๊ธฐ API๋ฅผ QueryDSL๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ
  • ํ•„ํ„ฐ๋ง ์กฐ๊ฑด : ๊ฐ€๊ฒŒ๋ณ„, ๋ณ„์ ๋ณ„
  • ์ œ์•ฝ ์กฐ๊ฑด : ํ•˜๋‚˜์˜ API๋กœ ์„ค๊ณ„ํ•  ๊ฒƒ

1๏ธโƒฃ reviewRepositoryQueryDsl ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ

public interface reviewRepositoryQueryDsl {
    
    List<ShopReview> searchShopReview(Predicate predicate);
}
  • QueryDSL๋กœ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์€ ๋ฉ”์†Œ๋“œ๋“ค์„ ์ธํ„ฐํŽ˜์ด์Šค ์•ˆ์—์„œ ๋ฏธ๋ฆฌ ์ •์˜ํ•œ๋‹ค.
  • ์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ๋Š” ์ž๋™์œผ๋กœ public์ด๊ณ  abstract๊ฐ€ ๋œ๋‹ค.
  • ๋”ฐ๋ผ์„œ public์ด๊ณ  abstract๋“ฑ์˜ ํ‚ค์›Œ๋“œ ์ž‘์„ฑ์€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.
  • ์ธ์ž๋กœ ๋ฐ›๊ฒŒ ๋˜๋Š” Predicate ํƒ€์ž…์˜ ๊ฒฝ์šฐ queryDSL์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ธ์ง€ ์•„๋‹Œ์ง€ ์ž˜ ๋ณด๊ณ  importํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

2๏ธโƒฃ reviewRepositoryQueryDsl๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ตฌํ˜„ ํด๋ž˜์Šค ์ž‘์„ฑ

@Repository
@RequiredArgsConstructor
public class reviewRepositoryQueryDslImpl implements reviewRepositoryQueryDsl {

    private final EntityManager em;

    @Override
    public List<ShopReview> searchShopReview(Predicate predicate){

        // JPA ์„ธํŒ…
        JPAQueryFactory queryFactory = new JPAQueryFactory(em);

        // Qํด๋ž˜์Šค ์„ ์–ธ
        QShopReview review = QShopReview.shopReview;
        QShop shop = QShop.shop;
        QMember member = QMember.member;

        return queryFactory
                .selectFrom(review)
                .join(review.shop, shop).fetchJoin()
                .join(review.member, member)
                .where(predicate)
                .fetch();
    }
}
  • ํ˜„์žฌ ๋ณ„์ ์€ ShopReview ์—”ํ‹ฐํ‹ฐ ์•ˆ์— ์กด์žฌ โ†’ Qํด๋ž˜์Šค๋ฅผ ํ†ตํ•œ join์ด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.
  • join์ด ํ•„์š”ํ•œ shop๊ณผ member์˜ ๊ฒฝ์šฐ Qํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด joinํ•œ๋‹ค.
  • ์ด๋•Œ join์€ ํ•„ํ„ฐ๋ง(where์ ˆ)์„ ์œ„ํ•œ ๊ฒƒ์ด๊ณ  fetchJoin์„ ์‚ฌ์šฉํ•ด์•ผ N+1 ๋ฌธ์ œ ์—†์ด ํ…Œ์ด๋ธ”์„ ๊ฒฐํ•ฉํ•ด์„œ ๊ฐ€์ ธ์˜จ๋‹ค.
  • ๋ชจ๋“  ๊ฒ€์ƒ‰์˜ ์กฐ๊ฑด์€ Service์—์„œ ์ „๋‹ฌ๋ฐ›์€ Predicate๋ฅผ ํ†ตํ•ด ๊ฒฐ์ •๋œ๋‹ค.
  • ์–ด๋–ค member์˜ ๋ฆฌ๋ทฐ์ธ์ง€, ์–ด๋–ค ๊ฐ€๊ฒŒ์˜ ๋ฆฌ๋ทฐ์ธ์ง€, ์–ด๋–ค ๋ณ„์ ์„ ๊ฐ€์ง„ ๋ฆฌ๋ทฐ์ธ์ง€๋Š” ์„œ๋น„์Šค์—์„œ ๊ฒฐ์ •๋˜๋Š” ๊ฒƒ.

3๏ธโƒฃ reviewRepository์— JPA์™€ QueryDsl ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋‘˜ ๋‹ค ์ƒ์†

public interface reviewRepository extends JpaRepository<ShopReview, Long>, reviewRepositoryQueryDsl {...}
  • ๋ฉ”์ธ ์ธํ„ฐํŽ˜์ด์Šค์ธ reviewRepository๊ฐ€ JpaRepository์™€ ์ง์ ‘ ๋งŒ๋“  reviewRepositoryQueryDsl๋ฅผ ๋‘˜ ๋‹ค ์ƒ์†
  • ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ๋Š” ReviewRepository ํ•˜๋‚˜๋งŒ ์ฃผ์ž…๋ฐ›์œผ๋ฉด reviewRepository.save() (JPA ๊ธฐ๋ณธ ๊ธฐ๋Šฅ)์™€ reviewRepository.searchShopReview() (๋‚ด๊ฐ€ ๋งŒ๋“  QueryDSL ๊ธฐ๋Šฅ)๋ฅผ ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.