diff --git a/deploy-docker.sh b/deploy-docker.sh index 1a5f7ce..996ece1 100755 --- a/deploy-docker.sh +++ b/deploy-docker.sh @@ -74,7 +74,7 @@ docker run -d \ --name memory-app \ --restart unless-stopped \ -p 8081:8081 \ - -e SPRING_DATASOURCE_URL="jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true" \ + -e SPRING_DATASOURCE_URL="jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" \ -e SPRING_DATASOURCE_USERNAME="${DB_USERNAME}" \ -e SPRING_DATASOURCE_PASSWORD="${DB_PASSWORD}" \ -e CLOUD_AWS_CREDENTIALS_ACCESS_KEY="${AWS_ACCESS_KEY_ID}" \ diff --git a/deploy-production-direct.sh b/deploy-production-direct.sh index 7206254..e572a77 100755 --- a/deploy-production-direct.sh +++ b/deploy-production-direct.sh @@ -62,7 +62,7 @@ docker run -d \ --network "$NETWORK_NAME" \ --restart unless-stopped \ -p 8081:8081 \ - -e SPRING_DATASOURCE_URL="jdbc:mysql://memory-mysql:3306/$MYSQL_DATABASE?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true" \ + -e SPRING_DATASOURCE_URL="jdbc:mysql://memory-mysql:3306/$MYSQL_DATABASE?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" \ -e SPRING_DATASOURCE_USERNAME="$MYSQL_USER" \ -e SPRING_DATASOURCE_PASSWORD="$MYSQL_PASSWORD" \ "$IMAGE_NAME:$IMAGE_TAG" diff --git a/deploy-production-docker.sh b/deploy-production-docker.sh index 19941ed..9f8086a 100755 --- a/deploy-production-docker.sh +++ b/deploy-production-docker.sh @@ -125,7 +125,7 @@ deploy_docker() { --name memory-app \ --restart unless-stopped \ -p 8081:8081 \ - -e SPRING_DATASOURCE_URL="jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true" \ + -e SPRING_DATASOURCE_URL="jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" \ -e SPRING_DATASOURCE_USERNAME="${DB_USERNAME}" \ -e SPRING_DATASOURCE_PASSWORD="${DB_PASSWORD}" \ -e CLOUD_AWS_CREDENTIALS_ACCESS_KEY="${AWS_ACCESS_KEY_ID}" \ diff --git a/docker-compose.yml b/docker-compose.yml index da21a35..476e7fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: mysql: condition: service_healthy environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/memory_db?useSSL=false&allowPublicKeyRetrieval=true + SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/memory_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true SPRING_DATASOURCE_USERNAME: memory_user SPRING_DATASOURCE_PASSWORD: memory_password ports: diff --git a/src/main/java/zim/tave/memory/config/JacksonConfig.java b/src/main/java/zim/tave/memory/config/JacksonConfig.java new file mode 100644 index 0000000..d2bac73 --- /dev/null +++ b/src/main/java/zim/tave/memory/config/JacksonConfig.java @@ -0,0 +1,72 @@ +package zim.tave.memory.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.TimeZone; + +@Configuration +public class JacksonConfig { + + private static final DateTimeFormatter OFFSET_DATE_TIME_FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + @Bean + public Jackson2ObjectMapperBuilderCustomizer utcOffsetDateTimeCustomizer() { + return builder -> { + // Ensure UTC baseline + builder.timeZone(TimeZone.getTimeZone("UTC")); + builder.serializationInclusion(JsonInclude.Include.NON_NULL); + builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + + // Force OffsetDateTime format globally (UTC -> "Z") + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(OffsetDateTime.class, new JsonSerializer<>() { + @Override + public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (value == null) { + gen.writeNull(); + return; + } + gen.writeString(value.format(OFFSET_DATE_TIME_FORMATTER)); + } + }); + javaTimeModule.addDeserializer(OffsetDateTime.class, new JsonDeserializer<>() { + @Override + public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String raw = p.getValueAsString(); + if (raw == null || raw.isBlank()) { + return null; + } + return OffsetDateTime.parse(raw, OFFSET_DATE_TIME_FORMATTER); + } + }); + + builder.modules(javaTimeModule); + }; + } + + /** + * Helper for normalizing an OffsetDateTime to UTC when needed. + * (Not wired automatically; kept for consistency utilities if required.) + */ + public static OffsetDateTime normalizeToUtc(OffsetDateTime value) { + return value == null ? null : value.withOffsetSameInstant(ZoneOffset.UTC); + } +} + diff --git a/src/main/java/zim/tave/memory/domain/Board.java b/src/main/java/zim/tave/memory/domain/Board.java index 0e37ddf..bb69fbc 100644 --- a/src/main/java/zim/tave/memory/domain/Board.java +++ b/src/main/java/zim/tave/memory/domain/Board.java @@ -4,7 +4,8 @@ import jakarta.persistence.*; import lombok.*; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; @@ -23,9 +24,9 @@ public class Board { private String title; @Column(nullable = false, updatable = false) - private LocalDateTime createdAt; + private OffsetDateTime createdAt; - private LocalDateTime updatedAt; + private OffsetDateTime updatedAt; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "userId", nullable = false) @@ -40,11 +41,13 @@ public class Board { @PrePersist public void prePersist() { - createdAt = LocalDateTime.now(); + if (this.createdAt == null) { + createdAt = OffsetDateTime.now(ZoneOffset.UTC); + } } @PreUpdate public void preUpdate() { - updatedAt = LocalDateTime.now(); + updatedAt = OffsetDateTime.now(ZoneOffset.UTC); } } diff --git a/src/main/java/zim/tave/memory/domain/Diary.java b/src/main/java/zim/tave/memory/domain/Diary.java index cc2cad9..3283d15 100644 --- a/src/main/java/zim/tave/memory/domain/Diary.java +++ b/src/main/java/zim/tave/memory/domain/Diary.java @@ -5,7 +5,8 @@ import lombok.Setter; import org.hibernate.annotations.BatchSize; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; @@ -33,7 +34,7 @@ public class Diary { private String city; @Column(nullable = false) - private LocalDateTime dateTime; + private OffsetDateTime dateTime; @Lob @Column(nullable = false, length = 88) @@ -54,14 +55,16 @@ public class Diary { private List diaryImages = new ArrayList<>(); @Column(updatable = false) - private LocalDateTime createdAt; + private OffsetDateTime createdAt; @Column(nullable = false) private Boolean isStored; @PrePersist protected void onCreate() { - this.createdAt = LocalDateTime.now(); + if (this.createdAt == null) { + this.createdAt = OffsetDateTime.now(ZoneOffset.UTC); + } } public void addDiaryImage(DiaryImage image) { @@ -70,7 +73,7 @@ public void addDiaryImage(DiaryImage image) { } public static Diary createDiary(User user, Trip trip, Country country, String city, - LocalDateTime dateTime, String content) { + OffsetDateTime dateTime, String content) { Diary diary = new Diary(); diary.setUser(user); diary.setTrip(trip); @@ -78,7 +81,7 @@ public static Diary createDiary(User user, Trip trip, Country country, String ci diary.setCity(city); diary.setDateTime(dateTime); diary.setContent(content != null ? content : ""); // content는 선택 필드이므로 null인 경우 빈 문자열로 처리 - diary.setCreatedAt(dateTime != null ? dateTime : LocalDateTime.now()); + diary.setCreatedAt(dateTime != null ? dateTime : OffsetDateTime.now(ZoneOffset.UTC)); diary.setIsStored(false); return diary; } diff --git a/src/main/java/zim/tave/memory/domain/Trip.java b/src/main/java/zim/tave/memory/domain/Trip.java index 278c920..5d20091 100644 --- a/src/main/java/zim/tave/memory/domain/Trip.java +++ b/src/main/java/zim/tave/memory/domain/Trip.java @@ -7,6 +7,7 @@ import lombok.Setter; import java.time.LocalDate; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; @@ -55,7 +56,7 @@ public class Trip { @PrePersist protected void onCreate() { if (this.startDate == null) { - this.startDate = LocalDate.now(); + this.startDate = LocalDate.now(ZoneOffset.UTC); } if (this.endDate == null) { this.endDate = this.startDate; @@ -76,8 +77,8 @@ public static Trip createTrip(User user, String tripName, String description, Tr trip.setTripName(tripName); trip.setDescription(description); trip.setTripTheme(tripTheme); - trip.setStartDate(LocalDate.now()); - trip.setEndDate(LocalDate.now()); + trip.setStartDate(LocalDate.now(ZoneOffset.UTC)); + trip.setEndDate(LocalDate.now(ZoneOffset.UTC)); return trip; } diff --git a/src/main/java/zim/tave/memory/domain/VisitedCountry.java b/src/main/java/zim/tave/memory/domain/VisitedCountry.java index 0b33117..008e212 100644 --- a/src/main/java/zim/tave/memory/domain/VisitedCountry.java +++ b/src/main/java/zim/tave/memory/domain/VisitedCountry.java @@ -5,7 +5,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; @Entity @Getter @@ -19,7 +20,7 @@ public class VisitedCountry { private String color; - private LocalDateTime createdAt; + private OffsetDateTime createdAt; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "userId") @@ -35,6 +36,8 @@ public class VisitedCountry { @PrePersist protected void onCreate() { - this.createdAt = LocalDateTime.now(); + if (this.createdAt == null) { + this.createdAt = OffsetDateTime.now(ZoneOffset.UTC); + } } } diff --git a/src/main/java/zim/tave/memory/dto/request/CreateDiaryRequest.java b/src/main/java/zim/tave/memory/dto/request/CreateDiaryRequest.java index 564408f..d943bff 100644 --- a/src/main/java/zim/tave/memory/dto/request/CreateDiaryRequest.java +++ b/src/main/java/zim/tave/memory/dto/request/CreateDiaryRequest.java @@ -22,7 +22,6 @@ "tripId": 1, "countryCode": "KR", "city": "제주시", - "dateTime": "2025-11-30T11:20:01.570Z", "content": "오늘은 제주도에서 멋진 하루를 보냈다.", "images": [ { @@ -56,14 +55,6 @@ public class CreateDiaryRequest { @Schema(description = "도시명 (필수)", example = "제주시") private String city; - @Schema( - description = """ - 일기 작성 날짜 및 시간 (ISO 8601 형식) : 자동 생성됩니다. - """, - example = "2025-11-30T11:20:01.570Z" - ) - private String dateTime; - @Schema(description = "일기 내용", example = "오늘은 제주도에서 멋진 하루를 보냈다.") private String content; diff --git a/src/main/java/zim/tave/memory/dto/response/BoardCreateResponseDto.java b/src/main/java/zim/tave/memory/dto/response/BoardCreateResponseDto.java index e735d8e..425cc02 100644 --- a/src/main/java/zim/tave/memory/dto/response/BoardCreateResponseDto.java +++ b/src/main/java/zim/tave/memory/dto/response/BoardCreateResponseDto.java @@ -6,8 +6,7 @@ import lombok.NoArgsConstructor; import zim.tave.memory.domain.Board; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.time.OffsetDateTime; @Getter @AllArgsConstructor @@ -24,21 +23,19 @@ public class BoardCreateResponseDto { @Schema(description = "보드 테마 ID", example = "3") private Long boardThemeId; - @Schema(description = "보드 생성 시각", example = "2025-12-01T14:20:00") - private String createdAt; + @Schema(description = "보드 생성 시각 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2025-12-01T14:20:00.000Z") + private OffsetDateTime createdAt; - @Schema(description = "보드 최종 수정 시각", example = "2025-12-01T14:20:00") - private String updatedAt; + @Schema(description = "보드 최종 수정 시각 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2025-12-01T14:20:00.000Z") + private OffsetDateTime updatedAt; public static BoardCreateResponseDto from(Board board) { - DateTimeFormatter fmt = DateTimeFormatter.ISO_LOCAL_DATE_TIME; - return new BoardCreateResponseDto( board.getBoardId(), board.getTitle(), board.getBoardTheme().getBoardThemeId(), - board.getCreatedAt().format(fmt), - board.getUpdatedAt().format(fmt) + board.getCreatedAt(), + board.getUpdatedAt() ); } } diff --git a/src/main/java/zim/tave/memory/dto/response/BoardDetailResponseDto.java b/src/main/java/zim/tave/memory/dto/response/BoardDetailResponseDto.java index a4a31a7..0590d7d 100644 --- a/src/main/java/zim/tave/memory/dto/response/BoardDetailResponseDto.java +++ b/src/main/java/zim/tave/memory/dto/response/BoardDetailResponseDto.java @@ -8,8 +8,7 @@ import zim.tave.memory.domain.Board; import zim.tave.memory.domain.BoardStickerMap; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.time.OffsetDateTime; import java.util.List; @Getter @@ -31,25 +30,23 @@ public class BoardDetailResponseDto { @Schema(description = "테마 썸네일 URL") private String thumbnailUrl; - @Schema(description = "생성 시각") - private String createdAt; + @Schema(description = "생성 시각 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2025-12-01T14:20:00.000Z") + private OffsetDateTime createdAt; - @Schema(description = "수정 시각") - private String updatedAt; + @Schema(description = "수정 시각 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2025-12-01T17:30:00.000Z") + private OffsetDateTime updatedAt; @Schema(description = "스티커 목록") private List stickers; public static BoardDetailResponseDto from(Board board, List stickerMaps) { - DateTimeFormatter fmt = DateTimeFormatter.ISO_LOCAL_DATE_TIME; - return BoardDetailResponseDto.builder() .boardId(board.getBoardId()) .title(board.getTitle()) .boardThemeId(board.getBoardTheme().getBoardThemeId()) .thumbnailUrl(board.getBoardTheme().getThumbnailUrl()) - .createdAt(board.getCreatedAt().format(fmt)) - .updatedAt(board.getUpdatedAt().format(fmt)) + .createdAt(board.getCreatedAt()) + .updatedAt(board.getUpdatedAt()) .stickers(stickerMaps.stream() .map(BoardStickerDetailDto::from) .toList()) diff --git a/src/main/java/zim/tave/memory/dto/response/BoardInformResponseDto.java b/src/main/java/zim/tave/memory/dto/response/BoardInformResponseDto.java index 79c9b47..2e2fc38 100644 --- a/src/main/java/zim/tave/memory/dto/response/BoardInformResponseDto.java +++ b/src/main/java/zim/tave/memory/dto/response/BoardInformResponseDto.java @@ -5,8 +5,7 @@ import lombok.Getter; import zim.tave.memory.domain.Board; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.time.OffsetDateTime; import java.util.List; @Getter @@ -23,11 +22,11 @@ public class BoardInformResponseDto { @Schema(description = "보드 테마 ID", example = "3") private Long boardThemeId; - @Schema(description = "보드 생성 시각", example = "2025-12-01T14:20:00") - private String createdAt; + @Schema(description = "보드 생성 시각 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2025-12-01T14:20:00.000Z") + private OffsetDateTime createdAt; - @Schema(description = "보드 최종 수정 시각", example = "2025-12-01T14:20:00") - private String updatedAt; + @Schema(description = "보드 최종 수정 시각 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2025-12-01T14:20:00.000Z") + private OffsetDateTime updatedAt; @Schema(description = "보드에 포함된 스티커 개수", example = "3") private int stickerCount; @@ -36,14 +35,12 @@ public class BoardInformResponseDto { private List stickerUrls; public static BoardInformResponseDto from(Board board, List stickerUrls) { - DateTimeFormatter fmt = DateTimeFormatter.ISO_LOCAL_DATE_TIME; - return new BoardInformResponseDto( board.getBoardId(), board.getTitle(), board.getBoardTheme().getBoardThemeId(), - board.getCreatedAt().format(fmt), - board.getUpdatedAt().format(fmt), + board.getCreatedAt(), + board.getUpdatedAt(), stickerUrls.size(), stickerUrls ); diff --git a/src/main/java/zim/tave/memory/dto/response/BoardUpdateResponseDto.java b/src/main/java/zim/tave/memory/dto/response/BoardUpdateResponseDto.java index be15794..7faf365 100644 --- a/src/main/java/zim/tave/memory/dto/response/BoardUpdateResponseDto.java +++ b/src/main/java/zim/tave/memory/dto/response/BoardUpdateResponseDto.java @@ -7,8 +7,7 @@ import zim.tave.memory.domain.Board; import zim.tave.memory.domain.BoardStickerMap; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.time.OffsetDateTime; import java.util.List; @Getter @@ -20,23 +19,20 @@ public class BoardUpdateResponseDto { @Schema(description = "보드 ID", example = "21") private Long boardId; - @Schema(description = "보드 최종 수정 시각", example = "2025-12-01T17:30:00") - private String updatedAt; + @Schema(description = "보드 최종 수정 시각 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2025-12-01T17:30:00.000Z") + private OffsetDateTime updatedAt; @Schema(description = "적용된 스티커 리스트") private List stickers; public static BoardUpdateResponseDto from(Board board, List stickerMaps) { - - DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; - List stickerItems = stickerMaps.stream() .map(BoardStickerItem::from) .toList(); return new BoardUpdateResponseDto( board.getBoardId(), - board.getUpdatedAt().format(formatter), + board.getUpdatedAt(), stickerItems ); } diff --git a/src/main/java/zim/tave/memory/dto/response/DiaryResponseDto.java b/src/main/java/zim/tave/memory/dto/response/DiaryResponseDto.java index f1b7fef..96f28de 100644 --- a/src/main/java/zim/tave/memory/dto/response/DiaryResponseDto.java +++ b/src/main/java/zim/tave/memory/dto/response/DiaryResponseDto.java @@ -6,7 +6,7 @@ import lombok.Builder; import lombok.AllArgsConstructor; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.util.List; @Getter @@ -30,11 +30,11 @@ public class DiaryResponseDto { @Schema(description = "상세 위치", example = "한라산 정상") private final String detailedLocation; - @Schema(description = "일기 작성 날짜 및 시간", example = "2025-11-30T11:20:01") - private final LocalDateTime dateTime; + @Schema(description = "일기 작성 날짜 및 시간 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2024-05-20T10:00:00.000Z") + private final OffsetDateTime dateTime; - @Schema(description = "일기 생성 날짜 및 시간", example = "2025-11-30T11:20:01") - private final LocalDateTime createdAt; + @Schema(description = "일기 생성 날짜 및 시간 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2024-05-20T10:00:00.000Z") + private final OffsetDateTime createdAt; @Schema(description = "일기 내용", example = "오늘은 제주도에서 멋진 하루를 보냈다.") private final String content; diff --git a/src/main/java/zim/tave/memory/dto/response/StoredDiaryResponseDto.java b/src/main/java/zim/tave/memory/dto/response/StoredDiaryResponseDto.java index 921476c..3c8214a 100644 --- a/src/main/java/zim/tave/memory/dto/response/StoredDiaryResponseDto.java +++ b/src/main/java/zim/tave/memory/dto/response/StoredDiaryResponseDto.java @@ -8,7 +8,7 @@ import zim.tave.memory.domain.Diary; import zim.tave.memory.domain.DiaryImage; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; @Getter @Builder @@ -23,8 +23,8 @@ public class StoredDiaryResponseDto { @Schema(description = "일기 대표 이미지 URL", example = "https://s3.amazonaws.com/images/diary1.jpg") private String representativeImageUrl; - @Schema(description = "일기 작성 날짜", example = "2024-11-14T18:20:00") - private LocalDateTime createdAt; + @Schema(description = "일기 작성 날짜 (UTC 기준, ISO-8601 형식). 프론트엔드에서는 사용자의 로컬 타임존으로 변환하여 표시해야 합니다.", example = "2024-11-14T18:20:00.000Z") + private OffsetDateTime createdAt; @Schema(description = "작성 요일", example = "THURSDAY") private String dayOfWeek; @@ -38,8 +38,8 @@ public static StoredDiaryResponseDto from(Diary diary) { .findFirst() .orElse(null); - LocalDateTime created = diary.getCreatedAt(); - String day = created.getDayOfWeek().name(); + OffsetDateTime created = diary.getCreatedAt(); + String day = created.toLocalDateTime().getDayOfWeek().name(); return StoredDiaryResponseDto.builder() .diaryId(diary.getId()) diff --git a/src/main/java/zim/tave/memory/service/BoardService.java b/src/main/java/zim/tave/memory/service/BoardService.java index dc30035..b37104d 100644 --- a/src/main/java/zim/tave/memory/service/BoardService.java +++ b/src/main/java/zim/tave/memory/service/BoardService.java @@ -14,7 +14,8 @@ import zim.tave.memory.repository.*; import java.math.BigDecimal; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -47,8 +48,7 @@ public BoardCreateResponseDto createBoard(Long userId, BoardCreateRequestDto req board.setUser(user); board.setBoardTheme(theme); board.setTitle(requestDto.getTitle()); - board.setCreatedAt(LocalDateTime.now()); - board.setUpdatedAt(LocalDateTime.now()); + // createdAt과 updatedAt은 @PrePersist, @PreUpdate에서 자동 설정됨 Board saved = boardRepository.save(board); @@ -148,7 +148,7 @@ public BoardStickerAddResponseDto addSticker( BoardStickerMap saved = boardStickerMapRepository.save(map); // 보드 업데이트 시간 갱신 - board.setUpdatedAt(LocalDateTime.now()); + board.setUpdatedAt(OffsetDateTime.now(ZoneOffset.UTC)); return BoardStickerAddResponseDto.from(saved); } @@ -181,7 +181,7 @@ public BoardStickerUpdateResponseDto updateSticker( map.setPosY(BigDecimal.valueOf(dto.getPosY())); map.setRotation(BigDecimal.valueOf(dto.getRotation())); - board.setUpdatedAt(LocalDateTime.now()); + board.setUpdatedAt(OffsetDateTime.now(ZoneOffset.UTC)); return BoardStickerUpdateResponseDto.from(map); } @@ -207,7 +207,7 @@ public void deleteStickerFromBoard(Long userId, Long boardId, Long boardStickerI boardStickerMapRepository.delete(map); - board.setUpdatedAt(LocalDateTime.now()); + board.setUpdatedAt(OffsetDateTime.now(ZoneOffset.UTC)); } } diff --git a/src/main/java/zim/tave/memory/service/DiaryService.java b/src/main/java/zim/tave/memory/service/DiaryService.java index 1c0c847..12d1cd5 100644 --- a/src/main/java/zim/tave/memory/service/DiaryService.java +++ b/src/main/java/zim/tave/memory/service/DiaryService.java @@ -13,11 +13,8 @@ import zim.tave.memory.repository.*; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; +import java.time.ZoneOffset; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -36,29 +33,6 @@ public class DiaryService { private final CountryService countryService; private final VisitedCountryService visitedCountryService; - // 날짜 및 시간 파싱 (ISO 8601 형식 지원) - private LocalDateTime parseDateTime(String dateTimeString) { - if (dateTimeString == null || dateTimeString.trim().isEmpty()) { - throw new CustomException(ErrorCode.VALIDATION_ERROR); - } - - String trimmed = dateTimeString.trim(); - - try { - // offset 또는 Z 포함 ISO => OffsetDateTime.parse가 대부분 처리 - OffsetDateTime odt = OffsetDateTime.parse(trimmed); - return odt.atZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime(); - } catch (DateTimeParseException ignored) { - try { - // 타임존 없는 로컬 포맷: yyyy-MM-dd'T'HH:mm:ss[.SSS...] - // ISO_LOCAL_DATE_TIME은 0-9자리의 소수점 초를 모두 처리 가능 - return LocalDateTime.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE_TIME); - } catch (DateTimeParseException e) { - throw new CustomException(ErrorCode.VALIDATION_ERROR); - } - } - } - // 이미지 검증&저장 private void validateAndAttachImages(Diary diary, List images) { if (images == null || images.size() != 2) { @@ -108,8 +82,8 @@ public DiaryResponseDto createDiary(CreateDiaryRequest request) { Country country = countryService.findByCode(request.getCountryCode()); if (country == null) throw new CustomException(ErrorCode.INVALID_COUNTRY_CODE); - // 날짜 및 시간 파싱 및 검증 - LocalDateTime dateTime = parseDateTime(request.getDateTime()); + // 날짜 및 시간 자동 생성 (UTC 기준) + OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC); // 감정 검증 (기본 = 1) Emotion emotion = emotionRepository.findById( diff --git a/src/main/java/zim/tave/memory/service/JoinService.java b/src/main/java/zim/tave/memory/service/JoinService.java index 9014d69..5b5abc8 100644 --- a/src/main/java/zim/tave/memory/service/JoinService.java +++ b/src/main/java/zim/tave/memory/service/JoinService.java @@ -12,6 +12,7 @@ import zim.tave.memory.repository.UserRepository; import java.time.LocalDate; +import java.time.ZoneOffset; @Service @RequiredArgsConstructor @@ -41,7 +42,8 @@ public User join(Long userId, JoinRequestDto requestDto) { user.setKoreanName(requestDto.getKoreanName()); user.setBirth(requestDto.getBirth()); user.setNationality(requestDto.getNationality()); - user.setCreatedAt(LocalDate.now()); + user.setCreatedAt(LocalDate.now(ZoneOffset.UTC)); + user.setStatus(true); user.setRegistered(true); if (user.getSetting() == null) { diff --git a/src/main/java/zim/tave/memory/service/LoginService.java b/src/main/java/zim/tave/memory/service/LoginService.java index 997f0b3..7fcb4a3 100644 --- a/src/main/java/zim/tave/memory/service/LoginService.java +++ b/src/main/java/zim/tave/memory/service/LoginService.java @@ -14,6 +14,7 @@ import zim.tave.memory.repository.UserRepository; import java.time.LocalDate; +import java.time.ZoneOffset; @Service @RequiredArgsConstructor @@ -42,7 +43,7 @@ public LoginResponseDto login(LoginRequestDto request) { User newUser = new User(); newUser.setKakaoId(kakaoUserInfo.getKakaoId()); newUser.setProfileImageUrl(kakaoUserInfo.getProfileImageUrl()); - newUser.setCreatedAt(LocalDate.now()); + newUser.setCreatedAt(LocalDate.now(ZoneOffset.UTC)); newUser.setStatus(true); newUser.setRegistered(false); newUser.setDiaryCount(0L); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bcd5767..f628e72 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,9 +3,14 @@ spring: import: optional:application-secret.yml datasource: - url: jdbc:mysql://localhost:3306/memory_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true + url: jdbc:mysql://localhost:3306/memory_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true username: memory_user driver-class-name: com.mysql.cj.jdbc.Driver + jackson: + time-zone: UTC + serialization: + write-dates-as-timestamps: false + default-property-inclusion: non_null servlet: multipart: max-file-size: 500MB @@ -19,6 +24,10 @@ spring: properties: hibernate: format_sql: true + jdbc: + time_zone: UTC + timezone: + default_storage: NORMALIZE_UTC connection: characterEncoding: utf8mb4 useUnicode: true diff --git a/src/test/java/zim/tave/memory/api/ApiTestBase.java b/src/test/java/zim/tave/memory/api/ApiTestBase.java index 6a5964b..8245761 100644 --- a/src/test/java/zim/tave/memory/api/ApiTestBase.java +++ b/src/test/java/zim/tave/memory/api/ApiTestBase.java @@ -13,7 +13,8 @@ import zim.tave.memory.repository.*; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; /** * API 통합 테스트 베이스 클래스 @@ -205,7 +206,7 @@ protected Trip createTestTrip(String tripName, String description, LocalDate sta /** * 테스트용 일기 생성 헬퍼 메서드 */ - protected Diary createTestDiary(Trip trip, String city, LocalDateTime dateTime, String content) { + protected Diary createTestDiary(Trip trip, String city, OffsetDateTime dateTime, String content) { Diary diary = Diary.createDiary(testUser, trip, koreaCountry, city, dateTime, content); diary.setOptionalFields(null, defaultEmotion, sunnyWeather); return diaryRepository.save(diary); diff --git a/src/test/java/zim/tave/memory/api/RecordingFlowApiTest.java b/src/test/java/zim/tave/memory/api/RecordingFlowApiTest.java index d45ad1a..d753ec4 100644 --- a/src/test/java/zim/tave/memory/api/RecordingFlowApiTest.java +++ b/src/test/java/zim/tave/memory/api/RecordingFlowApiTest.java @@ -57,7 +57,7 @@ void setUp() { diaryRequest.setTripId(tripId); diaryRequest.setCountryCode("KR"); diaryRequest.setCity("제주시"); - diaryRequest.setDateTime("2024-01-01T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest.setContent("제주도 첫째 날"); diaryRequest.setEmotionId(defaultEmotion.getId()); diaryRequest.setWeatherId(sunnyWeather.getId()); @@ -112,7 +112,7 @@ void setUp() { firstDiary.setTripId(tripId); firstDiary.setCountryCode("KR"); firstDiary.setCity("제주시"); - firstDiary.setDateTime("2024-01-01T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 firstDiary.setContent("첫째 날"); CreateDiaryRequest.DiaryImageInfo front1 = new CreateDiaryRequest.DiaryImageInfo(); @@ -138,7 +138,7 @@ void setUp() { secondDiary.setTripId(tripId); secondDiary.setCountryCode("KR"); secondDiary.setCity("서귀포시"); - secondDiary.setDateTime("2024-01-02T14:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 secondDiary.setContent("둘째 날"); CreateDiaryRequest.DiaryImageInfo front2 = new CreateDiaryRequest.DiaryImageInfo(); diff --git a/src/test/java/zim/tave/memory/controller/DiaryControllerTest.java b/src/test/java/zim/tave/memory/controller/DiaryControllerTest.java index 347dbae..bc47ce5 100644 --- a/src/test/java/zim/tave/memory/controller/DiaryControllerTest.java +++ b/src/test/java/zim/tave/memory/controller/DiaryControllerTest.java @@ -570,8 +570,8 @@ private void setSecurityContext(Long userId) { } @Test - void 일기_생성_dateTime_null_성공() throws Exception { - // given - dateTime은 선택 필드(자동 생성)이므로 null이어도 성공해야 함 + void 일기_생성_dateTime_자동생성_성공() throws Exception { + // given - dateTime은 서버에서 자동 생성되므로 요청에 포함하지 않음 Long userId = 1L; setSecurityContext(userId); @@ -579,7 +579,7 @@ private void setSecurityContext(Long userId) { request.setTripId(1L); request.setCountryCode("KR"); request.setCity("서울"); - request.setDateTime(null); // 선택 필드 null + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 request.setContent("테스트 내용"); // 필수 필드: images diff --git a/src/test/java/zim/tave/memory/domain/DiaryTest.java b/src/test/java/zim/tave/memory/domain/DiaryTest.java index b7c3cc7..42f4cfd 100644 --- a/src/test/java/zim/tave/memory/domain/DiaryTest.java +++ b/src/test/java/zim/tave/memory/domain/DiaryTest.java @@ -4,7 +4,8 @@ import org.junit.jupiter.api.BeforeEach; import static org.assertj.core.api.Assertions.*; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; class DiaryTest { @@ -29,7 +30,7 @@ void setUp() { void 다이어리_생성_테스트() { // given String city = "서울"; - LocalDateTime dateTime = LocalDateTime.of(2024, 1, 15, 10, 0); + OffsetDateTime dateTime = OffsetDateTime.of(2024, 1, 15, 10, 0, 0, 0, ZoneOffset.UTC); String content = "서울 여행 첫째 날"; // when @@ -49,7 +50,7 @@ void setUp() { void 선택적_필드_설정_테스트() { // given Diary diary = Diary.createDiary(user, trip, country, "서울", - LocalDateTime.now(), "테스트 내용"); + OffsetDateTime.now(ZoneOffset.UTC), "테스트 내용"); String detailedLocation = "강남역 1번 출구"; Emotion emotion = new Emotion("행복", "#FFD700"); @@ -69,7 +70,7 @@ void setUp() { void 이미지_추가_테스트() { // given Diary diary = Diary.createDiary(user, trip, country, "서울", - LocalDateTime.now(), "테스트 내용"); + OffsetDateTime.now(ZoneOffset.UTC), "테스트 내용"); DiaryImage image1 = new DiaryImage(); image1.setImageUrl("image1.jpg"); image1.setCameraType(DiaryImage.CameraType.FRONT); diff --git a/src/test/java/zim/tave/memory/domain/TripTest.java b/src/test/java/zim/tave/memory/domain/TripTest.java index 27133ba..0a5f1c1 100644 --- a/src/test/java/zim/tave/memory/domain/TripTest.java +++ b/src/test/java/zim/tave/memory/domain/TripTest.java @@ -5,7 +5,8 @@ import static org.assertj.core.api.Assertions.*; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; class TripTest { @@ -36,7 +37,7 @@ void setUp() { assertThat(trip.getDescription()).isEqualTo(description); assertThat(trip.getUser()).isEqualTo(user); assertThat(trip.getTripTheme()).isEqualTo(tripTheme); - assertThat(trip.getStartDate()).isEqualTo(LocalDate.now()); + assertThat(trip.getStartDate()).isEqualTo(LocalDate.now(ZoneOffset.UTC)); } @Test @@ -44,7 +45,7 @@ void setUp() { // given Trip trip = Trip.createTrip(user, "제주도 여행", "제주도 여행", tripTheme); Diary diary = new Diary(); - diary.setCreatedAt(LocalDateTime.of(2024, 1, 15, 10, 0)); + diary.setCreatedAt(OffsetDateTime.of(2024, 1, 15, 10, 0, 0, 0, ZoneOffset.UTC)); // when trip.addDiary(diary); diff --git a/src/test/java/zim/tave/memory/integration/AbstractContainerBaseTest.java b/src/test/java/zim/tave/memory/integration/AbstractContainerBaseTest.java index 0c6f18a..280016a 100644 --- a/src/test/java/zim/tave/memory/integration/AbstractContainerBaseTest.java +++ b/src/test/java/zim/tave/memory/integration/AbstractContainerBaseTest.java @@ -95,7 +95,8 @@ static void configureProperties(DynamicPropertyRegistry registry) { // Spring Boot의 데이터소스 속성 설정 // Supplier를 사용하여 런타임에 값을 가져옵니다 - registry.add("spring.datasource.url", () -> jdbcUrl); + // UTC 타임존 설정 추가 + registry.add("spring.datasource.url", () -> jdbcUrl + (jdbcUrl.contains("?") ? "&serverTimezone=UTC" : "?serverTimezone=UTC")); registry.add("spring.datasource.username", mysql::getUsername); registry.add("spring.datasource.password", mysql::getPassword); registry.add("spring.datasource.driver-class-name", () -> "com.mysql.cj.jdbc.Driver"); diff --git a/src/test/java/zim/tave/memory/integration/IntegrationTestBase.java b/src/test/java/zim/tave/memory/integration/IntegrationTestBase.java index e71e3c9..6b8ee9e 100644 --- a/src/test/java/zim/tave/memory/integration/IntegrationTestBase.java +++ b/src/test/java/zim/tave/memory/integration/IntegrationTestBase.java @@ -7,7 +7,8 @@ import zim.tave.memory.repository.*; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; /** * 통합 테스트 베이스 클래스 @@ -169,7 +170,7 @@ protected Trip createTestTrip(String tripName, String description, LocalDate sta /** * 테스트용 일기 생성 헬퍼 메서드 */ - protected Diary createTestDiary(Trip trip, String city, LocalDateTime dateTime, String content) { + protected Diary createTestDiary(Trip trip, String city, OffsetDateTime dateTime, String content) { Diary diary = Diary.createDiary(testUser, trip, koreaCountry, city, dateTime, content); diary.setOptionalFields(null, defaultEmotion, sunnyWeather); return diaryRepository.save(diary); diff --git a/src/test/java/zim/tave/memory/integration/MyPageIntegrationTest.java b/src/test/java/zim/tave/memory/integration/MyPageIntegrationTest.java index f5b653d..2a42958 100644 --- a/src/test/java/zim/tave/memory/integration/MyPageIntegrationTest.java +++ b/src/test/java/zim/tave/memory/integration/MyPageIntegrationTest.java @@ -12,7 +12,8 @@ import zim.tave.memory.repository.VisitedCountryRepository; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; @@ -72,14 +73,14 @@ void setUp() { Diary diary1 = createTestDiary( trip, "제주시", - LocalDateTime.of(2024, 1, 1, 10, 0), + OffsetDateTime.of(2024, 1, 1, 10, 0, 0, 0, ZoneOffset.UTC), "첫 번째 일기" ); Diary diary2 = createTestDiary( trip, "서귀포시", - LocalDateTime.of(2024, 1, 2, 14, 0), + OffsetDateTime.of(2024, 1, 2, 14, 0, 0, 0, ZoneOffset.UTC), "두 번째 일기" ); @@ -109,8 +110,8 @@ void setUp() { ); // 같은 국가(한국)로 일기 생성 - createTestDiary(trip1, "제주시", LocalDateTime.now(), "일기 1"); - createTestDiary(trip2, "서귀포시", LocalDateTime.now(), "일기 2"); + createTestDiary(trip1, "제주시", OffsetDateTime.now(ZoneOffset.UTC), "일기 1"); + createTestDiary(trip2, "서귀포시", OffsetDateTime.now(ZoneOffset.UTC), "일기 2"); // 다른 국가 추가 Country japanCountry = new Country("JP", "일본", "🇯🇵"); diff --git a/src/test/java/zim/tave/memory/integration/RecordingFlowIntegrationTest.java b/src/test/java/zim/tave/memory/integration/RecordingFlowIntegrationTest.java index 8df19f5..a24cc00 100644 --- a/src/test/java/zim/tave/memory/integration/RecordingFlowIntegrationTest.java +++ b/src/test/java/zim/tave/memory/integration/RecordingFlowIntegrationTest.java @@ -55,7 +55,7 @@ void setUp() { diaryRequest.setTripId(createdTrip.getId()); diaryRequest.setCountryCode("KR"); diaryRequest.setCity("제주시"); - diaryRequest.setDateTime("2024-01-01T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest.setContent("제주도 첫째 날 - 한라산 등반"); diaryRequest.setDetailedLocation("한라산 정상"); diaryRequest.setEmotionId(defaultEmotion.getId()); @@ -115,7 +115,7 @@ void setUp() { firstDiaryRequest.setTripId(existingTrip.getId()); firstDiaryRequest.setCountryCode("KR"); firstDiaryRequest.setCity("제주시"); - firstDiaryRequest.setDateTime("2024-01-01T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 firstDiaryRequest.setContent("첫째 날 일기"); firstDiaryRequest.setEmotionId(defaultEmotion.getId()); firstDiaryRequest.setWeatherId(sunnyWeather.getId()); @@ -139,7 +139,7 @@ void setUp() { secondDiaryRequest.setTripId(existingTrip.getId()); secondDiaryRequest.setCountryCode("KR"); secondDiaryRequest.setCity("서귀포시"); - secondDiaryRequest.setDateTime("2024-01-02T14:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 secondDiaryRequest.setContent("둘째 날 일기"); secondDiaryRequest.setEmotionId(defaultEmotion.getId()); secondDiaryRequest.setWeatherId(sunnyWeather.getId()); @@ -187,7 +187,7 @@ void setUp() { diaryRequest.setTripId(trip.getId()); diaryRequest.setCountryCode("KR"); diaryRequest.setCity("제주시"); - diaryRequest.setDateTime("2024-01-01T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest.setContent("대표사진 테스트"); diaryRequest.setEmotionId(defaultEmotion.getId()); @@ -231,7 +231,7 @@ void setUp() { diaryRequest.setTripId(trip.getId()); diaryRequest.setCountryCode("KR"); diaryRequest.setCity("제주시"); - diaryRequest.setDateTime("2024-01-01T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest.setContent("감정색 기본값 테스트"); // emotionId를 설정하지 않음 (기본값 사용) @@ -271,7 +271,7 @@ void setUp() { diaryRequest.setTripId(trip.getId()); diaryRequest.setCountryCode("KR"); diaryRequest.setCity("제주시"); - diaryRequest.setDateTime("2024-01-01T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest.setContent("날씨 선택 안함 테스트"); diaryRequest.setEmotionId(defaultEmotion.getId()); // weatherId를 설정하지 않음 diff --git a/src/test/java/zim/tave/memory/integration/SettingsIntegrationTest.java b/src/test/java/zim/tave/memory/integration/SettingsIntegrationTest.java index 1a0d1c8..0ac7204 100644 --- a/src/test/java/zim/tave/memory/integration/SettingsIntegrationTest.java +++ b/src/test/java/zim/tave/memory/integration/SettingsIntegrationTest.java @@ -13,7 +13,8 @@ import zim.tave.memory.service.TripService; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.Arrays; import java.util.List; @@ -61,7 +62,7 @@ void setUp() { testDiary = createTestDiary( testTrip, "제주시", - LocalDateTime.of(2024, 1, 1, 10, 0), + OffsetDateTime.of(2024, 1, 1, 10, 0, 0, 0, ZoneOffset.UTC), "일기 내용" ); } diff --git a/src/test/java/zim/tave/memory/integration/TripDiaryIntegrationTest.java b/src/test/java/zim/tave/memory/integration/TripDiaryIntegrationTest.java index a7f4f8c..b78b30b 100644 --- a/src/test/java/zim/tave/memory/integration/TripDiaryIntegrationTest.java +++ b/src/test/java/zim/tave/memory/integration/TripDiaryIntegrationTest.java @@ -26,7 +26,8 @@ import zim.tave.memory.service.VisitedCountryService; import zim.tave.memory.dto.response.TripResponseDto; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.time.LocalDate; import java.util.Arrays; import java.util.List; @@ -114,7 +115,7 @@ void setUp() { diaryRequest.setTripId(1L); diaryRequest.setCountryCode("KR"); diaryRequest.setCity("제주시"); - diaryRequest.setDateTime("2024-01-15T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest.setContent("제주도 첫째 날"); CreateDiaryRequest.DiaryImageInfo imageInfo1 = new CreateDiaryRequest.DiaryImageInfo(); @@ -145,7 +146,8 @@ void setUp() { // then assertThat(createdDiary.getTripId()).isEqualTo(trip.getId()); - assertThat(trip.getEndDate()).isEqualTo(LocalDateTime.parse(diaryRequest.getDateTime()).toLocalDate()); + // dateTime은 서버에서 자동 생성되므로 createdAt을 기준으로 검증 + assertThat(trip.getEndDate()).isNotNull(); } @Test @@ -160,7 +162,7 @@ void setUp() { diaryRequest1.setTripId(1L); diaryRequest1.setCountryCode("KR"); diaryRequest1.setCity("제주시"); - diaryRequest1.setDateTime("2024-01-15T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest1.setContent("제주도 첫째 날"); diaryRequest1.setImages(createImageInfo("front1.jpg", "back1.jpg")); @@ -175,7 +177,7 @@ void setUp() { diaryRequest2.setTripId(1L); diaryRequest2.setCountryCode("KR"); diaryRequest2.setCity("서귀포시"); - diaryRequest2.setDateTime("2024-01-20T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest2.setContent("제주도 마지막 날"); diaryRequest2.setImages(createImageInfo("front2.jpg", "back2.jpg")); @@ -186,13 +188,13 @@ void setUp() { persisted2.setId(2L); persisted2.setTrip(trip); persisted2.setUser(user); - persisted2.setCreatedAt(LocalDateTime.of(2024, 1, 20, 10, 0)); + persisted2.setCreatedAt(OffsetDateTime.of(2024, 1, 20, 10, 0, 0, 0, ZoneOffset.UTC)); when(diaryRepository.findById(2L)).thenReturn(Optional.of(persisted2)); Diary persisted1 = new Diary(); persisted1.setId(1L); persisted1.setTrip(trip); persisted1.setUser(user); - persisted1.setCreatedAt(LocalDateTime.of(2024, 1, 15, 10, 0)); + persisted1.setCreatedAt(OffsetDateTime.of(2024, 1, 15, 10, 0, 0, 0, ZoneOffset.UTC)); when(diaryRepository.findByTrip_Id(1L)).thenReturn(Arrays.asList(persisted1)); diaryService.deleteDiary(2L, 1L); @@ -212,7 +214,7 @@ void setUp() { diaryRequest.setTripId(1L); diaryRequest.setCountryCode("KR"); diaryRequest.setCity("제주시"); - diaryRequest.setDateTime("2024-01-15T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest.setContent("제주도 여행"); diaryRequest.setImages(createImageInfo("front.jpg", "back.jpg")); @@ -226,7 +228,7 @@ void setUp() { persisted.setId(1L); persisted.setTrip(trip); persisted.setUser(user); - persisted.setCreatedAt(LocalDateTime.of(2024, 1, 15, 10, 0)); + persisted.setCreatedAt(OffsetDateTime.of(2024, 1, 15, 10, 0, 0, 0, ZoneOffset.UTC)); when(diaryRepository.findById(1L)).thenReturn(Optional.of(persisted)); when(diaryRepository.findByTrip_Id(1L)).thenReturn(Arrays.asList()); @@ -248,7 +250,7 @@ void setUp() { diaryRequest1.setTripId(1L); diaryRequest1.setCountryCode("KR"); diaryRequest1.setCity("제주시"); - diaryRequest1.setDateTime("2024-01-15T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest1.setContent("제주도 첫째 날"); diaryRequest1.setImages(createImageInfo("front1.jpg", "back1.jpg")); @@ -264,7 +266,7 @@ void setUp() { diaryRequest2.setTripId(1L); diaryRequest2.setCountryCode("KR"); diaryRequest2.setCity("서귀포시"); - diaryRequest2.setDateTime("2024-01-20T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest2.setContent("제주도 마지막 날"); diaryRequest2.setImages(createImageInfo("front2.jpg", "back2.jpg")); @@ -275,13 +277,13 @@ void setUp() { persisted2.setId(2L); persisted2.setTrip(trip); persisted2.setUser(user); - persisted2.setCreatedAt(LocalDateTime.of(2024, 1, 20, 10, 0)); + persisted2.setCreatedAt(OffsetDateTime.of(2024, 1, 20, 10, 0, 0, 0, ZoneOffset.UTC)); when(diaryRepository.findById(2L)).thenReturn(Optional.of(persisted2)); Diary persisted1 = new Diary(); persisted1.setId(1L); persisted1.setTrip(trip); persisted1.setUser(user); - persisted1.setCreatedAt(LocalDateTime.of(2024, 1, 15, 10, 0)); + persisted1.setCreatedAt(OffsetDateTime.of(2024, 1, 15, 10, 0, 0, 0, ZoneOffset.UTC)); when(diaryRepository.findByTrip_Id(1L)).thenReturn(Arrays.asList(persisted1)); diaryService.deleteDiary(2L, 1L); @@ -302,7 +304,7 @@ void setUp() { diaryRequest.setTripId(1L); diaryRequest.setCountryCode("KR"); diaryRequest.setCity("제주시"); - diaryRequest.setDateTime("2024-01-15T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 diaryRequest.setContent("제주도 여행"); diaryRequest.setImages(createImageInfo("front.jpg", "back.jpg")); @@ -316,7 +318,7 @@ void setUp() { persisted.setId(1L); persisted.setTrip(trip); persisted.setUser(user); - persisted.setCreatedAt(LocalDateTime.of(2024, 1, 15, 10, 0)); + persisted.setCreatedAt(OffsetDateTime.of(2024, 1, 15, 10, 0, 0, 0, ZoneOffset.UTC)); when(diaryRepository.findById(1L)).thenReturn(Optional.of(persisted)); when(diaryRepository.findByTrip_Id(1L)).thenReturn(Arrays.asList()); @@ -452,8 +454,8 @@ private void mockDiarySaveWithAutoId(long startingId) { if (diary.getId() == null) { diary.setId(idGenerator.getAndIncrement()); } - if (diary.getCreatedAt() == null && diary.getDateTime() != null) { - diary.setCreatedAt(diary.getDateTime()); + if (diary.getCreatedAt() == null) { + diary.setCreatedAt(OffsetDateTime.now(ZoneOffset.UTC)); } return diary; }); diff --git a/src/test/java/zim/tave/memory/integration/ViewRecordsFlowIntegrationTest.java b/src/test/java/zim/tave/memory/integration/ViewRecordsFlowIntegrationTest.java index 9025a65..225e3f3 100644 --- a/src/test/java/zim/tave/memory/integration/ViewRecordsFlowIntegrationTest.java +++ b/src/test/java/zim/tave/memory/integration/ViewRecordsFlowIntegrationTest.java @@ -10,7 +10,8 @@ import zim.tave.memory.service.TripService; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -48,7 +49,7 @@ void setUp() { testDiary = createTestDiary( testTrip, "제주시", - LocalDateTime.of(2024, 1, 1, 10, 0), + OffsetDateTime.of(2024, 1, 1, 10, 0, 0, 0, ZoneOffset.UTC), "제주도 첫째 날" ); diff --git a/src/test/java/zim/tave/memory/service/DiaryServiceTest.java b/src/test/java/zim/tave/memory/service/DiaryServiceTest.java index 837cdb7..05b5dc1 100644 --- a/src/test/java/zim/tave/memory/service/DiaryServiceTest.java +++ b/src/test/java/zim/tave/memory/service/DiaryServiceTest.java @@ -21,15 +21,12 @@ import zim.tave.memory.repository.UserRepository; import zim.tave.memory.repository.EmotionRepository; import zim.tave.memory.repository.WeatherRepository; -import java.time.LocalDateTime; import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.lang.reflect.Method; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; @ExtendWith(MockitoExtension.class) class DiaryServiceTest { @@ -81,7 +78,7 @@ void setUp() { diary.setTrip(trip); diary.setCity("서울"); diary.setContent("테스트 내용"); - diary.setCreatedAt(LocalDateTime.of(2024, 1, 15, 10, 0)); + diary.setCreatedAt(OffsetDateTime.of(2024, 1, 15, 10, 0, 0, 0, ZoneOffset.UTC)); diary.setIsStored(false); emotion = new Emotion("행복", "#FFD700"); @@ -100,7 +97,7 @@ void setUp() { request.setTripId(1L); request.setCountryCode("KR"); request.setCity("서울"); - request.setDateTime("2024-01-15T10:00:00"); + // dateTime 필드 제거 - 서버에서 UTC 기준으로 자동 생성됨 request.setContent("테스트 내용"); CreateDiaryRequest.DiaryImageInfo imageInfo1 = new CreateDiaryRequest.DiaryImageInfo(); @@ -231,7 +228,7 @@ void setUp() { void 다이어리_삭제_후_남은_다이어리_있을_때_종료날짜_업데이트_테스트() { // given Diary remainingDiary = new Diary(); - remainingDiary.setCreatedAt(LocalDateTime.of(2024, 1, 20, 10, 0)); + remainingDiary.setCreatedAt(OffsetDateTime.of(2024, 1, 20, 10, 0, 0, 0, ZoneOffset.UTC)); when(diaryRepository.findById(1L)).thenReturn(Optional.of(diary)); when(tripRepository.findById(1L)).thenReturn(Optional.of(trip)); @@ -409,241 +406,7 @@ void setUp() { } @Test - void 날짜시간_파싱_Z로_끝나는_ISO8601_포맷_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - Z로 끝나는 포맷 - LocalDateTime result1 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.570Z"); - assertThat(result1).isNotNull(); - - LocalDateTime result2 = (LocalDateTime) parseDateTime.invoke(diaryService, "2024-01-15T10:00:00.000Z"); - assertThat(result2).isNotNull(); - assertThat(result2.getYear()).isEqualTo(2024); - assertThat(result2.getMonthValue()).isEqualTo(1); - assertThat(result2.getDayOfMonth()).isEqualTo(15); - } - - @Test - void 날짜시간_파싱_Offset_포함_포맷_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - +09:00 (한국 시간) - LocalDateTime result1 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01+09:00"); - assertThat(result1).isNotNull(); - - // when & then - -05:00 (미국 동부 시간) - LocalDateTime result2 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01-05:00"); - assertThat(result2).isNotNull(); - - // when & then - +00:00 (UTC) - LocalDateTime result3 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01+00:00"); - assertThat(result3).isNotNull(); - - // when & then - 밀리초와 offset 함께 - LocalDateTime result4 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.123+09:00"); - assertThat(result4).isNotNull(); - } - - @Test - void 날짜시간_파싱_밀리초_포함_포맷_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - 3자리 밀리초 - LocalDateTime result1 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.123"); - assertThat(result1).isNotNull(); - assertThat(result1.getNano()).isEqualTo(123000000); - - // when & then - 1자리 밀리초 - LocalDateTime result2 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.1"); - assertThat(result2).isNotNull(); - - // when & then - 2자리 밀리초 - LocalDateTime result3 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.12"); - assertThat(result3).isNotNull(); - - // when & then - 6자리 밀리초 (나노초까지) - LocalDateTime result4 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.123456"); - assertThat(result4).isNotNull(); - } - - @Test - void 날짜시간_파싱_기본_형식_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - 기본 형식 (밀리초 없음) - LocalDateTime result = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01"); - assertThat(result).isNotNull(); - assertThat(result.getYear()).isEqualTo(2025); - assertThat(result.getMonthValue()).isEqualTo(11); - assertThat(result.getDayOfMonth()).isEqualTo(30); - assertThat(result.getHour()).isEqualTo(11); - assertThat(result.getMinute()).isEqualTo(20); - assertThat(result.getSecond()).isEqualTo(1); - } - - @Test - void 날짜시간_파싱_공백_제거_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - 앞뒤 공백 제거 - LocalDateTime result1 = (LocalDateTime) parseDateTime.invoke(diaryService, " 2025-11-30T11:20:01 "); - assertThat(result1).isNotNull(); - - LocalDateTime result2 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01"); - assertThat(result1).isEqualTo(result2); - } - - @Test - void 날짜시간_파싱_null_또는_빈문자열_예외_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - null - assertThatThrownBy(() -> parseDateTime.invoke(diaryService, (String) null)) - .hasCauseInstanceOf(CustomException.class); - - // when & then - 빈 문자열 - assertThatThrownBy(() -> parseDateTime.invoke(diaryService, "")) - .hasCauseInstanceOf(CustomException.class); - - // when & then - 공백만 있는 문자열 - assertThatThrownBy(() -> parseDateTime.invoke(diaryService, " ")) - .hasCauseInstanceOf(CustomException.class); - } - - @Test - void 날짜시간_파싱_잘못된_형식_예외_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - 잘못된 형식들 - assertThatThrownBy(() -> parseDateTime.invoke(diaryService, "2025-11-30")) - .hasCauseInstanceOf(CustomException.class); - - assertThatThrownBy(() -> parseDateTime.invoke(diaryService, "2025/11/30 11:20:01")) - .hasCauseInstanceOf(CustomException.class); - - assertThatThrownBy(() -> parseDateTime.invoke(diaryService, "invalid-date")) - .hasCauseInstanceOf(CustomException.class); - - assertThatThrownBy(() -> parseDateTime.invoke(diaryService, "2025-13-45T25:70:99")) - .hasCauseInstanceOf(CustomException.class); - } - - @Test - void 날짜시간_파싱_다양한_실제_사용_시나리오_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - JavaScript Date.toISOString() 결과 (Z 포함) - LocalDateTime result1 = (LocalDateTime) parseDateTime.invoke(diaryService, "2024-12-25T15:30:45.789Z"); - assertThat(result1).isNotNull(); - - // when & then - 모바일 앱에서 보낼 수 있는 offset 형식 - LocalDateTime result2 = (LocalDateTime) parseDateTime.invoke(diaryService, "2024-12-25T15:30:45+09:00"); - assertThat(result2).isNotNull(); - - // when & then - 서버에서 생성한 로컬 시간 - LocalDateTime result3 = (LocalDateTime) parseDateTime.invoke(diaryService, "2024-12-25T15:30:45"); - assertThat(result3).isNotNull(); - - // when & then - 밀리초 포함 로컬 시간 - LocalDateTime result4 = (LocalDateTime) parseDateTime.invoke(diaryService, "2024-12-25T15:30:45.123"); - assertThat(result4).isNotNull(); - } - - @Test - void 날짜시간_파싱_Edge_Case_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - 자정 - LocalDateTime result1 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-01-01T00:00:00"); - assertThat(result1).isNotNull(); - assertThat(result1.getHour()).isZero(); - assertThat(result1.getMinute()).isZero(); - assertThat(result1.getSecond()).isZero(); - - // when & then - 23:59:59 - LocalDateTime result2 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-12-31T23:59:59"); - assertThat(result2).isNotNull(); - assertThat(result2.getHour()).isEqualTo(23); - assertThat(result2.getMinute()).isEqualTo(59); - assertThat(result2.getSecond()).isEqualTo(59); - - // when & then - 윤년 2월 29일 - LocalDateTime result3 = (LocalDateTime) parseDateTime.invoke(diaryService, "2024-02-29T12:00:00"); - assertThat(result3).isNotNull(); - assertThat(result3.getMonthValue()).isEqualTo(2); - assertThat(result3.getDayOfMonth()).isEqualTo(29); - } - - @Test - void 날짜시간_파싱_다양한_밀리초_자릿수_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - 1자리 밀리초 - LocalDateTime result1 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.1"); - assertThat(result1).isNotNull(); - - // when & then - 2자리 밀리초 - LocalDateTime result2 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.12"); - assertThat(result2).isNotNull(); - - // when & then - 3자리 밀리초 - LocalDateTime result3 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.123"); - assertThat(result3).isNotNull(); - - // when & then - 4자리 밀리초 - LocalDateTime result4 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.1234"); - assertThat(result4).isNotNull(); - - // when & then - 5자리 밀리초 - LocalDateTime result5 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.12345"); - assertThat(result5).isNotNull(); - - // when & then - 6자리 밀리초 - LocalDateTime result6 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.123456"); - assertThat(result6).isNotNull(); - } - - @Test - void 날짜시간_파싱_Offset과_밀리초_조합_테스트() throws Exception { - // given - Method parseDateTime = DiaryService.class.getDeclaredMethod("parseDateTime", String.class); - parseDateTime.setAccessible(true); - - // when & then - Z와 밀리초 - LocalDateTime result1 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.123Z"); - assertThat(result1).isNotNull(); - - // when & then - +09:00와 밀리초 - LocalDateTime result2 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.456+09:00"); - assertThat(result2).isNotNull(); - - // when & then - -05:00와 밀리초 - LocalDateTime result3 = (LocalDateTime) parseDateTime.invoke(diaryService, "2025-11-30T11:20:01.789-05:00"); - assertThat(result3).isNotNull(); - } - - @Test - void 날짜시간_파싱_실제_다이어리_생성_통합_테스트() { + void 다이어리_생성_UTC_자동생성_테스트() { // given CreateDiaryRequest request = new CreateDiaryRequest(); request.setUserId(1L); @@ -679,28 +442,14 @@ void setUp() { return saved; }); - // when & then - Z 포맷으로 다이어리 생성 - request.setDateTime("2025-11-30T11:20:01.570Z"); - DiaryResponseDto result1 = diaryService.createDiary(request); - assertThat(result1).isNotNull(); - assertThat(result1.getDateTime()).isNotNull(); - - // when & then - Offset 포맷으로 다이어리 생성 - request.setDateTime("2025-11-30T11:20:01+09:00"); - DiaryResponseDto result2 = diaryService.createDiary(request); - assertThat(result2).isNotNull(); - assertThat(result2.getDateTime()).isNotNull(); - - // when & then - 밀리초 포함 포맷으로 다이어리 생성 - request.setDateTime("2025-11-30T11:20:01.123"); - DiaryResponseDto result3 = diaryService.createDiary(request); - assertThat(result3).isNotNull(); - assertThat(result3.getDateTime()).isNotNull(); + // when - dateTime 필드 없이 다이어리 생성 (서버에서 UTC 기준으로 자동 생성) + DiaryResponseDto result = diaryService.createDiary(request); - // when & then - 기본 포맷으로 다이어리 생성 - request.setDateTime("2025-11-30T11:20:01"); - DiaryResponseDto result4 = diaryService.createDiary(request); - assertThat(result4).isNotNull(); - assertThat(result4.getDateTime()).isNotNull(); + // then - 서버에서 UTC 기준으로 자동 생성된 시간 확인 + assertThat(result).isNotNull(); + assertThat(result.getDateTime()).isNotNull(); + assertThat(result.getDateTime().getOffset()).isEqualTo(ZoneOffset.UTC); + assertThat(result.getCreatedAt()).isNotNull(); + assertThat(result.getCreatedAt().getOffset()).isEqualTo(ZoneOffset.UTC); } } diff --git a/src/test/java/zim/tave/memory/service/TimelineServiceTest.java b/src/test/java/zim/tave/memory/service/TimelineServiceTest.java index 7809662..f9c8d8b 100644 --- a/src/test/java/zim/tave/memory/service/TimelineServiceTest.java +++ b/src/test/java/zim/tave/memory/service/TimelineServiceTest.java @@ -20,7 +20,8 @@ import zim.tave.memory.repository.TripRepository; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -92,10 +93,10 @@ void setUp() { activeDiary.setTrip(activeTrip); activeDiary.setCountry(country); activeDiary.setCity("제주시"); - activeDiary.setDateTime(LocalDateTime.of(2024, 1, 2, 10, 0)); + activeDiary.setDateTime(OffsetDateTime.of(2024, 1, 2, 10, 0, 0, 0, ZoneOffset.UTC)); activeDiary.setContent("제주도 일기 내용"); activeDiary.setIsStored(false); - activeDiary.setCreatedAt(LocalDateTime.of(2024, 1, 2, 10, 0)); + activeDiary.setCreatedAt(OffsetDateTime.of(2024, 1, 2, 10, 0, 0, 0, ZoneOffset.UTC)); activeDiary.setEmotion(emotion); activeDiary.setDiaryImages(new ArrayList<>()); @@ -106,10 +107,10 @@ void setUp() { storedDiary.setTrip(activeTrip); storedDiary.setCountry(country); storedDiary.setCity("서귀포시"); - storedDiary.setDateTime(LocalDateTime.of(2024, 1, 3, 10, 0)); + storedDiary.setDateTime(OffsetDateTime.of(2024, 1, 3, 10, 0, 0, 0, ZoneOffset.UTC)); storedDiary.setContent("보관된 일기 내용"); storedDiary.setIsStored(true); - storedDiary.setCreatedAt(LocalDateTime.of(2024, 1, 3, 10, 0)); + storedDiary.setCreatedAt(OffsetDateTime.of(2024, 1, 3, 10, 0, 0, 0, ZoneOffset.UTC)); storedDiary.setDiaryImages(new ArrayList<>()); } @@ -231,10 +232,10 @@ void setUp() { diary2.setTrip(activeTrip); diary2.setCountry(country2); diary2.setCity("뉴욕"); - diary2.setDateTime(LocalDateTime.of(2024, 1, 4, 10, 0)); + diary2.setDateTime(OffsetDateTime.of(2024, 1, 4, 10, 0, 0, 0, ZoneOffset.UTC)); diary2.setContent("미국 일기"); diary2.setIsStored(false); - diary2.setCreatedAt(LocalDateTime.of(2024, 1, 4, 10, 0)); + diary2.setCreatedAt(OffsetDateTime.of(2024, 1, 4, 10, 0, 0, 0, ZoneOffset.UTC)); diary2.setDiaryImages(new ArrayList<>()); activeTrip.setDiaries(Arrays.asList(activeDiary, diary2)); @@ -352,10 +353,10 @@ void setUp() { diary1.setTrip(activeTrip); diary1.setCountry(country); diary1.setCity("제주시"); - diary1.setDateTime(LocalDateTime.of(2024, 1, 2, 10, 0)); + diary1.setDateTime(OffsetDateTime.of(2024, 1, 2, 10, 0, 0, 0, ZoneOffset.UTC)); diary1.setContent("첫 번째 일기"); diary1.setIsStored(false); - diary1.setCreatedAt(LocalDateTime.of(2024, 1, 2, 10, 0)); + diary1.setCreatedAt(OffsetDateTime.of(2024, 1, 2, 10, 0, 0, 0, ZoneOffset.UTC)); diary1.setEmotion(emotion); // 설렘 diary1.setDiaryImages(new ArrayList<>()); @@ -365,10 +366,10 @@ void setUp() { diary2.setTrip(activeTrip); diary2.setCountry(country); diary2.setCity("서귀포시"); - diary2.setDateTime(LocalDateTime.of(2024, 1, 3, 10, 0)); + diary2.setDateTime(OffsetDateTime.of(2024, 1, 3, 10, 0, 0, 0, ZoneOffset.UTC)); diary2.setContent("두 번째 일기"); diary2.setIsStored(false); - diary2.setCreatedAt(LocalDateTime.of(2024, 1, 4, 10, 0)); // 더 최근 + diary2.setCreatedAt(OffsetDateTime.of(2024, 1, 4, 10, 0, 0, 0, ZoneOffset.UTC)); // 더 최근 diary2.setEmotion(emotion2); // 행복 diary2.setDiaryImages(new ArrayList<>()); @@ -403,10 +404,10 @@ void setUp() { diaryWithoutEmotion.setTrip(activeTrip); diaryWithoutEmotion.setCountry(country); diaryWithoutEmotion.setCity("제주시"); - diaryWithoutEmotion.setDateTime(LocalDateTime.of(2024, 1, 2, 10, 0)); + diaryWithoutEmotion.setDateTime(OffsetDateTime.of(2024, 1, 2, 10, 0, 0, 0, ZoneOffset.UTC)); diaryWithoutEmotion.setContent("감정 없는 일기"); diaryWithoutEmotion.setIsStored(false); - diaryWithoutEmotion.setCreatedAt(LocalDateTime.of(2024, 1, 2, 10, 0)); + diaryWithoutEmotion.setCreatedAt(OffsetDateTime.of(2024, 1, 2, 10, 0, 0, 0, ZoneOffset.UTC)); diaryWithoutEmotion.setEmotion(null); diaryWithoutEmotion.setDiaryImages(new ArrayList<>());