From 65565f75d6a989c035246f8ac11a39488be576a1 Mon Sep 17 00:00:00 2001 From: Son Gahyun <77109954+hyun2371@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:31:53 +0900 Subject: [PATCH] =?UTF-8?q?[feat=20#92]=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20API=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [chore] : MongoDB 의존성 추가 * [chore] : docker-compose에 mongo 이미지 추가 * [feat] : messageType 변환 예외 구체화 * [feat] : chatMessage 엔티티 -> 몽고 DB collection으로 변경 * [feat] : 채팅 메시지 요청 dto 추가 * [feat] : 채팅 메시지 응답 dto 추가 * [feat] : 채팅 메시지<-> dto mapper 추가 * [feat] : 채팅 메시지 mongoRepository 추가 * [feat] : 채팅 메시지 저장, 조회 비즈니스 로직 추가 * [feat] : 채팅 메시지 저장, 조회 API 로직 추가 * [feat] : 채팅 메시지 조회 비즈니스 로직 테스트 * [test] : 테스트 컨테이너에 MongoDB 컨테이너 추가 * [test] : 채팅방 메시지 조회 통합 테스트 작성 * [rename] : chatMessage-> chatRoom으로 파일명 수정 * [chore] : PR시 배포 되도록 임시 수정 * [style] : 함수명 복수형으로 변경 * [feat] : dto validation 추가 * [chore] : cd 스크립트 원복 --- build.gradle | 5 ++ docker-compose.yml | 14 +++- .../chat/controller/ChatRoomController.java | 52 +++++++++++++ .../dnd/gongmuin/chat/domain/ChatMessage.java | 73 ++++++++----------- .../dnd/gongmuin/chat/domain/MessageType.java | 5 +- .../com/dnd/gongmuin/chat/dto/ChatMapper.java | 37 ++++++++++ .../gongmuin/chat/dto/ChatMessageRequest.java | 12 +++ .../chat/dto/ChatMessageResponse.java | 10 +++ .../chat/exception/ChatErrorCode.java | 16 ++++ .../repository/ChatMessageRepository.java | 13 ++++ .../chat/service/ChatRoomService.java | 41 +++++++++++ .../controller/ChatRoomControllerTest.java | 40 ++++++++++ .../chat/service/ChatRoomServiceTest.java | 52 +++++++++++++ .../common/fixture/ChatMessageFixture.java | 21 ++++++ .../common/support/TestContainerSupport.java | 12 +++ 15 files changed, 360 insertions(+), 43 deletions(-) create mode 100644 src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.java create mode 100644 src/main/java/com/dnd/gongmuin/chat/dto/ChatMapper.java create mode 100644 src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageRequest.java create mode 100644 src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageResponse.java create mode 100644 src/main/java/com/dnd/gongmuin/chat/exception/ChatErrorCode.java create mode 100644 src/main/java/com/dnd/gongmuin/chat/repository/ChatMessageRepository.java create mode 100644 src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java create mode 100644 src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.java create mode 100644 src/test/java/com/dnd/gongmuin/chat/service/ChatRoomServiceTest.java create mode 100644 src/test/java/com/dnd/gongmuin/common/fixture/ChatMessageFixture.java diff --git a/build.gradle b/build.gradle index d1295651..e7caf51f 100644 --- a/build.gradle +++ b/build.gradle @@ -108,6 +108,8 @@ dependencies { // TestContainer testImplementation "org.testcontainers:testcontainers:1.20.1" testImplementation "org.testcontainers:junit-jupiter:1.20.1" + // mongoDB 컨테이너 + testImplementation "org.testcontainers:mongodb:1.20.1" // mysql 컨테이너 testImplementation "org.testcontainers:mysql:1.20.1" // redis @@ -139,6 +141,9 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + // MongoDB + implementation 'org.springframework.boot:spring-boot-starter-data-mongodb:3.1.8' } // Querydsl diff --git a/docker-compose.yml b/docker-compose.yml index be58828a..a1b50579 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,4 +5,16 @@ services: container_name: gongmuin-redis ports: - "6379:6379" - restart: always \ No newline at end of file + restart: always + mongodb: + image: mongo + container_name: mongodb + ports: + - "27017:27017" + environment: + - MONGO_INITDB_ROOT_USERNAME=rootuser + - MONGO_INITDB_ROOT_PASSWORD=rootpass + volumes: + - mongo:/data/db +volumes: + mongo: \ No newline at end of file diff --git a/src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.java b/src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.java new file mode 100644 index 00000000..8f3b1cab --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.java @@ -0,0 +1,52 @@ +package com.dnd.gongmuin.chat.controller; + +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.dnd.gongmuin.chat.dto.ChatMessageRequest; +import com.dnd.gongmuin.chat.dto.ChatMessageResponse; +import com.dnd.gongmuin.chat.service.ChatRoomService; +import com.dnd.gongmuin.common.dto.PageResponse; +import com.dnd.gongmuin.member.domain.Member; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@Tag(name = "채팅방 API") +@RestController +@RequiredArgsConstructor +public class ChatRoomController { + + private final ChatRoomService chatRoomService; + + @Operation(summary = "채팅방 메시지 등록 임시 API", description = "웹소켓 연결 전 임시 API") + @PostMapping("/api/chat-messages/{chatRoomId}") + public ResponseEntity registerChatMessage( + @PathVariable Long chatRoomId, + @Valid @RequestBody ChatMessageRequest request, + @AuthenticationPrincipal Member member + ) { + chatRoomService.saveChatMessage(request, chatRoomId, member.getId()); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Operation(summary = "채팅방 메시지 조회 API", description = "채팅방 메시지를 최신순으로 페이징한다.") + @GetMapping("/api/chat-messages/{chatRoomId}") + public ResponseEntity> getChatMessages( + @PathVariable("chatRoomId") Long chatRoomId, + Pageable pageable + ) { + PageResponse response = + chatRoomService.getChatMessages(chatRoomId, pageable); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/com/dnd/gongmuin/chat/domain/ChatMessage.java b/src/main/java/com/dnd/gongmuin/chat/domain/ChatMessage.java index faedf1a2..6b220457 100644 --- a/src/main/java/com/dnd/gongmuin/chat/domain/ChatMessage.java +++ b/src/main/java/com/dnd/gongmuin/chat/domain/ChatMessage.java @@ -1,69 +1,60 @@ package com.dnd.gongmuin.chat.domain; -import static jakarta.persistence.ConstraintMode.*; -import static jakarta.persistence.EnumType.*; -import static jakarta.persistence.FetchType.*; -import static jakarta.persistence.GenerationType.*; import static lombok.AccessLevel.*; -import com.dnd.gongmuin.common.entity.TimeBaseEntity; -import com.dnd.gongmuin.member.domain.Member; +import java.time.LocalDateTime; + +import org.springframework.data.mongodb.core.mapping.Document; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Enumerated; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -@Entity +@Document(collection = "chat_messages") @Getter @NoArgsConstructor(access = PROTECTED) -public class ChatMessage extends TimeBaseEntity { +public class ChatMessage { @Id - @GeneratedValue(strategy = IDENTITY) - @Column(name = "chat_message_id") - private Long id; + private String id; - @Column(name = "content", nullable = false) private String content; - @Column(name = "is_read", nullable = false) + private long chatRoomId; + + private long memberId; + private Boolean isRead; - @Column(name = "media_url", nullable = false) private String mediaUrl; - @Enumerated(STRING) - @Column(name = "type", nullable = false) private MessageType type; - @ManyToOne(fetch = LAZY) - @JoinColumn(name = "member_id", - nullable = false, - foreignKey = @ForeignKey(NO_CONSTRAINT)) - private Member member; - - @ManyToOne(fetch = LAZY) - @JoinColumn(name = "chat_room_id", - nullable = false, - foreignKey = @ForeignKey(NO_CONSTRAINT)) - private ChatRoom chatRoom; + private LocalDateTime createdAt; - @Builder - public ChatMessage(String content, String mediaUrl, MessageType type, Member member, - ChatRoom chatRoom) { - this.isRead = false; + private ChatMessage( + String content, + long chatRoomId, + long memberId, + String mediaUrl, + MessageType type + ) { this.content = content; + this.chatRoomId = chatRoomId; + this.memberId = memberId; this.mediaUrl = mediaUrl; this.type = type; - this.member = member; - this.chatRoom = chatRoom; + this.isRead = false; + this.createdAt = LocalDateTime.now(); + } + + public static ChatMessage of( + String content, + long chatRoomId, + long memberId, + String mediaUrl, + MessageType type + ) { + return new ChatMessage(content, chatRoomId, memberId, mediaUrl, type); } } diff --git a/src/main/java/com/dnd/gongmuin/chat/domain/MessageType.java b/src/main/java/com/dnd/gongmuin/chat/domain/MessageType.java index d59ba81a..9850fee1 100644 --- a/src/main/java/com/dnd/gongmuin/chat/domain/MessageType.java +++ b/src/main/java/com/dnd/gongmuin/chat/domain/MessageType.java @@ -2,6 +2,9 @@ import java.util.Arrays; +import com.dnd.gongmuin.chat.exception.ChatErrorCode; +import com.dnd.gongmuin.common.exception.runtime.ValidationException; + import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -19,7 +22,7 @@ public static MessageType of(String input) { return Arrays.stream(values()) .filter(type -> type.isEqual(input)) .findAny() - .orElseThrow(IllegalArgumentException::new); + .orElseThrow(() -> new ValidationException(ChatErrorCode.INVALID_MESSAGE_TYPE)); } private boolean isEqual(String input) { diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/ChatMapper.java b/src/main/java/com/dnd/gongmuin/chat/dto/ChatMapper.java new file mode 100644 index 00000000..29e2d2c8 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/dto/ChatMapper.java @@ -0,0 +1,37 @@ +package com.dnd.gongmuin.chat.dto; + +import com.dnd.gongmuin.chat.domain.ChatMessage; +import com.dnd.gongmuin.chat.domain.MessageType; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ChatMapper { + + public static ChatMessageResponse toChatMessageResponse( + ChatMessage chatMessage + ) { + return new ChatMessageResponse( + chatMessage.getMemberId(), + chatMessage.getChatRoomId(), + chatMessage.getContent(), + chatMessage.getType().getLabel(), + chatMessage.getMediaUrl() + ); + } + + public static ChatMessage toChatMessage( + ChatMessageRequest request, + long chatRoomId, + long memberId + ) { + return ChatMessage.of( + request.content(), + chatRoomId, + memberId, + request.mediaUrl(), + MessageType.of(request.type()) + ); + } +} diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageRequest.java b/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageRequest.java new file mode 100644 index 00000000..9c159d74 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageRequest.java @@ -0,0 +1,12 @@ +package com.dnd.gongmuin.chat.dto; + +import jakarta.validation.constraints.NotBlank; + +public record ChatMessageRequest( + @NotBlank(message = "채팅 내용을 입력해주세요.") + String content, + @NotBlank(message = "채팅 타입을 입력해주세요.") + String type, + String mediaUrl +) { +} diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageResponse.java b/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageResponse.java new file mode 100644 index 00000000..f3cdab5e --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageResponse.java @@ -0,0 +1,10 @@ +package com.dnd.gongmuin.chat.dto; + +public record ChatMessageResponse( + Long memberId, + Long chatRoomId, + String content, + String type, + String mediaUrl +) { +} diff --git a/src/main/java/com/dnd/gongmuin/chat/exception/ChatErrorCode.java b/src/main/java/com/dnd/gongmuin/chat/exception/ChatErrorCode.java new file mode 100644 index 00000000..c0e65406 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/exception/ChatErrorCode.java @@ -0,0 +1,16 @@ +package com.dnd.gongmuin.chat.exception; + +import com.dnd.gongmuin.common.exception.ErrorCode; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ChatErrorCode implements ErrorCode { + + INVALID_MESSAGE_TYPE("메시지 타입을 올바르게 입력해주세요.", "CH_001"); + + private final String message; + private final String code; +} diff --git a/src/main/java/com/dnd/gongmuin/chat/repository/ChatMessageRepository.java b/src/main/java/com/dnd/gongmuin/chat/repository/ChatMessageRepository.java new file mode 100644 index 00000000..1572e546 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/repository/ChatMessageRepository.java @@ -0,0 +1,13 @@ +package com.dnd.gongmuin.chat.repository; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import com.dnd.gongmuin.chat.domain.ChatMessage; + +@Repository +public interface ChatMessageRepository extends MongoRepository { + Slice findByChatRoomIdOrderByCreatedAtDesc(long chatRoomId, Pageable pageable); +} diff --git a/src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java b/src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java new file mode 100644 index 00000000..756dab79 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java @@ -0,0 +1,41 @@ +package com.dnd.gongmuin.chat.service; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.dnd.gongmuin.chat.dto.ChatMapper; +import com.dnd.gongmuin.chat.dto.ChatMessageRequest; +import com.dnd.gongmuin.chat.dto.ChatMessageResponse; +import com.dnd.gongmuin.chat.repository.ChatMessageRepository; +import com.dnd.gongmuin.common.dto.PageMapper; +import com.dnd.gongmuin.common.dto.PageResponse; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ChatRoomService { + + private final ChatMessageRepository chatMessageRepository; + + @Transactional + public void saveChatMessage( + @Valid ChatMessageRequest request, + Long chatRoomId, + Long memberId + ) { + chatMessageRepository.save(ChatMapper.toChatMessage(request, chatRoomId, memberId)); + } + + @Transactional(readOnly = true) + public PageResponse getChatMessages(Long chatRoomId, Pageable pageable) { + Slice responsePage = chatMessageRepository + .findByChatRoomIdOrderByCreatedAtDesc(chatRoomId, pageable) + .map(ChatMapper::toChatMessageResponse); + return PageMapper.toPageResponse(responsePage); + } + +} diff --git a/src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.java b/src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.java new file mode 100644 index 00000000..2af0a823 --- /dev/null +++ b/src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.java @@ -0,0 +1,40 @@ +package com.dnd.gongmuin.chat.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import com.dnd.gongmuin.chat.domain.ChatMessage; +import com.dnd.gongmuin.chat.repository.ChatMessageRepository; +import com.dnd.gongmuin.common.fixture.ChatMessageFixture; +import com.dnd.gongmuin.common.support.ApiTestSupport; + +@DisplayName("[ChatMessage 통합 테스트]") +class ChatRoomControllerTest extends ApiTestSupport { + + @Autowired + private ChatMessageRepository chatMessageRepository; + + @DisplayName("[채팅방 아이디로 메시지를 조회할 수 있다.]") + @Test + void getChatMessages() throws Exception { + List chatMessages = chatMessageRepository.saveAll(List.of( + ChatMessageFixture.chatMessage(), + ChatMessageFixture.chatMessage() + )); + mockMvc.perform(get("/api/chat-messages/{chatRoomId}", 1L) + .cookie(accessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.size").value(2)) + .andExpect(jsonPath("$.content[0].memberId").value(chatMessages.get(0).getMemberId())) + .andExpect(jsonPath("$.content[0].chatRoomId").value(chatMessages.get(0).getChatRoomId())) + .andExpect(jsonPath("$.content[0].content").value(chatMessages.get(0).getContent())) + .andExpect(jsonPath("$.content[0].type").value(chatMessages.get(0).getType().getLabel())) + .andExpect(jsonPath("$.content[0].mediaUrl").value(chatMessages.get(0).getMediaUrl())); + } +} \ No newline at end of file diff --git a/src/test/java/com/dnd/gongmuin/chat/service/ChatRoomServiceTest.java b/src/test/java/com/dnd/gongmuin/chat/service/ChatRoomServiceTest.java new file mode 100644 index 00000000..347587ff --- /dev/null +++ b/src/test/java/com/dnd/gongmuin/chat/service/ChatRoomServiceTest.java @@ -0,0 +1,52 @@ +package com.dnd.gongmuin.chat.service; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.SliceImpl; + +import com.dnd.gongmuin.chat.domain.ChatMessage; +import com.dnd.gongmuin.chat.dto.ChatMessageResponse; +import com.dnd.gongmuin.chat.repository.ChatMessageRepository; +import com.dnd.gongmuin.common.fixture.ChatMessageFixture; + +@DisplayName("[채팅방 메시지 서비스 단위 테스트]") +@ExtendWith(MockitoExtension.class) +class ChatRoomServiceTest { + + private final PageRequest pageRequest = PageRequest.of(0, 5); + + @Mock + private ChatMessageRepository chatMessageRepository; + + @InjectMocks + private ChatRoomService chatRoomService; + + @DisplayName("[채팅방 아이디로 채팅방 메시지를 조회할 수 있다.]") + @Test + void getChatMessages() { + //given + ChatMessage chatMessage = ChatMessageFixture.chatMessage(); + given(chatMessageRepository.findByChatRoomIdOrderByCreatedAtDesc(1L, pageRequest)) + .willReturn(new SliceImpl<>(List.of(chatMessage))); + + //when + List response = chatRoomService.getChatMessages(1L, pageRequest).content(); + + //then + assertAll( + () -> assertThat(response.get(0).chatRoomId()).isEqualTo(1L), + () -> assertThat(response).hasSize(1) + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/dnd/gongmuin/common/fixture/ChatMessageFixture.java b/src/test/java/com/dnd/gongmuin/common/fixture/ChatMessageFixture.java new file mode 100644 index 00000000..84fc1a75 --- /dev/null +++ b/src/test/java/com/dnd/gongmuin/common/fixture/ChatMessageFixture.java @@ -0,0 +1,21 @@ +package com.dnd.gongmuin.common.fixture; + +import com.dnd.gongmuin.chat.domain.ChatMessage; +import com.dnd.gongmuin.chat.domain.MessageType; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ChatMessageFixture { + + public static ChatMessage chatMessage() { + return ChatMessage.of( + "하하", + 1L, + 1L, + null, + MessageType.TEXT + ); + } +} diff --git a/src/test/java/com/dnd/gongmuin/common/support/TestContainerSupport.java b/src/test/java/com/dnd/gongmuin/common/support/TestContainerSupport.java index 8380d3b3..3c3af247 100644 --- a/src/test/java/com/dnd/gongmuin/common/support/TestContainerSupport.java +++ b/src/test/java/com/dnd/gongmuin/common/support/TestContainerSupport.java @@ -6,6 +6,7 @@ import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.utility.DockerImageName; @@ -14,16 +15,23 @@ @NoArgsConstructor(access = PROTECTED) public abstract class TestContainerSupport { + private static final String MONGO_IMAGE = "mongo:latest"; private static final String REDIS_IMAGE = "redis:latest"; private static final String MYSQL_IMAGE = "mysql:8.0"; + private static final int MONGO_PORT = 27017; private static final int REDIS_PORT = 6379; private static final int MYSQL_PORT = 3306; + private static final MongoDBContainer MONGO; + private static final GenericContainer REDIS; private static final JdbcDatabaseContainer MYSQL; // 컨테이너 싱글톤으로 생성 static { + MONGO = new MongoDBContainer(DockerImageName.parse(MONGO_IMAGE)) + .withExposedPorts(MONGO_PORT) + .withReuse(true); REDIS = new GenericContainer<>(DockerImageName.parse(REDIS_IMAGE)) .withExposedPorts(REDIS_PORT) .withReuse(true); @@ -31,6 +39,7 @@ public abstract class TestContainerSupport { .withExposedPorts(MYSQL_PORT) .withReuse(true); + MONGO.start(); REDIS.start(); MYSQL.start(); } @@ -38,6 +47,9 @@ public abstract class TestContainerSupport { // 동적으로 속성 할당 @DynamicPropertySource public static void setUp(DynamicPropertyRegistry registry) { + registry.add("spring.data.mongodb.host", MONGO::getHost); + registry.add("spring.data.mongodb.port", () -> String.valueOf(MONGO.getMappedPort(MONGO_PORT))); + registry.add("spring.data.redis.host", REDIS::getHost); registry.add("spring.data.redis.port", () -> String.valueOf(REDIS.getMappedPort(REDIS_PORT)));