Conversation
- DTO 필드에 @jsonformat 추가하여 날짜/시간 포맷 지정 - Recruitment 관련 DTO 클래스 수정: RecruitmentResponseDTO, RecruitmentDetailResponseDTO, RecruitmentRequestDTO
WalkthroughQueryDSL과 Jakarta 지원을 위한 의존성 및 설정이 build.gradle에 추가되었습니다. Recruitment 관련 DTO에 날짜/시간 JSON 포맷 지정 어노테이션이 적용되었고, 기존 JPQL 기반 복잡 쿼리가 리포지토리 커스텀 인터페이스 및 QueryDSL 기반 구현체로 리팩토링되었습니다. QueryDSL 사용을 위한 Spring 설정 클래스도 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Controller
participant RecruitmentRepository (Spring Data)
participant RecruitmentRepositoryImpl (QueryDSL)
participant JPAQueryFactory
participant DB
Controller->>RecruitmentRepository: findBySearchConditions(cond, pageable)
RecruitmentRepository->>RecruitmentRepositoryImpl: findBySearchConditions(cond, pageable)
RecruitmentRepositoryImpl->>JPAQueryFactory: query().select(...).from(...).where(...)
JPAQueryFactory->>DB: 실행된 QueryDSL 쿼리
DB-->>JPAQueryFactory: 결과 반환
JPAQueryFactory-->>RecruitmentRepositoryImpl: 결과 DTO 및 count 반환
RecruitmentRepositoryImpl-->>RecruitmentRepository: Page<RecruitmentResponseDTO> 반환
RecruitmentRepository-->>Controller: Page<RecruitmentResponseDTO> 반환
Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (8)
build.gradle(1 hunks)src/main/java/org/example/hugmeexp/domain/recruitment/dto/RecruitmentDetailResponseDTO.java(2 hunks)src/main/java/org/example/hugmeexp/domain/recruitment/dto/RecruitmentRequestDTO.java(2 hunks)src/main/java/org/example/hugmeexp/domain/recruitment/dto/RecruitmentResponseDTO.java(2 hunks)src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepository.java(1 hunks)src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryCustom.java(1 hunks)src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java(1 hunks)src/main/java/org/example/hugmeexp/global/common/config/QueryDslConfig.java(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Roysung0625
PR: Please-Hug/HugmeEXP_Backend#32
File: src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java:5-5
Timestamp: 2025-07-23T09:00:11.298Z
Learning: ReservationDetailDto와 같은 response DTO에서는 Data 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다.
📚 Learning: reservationdetaildto와 같은 response dto에서는 @DaTa 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다....
Learnt from: Roysung0625
PR: Please-Hug/HugmeEXP_Backend#32
File: src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java:5-5
Timestamp: 2025-07-23T09:00:11.298Z
Learning: ReservationDetailDto와 같은 response DTO에서는 Data 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다.
Applied to files:
src/main/java/org/example/hugmeexp/domain/recruitment/dto/RecruitmentResponseDTO.javasrc/main/java/org/example/hugmeexp/domain/recruitment/dto/RecruitmentDetailResponseDTO.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (10)
build.gradle (1)
67-73: QueryDSL 의존성 설정이 올바르게 구성되었습니다.Jakarta 지원 버전의 QueryDSL 5.0.0과 필요한 annotation processor들이 모두 포함되어 있어 Spring Boot 3.5.0과 호환됩니다. 타입 안전한 쿼리 생성을 위한 설정이 완료되었습니다.
src/main/java/org/example/hugmeexp/domain/recruitment/dto/RecruitmentRequestDTO.java (1)
67-68: 날짜 포맷 어노테이션이 적절히 추가되었습니다.
LocalDateTime필드에 대한 JSON 직렬화 포맷이 명시되어 일관된 API 응답을 보장합니다.src/main/java/org/example/hugmeexp/domain/recruitment/dto/RecruitmentResponseDTO.java (3)
3-4: 필요한 import 구문이 적절히 추가되었습니다.QueryDSL projection과 JSON 포맷팅을 위한 import가 올바르게 추가되었습니다.
23-24: 날짜 필드에 일관된 JSON 포맷이 적용되었습니다.
dueDate와modifiedAt필드에 동일한 날짜 포맷 패턴이 적용되어 API 응답의 일관성이 보장됩니다.Also applies to: 31-32
35-51: QueryDSL projection을 위한 생성자 어노테이션이 올바르게 추가되었습니다.
@QueryProjection어노테이션을 통해 QueryDSL에서 타입 안전한 projection을 사용할 수 있게 되었습니다. 이는 복잡한 쿼리의 성능과 타입 안전성을 개선합니다.src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryCustom.java (1)
9-12: 커스텀 리포지토리 인터페이스가 올바른 패턴으로 구현되었습니다.Spring Data의 커스텀 리포지토리 패턴을 따르며, 페이지네이션을 지원하는 복잡한 검색 쿼리를 위한 인터페이스가 적절히 정의되었습니다.
src/main/java/org/example/hugmeexp/global/common/config/QueryDslConfig.java (1)
8-21: QueryDSL 설정 클래스가 Spring 모범 사례에 따라 올바르게 구현되었습니다.생성자 주입을 사용하여
EntityManager를 주입받고,JPAQueryFactory빈을 적절히 생성하는 간결하고 명확한 설정입니다.src/main/java/org/example/hugmeexp/domain/recruitment/dto/RecruitmentDetailResponseDTO.java (1)
3-3: JSON 날짜 포맷 적용이 적절합니다.날짜/시간 필드에 대한 일관된 JSON 직렬화 포맷을 적용한 것이 좋습니다.
LocalDate와LocalDateTime타입에 맞는 적절한 패턴을 사용하고 있습니다.또한 이전 학습 내용에 따라 response DTO에서
@Data어노테이션을 사용하지 않고 있는 점도 잘 지켜지고 있습니다.Also applies to: 25-26, 28-29
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepository.java (1)
17-17: QueryDSL 커스텀 리포지토리 통합이 적절합니다.
RecruitmentRepositoryCustom인터페이스를 확장하여 QueryDSL 기반 구현을 통합한 것이 좋습니다. 이는 복잡한 쿼리 로직을 별도 구현체로 분리하는 Spring Data JPA의 모범 사례입니다.src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java (1)
116-128: 지리적 경계 필터링 로직이 잘 구현되었습니다.좌표 순서와 관계없이 min/max를 올바르게 계산하여 경계 상자 내 필터링을 수행하는 로직이 견고합니다.
| // /** | ||
| // * 채용 공고 목록을 조회합니다. | ||
| // * 조건에 따라 필터링된 채용 공고를 페이지 단위로 반환합니다. | ||
| // * | ||
| // * @param cond 검색 조건 DTO | ||
| // * @param pageable 페이징 정보 | ||
| // * @return 필터링된 채용 공고 목록 (RecruitmentResponseDTO) | ||
| // * | ||
| // * 사용자가 지도를 드래그하여 선택한 사각형 범위 내에 있는 공고만 필터링 | ||
| // */ | ||
| // @Query( | ||
| // value = """ | ||
| // SELECT new org.example.hugmeexp.domain.recruitment.dto.RecruitmentResponseDTO( | ||
| // r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate, | ||
| // r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt | ||
| // ) | ||
| // FROM Recruitment r | ||
| // JOIN r.company c | ||
| // LEFT JOIN r.techStacks ts | ||
| // LEFT JOIN r.tags t | ||
| // WHERE r.dueDate > CURRENT_TIMESTAMP AND | ||
| // (:#{#cond.salaryMin} IS NULL OR r.salaryMin >= :#{#cond.salaryMin}) AND | ||
| // (:#{#cond.salaryMax} IS NULL OR r.salaryMax <= :#{#cond.salaryMax}) AND | ||
| // ( | ||
| // (:#{#cond.experienceMin} IS NULL OR :#{#cond.experienceMax} IS NULL) | ||
| // OR | ||
| // (r.experienceMax >= :#{#cond.experienceMin} AND r.experienceMin <= :#{#cond.experienceMax}) | ||
| // ) AND | ||
| // (:#{#cond.education} IS NULL OR r.education = :#{#cond.education}) AND | ||
| // (:#{#cond.workLocation} IS NULL OR r.workLocation LIKE CONCAT('%', :#{#cond.workLocation}, '%')) AND | ||
| // ( | ||
| // :#{#cond.topLeftLat} IS NULL OR | ||
| // :#{#cond.topLeftLng} IS NULL OR | ||
| // :#{#cond.bottomRightLat} IS NULL OR | ||
| // :#{#cond.bottomRightLng} IS NULL OR | ||
| // ( | ||
| // r.latitude BETWEEN LEAST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND GREATEST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND | ||
| // r.longitude BETWEEN LEAST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) AND GREATEST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) | ||
| // ) | ||
| // ) AND | ||
| // (:#{#cond.techStacks} IS NULL OR ts.techItem.id IN :#{#cond.techStacks}) AND | ||
| // (:#{#cond.tags} IS NULL OR t.tagItem.id IN :#{#cond.tags}) AND | ||
| // (:#{#cond.keyword} IS NULL OR | ||
| // LOWER(r.title) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) OR | ||
| // LOWER(c.companyName) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) | ||
| // ) | ||
| // GROUP BY r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate, | ||
| // r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt | ||
| // HAVING (:#{#cond.techStacks} IS NULL OR COUNT(DISTINCT ts.id) = :#{#cond.techStackCount}) AND | ||
| // (:#{#cond.tags} IS NULL OR COUNT(DISTINCT t.id) = :#{#cond.tagCount}) | ||
| // ORDER BY r.modifiedAt DESC | ||
| // """, | ||
| // countQuery = """ | ||
| // SELECT COUNT(DISTINCT r.id) | ||
| // FROM Recruitment r | ||
| // JOIN r.company c | ||
| // LEFT JOIN r.techStacks ts | ||
| // LEFT JOIN r.tags t | ||
| // WHERE r.dueDate > CURRENT_TIMESTAMP AND | ||
| // (:#{#cond.salaryMin} IS NULL OR r.salaryMin >= :#{#cond.salaryMin}) AND | ||
| // (:#{#cond.salaryMax} IS NULL OR r.salaryMax <= :#{#cond.salaryMax}) AND | ||
| // ( | ||
| // (:#{#cond.experienceMin} IS NULL OR :#{#cond.experienceMax} IS NULL) | ||
| // OR | ||
| // (r.experienceMax >= :#{#cond.experienceMin} AND r.experienceMin <= :#{#cond.experienceMax}) | ||
| // ) AND | ||
| // (:#{#cond.education} IS NULL OR r.education = :#{#cond.education}) AND | ||
| // (:#{#cond.workLocation} IS NULL OR r.workLocation LIKE CONCAT('%', :#{#cond.workLocation}, '%')) AND | ||
| // ( | ||
| // :#{#cond.topLeftLat} IS NULL OR | ||
| // :#{#cond.topLeftLng} IS NULL OR | ||
| // :#{#cond.bottomRightLat} IS NULL OR | ||
| // :#{#cond.bottomRightLng} IS NULL OR | ||
| // ( | ||
| // r.latitude BETWEEN LEAST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND GREATEST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND | ||
| // r.longitude BETWEEN LEAST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) AND GREATEST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) | ||
| // ) | ||
| // ) AND | ||
| // (:#{#cond.techStacks} IS NULL OR ts.techItem.id IN :#{#cond.techStacks}) AND | ||
| // (:#{#cond.tags} IS NULL OR t.tagItem.id IN :#{#cond.tags}) AND | ||
| // (:#{#cond.keyword} IS NULL OR | ||
| // LOWER(r.title) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) OR | ||
| // LOWER(c.companyName) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) | ||
| // ) | ||
| // """) | ||
| // Page<RecruitmentResponseDTO> findBySearchConditions(@Param("cond") RecruitmentSearchConditionDTO cond, Pageable pageable); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
주석 처리된 코드를 제거해주세요.
주석 처리된 JPQL 쿼리는 이미 QueryDSL 구현으로 대체되었으므로 제거하는 것이 좋습니다. 주석으로 남겨둔 코드는 유지보수성을 떨어뜨리고 혼란을 야기할 수 있습니다.
-// /**
-// * 채용 공고 목록을 조회합니다.
-// * 조건에 따라 필터링된 채용 공고를 페이지 단위로 반환합니다.
-// *
-// * @param cond 검색 조건 DTO
-// * @param pageable 페이징 정보
-// * @return 필터링된 채용 공고 목록 (RecruitmentResponseDTO)
-// *
-// * 사용자가 지도를 드래그하여 선택한 사각형 범위 내에 있는 공고만 필터링
-// */
-// @Query(
-// value = """
-// SELECT new org.example.hugmeexp.domain.recruitment.dto.RecruitmentResponseDTO(
-// r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate,
-// r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt
-// )
-// FROM Recruitment r
-// JOIN r.company c
-// LEFT JOIN r.techStacks ts
-// LEFT JOIN r.tags t
-// WHERE r.dueDate > CURRENT_TIMESTAMP AND
-// (:#{#cond.salaryMin} IS NULL OR r.salaryMin >= :#{#cond.salaryMin}) AND
-// (:#{#cond.salaryMax} IS NULL OR r.salaryMax <= :#{#cond.salaryMax}) AND
-// (
-// (:#{#cond.experienceMin} IS NULL OR :#{#cond.experienceMax} IS NULL)
-// OR
-// (r.experienceMax >= :#{#cond.experienceMin} AND r.experienceMin <= :#{#cond.experienceMax})
-// ) AND
-// (:#{#cond.education} IS NULL OR r.education = :#{#cond.education}) AND
-// (:#{#cond.workLocation} IS NULL OR r.workLocation LIKE CONCAT('%', :#{#cond.workLocation}, '%')) AND
-// (
-// :#{#cond.topLeftLat} IS NULL OR
-// :#{#cond.topLeftLng} IS NULL OR
-// :#{#cond.bottomRightLat} IS NULL OR
-// :#{#cond.bottomRightLng} IS NULL OR
-// (
-// r.latitude BETWEEN LEAST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND GREATEST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND
-// r.longitude BETWEEN LEAST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) AND GREATEST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng})
-// )
-// ) AND
-// (:#{#cond.techStacks} IS NULL OR ts.techItem.id IN :#{#cond.techStacks}) AND
-// (:#{#cond.tags} IS NULL OR t.tagItem.id IN :#{#cond.tags}) AND
-// (:#{#cond.keyword} IS NULL OR
-// LOWER(r.title) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) OR
-// LOWER(c.companyName) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%'))
-// )
-// GROUP BY r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate,
-// r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt
-// HAVING (:#{#cond.techStacks} IS NULL OR COUNT(DISTINCT ts.id) = :#{#cond.techStackCount}) AND
-// (:#{#cond.tags} IS NULL OR COUNT(DISTINCT t.id) = :#{#cond.tagCount})
-// ORDER BY r.modifiedAt DESC
-// """,
-// countQuery = """
-// SELECT COUNT(DISTINCT r.id)
-// FROM Recruitment r
-// JOIN r.company c
-// LEFT JOIN r.techStacks ts
-// LEFT JOIN r.tags t
-// WHERE r.dueDate > CURRENT_TIMESTAMP AND
-// (:#{#cond.salaryMin} IS NULL OR r.salaryMin >= :#{#cond.salaryMin}) AND
-// (:#{#cond.salaryMax} IS NULL OR r.salaryMax <= :#{#cond.salaryMax}) AND
-// (
-// (:#{#cond.experienceMin} IS NULL OR :#{#cond.experienceMax} IS NULL)
-// OR
-// (r.experienceMax >= :#{#cond.experienceMin} AND r.experienceMin <= :#{#cond.experienceMax})
-// ) AND
-// (:#{#cond.education} IS NULL OR r.education = :#{#cond.education}) AND
-// (:#{#cond.workLocation} IS NULL OR r.workLocation LIKE CONCAT('%', :#{#cond.workLocation}, '%')) AND
-// (
-// :#{#cond.topLeftLat} IS NULL OR
-// :#{#cond.topLeftLng} IS NULL OR
-// :#{#cond.bottomRightLat} IS NULL OR
-// :#{#cond.bottomRightLng} IS NULL OR
-// (
-// r.latitude BETWEEN LEAST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND GREATEST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND
-// r.longitude BETWEEN LEAST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) AND GREATEST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng})
-// )
-// ) AND
-// (:#{#cond.techStacks} IS NULL OR ts.techItem.id IN :#{#cond.techStacks}) AND
-// (:#{#cond.tags} IS NULL OR t.tagItem.id IN :#{#cond.tags}) AND
-// (:#{#cond.keyword} IS NULL OR
-// LOWER(r.title) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) OR
-// LOWER(c.companyName) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%'))
-// )
-// """)
-// Page<RecruitmentResponseDTO> findBySearchConditions(@Param("cond") RecruitmentSearchConditionDTO cond, Pageable pageable);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // /** | |
| // * 채용 공고 목록을 조회합니다. | |
| // * 조건에 따라 필터링된 채용 공고를 페이지 단위로 반환합니다. | |
| // * | |
| // * @param cond 검색 조건 DTO | |
| // * @param pageable 페이징 정보 | |
| // * @return 필터링된 채용 공고 목록 (RecruitmentResponseDTO) | |
| // * | |
| // * 사용자가 지도를 드래그하여 선택한 사각형 범위 내에 있는 공고만 필터링 | |
| // */ | |
| // @Query( | |
| // value = """ | |
| // SELECT new org.example.hugmeexp.domain.recruitment.dto.RecruitmentResponseDTO( | |
| // r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate, | |
| // r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt | |
| // ) | |
| // FROM Recruitment r | |
| // JOIN r.company c | |
| // LEFT JOIN r.techStacks ts | |
| // LEFT JOIN r.tags t | |
| // WHERE r.dueDate > CURRENT_TIMESTAMP AND | |
| // (:#{#cond.salaryMin} IS NULL OR r.salaryMin >= :#{#cond.salaryMin}) AND | |
| // (:#{#cond.salaryMax} IS NULL OR r.salaryMax <= :#{#cond.salaryMax}) AND | |
| // ( | |
| // (:#{#cond.experienceMin} IS NULL OR :#{#cond.experienceMax} IS NULL) | |
| // OR | |
| // (r.experienceMax >= :#{#cond.experienceMin} AND r.experienceMin <= :#{#cond.experienceMax}) | |
| // ) AND | |
| // (:#{#cond.education} IS NULL OR r.education = :#{#cond.education}) AND | |
| // (:#{#cond.workLocation} IS NULL OR r.workLocation LIKE CONCAT('%', :#{#cond.workLocation}, '%')) AND | |
| // ( | |
| // :#{#cond.topLeftLat} IS NULL OR | |
| // :#{#cond.topLeftLng} IS NULL OR | |
| // :#{#cond.bottomRightLat} IS NULL OR | |
| // :#{#cond.bottomRightLng} IS NULL OR | |
| // ( | |
| // r.latitude BETWEEN LEAST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND GREATEST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND | |
| // r.longitude BETWEEN LEAST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) AND GREATEST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) | |
| // ) | |
| // ) AND | |
| // (:#{#cond.techStacks} IS NULL OR ts.techItem.id IN :#{#cond.techStacks}) AND | |
| // (:#{#cond.tags} IS NULL OR t.tagItem.id IN :#{#cond.tags}) AND | |
| // (:#{#cond.keyword} IS NULL OR | |
| // LOWER(r.title) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) OR | |
| // LOWER(c.companyName) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) | |
| // ) | |
| // GROUP BY r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate, | |
| // r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt | |
| // HAVING (:#{#cond.techStacks} IS NULL OR COUNT(DISTINCT ts.id) = :#{#cond.techStackCount}) AND | |
| // (:#{#cond.tags} IS NULL OR COUNT(DISTINCT t.id) = :#{#cond.tagCount}) | |
| // ORDER BY r.modifiedAt DESC | |
| // """, | |
| // countQuery = """ | |
| // SELECT COUNT(DISTINCT r.id) | |
| // FROM Recruitment r | |
| // JOIN r.company c | |
| // LEFT JOIN r.techStacks ts | |
| // LEFT JOIN r.tags t | |
| // WHERE r.dueDate > CURRENT_TIMESTAMP AND | |
| // (:#{#cond.salaryMin} IS NULL OR r.salaryMin >= :#{#cond.salaryMin}) AND | |
| // (:#{#cond.salaryMax} IS NULL OR r.salaryMax <= :#{#cond.salaryMax}) AND | |
| // ( | |
| // (:#{#cond.experienceMin} IS NULL OR :#{#cond.experienceMax} IS NULL) | |
| // OR | |
| // (r.experienceMax >= :#{#cond.experienceMin} AND r.experienceMin <= :#{#cond.experienceMax}) | |
| // ) AND | |
| // (:#{#cond.education} IS NULL OR r.education = :#{#cond.education}) AND | |
| // (:#{#cond.workLocation} IS NULL OR r.workLocation LIKE CONCAT('%', :#{#cond.workLocation}, '%')) AND | |
| // ( | |
| // :#{#cond.topLeftLat} IS NULL OR | |
| // :#{#cond.topLeftLng} IS NULL OR | |
| // :#{#cond.bottomRightLat} IS NULL OR | |
| // :#{#cond.bottomRightLng} IS NULL OR | |
| // ( | |
| // r.latitude BETWEEN LEAST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND GREATEST(:#{#cond.topLeftLat}, :#{#cond.bottomRightLat}) AND | |
| // r.longitude BETWEEN LEAST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) AND GREATEST(:#{#cond.topLeftLng}, :#{#cond.bottomRightLng}) | |
| // ) | |
| // ) AND | |
| // (:#{#cond.techStacks} IS NULL OR ts.techItem.id IN :#{#cond.techStacks}) AND | |
| // (:#{#cond.tags} IS NULL OR t.tagItem.id IN :#{#cond.tags}) AND | |
| // (:#{#cond.keyword} IS NULL OR | |
| // LOWER(r.title) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) OR | |
| // LOWER(c.companyName) LIKE LOWER(CONCAT('%', :#{#cond.keyword}, '%')) | |
| // ) | |
| // """) | |
| // Page<RecruitmentResponseDTO> findBySearchConditions(@Param("cond") RecruitmentSearchConditionDTO cond, Pageable pageable); |
🤖 Prompt for AI Agents
In
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepository.java
between lines 19 and 104, remove the entire block of commented-out JPQL query
code. This code is obsolete because the functionality has been replaced by a
QueryDSL implementation, so deleting these commented lines will improve code
maintainability and reduce confusion.
| public Page<RecruitmentResponseDTO> findBySearchConditions(RecruitmentSearchConditionDTO cond, Pageable pageable) { | ||
| QRecruitment r = QRecruitment.recruitment; | ||
| QCompany c = QCompany.company; | ||
| QTechStack ts = QTechStack.techStack; | ||
| QTag t = QTag.tag; | ||
|
|
||
| // Group By 후 having 절에서 사용할 count 컬럼 | ||
| NumberExpression<Long> techStackCount = ts.id.count(); | ||
| NumberExpression<Long> tagCount = t.id.count(); | ||
|
|
||
| List<RecruitmentResponseDTO> content = queryFactory | ||
| .select(new QRecruitmentResponseDTO( | ||
| r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate, | ||
| r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt | ||
| )) | ||
| .from(r) | ||
| .join(r.company, c) | ||
| .leftJoin(r.techStacks, ts) | ||
| .leftJoin(r.tags, t) | ||
| .where( | ||
| r.dueDate.gt(LocalDateTime.now()), | ||
| salaryMinGoe(cond.getSalaryMin()), | ||
| salaryMaxLoe(cond.getSalaryMax()), | ||
| experienceBetween(cond.getExperienceMin(), cond.getExperienceMax()), | ||
| educationEq(cond.getEducation()), | ||
| workLocationLike(cond.getWorkLocation()), | ||
| withinBounds(cond), | ||
| keywordLike(cond.getKeyword()), | ||
| techStacksIn(cond.getTechStacks()), | ||
| tagsIn(cond.getTags()) | ||
| ) | ||
| .groupBy(r.id) | ||
| .having( | ||
| techStackCountEq(techStackCount, cond.getTechStacks(), cond.getTechStackCount()), | ||
| tagCountEq(tagCount, cond.getTags(), cond.getTagCount()) | ||
| ) | ||
| .orderBy(r.modifiedAt.desc()) | ||
| .offset(pageable.getOffset()) | ||
| .limit(pageable.getPageSize()) | ||
| .fetch(); | ||
|
|
||
| Long total = queryFactory | ||
| .select(r.id.countDistinct()) | ||
| .from(r) | ||
| .join(r.company, c) | ||
| .leftJoin(r.techStacks, ts) | ||
| .leftJoin(r.tags, t) | ||
| .where( | ||
| r.dueDate.gt(LocalDateTime.now()), | ||
| salaryMinGoe(cond.getSalaryMin()), | ||
| salaryMaxLoe(cond.getSalaryMax()), | ||
| experienceBetween(cond.getExperienceMin(), cond.getExperienceMax()), | ||
| educationEq(cond.getEducation()), | ||
| workLocationLike(cond.getWorkLocation()), | ||
| withinBounds(cond), | ||
| keywordLike(cond.getKeyword()), | ||
| techStacksIn(cond.getTechStacks()), | ||
| tagsIn(cond.getTags()) | ||
| ) | ||
| .fetchOne(); | ||
|
|
||
| return new PageImpl<>(content, pageable, total); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
🛠️ Refactor suggestion
QueryDSL 구현에 대한 통합 테스트를 추가하세요.
복잡한 필터링 로직이 올바르게 동작하는지 확인하기 위해 다음 시나리오에 대한 테스트가 필요합니다:
- techStacks/tags 필터링 시 정확한 카운트 매칭
- 지리적 경계 필터링
- 페이지네이션이 올바른 총 개수를 반환하는지
통합 테스트 코드를 생성해드릴까요? 또는 이를 추적하기 위한 새 이슈를 생성할까요?
🤖 Prompt for AI Agents
In
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java
around lines 28 to 90, the complex filtering logic in findBySearchConditions
requires integration tests to verify correct behavior. Add tests covering
scenarios for exact count matching of techStacks and tags filters, geographic
boundary filtering, and pagination returning the correct total count. These
tests should use realistic data setups and assert that the repository method
returns expected results under these conditions.
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java
Outdated
Show resolved
Hide resolved
| Long total = queryFactory | ||
| .select(r.id.countDistinct()) | ||
| .from(r) | ||
| .join(r.company, c) | ||
| .leftJoin(r.techStacks, ts) | ||
| .leftJoin(r.tags, t) | ||
| .where( | ||
| r.dueDate.gt(LocalDateTime.now()), | ||
| salaryMinGoe(cond.getSalaryMin()), | ||
| salaryMaxLoe(cond.getSalaryMax()), | ||
| experienceBetween(cond.getExperienceMin(), cond.getExperienceMax()), | ||
| educationEq(cond.getEducation()), | ||
| workLocationLike(cond.getWorkLocation()), | ||
| withinBounds(cond), | ||
| keywordLike(cond.getKeyword()), | ||
| techStacksIn(cond.getTechStacks()), | ||
| tagsIn(cond.getTags()) | ||
| ) | ||
| .fetchOne(); |
There was a problem hiding this comment.
카운트 쿼리에 GROUP BY와 HAVING 절이 누락되었습니다.
techStacks나 tags 필터를 사용할 때 카운트 쿼리가 부정확한 결과를 반환할 수 있습니다. 컨텐츠 쿼리에는 GROUP BY와 HAVING이 있지만 카운트 쿼리에는 없어서 페이지네이션 총 개수가 잘못 계산됩니다.
두 가지 해결 방법을 제안합니다:
방법 1: 서브쿼리를 사용한 카운트
Long total = queryFactory
.select(r.id.countDistinct())
.from(r)
.join(r.company, c)
.leftJoin(r.techStacks, ts)
.leftJoin(r.tags, t)
.where(
r.dueDate.gt(LocalDateTime.now()),
// ... 기존 조건들
)
.groupBy(r.id)
.having(
techStackCountEq(ts.id.count(), cond.getTechStacks(), cond.getTechStackCount()),
tagCountEq(t.id.count(), cond.getTags(), cond.getTagCount())
)
.fetch().size(); // 또는 서브쿼리로 감싸서 count방법 2: 두 단계 쿼리 (PR 목표에 언급된 접근법)
// 먼저 필터링된 ID 목록을 조회
List<Long> filteredIds = queryFactory
.select(r.id)
.from(r)
.join(r.company, c)
.leftJoin(r.techStacks, ts)
.leftJoin(r.tags, t)
.where(/* 조건들 */)
.groupBy(r.id)
.having(/* having 조건들 */)
.fetch();
// 그 다음 ID 목록으로 페이지네이션🤖 Prompt for AI Agents
In
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java
around lines 69 to 87, the count query lacks GROUP BY and HAVING clauses,
causing inaccurate total counts when filtering by techStacks or tags. To fix
this, modify the count query to include GROUP BY r.id and HAVING clauses that
match the filters on techStacks and tags, then either count the resulting
grouped records or wrap the query as a subquery to get the total count.
Alternatively, implement a two-step query: first fetch the filtered IDs with
GROUP BY and HAVING, then count or paginate based on that ID list.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java (2)
55-63: 카운트 쿼리에 GROUP BY와 HAVING 절이 누락되었습니다.techStacks나 tags 필터를 사용할 때 카운트 쿼리가 부정확한 결과를 반환할 수 있습니다. 컨텐츠 쿼리에는 GROUP BY와 HAVING이 있지만 카운트 쿼리에는 없어서 페이지네이션 총 개수가 잘못 계산됩니다.
28-65: QueryDSL 구현에 대한 통합 테스트를 추가하세요.복잡한 필터링 로직이 올바르게 동작하는지 확인하기 위해 다음 시나리오에 대한 테스트가 필요합니다:
- techStacks/tags 필터링 시 정확한 카운트 매칭
- 지리적 경계 필터링
- 페이지네이션이 올바른 총 개수를 반환하는지
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Roysung0625
PR: Please-Hug/HugmeEXP_Backend#32
File: src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java:5-5
Timestamp: 2025-07-23T09:00:11.298Z
Learning: ReservationDetailDto와 같은 response DTO에서는 Data 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (1)
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java (1)
128-134:techItem.id및tagItem.id경로 검증 완료
TechStack 엔티티가techItem필드를, Tag 엔티티가tagItem필드를 통해 각각 TechItem/TagItem 엔티티를 참조하고 있으므로,
QueryDSL에서 사용한QTechStack.techStack.techItem.id.in(...)및QTag.tag.tagItem.id.in(...)구문은 엔티티 관계에 부합합니다.
추가 수정 사항은 없습니다.
| private BooleanExpression buildHavingClause(NumberExpression<Long> techStackCountExpr, List<Long> techStacks, Long techStackCount, | ||
| NumberExpression<Long> tagCountExpr, List<Long> tags, Long tagCount) { | ||
| BooleanExpression techExpr = techStacks != null && techStackCount != null ? techStackCountExpr.eq(techStackCount) : null; | ||
| BooleanExpression tagExpr = tags != null && tagCount != null ? tagCountExpr.eq(tagCount) : null; | ||
| return techExpr != null ? (tagExpr != null ? techExpr.and(tagExpr) : techExpr) : tagExpr; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
HAVING 절 로직을 더 읽기 쉽게 개선할 수 있습니다.
현재 구현은 정확하지만, 중첩된 삼항 연산자로 인해 가독성이 떨어집니다.
-private BooleanExpression buildHavingClause(NumberExpression<Long> techStackCountExpr, List<Long> techStacks, Long techStackCount,
- NumberExpression<Long> tagCountExpr, List<Long> tags, Long tagCount) {
- BooleanExpression techExpr = techStacks != null && techStackCount != null ? techStackCountExpr.eq(techStackCount) : null;
- BooleanExpression tagExpr = tags != null && tagCount != null ? tagCountExpr.eq(tagCount) : null;
- return techExpr != null ? (tagExpr != null ? techExpr.and(tagExpr) : techExpr) : tagExpr;
-}
+private BooleanExpression buildHavingClause(NumberExpression<Long> techStackCountExpr, List<Long> techStacks, Long techStackCount,
+ NumberExpression<Long> tagCountExpr, List<Long> tags, Long tagCount) {
+ BooleanExpression techExpr = techStacks != null && techStackCount != null ? techStackCountExpr.eq(techStackCount) : null;
+ BooleanExpression tagExpr = tags != null && tagCount != null ? tagCountExpr.eq(tagCount) : null;
+
+ if (techExpr == null) return tagExpr;
+ if (tagExpr == null) return techExpr;
+ return techExpr.and(tagExpr);
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private BooleanExpression buildHavingClause(NumberExpression<Long> techStackCountExpr, List<Long> techStacks, Long techStackCount, | |
| NumberExpression<Long> tagCountExpr, List<Long> tags, Long tagCount) { | |
| BooleanExpression techExpr = techStacks != null && techStackCount != null ? techStackCountExpr.eq(techStackCount) : null; | |
| BooleanExpression tagExpr = tags != null && tagCount != null ? tagCountExpr.eq(tagCount) : null; | |
| return techExpr != null ? (tagExpr != null ? techExpr.and(tagExpr) : techExpr) : tagExpr; | |
| } | |
| private BooleanExpression buildHavingClause(NumberExpression<Long> techStackCountExpr, List<Long> techStacks, Long techStackCount, | |
| NumberExpression<Long> tagCountExpr, List<Long> tags, Long tagCount) { | |
| BooleanExpression techExpr = techStacks != null && techStackCount != null | |
| ? techStackCountExpr.eq(techStackCount) | |
| : null; | |
| BooleanExpression tagExpr = tags != null && tagCount != null | |
| ? tagCountExpr.eq(tagCount) | |
| : null; | |
| if (techExpr == null) return tagExpr; | |
| if (tagExpr == null) return techExpr; | |
| return techExpr.and(tagExpr); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java
around lines 80 to 85, the buildHavingClause method uses nested ternary
operators that reduce readability. Refactor the method to use clear if-else
statements or separate conditional checks to build and combine techExpr and
tagExpr expressions, improving code clarity while preserving the existing logic.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java (3)
68-75: 카운트 쿼리에 GROUP BY와 HAVING 절이 여전히 누락되어 있습니다.techStacks나 tags 필터에 정확한 개수 매칭 조건이 있을 때, 카운트 쿼리가 부정확한 결과를 반환합니다. ID 조회 쿼리(35-48줄)에는 GROUP BY와 HAVING이 있지만 카운트 쿼리에는 없어서 페이지네이션 총 개수가 잘못 계산됩니다.
다음과 같이 수정하세요:
// 3. 총 개수 조회 (조건만 적용) -Long total = queryFactory - .select(r.id.countDistinct()) - .from(r) - .join(r.company, c) - .leftJoin(r.techStacks, ts) - .leftJoin(r.tags, t) - .where(buildWhereClause(r, cond)) - .fetchOne(); +List<Long> totalIds = queryFactory + .select(r.id) + .from(r) + .join(r.company, c) + .leftJoin(r.techStacks, ts) + .leftJoin(r.tags, t) + .where(buildWhereClause(r, cond)) + .groupBy(r.id) + .having(buildHavingClause(ts.id.count(), cond.getTechStacks(), cond.getTechStackCount(), + t.id.count(), cond.getTags(), cond.getTagCount())) + .fetch(); +Long total = (long) totalIds.size();
94-99: HAVING 절 로직을 더 읽기 쉽게 개선하세요.중첩된 삼항 연산자로 인해 가독성이 떨어집니다.
private BooleanExpression buildHavingClause(NumberExpression<Long> techStackCountExpr, List<Long> techStacks, Long techStackCount, NumberExpression<Long> tagCountExpr, List<Long> tags, Long tagCount) { BooleanExpression techExpr = techStacks != null && techStackCount != null ? techStackCountExpr.eq(techStackCount) : null; BooleanExpression tagExpr = tags != null && tagCount != null ? tagCountExpr.eq(tagCount) : null; - return techExpr != null ? (tagExpr != null ? techExpr.and(tagExpr) : techExpr) : tagExpr; + + if (techExpr == null) return tagExpr; + if (tagExpr == null) return techExpr; + return techExpr.and(tagExpr); }
28-78: 복잡한 필터링 로직에 대한 통합 테스트를 추가하세요.특히 다음 시나리오에 대한 테스트가 필요합니다:
- techStacks/tags 정확한 개수 매칭
- 지리적 경계 필터링
- 페이지네이션 정확성
- 여러 조건 조합 시 동작
통합 테스트 코드를 생성해드릴까요? 또는 이를 추적하기 위한 새 이슈를 생성할까요?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Roysung0625
PR: Please-Hug/HugmeEXP_Backend#32
File: src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java:5-5
Timestamp: 2025-07-23T09:00:11.298Z
Learning: ReservationDetailDto와 같은 response DTO에서는 Data 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다.
| List<RecruitmentResponseDTO> content = queryFactory | ||
| .select(new QRecruitmentResponseDTO( | ||
| r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate, | ||
| r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt | ||
| )) | ||
| .from(r) | ||
| .join(r.company, c) | ||
| .leftJoin(r.techStacks, ts) | ||
| .leftJoin(r.tags, t) | ||
| .where(r.id.in(ids)) | ||
| .fetch(); |
There was a problem hiding this comment.
콘텐츠 조회 시 정렬 순서가 보장되지 않을 수 있습니다.
ID 조회 시에는 modifiedAt.desc()로 정렬했지만, IN 절로 콘텐츠를 조회할 때는 정렬이 적용되지 않아 순서가 뒤바뀔 수 있습니다.
List<RecruitmentResponseDTO> content = queryFactory
.select(new QRecruitmentResponseDTO(
r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate,
r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt
))
.from(r)
.join(r.company, c)
.leftJoin(r.techStacks, ts)
.leftJoin(r.tags, t)
.where(r.id.in(ids))
+ .orderBy(r.modifiedAt.desc())
.fetch();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| List<RecruitmentResponseDTO> content = queryFactory | |
| .select(new QRecruitmentResponseDTO( | |
| r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate, | |
| r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt | |
| )) | |
| .from(r) | |
| .join(r.company, c) | |
| .leftJoin(r.techStacks, ts) | |
| .leftJoin(r.tags, t) | |
| .where(r.id.in(ids)) | |
| .fetch(); | |
| List<RecruitmentResponseDTO> content = queryFactory | |
| .select(new QRecruitmentResponseDTO( | |
| r.id, r.recruitmentSourceId, r.title, c.companyName, c.companyImageUrl, r.dueDate, | |
| r.experienceMin, r.experienceMax, r.workLocation, r.latitude, r.longitude, r.modifiedAt | |
| )) | |
| .from(r) | |
| .join(r.company, c) | |
| .leftJoin(r.techStacks, ts) | |
| .leftJoin(r.tags, t) | |
| .where(r.id.in(ids)) | |
| .orderBy(r.modifiedAt.desc()) | |
| .fetch(); |
🤖 Prompt for AI Agents
In
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java
around lines 55 to 65, the query fetching RecruitmentResponseDTO by IDs does not
apply any sorting, which can cause the result order to be inconsistent. To fix
this, add an orderBy clause with modifiedAt.desc() to the query to ensure the
results are sorted by modification date in descending order.
| } | ||
|
|
||
|
|
||
| private BooleanExpression buildWhereClause(QRecruitment r,RecruitmentSearchConditionDTO cond) { |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
파라미터 사이에 공백을 추가하세요.
코드 일관성을 위해 파라미터 사이에 공백을 추가하세요.
-private BooleanExpression buildWhereClause(QRecruitment r,RecruitmentSearchConditionDTO cond) {
+private BooleanExpression buildWhereClause(QRecruitment r, RecruitmentSearchConditionDTO cond) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private BooleanExpression buildWhereClause(QRecruitment r,RecruitmentSearchConditionDTO cond) { | |
| private BooleanExpression buildWhereClause(QRecruitment r, RecruitmentSearchConditionDTO cond) { |
🤖 Prompt for AI Agents
In
src/main/java/org/example/hugmeexp/domain/recruitment/repository/RecruitmentRepositoryImpl.java
at line 81, add a space between the parameters 'QRecruitment r' and
'RecruitmentSearchConditionDTO cond' in the method signature of buildWhereClause
to maintain code consistency and readability.
🚀 What’s this PR about?
🛠️ What’s been done?
🧪 Testing Details
👀 Checkpoints for Reviewers
📚 References & Resources
🎯 Related Issues
Summary by CodeRabbit
신규 기능
버그 수정
환경 설정