TL;DR — 강좌 성적 조회의 N+1(학생×퀘스트×문제 중첩 루프, 요청당 최대 1,500 SELECT)을 IN + JOIN FETCH 단일 쿼리 + 인메모리 best-submit 선별로 1개로 줄였다. Submit 목록 조회 N+1(페이지당 21 → 1)도 함께 제거.
GradeService 성적 조회가 학생 → 퀘스트 → 문제 3중 루프, 각 루프 끝에서 submit을 건별 SELECTResultService Submit 목록은 페이지(20건)마다 result를 건별 조회 → 페이지당 21 쿼리findBy 반복) → N+11) GradeService — 배치 IN + JOIN FETCH 1쿼리 후 인메모리 선별
@Query("""
SELECT s FROM Submit s
JOIN FETCH s.result
WHERE s.problem.problemId IN :problemIds
AND s.student.userId IN :studentIds
""")
List<Submit> findAllSubmitsWithResultByProblemsAndStudents(
@Param("problemIds") List<Long> problemIds,
@Param("studentIds") List<Long> studentIds);
Map<"problemId:studentId", Submit>으로 변환(buildTopScoreMap)ORDER BY 의미를 그대로 보존2) ResultService — LEFT JOIN FETCH로 목록 N+1 제거
stream().map(s -> resultRepository.findResultBySubmit(s)) 제거