-
Notifications
You must be signed in to change notification settings - Fork 1
[WTH-65] @Query 기반 JPQL을 QueryDSL로 리팩토링 #228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
The head ref may contain hidden characters: "WTH-65-weeth-\uCFFC\uB9AC-\uCD5C\uC801\uD654-\uC791\uC5C5"
Changes from all commits
99e2639
63030e3
d369f79
d805a91
0f71fbd
90695aa
bd2642b
a6647fe
4997e03
33f8145
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package leets.weeth.domain.board.domain.repository; | ||
|
|
||
| import java.util.Collection; | ||
| import java.util.List; | ||
|
|
||
| import org.springframework.data.domain.Pageable; | ||
| import org.springframework.data.domain.Slice; | ||
| import org.springframework.data.repository.query.Param; | ||
|
|
||
| import leets.weeth.domain.board.domain.entity.Post; | ||
| import leets.weeth.domain.board.domain.entity.enums.Category; | ||
| import leets.weeth.domain.board.domain.entity.enums.Part; | ||
|
|
||
| public interface PostRepositoryCustom { | ||
|
|
||
| Slice<Post> findRecentPart(Pageable pageable); | ||
| Slice<Post> findRecentEducation(Pageable pageable); | ||
| Slice<Post> searchPart(String kw,Pageable pageable); | ||
| Slice<Post> searchEducation(String kw, Pageable pageable); | ||
| List<String> findDistinctStudyNamesByPart(Part part); | ||
| Slice<Post> findByPartAndOptionalFilters(Part part, Category category, Integer cardinal, String studyName, Integer week, Pageable pageable); | ||
| Slice<Post> findByCategoryAndOptionalCardinalWithPart(String partName, Category category, Integer cardinal, Pageable pageable); | ||
| Slice<Post> findByCategoryAndCardinalNumberWithPart(String partName, Category category, Integer cardinal, Pageable pageable); | ||
| Slice<Post> findByCategoryAndCardinalInWithPart(String partName, Category category, Collection<Integer> cardinals, Pageable pageable); | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,279 @@ | ||||||||
| package leets.weeth.domain.board.domain.repository; | ||||||||
|
|
||||||||
| import java.beans.Expression; | ||||||||
| import java.util.Collection; | ||||||||
| import java.util.List; | ||||||||
|
|
||||||||
| import org.springframework.data.domain.Pageable; | ||||||||
| import org.springframework.data.domain.Slice; | ||||||||
| import org.springframework.data.domain.SliceImpl; | ||||||||
| import org.springframework.data.repository.query.Param; | ||||||||
| import org.springframework.security.core.parameters.P; | ||||||||
| import org.springframework.stereotype.Repository; | ||||||||
|
|
||||||||
| import com.querydsl.core.BooleanBuilder; | ||||||||
| import com.querydsl.core.types.dsl.BooleanExpression; | ||||||||
| import com.querydsl.core.types.dsl.Expressions; | ||||||||
| import com.querydsl.jpa.impl.JPAQueryFactory; | ||||||||
|
|
||||||||
| import jakarta.persistence.EntityManager; | ||||||||
| import leets.weeth.domain.board.domain.entity.Post; | ||||||||
| import leets.weeth.domain.board.domain.entity.QPost; | ||||||||
| import leets.weeth.domain.board.domain.entity.enums.Category; | ||||||||
| import leets.weeth.domain.board.domain.entity.enums.Part; | ||||||||
| import lombok.RequiredArgsConstructor; | ||||||||
|
|
||||||||
|
|
||||||||
| public class PostRepositoryImpl implements PostRepositoryCustom { | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @repository 어노테이션이 누락되었습니다. 클래스에 다음 diff를 적용하세요: +@Repository
public class PostRepositoryImpl implements PostRepositoryCustom {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 방식의 구현법은 공식문서를 기반으로 구현해주신 걸까요?? 단순 궁금
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://sjh9708.tistory.com/174#google_vignette |
||||||||
|
|
||||||||
| private final JPAQueryFactory queryFactory; | ||||||||
| private final QPost post = QPost.post; | ||||||||
|
|
||||||||
| public PostRepositoryImpl(EntityManager em) { | ||||||||
| this.queryFactory = new JPAQueryFactory(em); | ||||||||
| } | ||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
| @Override | ||||||||
| public Slice<Post> findRecentPart(Pageable pageable) { | ||||||||
| List<Post> result = queryFactory | ||||||||
| .selectFrom(post) | ||||||||
| .where(post.category.in(Category.StudyLog, Category.Article)) | ||||||||
| .orderBy(post.id.desc()) | ||||||||
| .offset(pageable.getOffset()) | ||||||||
| .limit(pageable.getPageSize() + 1) | ||||||||
| .fetch(); | ||||||||
|
|
||||||||
| boolean hasNext = result.size() > pageable.getPageSize(); | ||||||||
|
|
||||||||
| if (hasNext) { | ||||||||
| result.remove(pageable.getPageSize()); | ||||||||
| } | ||||||||
|
|
||||||||
| return new SliceImpl<>(result, pageable, hasNext); | ||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public Slice<Post> searchPart(String kw, Pageable pageable) { | ||||||||
| List<Post> result = queryFactory | ||||||||
| .selectFrom(post) | ||||||||
| .where(post.category.in(Category.StudyLog, Category.Article) | ||||||||
| .and(post.title.containsIgnoreCase(kw) | ||||||||
| .or(post.content.containsIgnoreCase(kw)) | ||||||||
|
Comment on lines
+60
to
+61
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 테스트 해보았는데 아무것도 입력하지 않는 경우 호출이 불가합니다! 따라서 npe발생이 안될거라 예상됩니당 Controller 확인 결과 , RequestParam이 붙여져있어서 괜찮을 것 같습니다! |
||||||||
| )) | ||||||||
| .orderBy(post.id.desc()) | ||||||||
| .offset(pageable.getOffset()) | ||||||||
| .limit(pageable.getPageSize() + 1) | ||||||||
| .fetch(); | ||||||||
|
|
||||||||
| boolean hasNext = result.size() > pageable.getPageSize(); | ||||||||
|
|
||||||||
| if (hasNext) { | ||||||||
| result.remove(pageable.getPageSize()); | ||||||||
| } | ||||||||
|
|
||||||||
| return new SliceImpl<>(result, pageable, hasNext); | ||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public Slice<Post> findRecentEducation(Pageable pageable) { | ||||||||
| List<Post> result = queryFactory | ||||||||
| .selectFrom(post) | ||||||||
| .where(post.category.eq(Category.Education)) | ||||||||
| .orderBy(post.id.desc()) | ||||||||
| .offset(pageable.getOffset()) | ||||||||
| .limit(pageable.getPageSize() + 1) | ||||||||
| .fetch(); | ||||||||
|
|
||||||||
| boolean hasNext = result.size() > pageable.getPageSize(); | ||||||||
|
|
||||||||
| if (hasNext) { | ||||||||
| result.remove(pageable.getPageSize()); | ||||||||
| } | ||||||||
|
|
||||||||
| return new SliceImpl<>(result, pageable, hasNext); | ||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public Slice<Post> searchEducation(String kw, Pageable pageable) { | ||||||||
| List<Post> result = queryFactory | ||||||||
| .selectFrom(post) | ||||||||
| .where( | ||||||||
| post.category.eq(Category.Education) | ||||||||
| .and( | ||||||||
| post.title.containsIgnoreCase(kw) | ||||||||
| .or(post.content.containsIgnoreCase(kw)) | ||||||||
| ) | ||||||||
| ) | ||||||||
| .orderBy(post.id.desc()) | ||||||||
| .offset(pageable.getOffset()) | ||||||||
| .limit(pageable.getPageSize() + 1) | ||||||||
| .fetch(); | ||||||||
|
|
||||||||
| boolean hasNext = result.size() > pageable.getPageSize(); | ||||||||
|
|
||||||||
| if (hasNext) { | ||||||||
| result.remove(pageable.getPageSize()); | ||||||||
| } | ||||||||
|
|
||||||||
| return new SliceImpl<>(result, pageable, hasNext); | ||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public List<String> findDistinctStudyNamesByPart(Part part) { | ||||||||
|
|
||||||||
| return queryFactory | ||||||||
| .select(post.studyName) | ||||||||
| .distinct() | ||||||||
| .from(post) | ||||||||
| .where( | ||||||||
| part == Part.ALL | ||||||||
| ? post.studyName.isNotNull() | ||||||||
| : post.part.eq(part).and(post.studyName.isNotNull()) | ||||||||
| ) | ||||||||
| .orderBy(post.studyName.asc()) | ||||||||
| .fetch(); | ||||||||
| } | ||||||||
|
|
||||||||
| @Override //조건이 많은 쿼리라 BooleanBuilder 사용 | ||||||||
| public Slice<Post> findByPartAndOptionalFilters(Part part, Category category, Integer cardinal, String studyName, Integer week, Pageable pageable) { | ||||||||
|
|
||||||||
| BooleanBuilder builder = new BooleanBuilder(); | ||||||||
|
|
||||||||
| if (part == Part.ALL) { | ||||||||
| builder.and(post.part.eq(Part.ALL).or(post.part.ne(Part.ALL))); | ||||||||
| }else { | ||||||||
| builder.and(post.part.eq(part).or(post.part.eq(Part.ALL))); | ||||||||
| } | ||||||||
|
|
||||||||
| if (category != null) { | ||||||||
| builder.and(post.category.eq(category)); | ||||||||
| } | ||||||||
|
|
||||||||
| if (cardinal != null) { | ||||||||
| builder.and(post.cardinalNumber.eq(cardinal)); | ||||||||
| } | ||||||||
|
|
||||||||
| if (studyName != null) { | ||||||||
| builder.and(post.studyName.eq(studyName)); | ||||||||
| } | ||||||||
|
|
||||||||
| if (week != null) { | ||||||||
| builder.and(post.week.eq(week)); | ||||||||
| } | ||||||||
|
|
||||||||
| List<Post> result = queryFactory | ||||||||
| .selectFrom(post) | ||||||||
| .where(builder) | ||||||||
| .orderBy(post.id.desc()) | ||||||||
| .offset(pageable.getOffset()) | ||||||||
| .limit(pageable.getPageSize() + 1) | ||||||||
| .fetch(); | ||||||||
|
|
||||||||
| boolean hasNext = result.size() > pageable.getPageSize(); | ||||||||
|
|
||||||||
| if (hasNext) { | ||||||||
| result.remove(pageable.getPageSize()); | ||||||||
| } | ||||||||
| return new SliceImpl<>(result, pageable, hasNext); | ||||||||
|
|
||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public Slice<Post> findByCategoryAndOptionalCardinalWithPart(String partName, Category category, Integer cardinal, Pageable pageable) { | ||||||||
|
|
||||||||
| BooleanBuilder builder = new BooleanBuilder(); | ||||||||
|
|
||||||||
| builder.and(post.category.eq(category)); | ||||||||
|
|
||||||||
| if (cardinal != null) { | ||||||||
| builder.and(post.cardinalNumber.eq(cardinal)); | ||||||||
| } | ||||||||
|
|
||||||||
| if (!"ALL".equals(partName)) { //partName 조건 | ||||||||
| builder.and( | ||||||||
| Expressions.numberTemplate(Integer.class, | ||||||||
| "FIND_IN_SET({0}, {1})", partName, post.parts).gt(0) | ||||||||
| .or(Expressions.numberTemplate(Integer.class, | ||||||||
| "FIND_IN_SET('ALL', {0})", post.parts).gt(0)) | ||||||||
| ); | ||||||||
|
Comment on lines
+194
to
+198
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain데이터베이스 종속적인 SQL 함수 사용에 대한 검토가 필요합니다.
영향:
권장 사항:
private BooleanExpression createPartFilter(String partName) {
if ("ALL".equals(partName)) {
return null;
}
BooleanExpression containsPart =
Expressions.numberTemplate(Integer.class,
"FIND_IN_SET({0}, {1})", partName, post.parts).gt(0);
BooleanExpression containsAll =
Expressions.numberTemplate(Integer.class,
"FIND_IN_SET('ALL', {0})", post.parts).gt(0);
return containsPart.or(containsAll);
}
현재 구현을 유지할지, 데이터 모델 변경을 고려할지 팀 내에서 논의가 필요합니다. Also applies to: 224-228, 256-261 🏁 Script executed: #!/bin/bash
# Verify FIND_IN_SET usage at specified lines
echo "=== Checking lines 194-198, 224-228, 256-261 in PostRepositoryImpl.java ==="
fd PostRepositoryImpl.java --type f --exec cat {} \; | head -300 | tail -120Length of output: 3497 🏁 Script executed: #!/bin/bash
# Get full method context around the specified lines
echo "=== Getting full method implementations ==="
fd PostRepositoryImpl.java --type f --exec sed -n '170,210p;220,235p;245,270p' {} \;Length of output: 2425 🏁 Script executed: #!/bin/bash
# Search for all FIND_IN_SET occurrences in the repository
echo "=== Searching for all FIND_IN_SET usage ==="
rg -n "FIND_IN_SET" -C 5 --type javaLength of output: 9274 🏁 Script executed: #!/bin/bash
# Check for database configuration files
echo "=== Looking for database/persistence configuration ==="
fd -e yml -e yaml -e properties -e xml | xargs rg -l "database|persistence|jpa|hibernate|datasource" 2>/dev/null | head -10Length of output: 214 🏁 Script executed: #!/bin/bash
# Check if there's a query abstraction or utility layer
echo "=== Searching for query helper or expression builder classes ==="
rg -n "class.*Expression|class.*Query|interface.*Builder" --type java | grep -i "helper\|util\|builder" | head -20Length of output: 202 🏁 Script executed: #!/bin/bash
# Check database configuration in properties files
echo "=== Checking database configuration ==="
fd application-prod.yml application-dev.yml application-local.yml --type f --exec cat {} \;Length of output: 214 🏁 Script executed: #!/bin/bash
# Search for any comments or documentation about database choice
echo "=== Searching for database-related comments or docs ==="
rg -i "mysql|postgres|oracle|database.*driver|h2" --type yml --type yaml --type properties -B 2 -A 2Length of output: 83 🏁 Script executed: #!/bin/bash
# Check if there are any GitHub issues or documentation about database portability
echo "=== Looking for README or documentation files ==="
fd -e md -e txt | xargs grep -l -i "database\|mysql\|postgres" 2>/dev/null | head -5Length of output: 59 🏁 Script executed: #!/bin/bash
# Check the exact state: compare all three FIND_IN_SET implementations
echo "=== Comparing all three FIND_IN_SET implementations ==="
echo "=== Method 1: findByCategoryAndOptionalCardinalWithPart (lines 193-199) ==="
rg -A 7 "findByCategoryAndOptionalCardinalWithPart" src/main/java/leets/weeth/domain/board/domain/repository/PostRepositoryImpl.java | head -20
echo ""
echo "=== Method 2: findByCategoryAndCardinalNumberWithPart (lines 222-231) ==="
rg -A 10 "findByCategoryAndCardinalNumberWithPart" src/main/java/leets/weeth/domain/board/domain/repository/PostRepositoryImpl.java | grep -A 10 "if.*ALL"
echo ""
echo "=== Method 3: findByCategoryAndCardinalInWithPart (lines 254-264) ==="
rg -A 10 "findByCategoryAndCardinalInWithPart" src/main/java/leets/weeth/domain/board/domain/repository/PostRepositoryImpl.java | grep -A 10 "if.*ALL"Length of output: 930 🏁 Script executed: #!/bin/bash
# Check README for database information
echo "=== Checking README.md for database information ==="
cat README.md | head -100Length of output: 2247 🏁 Script executed: #!/bin/bash
# Verify database config files with correct fd syntax
echo "=== Checking database configuration files ==="
fd application.*\.y -e ymlLength of output: 240 🏁 Script executed: #!/bin/bash
# Extract database info from config
echo "=== Database configuration details ==="
find . -name "application*.y*ml" 2>/dev/null | xargs cat 2>/dev/null | grep -A 5 -B 5 -i "database\|datasource\|jpa\|hibernate" | head -50Length of output: 1030 메서드 1만 리팩토링이 필요합니다. 코드 패턴 일관성을 맞추세요. 세 메서드 중 첫 번째 메서드만 인라인 표현식을 사용하고 있고, 두 번째와 세 번째 메서드는 이미 변수로 추출되어 있습니다. MySQL은 의도적으로 설정되어 있으므로 데이터베이스 이식성은 현재 주요 우려사항이 아닙니다. 조치 사항: if (!"ALL".equals(partName)) {
BooleanExpression containsPart =
Expressions.numberTemplate(Integer.class,
"FIND_IN_SET({0}, {1})", partName, post.parts).gt(0);
BooleanExpression containsAll =
Expressions.numberTemplate(Integer.class,
"FIND_IN_SET('ALL', {0})", post.parts).gt(0);
builder.and(containsPart.or(containsAll));
}🤖 Prompt for AI Agents |
||||||||
| } | ||||||||
|
|
||||||||
| List<Post> result = queryFactory | ||||||||
| .selectFrom(post) | ||||||||
| .where(builder) | ||||||||
| .orderBy(post.id.desc()) | ||||||||
| .offset(pageable.getOffset()) | ||||||||
| .limit(pageable.getPageSize() + 1) | ||||||||
| .fetch(); | ||||||||
| boolean hasNext = result.size() > pageable.getPageSize(); | ||||||||
| if (hasNext) result.remove(pageable.getPageSize()); | ||||||||
|
|
||||||||
| return new SliceImpl<>(result, pageable, hasNext); | ||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public Slice<Post> findByCategoryAndCardinalNumberWithPart(String partName, Category category, Integer cardinal, Pageable pageable) { | ||||||||
|
|
||||||||
| BooleanBuilder builder = new BooleanBuilder(); | ||||||||
|
|
||||||||
| builder.and(post.category.eq(category)); | ||||||||
| builder.and(post.cardinalNumber.eq(cardinal)); | ||||||||
|
|
||||||||
| if (!"ALL".equals(partName)) { | ||||||||
| BooleanExpression containsPart = | ||||||||
| Expressions.numberTemplate(Integer.class, "FIND_IN_SET({0}, {1})", partName, post.parts).gt(0); | ||||||||
|
|
||||||||
| BooleanExpression containsAll = | ||||||||
| Expressions.numberTemplate(Integer.class, | ||||||||
| "FIND_IN_SET('ALL', {0})", post.parts).gt(0); | ||||||||
|
|
||||||||
| builder.and(containsPart.or(containsAll)); | ||||||||
| } | ||||||||
|
|
||||||||
| List<Post> result = queryFactory | ||||||||
| .selectFrom(post) | ||||||||
| .where(builder) | ||||||||
| .orderBy(post.id.desc()) | ||||||||
| .offset(pageable.getOffset()) | ||||||||
| .limit(pageable.getPageSize() + 1) | ||||||||
| .fetch(); | ||||||||
|
|
||||||||
| boolean hasNext = result.size() > pageable.getPageSize(); | ||||||||
| if (hasNext) result.remove(pageable.getPageSize()); | ||||||||
|
|
||||||||
| return new SliceImpl<>(result, pageable, hasNext); | ||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public Slice<Post> findByCategoryAndCardinalInWithPart(String partName, Category category, Collection<Integer> cardinals, Pageable pageable) { | ||||||||
| BooleanBuilder builder = new BooleanBuilder(); | ||||||||
|
|
||||||||
| builder.and(post.category.eq(category)); | ||||||||
| builder.and(post.cardinalNumber.in(cardinals)); | ||||||||
|
|
||||||||
| if (!"ALL".equals(partName)) { | ||||||||
| BooleanExpression containsPart = | ||||||||
| Expressions.numberTemplate(Integer.class, | ||||||||
| "FIND_IN_SET({0}, {1})", partName, post.parts).gt(0); | ||||||||
|
|
||||||||
| BooleanExpression containsAll = | ||||||||
| Expressions.numberTemplate(Integer.class, | ||||||||
| "FIND_IN_SET('ALL', {0})", post.parts).gt(0); | ||||||||
|
|
||||||||
| builder.and(containsPart.or(containsAll)); | ||||||||
| } | ||||||||
|
|
||||||||
| List<Post> result = queryFactory | ||||||||
| .selectFrom(post) | ||||||||
| .where(builder) | ||||||||
| .orderBy(post.id.desc()) | ||||||||
| .offset(pageable.getOffset()) | ||||||||
| .limit(pageable.getPageSize() + 1) | ||||||||
| .fetch(); | ||||||||
|
|
||||||||
| boolean hasNext = result.size() > pageable.getPageSize(); | ||||||||
| if (hasNext) result.remove(pageable.getPageSize()); | ||||||||
|
|
||||||||
| return new SliceImpl<>(result, pageable, hasNext); | ||||||||
| } | ||||||||
| } | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package leets.weeth.global.config; | ||
|
|
||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
|
|
||
| import com.querydsl.jpa.impl.JPAQueryFactory; | ||
|
|
||
| import jakarta.persistence.EntityManager; | ||
|
|
||
| @Configuration | ||
| public class QuerydslConfig { | ||
|
|
||
| @Bean | ||
| public JPAQueryFactory jpaQueryFactory(EntityManager em) { | ||
| return new JPAQueryFactory(em); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
QueryDSL 버전 확인 필요
QueryDSL 5.0.0을 사용 중이지만, 이 버전이 최신 안정 버전인지 확인이 필요합니다. 보안 패치와 버그 수정이 포함된 최신 버전을 사용하는 것이 좋습니다.
🌐 Web query:
💡 Result:
The latest stable QueryDSL release for Jakarta EE is 5.1.0 (checked Nov 11, 2025). [1][2]
Sources:
QueryDSL을 5.1.0으로 업그레이드하세요
현재 QueryDSL 5.0.0을 사용 중이지만, 최신 안정 버전은 5.1.0입니다. 보안 패치와 버그 수정을 위해 build.gradle의 44-45번 줄을 다음과 같이 업데이트하세요:
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa/5.1.0
확인해보니 쿼리 DSL 전 버전에 취약점이 있네요. 지원 중단돼서 그런가...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
💡 Result:
Short answer: Yes — Querydsl has a reported HQL/SQL injection vulnerability identified as CVE-2024-49203 that affects Querydsl 5.x (including 5.0.0/<=5.1.0 in multiple advisories). The issue concerns injection via JPAQuery.orderBy when untrusted input is used. Recommend not passing untrusted input into orderBy and upgrading to a patched/non-vulnerable release or forked build where the fix is available. [1][2][3][4]
Sources
If you want, I can: (a) check the current latest Querydsl release and which exact versions include the fix, or (b) show concrete mitigations/workarounds and sample code to safely handle ordering parameters.
🌐 Web query:
💡 Result:
Short answer: Querydsl is actively maintained in 2025 and the CVE-2024-49203 HQL/SQL‑injection issue was fixed — upgrade to the patched releases (Querydsl 5.6.1, 6.10.1; OpenFeign Querydsl 7.0 also lists fixes). Apply the update or validate/whitelist any user-controlled orderBy input if you cannot upgrade immediately. [1][2][3]
Sources:
@hyxklee 좋은 지적 감사합니다! 취약점 확인 결과를 공유드립니다:
취약점 정보 (CVE-2024-49203)
JPAQuery.orderBy()에 검증되지 않은 사용자 입력이 전달될 때 발생합니다유지보수 상태
QueryDSL은 여전히 활발히 유지보수되고 있습니다. 지원 중단이 아니며, 해당 취약점은 이미 패치되었습니다.
권장사항
build.gradle의 QueryDSL 버전을 5.6.1 이상으로 업그레이드하세요:
이 PR에서 QueryDSL로 전환하는 시점에 보안 패치가 적용된 버전을 사용하는 것이 중요합니다. 현재 코드를 확인한 결과
orderBy(post.id.desc())처럼 정적 표현식을 사용하고 있어 직접적인 위험은 낮지만, 예방 차원에서 패치된 버전 사용을 강력히 권장합니다.