feat : 서술형 문제 없을 시, 객관식 문제를 출제하도록 fallback 구조 도입#418
Conversation
|
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ 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/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
cs25-batch/src/main/java/com/example/cs25batch/batch/service/TodayQuizService.java (6)
37-38: 동일 클래스 내부 호출 시 @transactional(readOnly = true)가 적용되지 않을 수 있음issueTodayQuiz(…)가 같은 클래스 내에서 getTodayQuizBySubscription(…)을 직접 호출하므로, Spring AOP 프록시를 우회하여 메서드 단위 트랜잭션 속성이 적용되지 않을 수 있습니다. 현재는 외부에서 이 메서드를 직접 호출할 때만 readOnly 힌트가 의미가 있고, 내부 호출 경로에서는 상위 트랜잭션(쓰기 트랜잭션) 속성이 그대로 유지됩니다. 의도한 최적화/일관성이 필요한지 확인 부탁드립니다. 필요하다면 읽기 전용 전용 빈으로 분리하거나, 자기주입(self-injection) 등 프록시를 통해 호출하도록 구조화하는 방법이 있습니다.
58-61: 변수명 alterType은 의도가 모호합니다 — fallbackType(또는 alternateType)으로 명확화 제안가독성을 위해 폴백 용도를 드러내는 이름이 더 적합해 보입니다.
적용 예시:
- QuizFormatType alterType = isEssayDay - ? QuizFormatType.MULTIPLE_CHOICE - : QuizFormatType.SUBJECTIVE; + QuizFormatType fallbackType = isEssayDay + ? QuizFormatType.MULTIPLE_CHOICE + : QuizFormatType.SUBJECTIVE;
71-90: 주석 처리된 이전 구현은 제거 권장히스토리는 VCS(git)가 관리하므로, 대량의 주석 처리된 코드 블록은 유지보수와 가독성을 해칩니다. 삭제하고 커밋 메시지로 의도를 남기는 편이 깔끔합니다.
- /* Quiz todayQuiz = quizRepository.findAvailableQuizzesUnderParentCategory( - parentCategoryId, - allowedDifficulties, - sentQuizIds, - //excludedCategoryIds, - targetType, - offset - ); - - // offset이 너무 커서 결과가 없을 수 있으므로, offset=0으로 한 번 더 조회 - if (todayQuiz == null && offset > 0) { - todayQuiz = quizRepository.findAvailableQuizzesUnderParentCategory( - parentCategoryId, - allowedDifficulties, - sentQuizIds, - targetType, - 0 - ); - }*/
91-97: 현재 구현은 ‘객관식 날’에도 서술형으로 폴백합니다 — 요구사항 확인 필요PR 요약은 “서술형이 없을 때 객관식으로 폴백”을 강조합니다. 하지만 지금 로직은 비-서술형 날에도 서술형으로 폴백합니다. 의도된 대칭 폴백인지 확인 부탁드립니다. 만약 서술형 날에만 폴백하려면 아래처럼 타입 시도 순서를 조건화하세요. 또한, 앞서 제안한 변수명 변경(fallbackType)도 반영합니다.
- QuizFormatType[] typesToTry = new QuizFormatType[]{targetType, alterType}; + QuizFormatType[] typesToTry = isEssayDay + ? new QuizFormatType[]{targetType, fallbackType} // 서술형 날: 서술형 → 객관식 폴백 + : new QuizFormatType[]{targetType}; // 그 외 날: 폴백 없음추가 제안:
- 단위 테스트 보강: (1) 서술형 날에 서술형 없음 → 객관식 선택, (2) 서술형/객관식 모두 없음 → 예외, (3) 비-서술형 날에 객관식 없음 → 선택 불가 유지. Mock 리포지토리 기반으로 빠르게 작성 가능합니다. 원하시면 테스트 템플릿을 드리겠습니다.
- offset 시드(LocalDate.now())는 서버 타임존 영향이 있으니 서비스 표준 타임존 또는 구독자 타임존 기준 사용 여부를 검토해 주세요.
98-101: 실패 로그에 시도한 type/offset을 포함하고, 경고 레벨로 상향 권장운영 관점에서 어떤 타입/오프셋을 시도했는지 남겨두면 추적이 쉬워집니다. 또한 출제 실패는 운영 경고에 가까우므로 warn 레벨이 적합합니다.
- log.info("subscription_id {} - 문제 출제 실패, targetType {}", subscriptionId, targetType); + log.warn("subscription_id={} - 문제 출제 실패 (typesTried={}, offsetsTried={})", + subscriptionId, + java.util.Arrays.toString(typesToTry), + java.util.Arrays.toString(offsetsToTry));참고:
- 큐 처리 정책: 본 예외 발생 시 메시지 ACK/NACK/Retry 정책에 따라 “삭제되지 않은 큐 메시지”가 남는 문제가 재현될 수 있습니다(남은 문제/고려사항에 언급하신 이슈). 컨슈머/잡 레이어에서 예외 매핑, 재시도 횟수, DLQ 라우팅 등을 명확히 하거나, 출제 실패 사유를 기록하고 메시지는 ACK하는 방식을 검토해 주세요. 필요하시면 표준 재시도/백오프 설정 샘플을 제공하겠습니다.
119-134: 시도/성공 지표를 남기도록 tryFindQuiz에 디버그 로그 추가 제안운영 이슈 추적 시 어떤 조합(type, offset)에서 성공/실패했는지가 유용합니다. 디버그 레벨로 남기면 평소에는 조용하고, 필요 시에만 활성화할 수 있습니다.
private Quiz tryFindQuiz(Long parentCategoryId, List<QuizLevel> difficulties, Set<Long> sentQuizIds, QuizFormatType[] types, int[] offsets) { - for (QuizFormatType type : types) { - for (int off : offsets) { - Quiz q = quizRepository.findAvailableQuizzesUnderParentCategory( - parentCategoryId, difficulties, sentQuizIds, type, off - ); - if (q != null) return q; - } - } + for (QuizFormatType type : types) { + for (int off : offsets) { + if (log.isDebugEnabled()) { + log.debug("tryFindQuiz - trying type={}, offset={}", type, off); + } + Quiz q = quizRepository.findAvailableQuizzesUnderParentCategory( + parentCategoryId, difficulties, sentQuizIds, type, off + ); + if (q != null) { + if (log.isDebugEnabled()) { + // q.getId() 사용이 안전한지(프록시/LAZY) 검토. 불안하면 생략 가능. + try { + log.debug("tryFindQuiz - selected quizId={}, type={}, offset={}", + q.getId(), type, off); + } catch (Exception ignore) { + log.debug("tryFindQuiz - selected quiz (id unknown), type={}, offset={}", type, off); + } + } + return q; + } + } + } return null; }추가 아이디어(선택):
- 정말로 아무 것도 안 나올 때, 마지막 단계에서 난이도 범위를 한 단계 완화하는 “난이도 폴백”도 고려할 수 있습니다. 현재 PR 범위를 넘는다면 무시하셔도 됩니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
cs25-batch/src/main/java/com/example/cs25batch/batch/service/TodayQuizService.java(5 hunks)
🔎 작업 내용
🛠️ 변경 사항
🧩 트러블 슈팅
🧯 해결해야 할 문제
Summary by CodeRabbit
New Features
Bug Fixes