From 20277c3852bd44c766b1158b17e0a2092cb457af Mon Sep 17 00:00:00 2001 From: Minjae Chung Date: Fri, 27 Feb 2026 00:14:28 +0900 Subject: [PATCH] =?UTF-8?q?[fix]=20UTC(Instant)=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20+=20commentCount=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20+=20=EA=B8=B0=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../server/comment/domain/QComment.java | 4 +-- .../server/comment/domain/QCommentLike.java | 4 +-- .../server/common/domain/QBaseEntity.java | 4 +-- .../server/composer/domain/QComposer.java | 4 +-- .../server/composer/domain/QComposerLike.java | 4 +-- .../server/composer/domain/QComposerPost.java | 4 +-- .../server/post/domain/QCurationPost.java | 10 ++++-- .../daramg/server/post/domain/QFreePost.java | 10 ++++-- .../com/daramg/server/post/domain/QPost.java | 8 +++-- .../daramg/server/post/domain/QPostLike.java | 4 +-- .../daramg/server/post/domain/QPostScrap.java | 4 +-- .../daramg/server/post/domain/QReport.java | 4 +-- .../daramg/server/post/domain/QStoryPost.java | 10 ++++-- .../com/daramg/server/user/domain/QUser.java | 6 ++-- .../server/user/domain/QUserFollow.java | 4 +-- .../com/daramg/server/ServerApplication.java | 9 ----- .../server/auth/application/AuthService.java | 5 +-- .../comment/application/CommentService.java | 1 + .../comment/dto/CommentResponseDto.java | 10 +++--- .../server/common/domain/BaseEntity.java | 10 +++--- .../server/common/util/PagingUtils.java | 18 +++++----- .../daramg/server/notice/domain/Notice.java | 6 ++-- .../notice/dto/NoticeDetailResponse.java | 4 +-- .../server/notice/dto/NoticeResponseDto.java | 4 +-- .../application/NotificationQueryService.java | 5 +-- .../dto/NotificationResponseDto.java | 4 +-- .../NotificationQueryRepositoryImpl.java | 5 +-- .../repository/NotificationRepository.java | 4 +-- .../com/daramg/server/post/domain/Post.java | 12 +++++-- .../server/post/dto/PostDetailResponse.java | 6 ++-- .../server/post/dto/PostResponseDto.java | 4 +-- .../post/repository/PostRepository.java | 4 +-- .../server/search/domain/SearchLog.java | 6 ++-- .../server/search/dto/SearchResponseDto.java | 4 +-- .../server/user/application/UserService.java | 7 ++-- .../com/daramg/server/user/domain/User.java | 6 ++-- .../user/exception/UserErrorStatus.java | 3 +- src/main/resources/application-local.yml | 2 ++ src/main/resources/application-prod.yml | 2 ++ src/main/resources/application.yml | 7 ++++ .../application/CommentServiceTest.java | 26 ++++++++++++++ .../server/common/util/PagingUtilsTest.java | 13 +++---- .../ComposerQueryControllerTest.java | 6 ++-- .../NoticeQueryControllerTest.java | 8 ++--- .../NotificationQueryControllerTest.java | 6 ++-- .../application/PostQueryServiceTest.java | 17 ++++----- .../presentation/PostQueryControllerTest.java | 35 ++++++++++--------- .../presentation/SearchControllerTest.java | 4 +-- .../user/application/UserServiceTest.java | 30 +++++++++++++++- src/test/resources/application.yml | 4 +++ 51 files changed, 237 insertions(+), 146 deletions(-) diff --git a/build.gradle b/build.gradle index 4d46d65..f667c08 100644 --- a/build.gradle +++ b/build.gradle @@ -102,7 +102,7 @@ clean { test { outputs.dir snippetsDir useJUnitPlatform() - jvmArgs '-Duser.timezone=Asia/Seoul' + jvmArgs '-Duser.timezone=UTC' testLogging { events "failed" exceptionFormat "full" diff --git a/src/main/generated/com/daramg/server/comment/domain/QComment.java b/src/main/generated/com/daramg/server/comment/domain/QComment.java index 11e5ec2..8709402 100644 --- a/src/main/generated/com/daramg/server/comment/domain/QComment.java +++ b/src/main/generated/com/daramg/server/comment/domain/QComment.java @@ -29,7 +29,7 @@ public class QComment extends EntityPathBase { public final StringPath content = createString("content"); //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; //inherited public final NumberPath id = _super.id; @@ -45,7 +45,7 @@ public class QComment extends EntityPathBase { public final com.daramg.server.post.domain.QPost post; //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public final com.daramg.server.user.domain.QUser user; diff --git a/src/main/generated/com/daramg/server/comment/domain/QCommentLike.java b/src/main/generated/com/daramg/server/comment/domain/QCommentLike.java index 971de4b..355c390 100644 --- a/src/main/generated/com/daramg/server/comment/domain/QCommentLike.java +++ b/src/main/generated/com/daramg/server/comment/domain/QCommentLike.java @@ -27,13 +27,13 @@ public class QCommentLike extends EntityPathBase { public final QComment comment; //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; //inherited public final NumberPath id = _super.id; //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public final com.daramg.server.user.domain.QUser user; diff --git a/src/main/generated/com/daramg/server/common/domain/QBaseEntity.java b/src/main/generated/com/daramg/server/common/domain/QBaseEntity.java index 92e7c79..42232a7 100644 --- a/src/main/generated/com/daramg/server/common/domain/QBaseEntity.java +++ b/src/main/generated/com/daramg/server/common/domain/QBaseEntity.java @@ -19,11 +19,11 @@ public class QBaseEntity extends EntityPathBase createdAt = createDateTime("createdAt", java.time.LocalDateTime.class); + public final DateTimePath createdAt = createDateTime("createdAt", java.time.Instant.class); public final NumberPath id = createNumber("id", Long.class); - public final DateTimePath updatedAt = createDateTime("updatedAt", java.time.LocalDateTime.class); + public final DateTimePath updatedAt = createDateTime("updatedAt", java.time.Instant.class); @SuppressWarnings({"all", "rawtypes", "unchecked"}) public QBaseEntity(String variable) { diff --git a/src/main/generated/com/daramg/server/composer/domain/QComposer.java b/src/main/generated/com/daramg/server/composer/domain/QComposer.java index 5c97605..e2033f3 100644 --- a/src/main/generated/com/daramg/server/composer/domain/QComposer.java +++ b/src/main/generated/com/daramg/server/composer/domain/QComposer.java @@ -28,7 +28,7 @@ public class QComposer extends EntityPathBase { public final EnumPath continent = createEnum("continent", Continent.class); //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; public final NumberPath deathYear = createNumber("deathYear", Short.class); @@ -48,7 +48,7 @@ public class QComposer extends EntityPathBase { public final StringPath nativeName = createString("nativeName"); //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public QComposer(String variable) { super(Composer.class, forVariable(variable)); diff --git a/src/main/generated/com/daramg/server/composer/domain/QComposerLike.java b/src/main/generated/com/daramg/server/composer/domain/QComposerLike.java index 8635b08..a4946e4 100644 --- a/src/main/generated/com/daramg/server/composer/domain/QComposerLike.java +++ b/src/main/generated/com/daramg/server/composer/domain/QComposerLike.java @@ -27,13 +27,13 @@ public class QComposerLike extends EntityPathBase { public final QComposer composer; //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; //inherited public final NumberPath id = _super.id; //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public final com.daramg.server.user.domain.QUser user; diff --git a/src/main/generated/com/daramg/server/composer/domain/QComposerPost.java b/src/main/generated/com/daramg/server/composer/domain/QComposerPost.java index 90de682..83802da 100644 --- a/src/main/generated/com/daramg/server/composer/domain/QComposerPost.java +++ b/src/main/generated/com/daramg/server/composer/domain/QComposerPost.java @@ -27,7 +27,7 @@ public class QComposerPost extends EntityPathBase { public final QComposer composer; //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; //inherited public final NumberPath id = _super.id; @@ -35,7 +35,7 @@ public class QComposerPost extends EntityPathBase { public final com.daramg.server.post.domain.QPost post; //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public QComposerPost(String variable) { this(ComposerPost.class, forVariable(variable), INITS); diff --git a/src/main/generated/com/daramg/server/post/domain/QCurationPost.java b/src/main/generated/com/daramg/server/post/domain/QCurationPost.java index 8d13f77..18a28f8 100644 --- a/src/main/generated/com/daramg/server/post/domain/QCurationPost.java +++ b/src/main/generated/com/daramg/server/post/domain/QCurationPost.java @@ -33,10 +33,10 @@ public class QCurationPost extends EntityPathBase { public final StringPath content; //inherited - public final DateTimePath createdAt; + public final DateTimePath createdAt; //inherited - public final DateTimePath deletedAt; + public final DateTimePath deletedAt; //inherited public final ListPath hashtags; @@ -65,7 +65,7 @@ public class QCurationPost extends EntityPathBase { public final StringPath title; //inherited - public final DateTimePath updatedAt; + public final DateTimePath updatedAt; // inherited public final com.daramg.server.user.domain.QUser user; @@ -73,6 +73,9 @@ public class QCurationPost extends EntityPathBase { //inherited public final StringPath videoUrl; + //inherited + public final NumberPath viewCount; + public QCurationPost(String variable) { this(CurationPost.class, forVariable(variable), INITS); } @@ -108,6 +111,7 @@ public QCurationPost(Class type, PathMetadata metadata, this.updatedAt = _super.updatedAt; this.user = _super.user; this.videoUrl = _super.videoUrl; + this.viewCount = _super.viewCount; } } diff --git a/src/main/generated/com/daramg/server/post/domain/QFreePost.java b/src/main/generated/com/daramg/server/post/domain/QFreePost.java index 9936c94..1c97799 100644 --- a/src/main/generated/com/daramg/server/post/domain/QFreePost.java +++ b/src/main/generated/com/daramg/server/post/domain/QFreePost.java @@ -31,10 +31,10 @@ public class QFreePost extends EntityPathBase { public final StringPath content; //inherited - public final DateTimePath createdAt; + public final DateTimePath createdAt; //inherited - public final DateTimePath deletedAt; + public final DateTimePath deletedAt; //inherited public final ListPath hashtags; @@ -61,7 +61,7 @@ public class QFreePost extends EntityPathBase { public final StringPath title; //inherited - public final DateTimePath updatedAt; + public final DateTimePath updatedAt; // inherited public final com.daramg.server.user.domain.QUser user; @@ -69,6 +69,9 @@ public class QFreePost extends EntityPathBase { //inherited public final StringPath videoUrl; + //inherited + public final NumberPath viewCount; + public QFreePost(String variable) { this(FreePost.class, forVariable(variable), INITS); } @@ -103,6 +106,7 @@ public QFreePost(Class type, PathMetadata metadata, PathInit this.updatedAt = _super.updatedAt; this.user = _super.user; this.videoUrl = _super.videoUrl; + this.viewCount = _super.viewCount; } } diff --git a/src/main/generated/com/daramg/server/post/domain/QPost.java b/src/main/generated/com/daramg/server/post/domain/QPost.java index c937418..667b8a7 100644 --- a/src/main/generated/com/daramg/server/post/domain/QPost.java +++ b/src/main/generated/com/daramg/server/post/domain/QPost.java @@ -29,9 +29,9 @@ public class QPost extends EntityPathBase { public final StringPath content = createString("content"); //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; - public final DateTimePath deletedAt = createDateTime("deletedAt", java.time.LocalDateTime.class); + public final DateTimePath deletedAt = createDateTime("deletedAt", java.time.Instant.class); public final ListPath hashtags = this.createList("hashtags", String.class, StringPath.class, PathInits.DIRECT2); @@ -51,12 +51,14 @@ public class QPost extends EntityPathBase { public final StringPath title = createString("title"); //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public final com.daramg.server.user.domain.QUser user; public final StringPath videoUrl = createString("videoUrl"); + public final NumberPath viewCount = createNumber("viewCount", Integer.class); + public QPost(String variable) { this(Post.class, forVariable(variable), INITS); } diff --git a/src/main/generated/com/daramg/server/post/domain/QPostLike.java b/src/main/generated/com/daramg/server/post/domain/QPostLike.java index 257152e..58128b9 100644 --- a/src/main/generated/com/daramg/server/post/domain/QPostLike.java +++ b/src/main/generated/com/daramg/server/post/domain/QPostLike.java @@ -25,7 +25,7 @@ public class QPostLike extends EntityPathBase { public final com.daramg.server.common.domain.QBaseEntity _super = new com.daramg.server.common.domain.QBaseEntity(this); //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; //inherited public final NumberPath id = _super.id; @@ -33,7 +33,7 @@ public class QPostLike extends EntityPathBase { public final QPost post; //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public final com.daramg.server.user.domain.QUser user; diff --git a/src/main/generated/com/daramg/server/post/domain/QPostScrap.java b/src/main/generated/com/daramg/server/post/domain/QPostScrap.java index f57a017..ebb9420 100644 --- a/src/main/generated/com/daramg/server/post/domain/QPostScrap.java +++ b/src/main/generated/com/daramg/server/post/domain/QPostScrap.java @@ -25,7 +25,7 @@ public class QPostScrap extends EntityPathBase { public final com.daramg.server.common.domain.QBaseEntity _super = new com.daramg.server.common.domain.QBaseEntity(this); //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; //inherited public final NumberPath id = _super.id; @@ -33,7 +33,7 @@ public class QPostScrap extends EntityPathBase { public final QPost post; //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public final com.daramg.server.user.domain.QUser user; diff --git a/src/main/generated/com/daramg/server/post/domain/QReport.java b/src/main/generated/com/daramg/server/post/domain/QReport.java index 0a7cbac..a4bb8bd 100644 --- a/src/main/generated/com/daramg/server/post/domain/QReport.java +++ b/src/main/generated/com/daramg/server/post/domain/QReport.java @@ -27,7 +27,7 @@ public class QReport extends EntityPathBase { public final com.daramg.server.comment.domain.QComment comment; //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; //inherited public final NumberPath id = _super.id; @@ -45,7 +45,7 @@ public class QReport extends EntityPathBase { public final EnumPath type = createEnum("type", Report.ReportType.class); //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public QReport(String variable) { this(Report.class, forVariable(variable), INITS); diff --git a/src/main/generated/com/daramg/server/post/domain/QStoryPost.java b/src/main/generated/com/daramg/server/post/domain/QStoryPost.java index 56c31b7..1d27a3b 100644 --- a/src/main/generated/com/daramg/server/post/domain/QStoryPost.java +++ b/src/main/generated/com/daramg/server/post/domain/QStoryPost.java @@ -31,10 +31,10 @@ public class QStoryPost extends EntityPathBase { public final StringPath content; //inherited - public final DateTimePath createdAt; + public final DateTimePath createdAt; //inherited - public final DateTimePath deletedAt; + public final DateTimePath deletedAt; //inherited public final ListPath hashtags; @@ -63,7 +63,7 @@ public class QStoryPost extends EntityPathBase { public final StringPath title; //inherited - public final DateTimePath updatedAt; + public final DateTimePath updatedAt; // inherited public final com.daramg.server.user.domain.QUser user; @@ -71,6 +71,9 @@ public class QStoryPost extends EntityPathBase { //inherited public final StringPath videoUrl; + //inherited + public final NumberPath viewCount; + public QStoryPost(String variable) { this(StoryPost.class, forVariable(variable), INITS); } @@ -106,6 +109,7 @@ public QStoryPost(Class type, PathMetadata metadata, PathIn this.updatedAt = _super.updatedAt; this.user = _super.user; this.videoUrl = _super.videoUrl; + this.viewCount = _super.viewCount; } } diff --git a/src/main/generated/com/daramg/server/user/domain/QUser.java b/src/main/generated/com/daramg/server/user/domain/QUser.java index a5c66e7..2550d80 100644 --- a/src/main/generated/com/daramg/server/user/domain/QUser.java +++ b/src/main/generated/com/daramg/server/user/domain/QUser.java @@ -29,9 +29,9 @@ public class QUser extends EntityPathBase { public final DatePath birthDate = createDate("birthDate", java.time.LocalDate.class); //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; - public final DateTimePath deletedAt = createDateTime("deletedAt", java.time.LocalDateTime.class); + public final DateTimePath deletedAt = createDateTime("deletedAt", java.time.Instant.class); public final StringPath email = createString("email"); @@ -51,7 +51,7 @@ public class QUser extends EntityPathBase { public final StringPath profileImage = createString("profileImage"); //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public final EnumPath userStatus = createEnum("userStatus", UserStatus.class); diff --git a/src/main/generated/com/daramg/server/user/domain/QUserFollow.java b/src/main/generated/com/daramg/server/user/domain/QUserFollow.java index 78ac74a..01d7d36 100644 --- a/src/main/generated/com/daramg/server/user/domain/QUserFollow.java +++ b/src/main/generated/com/daramg/server/user/domain/QUserFollow.java @@ -25,7 +25,7 @@ public class QUserFollow extends EntityPathBase { public final com.daramg.server.common.domain.QBaseEntity _super = new com.daramg.server.common.domain.QBaseEntity(this); //inherited - public final DateTimePath createdAt = _super.createdAt; + public final DateTimePath createdAt = _super.createdAt; public final QUser followed; @@ -35,7 +35,7 @@ public class QUserFollow extends EntityPathBase { public final NumberPath id = _super.id; //inherited - public final DateTimePath updatedAt = _super.updatedAt; + public final DateTimePath updatedAt = _super.updatedAt; public QUserFollow(String variable) { this(UserFollow.class, forVariable(variable), INITS); diff --git a/src/main/java/com/daramg/server/ServerApplication.java b/src/main/java/com/daramg/server/ServerApplication.java index 3de55dd..9f4ec2c 100644 --- a/src/main/java/com/daramg/server/ServerApplication.java +++ b/src/main/java/com/daramg/server/ServerApplication.java @@ -1,21 +1,12 @@ package com.daramg.server; -import jakarta.annotation.PostConstruct; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import java.util.TimeZone; - @SpringBootApplication public class ServerApplication { - @PostConstruct - void setTimeZone() { - TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); - } - public static void main(String[] args) { - TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); SpringApplication.run(ServerApplication.class, args); } } diff --git a/src/main/java/com/daramg/server/auth/application/AuthService.java b/src/main/java/com/daramg/server/auth/application/AuthService.java index 6b2c8cc..2eb9369 100644 --- a/src/main/java/com/daramg/server/auth/application/AuthService.java +++ b/src/main/java/com/daramg/server/auth/application/AuthService.java @@ -21,7 +21,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import java.time.LocalDateTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; @Service @@ -45,7 +46,7 @@ public void signup(SignupRequestDto dto, MultipartFile image){ userRepository.findByEmailAndUserStatus(dto.getEmail(), UserStatus.DELETED) .ifPresent(deletedUser -> { if (deletedUser.getDeletedAt() != null - && deletedUser.getDeletedAt().isAfter(LocalDateTime.now().minusDays(30))) { + && deletedUser.getDeletedAt().isAfter(Instant.now().minus(30, ChronoUnit.DAYS))) { throw new BusinessException(AuthErrorStatus.REJOIN_NOT_ALLOWED); } }); diff --git a/src/main/java/com/daramg/server/comment/application/CommentService.java b/src/main/java/com/daramg/server/comment/application/CommentService.java index 232ab4b..7ad824d 100644 --- a/src/main/java/com/daramg/server/comment/application/CommentService.java +++ b/src/main/java/com/daramg/server/comment/application/CommentService.java @@ -113,6 +113,7 @@ public void deleteComment(Long commentId, User user){ } comment.softDelete(); + comment.getPost().decrementCommentCount(); commentLikeRepository.deleteAllByCommentId(commentId); comment.resetLikeCount(); } diff --git a/src/main/java/com/daramg/server/comment/dto/CommentResponseDto.java b/src/main/java/com/daramg/server/comment/dto/CommentResponseDto.java index fee10a0..e648b33 100644 --- a/src/main/java/com/daramg/server/comment/dto/CommentResponseDto.java +++ b/src/main/java/com/daramg/server/comment/dto/CommentResponseDto.java @@ -2,7 +2,7 @@ import com.daramg.server.comment.domain.Comment; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; public record CommentResponseDto( @@ -11,7 +11,7 @@ public record CommentResponseDto( boolean isDeleted, int likeCount, int childCommentCount, - LocalDateTime createdAt, + Instant createdAt, String writerNickname, String writerProfileImage, Boolean isLiked, @@ -19,7 +19,9 @@ public record CommentResponseDto( ) { public static CommentResponseDto from(Comment comment, Boolean isLiked, List childComments) { - int childCommentCount = childComments != null ? childComments.size() : 0; + int childCommentCount = childComments != null + ? (int) childComments.stream().filter(c -> !c.isDeleted()).count() + : 0; return new CommentResponseDto( comment.getId(), @@ -40,7 +42,7 @@ public record ChildCommentResponseDto( String content, boolean isDeleted, int likeCount, - LocalDateTime createdAt, + Instant createdAt, String writerNickname, String writerProfileImage, Boolean isLiked diff --git a/src/main/java/com/daramg/server/common/domain/BaseEntity.java b/src/main/java/com/daramg/server/common/domain/BaseEntity.java index 207cfb1..c18e8d2 100644 --- a/src/main/java/com/daramg/server/common/domain/BaseEntity.java +++ b/src/main/java/com/daramg/server/common/domain/BaseEntity.java @@ -4,7 +4,7 @@ import lombok.Getter; import org.springframework.data.domain.AbstractAggregateRoot; -import java.time.LocalDateTime; +import java.time.Instant; @MappedSuperclass @Getter @@ -15,21 +15,21 @@ public class BaseEntity> extends AbstractAggr private Long id; @Column(name = "created_at", nullable = false, updatable = false) - private LocalDateTime createdAt; + private Instant createdAt; @Column(name = "updated_at", nullable = false) - private LocalDateTime updatedAt; + private Instant updatedAt; @PrePersist protected void onCreate() { - LocalDateTime now = LocalDateTime.now(); + Instant now = Instant.now(); this.createdAt = now; this.updatedAt = now; } @PreUpdate protected void onUpdate() { - this.updatedAt = LocalDateTime.now(); + this.updatedAt = Instant.now(); } @Override diff --git a/src/main/java/com/daramg/server/common/util/PagingUtils.java b/src/main/java/com/daramg/server/common/util/PagingUtils.java index 1f9f941..66a6812 100644 --- a/src/main/java/com/daramg/server/common/util/PagingUtils.java +++ b/src/main/java/com/daramg/server/common/util/PagingUtils.java @@ -10,7 +10,7 @@ import com.querydsl.jpa.impl.JPAQuery; import org.springframework.stereotype.Component; -import java.time.LocalDateTime; +import java.time.Instant; import java.time.format.DateTimeParseException; import java.util.Base64; import java.util.List; @@ -24,7 +24,7 @@ public class PagingUtils { public List applyCursorPagination( JPAQuery query, PageRequestDto request, - DateTimePath createdAtPath, + DateTimePath createdAtPath, NumberPath idPath ) { int querySize = request.getValidatedSize() + 1; @@ -41,7 +41,7 @@ public PageResponseDto createPageResponse( List content, int size, Function dtoMapper, - Function createdAtExtractor, + Function createdAtExtractor, Function idExtractor ) { boolean hasNext = content.size() > size; @@ -65,7 +65,7 @@ public PageResponseDto createPageResponse( public List applyCursorPaginationToList( List content, PageRequestDto request, - Function createdAtExtractor, + Function createdAtExtractor, Function idExtractor ) { Cursor cursor = decodeCursor(request.getCursor()); @@ -76,7 +76,7 @@ public List applyCursorPaginationToList( if (cursor == null) { return true; } - LocalDateTime createdAt = createdAtExtractor.apply(item); + Instant createdAt = createdAtExtractor.apply(item); Long id = idExtractor.apply(item); return createdAt.isBefore(cursor.createdAt()) || (createdAt.equals(cursor.createdAt()) && id < cursor.id()); @@ -89,7 +89,7 @@ public List applyCursorPaginationToList( private BooleanExpression getPaginationConditions( Cursor cursor, - DateTimePath createdAtPath, + DateTimePath createdAtPath, NumberPath idPath ) { if (cursor == null) { @@ -101,7 +101,7 @@ private BooleanExpression getPaginationConditions( .and(idPath.lt(cursor.id()))); } - public String encodeCursor(LocalDateTime createdAt, Long id) { + public String encodeCursor(Instant createdAt, Long id) { if (createdAt == null || id == null) return null; String cursorStr = createdAt.toString() + CURSOR_DELIMITER + id; return Base64.getEncoder().encodeToString(cursorStr.getBytes()); @@ -114,12 +114,12 @@ private Cursor decodeCursor(String cursorString) { try { String decoded = new String(Base64.getDecoder().decode(cursorString)); String[] parts = decoded.split(CURSOR_DELIMITER); - return new Cursor(LocalDateTime.parse(parts[0]), Long.parseLong(parts[1])); + return new Cursor(Instant.parse(parts[0]), Long.parseLong(parts[1])); } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException | DateTimeParseException e) { throw new BusinessException(CommonErrorStatus.INVALID_CURSOR); } } - private record Cursor(LocalDateTime createdAt, Long id) {} + private record Cursor(Instant createdAt, Long id) {} } diff --git a/src/main/java/com/daramg/server/notice/domain/Notice.java b/src/main/java/com/daramg/server/notice/domain/Notice.java index b753fb3..9b24e88 100644 --- a/src/main/java/com/daramg/server/notice/domain/Notice.java +++ b/src/main/java/com/daramg/server/notice/domain/Notice.java @@ -10,7 +10,7 @@ import lombok.*; import org.hibernate.annotations.SQLRestriction; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -41,7 +41,7 @@ public class Notice extends BaseEntity { private boolean isDeleted = false; @Column(name = "deleted_at") - private LocalDateTime deletedAt; + private Instant deletedAt; public void update(NoticeUpdateVo vo) { if (vo.getTitle() == null && vo.getContent() == null @@ -95,6 +95,6 @@ public void softDelete() { if (this.isDeleted) return; this.isDeleted = true; - this.deletedAt = LocalDateTime.now(); + this.deletedAt = Instant.now(); } } diff --git a/src/main/java/com/daramg/server/notice/dto/NoticeDetailResponse.java b/src/main/java/com/daramg/server/notice/dto/NoticeDetailResponse.java index 3440bba..a190338 100644 --- a/src/main/java/com/daramg/server/notice/dto/NoticeDetailResponse.java +++ b/src/main/java/com/daramg/server/notice/dto/NoticeDetailResponse.java @@ -3,7 +3,7 @@ import com.daramg.server.notice.domain.Notice; import com.daramg.server.post.domain.*; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; public record NoticeDetailResponse( @@ -13,7 +13,7 @@ public record NoticeDetailResponse( String title, String content, List images, - LocalDateTime createdAt + Instant createdAt ) { public static NoticeDetailResponse from(Notice notice) { diff --git a/src/main/java/com/daramg/server/notice/dto/NoticeResponseDto.java b/src/main/java/com/daramg/server/notice/dto/NoticeResponseDto.java index 8086c32..e48a0d4 100644 --- a/src/main/java/com/daramg/server/notice/dto/NoticeResponseDto.java +++ b/src/main/java/com/daramg/server/notice/dto/NoticeResponseDto.java @@ -2,14 +2,14 @@ import com.daramg.server.notice.domain.Notice; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; public record NoticeResponseDto( Long id, String title, String writerNickname, - LocalDateTime createdAt, + Instant createdAt, String content, String thumbnailImageUrl ) { diff --git a/src/main/java/com/daramg/server/notification/application/NotificationQueryService.java b/src/main/java/com/daramg/server/notification/application/NotificationQueryService.java index cf1114c..4bdd122 100644 --- a/src/main/java/com/daramg/server/notification/application/NotificationQueryService.java +++ b/src/main/java/com/daramg/server/notification/application/NotificationQueryService.java @@ -12,7 +12,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; @Service @@ -38,6 +39,6 @@ public PageResponseDto getNotifications(User user, Page } public long getUnreadCount(User user) { - return notificationRepository.countUnreadByReceiverIdSince(user.getId(), LocalDateTime.now().minusDays(30)); + return notificationRepository.countUnreadByReceiverIdSince(user.getId(), Instant.now().minus(30, ChronoUnit.DAYS)); } } diff --git a/src/main/java/com/daramg/server/notification/dto/NotificationResponseDto.java b/src/main/java/com/daramg/server/notification/dto/NotificationResponseDto.java index ab8c59a..b7afecd 100644 --- a/src/main/java/com/daramg/server/notification/dto/NotificationResponseDto.java +++ b/src/main/java/com/daramg/server/notification/dto/NotificationResponseDto.java @@ -3,7 +3,7 @@ import com.daramg.server.notification.domain.Notification; import com.daramg.server.notification.domain.NotificationType; -import java.time.LocalDateTime; +import java.time.Instant; public record NotificationResponseDto( Long id, @@ -13,7 +13,7 @@ public record NotificationResponseDto( String postTitle, NotificationType type, boolean isRead, - LocalDateTime createdAt + Instant createdAt ) { public static NotificationResponseDto from(Notification notification) { return new NotificationResponseDto( diff --git a/src/main/java/com/daramg/server/notification/repository/NotificationQueryRepositoryImpl.java b/src/main/java/com/daramg/server/notification/repository/NotificationQueryRepositoryImpl.java index 080bbe6..20b12f5 100644 --- a/src/main/java/com/daramg/server/notification/repository/NotificationQueryRepositoryImpl.java +++ b/src/main/java/com/daramg/server/notification/repository/NotificationQueryRepositoryImpl.java @@ -8,7 +8,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; -import java.time.LocalDateTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; import static com.daramg.server.notification.domain.QNotification.notification; @@ -30,7 +31,7 @@ public List getNotificationsWithPaging(Long receiverId, PageReques .leftJoin(notification.post, post).fetchJoin() .where( notification.receiver.id.eq(receiverId), - notification.createdAt.goe(LocalDateTime.now().minusDays(30)) + notification.createdAt.goe(Instant.now().minus(30, ChronoUnit.DAYS)) ); return pagingUtils.applyCursorPagination( diff --git a/src/main/java/com/daramg/server/notification/repository/NotificationRepository.java b/src/main/java/com/daramg/server/notification/repository/NotificationRepository.java index 55b9c41..802a883 100644 --- a/src/main/java/com/daramg/server/notification/repository/NotificationRepository.java +++ b/src/main/java/com/daramg/server/notification/repository/NotificationRepository.java @@ -5,12 +5,12 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import java.time.LocalDateTime; +import java.time.Instant; public interface NotificationRepository extends JpaRepository { @Query("SELECT COUNT(n) FROM Notification n WHERE n.receiver.id = :receiverId AND n.isRead = false AND n.createdAt >= :since") - long countUnreadByReceiverIdSince(Long receiverId, LocalDateTime since); + long countUnreadByReceiverIdSince(Long receiverId, Instant since); long countByReceiverId(Long receiverId); diff --git a/src/main/java/com/daramg/server/post/domain/Post.java b/src/main/java/com/daramg/server/post/domain/Post.java index 8536703..5f8acb4 100644 --- a/src/main/java/com/daramg/server/post/domain/Post.java +++ b/src/main/java/com/daramg/server/post/domain/Post.java @@ -9,7 +9,7 @@ import org.hibernate.annotations.SQLRestriction; import org.springframework.lang.NonNull; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -63,7 +63,7 @@ public abstract class Post extends BaseEntity { private boolean isDeleted = false; @Column(name = "deleted_at") - private LocalDateTime deletedAt; + private Instant deletedAt; protected Post(@NonNull User user, @NonNull String title, @NonNull String content, @Singular List images, String videoUrl, @@ -136,6 +136,12 @@ public void incrementCommentCount(){ commentCount++; } + public void decrementCommentCount() { + if (commentCount > 0) { + commentCount--; + } + } + public void incrementViewCount(){ viewCount++; } @@ -144,6 +150,6 @@ public void softDelete() { if (this.isDeleted) return; this.isDeleted = true; - this.deletedAt = LocalDateTime.now(); + this.deletedAt = Instant.now(); } } diff --git a/src/main/java/com/daramg/server/post/dto/PostDetailResponse.java b/src/main/java/com/daramg/server/post/dto/PostDetailResponse.java index f7f0271..c91e8dc 100644 --- a/src/main/java/com/daramg/server/post/dto/PostDetailResponse.java +++ b/src/main/java/com/daramg/server/post/dto/PostDetailResponse.java @@ -10,7 +10,7 @@ import com.daramg.server.post.domain.PostType; import com.daramg.server.post.domain.StoryPost; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; import java.util.stream.Collectors; @@ -28,8 +28,8 @@ public record PostDetailResponse( int commentCount, int viewCount, boolean isBlocked, - LocalDateTime createdAt, - LocalDateTime updatedAt, + Instant createdAt, + Instant updatedAt, PostType type, ComposerInfo primaryComposer, List additionalComposers, diff --git a/src/main/java/com/daramg/server/post/dto/PostResponseDto.java b/src/main/java/com/daramg/server/post/dto/PostResponseDto.java index 8210b1d..2583335 100644 --- a/src/main/java/com/daramg/server/post/dto/PostResponseDto.java +++ b/src/main/java/com/daramg/server/post/dto/PostResponseDto.java @@ -10,7 +10,7 @@ import com.daramg.server.post.domain.PostType; import com.daramg.server.post.domain.StoryPost; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; public record PostResponseDto( @@ -18,7 +18,7 @@ public record PostResponseDto( String title, String content, List hashtags, - LocalDateTime createdAt, + Instant createdAt, String writerNickname, int likeCount, int commentCount, diff --git a/src/main/java/com/daramg/server/post/repository/PostRepository.java b/src/main/java/com/daramg/server/post/repository/PostRepository.java index 2536f6e..5547f38 100644 --- a/src/main/java/com/daramg/server/post/repository/PostRepository.java +++ b/src/main/java/com/daramg/server/post/repository/PostRepository.java @@ -8,7 +8,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; @Repository @@ -24,6 +24,6 @@ public interface PostRepository extends JpaRepository { where p.user.id = :userId and p.isDeleted = false """) - int softDeleteAllByUserId(@Param("userId") Long userId, @Param("now") LocalDateTime now); + int softDeleteAllByUserId(@Param("userId") Long userId, @Param("now") Instant now); } diff --git a/src/main/java/com/daramg/server/search/domain/SearchLog.java b/src/main/java/com/daramg/server/search/domain/SearchLog.java index fbeede2..d35d6ff 100644 --- a/src/main/java/com/daramg/server/search/domain/SearchLog.java +++ b/src/main/java/com/daramg/server/search/domain/SearchLog.java @@ -5,7 +5,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import java.time.LocalDateTime; +import java.time.Instant; @Entity @Getter @@ -21,7 +21,7 @@ public class SearchLog { private String keyword; @Column(name = "searched_at", nullable = false) - private LocalDateTime searchedAt; + private Instant searchedAt; @Column(name = "user_id") private Long userId; @@ -33,7 +33,7 @@ private SearchLog(String keyword, Long userId) { @PrePersist protected void onCreate() { - this.searchedAt = LocalDateTime.now(); + this.searchedAt = Instant.now(); } public static SearchLog of(String keyword, Long userId) { diff --git a/src/main/java/com/daramg/server/search/dto/SearchResponseDto.java b/src/main/java/com/daramg/server/search/dto/SearchResponseDto.java index df17c48..6cdbfe8 100644 --- a/src/main/java/com/daramg/server/search/dto/SearchResponseDto.java +++ b/src/main/java/com/daramg/server/search/dto/SearchResponseDto.java @@ -4,7 +4,7 @@ import com.daramg.server.post.domain.Post; import com.daramg.server.post.domain.PostType; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; public record SearchResponseDto( @@ -30,7 +30,7 @@ public record PostResult( String title, PostType type, String writerNickname, - LocalDateTime createdAt + Instant createdAt ) { public static PostResult from(Post post, PostType type) { return new PostResult( diff --git a/src/main/java/com/daramg/server/user/application/UserService.java b/src/main/java/com/daramg/server/user/application/UserService.java index 3c288b7..d4a20ab 100644 --- a/src/main/java/com/daramg/server/user/application/UserService.java +++ b/src/main/java/com/daramg/server/user/application/UserService.java @@ -19,7 +19,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; +import java.time.Instant; @Service @RequiredArgsConstructor @@ -129,9 +129,10 @@ public void withdraw(Long userId) { user.withdraw(); - User admin = userRepository.getReferenceById(1L); + User admin = userRepository.findById(1L) + .orElseThrow(() -> new BusinessException(UserErrorStatus.ADMIN_NOT_FOUND)); noticeRepository.transferToAdmin(userId, admin); - postRepository.softDeleteAllByUserId(userId, LocalDateTime.now()); + postRepository.softDeleteAllByUserId(userId, Instant.now()); } } diff --git a/src/main/java/com/daramg/server/user/domain/User.java b/src/main/java/com/daramg/server/user/domain/User.java index 85d949c..dae2784 100644 --- a/src/main/java/com/daramg/server/user/domain/User.java +++ b/src/main/java/com/daramg/server/user/domain/User.java @@ -9,8 +9,8 @@ import lombok.NoArgsConstructor; import org.springframework.lang.NonNull; +import java.time.Instant; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -57,7 +57,7 @@ public class User extends BaseEntity { private UserStatus userStatus; @Column(name = "deleted_at") - private LocalDateTime deletedAt; + private Instant deletedAt; @Builder public User(@NonNull String email, @NonNull String password, @NonNull String name, @@ -122,7 +122,7 @@ public void withdraw(){ if (this.userStatus == UserStatus.DELETED) return; userStatus = UserStatus.DELETED; - this.deletedAt = LocalDateTime.now(); + this.deletedAt = Instant.now(); } public boolean isActive() { diff --git a/src/main/java/com/daramg/server/user/exception/UserErrorStatus.java b/src/main/java/com/daramg/server/user/exception/UserErrorStatus.java index 9cd3ef8..5bac893 100644 --- a/src/main/java/com/daramg/server/user/exception/UserErrorStatus.java +++ b/src/main/java/com/daramg/server/user/exception/UserErrorStatus.java @@ -17,7 +17,8 @@ public enum UserErrorStatus implements BaseErrorCode { SELF_FOLLOW(HttpStatus.BAD_REQUEST, ErrorCategory.USER.generate(400_2), "팔로우 대상과 주체의 유저가 동일합니다."), ALREADY_FOLLOWING(HttpStatus.CONFLICT, ErrorCategory.USER.generate(409_2), "이미 팔로우하고 있는 상태입니다."), SELF_UNFOLLOW(HttpStatus.BAD_REQUEST, ErrorCategory.USER.generate(400_3), "언팔로우 대상과 주체의 유저가 동일합니다."), - NOT_FOLLOWING(HttpStatus.BAD_REQUEST, ErrorCategory.USER.generate(400_4), "언팔로우할 유저를 팔로우하지 않은 상태입니다."); + NOT_FOLLOWING(HttpStatus.BAD_REQUEST, ErrorCategory.USER.generate(400_4), "언팔로우할 유저를 팔로우하지 않은 상태입니다."), + ADMIN_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, ErrorCategory.USER.generate(500_1), "관리자 계정을 찾을 수 없습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index ffd5641..21edc37 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -20,6 +20,8 @@ spring: ddl-auto: create-drop dialect: org.hibernate.dialect.H2Dialect format_sql: true + jdbc: + time_zone: UTC flyway: enabled: false diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 7c30d61..32834d3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -15,6 +15,8 @@ spring: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect format_sql: false + jdbc: + time_zone: UTC flyway: enabled: true baseline-on-migrate: true diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 40b8610..fed1733 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -10,6 +10,13 @@ spring: open-in-view: false hibernate: ddl-auto: none + properties: + hibernate: + jdbc: + time_zone: UTC + jackson: + serialization: + write-dates-as-timestamps: false flyway: enabled: true baseline-on-migrate: true diff --git a/src/test/java/com/daramg/server/comment/application/CommentServiceTest.java b/src/test/java/com/daramg/server/comment/application/CommentServiceTest.java index c68a90a..b8a1d77 100644 --- a/src/test/java/com/daramg/server/comment/application/CommentServiceTest.java +++ b/src/test/java/com/daramg/server/comment/application/CommentServiceTest.java @@ -23,6 +23,7 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.annotation.Transactional; +import java.time.Instant; import java.time.LocalDate; import java.util.List; @@ -273,6 +274,31 @@ class DeleteCommentTest { .isInstanceOf(BusinessException.class) .hasMessage("댓글을 작성한 유저만 댓글을 삭제할 수 있습니다."); } + + @Test + void 댓글_삭제_시_포스트의_댓글_카운트가_감소한다() { + // given + commentService.createComment(post.getId(), new CommentCreateDto("댓글"), user); + assertThat(postRepository.findById(post.getId()).orElseThrow().getCommentCount()).isEqualTo(1); + Comment comment = commentRepository.findAll().getFirst(); + + // when + commentService.deleteComment(comment.getId(), user); + + // then + assertThat(postRepository.findById(post.getId()).orElseThrow().getCommentCount()).isEqualTo(0); + } + } + + @Test + void 엔티티_생성_시_createdAt이_UTC_Instant로_저장된다() { + Instant before = Instant.now(); + commentRepository.save(Comment.of(post, user, "테스트", null)); + Instant after = Instant.now(); + + Comment saved = commentRepository.findAll().getFirst(); + assertThat(saved.getCreatedAt()).isAfterOrEqualTo(before); + assertThat(saved.getCreatedAt()).isBeforeOrEqualTo(after); } } diff --git a/src/test/java/com/daramg/server/common/util/PagingUtilsTest.java b/src/test/java/com/daramg/server/common/util/PagingUtilsTest.java index 1eb05ab..334406b 100644 --- a/src/test/java/com/daramg/server/common/util/PagingUtilsTest.java +++ b/src/test/java/com/daramg/server/common/util/PagingUtilsTest.java @@ -8,7 +8,8 @@ import org.junit.jupiter.api.Test; import org.springframework.test.util.ReflectionTestUtils; -import java.time.LocalDateTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.stream.LongStream; @@ -24,7 +25,7 @@ void setUp() { pagingUtils = new PagingUtils(); } - private record TestEntity(Long id, String name, LocalDateTime createdAt) {} + private record TestEntity(Long id, String name, Instant createdAt) {} private record TestDto(Long id, String name) {} @Nested @@ -38,7 +39,7 @@ void createPageResponse_HasNext_True() { int requestSize = 10; List content = LongStream.range(1, 12) // 11개 - .mapToObj(i -> new TestEntity(11 - i, "Test " + i, LocalDateTime.now().minusDays(i))) + .mapToObj(i -> new TestEntity(11 - i, "Test " + i, Instant.now().minus(i, ChronoUnit.DAYS))) .toList(); // when @@ -68,7 +69,7 @@ void createPageResponse_HasNext_False() { // given int requestSize = 10; List content = LongStream.range(1, 11) // 10개 - .mapToObj(i -> new TestEntity(10 - i, "Test " + i, LocalDateTime.now().minusDays(i))) + .mapToObj(i -> new TestEntity(10 - i, "Test " + i, Instant.now().minus(i, ChronoUnit.DAYS))) .toList(); // when @@ -117,7 +118,7 @@ class CursorEncodingTest { @DisplayName("커서 인코딩 및 디코딩 라운드트립") void cursor_EncodeDecode_Roundtrip() { // given - LocalDateTime now = LocalDateTime.now(); + Instant now = Instant.now(); Long id = 123L; // when @@ -131,7 +132,7 @@ void cursor_EncodeDecode_Roundtrip() { assertThat(encodedCursor).isNotNull().isNotEqualTo(now.toString() + "_" + id); // Base64 인코딩 assertThat(decodedRecord).isNotNull(); - LocalDateTime decodedTime = (LocalDateTime) ReflectionTestUtils.getField(decodedRecord, "createdAt"); + Instant decodedTime = (Instant) ReflectionTestUtils.getField(decodedRecord, "createdAt"); Long decodedId = (Long) ReflectionTestUtils.getField(decodedRecord, "id"); assertThat(decodedTime).isEqualTo(now); diff --git a/src/test/java/com/daramg/server/composer/presentation/ComposerQueryControllerTest.java b/src/test/java/com/daramg/server/composer/presentation/ComposerQueryControllerTest.java index 263e341..95a87f0 100644 --- a/src/test/java/com/daramg/server/composer/presentation/ComposerQueryControllerTest.java +++ b/src/test/java/com/daramg/server/composer/presentation/ComposerQueryControllerTest.java @@ -17,7 +17,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.ResultActions; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; import static com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName; @@ -102,7 +102,7 @@ public class ComposerQueryControllerTest extends ControllerTestSupport { "코렐리 스토리 포스트", "코렐리에 대한 스토리 포스트 내용입니다", List.of("#코렐리", "#바로크"), - LocalDateTime.of(2024, 1, 15, 10, 30, 0), + Instant.parse("2024-01-15T10:30:00Z"), "작성자1", 10, 5, @@ -119,7 +119,7 @@ public class ComposerQueryControllerTest extends ControllerTestSupport { "코렐리 큐레이션 포스트", "코렐리에 대한 큐레이션 포스트 내용입니다", List.of("#코렐리", "#큐레이션"), - LocalDateTime.of(2024, 1, 14, 15, 20, 0), + Instant.parse("2024-01-14T15:20:00Z"), "작성자2", 20, 10, diff --git a/src/test/java/com/daramg/server/notice/presentation/NoticeQueryControllerTest.java b/src/test/java/com/daramg/server/notice/presentation/NoticeQueryControllerTest.java index 48446f0..b24c1af 100644 --- a/src/test/java/com/daramg/server/notice/presentation/NoticeQueryControllerTest.java +++ b/src/test/java/com/daramg/server/notice/presentation/NoticeQueryControllerTest.java @@ -13,7 +13,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.ResultActions; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; import static com.epages.restdocs.apispec.ResourceDocumentation.resource; @@ -40,8 +40,8 @@ public class NoticeQueryControllerTest extends ControllerTestSupport { // given Cookie cookie = new Cookie(COOKIE_NAME, "access_token"); List notices = List.of( - new NoticeResponseDto(1L, "첫 번째 공지", "관리자", LocalDateTime.now(), "공지 내용입니다.", "https://example.com/thumb1.jpg"), - new NoticeResponseDto(2L, "두 번째 공지", "관리자", LocalDateTime.now(), "공지 내용입니다.", null) + new NoticeResponseDto(1L, "첫 번째 공지", "관리자", Instant.now(), "공지 내용입니다.", "https://example.com/thumb1.jpg"), + new NoticeResponseDto(2L, "두 번째 공지", "관리자", Instant.now(), "공지 내용입니다.", null) ); PageResponseDto response = new PageResponseDto<>(notices, "2", true); @@ -95,7 +95,7 @@ public class NoticeQueryControllerTest extends ControllerTestSupport { NoticeDetailResponse response = new NoticeDetailResponse( 1L, "관리자", "https://example.com/profile.jpg", "공지사항 제목", "공지사항 상세 내용입니다.", - List.of("https://example.com/image1.jpg"), LocalDateTime.now() + List.of("https://example.com/image1.jpg"), Instant.now() ); when(noticeQueryService.getNoticeDetail(anyLong())).thenReturn(response); diff --git a/src/test/java/com/daramg/server/notification/presentation/NotificationQueryControllerTest.java b/src/test/java/com/daramg/server/notification/presentation/NotificationQueryControllerTest.java index 4111739..0264a71 100644 --- a/src/test/java/com/daramg/server/notification/presentation/NotificationQueryControllerTest.java +++ b/src/test/java/com/daramg/server/notification/presentation/NotificationQueryControllerTest.java @@ -13,7 +13,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.ResultActions; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; import static com.epages.restdocs.apispec.ResourceDocumentation.resource; @@ -40,11 +40,11 @@ public class NotificationQueryControllerTest extends ControllerTestSupport { List content = List.of( new NotificationResponseDto( 1L, "보내는사람", "profile.jpg", 10L, "게시글 제목", - NotificationType.COMMENT, false, LocalDateTime.of(2025, 1, 1, 12, 0) + NotificationType.COMMENT, false, Instant.parse("2025-01-01T12:00:00Z") ), new NotificationResponseDto( 2L, "다른사람", null, 10L, "게시글 제목", - NotificationType.POST_LIKE, true, LocalDateTime.of(2025, 1, 1, 11, 0) + NotificationType.POST_LIKE, true, Instant.parse("2025-01-01T11:00:00Z") ) ); PageResponseDto response = new PageResponseDto<>(content, null, false); diff --git a/src/test/java/com/daramg/server/post/application/PostQueryServiceTest.java b/src/test/java/com/daramg/server/post/application/PostQueryServiceTest.java index c257d75..284070d 100644 --- a/src/test/java/com/daramg/server/post/application/PostQueryServiceTest.java +++ b/src/test/java/com/daramg/server/post/application/PostQueryServiceTest.java @@ -36,8 +36,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.ReflectionTestUtils; +import java.time.Instant; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; @@ -288,7 +289,7 @@ void getAllFreePosts_WithInvalidCursorFormat_ThrowsBusinessException() { @DisplayName("cursor가 유효한 포맷이지만 존재하지 않는 포스트를 가리킬 경우 빈 리스트를 반환한다") void getAllFreePosts_WithNonExistentCursor_ReturnsEmptyList() { // given - LocalDateTime pastDateTime = LocalDateTime.now().minusYears(100); + Instant pastDateTime = Instant.now().minus(100 * 365L, ChronoUnit.DAYS); Long nonExistentId = 0L; String validFormatCursor = pagingUtils.encodeCursor(pastDateTime, nonExistentId); PageRequestDto pageRequest = new PageRequestDto(validFormatCursor, 10); @@ -737,15 +738,15 @@ void getPostById_CommentsAndRepliesAreSortedByCreatedAtAsc() { FreePost savedPost = freePosts.get(0); Long postId = savedPost.getId(); - LocalDateTime baseTime = LocalDateTime.now().minusHours(1); + Instant baseTime = Instant.now().minus(1, ChronoUnit.HOURS); Comment parent1 = commentRepository.save(Comment.of(savedPost, user, "부모1", null)); Comment parent2 = commentRepository.save(Comment.of(savedPost, user, "부모2", null)); Comment parent3 = commentRepository.save(Comment.of(savedPost, user, "부모3", null)); ReflectionTestUtils.setField(parent1, "createdAt", baseTime); - ReflectionTestUtils.setField(parent2, "createdAt", baseTime.plusMinutes(10)); - ReflectionTestUtils.setField(parent3, "createdAt", baseTime.plusMinutes(20)); + ReflectionTestUtils.setField(parent2, "createdAt", baseTime.plus(10, ChronoUnit.MINUTES)); + ReflectionTestUtils.setField(parent3, "createdAt", baseTime.plus(20, ChronoUnit.MINUTES)); commentRepository.saveAll(List.of(parent1, parent2, parent3)); @@ -753,9 +754,9 @@ void getPostById_CommentsAndRepliesAreSortedByCreatedAtAsc() { Comment child2 = commentRepository.save(Comment.of(savedPost, user, "자식2", parent2)); Comment child3 = commentRepository.save(Comment.of(savedPost, user, "자식3", parent3)); - ReflectionTestUtils.setField(child1, "createdAt", baseTime.plusMinutes(1)); - ReflectionTestUtils.setField(child2, "createdAt", baseTime.plusMinutes(11)); - ReflectionTestUtils.setField(child3, "createdAt", baseTime.plusMinutes(21)); + ReflectionTestUtils.setField(child1, "createdAt", baseTime.plus(1, ChronoUnit.MINUTES)); + ReflectionTestUtils.setField(child2, "createdAt", baseTime.plus(11, ChronoUnit.MINUTES)); + ReflectionTestUtils.setField(child3, "createdAt", baseTime.plus(21, ChronoUnit.MINUTES)); commentRepository.saveAll(List.of(child1, child2, child3)); diff --git a/src/test/java/com/daramg/server/post/presentation/PostQueryControllerTest.java b/src/test/java/com/daramg/server/post/presentation/PostQueryControllerTest.java index 6d2dd67..79ab039 100644 --- a/src/test/java/com/daramg/server/post/presentation/PostQueryControllerTest.java +++ b/src/test/java/com/daramg/server/post/presentation/PostQueryControllerTest.java @@ -17,7 +17,8 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.ResultActions; -import java.time.LocalDateTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; @@ -44,7 +45,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "자유 포스트 제목 1", "자유 포스트 내용입니다", List.of("#해시태그1", "#해시태그2"), - LocalDateTime.of(2024, 1, 15, 10, 30, 0), + Instant.parse("2024-01-15T10:30:00Z"), "작성자1", 10, 5, @@ -60,7 +61,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "자유 포스트 제목 2", "자유 포스트 내용입니다", List.of("#해시태그3"), - LocalDateTime.of(2024, 1, 14, 15, 20, 0), + Instant.parse("2024-01-14T15:20:00Z"), "작성자2", 20, 10, @@ -140,7 +141,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "큐레이션 포스트 제목 1", "큐레이션 포스트 내용입니다", List.of("#큐레이션1", "#음악"), - LocalDateTime.of(2024, 1, 15, 12, 0, 0), + Instant.parse("2024-01-15T12:00:00Z"), "작성자1", 30, 15, @@ -156,7 +157,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "큐레이션 포스트 제목 2", "큐레이션 포스트 내용입니다", List.of("#큐레이션2"), - LocalDateTime.of(2024, 1, 14, 18, 45, 0), + Instant.parse("2024-01-14T18:45:00Z"), "작성자2", 25, 12, @@ -240,7 +241,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "스토리 포스트 제목 1", "스토리 포스트 내용입니다", List.of("#스토리1", "#음악이야기"), - LocalDateTime.of(2024, 1, 15, 14, 30, 0), + Instant.parse("2024-01-15T14:30:00Z"), "작성자1", 50, 25, @@ -256,7 +257,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "스토리 포스트 제목 2", "스토리 포스트 내용입니다", List.of("#스토리2"), - LocalDateTime.of(2024, 1, 14, 20, 15, 0), + Instant.parse("2024-01-14T20:15:00Z"), "작성자2", 40, 20, @@ -335,7 +336,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "발행된 포스트 제목 1", "발행된 포스트 내용입니다", List.of("#발행1", "#음악"), - LocalDateTime.of(2024, 1, 15, 10, 30, 0), + Instant.parse("2024-01-15T10:30:00Z"), "작성자1", 15, 8, @@ -351,7 +352,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "발행된 포스트 제목 2", "발행된 포스트 내용입니다", List.of("#발행2"), - LocalDateTime.of(2024, 1, 14, 15, 20, 0), + Instant.parse("2024-01-14T15:20:00Z"), "작성자1", 20, 10, @@ -435,7 +436,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "임시저장 포스트 제목 1", "임시저장 포스트 내용입니다", List.of("#임시저장1", "#작업중"), - LocalDateTime.of(2024, 1, 15, 10, 30, 0), + Instant.parse("2024-01-15T10:30:00Z"), "작성자1", 0, 0, @@ -451,7 +452,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "임시저장 포스트 제목 2", "임시저장 포스트 내용입니다", List.of("#임시저장2"), - LocalDateTime.of(2024, 1, 14, 15, 20, 0), + Instant.parse("2024-01-14T15:20:00Z"), "작성자1", 0, 0, @@ -535,7 +536,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "스크랩한 포스트 제목 1", "스크랩한 포스트 내용입니다", List.of("#스크랩1", "#좋은음악"), - LocalDateTime.of(2024, 1, 15, 10, 30, 0), + Instant.parse("2024-01-15T10:30:00Z"), "다른작성자1", 50, 25, @@ -551,7 +552,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { "스크랩한 포스트 제목 2", "스크랩한 포스트 내용입니다", List.of("#스크랩2"), - LocalDateTime.of(2024, 1, 14, 15, 20, 0), + Instant.parse("2024-01-14T15:20:00Z"), "다른작성자2", 30, 15, @@ -628,14 +629,14 @@ public class PostQueryControllerTest extends ControllerTestSupport { void 포스트_상세를_조회한다() throws Exception { // given Long postId = 1L; - LocalDateTime createdAt = LocalDateTime.of(2024, 1, 15, 10, 30, 0); - LocalDateTime updatedAt = LocalDateTime.of(2024, 1, 15, 11, 0, 0); + Instant createdAt = Instant.parse("2024-01-15T10:30:00Z"); + Instant updatedAt = Instant.parse("2024-01-15T11:00:00Z"); CommentResponseDto.ChildCommentResponseDto childComment = new CommentResponseDto.ChildCommentResponseDto( 2L, "대댓글 내용", false, 1, - createdAt.plusMinutes(5), + createdAt.plus(5, ChronoUnit.MINUTES), "대댓글작성자", "https://example.com/child-profile.jpg", true @@ -646,7 +647,7 @@ public class PostQueryControllerTest extends ControllerTestSupport { false, 3, 1, - createdAt.plusMinutes(1), + createdAt.plus(1, ChronoUnit.MINUTES), "댓글작성자", "https://example.com/comment-profile.jpg", false, diff --git a/src/test/java/com/daramg/server/search/presentation/SearchControllerTest.java b/src/test/java/com/daramg/server/search/presentation/SearchControllerTest.java index 2a9ca41..cc67f95 100644 --- a/src/test/java/com/daramg/server/search/presentation/SearchControllerTest.java +++ b/src/test/java/com/daramg/server/search/presentation/SearchControllerTest.java @@ -12,7 +12,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.ResultActions; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; @@ -47,7 +47,7 @@ public class SearchControllerTest extends ControllerTestSupport { "모차르트의 피아노 협주곡", PostType.STORY, "작성자닉네임", - LocalDateTime.of(2024, 3, 1, 12, 0, 0) + Instant.parse("2024-03-01T12:00:00Z") ); SearchResponseDto response = new SearchResponseDto(List.of(composer), List.of(post)); diff --git a/src/test/java/com/daramg/server/user/application/UserServiceTest.java b/src/test/java/com/daramg/server/user/application/UserServiceTest.java index 4f8025e..0bae854 100644 --- a/src/test/java/com/daramg/server/user/application/UserServiceTest.java +++ b/src/test/java/com/daramg/server/user/application/UserServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.transaction.annotation.Transactional; +import com.daramg.server.user.domain.UserStatus; import java.time.LocalDate; import static org.assertj.core.api.Assertions.assertThat; @@ -43,9 +44,12 @@ public class UserServiceTest extends ServiceTestSupport { @BeforeEach void setUp() { String encodedPassword = passwordEncoder.encode("password"); + User admin = new User("admin@test.com", encodedPassword, "Admin", LocalDate.now(), null, "admin닉네임", null, null); + userRepository.save(admin); // id=1 로 저장되어 withdraw 테스트에서 사용됨 + follower = new User("follower@email.com", encodedPassword, "팔로워", LocalDate.now(), "profile image", "팔로워닉네임", "bio", null); followed = new User("followed@email.com", encodedPassword, "팔로우당하는사람", LocalDate.now(), "profile image", "팔로우당하는닉네임", "bio", null); - + userRepository.save(follower); userRepository.save(followed); } @@ -461,4 +465,28 @@ class UnfollowFailTest { .hasMessageContaining("언팔로우 대상과 주체의 유저가 동일합니다."); } } + + @Nested + @DisplayName("회원 탈퇴 테스트") + class WithdrawTest { + + @Test + @Transactional + void 회원_탈퇴_시_유저_상태가_DELETED로_변경된다() { + // when + userService.withdraw(follower.getId()); + + // then + User withdrawnUser = userRepository.findById(follower.getId()).orElseThrow(); + assertThat(withdrawnUser.getUserStatus()).isEqualTo(UserStatus.DELETED); + assertThat(withdrawnUser.getDeletedAt()).isNotNull(); + } + + @Test + void 존재하지_않는_유저_탈퇴_시_예외가_발생한다() { + assertThatThrownBy(() -> userService.withdraw(999L)) + .isInstanceOf(BusinessException.class) + .hasMessageContaining("유저를 찾을 수 없습니다."); + } + } } diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 9c50e59..7df0cf9 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -16,6 +16,10 @@ spring: hibernate: ddl-auto: create-drop database-platform: org.hibernate.dialect.H2Dialect + properties: + hibernate: + jdbc: + time_zone: UTC flyway: enabled: false data: