본문 바로가기
Projects/청하-청년을 위한 커뮤니티 서비스

[청하] 18. 댓글 신고 기능 구현

by Lpromotion 2024. 10. 15.

게시글 신고 기능에 이어 댓글 신고 기능을 구현했다.

 

1. 프로젝트 구조

src/main/java/com/example/withpeace/
│
├── domain/                 # 도메인 모델 (엔티티)
│   ├── Post.java           # 게시글 엔티티
│   └── Report.java         # 신고 엔티티
│
├── repository/                   # 데이터 접근 계층
│   └── PostRepository.java       # 게시글 레포지토리
│   └── ReportRepository.java     # 신고 레포지토리
│
├── dto/                                    # 데이터 전송 객체
│   └── request/
│       ├── ReportRegisterRequestDto.java   # 신고 생성 요청 DTO
│
├── controller/             # 컨트롤러 계층
│   └── PostController.java # 게시글 관련 API 엔드포인트
│
├── service/                # 비즈니스 로직 계층
│   ├── PostService.java    # 게시글 관련 비즈니스 로직
│
└── type/                   # 열거형 및 상수
    ├── EReportType.java    # 신고 유형 열거형
    └── EReason.java        # 신고 이유 열거형

 

2. 도메인 모델 구현

Report 엔티티

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@DynamicUpdate
@Table(name = "reports")
public class Report {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false, updatable = false, unique = true)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "writer_id", nullable = false)
    private User writer; // 신고자

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id")
    private Post post; // 신고된 게시글

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "comment_id")
    private Comment comment; // 신고된 댓글

    @Column(name = "type", nullable = false)
    @Enumerated(EnumType.STRING)
    private EReportType type; // 신고 유형 (게시글 or 댓글)

    @Column(name = "reason", nullable = false)
    @Enumerated(EnumType.STRING)
    private EReason reason; // 신고 이유

    @Column(name = "create_date", nullable = false)
    private LocalDateTime createDate; // 신고 생성 일시

    @Builder
    public Report(User writer, Post post, Comment comment, EReportType type, EReason reason) {
        this.writer = writer;
        this.post = post;
        this.comment = comment;
        this.type = type;
        this.reason = reason;
        this.createDate = LocalDateTime.now();
    }
}

Report 엔티티는 신고 정보를 담는다. 신고자, 신고된 게시글, 신고 유형, 신고 유형 등이 포함된다.

 

3. 레포지토리 구현

ReportRepository

@Repository
public interface ReportRepository extends JpaRepository<Report, Long> {
    // ... (기존 코드 생략)

    boolean existsByWriterAndCommentAndType(User user, Comment comment, EReportType type);
}

`existsByWriterAndCommentAndType` 는 중복 신고를 방지하기 위한 메서드이다. 동일한 사용자가 같은 댓글에 대해 이미 신고한 적이 있는지 확인한다.

 

4. DTO 구현

ReportRegisterRequestDto

public record ReportRegisterRequestDto(
        @NotNull @JsonProperty("reason") @Schema(description = "신고이유") EReason reason){
}

신고 요청을 위한 DTO 이다.

  • `@NotNull`: 필드가 null이 아니어야 한다.
  • `@JsonProperty`: JSON 직렬화/역직렬화 시 사용할 속성 이름을 지정한다.
  • `@Schema`: Swagger 에서 API 문서를 생성할 때 사용하는 어노테이션이다.

`reason`은 반드시 입력해야 하는 값이므로 `@NotNull` 어노테이션을 설정했다.

 

5. 컨트롤러 구현

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/posts")
public class PostController {
    // ... (기존 코드 생략)

    // 댓글 신고
    @PostMapping("/{commentId}/reportComment")
    public ResponseDto<?> reportComment(@UserId Long userId, // 사용자 ID
                                        @PathVariable Long commentId, // 댓글 ID
                                        @Valid @RequestBody ReportRegisterRequestDto reason // 신고 이유
                                        ) {
        return ResponseDto.ok(postService.reportComment(userId, commentId, reason.reason()));
    }
}

`reportComment` 메서드에서 댓글 신고 요청을 처리한다. 인증된 사용자 ID, 댓글 ID, 신고 이유를 입력받아 서비스로 전달한다.

 

6. 서비스 구현

@Service
@RequiredArgsConstructor
public class PostService {
		// ... (기존 코드 생략)

    @Transactional
    public Boolean reportComment(Long userId, Long commentId, EReason reason) {
        // 사용자 및 댓글 존재 여부 확인
        User user = getUserById(userId);
        Comment comment = getCommentById(commentId);

        // 해당 댓글 중복 신고 확인
        boolean alreadyReported = reportRepository.existsByWriterAndCommentAndType(user, comment, EReportType.COMMENT);
        if(alreadyReported) { throw new CommonException(ErrorCode.COMMENT_ALREADY_REPORTED);}

        try {
            reportRepository.save(Report.builder()
                    .writer(user)
                    .comment(comment)
                    .type(EReportType.COMMENT)
                    .reason(reason)
                    .build());

            return true;
        } catch (Exception e) {
            throw new CommonException(ErrorCode.POST_ERROR);
        }
    }
}
  1. 사용자와 댓글을 조회한다. (존재 여부 확인)
  2. 사용자가 이미 해당 댓글을 신고했는지 확인한다. (`existsByWriterAndCommentAndType`)
  3. 중복 신고인 경우 예외를 반환한다.
  4. 중복 신고가 아닌 경우 새로운 Report 엔티티를 생성하고 저장한다.

 

8. API 응답 예시

요청 URL

[POST] http://localhost:8080/api/v1/posts/5/reportComment

 

Response Body

{
  "data": true,
  "error": null
}

 

반응형

댓글