Skip to content

[fix] 신고 중복 문제 해결 - #136

Merged
dldusgh318 merged 2 commits into
developfrom
fix/135-report
Feb 2, 2026
Merged

[fix] 신고 중복 문제 해결#136
dldusgh318 merged 2 commits into
developfrom
fix/135-report

Conversation

@dldusgh318

@dldusgh318 dldusgh318 commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

#️⃣연관된 이슈

close #135

📝작업 내용

  1. 신고 버튼을 빠르게 여러번 클릭 시, db에 데이터가 누른 횟수만큼 저장되는 문제
    -> Transactional과 alreadyExistReport 검사 사이에 Race Condition이 발생한 거 같아 유니크 제약 조건과 try-catch문 추가했습니다

  2. 신고 후 API 응답까지 시간이 오래 걸리는 문제
    -> 이메일 전송까지 동기적으로 처리되어 오래 걸린 거 같습니다 이메일 전송은 Async처리 했습니다.

스크린샷

image 스웨거로 더블클릭하며 테스트해본 결과 디비에도 한번만 저장되는 거 확인했습니다.

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

Summary by CodeRabbit

  • 버그 수정

    • 사용자별 중복 리포트 제출 방지 로직 강화(중복 검증 및 저장 시 제약 위반 처리)
    • 리포트 이메일 전송 실패 시 예외를 내부에서 처리하도록 개선
  • 개선 사항

    • 애플리케이션 수준 비동기 처리 활성화로 전반적 응답성 향상
    • 이메일 알림 전송을 비동기로 실행해 성능 최적화
    • 관련 오류 코드 값 정비 및 데이터 무결성 검증 개선

@dldusgh318 dldusgh318 self-assigned this Feb 2, 2026
@dldusgh318 dldusgh318 added ♻️ refactor 코드 리팩토링 🐛 fix labels Feb 2, 2026
@dldusgh318 dldusgh318 linked an issue Feb 2, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Feb 2, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Walkthrough

신고 처리에 사용자 단위 중복 제약을 추가하고, 저장 중 제약 위반을 예외로 처리하도록 변경했으며 애플리케이션 수준 비동기(@EnableAsync)와 MailService의 비동기 전송(@Async, 로깅)을 도입했습니다.

Changes

Cohort / File(s) Summary
애플리케이션 설정
src/main/java/teamficial/teamficial_be/TeamficialBeApplication.java
애플리케이션 클래스에 @EnableAsync 추가로 스프링 비동기 처리 활성화
신고 엔티티
src/main/java/teamficial/teamficial_be/domain/report/entity/Report.java
엔티티에 (user_id, reported_comment_id) 복합 유니크 제약 추가 및 reportedCommentId 컬럼명 명시(reported_comment_id)
리포지토리
src/main/java/teamficial/teamficial_be/domain/report/repository/ReportRepository.java
존재 확인 API 변경: existsByReportedCommentId(Long)existsByReportedCommentIdAndUserId(Long, Long) (userId 포함)
서비스 — 신고 처리
src/main/java/teamficial/teamficial_be/domain/report/service/ReportService.java
중복 검사 메서드에 userId 인자 추가로 사용자 기준 중복 검사 수행, reportRepository.save에 대해 DataIntegrityViolationException을 잡아 GeneralException(REPORT_DUPLICATE)로 전환
서비스 — 메일
src/main/java/teamficial/teamficial_be/domain/report/service/MailService.java
클래스에 @Slf4j 추가, sendReportEmail@Async 적용 및 예외 캐치 후 로깅 처리(비동기 이메일 전송)
에러 코드
src/main/java/teamficial/teamficial_be/global/apiPayload/code/status/ErrorStatus.java
REPORT_DUPLICATE의 코드 값을 "REPORT4001"에서 "REPORT409"로 변경

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as 사용자
    participant Controller as ReportController
    participant Service as ReportService
    participant Repo as ReportRepository
    participant DB as Database
    participant Mail as MailService (Async)

    Client->>Controller: 신고 요청 (commentId, userId, ... )
    Controller->>Service: reportTeamficialLog(...)
    Service->>Repo: existsByReportedCommentIdAndUserId(commentId, userId)
    Repo->>DB: SELECT EXISTS ...
    DB-->>Repo: 존재 여부 반환
    Repo-->>Service: exists? false
    Service->>Repo: save(report)
    Repo->>DB: INSERT report
    DB-->>Repo: 성공 or ConstraintViolation
    alt insert success
        Service-->>Mail: sendReportEmail(toEmail) (비동기)
        Mail-->>Service: (비동기 실행, 로그 처리 on error)
        Service-->>Controller: 생성 완료 응답
        Controller-->>Client: 201 Created
    else constraint violation
        Repo-->>Service: DataIntegrityViolationException
        Service-->>Controller: throw GeneralException(REPORT_DUPLICATE)
        Controller-->>Client: 409 Conflict (REPORT_DUPLICATE)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 '[fix] 신고 중복 문제 해결'은 핵심 변경사항인 신고 중복 문제 해결을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 #135의 요구사항을 충족하고 있습니다: 유니크 제약 조건 추가, try-catch 예외 처리, 비동기 이메일 처리를 통해 신고 중복 문제를 해결했습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 신고 중복 문제 해결을 위한 범위 내 변경입니다. @EnableAsync 추가는 비동기 처리 구현에 필요하며 범위 내입니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/135-report

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@src/main/java/teamficial/teamficial_be/domain/report/service/MailService.java`:
- Around line 31-33: The catch block in MailService currently logs raw PII
(toEmail); replace that with a non-PII identifier (e.g., userId) or a masked
email before logging: update the catch in the method in MailService to log
either the associated userId or call a new/existing maskEmail(toEmail) helper
and pass that masked value to log.error instead of toEmail, keeping the
exception e as the last parameter; ensure maskEmail produces a deterministic,
irreversible mask (like keeping first char and domain or replacing local-part
with asterisks).
🧹 Nitpick comments (3)
src/main/java/teamficial/teamficial_be/global/apiPayload/code/status/ErrorStatus.java (1)

59-61: 에러 코드 명명 규칙 불일치

REPORT_DUPLICATE의 에러 코드가 "REPORT409"로 변경되었는데, 동일 파일 내 다른 에러 코드들(REPORT4002, REPORT404 등)과 명명 패턴이 일치하지 않습니다. HTTP 상태 코드를 에러 코드 문자열에 직접 포함하는 것은 다른 코드들의 패턴과 다릅니다.

일관성을 위해 기존 패턴(REPORT4001)을 유지하거나, 전체 에러 코드 체계를 통일하는 것을 권장합니다.

src/main/java/teamficial/teamficial_be/TeamficialBeApplication.java (1)

9-9: 비동기 처리를 위한 커스텀 Executor 설정 고려

@EnableAsync만 사용하면 기본 SimpleAsyncTaskExecutor가 사용되며, 이는 요청마다 새 스레드를 생성합니다. 프로덕션 환경에서는 스레드 풀 크기, 큐 용량 등을 제어할 수 있는 ThreadPoolTaskExecutor를 별도로 설정하는 것이 권장됩니다.

현재 이메일 전송만 비동기로 처리하므로 즉시 필요하지는 않지만, 향후 비동기 작업이 늘어날 경우 고려해 주세요.

src/main/java/teamficial/teamficial_be/domain/report/service/ReportService.java (1)

53-57: DataIntegrityViolationException 포괄적 캐치 주의

DataIntegrityViolationException을 포괄적으로 캐치하면, 유니크 제약 위반 외에 다른 무결성 제약 위반(예: 외래키 위반, NOT NULL 위반 등)도 모두 REPORT_DUPLICATE로 처리될 수 있습니다.

더 정확한 에러 처리를 위해 예외 메시지나 원인을 검사하여 실제 유니크 제약 위반인지 확인하는 것을 고려해 주세요.

♻️ 개선 제안
         try {
             reportRepository.save(report);
         } catch (DataIntegrityViolationException e) {
+            if (e.getMessage() != null && e.getMessage().contains("user_id") && e.getMessage().contains("reported_comment_id")) {
+                throw new GeneralException(ErrorStatus.REPORT_DUPLICATE);
+            }
+            throw e; // 다른 무결성 위반은 그대로 전파
-            throw new GeneralException(ErrorStatus.REPORT_DUPLICATE);
         }

@dldusgh318
dldusgh318 merged commit dd8d5b1 into develop Feb 2, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 fix ♻️ refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[fix] 신고 중복 문제 해결

1 participant