Skip to content

[fix] UTC(Instant) 마이그레이션 + commentCount 버그 + 기타 수정#90

Merged
k3vin7 merged 1 commit intomainfrom
fix/utc-instant-migration
Feb 26, 2026
Merged

[fix] UTC(Instant) 마이그레이션 + commentCount 버그 + 기타 수정#90
k3vin7 merged 1 commit intomainfrom
fix/utc-instant-migration

Conversation

@k3vin7
Copy link
Contributor

@k3vin7 k3vin7 commented Feb 26, 2026

Summary

  • LocalDateTimejava.time.Instant 전면 교체 (UTC 기반 시간 저장)
  • 댓글 삭제 시 commentCount 감소 누락 버그 수정
  • CommentResponseDto.childCommentCount 삭제된 자식 댓글 포함 버그 수정
  • UserService.withdraw() admin 조회 시 getReferenceByIdfindById().orElseThrow() 변경

Changes

Bug Fixes

  • [CRITICAL] commentCount 버그: Post.decrementCommentCount() 메서드 추가, CommentService.deleteComment()에서 호출
  • [MEDIUM] childCommentCount 버그: 삭제된 자식 댓글을 카운트에서 제외하도록 필터링 추가
  • [MEDIUM] admin 조회 버그: getReferenceById(1L)findById(1L).orElseThrow(ADMIN_NOT_FOUND)

UTC 마이그레이션

  • BaseEntity, Post, Notice, User, SearchLog 및 모든 DTO의 LocalDateTimeInstant
  • ServerApplication에서 KST 강제 설정(TimeZone.setDefault) 제거
  • 전체 환경 yml에 spring.jpa.properties.hibernate.jdbc.time_zone: UTC 추가
  • spring.jackson.serialization.write-dates-as-timestamps: false 추가
  • PagingUtils 커서 인코딩/디코딩 Instant 기반으로 교체

Tests

  • CommentServiceTest: 댓글 삭제 시 commentCount 감소 검증 테스트 추가
  • CommentServiceTest: 엔티티 생성 시 createdAt이 UTC Instant로 저장되는지 검증 테스트 추가
  • UserServiceTest: WithdrawTest 클래스 추가 (탈퇴 성공 / 존재하지 않는 유저 예외)

@gemini-code-assist
Copy link

Summary of Changes

Hello @k3vin7, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 Pull Request는 애플리케이션의 시간 처리 방식을 LocalDateTime에서 UTC 기반의 Instant로 전면 마이그레이션하여 시간대 관련 잠재적 문제를 해결하고 데이터 일관성을 확보합니다. 또한, 댓글 삭제 시 게시글의 댓글 수를 정확히 반영하지 못하던 버그와 삭제된 자식 댓글이 카운트에 포함되던 버그를 수정하여 데이터 정확성을 높였습니다. 관리자 계정 조회 로직을 개선하고 관련 테스트를 추가하여 시스템의 안정성과 신뢰성을 향상시켰습니다.

Highlights

  • UTC 기반 시간 마이그레이션: 애플리케이션 전반에 걸쳐 LocalDateTimejava.time.Instant로 교체하여 모든 시간 관련 데이터를 UTC 기준으로 저장하도록 변경했습니다. 이는 BaseEntity, Post, Notice, User, SearchLog 및 모든 DTO에 적용되었으며, ServerApplication에서 KST 강제 설정을 제거하고 Hibernate 및 Jackson 설정에 UTC 타임존을 명시했습니다.
  • 댓글 카운트 버그 수정: 댓글 삭제 시 게시글의 commentCount가 감소하지 않던 버그를 수정하기 위해 Post.decrementCommentCount() 메서드를 추가하고 CommentService.deleteComment()에서 이를 호출하도록 했습니다.
  • 자식 댓글 카운트 버그 수정: CommentResponseDto.childCommentCount가 삭제된 자식 댓글까지 포함하여 카운트하던 버그를 수정했습니다. 이제 삭제된 자식 댓글은 카운트에서 제외됩니다.
  • 관리자 조회 로직 개선: UserService.withdraw() 메서드에서 관리자 계정을 조회할 때 getReferenceById(1L) 대신 findById(1L).orElseThrow()를 사용하여 존재하지 않는 관리자 ID에 대한 예외 처리를 명확히 했습니다.
  • 테스트 코드 강화: 댓글 삭제 시 commentCount 감소 검증 테스트, 엔티티 생성 시 createdAt이 UTC Instant로 저장되는지 검증하는 테스트, 그리고 회원 탈퇴 성공 및 존재하지 않는 유저 예외 처리에 대한 UserServiceTest를 추가했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • build.gradle
    • 테스트 JVM 인수에 user.timezone=UTC를 설정하여 테스트 환경의 시간대를 UTC로 변경했습니다.
  • src/main/generated/com/daramg/server/comment/domain/QComment.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/comment/domain/QCommentLike.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/common/domain/QBaseEntity.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/composer/domain/QComposer.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/composer/domain/QComposerLike.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/composer/domain/QComposerPost.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/post/domain/QCurationPost.java
    • createdAt, deletedAt, updatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
    • viewCount 필드를 추가했습니다.
  • src/main/generated/com/daramg/server/post/domain/QFreePost.java
    • createdAt, deletedAt, updatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
    • viewCount 필드를 추가했습니다.
  • src/main/generated/com/daramg/server/post/domain/QPost.java
    • createdAt, deletedAt, updatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
    • viewCount 필드를 추가했습니다.
  • src/main/generated/com/daramg/server/post/domain/QPostLike.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/post/domain/QPostScrap.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/post/domain/QReport.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/post/domain/QStoryPost.java
    • createdAt, deletedAt, updatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
    • viewCount 필드를 추가했습니다.
  • src/main/generated/com/daramg/server/user/domain/QUser.java
    • createdAt, deletedAt, updatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/generated/com/daramg/server/user/domain/QUserFollow.java
    • createdAtupdatedAt 필드의 타입을 java.time.LocalDateTime에서 java.time.Instant로 변경했습니다.
  • src/main/java/com/daramg/server/ServerApplication.java
    • 애플리케이션 시작 시 TimeZone.setDefault("Asia/Seoul")을 호출하는 로직을 제거했습니다.
  • src/main/java/com/daramg/server/auth/application/AuthService.java
    • 재가입 불가 로직에서 LocalDateTime.now() 대신 Instant.now()를 사용하도록 변경했습니다.
  • src/main/java/com/daramg/server/comment/application/CommentService.java
    • 댓글 삭제 시 해당 게시글의 commentCount를 감소시키는 로직을 추가했습니다.
  • src/main/java/com/daramg/server/comment/dto/CommentResponseDto.java
    • createdAt 필드의 타입을 LocalDateTime에서 Instant로 변경했습니다.
    • childCommentCount 계산 시 삭제된 자식 댓글을 제외하도록 필터링 로직을 추가했습니다.
  • src/main/java/com/daramg/server/common/domain/BaseEntity.java
    • createdAtupdatedAt 필드의 타입을 LocalDateTime에서 Instant로 변경하고, PrePersistPreUpdate 메서드에서 Instant.now()를 사용하도록 수정했습니다.
  • src/main/java/com/daramg/server/common/util/PagingUtils.java
    • 커서 기반 페이지네이션 로직에서 LocalDateTime 관련 타입을 모두 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/notice/domain/Notice.java
    • deletedAt 필드의 타입을 LocalDateTime에서 Instant로 변경하고, softDelete() 메서드에서 Instant.now()를 사용하도록 수정했습니다.
  • src/main/java/com/daramg/server/notice/dto/NoticeDetailResponse.java
    • createdAt 필드의 타입을 LocalDateTime에서 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/notice/dto/NoticeResponseDto.java
    • createdAt 필드의 타입을 LocalDateTime에서 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/notification/application/NotificationQueryService.java
    • 읽지 않은 알림 개수 조회 시 LocalDateTime.now() 대신 Instant.now()를 사용하도록 변경했습니다.
  • src/main/java/com/daramg/server/notification/dto/NotificationResponseDto.java
    • createdAt 필드의 타입을 LocalDateTime에서 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/notification/repository/NotificationQueryRepositoryImpl.java
    • 알림 조회 쿼리에서 LocalDateTime.now() 대신 Instant.now()를 사용하도록 변경했습니다.
  • src/main/java/com/daramg/server/notification/repository/NotificationRepository.java
    • countUnreadByReceiverIdSince 메서드의 since 파라미터 타입을 LocalDateTime에서 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/post/domain/Post.java
    • deletedAt 필드의 타입을 LocalDateTime에서 Instant로 변경하고, softDelete() 메서드에서 Instant.now()를 사용하도록 수정했습니다.
    • decrementCommentCount() 메서드를 추가하여 commentCount를 안전하게 감소시킬 수 있도록 했습니다.
  • src/main/java/com/daramg/server/post/dto/PostDetailResponse.java
    • createdAtupdatedAt 필드의 타입을 LocalDateTime에서 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/post/dto/PostResponseDto.java
    • createdAt 필드의 타입을 LocalDateTime에서 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/post/repository/PostRepository.java
    • softDeleteAllByUserId 메서드의 now 파라미터 타입을 LocalDateTime에서 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/search/domain/SearchLog.java
    • searchedAt 필드의 타입을 LocalDateTime에서 Instant로 변경하고, PrePersist 메서드에서 Instant.now()를 사용하도록 수정했습니다.
  • src/main/java/com/daramg/server/search/dto/SearchResponseDto.java
    • 내부 PostResult 레코드의 createdAt 필드 타입을 LocalDateTime에서 Instant로 변경했습니다.
  • src/main/java/com/daramg/server/user/application/UserService.java
    • 회원 탈퇴 시 postRepository.softDeleteAllByUserId 호출에 Instant.now()를 사용하도록 변경했습니다.
    • 관리자 조회 로직을 userRepository.getReferenceById(1L)에서 userRepository.findById(1L).orElseThrow()로 변경하여 안정성을 높였습니다.
  • src/main/java/com/daramg/server/user/domain/User.java
    • deletedAt 필드의 타입을 LocalDateTime에서 Instant로 변경하고, withdraw() 메서드에서 Instant.now()를 사용하도록 수정했습니다.
  • src/main/java/com/daramg/server/user/exception/UserErrorStatus.java
    • 관리자 계정을 찾을 수 없을 때 발생하는 ADMIN_NOT_FOUND 에러 상태를 추가했습니다.
  • src/main/resources/application-local.yml
    • Hibernate JDBC 타임존을 UTC로 설정하는 spring.jpa.properties.hibernate.jdbc.time_zone: UTC를 추가했습니다.
  • src/main/resources/application-prod.yml
    • Hibernate JDBC 타임존을 UTC로 설정하는 spring.jpa.properties.hibernate.jdbc.time_zone: UTC를 추가했습니다.
  • src/main/resources/application.yml
    • Hibernate JDBC 타임존을 UTC로 설정하는 spring.jpa.properties.hibernate.jdbc.time_zone: UTC를 추가했습니다.
    • Jackson 직렬화 시 날짜를 타임스탬프가 아닌 ISO-8601 형식으로 출력하도록 spring.jackson.serialization.write-dates-as-timestamps: false를 추가했습니다.
  • src/test/java/com/daramg/server/comment/application/CommentServiceTest.java
    • 테스트 클래스에 Instant 임포트를 추가했습니다.
    • 댓글 삭제 시 게시글의 댓글 카운트가 감소하는지 검증하는 테스트를 추가했습니다.
    • 엔티티 생성 시 createdAt이 UTC Instant로 저장되는지 검증하는 테스트를 추가했습니다.
  • src/test/java/com/daramg/server/common/util/PagingUtilsTest.java
    • 테스트 클래스에 InstantChronoUnit 임포트를 추가했습니다.
    • 테스트 엔티티 TestEntitycreatedAt 필드 타입을 LocalDateTime에서 Instant로 변경했습니다.
    • 테스트 데이터 생성 시 LocalDateTime.now() 대신 Instant.now()를 사용하도록 변경했습니다.
    • 커서 인코딩/디코딩 테스트에서 LocalDateTime 대신 Instant를 사용하도록 변경했습니다.
  • src/test/java/com/daramg/server/composer/presentation/ComposerQueryControllerTest.java
    • 테스트 클래스에 Instant 임포트를 추가했습니다.
    • 응답 DTO 생성 시 LocalDateTime 대신 Instant.parse("...")를 사용하도록 변경했습니다.
  • src/test/java/com/daramg/server/notice/presentation/NoticeQueryControllerTest.java
    • 테스트 클래스에 Instant 임포트를 추가했습니다.
    • 응답 DTO 생성 시 LocalDateTime.now() 대신 Instant.now()를 사용하도록 변경했습니다.
  • src/test/java/com/daramg/server/notification/presentation/NotificationQueryControllerTest.java
    • 테스트 클래스에 Instant 임포트를 추가했습니다.
    • 응답 DTO 생성 시 LocalDateTime.of() 대신 Instant.parse("...")를 사용하도록 변경했습니다.
  • src/test/java/com/daramg/server/post/application/PostQueryServiceTest.java
    • 테스트 클래스에 InstantChronoUnit 임포트를 추가했습니다.
    • 유효하지 않은 커서 테스트에서 LocalDateTime.now() 대신 Instant.now()를 사용하도록 변경했습니다.
    • 댓글 및 답글 정렬 테스트에서 LocalDateTime 대신 Instant를 사용하고 ChronoUnit으로 시간 계산을 수행하도록 변경했습니다.
  • src/test/java/com/daramg/server/post/presentation/PostQueryControllerTest.java
    • 테스트 클래스에 InstantChronoUnit 임포트를 추가했습니다.
    • 응답 DTO 생성 시 LocalDateTime.of() 대신 Instant.parse("...")를 사용하도록 변경했습니다.
    • 포스트 상세 조회 테스트에서 LocalDateTime 대신 Instant를 사용하고 ChronoUnit으로 시간 계산을 수행하도록 변경했습니다.
  • src/test/java/com/daramg/server/search/presentation/SearchControllerTest.java
    • 테스트 클래스에 Instant 임포트를 추가했습니다.
    • 응답 DTO 생성 시 LocalDateTime.of() 대신 Instant.parse("...")를 사용하도록 변경했습니다.
  • src/test/java/com/daramg/server/user/application/UserServiceTest.java
    • 테스트 클래스에 UserStatus 임포트를 추가했습니다.
    • setUp 메서드에 관리자 유저를 미리 저장하는 로직을 추가했습니다.
    • 회원 탈퇴 테스트(WithdrawTest) 클래스를 추가하여 유저 상태 변경 및 존재하지 않는 유저 탈퇴 시 예외 발생을 검증했습니다.
  • src/test/resources/application.yml
    • 테스트 환경에서 Hibernate JDBC 타임존을 UTC로 설정하는 spring.jpa.properties.hibernate.jdbc.time_zone: UTC를 추가했습니다.
Activity
  • 댓글 삭제 시 commentCount 감소 검증 테스트가 CommentServiceTest에 추가되었습니다.
  • 엔티티 생성 시 createdAt이 UTC Instant로 저장되는지 검증하는 테스트가 CommentServiceTest에 추가되었습니다.
  • UserServiceTest에 회원 탈퇴 성공 및 존재하지 않는 유저 탈퇴 시 예외 처리를 검증하는 WithdrawTest 클래스가 추가되었습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 Pull Request는 전반적인 시간 처리 방식을 LocalDateTime에서 UTC 기반의 Instant로 마이그레이션하는 중요한 작업을 수행했습니다. 이와 함께 댓글 수 계산 버그, 관리자 계정 조회 방식 등 여러 중요한 수정이 포함되었습니다.

전체적으로 변경 사항은 매우 체계적이고 일관성 있게 적용되었습니다. 특히, Instant로의 마이그레이션은 애플리케이션의 시간 처리 안정성을 크게 향상시킬 것입니다. 버그 수정 사항들도 정확하며, 관련 테스트 케이스가 추가되어 코드의 신뢰성을 높였습니다.

한 가지 개선점을 제안합니다. UserService에서 관리자 계정을 조회할 때 하드코딩된 ID를 사용하는 대신, 설정 파일을 통해 이메일 주소 등으로 조회하도록 변경하면 더 유연하고 안정적인 코드가 될 것입니다.

훌륭한 기여에 감사드립니다!

Comment on lines +132 to +133
User admin = userRepository.findById(1L)
.orElseThrow(() -> new BusinessException(UserErrorStatus.ADMIN_NOT_FOUND));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

관리자 계정을 하드코딩된 ID 1L로 조회하는 것은 위험할 수 있습니다. 데이터베이스 환경에 따라 관리자 ID가 1이 아닐 수 있으며, 이는 예기치 않은 ADMIN_NOT_FOUND 오류를 발생시킬 수 있습니다.

ID 대신 이메일 주소와 같이 더 안정적인 속성으로 관리자를 조회하는 것을 고려해 보세요. 예를 들어, 설정 파일에 관리자 이메일을 정의하고 이를 사용하여 조회할 수 있습니다.

Example:

application.yml:

app:
  admin:
    email: admin@example.com

UserService.java:

@Value("${app.admin.email}")
private String adminEmail;

// ... in withdraw()
User admin = userRepository.findByEmail(adminEmail)
        .orElseThrow(() -> new BusinessException(UserErrorStatus.ADMIN_NOT_FOUND));

@k3vin7 k3vin7 merged commit 2183307 into main Feb 26, 2026
1 check passed
@k3vin7 k3vin7 deleted the fix/utc-instant-migration branch February 26, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant