Skip to content

feat : 서술형 문제 없을 시, 객관식 문제를 출제하도록 fallback 구조 도입#418

Merged
crocusia merged 1 commit intodevfrom
refactor/417
Aug 20, 2025
Merged

feat : 서술형 문제 없을 시, 객관식 문제를 출제하도록 fallback 구조 도입#418
crocusia merged 1 commit intodevfrom
refactor/417

Conversation

@crocusia
Copy link
Member

@crocusia crocusia commented Aug 20, 2025

🔎 작업 내용

  • 서술형 문제를 받는 날이지만 서술형 문제가 없는 경우, 문제가 발송되지 않고 queue에 그대로 남아있는 문제를 확인 후 fallback 구조 도입

🛠️ 변경 사항

  • 대체 type 준비 후 배열을 통해 서술형이 없는 경우 객관식 문제로 변환해서 1번 더 조회

🧩 트러블 슈팅

  • 로컬 테스트 시, docker 업데이트 필요!!

🧯 해결해야 할 문제

  • 객관식 문제도 다 떨어진다면?
  • 예외가 던져져서 삭제되지 않은 queue의 메시지를 어떻게 처리해야 할까?

Summary by CodeRabbit

  • New Features

    • 날짜에 따라 서술형과 객관식 퀴즈를 번갈아 제공하여 학습 경험을 다양화했습니다.
  • Bug Fixes

    • 단일 조건에서 퀴즈가 없던 상황을 개선했습니다. 여러 조건과 오프셋을 순차적으로 탐색해 퀴즈 제공 가능성을 높였으며, 적절한 안내가 이뤄지도록 처리했습니다.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 20, 2025

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 Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/417

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@crocusia crocusia merged commit 264ebd5 into dev Aug 20, 2025
1 of 2 checks passed
@crocusia crocusia deleted the refactor/417 branch August 20, 2025 08:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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.

📥 Commits

Reviewing files that changed from the base of the PR and between 62dde4e and 8005abd.

📒 Files selected for processing (1)
  • cs25-batch/src/main/java/com/example/cs25batch/batch/service/TodayQuizService.java (5 hunks)

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.

2 participants