From f4a8bf36b0847543ccfc69b99fee30deedd35ffc Mon Sep 17 00:00:00 2001 From: pparkjs Date: Sun, 1 Mar 2026 17:41:02 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=EA=B8=B0=ED=9A=8D=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=97=AC=ED=96=89=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=20=EC=88=98=EC=A0=95=20-=20=EC=86=8C=EA=B0=9C?= =?UTF-8?q?=EA=B8=80=2070=EC=9E=90=EB=A1=9C=20Max=20=EB=B3=80=EA=B2=BD=20-?= =?UTF-8?q?=20=EC=97=AC=ED=96=89=20=EC=83=9D=EC=84=B1=20=EC=8B=9C=20HashTa?= =?UTF-8?q?g=20=EA=B0=92=20=EC=88=98=EC=A0=95=20-=20=EC=97=AC=ED=96=89?= =?UTF-8?q?=EC=A7=80=20=EB=8B=A4=EC=88=98=20=EB=93=B1=EB=A1=9D=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/request/TripCreateRequest.java | 33 +++++++++++---- .../in/response/TripCreateResponse.java | 23 +++++++--- .../in/response/TripDetailResponse.java | 32 ++++++++------ .../application/in/response/TripResponse.java | 31 +++++++++----- .../application/in/service/ImageService.java | 2 +- .../com/retrip/trip/domain/entity/Trip.java | 16 +++---- .../trip/domain/entity/TripDestination.java | 42 +++++++++++++++++++ .../trip/domain/entity/TripDestinations.java | 33 +++++++++++++++ .../trip/domain/entity/TripHashTag.java | 7 +++- .../trip/domain/entity/TripHashTags.java | 17 +++++--- .../retrip/trip/domain/vo/HashTagInfo.java | 7 ++++ .../trip/domain/vo/TripDescription.java | 2 +- .../retrip/trip/infra/config/S3Config.java | 1 + src/main/resources/application.yml | 4 +- 14 files changed, 196 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/retrip/trip/domain/entity/TripDestination.java create mode 100644 src/main/java/com/retrip/trip/domain/entity/TripDestinations.java create mode 100644 src/main/java/com/retrip/trip/domain/vo/HashTagInfo.java diff --git a/src/main/java/com/retrip/trip/application/in/request/TripCreateRequest.java b/src/main/java/com/retrip/trip/application/in/request/TripCreateRequest.java index a705aae..c4818cf 100644 --- a/src/main/java/com/retrip/trip/application/in/request/TripCreateRequest.java +++ b/src/main/java/com/retrip/trip/application/in/request/TripCreateRequest.java @@ -1,6 +1,7 @@ package com.retrip.trip.application.in.request; import com.retrip.trip.domain.entity.Trip; +import com.retrip.trip.domain.vo.HashTagInfo; import com.retrip.trip.domain.vo.TripCategory; import com.retrip.trip.domain.vo.TripDescription; import com.retrip.trip.domain.vo.TripPeriod; @@ -17,9 +18,9 @@ @Schema(description = "여행 생성 Request") public record TripCreateRequest( - @Schema(description = "여행 위치 ID", example = "550e8400-e29b-41d4-a716-446655440001") + @Schema(description = "여행 위치 ID 목록") @NotNull - UUID locationId, + List locationIds, @Schema(description = "여행 제목", example = "유럽 배낭여행") @NotNull @@ -48,24 +49,40 @@ public record TripCreateRequest( @Schema(description = "여행 최대 참가 인원") int maxParticipants, - @Schema(description = "HashTag") - List hashTags, + @Schema(description = "HashTag 목록") + List hashTags, @Schema(description = "여행 카테고리") TripCategory category ) { + @Schema(description = "해시태그 입력") + public record HashTagInput( + @Schema(description = "해시태그 값", example = "10대") + String tag, + + @Schema(description = "정렬 순서", example = "1") + int order + ) {} + + private List toHashTagInfos() { + if (hashTags == null) return List.of(); + return hashTags.stream() + .map(h -> new HashTagInfo(h.tag(), h.order())) + .toList(); + } + public Trip to(UUID memberId) { return Trip.create( memberId, - locationId, + locationIds, new TripTitle(title), imageUrl, new TripDescription(description), new TripPeriod(start, end), open, maxParticipants, - hashTags, + toHashTagInfos(), category, TripStatus.RECRUITING ); @@ -74,14 +91,14 @@ public Trip to(UUID memberId) { public Trip toWithItineraries(UUID memberId) { return Trip.createWithItineraries( memberId, - locationId, + locationIds, new TripTitle(title), imageUrl, new TripDescription(description), new TripPeriod(start, end), open, maxParticipants, - hashTags, + toHashTagInfos(), category, TripStatus.RECRUITING ); diff --git a/src/main/java/com/retrip/trip/application/in/response/TripCreateResponse.java b/src/main/java/com/retrip/trip/application/in/response/TripCreateResponse.java index 132d4de..36a637f 100644 --- a/src/main/java/com/retrip/trip/application/in/response/TripCreateResponse.java +++ b/src/main/java/com/retrip/trip/application/in/response/TripCreateResponse.java @@ -14,8 +14,8 @@ public record TripCreateResponse( @Schema(description = "여행 ID", example = "550e8400-e29b-41d4-a716-446655440000") UUID id, - @Schema(description = "여행 목적지 ID", example = "550e8400-e29b-41d4-a716-446655440001") - UUID destinationId, + @Schema(description = "여행 목적지 ID 목록") + List destinationIds, @Schema(description = "여행 제목", example = "파리 여행") String title, @@ -35,8 +35,8 @@ public record TripCreateResponse( @Schema(description = "여행 최대 참가 인원") int maxParticipants, - @Schema(description = "여행 카테고리") - List hashTags, + @Schema(description = "HashTag 목록") + List hashTags, @Schema(description = "여행 카테고리") String category, @@ -50,14 +50,16 @@ public record TripCreateResponse( public static TripCreateResponse of(Trip trip) { return new TripCreateResponse( trip.getId(), - trip.getDestinationId(), + trip.getDestinations().getDestinationIds(), trip.getTitle().getValue(), trip.getDescription().getValue(), trip.getPeriod().getStart(), trip.getPeriod().getEnd(), trip.isOpen(), trip.getTripParticipants().getMaxParticipants(), - trip.getHashTags().getValues().stream().map(TripHashTag::getName).toList(), + trip.getHashTags().getValues().stream() + .map(h -> new HashTagResponse(h.getName(), h.getTagOrder())) + .toList(), trip.getCategory().getViewName(), trip.getImageUrl(), trip.getItineraries() == null ? new ArrayList<>() : @@ -67,6 +69,15 @@ public static TripCreateResponse of(Trip trip) { ); } + @Schema(description = "해시태그 Response") + public record HashTagResponse( + @Schema(description = "해시태그") + String tag, + + @Schema(description = "정렬 순서") + int order + ) {} + @Schema(description = "여행 일정 Response") private record ItineraryCreateResponse( @Schema(description = "일정 ID") diff --git a/src/main/java/com/retrip/trip/application/in/response/TripDetailResponse.java b/src/main/java/com/retrip/trip/application/in/response/TripDetailResponse.java index fce91ab..119a811 100644 --- a/src/main/java/com/retrip/trip/application/in/response/TripDetailResponse.java +++ b/src/main/java/com/retrip/trip/application/in/response/TripDetailResponse.java @@ -67,11 +67,11 @@ public record TripDetailResponse( ) String tripStatusName, - @Schema( - description = "여행 목적지", - example = "부산 해운대" - ) - String tripLocation, + @Schema(description = "여행 목적지 명 목록(아직 빈값 내려갈 예정)") + List destinationNames, + + @Schema(description = "여행 목적지 ID 목록") + List destinationIds, @Schema( description = "여행 대표 이미지 URL", @@ -85,15 +85,20 @@ public record TripDetailResponse( ) String description, - @Schema( - description = "여행 해시태그 목록", - example = "[\"힐링\", \"바다\", \"맛집투어\"]" - ) - List hashTags, + @Schema(description = "여행 해시태그 목록") + List hashTags, @Schema(description = "여행 참가자 목록") List participants ) { + @Schema(description = "해시태그 응답") + public record HashTagResponse( + @Schema(description = "해시태그 값", example = "맛집투어") + String tag, + + @Schema(description = "정렬 순서", example = "1") + int order + ) {} public static TripDetailResponse of(UUID memberId, Trip trip) { return TripDetailResponse.builder() @@ -106,10 +111,13 @@ public static TripDetailResponse of(UUID memberId, Trip trip) { .maxParticipantCount(trip.getTripParticipants().getMaxParticipants()) .tripStatus(trip.getStatus()) .tripStatusName(trip.getStatus().getViewName()) - .tripLocation("") // TODO : Location 구현 시 채울 예정 + .destinationNames(List.of()) // TODO : Location 구현 시 채울 예정 + .destinationIds(trip.getDestinations().getDestinationIds()) .imageUrl(trip.getImageUrl()) .description(trip.getDescription().getValue()) - .hashTags(trip.getHashTags().getHashTagNames()) + .hashTags(trip.getHashTags().getValues().stream() + .map(h -> new HashTagResponse(h.getName(), h.getTagOrder())) + .toList()) .participants(TripParticipantResponse.toList(trip.getTripParticipants().getValues())) .build(); } diff --git a/src/main/java/com/retrip/trip/application/in/response/TripResponse.java b/src/main/java/com/retrip/trip/application/in/response/TripResponse.java index f5ca7d8..71e6ece 100644 --- a/src/main/java/com/retrip/trip/application/in/response/TripResponse.java +++ b/src/main/java/com/retrip/trip/application/in/response/TripResponse.java @@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import java.time.LocalDate; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.UUID; @@ -19,8 +20,8 @@ public record TripResponse( @Schema(description = "여행 제목") String title, - @Schema(description = "목적지 ID") - UUID destinationId, + @Schema(description = "목적지 ID 목록") + List destinationIds, @Schema(description = "목적지 명 (예: 파리, 제주)") String destinationName, // TODO: 추후 QueryDSL Join으로 데이터 채우기 구현 필요 @@ -47,20 +48,27 @@ public record TripResponse( boolean open, @Schema(description = "HashTag 목록") - List hashTags + List hashTags ) { + + @Schema(description = "해시태그 응답") + public record HashTagResponse( + @Schema(description = "해시태그 값") + String tag, + + @Schema(description = "정렬 순서") + int order + ) {} + public static List of(List trips, List hashTags) { - Map> tags = hashTags.stream() - .collect(Collectors.groupingBy( - h -> h.getTrip().getId(), - Collectors.mapping(TripHashTag::getName, Collectors.toList()) - )); + Map> tagMap = hashTags.stream() + .collect(Collectors.groupingBy(h -> h.getTrip().getId())); return trips.stream() .map(trip -> new TripResponse( trip.getId(), trip.getTitle().getValue(), - trip.getDestinationId(), + trip.getDestinations().getDestinationIds(), "", // destinationName: 현재 Location 정보가 없으므로 빈 값 또는 추후 구현 trip.getImageUrl(), trip.getStatus(), @@ -69,7 +77,10 @@ public static List of(List trips, List hashTags trip.getTripParticipants().getCurrentCount(), trip.getTripParticipants().getMaxParticipants(), trip.isOpen(), - tags.getOrDefault(trip.getId(), List.of()) + tagMap.getOrDefault(trip.getId(), List.of()).stream() + .sorted(Comparator.comparingInt(TripHashTag::getTagOrder)) + .map(h -> new HashTagResponse(h.getName(), h.getTagOrder())) + .toList() )) .toList(); } diff --git a/src/main/java/com/retrip/trip/application/in/service/ImageService.java b/src/main/java/com/retrip/trip/application/in/service/ImageService.java index 138899e..9b94de9 100644 --- a/src/main/java/com/retrip/trip/application/in/service/ImageService.java +++ b/src/main/java/com/retrip/trip/application/in/service/ImageService.java @@ -25,7 +25,7 @@ public class ImageService implements ImageManageUseCase { private static final String IMAGE_DOMAIN_URL = "https://retrip-media.s3.ap-northeast-2.amazonaws.com"; - private static final String FORDER_NAME = "media"; // 기존 오타 유지 + private static final String FORDER_NAME = "media"; private static final Duration PRESIGN_DURATION = Duration.ofMinutes(5); @Value("${cloud.s3.bucket}") diff --git a/src/main/java/com/retrip/trip/domain/entity/Trip.java b/src/main/java/com/retrip/trip/domain/entity/Trip.java index 812d639..3502064 100644 --- a/src/main/java/com/retrip/trip/domain/entity/Trip.java +++ b/src/main/java/com/retrip/trip/domain/entity/Trip.java @@ -31,7 +31,6 @@ public class Trip extends BaseEntity { @Id @Column(columnDefinition = "varbinary(16)") private UUID id; - private UUID destinationId; @Version private long version; @@ -73,22 +72,24 @@ public class Trip extends BaseEntity { @Embedded private TripHashTags hashTags; + @Embedded + private TripDestinations destinations; + public static Trip create( UUID memberId, - UUID destinationId, + List destinationIds, TripTitle title, String imageUrl, TripDescription description, TripPeriod period, boolean open, int maxParticipants, - List hashTags, + List hashTags, TripCategory category, TripStatus status ) { Trip trip = Trip.builder() .id(UUID.randomUUID()) - .destinationId(destinationId) .title(title) .imageUrl(imageUrl) .description(description) @@ -97,6 +98,7 @@ public static Trip create( .category(category) .status(status) .build(); + trip.destinations = new TripDestinations(trip, destinationIds); trip.tripParticipants = new TripParticipants(memberId, trip, maxParticipants); trip.hashTags = new TripHashTags(trip, hashTags); return trip; @@ -104,20 +106,19 @@ public static Trip create( public static Trip createWithItineraries( UUID leaderId, - UUID destinationId, + List destinationIds, TripTitle title, String imageUrl, TripDescription description, TripPeriod period, boolean open, int maxParticipants, - List hashTags, + List hashTags, TripCategory category, TripStatus status ) { Trip trip = Trip.builder() .id(UUID.randomUUID()) - .destinationId(destinationId) .title(title) .imageUrl(imageUrl) .description(description) @@ -126,6 +127,7 @@ public static Trip createWithItineraries( .category(category) .status(status) .build(); + trip.destinations = new TripDestinations(trip, destinationIds); trip.itineraries = new Itineraries(trip, period); trip.tripParticipants = new TripParticipants(leaderId, trip, maxParticipants); trip.hashTags = new TripHashTags(trip, hashTags); diff --git a/src/main/java/com/retrip/trip/domain/entity/TripDestination.java b/src/main/java/com/retrip/trip/domain/entity/TripDestination.java new file mode 100644 index 0000000..5c2c908 --- /dev/null +++ b/src/main/java/com/retrip/trip/domain/entity/TripDestination.java @@ -0,0 +1,42 @@ +package com.retrip.trip.domain.entity; + +import static lombok.AccessLevel.PROTECTED; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@AllArgsConstructor +@NoArgsConstructor(access = PROTECTED) +public class TripDestination extends BaseEntity { + + @Id + @Column(columnDefinition = "varbinary(16)") + private UUID id; + + @Column(columnDefinition = "varbinary(16)", nullable = false) + private UUID destinationId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "trip_id", + nullable = false, + columnDefinition = "varbinary(16)", + foreignKey = @ForeignKey(name = "fk_trip_destination_to_trip") + ) + private Trip trip; + + public static TripDestination of(Trip trip, UUID destinationId) { + return new TripDestination(UUID.randomUUID(), destinationId, trip); + } +} diff --git a/src/main/java/com/retrip/trip/domain/entity/TripDestinations.java b/src/main/java/com/retrip/trip/domain/entity/TripDestinations.java new file mode 100644 index 0000000..a5cd501 --- /dev/null +++ b/src/main/java/com/retrip/trip/domain/entity/TripDestinations.java @@ -0,0 +1,33 @@ +package com.retrip.trip.domain.entity; + +import static lombok.AccessLevel.PROTECTED; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Embeddable; +import jakarta.persistence.OneToMany; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Embeddable +@NoArgsConstructor(access = PROTECTED, force = true) +public class TripDestinations { + + @OneToMany(mappedBy = "trip", cascade = CascadeType.ALL, orphanRemoval = true) + private final List values = new ArrayList<>(); + + public TripDestinations(Trip trip, List destinationIds) { + values.addAll(destinationIds.stream() + .map(id -> TripDestination.of(trip, id)) + .toList()); + } + + public List getDestinationIds() { + return values.stream() + .map(TripDestination::getDestinationId) + .toList(); + } +} diff --git a/src/main/java/com/retrip/trip/domain/entity/TripHashTag.java b/src/main/java/com/retrip/trip/domain/entity/TripHashTag.java index 9b8d115..7416b47 100644 --- a/src/main/java/com/retrip/trip/domain/entity/TripHashTag.java +++ b/src/main/java/com/retrip/trip/domain/entity/TripHashTag.java @@ -28,6 +28,9 @@ public class TripHashTag extends BaseEntity { @Column private String name; + @Column(name = "tag_order", nullable = false) + private int tagOrder; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn( name = "trip_id", @@ -37,7 +40,7 @@ public class TripHashTag extends BaseEntity { ) private Trip trip; - public static TripHashTag of(Trip trip, String name) { - return new TripHashTag(UUID.randomUUID(), name, trip); + public static TripHashTag of(Trip trip, String name, int tagOrder) { + return new TripHashTag(UUID.randomUUID(), name, tagOrder, trip); } } diff --git a/src/main/java/com/retrip/trip/domain/entity/TripHashTags.java b/src/main/java/com/retrip/trip/domain/entity/TripHashTags.java index 7ceeabd..1dabef4 100644 --- a/src/main/java/com/retrip/trip/domain/entity/TripHashTags.java +++ b/src/main/java/com/retrip/trip/domain/entity/TripHashTags.java @@ -4,11 +4,14 @@ import static lombok.AccessLevel.PROTECTED; import com.retrip.trip.domain.exception.common.BusinessException; +import com.retrip.trip.domain.vo.HashTagInfo; import jakarta.persistence.CascadeType; import jakarta.persistence.Embeddable; import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -22,17 +25,21 @@ @Embeddable public class TripHashTags { @OneToMany(mappedBy = "trip", cascade = CascadeType.ALL, orphanRemoval = true) + @OrderBy("tagOrder ASC") private final List values = new ArrayList<>(); - public TripHashTags(Trip trip, List hashTags) { + public TripHashTags(Trip trip, List hashTags) { validate(hashTags); - values.addAll(hashTags.stream().map(m -> TripHashTag.of(trip, m)).toList()); + values.addAll(hashTags.stream() + .sorted(Comparator.comparingInt(HashTagInfo::order)) + .map(h -> TripHashTag.of(trip, h.tag(), h.order())) + .toList()); } - private void validate(List hashTags) { + private void validate(List hashTags) { if (hashTags != null && !hashTags.isEmpty()) { - new HashSet<>(hashTags).forEach(hashTag -> { - if (hashTag.length() > 10 || hashTag.isEmpty()) { + hashTags.forEach(h -> { + if (h.tag().isEmpty() || h.tag().length() > 10) { throw new BusinessException(INVALID_HASHTAG_LENGTH); } }); diff --git a/src/main/java/com/retrip/trip/domain/vo/HashTagInfo.java b/src/main/java/com/retrip/trip/domain/vo/HashTagInfo.java new file mode 100644 index 0000000..0ce98fb --- /dev/null +++ b/src/main/java/com/retrip/trip/domain/vo/HashTagInfo.java @@ -0,0 +1,7 @@ +package com.retrip.trip.domain.vo; + +public record HashTagInfo( + String tag, + int order +) { +} diff --git a/src/main/java/com/retrip/trip/domain/vo/TripDescription.java b/src/main/java/com/retrip/trip/domain/vo/TripDescription.java index 1b0abe4..e8b4657 100644 --- a/src/main/java/com/retrip/trip/domain/vo/TripDescription.java +++ b/src/main/java/com/retrip/trip/domain/vo/TripDescription.java @@ -12,7 +12,7 @@ @EqualsAndHashCode @NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) public class TripDescription { - private static final int LENGTH_LIMIT = 1000; + private static final int LENGTH_LIMIT = 70; @Column(name = "description", nullable = false, length = LENGTH_LIMIT) private final String value; diff --git a/src/main/java/com/retrip/trip/infra/config/S3Config.java b/src/main/java/com/retrip/trip/infra/config/S3Config.java index 2f3e8f3..ff6b1ae 100644 --- a/src/main/java/com/retrip/trip/infra/config/S3Config.java +++ b/src/main/java/com/retrip/trip/infra/config/S3Config.java @@ -13,6 +13,7 @@ import software.amazon.awssdk.services.s3.presigner.S3Presigner; @Configuration +@Profile("!test") @RequiredArgsConstructor public class S3Config { @Value("${cloud.aws.region:ap-northeast-2}") diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5c52589..2753ba1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,8 +6,8 @@ spring: enabled: true datasource: driver-class-name: org.h2.Driver - url: jdbc:h2:mem:trip;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MySQL -# url: jdbc:h2:tcp://localhost/~/trip;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MySQL +# url: jdbc:h2:mem:trip;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MySQL + url: jdbc:h2:tcp://localhost/~/trip;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MySQL username: sa password: From fd792bfd9f1ca44c877fc7f6862427ee11f6d41d Mon Sep 17 00:00:00 2001 From: pparkjs Date: Sun, 1 Mar 2026 17:41:48 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test:=20=EA=B8=B0=ED=9A=8D=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20API=20=EC=88=98=EC=A0=95=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/retrip/trip/TripApplicationTests.java | 13 -- .../trip/application/in/TripServiceTest.java | 143 ++++++++++++++++-- .../application/in/base/BaseServiceTest.java | 1 + .../retrip/trip/domain/entity/TripTest.java | 25 +-- .../trip/domain/fixture/TripFixture.java | 31 ++-- .../trip/domain/vo/TripDescriptionTest.java | 4 +- src/test/resources/application-test.yml | 41 +++++ src/test/resources/application.properties | 21 --- 8 files changed, 211 insertions(+), 68 deletions(-) delete mode 100644 src/test/java/com/retrip/trip/TripApplicationTests.java create mode 100644 src/test/resources/application-test.yml delete mode 100644 src/test/resources/application.properties diff --git a/src/test/java/com/retrip/trip/TripApplicationTests.java b/src/test/java/com/retrip/trip/TripApplicationTests.java deleted file mode 100644 index 1fbe03c..0000000 --- a/src/test/java/com/retrip/trip/TripApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.retrip.trip; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class TripApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/com/retrip/trip/application/in/TripServiceTest.java b/src/test/java/com/retrip/trip/application/in/TripServiceTest.java index a66d84c..338f08d 100644 --- a/src/test/java/com/retrip/trip/application/in/TripServiceTest.java +++ b/src/test/java/com/retrip/trip/application/in/TripServiceTest.java @@ -61,9 +61,15 @@ private Trip createProgressTrip(UUID leaderId) { @Test void 여행을_생성_한다() { - TripCreateRequest request = - new TripCreateRequest( - locationId, + // given + List locationIds = List.of(locationId, UUID.randomUUID()); + List hashTags = List.of( + new TripCreateRequest.HashTagInput("남자", 1), + new TripCreateRequest.HashTagInput("20대", 2) + ); + + TripCreateRequest request = new TripCreateRequest( + locationIds, "속초 여행 멤버 구함", "https://k.kakaocdn.net/dn/image.jpg", "속초 여행은 이렇게이렇게 갈겁니다~", @@ -72,12 +78,120 @@ private Trip createProgressTrip(UUID leaderId) { true, "a".repeat(PASSWORD_MIN_LENGTH + 1), 4, - List.of("속초 여행", "MZ"), + hashTags, TripCategory.DOMESTIC); + + // then TripCreateResponse response = tripService.createTrip(memberId, request); + + // when assertThat(response.id()).isNotNull(); - assertThat(response.destinationId()).isEqualTo(locationId); - assertThat(response.hashTags()).contains("속초 여행", "MZ"); + assertThat(response.destinationIds()).hasSize(2); + assertThat(response.destinationIds()).contains(locationId); + assertThat(response.hashTags()).hasSize(2); + assertThat(response.hashTags().get(0).tag()).isEqualTo("남자"); + assertThat(response.hashTags().get(0).order()).isEqualTo(1); + assertThat(response.hashTags().get(1).tag()).isEqualTo("20대"); + assertThat(response.hashTags().get(1).order()).isEqualTo(2); + } + + @Test + void 여행_생성시_여행지를_다수_등록할_수_있다() { + UUID destinationId1 = UUID.randomUUID(); + UUID destinationId2 = UUID.randomUUID(); + UUID destinationId3 = UUID.randomUUID(); + + TripCreateRequest request = new TripCreateRequest( + List.of(destinationId1, destinationId2, destinationId3), + "유럽 여행", + null, + "유럽 3개국 여행", + LocalDate.now().plusDays(1), + LocalDate.now().plusDays(10), + true, + null, + 4, + List.of(new TripCreateRequest.HashTagInput("남자", 1)), + TripCategory.OVERSEAS); + + TripCreateResponse response = tripService.createTrip(memberId, request); + + assertThat(response.destinationIds()).hasSize(3); + assertThat(response.destinationIds()).containsExactlyInAnyOrder(destinationId1, destinationId2, destinationId3); + } + + @Test + void 여행_생성시_해시태그_순서가_보장된다() { + List hashTags = List.of( + new TripCreateRequest.HashTagInput("세번째", 3), + new TripCreateRequest.HashTagInput("첫번째", 1), + new TripCreateRequest.HashTagInput("두번째", 2) + ); + + TripCreateRequest request = new TripCreateRequest( + List.of(UUID.randomUUID()), + "순서 테스트", + null, + "해시태그 순서 테스트", + LocalDate.now().plusDays(1), + LocalDate.now().plusDays(3), + true, + null, + 4, + hashTags, + TripCategory.DOMESTIC); + + TripCreateResponse response = tripService.createTrip(memberId, request); + + assertThat(response.hashTags().get(0).tag()).isEqualTo("첫번째"); + assertThat(response.hashTags().get(1).tag()).isEqualTo("두번째"); + assertThat(response.hashTags().get(2).tag()).isEqualTo("세번째"); + } + + @Test + void 여행_생성시_소개글이_70자를_초과하면_실패한다() { + String longDescription = "a".repeat(71); + + TripCreateRequest request = new TripCreateRequest( + List.of(UUID.randomUUID()), + "테스트 여행", + null, + longDescription, + LocalDate.now().plusDays(1), + LocalDate.now().plusDays(3), + true, + null, + 4, + List.of(new TripCreateRequest.HashTagInput("태그", 1)), + TripCategory.DOMESTIC); + + assertThrows(IllegalArgumentException.class, () -> { + tripService.createTrip(memberId, request); + }); + } + + @Test + void 여행_생성시_해시태그가_10자를_초과하면_실패한다() { + List hashTags = List.of( + new TripCreateRequest.HashTagInput("a".repeat(11), 1) + ); + + TripCreateRequest request = new TripCreateRequest( + List.of(UUID.randomUUID()), + "테스트 여행", + null, + "설명", + LocalDate.now().plusDays(1), + LocalDate.now().plusDays(3), + true, + null, + 4, + hashTags, + TripCategory.DOMESTIC); + + assertThrows(BusinessException.class, () -> { + tripService.createTrip(memberId, request); + }); } @Test @@ -90,7 +204,13 @@ private Trip createProgressTrip(UUID leaderId) { assertThat(trips.getTotalElements()).isEqualTo(3); assertThat(trips.getPageable().getOffset()).isEqualTo(0); assertThat(trips.getPageable().getPageSize()).isEqualTo(2); - assertThat(trips.getContent().getFirst().hashTags()).contains("test", "Test 해시 코드"); + + List hashTags = trips.getContent().getFirst().hashTags(); + assertThat(hashTags).hasSize(2); + assertThat(hashTags.get(0).tag()).isEqualTo("남자"); + assertThat(hashTags.get(0).order()).isEqualTo(1); + assertThat(hashTags.get(1).tag()).isEqualTo("20대"); + assertThat(hashTags.get(1).order()).isEqualTo(2); } @@ -655,17 +775,20 @@ void isLeader_check_works_correctly() { 4, TripStatus.RECRUITING, TripStatus.RECRUITING.getViewName(), - "", + List.of(), + trip.getDestinations().getDestinationIds(), TEST_IMAGE_URL, "여행 설명", - List.of("test", "Test 해시 코드"), + List.of( + new TripDetailResponse.HashTagResponse("남자", 1), + new TripDetailResponse.HashTagResponse("20대", 2) + ), List.of( new TripParticipantResponse(UUID.randomUUID(), memberId, "안녕하세요 테스트 입니다", "홍길동", TEST_IMAGE_URL, ParticipantRole.LEADER), new TripParticipantResponse(UUID.randomUUID(), 정수_ID, "안녕하세요 박정수 입니다", "홍길동", TEST_IMAGE_URL, ParticipantRole.PARTICIPANT) ) ); - //then assertThat(tripDetail).usingRecursiveComparison() .ignoringFields("participants.participantId", "participants.introduction", "participants.nickName", "participants.imageUrl") diff --git a/src/test/java/com/retrip/trip/application/in/base/BaseServiceTest.java b/src/test/java/com/retrip/trip/application/in/base/BaseServiceTest.java index e1ab532..a5ae887 100644 --- a/src/test/java/com/retrip/trip/application/in/base/BaseServiceTest.java +++ b/src/test/java/com/retrip/trip/application/in/base/BaseServiceTest.java @@ -4,6 +4,7 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; @DataJpaTest @Import(QuerydslConfig.class) diff --git a/src/test/java/com/retrip/trip/domain/entity/TripTest.java b/src/test/java/com/retrip/trip/domain/entity/TripTest.java index 2ebbf1b..a36057c 100644 --- a/src/test/java/com/retrip/trip/domain/entity/TripTest.java +++ b/src/test/java/com/retrip/trip/domain/entity/TripTest.java @@ -1,6 +1,7 @@ package com.retrip.trip.domain.entity; import com.retrip.trip.domain.exception.PeriodUpdateFailedException; +import com.retrip.trip.domain.vo.HashTagInfo; import com.retrip.trip.domain.vo.ParticipantRole; import com.retrip.trip.domain.vo.TripCategory; import com.retrip.trip.domain.vo.TripDescription; @@ -25,12 +26,16 @@ class TripTest { UUID memberId = UUID.fromString("c076d246-7e6d-4191-bf5c-310aebf4c003"); UUID destinationId = UUID.fromString("13c8ab91-76bc-4f70-93e9-89f1a65dc64a"); + List destinationIds = List.of(destinationId); + List hashTags = List.of(new HashTagInfo("속초 여행", 1)); + List testHashTags = List.of(new HashTagInfo("Test Tag", 1)); + @DisplayName("제목, 설명, 여행지, 기간, 공개 여부, 참가인원수,카테고리, 해시태그를 입력해 여행을 생성할 수 있다.") @Test void create() { assertThatCode(() -> Trip.create( memberId, - destinationId, + destinationIds, new TripTitle("속초 여행 멤버 구함"), "https://image.url", new TripDescription("속초 여행은 이렇게이렇게 갈겁니다~"), @@ -39,7 +44,7 @@ void create() { LocalDate.now().plusDays(5)), true, 4, - List.of("속초 여행"), + hashTags, TripCategory.DOMESTIC, TripStatus.RECRUITING)).doesNotThrowAnyException(); } @@ -49,7 +54,7 @@ void create() { void createWithItinerary() { assertThatCode(() -> Trip.createWithItineraries( memberId, - destinationId, + destinationIds, new TripTitle("속초 여행 멤버 구함"), "https://image.url", new TripDescription("속초 여행은 이렇게이렇게 갈겁니다~"), @@ -58,7 +63,7 @@ void createWithItinerary() { LocalDate.now().plusDays(5)), true, 4, - List.of("속초 여행"), + hashTags, TripCategory.DOMESTIC, TripStatus.RECRUITING )).doesNotThrowAnyException(); @@ -69,7 +74,7 @@ void createWithItinerary() { // given Trip trip = Trip.create( memberId, - destinationId, + destinationIds, new TripTitle("속초 여행 멤버 구함"), "https://image.url", new TripDescription("속초 여행은 이렇게이렇게 갈겁니다~"), @@ -78,7 +83,7 @@ void createWithItinerary() { LocalDate.now().plusDays(5)), true, 4, - List.of("속초 여행"), + hashTags, TripCategory.DOMESTIC, TripStatus.RECRUITING); @@ -98,14 +103,14 @@ void createWithItinerary() { TripPeriod period = new TripPeriod(LocalDate.now().plusDays(1), LocalDate.now().plusDays(6)); Trip trip = Trip.create( memberId, - UUID.randomUUID(), + destinationIds, new TripTitle("속초 여행 멤버 구함"), "https://image.url", new TripDescription("속초 여행은 이렇게이렇게 갈겁니다~"), period, true, 4, - List.of("Test Tag"), + testHashTags, TripCategory.DOMESTIC, TripStatus.RECRUITING); //when @@ -125,14 +130,14 @@ void createWithItinerary() { TripPeriod period = new TripPeriod(LocalDate.now().plusDays(1), LocalDate.now().plusDays(6)); Trip trip = Trip.create( UUID.randomUUID(), - UUID.randomUUID(), + destinationIds, new TripTitle("속초 여행 멤버 구함"), "https://image.url", new TripDescription("속초 여행은 이렇게이렇게 갈겁니다~"), period, true, 4, - List.of("Test Tag"), + testHashTags, TripCategory.DOMESTIC, TripStatus.RECRUITING); trip.addParticipant(TripParticipant.createTripParticipant(memberId, trip)); diff --git a/src/test/java/com/retrip/trip/domain/fixture/TripFixture.java b/src/test/java/com/retrip/trip/domain/fixture/TripFixture.java index c98a5fe..aa3678d 100644 --- a/src/test/java/com/retrip/trip/domain/fixture/TripFixture.java +++ b/src/test/java/com/retrip/trip/domain/fixture/TripFixture.java @@ -21,17 +21,24 @@ public class TripFixture { private static final String TEST_IMAGE_URL = "https://test-image.com/default.jpg"; + private static final List DEFAULT_HASH_TAGS = List.of( + new HashTagInfo("남자", 1), + new HashTagInfo("20대", 2) + ); + + private static final List DEFAULT_DESTINATION_IDS = List.of(UUID.randomUUID()); + public static Trip createTrip(UUID tripId) { Trip trip = Trip.createWithItineraries( LEADER_ID, - UUID.randomUUID(), + DEFAULT_DESTINATION_IDS, new TripTitle("속초 여행 멤버 구함"), TEST_IMAGE_URL, new TripDescription("속초 여행은 이렇게이렇게 갈겁니다~"), new TripPeriod(LocalDate.now().plusDays(1), LocalDate.now().plusDays(10)), true, 4, - List.of("test", "Test 해시 코드"), + DEFAULT_HASH_TAGS, TripCategory.DOMESTIC, TripStatus.RECRUITING); ReflectionTestUtils.setField(trip, "id", tripId); @@ -42,14 +49,14 @@ public static Trip createTestTrip(UUID memberId, String title, String descriptio TripPeriod period = createFuturePeriod(); return Trip.create( memberId, - UUID.randomUUID(), + DEFAULT_DESTINATION_IDS, new TripTitle(title), TEST_IMAGE_URL, new TripDescription(description), period, true, 4, - List.of("test", "Test 해시 코드"), + DEFAULT_HASH_TAGS, category, TripStatus.RECRUITING); } @@ -57,14 +64,14 @@ public static Trip createTestTrip(UUID memberId, String title, String descriptio public static Trip createTestTripWithPeriod(UUID memberId, String title, String description, TripCategory category, TripPeriod period) { return Trip.createWithItineraries( memberId, - UUID.randomUUID(), + DEFAULT_DESTINATION_IDS, new TripTitle(title), TEST_IMAGE_URL, new TripDescription(description), period, true, 4, - List.of("test", "Test 해시 코드"), + DEFAULT_HASH_TAGS, category, TripStatus.RECRUITING); } @@ -73,14 +80,14 @@ public static Trip createTestTripWithMaxParticipants(UUID memberId, String title TripPeriod period = createFuturePeriod(); return Trip.create( memberId, - UUID.randomUUID(), + DEFAULT_DESTINATION_IDS, new TripTitle(title), TEST_IMAGE_URL, new TripDescription(description), period, true, maxParticipants, - List.of("test", "Test 해시 코드"), + DEFAULT_HASH_TAGS, category, TripStatus.RECRUITING); } @@ -89,14 +96,14 @@ public static Trip createReadyTrip(UUID memberId, String title, String descripti TripPeriod period = createFuturePeriod(); Trip trip = Trip.create( memberId, - UUID.randomUUID(), + DEFAULT_DESTINATION_IDS, new TripTitle(title), TEST_IMAGE_URL, new TripDescription(description), period, true, 4, - List.of("test", "Test 해시 코드"), + DEFAULT_HASH_TAGS, category, TripStatus.RECRUITING); ReflectionTestUtils.setField(trip, "status", TripStatus.BEFORE_TRIP); @@ -107,14 +114,14 @@ public static Trip createProgressTrip(UUID memberId, String title, String descri TripPeriod period = createFuturePeriod(); Trip trip = Trip.create( memberId, - UUID.randomUUID(), + DEFAULT_DESTINATION_IDS, new TripTitle(title), TEST_IMAGE_URL, new TripDescription(description), period, true, 4, - List.of("test", "Test 해시 코드"), + DEFAULT_HASH_TAGS, category, TripStatus.RECRUITING); ReflectionTestUtils.setField(trip, "status", TripStatus.IN_PROGRESS); diff --git a/src/test/java/com/retrip/trip/domain/vo/TripDescriptionTest.java b/src/test/java/com/retrip/trip/domain/vo/TripDescriptionTest.java index b5e0195..1109c8f 100644 --- a/src/test/java/com/retrip/trip/domain/vo/TripDescriptionTest.java +++ b/src/test/java/com/retrip/trip/domain/vo/TripDescriptionTest.java @@ -12,10 +12,10 @@ class TripDescriptionTest { @Test void 최대_제한_길이가_존재한다() { //given - String input = IntStream.range(0, 1001) + String input = IntStream.range(0, 71) .mapToObj(i -> "A") // .collect(Collectors.joining("")); - String expectedMessage = "여행 소개글은 1000자를 넘을 수 없습니다."; + String expectedMessage = "여행 소개글은 70자를 넘을 수 없습니다."; //when & then assertThatThrownBy(() -> new TripDescription(input)) diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 0000000..0fa596a --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,41 @@ +spring: + application: + name: trip + datasource: + driver-class-name: org.h2.Driver + url: jdbc:h2:tcp://localhost/~/trip;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MySQL +# url: jdbc:h2:mem:trip;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MySQL + username: sa + password: + jpa: + hibernate: + ddl-auto: create-drop + properties: + hibernate: + show_sql: true + format_sql: true + dialect: org.hibernate.dialect.MySQL8Dialect + open-in-view: false + +logging: + level: + org.hibernate.type.descriptor.sql: trace + org.springframework.web.client.RestTemplate: DEBUG + +springdoc: + swagger-ui: + use-root-path: true + enabled: true + api-docs: + enabled: true + +jwt: + public-key: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlfO/CoX9EzhskpAKX9ADH0wfEQZP4rAq + ptqq80W2YaOHAnXu+oU1UrP0b9ccKKSzMCVDwdmXrecZB0dFLPnoazMEbVOf6MSwrhGfxupPRmmJ + sIYgmQwo8/vnjaq/GYFfnHyCy6yKL41G+GZVqgeKdhr+w1jUw4L9Fs0l2J/AYqwTxZOnzxrU5erP + GSE5Dd3AwWt/brxuwA7sRfVS3mbbsYyYExjUrEbst8VtF3Pis35T8YfSDKMOUgiDnp30EAdGU1Up + u59J3+ToLRrIqIszRZqmasrWTL2/ihPO76PSTIMAsJMScjAwjUXA47YOOy6Vkzy8r3bPTYHp5C1N + KucWwwIDAQAB + -----END PUBLIC KEY----- \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties deleted file mode 100644 index e48966c..0000000 --- a/src/test/resources/application.properties +++ /dev/null @@ -1,21 +0,0 @@ -spring.application.name=trip - -# jpa -spring.datasource.driver-class-name=org.h2.Driver -spring.datasource.url=jdbc:h2:tcp://localhost/~/trip;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MySQL -spring.datasource.username=sa -spring.datasource.password= -spring.jpa.properties.hibernate.show_sql=true -spring.jpa.properties.hibernate.format_sql=true -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect -spring.jpa.hibernate.ddl-auto=create-drop -spring.jpa.open-in-view=false - -# logging -logging.level.org.hibernate.type.descriptor.sql=trace -logging.level.org.springframework.web.client.RestTemplate=DEBUG - -# Swagger 설정 -springdoc.swagger-ui.use-root-path=true -springdoc.api-docs.enabled=true -springdoc.swagger-ui.enabled=true