Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8e163bc
[FEAT] ComponentCommentCountIncreaseEvent 추가 (#95)
kmw2378 Feb 7, 2025
4197814
[FEAT] ComponentCommentCountDecreaseEvent 추가 (#95)
kmw2378 Feb 7, 2025
084873a
[FEAT] Component 엔티티에 댓글 수 증가/감소 메서드 추가 (#95)
kmw2378 Feb 7, 2025
b2a4464
[FEAT] ComponentCommentCountEventListener 추가 (#95)
kmw2378 Feb 7, 2025
82cb468
[FEAT] CommentImageMoveEvent 추가 (#95)
kmw2378 Feb 7, 2025
1537939
[FEAT] CommentImageMoveEvent 추가 (#95)
kmw2378 Feb 7, 2025
54803ed
[REFACTOR] Component 이벤트 객체 패키지 변경 (#95)
kmw2378 Feb 7, 2025
c19db7c
[REFACTOR] 댓글 생성/삭제 시 발행할 이벤트 수정, 댓글 생성 시 ComponentId 검증 로직 추가 (#95)
kmw2378 Feb 7, 2025
65efdda
[REFACTOR] 이벤트 리스너 클래스명 suffix를 Listener로 수정 (#95)
kmw2378 Feb 7, 2025
3fc2329
[TEST] 미완성 테스트 주석화 (#95)
kmw2378 Feb 7, 2025
9e90237
[REFACTOR] 댓글 이미지 경로 변경 이벤트 처리 로직 변경 (#95)
kmw2378 Feb 7, 2025
e26c574
[REFACTOR] 댓글 이미지 수정 로직 변경 (#95)
kmw2378 Feb 7, 2025
978e468
[TEST] CommentServiceTest 수정 (#95)
kmw2378 Feb 7, 2025
5236e67
[REFACTOR] NotFoundParentCommentException 예외 메시지 수정 (#95)
kmw2378 Feb 7, 2025
ad09780
[FEAT] CommentRepository.existsByIdAndComponentId() 메서드 추가 (#95)
kmw2378 Feb 7, 2025
6c188ff
[FEAT] 답글 생성 시 컴포넌트 ID, 부모 댓글 ID 관계 검증 추가 (#95)
kmw2378 Feb 7, 2025
0d23559
[REFACTOR] 댓글 수 증가 이벤트를 같은 트랜잭션에서 수행하도록 변경 (#95)
kmw2378 Feb 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ject.componote.domain.comment.application;

import ject.componote.domain.comment.dto.image.event.CommentImageMoveEvent;
import ject.componote.domain.comment.model.CommentImage;
import ject.componote.infra.storage.application.StorageProducer;
import ject.componote.infra.storage.dto.move.request.ImageMoveRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Component
@RequiredArgsConstructor
public class CommentImageEventListener {
private final StorageProducer storageProducer;

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleImageMove(final CommentImageMoveEvent event) {
final CommentImage image = event.image();
if (image == null || image.isEmpty()) {
return;
}
storageProducer.sendImageMoveMessage(ImageMoveRequest.from(image));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

@Component
@RequiredArgsConstructor
public class CommentReplyCountEventHandler {
public class CommentReplyCountEventListener {
private final CommentRepository commentRepository;

@Async
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
@Component
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CommentReportEventHandler {
public class CommentReportEventListener {
private final CommentRepository commentRepository;

@Async
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import ject.componote.domain.comment.dto.find.response.CommentFindByComponentResponse;
import ject.componote.domain.comment.dto.find.response.CommentFindByMemberResponse;
import ject.componote.domain.comment.dto.find.response.CommentFindByParentResponse;
import ject.componote.domain.comment.dto.image.event.CommentImageMoveEvent;
import ject.componote.domain.comment.dto.reply.event.CommentReplyCountIncreaseEvent;
import ject.componote.domain.comment.dto.reply.event.CommentReplyNotificationEvent;
import ject.componote.domain.comment.dto.update.request.CommentUpdateRequest;
Expand All @@ -19,7 +20,10 @@
import ject.componote.domain.comment.model.CommentImage;
import ject.componote.domain.comment.validation.CommenterValidation;
import ject.componote.domain.common.dto.response.PageResponse;
import ject.componote.infra.storage.application.StorageService;
import ject.componote.domain.component.dao.ComponentRepository;
import ject.componote.domain.component.dto.event.ComponentCommentCountDecreaseEvent;
import ject.componote.domain.component.dto.event.ComponentCommentCountIncreaseEvent;
import ject.componote.domain.component.error.NotFoundComponentException;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
Expand All @@ -33,16 +37,17 @@
public class CommentService {
private final ApplicationEventPublisher eventPublisher;
private final CommentRepository commentRepository;
private final StorageService storageService;
private final ComponentRepository componentRepository;

@Transactional
public CommentCreateResponse create(final AuthPrincipal authPrincipal, final CommentCreateRequest request) {
validateComponentId(request);
validateParentId(request);
final Comment comment = commentRepository.save(
CommentCreationStrategy.createBy(request, authPrincipal.id())
);
storageService.moveImage(comment.getImage());
final Comment comment = commentRepository.save(CommentCreationStrategy.createBy(request, authPrincipal.id()));

// 아래 코드 이벤트 계층을 분리하여 리펙토링 예정
eventPublisher.publishEvent(CommentImageMoveEvent.from(comment));
eventPublisher.publishEvent(ComponentCommentCountIncreaseEvent.from(comment));
if (isReply(request)) {
eventPublisher.publishEvent(CommentReplyCountIncreaseEvent.from(comment));
eventPublisher.publishEvent(CommentReplyNotificationEvent.from(comment));
Expand Down Expand Up @@ -80,7 +85,7 @@ public void update(final AuthPrincipal authPrincipal, final Long commentId, fina

final CommentImage image = CommentImage.from(commentUpdateRequest.imageObjectKey());
if (!comment.equalsImage(image)) {
storageService.moveImage(image);
eventPublisher.publishEvent(CommentImageMoveEvent.from(comment));
}

final CommentContent content = CommentContent.from(commentUpdateRequest.content()); // 가비지가 발생하지 않을까?
Expand All @@ -90,7 +95,17 @@ public void update(final AuthPrincipal authPrincipal, final Long commentId, fina
@CommenterValidation
@Transactional
public void delete(final AuthPrincipal authPrincipal, final Long commentId) {
commentRepository.deleteByIdAndMemberId(commentId, authPrincipal.id());
final Long memberId = authPrincipal.id();
final Comment comment = findCommentByIdAndMemberId(commentId, memberId); // 로직 수정 예정
commentRepository.deleteByIdAndMemberId(commentId, memberId);
eventPublisher.publishEvent(ComponentCommentCountDecreaseEvent.from(comment));
}

private void validateComponentId(final CommentCreateRequest request) {
final Long componentId = request.componentId();
if (!componentRepository.existsById(componentId)) {
throw new NotFoundComponentException(componentId);
}
}

private Comment findCommentByIdAndMemberId(final Long commentId, final Long memberId) {
Expand Down Expand Up @@ -128,8 +143,9 @@ private void validateParentId(final CommentCreateRequest request) {
}

final Long parentId = request.parentId();
if (!commentRepository.existsById(parentId)) {
throw new NotFoundParentCommentException(parentId);
final Long componentId = request.componentId();
if (!commentRepository.existsByIdAndComponentId(parentId, componentId)) {
throw new NotFoundParentCommentException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
public interface CommentRepository extends JpaRepository<Comment, Long>, CommentQueryDsl {
Optional<Comment> findByIdAndMemberId(final Long id, final Long memberId);
boolean existsByIdAndMemberId(final Long id, final Long memberId);
boolean existsByIdAndComponentId(final Long id, final Long componentId);
void deleteByIdAndMemberId(final Long commentId, final Long memberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ject.componote.domain.comment.dto.image.event;

import ject.componote.domain.comment.domain.Comment;
import ject.componote.domain.comment.model.CommentImage;

public record CommentImageMoveEvent(CommentImage image) {
public static CommentImageMoveEvent from(final Comment comment) {
return new CommentImageMoveEvent(comment.getImage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.springframework.http.HttpStatus;

public class NotFoundParentCommentException extends CommentException {
public NotFoundParentCommentException(final Long parentId) {
super("일치하는 부모 댓글을 찾을 수 없습니다. 댓글 ID: " + parentId, HttpStatus.NOT_FOUND);
public NotFoundParentCommentException() {
super("컴포넌트에 부모 댓글이 존재하지 않습니다.", HttpStatus.NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ject.componote.domain.component.application;

import ject.componote.domain.component.dao.ComponentRepository;
import ject.componote.domain.component.domain.Component;
import ject.componote.domain.component.dto.event.ComponentCommentCountDecreaseEvent;
import ject.componote.domain.component.dto.event.ComponentCommentCountIncreaseEvent;
import ject.componote.domain.component.error.NotFoundComponentException;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@org.springframework.stereotype.Component
@RequiredArgsConstructor
public class ComponentCommentCountEventListener {
private final ComponentRepository componentRepository;

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) // cf) Spirng은 내부적으로 트랜잭션 정보를 ThreadLocal 변수에 저장하기 때문에 다른 쓰레드로 트랜잭션이 전파되지 않는다.
public void handleCommentCountIncreaseEvent(final ComponentCommentCountIncreaseEvent event) {
final Long componentId = event.componentId();
final Component component = findComponentById(componentId);
component.increaseCommentCount();
}

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleCommentCountDecreaseEvent(final ComponentCommentCountDecreaseEvent event) {
final Long componentId = event.componentId();
final Component component = findComponentById(componentId);
component.decreaseCommentCount();
}

private Component findComponentById(final Long componentId) {
return componentRepository.findById(componentId)
.orElseThrow(() -> new NotFoundComponentException(componentId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import ject.componote.domain.component.dao.ComponentSummaryDao;
import ject.componote.domain.component.domain.Component;
import ject.componote.domain.component.domain.ComponentType;
import ject.componote.domain.component.dto.find.event.ComponentViewCountIncreaseEvent;
import ject.componote.domain.component.dto.event.ComponentViewCountIncreaseEvent;
import ject.componote.domain.component.dto.find.request.ComponentSearchRequest;
import ject.componote.domain.component.dto.find.response.ComponentDetailResponse;
import ject.componote.domain.component.dto.find.response.ComponentSummaryResponse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import ject.componote.domain.component.dao.ComponentRepository;
import ject.componote.domain.component.domain.Component;
import ject.componote.domain.component.dto.find.event.ComponentViewCountIncreaseEvent;
import ject.componote.domain.component.dto.event.ComponentViewCountIncreaseEvent;
import ject.componote.domain.component.error.NotFoundComponentException;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
Expand All @@ -11,7 +11,7 @@

@org.springframework.stereotype.Component
@RequiredArgsConstructor
public class ComponentViewCountEventHandler {
public class ComponentViewCountEventListener {
private final ComponentRepository componentRepository;

@Async
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ public void increaseViewCount() {
this.viewCount.increase();
}

public void increaseCommentCount() {
this.commentCount.increase();
}

public void decreaseCommentCount() {
this.commentCount.decrease();
}

private List<MixedName> parseMixedNames(final List<String> mixedNames) {
return mixedNames.stream()
.map(MixedName::from)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ject.componote.domain.component.dto.event;

import ject.componote.domain.comment.domain.Comment;

public record ComponentCommentCountDecreaseEvent(Long componentId) {
public static ComponentCommentCountDecreaseEvent from(final Comment comment) {
return new ComponentCommentCountDecreaseEvent(comment.getComponentId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ject.componote.domain.component.dto.event;

import ject.componote.domain.comment.domain.Comment;

public record ComponentCommentCountIncreaseEvent(Long componentId) {
public static ComponentCommentCountIncreaseEvent from(final Comment comment) {
return new ComponentCommentCountIncreaseEvent(comment.getComponentId());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ject.componote.domain.component.dto.find.event;
package ject.componote.domain.component.dto.event;

import ject.componote.domain.component.domain.Component;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//package ject.componote.domain.comment.application;
//
//import ject.componote.domain.auth.domain.Member;
//import ject.componote.domain.comment.dao.CommentRepository;
//import ject.componote.domain.comment.domain.Comment;
//import ject.componote.domain.comment.dto.reply.event.CommentReplyCountIncreaseEvent;
//import ject.componote.domain.common.model.Count;
//import ject.componote.fixture.CommentFixture;
//import org.junit.jupiter.api.DisplayName;
//import org.junit.jupiter.api.Test;
//import org.junit.jupiter.api.extension.ExtendWith;
//import org.junit.jupiter.params.ParameterizedTest;
//import org.junit.jupiter.params.provider.EnumSource;
//import org.mockito.InjectMocks;
//import org.mockito.Mock;
//import org.mockito.junit.jupiter.MockitoExtension;
//
//import java.util.Optional;
//
//import static ject.componote.fixture.CommentFixture.답글_이미지O;
//import static ject.componote.fixture.MemberFixture.이메일X_회원;
//import static org.assertj.core.api.Assertions.assertThat;
//import static org.mockito.Mockito.doReturn;
//import static org.mockito.Mockito.spy;
//
//@ExtendWith(MockitoExtension.class)
//class CommentReplyCountEventListenerTest {
// @Mock
// CommentRepository commentRepository;
//
// @InjectMocks
// CommentReplyCountEventListener commentReplyCountEventListener;
//
// Member member = 이메일X_회원.생성(1L);
//
// @ParameterizedTest
// @DisplayName("대댓글 개수 증가 이벤트 처리")
// @EnumSource(value = CommentFixture.class)
// public void handleCommentReplyCountIncreaseEvent(final CommentFixture fixture) throws Exception {
// // given
// final Long memberId = member.getId();
// final Comment parent = fixture.생성(memberId);
// final Long parentId = parent.getParentId();
//
// final Comment comment = spy(답글_이미지O.생성());
// doReturn(parentId).when(comment)
// .getParentId();
//
// final CommentReplyCountIncreaseEvent event = CommentReplyCountIncreaseEvent.from(comment);
// final Count previousReplyCount = parent.getReplyCount();
//
// // when
// doReturn(Optional.of(parent)).when(commentRepository)
// .findById(parent.getId());
// commentReplyCountEventListener.handleCommentReplyCountIncreaseEvent(event);
//
// // then
// final Count currentReplyCount = parent.getReplyCount();
// previousReplyCount.increase();
// assertThat(currentReplyCount).isEqualTo(previousReplyCount);
// }
//
// @Test
// @DisplayName("대댓글 수 증가 이벤트 처리 시 댓글 ID가 잘못된 경우 예외 발생")
// public void handleCommentReplyCountIncreaseEventWhenInvalidCommentId() throws Exception {
//
// // given
//
// // when
//
// // then
//
// }
//}
Loading