Skip to content

feat : Long 타입 반환 메서드에 Query 추가#364

Merged
crocusia merged 2 commits intodevfrom
fix/363
Aug 11, 2025
Merged

feat : Long 타입 반환 메서드에 Query 추가#364
crocusia merged 2 commits intodevfrom
fix/363

Conversation

@crocusia
Copy link
Copy Markdown
Member

@crocusia crocusia commented Aug 11, 2025

🔎 작업 내용

  • mailLogRepository 수정 - Query 추가
  • 메일 발송 실패 로그 추가

🧩 트러블 슈팅

  • JPA에서 기본적으로 mailLog 타입을 반환하는데
    Long 타입을 조회하면서 타입이 불일치해서 발생한 문제로 추정됨

close #363

Summary by CodeRabbit

  • New Features
    • 구독별 퀴즈 이력 집계를 조회할 수 있도록 기능을 추가하고, 발송 완료된 항목만 집계하도록 조회 동작을 개선했습니다.
  • Chores
    • 메일 발송 실패 시 구독 ID와 원인을 기록해 문제 추적성을 강화했습니다.
  • Refactor
    • 변수명을 정리해 코드 가독성과 유지보수성을 개선했습니다.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Aug 11, 2025

Walkthrough

MailLogAspect에서 finally 블록의 로컬 변수명 변경 및 실패 경로에 상세 로그 추가; MailLogRepository에 구독별로 발송된(SENT) 퀴즈 ID 집합을 반환하는 JPQL 쿼리 주석이 추가됨. 제어 흐름과 Redis 재시도 로직은 변경 없음.

Changes

Cohort / File(s) Summary
AOP 메일 로그 처리
cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java
finally 블록에서 로컬 변수명 logmailLog로 변경. MailStatus.FAILED 분기에서 subscriptionIdcause를 기록하는 log.info(...) 추가. MailLog 빌드·저장 및 Redis 재시도 로직은 유지.
메일 로그 리포지토리 쿼리 추가
cs25-entity/src/main/java/com/example/cs25entity/domain/mail/repository/MailLogRepository.java
findDistinctQuiz_IdBySubscription_Id(Long)에 JPQL @Query 추가하여 ml.subscription.id = :subscriptionIdml.status = ...MailStatus.SENT로 필터링해 DISTINCT quiz ID Set 반환; @Param("subscriptionId") 사용.

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler
  participant MailSender
  participant MailLogAspect
  participant MailLogRepo
  participant Redis

  Scheduler->>MailSender: sendMail(subscription, quiz)
  MailSender->>MailLogAspect: around advice
  alt send success
    MailLogAspect->>MailLogRepo: save(MailLog(status=SENT))
  else send failed
    MailLogAspect->>MailLogAspect: log.info(subscriptionId, cause)
    MailLogAspect->>MailLogRepo: save(MailLog(status=FAILED, caused=...))
    MailLogAspect->>Redis: enqueue retry (unchanged)
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Assessment against linked issues

Objective Addressed Explanation
문제 발송 안되는 버그 원인 파악을 위한 로깅 보강 (#363)
문제 발송 실패 시 재시도/흐름 수정으로 실제 미발송 문제 해결 (#363) 재시도 로직이나 제어 흐름 변경은 코드상 확인되지 않음.
발송 이력 기반 중복 발송 방지/문제 선택 로직 보완 (#363) SENT 필터 쿼리 추가는 있으나 호출부 반영 여부가 PR에서 확인되지 않음.

Possibly related PRs

Suggested reviewers

  • Kimyoonbeom
  • HeeMang-Lee
  • wannabeing

Poem

깡총깡총, 로그를 남긴 토끼예요 🐇
실패에선 이유도 콕, 기록을 해요.
퀴즈 ID 모아 중복은 컷!
재시도 줄은 그대로, 길을 다시 찾네.
배포 뒤에도 토끼는 깡충, 웃음 한 줌.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/363

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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
  • 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 src/utils.ts and explain its main purpose.
    • @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 comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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.

Copy link
Copy Markdown
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: 2

🔭 Outside diff range comments (1)
cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java (1)

46-50: MailLogAspect: 실패 로깅 레벨 및 예외 원인 보존 강화

  • 현재 catch 블록에서 throw new CustomMailException(MailExceptionCode.EMAIL_SEND_FAILED_ERROR); 로직으로 인해 원인 예외가 손실됩니다.
    → CustomMailException에 (MailExceptionCode, Throwable) 생성자 오버로드를 추가하거나, 최소한 로그에 스택트레이스를 남겨야 합니다.
  • 운영 가시성을 위해 log.info 대신 log.warn 이상으로 로그 레벨을 상향하세요.

조치 대상:

  • cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java (46–50)
  • CustomMailException 클래스
    (cs25-entity/src/main/java/com/example/cs25entity/domain/mail/exception/CustomMailException.java)

수정 예시:

--- a/cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java
+++ b/cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java
         } catch (Exception e) {
             status = MailStatus.FAILED;
-            caused = e.getMessage();
-            throw new CustomMailException(MailExceptionCode.EMAIL_SEND_FAILED_ERROR);
+            caused = e.getMessage();
+            // 스택트레이스를 보존하고 WARN 레벨로 기록
+            log.warn("메일 발송 실패 : subscriptionId - {}, cause - {}", subscription.getId(), caused, e);
+            // CustomMailException에 (MailExceptionCode, Throwable) 생성자 추가 후 사용
+            throw new CustomMailException(MailExceptionCode.EMAIL_SEND_FAILED_ERROR, e);
         } finally {
             …

그리고 다음과 같이 생성자를 추가하세요:

// cs25-entity/src/main/java/com/example/cs25entity/domain/mail/exception/CustomMailException.java
public CustomMailException(MailExceptionCode errorCode, Throwable cause) {
    super(cause);
    this.errorCode = errorCode;
    this.httpStatus = errorCode.getHttpStatus();
    this.message = errorCode.getMessage();
}
🧹 Nitpick comments (1)
cs25-entity/src/main/java/com/example/cs25entity/domain/mail/repository/MailLogRepository.java (1)

32-32: 메서드명 가독성 개선 제안(선택)

@query를 사용하는 만큼 파스 경로 기반 네이밍(언더스코어) 규칙을 따를 필요는 없습니다. 도메인 의도가 더 명확하도록 아래와 같이 권장합니다.

-Set<Long> findDistinctQuiz_IdBySubscription_Id(@Param("subscriptionId") Long subscriptionId);
+Set<Long> findDistinctQuizIdsBySubscriptionId(@Param("subscriptionId") Long subscriptionId);

SENT로 한정한 경우라면 더 구체적으로:

Set<Long> findDistinctSentQuizIdsBySubscriptionId(@Param("subscriptionId") Long subscriptionId);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b256946 and 347c636.

📒 Files selected for processing (2)
  • cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java (1 hunks)
  • cs25-entity/src/main/java/com/example/cs25entity/domain/mail/repository/MailLogRepository.java (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: crocusia
PR: NBC-finalProject/CS25-BE#191
File: cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java:60-60
Timestamp: 2025-06-27T09:00:50.236Z
Learning: MailLogAspect에서 mailLogRepository.flush() 호출은 배치 처리의 청크 단위 트랜잭션과 비동기 처리 환경에서 MailLog의 즉시 영속화가 필요해서 추가된 것입니다. 재시도 로직이 실패한 메일 로그에 즉시 접근해야 하는 요구사항 때문에 필수적입니다.
🔇 Additional comments (3)
cs25-entity/src/main/java/com/example/cs25entity/domain/mail/repository/MailLogRepository.java (2)

10-10: 필요한 @query 임포트 추가 적절합니다

쿼리 기반 메서드 추가에 필요한 임포트가 정확합니다.


27-31: Java Text Block 지원을 위한 Java 버전 설정 확인 필요

@Query(""" … """)에 사용된 Java Text Block 문법은 JDK 15 이상에서만 지원됩니다.
현재 각 모듈의 build.gradlepom.xml에서 sourceCompatibility/targetCompatibility 또는 Java Toolchain 설정이 검색되지 않아, 실제 컴파일 환경의 Java 버전이 불분명합니다. 최소 JDK 15(권장 JDK 17) 이상으로 설정되어 있는지 확인해주세요.

점검 및 조치 사항:

  • cs25-entity를 포함한 모든 Gradle 모듈의 build.gradle
    java {
      toolchain {
        languageVersion = JavaLanguageVersion.of(17)
      }
    }
    또는
    sourceCompatibility = '17'
    targetCompatibility = '17'
    설정 추가
  • Maven 모듈이 존재한다면 pom.xml<maven-compiler-plugin>
    <configuration>
      <release>17</release>
    </configuration>
    또는 <source>17</source>/<target>17</target> 적용
  • 공통 설정(예: Gradle 설정 플러그인) 또는 CI 빌드 환경에서도 JDK 15+ 이상 사용 보장
cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java (1)

51-57: 변수명 정리 및 save/flush 동작 유지 👍

  • logmailLog로 명확해졌습니다.
  • saveflush 유지도 적절합니다. 과거 러닝(배치/비동기 재시도 로직에서 MailLog 즉시 영속화 필요)에 부합합니다.

Also applies to: 59-60

Copy link
Copy Markdown
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 (2)
cs25-entity/src/main/java/com/example/cs25entity/domain/mail/repository/MailLogRepository.java (2)

28-34: 선택: 재사용성 향상 — 상태 파라미터화된 오버로드 추가 제안

현재 메서드는 SENT로 고정되어 있어 목적에는 정확합니다. 다만 재사용성을 위해 동일 쿼리의 상태 파라미터 버전을 오버로드로 추가하는 것을 권장합니다(기존 메서드는 유지).

아래 메서드를 추가하는 방식을 고려해 주세요:

import com.example.cs25entity.domain.mail.enums.MailStatus;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

@Query("""
    select distinct ml.quiz.id
    from MailLog ml
    where ml.subscription.id = :subscriptionId
      and ml.status = :status
""")
Set<Long> findDistinctQuizIdsBySubscriptionIdAndStatus(
    @Param("subscriptionId") Long subscriptionId,
    @Param("status") MailStatus status
);

추가로, 현 메서드의 의미가 “SENT만 반환”임을 드러내기 위해 Javadoc을 붙이거나(비파괴), 장기적으로는 메서드명을 findDistinctSentQuizIdsBySubscriptionId로 리네이밍하는 방안도 고려할 수 있습니다.


28-34: 선택: 성능 최적화 — 인덱스, 읽기 전용 힌트 고려

대량 데이터 기준 최적화를 위해 다음을 검토해 주세요.

  • DB 인덱스: subscription_id, status, quiz_id 복합 인덱스 추가.
  • 읽기 전용 힌트: Hibernate read-only 힌트를 걸어 불필요한 더티 체킹 방지.

예시(엔티티에 인덱스 추가 — 실제 컬럼명 확인 필요):

// MailLog 엔티티
@Table(
  name = "mail_log",
  indexes = {
    @Index(name = "idx_mail_log_subscription_status_quiz",
           columnList = "subscription_id,status,quiz_id")
  }
)

예시(읽기 전용 힌트):

import jakarta.persistence.QueryHint;
import org.springframework.data.jpa.repository.QueryHints;

@QueryHints(@QueryHint(name = "org.hibernate.readOnly", value = "true"))
@Query("""
    select distinct ml.quiz.id
    from MailLog ml
    where ml.subscription.id = :subscriptionId
      and ml.status = com.example.cs25entity.domain.mail.enums.MailStatus.SENT
""")
Set<Long> findDistinctQuiz_IdBySubscription_Id(@Param("subscriptionId") Long subscriptionId);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 347c636 and f30f74f.

📒 Files selected for processing (1)
  • cs25-entity/src/main/java/com/example/cs25entity/domain/mail/repository/MailLogRepository.java (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: crocusia
PR: NBC-finalProject/CS25-BE#191
File: cs25-batch/src/main/java/com/example/cs25batch/aop/MailLogAspect.java:60-60
Timestamp: 2025-06-27T09:00:50.236Z
Learning: MailLogAspect에서 mailLogRepository.flush() 호출은 배치 처리의 청크 단위 트랜잭션과 비동기 처리 환경에서 MailLog의 즉시 영속화가 필요해서 추가된 것입니다. 재시도 로직이 실패한 메일 로그에 즉시 접근해야 하는 요구사항 때문에 필수적입니다.
🔇 Additional comments (3)
cs25-entity/src/main/java/com/example/cs25entity/domain/mail/repository/MailLogRepository.java (3)

10-11: 임포트 및 @param 추가 적절 — 파라미터 바인딩 안정화

명시적 @Param과 관련 임포트 추가로 런타임 바인딩 이슈 없이 안전합니다. LGTM.


28-34: SENT 상태로 필터링 + distinct 선택 — 요구사항에 부합하고 이전 피드백도 해소됨

FAILED 로그가 포함되지 않도록 SENT로 제한했고, distinct로 중복 제거까지 충족합니다. 이전 리뷰에서 제기된 상태 필터 및 파라미터 바인딩 이슈가 모두 해결되었습니다.


28-34: 영향 범위 확인 완료 — 기존 호출부에서 모두 ‘SENT’ 전용으로 사용 중

기존 호출부를 확인한 결과, 아래 두 곳 모두 반환값을 sentQuizIds로 명명하고 size()를 통해 “전송된 문제 수”로만 처리하고 있어, FAILED 로그 제외에 따른 추가 영향은 없습니다.

  • cs25-service/src/main/java/com/example/cs25service/domain/quiz/service/QuizAccuracyCalculateService.java (라인 45)
  • cs25-batch/src/main/java/com/example/cs25batch/batch/service/TodayQuizService.java (라인 48)

추가 조치 없이 머지 가능합니다.

@crocusia crocusia merged commit b508652 into dev Aug 11, 2025
2 checks passed
@crocusia crocusia deleted the fix/363 branch August 11, 2025 05:37
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.

10차 배포 후, 문제 발송 안되는 버그 수정

3 participants