[FEAT] 마이페이지 면접 내역 조회 필터링/검색 추가 및 Redis 직렬화 설정 보완#141
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 PR은 사용자 마이페이지의 면접 세션 목록 조회 기능을 강화하고, Redis 데이터 처리의 안정성을 높입니다. 사용자는 이제 면접 모드별로 필터링하거나 특정 키워드로 면접 내역을 검색할 수 있어, 자신의 면접 기록을 더욱 효율적으로 관리하고 탐색할 수 있게 됩니다. 또한, Redis에 BigDecimal 타입의 데이터가 올바르게 저장되도록 직렬화 설정을 보완하여 데이터 무결성을 확보했습니다. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
이 PR은 마이페이지의 면접 내역 조회 기능에 필터링과 검색을 추가하고, Redis 직렬화 설정을 개선하는 내용을 담고 있습니다. 전반적으로 기능 구현은 잘 이루어졌습니다. QueryDSL을 사용한 동적 쿼리 구현과 카운트 쿼리 동기화 등 좋은 변경 사항들이 포함되어 있습니다. 다만, 코드의 유지보수성과 명확성을 높이기 위한 몇 가지 개선점을 제안합니다. 컨트롤러에서 요청 파라미터 이름과 변수 이름의 일관성을 맞추고, Repository 구현체에서 중복되는 쿼리 로직을 리팩토링하는 것을 고려해볼 수 있습니다. 또한, 더 타입-안전한 코드를 위해 엔티티 필드 타입에 대한 제안도 포함했습니다. 자세한 내용은 각 파일의 주석을 참고해주세요.
| 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) | ||
| ); |
There was a problem hiding this comment.
내용 조회 쿼리와 개수 조회 쿼리에서 from, leftJoin, where 절이 중복되고 있습니다. 이는 향후 쿼리 조건이 변경될 때 두 군데를 모두 수정해야 하므로 유지보수 과정에서 실수를 유발할 수 있습니다.
다음과 같이 공통 쿼리 로직을 추출하여 중복을 제거하는 리팩토링을 고려해볼 수 있습니다.
@Override
public Page<InterviewSessionListResponse> findInterviewSessionList(
Long memberId,
InterviewType type,
String keyword,
Pageable pageable
) {
// from, join, where 절을 포함하는 기본 쿼리
JPAQuery<?> baseQuery = queryFactory
.from(interviewSession)
.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)
);
// 1. 실제 데이터 조회 쿼리 (select, orderBy, offset, limit 추가)
List<InterviewSessionListResponse> content = baseQuery.clone() // baseQuery를 복제하여 사용
.select(Projections.constructor(InterviewSessionListResponse.class,
interviewSession.id,
resume.title,
jobPosting.companyName,
jobPosting.jobTitle,
interviewSession.interviewMode,
interviewSession.interviewType,
interviewSession.status,
interviewSession.finalScore,
interviewSession.createdAt
))
.orderBy(interviewSession.createdAt.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
// 2. 전체 개수 조회 쿼리 (select count 추가)
JPAQuery<Long> countQuery = baseQuery.clone().select(interviewSession.count());
// 3. 데이터와 카운트 쿼리를 조합하여 Page 객체로 반환
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
}JPAQuery는 상태를 가지므로, clone()을 사용하여 각 쿼리를 독립적으로 실행하는 것이 중요합니다.
| private BooleanExpression eqType(InterviewType type) { | ||
| if (type == null) { | ||
| return null; // 프론트에서 값이 안 오면(전체 조회 시) 조건을 무시 | ||
| } | ||
| return interviewSession.interviewType.eq(type.name()); | ||
| } |
There was a problem hiding this comment.
InterviewSession 엔티티의 interviewType 필드가 String 타입이어서 type.name()으로 비교하고 있습니다. 이는 enum의 이름이 변경될 경우 문제가 발생할 수 있어 잠재적으로 취약합니다. 장기적인 유지보수성을 위해 InterviewSession 엔티티의 interviewType 필드 타입을 InterviewType enum으로 변경하고 @Enumerated(EnumType.STRING)을 사용하는 것을 고려해보세요. 이렇게 하면 interviewSession.interviewType.eq(type)와 같이 타입-안전한 비교가 가능해져 코드가 더 견고해집니다.
| @RequestParam(defaultValue = "0") int page, | ||
| @RequestParam(defaultValue = "10") int size | ||
| @RequestParam(defaultValue = "10") int size, | ||
| @RequestParam(name = "mode", required = false) InterviewType type, |
There was a problem hiding this comment.
작업 내용
변경 사항 및 주의 사항
체크 리스트