-
Notifications
You must be signed in to change notification settings - Fork 2
[FEAT] 마이페이지 면접 내역 조회 필터링/검색 추가 및 Redis 직렬화 설정 보완 #141
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
Changes from 1 commit
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 |
|---|---|---|
| @@ -1,9 +1,16 @@ | ||
| package com.aibe.team2.domain.interview.repository; | ||
|
|
||
| import com.aibe.team2.domain.interview.enums.InterviewMode; | ||
| import com.aibe.team2.domain.interview.enums.InterviewType; | ||
| import com.aibe.team2.domain.mypage.dto.response.InterviewSessionListResponse; | ||
| import org.springframework.data.domain.Page; | ||
| import org.springframework.data.domain.Pageable; | ||
|
|
||
| public interface InterviewSessionRepositoryCustom { | ||
| Page<InterviewSessionListResponse> findInterviewSessionList(Long memberId, Pageable pageable); | ||
| Page<InterviewSessionListResponse> findInterviewSessionList( | ||
| Long memberId, | ||
| InterviewType type, | ||
| String keyword, | ||
| Pageable pageable | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,16 @@ | ||
| package com.aibe.team2.domain.interview.repository; | ||
|
|
||
| import com.aibe.team2.domain.interview.enums.InterviewType; | ||
| import com.aibe.team2.domain.mypage.dto.response.InterviewSessionListResponse; | ||
| import com.querydsl.core.types.Projections; | ||
| import com.querydsl.core.types.dsl.BooleanExpression; | ||
| import com.querydsl.jpa.impl.JPAQuery; | ||
| import com.querydsl.jpa.impl.JPAQueryFactory; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.data.domain.Page; | ||
| import org.springframework.data.domain.Pageable; | ||
| import org.springframework.data.support.PageableExecutionUtils; | ||
| import org.springframework.util.StringUtils; | ||
|
|
||
| import java.util.List; | ||
|
|
||
|
|
@@ -21,7 +24,12 @@ public class InterviewSessionRepositoryImpl implements InterviewSessionRepositor | |
| private final JPAQueryFactory queryFactory; | ||
|
|
||
| @Override | ||
| public Page<InterviewSessionListResponse> findInterviewSessionList(Long memberId, Pageable pageable) { | ||
| public Page<InterviewSessionListResponse> findInterviewSessionList( | ||
| Long memberId, | ||
| InterviewType type, | ||
| String keyword, | ||
| Pageable pageable | ||
| ) { | ||
|
|
||
| // 1. 실제 데이터 조회 쿼리 | ||
| List<InterviewSessionListResponse> content = queryFactory | ||
|
|
@@ -40,7 +48,11 @@ public Page<InterviewSessionListResponse> findInterviewSessionList(Long memberId | |
| // [핵심] 엔티티에 연관관계가 없으므로 ON 절로 ID 값을 직접 매칭 | ||
| .leftJoin(resume).on(interviewSession.resumeId.eq(resume.id)) | ||
| .leftJoin(jobPosting).on(interviewSession.jobPostingId.eq(jobPosting.id)) | ||
| .where(interviewSession.memberId.eq(memberId)) | ||
| .where( | ||
| interviewSession.memberId.eq(memberId), | ||
| eqType(type), // 모드 필터 (VOICE, TEXT) | ||
| containsKeyword(keyword) // 검색어 필터 | ||
| ) | ||
| .orderBy(interviewSession.createdAt.desc()) // 최신순 정렬 | ||
| .offset(pageable.getOffset()) // 페이징 시작점 | ||
| .limit(pageable.getPageSize()) // 페이지 크기 | ||
|
|
@@ -50,9 +62,35 @@ public Page<InterviewSessionListResponse> findInterviewSessionList(Long memberId | |
| JPAQuery<Long> countQuery = queryFactory | ||
| .select(interviewSession.count()) | ||
| .from(interviewSession) | ||
| .where(interviewSession.memberId.eq(memberId)); | ||
| // ✨ [수정됨] 검색어 필터링을 위해 JOIN이 필요하므로 카운트 쿼리에도 추가 | ||
| .leftJoin(resume).on(interviewSession.resumeId.eq(resume.id)) | ||
| .leftJoin(jobPosting).on(interviewSession.jobPostingId.eq(jobPosting.id)) | ||
| // ✨ [수정됨] 데이터 쿼리와 완벽하게 동일한 조건 적용 | ||
| .where( | ||
| interviewSession.memberId.eq(memberId), | ||
| eqType(type), | ||
| containsKeyword(keyword) | ||
| ); | ||
|
|
||
| // 3. 데이터와 카운트 쿼리를 조합하여 Page 객체로 반환 | ||
| return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); | ||
| } | ||
|
|
||
| // 면접 모드(VOICE, TEXT) 일치 여부 확인 | ||
| private BooleanExpression eqType(InterviewType type) { | ||
| if (type == null) { | ||
| return null; // 프론트에서 값이 안 오면(전체 조회 시) 조건을 무시 | ||
| } | ||
| return interviewSession.interviewType.eq(type.name()); | ||
| } | ||
|
||
|
|
||
| // 검색어 포함 여부 확인 | ||
| private BooleanExpression containsKeyword(String keyword) { | ||
| if (!StringUtils.hasText(keyword)) { | ||
| return null; // 검색어가 없으면 조건을 무시 | ||
| } | ||
| // 예시: 회사명 또는 자기소개서 제목에 검색어가 포함되어 있는지 검사 | ||
| return jobPosting.companyName.contains(keyword) | ||
| .or(resume.title.contains(keyword)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| package com.aibe.team2.domain.mypage.controller; | ||
|
|
||
| import com.aibe.team2.domain.auth.dto.CustomUserDetails; | ||
| import com.aibe.team2.domain.interview.enums.InterviewType; | ||
| import com.aibe.team2.domain.mypage.dto.response.InterviewSessionListResponse; | ||
| import com.aibe.team2.domain.mypage.service.MypageInterviewService; | ||
| import com.aibe.team2.global.common.annotation.LoginMemberId; | ||
|
|
@@ -10,7 +10,6 @@ | |
| import org.springframework.data.domain.PageRequest; | ||
| import org.springframework.data.domain.Pageable; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
|
|
@@ -28,10 +27,13 @@ public class MypageInterviewController { | |
| public ResponseEntity<Page<InterviewSessionListResponse>> getInterviewSessionList( | ||
| @LoginMemberId Long memberId, | ||
| @RequestParam(defaultValue = "0") int page, | ||
| @RequestParam(defaultValue = "10") int size | ||
| @RequestParam(defaultValue = "10") int size, | ||
| @RequestParam(name = "mode", required = false) InterviewType type, | ||
|
||
| @RequestParam(required = false) String keyword | ||
| ) { | ||
| Pageable pageRequest = PageRequest.of(page, size); | ||
| Page<InterviewSessionListResponse> response = mypageInterviewService.getInterviewSessionList(memberId, pageRequest); | ||
| Page<InterviewSessionListResponse> response = | ||
| mypageInterviewService.getInterviewSessionList(memberId, type, keyword, pageRequest); | ||
|
|
||
| return ResponseEntity.ok(response); | ||
| } | ||
|
|
||
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.
내용 조회 쿼리와 개수 조회 쿼리에서
from,leftJoin,where절이 중복되고 있습니다. 이는 향후 쿼리 조건이 변경될 때 두 군데를 모두 수정해야 하므로 유지보수 과정에서 실수를 유발할 수 있습니다.다음과 같이 공통 쿼리 로직을 추출하여 중복을 제거하는 리팩토링을 고려해볼 수 있습니다.
JPAQuery는 상태를 가지므로,clone()을 사용하여 각 쿼리를 독립적으로 실행하는 것이 중요합니다.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.
수정완료