Skip to content

feat: 화상 면접 조회 기능 구현#84

Merged
Boyeon-Shin merged 2 commits into
mainfrom
feat/session-query-retry
Jun 1, 2026
Merged

feat: 화상 면접 조회 기능 구현#84
Boyeon-Shin merged 2 commits into
mainfrom
feat/session-query-retry

Conversation

@Boyeon-Shin
Copy link
Copy Markdown
Member

📌 관련 이슈 (Related Issue)

📝 작업 내용 (Description)

화상 면접 조회 기능 구현

🔄 변경 유형 (Type of Change)

  • ✨ 새로운 기능 (feat)
  • 🐛 버그 수정 (fix)
  • 📝 문서 수정 (docs)
  • 💄 스타일 (style)
  • ♻️ 리팩토링 (refactor)
  • ✅ 테스트 (test)
  • 🔧 기타 (chore)

✅ 체크리스트 (Checklist)

  • 코드가 정상적으로 동작하는지 확인했습니다
  • 기존 테스트가 통과합니다
  • 필요한 경우 새로운 테스트를 추가했습니다

@Boyeon-Shin Boyeon-Shin self-assigned this Jun 1, 2026
@Boyeon-Shin Boyeon-Shin merged commit 336e628 into main Jun 1, 2026
3 checks passed
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors session management by replacing SessionPersistenceService with a more comprehensive SessionService, adding a SessionController for session retrieval endpoints, and introducing corresponding response DTOs (SessionResponse and SessionDetailResponse). Feedback on these changes highlights opportunities to improve exception consistency by using BusinessException instead of IllegalArgumentException. Additionally, there are recommendations to prevent a potential NullPointerException when comparing creation timestamps and to address potential N+1 query performance issues by utilizing fetch joins on lazy-loaded associations.

Comment on lines +96 to +97
InterviewSession session = sessionRepository.findById(sessionId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 세션입니다."));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

예외 처리의 일관성을 위해 IllegalArgumentException 대신 getSessionDetail 메서드에서 사용된 것과 동일하게 BusinessException(ErrorCode.SESSION_NOT_FOUND)을 던지는 것이 좋습니다.

Suggested change
InterviewSession session = sessionRepository.findById(sessionId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 세션입니다."));
InterviewSession session = sessionRepository.findById(sessionId)
.orElseThrow(() -> new BusinessException(ErrorCode.SESSION_NOT_FOUND));

Comment on lines +108 to +109
InterviewSession session = sessionRepository.findById(sessionId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 세션입니다."));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

예외 처리의 일관성을 위해 IllegalArgumentException 대신 BusinessException(ErrorCode.SESSION_NOT_FOUND)을 던지는 것이 좋습니다.

Suggested change
InterviewSession session = sessionRepository.findById(sessionId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 세션입니다."));
InterviewSession session = sessionRepository.findById(sessionId)
.orElseThrow(() -> new BusinessException(ErrorCode.SESSION_NOT_FOUND));

Comment on lines +118 to +126
private Map<UUID, InterviewAnswer> getAnswerMap(UUID sessionId) {
return answerRepository.findBySessionId(sessionId).stream()
.collect(Collectors.toMap(
a -> a.getInterviewQuestion().getId(),
a -> a,
(existing, replacement) ->
existing.getCreatedAt().isAfter(replacement.getCreatedAt()) ? existing : replacement
));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

existing.getCreatedAt() 또는 replacement.getCreatedAt()null인 경우 isAfter() 호출 시 NullPointerException이 발생할 수 있습니다.

특히 JPA Auditing이 활성화되지 않은 테스트 환경이나 엔티티가 아직 영속화되지 않은 상태에서 테스트 대역(Mock)을 사용할 때 이러한 문제가 자주 발생합니다. 안전한 비교를 위해 null 체크 로직을 추가하는 것을 권장합니다.

또한, a.getInterviewQuestion().getId() 호출 시 InterviewQuestion과의 연관 관계가 지연 로딩(Lazy Loading)으로 설정되어 있다면 N+1 쿼리가 발생할 수 있으므로, answerRepository.findBySessionId(sessionId) 호출 시 페치 조인(Fetch Join)을 사용하는 것이 안전합니다.

    private Map<UUID, InterviewAnswer> getAnswerMap(UUID sessionId) {
        return answerRepository.findBySessionId(sessionId).stream()
                .collect(Collectors.toMap(
                        a -> a.getInterviewQuestion().getId(),
                        a -> a,
                        (existing, replacement) -> {
                            if (existing.getCreatedAt() == null) return replacement;
                            if (replacement.getCreatedAt() == null) return existing;
                            return existing.getCreatedAt().isAfter(replacement.getCreatedAt()) ? existing : replacement;
                        }
                ));
    }

Comment on lines +128 to +131
private Map<UUID, List<InterviewFeedback>> getFeedbackMap(UUID sessionId) {
return feedbackRepository.findAllBySessionId(sessionId).stream()
.collect(Collectors.groupingBy(f -> f.getInterviewAnswer().getId()));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

f.getInterviewAnswer().getId() 호출 시 InterviewAnswer와의 연관 관계가 지연 로딩(Lazy Loading)으로 설정되어 있다면, 각 피드백마다 추가적인 조회 쿼리가 실행되어 N+1 문제가 발생할 수 있습니다.

성능 최적화를 위해 feedbackRepository.findAllBySessionId(sessionId) 호출 시 InterviewAnswer를 페치 조인(Fetch Join)하여 한 번에 가져오도록 구성하는 것을 권장합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 화상 면접 결과 조회 및 재시도 기능

1 participant