๐ฑ Querydsl์์ Entity๋ฅผ DTO๋ก ์๋์ผ๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ๊ณผ @QueryProjection์ ์์กด์ฑ์ ์ ๊ฑฐํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๊ณ ์ฐฐโฆ
๐ค Introโฆ
์ต๊ทผ์ Querydsl์ ๋ํ ์คํฐ๋๋ฅผ ์งํํ๋ฉด์ Querydsl์ ์์ฃผ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์ธ DTO Projections์ ๋ํด์ ๊ฐ๋ตํ๊ฒ ์๊ฐํ๋ค. ํนํ ์ด ์ค @QueryProjection์ด ๋งค์ฐ ํธ๋ฆฌํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ธฐ์ ์ด๊ฑธ ์์ฃผ๋ก ์คํฐ๋๋ฅผ ์งํํ๋๋ฐ, ๋ฌธ๋ ์๋ฌธ์ด ์๊ฒผ๋ค.
๐ซจ โ์ด๊ฑธ ์ฌ์ฉํ๋ฉด QueryDSL์ ์์กด์ฑ์ด ์๊ธด๋คโ๋ ๋ฌธ์ ์ ์ด ์๋๊ฑด ์๊ฒ ๋๋ฐ, ๊ทธ๋ ๋ค๋ฉด ๋ฌธ์ ์ ์ด ๊ณผ์ฐ ์ด๊ฑฐ ํ๋์ผ๊น?
์ด๋ ๊ฒ ๊ฐ๋จํ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋๊ฒ ์๋ค๋ฉด, ๊ตณ์ด Projections.bean์ด๋ผ๋ ๊ฒ์ ์ ์กด์ฌํ๋ ๊ฒ์ผ๊น?
API ๋ช ์ธ์ ๋ค์ด๊ฐ๋ Request, Response ๊ฐ์ ๊ฒฝ์ฐ๋ ์ธ๋ถ ์์์ ์ํ ๋ณ๋์ด ์์ด์ผ ํ๋ค๋ ๊ฒ์ผ๋ก ์๊ณ ์๋๋ฐ, ๊ทธ๋ ๋ค๋ฉด ๋๋ถ๋ถ์ด ์ด Request, Response์ ๋๋ฉ์ธ ์์ญ์ธ Entity๋ฅผ ์ง์ ๋ ธ์ถํ์ง ์๊ธฐ ์ํด์ DTO๋ก ์ด๋ฅผ ๋ณํํด์ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก ์๊ณ ์๋๋ฐ, ์ด DTO์ QueryDSL์์กด์ฑ์ด ์๊ธฐ๋ ๊ฒ์ด ๊ณผ์ฐ โ์์ ํโ๊ฒ์ธ๊ฐ? ์ค๋์ ์ด ๋ถ๋ถ์ ๋ํ ์๋ฌธ์ ํด์ํด๋ณด์!
๐ฉถ Start
์ฐ์ , ์ด ๋ถ๋ถ์ ์์๋ณด๊ธฐ ์ํด โํ๋ก์ ์ โ์ ๊ฐ๋ ์ ๋จผ์ ์์๋ณด๋๋ก ํ์. ํ๋ก์ ์ ์ด๋, select๋ก ๋์์ ์ง์ ํ๋ ๊ฒ์ ๋งํ๋ค. QueryDSL์ ์ด Projections์์, Entity๋ฅผ DTO๋ก ์ฝ๊ฒ ๋ณํํ ์ ์๋๋ก ํ๋ ํ๊ธฐ์ ์ธ ๋ฐฉ์์ ๋ช๊ฐ์ง ์ ๊ณตํ๋ค. ์ด์ ๋ํด ๋จผ์ ์์๋ณด์.
QueryDSL์์ ํ๋ก์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๋ฐฉ๋ฒ
QueryDSL์์ ํ๋ก์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ DTO๋ก ์กฐํํ๋ ๊ฒ, ๊ทธ๋ฆฌ๊ณ ํํ๋ก ์กฐํํ๋ ๊ฒ ๋ ๊ฐ์ง๊ฐ ์๋ค. ํํ์ QueryDSL์์ ์ ๊ณตํ๋ Tuple ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํ๋ ๊ฒ์ด๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
ํ๋ก์ ์
๊ฒฐ๊ณผ ๋ฐํ - ์ฌ๋ฌ๊ฐ (ํํ)
- ํ๋ก์ ์
๊ฒฐ๊ณผ๊ฐ ์ฌ๋ฌ๊ฐ์ผ๋, ํํ๋ก ๋ฐํ
- ํํ์ ๊ฒฝ์ฐ, QueryDSL์์ ์ ๊ณตํ๋ Tuple ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด์ผ ํ๋ฏ๋ก QueryDSL์ ์์กด์ ์ด๋ผ๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
*/
@Test
void ํ๋ก์ ์
_ํํ() throws Exception {
//given
//when
List<Tuple> result = queryFactory
.select(member.username, member.age) // member.username๊ณผ member.age๋ฅผ ์ ํ
.from(member)
.fetch();
//then
for (Tuple tuple : result) {
String username = tuple.get(member.username);
Integer age = tuple.get(member.age);
System.out.println("username = " + username + ", age = " + age);
}
}
์ด๊ฒ ์ญ์ QueryDSL์์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ฏ๋ก, ์ด๋ ๊ฒ ์์กด์ฑ์ ๊ฐ์ง ๊ฐ์ฒด๊ฐ repository ๋ฐ์ผ๋ก ์์ด ๋๊ฐ๋ ๊ฒ์ ์ข์ ์ค๊ณ๊ฐ ์๋๋ค. ์ฌ๊ธฐ์ ๋ง๊ฐ์ ์ด์ฉํด์ ์ ๊ธฐ์ ์์กด์ฑ์ ๊ฐ์ง ๊ฐ์ฒด๊ฐ repository ๋ฐ์ผ๋ก ์์ด ๋๊ฐ๋ ๊ฒ์ด ์ข์ง ์์์ง๋ฅผ ์์๋ณด๋๋ก ํ์.
์ ์์กด์ฑ์ ๊ฐ์ง ๊ฐ์ฒด๊ฐ repository ๋ฐ์ผ๋ก ์์ด ๋๊ฐ๋ ๊ฒ์ด ์ข์ง ์์๊ฑธ๊น?
์ด๋ ๋ ํฌ์งํ ๋ฆฌ ๊ฒฝ๊ณ๋ฅผ ๋์ด์ ์์กด์ฑ์ ๊ฐ์ง ๊ฐ์ฒด(์ฆ, ํ๋ ์ ์ํฌ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์กดํ๋ ์์ํ ์ํ๊ฐ ์๋ ๊ฐ์ฒด๋ฅผ ์๋ฏธํ๋ค.)๊ฐ ์๋น์ค ๋จ์ผ๋ก ์์ด๋๊ฐ๋ฉด, ๊ณ์ธต๊ฐ ๊ฒฐํฉ์ด ์ปค์ง๊ณ ๊ต์ฒด, ํ ์คํธ, ๋ฆฌํฉํฐ๋ง ๋น์ฉ์ด ํญ์ฆํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ฆ, ์ฌ์ค SOLID ์์น์ ์ค์ํ๊ธฐ ์ํด์๋ ๊ฒฐํฉ๋๋ ๋ฎ์ถ๊ณ ์์ง์ฑ์ ๊ฐํ๊ฒ ํ๋ ๊ฒ์ด ๋งค์ฐ ์ค์ํ๋ฐ, ์ด๋ ๊ฒ ํ๋ฉด โ๊ฐํ ๊ฒฐํฉโ์ด ๋๋ค๋ ๋ฌธ์ ์ ์ด ์กด์ฌํ๋ค. ๋ํ, ์์ํ ํ๊ฒฝ์์ ์งํํด์ผ ํ๋ ๋จ์ ํ ์คํธ ์ญ์ ์ด๋ ๊ฒ ์์กด์ฑ์ ๊ฐ์ง ๊ฒฝ์ฐ ์๋นํ ์ ์ฝ์ ๋ฐ๋๋ค.
QueryDSL์์ ํ๋ก์ ์ ๊ฒฐ๊ณผ๋ฅผ DTO๋ก ๋ฐํํ๋ ๋ฐฉ๋ฒ
๊ทธ๋ ๋ค๋ฉด DTO๋ก ์ง์ ๋ฐํํ๋ ๋ฐฉ๋ฒ์ ์ด๋จ๊น? QueryDSL์ ํ๋ก์ ์ ๊ฒฐ๊ณผ๋ฅผ DTO๋ก ๋ฐํํ๊ธฐ ์ํด ๋ค์์ ๋ฐฉ๋ฒ์ ์ ์ํ๊ณ ์๋ค.
- Projections.bean
- Projections.fields
- Projections.constructor
- @QueryProjection
ํ๋์ฉ ์ดํด๋ณด์.
Projections.bean
setter ๋ฉ์๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ ๋ฐฉ์์ด๋ค. ์ฆ, Entity์ ๋ชจ๋ ํ๋๋ฅผ DTO์ setter๋ฅผ ์ด์ฉํด์ ์ฃผ์ ํ๋ ๋ฐฉ์์ด๋ค. ๋ฐ๋ผ์ ์ด ๋ฐฉ์์ ์ด์ฉํ ์ DTO์ ๋ชจ๋ ํ๋์ Setter๊ฐ ํ์ํ๋ค. ๊ทธ๋์ @Setter๋ฅผ ๊ฐ์ด ํ์ฉํด์ผ ํ๋ค. ๋ง์น MyBatis์์ ๋งคํํ๋ ๋ฐฉ์๊ณผ ์ ์ฌํ๋ค. (MyBatis์ญ์ Setter ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ฏ๋ก..)
1
2
3
4
5
6
7
8
//when
//ํ๋กํผํฐ ์ ๊ทผ - Setter
List<MemberDTO> result1 = queryFactory
.select(Projections.bean(MemberDTO.class,
member.username,
member.age))
.from(member)
.fetch();
๊ทธ๋ฌ๋, Setter๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ค๋ ๊ฒ ์์ฒด๊ฐ ์ด ๋ฐฉ์์ ๊ฐ์ฅ ํฐ ๋จ์ ์ด๋ค. Setter๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ, ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ ์ทจ์ฝํ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ง๊ฐ์ ์ด์ฉํด์ ์ Setter๋ฅผ ์ฌ์ฉํ์ง ์๋๊ฒ ์ข์์ง ์์๋ณด๋๋ก ํ์.
์ Setter๋ฅผ ์ง์ํ๋ ๊ฒ์ด ์ข์๊น?
Setter ๊ธฐ๋ฐ์ด๋ผ๋ ๊ฒ์, ๊ฒฐ๊ตญ ๊ฐ์ฒด์ ์ํ๋ฅผ ์ธ๋ถ์์ ๋ฐ๊ฟ ์ ์๊ฒ ๋๋ ๊ฒ์ด๋ผ๋ ๊ฒ์ ์๋ฏธํ๋ค. ์ฆ, Setter๋ฅผ ์ฌ์ฉํ๋ฉด ๋๊ฐ, ์ด๋์๋ ์ง ๊ฐ์ ๋ฐ๊ฟ ์ ์๋ค. ์์ฑ์(๋น๋)๋ฅผ ์ด์ฉํ๋, ์ ๊ทผ ์ ํ์๋ฅผ private, protected๋ฑ์ผ๋ก ์ค์ ํ๋ฉด ์ธ๋ถ ์์ ์์ฑ์ ๋ง์ ์ ์๋๊ฒ์ ๋ฐํด, Setter๋ ์ด๋ฐ ๋ณด์กฐ ๊ธฐ๋ฅ์ ๋ถ์ผ ์ ์์ด์ ๋งค์ฐ ์ทจ์ฝํ๋ค. ์๋ฅผ ๋ค์ด, ์ธ๋ถ์์ ์ํ ๊ณ์ข์ ์ ๊ทผํด์ setBalance(0)๋ฑ์ผ๋ก ์ค์ ์ ํด๋ฒ๋ฆฐ๋ค๋ฉดโฆ? ์๊ฐ๋งํด๋ ๋ฑ๊ณจ์ด ์ค์นํ ๊ฒฝํ์ ํ ๊ฒ์ด๋คโฆ
Projections.fields
์ด ๋ฐฉ์์ ํ๋์ ์ง์ ์ ๊ทผํด์ ๊ฐ์ ์ฃผ์ ํ์ฌ, DTO๋ก ๋ด๋ณด๋ด๋ ๋ฐฉ์์ด๋ค.
1
2
3
4
5
6
7
//ํ๋ ์ง์ ์ ๊ทผ - ํ๋์ ์ง์ ์ ๊ทผํ์ฌ ๊ฐ์ ์ค์
List<MemberDTO> result2 = queryFactory
.select(Projections.fields(MemberDTO.class,
member.username,
member.age))
.from(member)
.fetch();
๋จ, type์ด ๋ค๋ฅผ ๊ฒฝ์ฐ ๋งค์นญ๋์ง ์์ผ๋ฉฐ, dto์์ ์ค์ ํ ๋ณ์นญ๊ณผ ์ํฐํฐ์ ํ๋๋ช ์ด ๋ค๋ฅผ ๊ฒฝ์ฐ, as๋ฑ์ ์ด์ฉํด์ ์ถ๊ฐ์ ์ผ๋ก ๋ง์ถฐ์ค์ผ ํ๋ค๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
1
member.username.as("name")
๋ํ ๊ฒฐ์ ์ ์ผ๋ก, ์ปดํ์ผ ์์ ์ ์๋ฌ๋ฅผ ์ฐพ์ ์๊ฐ ์๋ค!!! ๋ฐํ์ ์๋ฌ๋ง ๋ฐ์ํ๋ค. ์ด๋ ์ปดํ์ผ๋ฌ๊ฐ dto์ ํด๋น ํ๋๋ค์ด ์๋์ง๋ฅผ ์ ํ ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์, ๋ฐํ์์ ์ด๋ฅผ ๋งค์นญํ๊ณ ์ ์๋ํ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ฐ์ธ์ ์ผ๋ก ๋๋ ์ปดํ์ผ ์๋ฌ๋ฅผ ๋งค์ฐ ์ฌ๋ํ๊ณ โฆ.๋ฐํ์ ์๋ฌ๋ฅผ ๋งค์ฐ ์ซ์ดํ๋คโฆ.๋๋ฒ๊น ์ด ์ด๋ ต๋ค ๊ทธ๋์ ์ด ๋จ์ ์ด ์ข ํฌ๊ฒ ๋ค๊ฐ์๋ค.
Projections.constructors
1
2
3
4
5
6
7
//์์ฑ์ ์ฌ์ฉ - ์์ฑ์๋ฅผ ํตํด ๊ฐ์ ์ค์
List<MemberDTO> result3 = queryFactory
.select(Projections.constructor(MemberDTO.class,
member.username,
member.age))
.from(member)
.fetch();
์์ฑ์๋ก ๊ฐ์ ์ค์ ํด์ DTO๋ก ๋ด๋ณด๋ด๋ ๋ฐฉ์์ด๋ค. ์ฆ, ์์ฑ์ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ์ ํ๋ ๊ฒ์ธ๋ฐ ์์ฑ์๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ ์์์ ๋งํ๋ ๊ฒ์ฒ๋ผ, setter์ ๋ค๋ฅด๊ฒ ๊ฐ์ฒด์ ๋ถ๋ณ์ฑ์ ๊ฐ์ ธ๊ฐ ์ ์๋ค๋ ์ฅ์ ์ด ์กด์ฌํ๋ค.
๊ทธ๋ฌ๋, ๊ฐ์ ๋๊ธธ๋ ์์ฑ์์ ํ๋์ ์์๋ฅผ ๋ฐ๋์ ์ผ์น์์ผ์ผ๋ง ํ๋ค๋ ๋จ์ ์ด ์๊ธฐ์, ํ๋ผ๋ฏธํฐ๊ฐ ๋๋ฌด ๋ง์ผ๋ฉด ๋ฐ์ธ๋ฉ ์๋ฌ๊ฐ ๋ฐ์ํ ํ๋ฅ ์ด ๋งค์ฐ ํฌ๋ค ๋ํ, ์ด Projections.constructors ์ญ์ ๋ฐํ์ ์์ ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ํ์์ด๋ผ, ์ปดํ์ผ ์๋ฌ๋ฅผ ์ก์ ์ ์๋ค๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
@QueryProjection
์ด๋ ธํ ์ด์ ์ ์ด์ฉํด์ ํ๋ก์ ์ ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ด๋ค. ์ด ์ด๋ ธํ ์ด์ ์ ์ ์ฉํ DTO์ ์์ฑ์์ ๋ถ์ฌ์ฃผ๋ฉด, QDTO๋ผ๋ ๊ฒ์ ์์ฑํ๋๋ฐ ์ด๋ฅผ ํ์ฉํ๋ฉด ์์ฑ์ ๋ฐฉ์์ผ๋ก ์์ฝ๊ฒ ์ํฐํฐ > DTO ๋ณํ์ด ๊ฐ๋ฅํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
ํ๋ก์ ์
๊ฒฐ๊ณผ ๋ฐํ - @QueryProjection
- ์ด๊ฑธ ์ฌ์ฉํ ๊ฒฝ์ฐ, DTO๋ Qํ์ผ๋ก ์์ฑํด์ ์ฟผ๋ฆฌ ๋ ๋ฆด๋๋ ํ์ฉ์ด ๊ฐ๋ฅํ๋ค!
- ๋ค๋ง, DTO์ @QueryProjection ์ด๋
ธํ
์ด์
์ ๋ถ์ฌ์ผ ํ๋ค. ์ฆ, QueryDSL์ ์์กดํด์ผ ํ๋ค๋ ๋จ์ ์ด ์์.
*/
@Test
void ํ๋ก์ ์
_QueryProjection() throws Exception {
//given
//when
List<MemberDTO> result = queryFactory
.select(new QMemberDTO(member.username, member.age)) // QMemberDTO๋ฅผ ์ฌ์ฉํ์ฌ DTO ์์ฑ
.from(member)
.fetch();
//distinct ์ฌ์ฉ๋ฒ
List<String> resultDist = queryFactory
.select(member.username)
.distinct() // ์ค๋ณต ์ ๊ฑฐ
.from(member)
.fetch();
//then
}
์ด ๋ฐฉ์์ ์ฅ์ ์ โ์ปดํ์ผ ์์ ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค๋ ๊ฒโ์ด๋ค!!! ์ด๋ Querydsl์ด ์ ๋ ธํ ์ด์ ์ ํตํด Qํด๋์ค (QDTO)๋ฅผ ์์ฑํด ์ฃผ๊ธฐ ๋๋ฌธ์, apt๊ฐ ์ด๋ฅผ ํธ์ถํด์ ์์ฑํ๋ ๊ณผ์ ์์ ์๋จ์์ ๋จผ์ ์๋ฌ๋ฅผ ๋ด์ฃผ๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ ๊ฒ ํธ๋ฆฌํ ๊ธฐ๋ฅ์๋ ๋จ์ ์ด ์กด์ฌํ๊ธฐ ๋ง๋ จ์ด๋ค.
์ฐ๋ฆฌ๋ ์์์ ์ธ๋ถ์ ๊ธฐ์ ์ข ์์ ์ธ ๊ฐ์ฒด๋ฅผ ๋ ธ์ถํ๋ฉด ์๋๋ค๋ ๊ฒ์ ๋งํ๋ค ๊ทธ๋ฌ๋ @QueryProjection์ ํนํ Q๊ฐ ๋ถ์ DTO๋ฅผ ์ด์ฉํด์ ๋ณํํ๋ค๋ ์ ์์ ์ด์ ์๋ฒฝํ ์๋ฐฐ๋๋ค. (์ฌ์ค ๋ค๋ฅธ Projections ์ญ์ ๋ง์ฐฌ๊ฐ์ง๋คโฆ.) ๊ทธ๋์ ์ฐ๋ฆฌ๋ ๋ชจ๋ ๋ ์ด์ด์์ ์ด QDTO๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ ์ด ํจํด์์ ๋ฒ์ด๋๋ ๋ฐฉ์์ ๊ณ ์ํ์ฌ ์ด๋ฅผ ํ์ฉํด์ผ ํ๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๋ป๊ฒ @QueryProjection์ ๋น์ข ์์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋?
์ฐ์ @QueryProjection์ด๊ฑธ ์ข ์์ ์ผ๋ก ์ฌ์ฉํ ๋ด ๋ฆฌํฉํฐ๋ง ์ ์ฝ๋๋ฅผ ํ ๋ฒ ์ดํด๋ณด์.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Repository
@RequiredArgsConstructor //์์ฑ์ ์ฃผ์
public class ShortsQueryRepositoryImpl implements ShortsQueryRepository {
private final JPAQueryFactory query; //config ํ์ผ์ ํตํด ๋ฐ๋ก ์ฃผ์
@Override
public List<ShortsResponseDto> searchShorts(String nickname, String keyword) {
return query
.select(
new QShortsResponseDto(
shorts.id,
shorts.shortsName,
shorts.thumbnail
))
.from(shorts)
.where(
nicknameEq(nickname),
keywordContains(keyword)
).orderBy(shorts.shortsName.desc())
.fetch();
}
// --- ์กฐ๊ฑด ๋ฉ์๋ (๋์ ์ฟผ๋ฆฌ) ---
// ์กฐ๊ฑด ๋ฉ์๋๋ผ๋ฆฌ ์กฐํฉํด์ ์ฌ์ฌ์ฉ๋ ๊ฐ๋ฅํ๋ค. (๋ชจ๋ํ๊ฐ ์ ๋์ด ์์)
private BooleanExpression nicknameEq(String nickname) {
return nickname != null ? shorts.customer.nickname.eq(nickname) : null;
}
private BooleanExpression keywordContains(String keyword) {
return keyword != null ? shorts.shortsName.containsIgnoreCase(keyword) : null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Getter
@NoArgsConstructor
@Builder
@ToString
public class ShortsResponseDto {
private Long id;
private String shortsName;
private String thumbnail;
//์ด๋ฅผ ํตํด QDTO๋ฅผ ์์ฑํด์, DTO ๋ณํ ๊ณผ์ ์์ด (QueryDSL์ด ์์์ ํด์ค) ํธํ๊ฒ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
@QueryProjection
public ShortsResponseDto(Long id, String shortsName, String thumbnail) {
this.id = id;
this.shortsName = shortsName;
this.thumbnail = thumbnail;
}
}
๋ณด๋ฉด ์๊ฒ ์ง๋ง, API์ ๋
ธ์ถํ๋ ResponseDto์ Qdto๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ๋ค. ์ด๋ ๊ฒ ํ ๊ฒฝ์ฐ, ํนํ ์ธ๋ถ์ ์ํ ๋ณํ ๊ฐ๋ฅ์ฑ์ ์ต๋ํ ์ ๊ฑฐํด์ผ ํ๋ Requsest, Response์ ํฐ ๋ฌธ์ ..๊ฐ ์๊ธด๋ค. ์ฌ์ค์ ์ธ๋ถ ๋ณํ ์ ํ๋ฅผ ๋ง์ผ๋ ค๊ณ Entity๋ฅผ ์์ฐ๊ธฐ๋ก ํ๊ฑด๋ฐ ์ด๋ฌ๋ฉด ์๋ฏธ๊ฐ ์์ด์ง๋ ๊ฒ์ด๋คโฆ. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ๊ตฌ์กฐ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๊ฟ์ผ ํ๋ค.
์ด๋ฅผ ์ฝ๋์ ๋ฐ์ํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
DTO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
@QueryProjection ์ฌ์ฉ์ ์ํ ์ค๊ฐ ๋ด๋ถ DTO
*/
@Getter
@NoArgsConstructor
@Builder
@ToString
public class FilteredShortsResponse {
private Long id;
private String shortsName;
private String thumbnail;
//์ด๋ฅผ ํตํด QDTO๋ฅผ ์์ฑํด์, DTO ๋ณํ ๊ณผ์ ์์ด (QueryDSL์ด ์์์ ํด์ค) ํธํ๊ฒ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
@QueryProjection
public FilteredShortsResponse(Long id, String shortsName, String thumbnail) {
this.id = id;
this.shortsName = shortsName;
this.thumbnail = thumbnail;
}
}
1
2
3
4
5
6
7
8
9
10
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class ShortsResponse {
private Long id;
private String shortsName;
private String thumbnail;
}
์ปค์คํ Repository
1
2
3
4
public interface ShortsQueryRepository {
//๋์ ์ฟผ๋ฆฌ ์์ : ํน์ ์กฐ๊ฑด์ผ๋ก ์ผ์ธ ๋ฅผ ๊ฒ์ํ๋ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํด๋ณด์.
List<FilteredShortsResponse> searchShorts(String nickname, String keyword);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Repository
@RequiredArgsConstructor //์์ฑ์ ์ฃผ์
public class ShortsQueryRepositoryImpl implements ShortsQueryRepository {
private final JPAQueryFactory query; //config ํ์ผ์ ํตํด ๋ฐ๋ก ์ฃผ์
@Override
public List<FilteredShortsResponse> searchShorts(String nickname, String keyword) {
return query
.select(
new QFilteredShortsResponse(
shorts.id,
shorts.shortsName,
shorts.thumbnail
))
.from(shorts)
.where(
nicknameEq(nickname),
keywordContains(keyword)
).orderBy(shorts.shortsName.desc())
.fetch();
}
// --- ์กฐ๊ฑด ๋ฉ์๋ (๋์ ์ฟผ๋ฆฌ) ---
// ์กฐ๊ฑด ๋ฉ์๋๋ผ๋ฆฌ ์กฐํฉํด์ ์ฌ์ฌ์ฉ๋ ๊ฐ๋ฅํ๋ค. (๋ชจ๋ํ๊ฐ ์ ๋์ด ์์)
private BooleanExpression nicknameEq(String nickname) {
return nickname != null ? shorts.customer.nickname.eq(nickname) : null;
}
private BooleanExpression keywordContains(String keyword) {
return keyword != null ? shorts.shortsName.containsIgnoreCase(keyword) : null;
}
}
JPA์ Querydsl์ ์๋ฉด ์์๋ก ์ฌ์คํ ๊ธฐ์ ์ธ ๊ฒ ๊ฐ๋ค. ๋ง์น ๊ฐ์ฒด์งํฅ์ ์ถ๊ตฌํ๊ธฐ ์ํ ๊ฐ๋ฐ์๋ค์ ์ฌ์คํ ๋ ธ๋ ฅ์ด ๋๊ปด์ง๋ฌ๊น ์ด๋ฐ ๊ธฐ์ ์ ์จ๋ณผ ์ ์๋ค๋๊ฑฐ ์์ฒด๊ฐ ์ฐธ ํ์ด์ธ ๊ฒ ๊ฐ๋คโฆ.๊ตณ
์ฐธ๊ณ ์๋ฃ
๐ค[Querydsl] Querydsl๊ณผ DTO, @QueryProjection ๋น์ข ์์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ