From bae06f3d5ff6c7d940d24154b3d58975b5205f29 Mon Sep 17 00:00:00 2001 From: Donghun Shin Date: Tue, 21 Nov 2023 10:38:24 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=A1=9C=EA=B7=B8=20=EC=B6=94=EA=B0=80=20(#85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [#42] refactor: post like 패키지 구조 수정 * [#42] fix: 포스트 삭제 시 좋아요가 있으면 오류가 발생하는 문제 해결 * [#42] feat: 이벤트 처리 로그 AOP 구현 * [#42] chore: jacoco, sonarcloud에서 AOP 제거 --- build.gradle | 2 + .../domain/event/CategoryDeletedEvent.java | 9 +++- .../mallang/common/domain/DomainEvent.java | 6 +++ .../common/log/event/EventHandleLogAop.java | 45 +++++++++++++++++++ .../application/PostLikeEventHandler.java | 23 ++++++++++ .../post/application/PostLikeService.java | 8 ++-- .../mallang/post/domain/PostDeleteEvent.java | 8 +++- .../post/domain/{ => like}/PostLike.java | 5 ++- .../domain/{ => like}/PostLikeRepository.java | 10 ++++- .../domain/{ => like}/PostLikeValidator.java | 3 +- .../dao/support/PostLikeQuerySupport.java | 2 +- .../post/application/PostLikeServiceTest.java | 20 ++++++++- .../application/PostServiceTestHelper.java | 6 +++ .../post/domain/{ => like}/PostLikeTest.java | 34 +++++++------- .../{ => like}/PostLikeValidatorTest.java | 3 +- 15 files changed, 155 insertions(+), 29 deletions(-) create mode 100644 src/main/java/com/mallang/common/domain/DomainEvent.java create mode 100644 src/main/java/com/mallang/common/log/event/EventHandleLogAop.java create mode 100644 src/main/java/com/mallang/post/application/PostLikeEventHandler.java rename src/main/java/com/mallang/post/domain/{ => like}/PostLike.java (86%) rename src/main/java/com/mallang/post/domain/{ => like}/PostLikeRepository.java (61%) rename src/main/java/com/mallang/post/domain/{ => like}/PostLikeValidator.java (87%) rename src/test/java/com/mallang/post/domain/{ => like}/PostLikeTest.java (91%) rename src/test/java/com/mallang/post/domain/{ => like}/PostLikeValidatorTest.java (96%) diff --git a/build.gradle b/build.gradle index 853163bb..2e015432 100644 --- a/build.gradle +++ b/build.gradle @@ -100,6 +100,7 @@ def excludeCoverage = [ '**/auth/infrastructure/oauth/*/**', '**/auth/presentation/OauthController*', '**/common/domain/CommonDomainModel*', + '**/common/log/**', ] + Qdomains jacocoTestReport { @@ -149,6 +150,7 @@ jacocoTestCoverageVerification { '*.auth.infrastructure.oauth.*.*', '*.OauthController', '*.common.domain.CommonDomainModel*', + '*.common.log.*', ] + QdomainsInVerification limit { diff --git a/src/main/java/com/mallang/category/domain/event/CategoryDeletedEvent.java b/src/main/java/com/mallang/category/domain/event/CategoryDeletedEvent.java index b1111d3e..c21d9d2a 100644 --- a/src/main/java/com/mallang/category/domain/event/CategoryDeletedEvent.java +++ b/src/main/java/com/mallang/category/domain/event/CategoryDeletedEvent.java @@ -1,6 +1,13 @@ package com.mallang.category.domain.event; +import com.mallang.common.domain.DomainEvent; + public record CategoryDeletedEvent( Long categoryId -) { +) implements DomainEvent { + + @Override + public Long id() { + return categoryId(); + } } diff --git a/src/main/java/com/mallang/common/domain/DomainEvent.java b/src/main/java/com/mallang/common/domain/DomainEvent.java new file mode 100644 index 00000000..9e92ac38 --- /dev/null +++ b/src/main/java/com/mallang/common/domain/DomainEvent.java @@ -0,0 +1,6 @@ +package com.mallang.common.domain; + +public interface DomainEvent { + + Long id(); +} diff --git a/src/main/java/com/mallang/common/log/event/EventHandleLogAop.java b/src/main/java/com/mallang/common/log/event/EventHandleLogAop.java new file mode 100644 index 00000000..71c255d3 --- /dev/null +++ b/src/main/java/com/mallang/common/log/event/EventHandleLogAop.java @@ -0,0 +1,45 @@ +package com.mallang.common.log.event; + +import com.mallang.common.domain.DomainEvent; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +@Slf4j +@RequiredArgsConstructor +@Aspect +@Component +public class EventHandleLogAop { + + @Pointcut("@annotation(org.springframework.context.event.EventListener)") + public void eventListeners() { + } + + @Pointcut("@annotation(org.springframework.transaction.event.TransactionalEventListener)") + public void transactionalEventListeners() { + } + + @Before("(eventListeners() || transactionalEventListeners()) && args(event)") + public void handleEventLog(JoinPoint joinPoint, DomainEvent event) { + String className = getClassSimpleName(joinPoint); + String methodName = getMethodName(joinPoint); + log.info("Handle [{}(Domain Id: {})] by [{}.{}()]", + event.getClass().getSimpleName(), + event.id(), + className, + methodName); + } + + private String getClassSimpleName(JoinPoint joinPoint) { + Class clazz = joinPoint.getTarget().getClass(); + return clazz.getSimpleName(); + } + + private String getMethodName(JoinPoint joinPoint) { + return joinPoint.getSignature().getName(); + } +} diff --git a/src/main/java/com/mallang/post/application/PostLikeEventHandler.java b/src/main/java/com/mallang/post/application/PostLikeEventHandler.java new file mode 100644 index 00000000..2affa3d0 --- /dev/null +++ b/src/main/java/com/mallang/post/application/PostLikeEventHandler.java @@ -0,0 +1,23 @@ +package com.mallang.post.application; + +import com.mallang.post.domain.PostDeleteEvent; +import com.mallang.post.domain.like.PostLikeRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@RequiredArgsConstructor +@Transactional +@Service +public class PostLikeEventHandler { + + private final PostLikeRepository postLikeRepository; + + @EventListener(PostDeleteEvent.class) + void deletePostLike(PostDeleteEvent event) { + postLikeRepository.deleteAllByPostId(event.postId()); + } +} diff --git a/src/main/java/com/mallang/post/application/PostLikeService.java b/src/main/java/com/mallang/post/application/PostLikeService.java index 5e69e1d3..5d76f92f 100644 --- a/src/main/java/com/mallang/post/application/PostLikeService.java +++ b/src/main/java/com/mallang/post/application/PostLikeService.java @@ -5,10 +5,10 @@ import com.mallang.post.application.command.CancelPostLikeCommand; import com.mallang.post.application.command.ClickPostLikeCommand; import com.mallang.post.domain.Post; -import com.mallang.post.domain.PostLike; -import com.mallang.post.domain.PostLikeRepository; -import com.mallang.post.domain.PostLikeValidator; import com.mallang.post.domain.PostRepository; +import com.mallang.post.domain.like.PostLike; +import com.mallang.post.domain.like.PostLikeRepository; +import com.mallang.post.domain.like.PostLikeValidator; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -27,7 +27,7 @@ public void click(ClickPostLikeCommand command) { Post post = postRepository.getById(command.postId()); Member member = memberRepository.getById(command.memberId()); PostLike postLike = new PostLike(post, member); - postLike.click(postLikeValidator, command.postPassword()); + postLike.like(postLikeValidator, command.postPassword()); postLikeRepository.save(postLike); } diff --git a/src/main/java/com/mallang/post/domain/PostDeleteEvent.java b/src/main/java/com/mallang/post/domain/PostDeleteEvent.java index 8f1a461c..11cfdf91 100644 --- a/src/main/java/com/mallang/post/domain/PostDeleteEvent.java +++ b/src/main/java/com/mallang/post/domain/PostDeleteEvent.java @@ -1,13 +1,19 @@ package com.mallang.post.domain; +import com.mallang.common.domain.DomainEvent; import java.time.LocalDateTime; public record PostDeleteEvent( Long postId, LocalDateTime deletedDate -) { +) implements DomainEvent { public PostDeleteEvent(Long postId) { this(postId, LocalDateTime.now()); } + + @Override + public Long id() { + return postId(); + } } diff --git a/src/main/java/com/mallang/post/domain/PostLike.java b/src/main/java/com/mallang/post/domain/like/PostLike.java similarity index 86% rename from src/main/java/com/mallang/post/domain/PostLike.java rename to src/main/java/com/mallang/post/domain/like/PostLike.java index 158519e0..92bb58f6 100644 --- a/src/main/java/com/mallang/post/domain/PostLike.java +++ b/src/main/java/com/mallang/post/domain/like/PostLike.java @@ -1,9 +1,10 @@ -package com.mallang.post.domain; +package com.mallang.post.domain.like; import static jakarta.persistence.FetchType.LAZY; import com.mallang.auth.domain.Member; import com.mallang.common.domain.CommonDomainModel; +import com.mallang.post.domain.Post; import jakarta.annotation.Nullable; import jakarta.persistence.Entity; import jakarta.persistence.JoinColumn; @@ -30,7 +31,7 @@ public PostLike(Post post, Member member) { this.member = member; } - public void click(PostLikeValidator postLikeValidator, @Nullable String postPassword) { + public void like(PostLikeValidator postLikeValidator, @Nullable String postPassword) { post.validatePostAccessibility(member, postPassword); postLikeValidator.validateClickLike(post, member); post.clickLike(); diff --git a/src/main/java/com/mallang/post/domain/PostLikeRepository.java b/src/main/java/com/mallang/post/domain/like/PostLikeRepository.java similarity index 61% rename from src/main/java/com/mallang/post/domain/PostLikeRepository.java rename to src/main/java/com/mallang/post/domain/like/PostLikeRepository.java index ef70ef24..3d7f031a 100644 --- a/src/main/java/com/mallang/post/domain/PostLikeRepository.java +++ b/src/main/java/com/mallang/post/domain/like/PostLikeRepository.java @@ -1,9 +1,13 @@ -package com.mallang.post.domain; +package com.mallang.post.domain.like; import com.mallang.auth.domain.Member; +import com.mallang.post.domain.Post; import com.mallang.post.exception.NotFoundPostLikeException; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface PostLikeRepository extends JpaRepository { @@ -15,4 +19,8 @@ default PostLike getByPostIdAndMemberId(Long postId, Long memberId) { } Optional findByPostIdAndMemberId(Long postId, Long memberId); + + @Modifying + @Query("DELETE FROM PostLike pl WHERE pl.post.id = :postId") + void deleteAllByPostId(@Param("postId") Long postId); } diff --git a/src/main/java/com/mallang/post/domain/PostLikeValidator.java b/src/main/java/com/mallang/post/domain/like/PostLikeValidator.java similarity index 87% rename from src/main/java/com/mallang/post/domain/PostLikeValidator.java rename to src/main/java/com/mallang/post/domain/like/PostLikeValidator.java index 65d15e4b..b52d37db 100644 --- a/src/main/java/com/mallang/post/domain/PostLikeValidator.java +++ b/src/main/java/com/mallang/post/domain/like/PostLikeValidator.java @@ -1,6 +1,7 @@ -package com.mallang.post.domain; +package com.mallang.post.domain.like; import com.mallang.auth.domain.Member; +import com.mallang.post.domain.Post; import com.mallang.post.exception.AlreadyLikedPostException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/mallang/post/query/dao/support/PostLikeQuerySupport.java b/src/main/java/com/mallang/post/query/dao/support/PostLikeQuerySupport.java index 7232fbcc..4abbfc1b 100644 --- a/src/main/java/com/mallang/post/query/dao/support/PostLikeQuerySupport.java +++ b/src/main/java/com/mallang/post/query/dao/support/PostLikeQuerySupport.java @@ -1,6 +1,6 @@ package com.mallang.post.query.dao.support; -import com.mallang.post.domain.PostLike; +import com.mallang.post.domain.like.PostLike; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; diff --git a/src/test/java/com/mallang/post/application/PostLikeServiceTest.java b/src/test/java/com/mallang/post/application/PostLikeServiceTest.java index c3fad360..2cf06ec4 100644 --- a/src/test/java/com/mallang/post/application/PostLikeServiceTest.java +++ b/src/test/java/com/mallang/post/application/PostLikeServiceTest.java @@ -10,6 +10,7 @@ import com.mallang.post.application.command.ClickPostLikeCommand; import com.mallang.post.domain.Post; import com.mallang.post.domain.PostRepository; +import com.mallang.post.domain.like.PostLikeRepository; import com.mallang.post.exception.AlreadyLikedPostException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -40,6 +41,9 @@ class PostLikeServiceTest { @Autowired private PostLikeService postLikeService; + @Autowired + private PostLikeRepository postLikeRepository; + private Long memberId; private String blogName; private Long postId; @@ -87,9 +91,23 @@ class 좋아요_시 { // when postLikeService.cancel(new CancelPostLikeCommand(postId, memberId, null)); - // then Post post = postRepository.getById(postId); assertThat(post.getLikeCount()).isZero(); } + + @Nested + class 포스트_삭제_시 { + + @Test + void 좋아요도_삭제되어야_한다() { + postLikeService.click(new ClickPostLikeCommand(postId, memberId, null)); + + // when + postServiceTestHelper.포스트를_삭제한다(memberId, postId); + + // then + assertThat(postLikeRepository.findByPostIdAndMemberId(postId, memberId)).isEmpty(); + } + } } diff --git a/src/test/java/com/mallang/post/application/PostServiceTestHelper.java b/src/test/java/com/mallang/post/application/PostServiceTestHelper.java index 75d01213..102fb032 100644 --- a/src/test/java/com/mallang/post/application/PostServiceTestHelper.java +++ b/src/test/java/com/mallang/post/application/PostServiceTestHelper.java @@ -3,10 +3,12 @@ import static com.mallang.post.domain.visibility.PostVisibilityPolicy.Visibility.PUBLIC; import com.mallang.post.application.command.CreatePostCommand; +import com.mallang.post.application.command.DeletePostCommand; import com.mallang.post.domain.Post; import com.mallang.post.domain.PostRepository; import com.mallang.post.domain.visibility.PostVisibilityPolicy; import java.util.Arrays; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.test.context.ActiveProfiles; @@ -55,6 +57,10 @@ public class PostServiceTestHelper { )); } + public void 포스트를_삭제한다(Long memberId, Long postId) { + postService.delete(new DeletePostCommand(memberId, List.of(postId))); + } + public Post 포스트를_조회한다(Long 포스트_ID) { return postRepository.getById(포스트_ID); } diff --git a/src/test/java/com/mallang/post/domain/PostLikeTest.java b/src/test/java/com/mallang/post/domain/like/PostLikeTest.java similarity index 91% rename from src/test/java/com/mallang/post/domain/PostLikeTest.java rename to src/test/java/com/mallang/post/domain/like/PostLikeTest.java index 7b2ac736..09e99b82 100644 --- a/src/test/java/com/mallang/post/domain/PostLikeTest.java +++ b/src/test/java/com/mallang/post/domain/like/PostLikeTest.java @@ -1,4 +1,4 @@ -package com.mallang.post.domain; +package com.mallang.post.domain.like; import static com.mallang.auth.MemberFixture.말랑; import static com.mallang.auth.MemberFixture.회원; @@ -14,6 +14,8 @@ import com.mallang.auth.domain.Member; import com.mallang.blog.domain.Blog; +import com.mallang.post.domain.Post; +import com.mallang.post.domain.PostIntro; import com.mallang.post.domain.visibility.PostVisibilityPolicy; import com.mallang.post.exception.AlreadyLikedPostException; import com.mallang.post.exception.NoAuthorityAccessPostException; @@ -49,7 +51,7 @@ class 좋아요_시 { PostLike postLike = new PostLike(post, mallang); // when - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); // then assertThat(post.getLikeCount()).isEqualTo(1); @@ -65,7 +67,7 @@ class 좋아요_시 { // when assertThatThrownBy(() -> { - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); }).isInstanceOf(AlreadyLikedPostException.class); // then @@ -82,7 +84,7 @@ class 공개_포스트인_경우 { // when & then assertDoesNotThrow(() -> { - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); }); assertThat(post.getLikeCount()).isEqualTo(1); } @@ -106,7 +108,7 @@ class 보호_포스트인_경우 { // when & then assertDoesNotThrow(() -> { - postLike.click(postLikeValidator, "1234"); + postLike.like(postLikeValidator, "1234"); }); assertThat(post.getLikeCount()).isEqualTo(1); } @@ -118,7 +120,7 @@ class 보호_포스트인_경우 { // when & then assertDoesNotThrow(() -> { - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); }); assertThat(post.getLikeCount()).isEqualTo(1); } @@ -130,7 +132,7 @@ class 보호_포스트인_경우 { // when & then assertThatThrownBy(() -> { - postLike.click(postLikeValidator, "12345"); + postLike.like(postLikeValidator, "12345"); }).isInstanceOf(NoAuthorityAccessPostException.class); assertThat(post.getLikeCount()).isZero(); } @@ -154,7 +156,7 @@ class 비공개_포스트인_경우 { // when & then assertDoesNotThrow(() -> { - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); }); assertThat(post.getLikeCount()).isEqualTo(1); } @@ -166,7 +168,7 @@ class 비공개_포스트인_경우 { // when & then assertThatThrownBy(() -> { - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); }).isInstanceOf(NoAuthorityAccessPostException.class); assertThat(post.getLikeCount()).isZero(); } @@ -180,7 +182,7 @@ class 좋아요_취소_시 { void 취소_시_포스트의_좋아요_수가_1_감소한다() { // given PostLike postLike = new PostLike(post, mallang); - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); // when postLike.cancel(null); @@ -196,7 +198,7 @@ class 공개_포스트인_경우 { void 누구나_접근_가능하다() { // given PostLike postLike = new PostLike(post, other); - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); // when postLike.cancel(null); @@ -221,7 +223,7 @@ class 보호_포스트인_경우 { void 비밀번호가_일치하면_접근할_수_있다() { // given PostLike postLike = new PostLike(post, other); - postLike.click(postLikeValidator, "1234"); + postLike.like(postLikeValidator, "1234"); // when & then assertDoesNotThrow(() -> { @@ -234,7 +236,7 @@ class 보호_포스트인_경우 { void 글_작성자라면_접근할_수_있다() { // given PostLike postLike = new PostLike(post, mallang); - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); // when & then assertDoesNotThrow(() -> { @@ -247,7 +249,7 @@ class 보호_포스트인_경우 { void 글_작성자가_아니며_비밀번호도_일치하지_않으면_접근할_수_없다() { // given PostLike postLike = new PostLike(post, other); - postLike.click(postLikeValidator, "1234"); + postLike.like(postLikeValidator, "1234"); // when & then assertThatThrownBy(() -> { @@ -271,7 +273,7 @@ class 비공개_포스트인_경우 { .blog(blog) .build(); PostLike postLike = new PostLike(post, mallang); - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); // when & then assertDoesNotThrow(() -> { @@ -291,7 +293,7 @@ class 비공개_포스트인_경우 { .blog(blog) .build(); PostLike postLike = new PostLike(post, other); - postLike.click(postLikeValidator, null); + postLike.like(postLikeValidator, null); post.update( "up", "up", diff --git a/src/test/java/com/mallang/post/domain/PostLikeValidatorTest.java b/src/test/java/com/mallang/post/domain/like/PostLikeValidatorTest.java similarity index 96% rename from src/test/java/com/mallang/post/domain/PostLikeValidatorTest.java rename to src/test/java/com/mallang/post/domain/like/PostLikeValidatorTest.java index 02c2eba8..eed2c174 100644 --- a/src/test/java/com/mallang/post/domain/PostLikeValidatorTest.java +++ b/src/test/java/com/mallang/post/domain/like/PostLikeValidatorTest.java @@ -1,4 +1,4 @@ -package com.mallang.post.domain; +package com.mallang.post.domain.like; import static com.mallang.auth.MemberFixture.말랑; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -8,6 +8,7 @@ import com.mallang.auth.domain.Member; import com.mallang.blog.domain.Blog; +import com.mallang.post.domain.Post; import com.mallang.post.exception.AlreadyLikedPostException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration;