Skip to content

[STORY-201] 메일 스레드 데이터 누락 가능 무결성 문제 수정 - #168

Merged
asitisdev merged 4 commits into
mainfrom
201-thread-nullable-fix
Jul 12, 2026
Merged

[STORY-201] 메일 스레드 데이터 누락 가능 무결성 문제 수정#168
asitisdev merged 4 commits into
mainfrom
201-thread-nullable-fix

Conversation

@asitisdev

Copy link
Copy Markdown
Member

🔗 관련 작업

👤 User Story

📌 Task

💡 작업 내용

  • Thread 목록 응답의 nullable 제목, snippet, 참여자 이메일을 빈 문자열로 정규화했습니다.
  • Gmail 메시지의 internalDate가 누락된 경우 유효하지 않은 외부 응답으로 처리해 저장을 차단했습니다.
  • 상태 변경 스냅샷으로 누락 메시지를 저장할 때 Thread 최신 요약 정보도 함께 갱신하도록 수정했습니다.
  • 배포 DB의 불완전 Thread 9건과 하위 데이터를 정리하고 해당 Gmail Thread만 재동기화했습니다.

📝 추가 설명

1. 문제 원인 및 수정

  • saveMissingMessagesFromThreadSnapshot() 경로에서 Message는 정상 저장됐지만 Thread의 최신 제목, snippet, 참여자, 메시지 시각은 갱신되지 않았습니다.
  • 이 상태의 Thread가 목록 API에 포함되면서 프론트엔드가 nullreplace() 또는 trim()을 호출하는 런타임 오류가 발생했습니다.
  • 누락 메시지 저장 시 ThreadAggregate에 신규 또는 기존 활성 Message를 병합하고 최신 요약 정보를 Thread에 반영하도록 수정했습니다.
flowchart LR
    A[Gmail 상태 변경 이벤트] --> B[Thread 스냅샷 조회]
    B --> C[누락 Message 저장]
    C --> D[ThreadAggregate 병합]
    D --> E[Thread 최신 요약 갱신]
    E --> F[nonnull 목록 응답]
Loading

2. 응답 및 외부 연동 방어

  • ThreadSummaryResponselatestSubject, snippetMailAddressResponse.emailnull이면 빈 문자열로 변환합니다.
  • Gmail internalDate가 없으면 core는 MailSendException, worker는 MailPushException을 발생시켜 불완전 메시지 저장을 방지합니다.
  • lastMessageAt에는 임의 시각을 사용하지 않고 Gmail 메시지 시각의 유효성을 보장합니다.

3. 배포 DB 정리 및 검증

  • 활성 상태이면서 last_message_at IS NULL인 Thread 9건을 대상으로 확정했습니다.
  • FK 순서에 따라 Attachment 1건, Message 9건, Thread 9건을 하나의 트랜잭션에서 삭제했습니다.
  • 4개 Gmail 계정의 대상 Thread 9건을 mail.sync.gmail.initial.thread-batch 큐로 재동기화했습니다.
  • 재생성된 모든 Thread의 제목, snippet, 참여자, last_message_at이 유효함을 확인했습니다.
  • 전체 활성 Thread의 last_message_at IS NULL 잔여 건수는 0건이며, 동기화 DLQ도 증가하지 않았습니다.

⚡️ Test 결과

검증 항목 대상 결과 비고
core 전체 테스트 cd core && ./gradlew :test 통과 응답 정규화 및 Gmail 시각 검증 포함
worker 전체 테스트 cd worker && ./gradlew :test 통과 스냅샷 집계 및 Gmail 시각 검증 포함
코드 형식 검사 git diff --check 통과 공백 오류 없음
배포 DB 검증 재동기화 Thread 9건 통과 필수 요약 필드 모두 유효
RabbitMQ 검증 targeted sync queue / DLQ 통과 queue 0건, DLQ 증가 없음

@asitisdev
asitisdev requested a review from Copilot July 12, 2026 11:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@asitisdev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ca493fe-4ed5-4819-8921-3779cad8e175

📥 Commits

Reviewing files that changed from the base of the PR and between f340501 and 9dfd1ff.

📒 Files selected for processing (1)
  • worker/src/test/java/com/mailsangja/worker/service/mail/InitialMailSyncCommandServiceTest.java
📝 Walkthrough

Walkthrough

메일 응답의 nullable 문자열을 빈 문자열로 정규화하고, Gmail 응답의 누락된 internalDate를 예외로 처리합니다. 초기 스레드 동기화는 활성 및 soft-deleted 메시지의 집계와 스레드 요약 갱신을 보정합니다.

Changes

메일 데이터 무결성

Layer / File(s) Summary
응답 문자열 정규화
core/src/main/java/com/mailsangja/core/dto/inbox/..., core/src/test/java/com/mailsangja/core/dto/inbox/ThreadSummaryResponseTest.java
MailAddressResponseemail, ThreadSummaryResponselatestSubjectsnippetnull이면 빈 문자열로 변환되며 관련 테스트가 추가되었습니다.
Gmail 날짜 필수 검증
core/src/main/java/com/mailsangja/core/service/google/GoogleMailMessageQueryService.java, worker/src/main/java/com/mailsangja/worker/service/google/GmailMessageApiService.java, core/src/test/java/com/mailsangja/core/service/google/GoogleMailMessageQueryServiceTest.java, worker/src/test/java/com/mailsangja/worker/service/google/GoogleMailMessageQueryServiceTest.java
internalDate가 비어 있으면 기존의 null 반환 대신 서비스별 메일 예외를 발생시키고, 누락 응답에 대한 테스트를 추가했습니다.
초기 동기화 집계 보정
worker/src/main/java/com/mailsangja/worker/service/mail/InitialMailSyncCommandService.java, worker/src/test/java/com/mailsangja/worker/service/mail/InitialMailSyncCommandServiceTest.java
메시지 저장 시 전체 threadCommand를 사용하며, 활성 기존 메시지는 집계에 반영하고 soft-deleted 메시지는 새로 저장하도록 변경했습니다. 삽입 및 기존 활성 메시지에 대한 스레드 요약 테스트가 추가되었습니다.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 메일 스레드의 누락 데이터 무결성 문제 수정이라는 핵심 변경을 잘 요약합니다.
Description check ✅ Passed 설명이 응답 정규화, internalDate 검증, 스냅샷 동기화 수정 등 변경 내용과 일치합니다.
Linked Issues check ✅ Passed #167의 요구사항인 null 방지, 유효한 lastMessageAt 보장, 불완전 Thread 저장 차단을 모두 반영합니다.
Out of Scope Changes check ✅ Passed 테스트 추가와 soft-deleted 메시지 처리 보정은 이슈의 스레드 무결성 및 복구 범위 안에 있습니다.

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

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

@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@asitisdev
asitisdev merged commit e235b0e into main Jul 12, 2026
3 checks passed
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.

[TASK-219] 메일 스레드의 Nullable 데이터 무결성 문제 수정

2 participants