게시글 CRUD 기능이 완성되었기 때문에 커뮤니티의 완성도를 위해 신고 기능을 추가하게 되었다.
게시글 상세조회 페이지에서 이 기능을 통해 사용자들이 부적절하다고 생각하는 게시글을 신고할 수 있다.
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 엔티티는 신고 정보를 담는다. 신고자, 신고된 게시글, 신고 유형, 신고 유형 등이 포함된다.
`@ManyToOne` 를 통해 신고자, 신고된 게시글, 신고된 댓글과의 관계를 표현했다. `EReportType`과 `EReason` Enum을 사용해 신고 유형과 신고 이유를 구현했다.
3. 레포지토리 구현
ReportRepository
@Repository
public interface ReportRepository extends JpaRepository<Report, Long> {
boolean existsByWriterAndPostAndType(User user, Post post, EReportType type);
}
`existsByWriterAndPostAndType` 는 중복 신고를 방지하기 위한 메서드이다. 동일한 사용자가 같은 게시글에 대해 이미 신고한 적이 있는지 확인한다.
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("/{postId}/reports")
public ResponseDto<?> reportPost(@UserId Long userId, // 사용자 ID
@PathVariable Long postId, // 게시글 ID
@Valid @RequestBody ReportRegisterRequestDto reason // 신고 이유
) {
return ResponseDto.ok(postService.reportPost(userId, postId, reason.reason()));
}
}
`reportPost` 메서드에서 게시글 신고 요청을 처리한다. 인증된 사용자 ID, 게시글 ID, 신고 이유를 입력받아 서비스로 전달한다.
6. 서비스 구현
@Service
@RequiredArgsConstructor
public class PostService {
// ... (기존 코드 생략)
@Transactional
public Boolean reportPost(Long userId, Long postId, EReason reason) {
// 사용자 및 게시글 존재 여부 확인
User user = getUserById(userId);
Post post = getPostById(postId);
// 해당 게시글 중복 신고 확인
boolean alreadyReported = reportRepository.existsByWriterAndPostAndType(user, post, EReportType.POST);
if(alreadyReported) { throw new CommonException(ErrorCode.POST_ALREADY_REPORTED);}
try {
reportRepository.save(Report.builder()
.writer(user)
.post(post)
.type(EReportType.POST)
.reason(reason)
.build());
return true;
} catch (Exception e) {
throw new CommonException(ErrorCode.POST_ERROR);
}
}
}
- 사용자와 게시글을 조회한다. (존재 여부 확인)
- 사용자가 이미 해당 게시글을 신고했는지 확인한다. (`existsByWriterAndPostAndType`)
- 중복 신고인 경우 예외를 반환한다.
- 중복 신고가 아닌 경우 새로운 Report 엔티티를 생성하고 저장한다.
7. Enum 구현
신고 이유와 신고 유형을 Enum을 사용해 구현한다.
Enum 을 사용하면 잘못된 값이 할당되는 것을 컴파일 시점에 방지할 수 있고, 데이터베이스에 저장될 때 일관된 값을 보장한다.
7.1. EReason
public enum EReason {
DUPLICATE, // 중복
ADVERTISEMENT, // 광고
INAPPROPRIATE, // 부적절
PROFANITY, // 욕설
OBSCENITY // 음란물
}
7.2. EReportType
public enum EReportType {
POST, // 게시글 신고
COMMENT // 댓글 신고
}
8. API 응답 예시
요청 URL
[POST] http://cheongha.site/api/v1/posts/5/reportPost
Response Body
{
"data": true,
"error": null
}
반응형
'Projects > 청하-청년을 위한 커뮤니티 서비스' 카테고리의 다른 글
[청하] 19. 인앱 업데이트 테크 스팩 작성 (2) | 2024.10.15 |
---|---|
[청하] 18. 댓글 신고 기능 구현 (1) | 2024.10.15 |
[청하] 16. 게시글 리스트 조회 기능 - 댓글수 반환 추가 (0) | 2024.10.14 |
[청하] 15. 댓글 생성 기능 구현 및 게시글 상세조회 기능 수정 (0) | 2024.10.10 |
[청하] 14. Spring Boot 파일 업로드 용량 문제 해결 (0) | 2024.10.10 |
댓글