From 6700619bd8941f92fdaef88715f46d5fae4f422b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A7=80=ED=98=81?= Date: Sun, 1 Sep 2024 14:02:32 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20CI/CD=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20(#9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feature: 채팅 기능 구현 (#2) * Feat: 웹 소켓 의존성 추가 * Feat: 웹 소켓에 대한 설정 클래스 작성 * Feat: Redis에 필요한 클래스 작성 및 DTO 추가 Redis의 Pub/Sub에 필요한 클래스와 설정을 작성하고 필요한 DTO를 추가함. * Test: Redis Sub/Pub 테스트 코드 작성 * Feat: 채팅에 필요한 Entity 클래스 작성 * Refactor: DTO 클래스 필드 수정 채팅 메시지 DTO 클래스의 필드를 수정하였고 그에 따른 다른 코드들도 수정함 * Refactor: 채팅 메시지 DTO 클래스명 수정 채팅 메시지 DTO 클래스명을 좀 더 명확하게 하기 위해 ChatReqeust -> ChatMessageRequest로 변경 또한, 변경에 따른 여러 코드 수정 * Refactor: Entity들의 Builder에 외부접근이 불가능하도록 PRIVATE 으로 설정 * Feat: 채팅에 필요한 여러 Repository 추가 * Test: Repository 테스트 코드 작성 * Feat: 채팅 서비스에 필요한 DTO 클래스 추가 * Feat: 채팅 서비스 로직 작성 * Test: 채팅 서비스 로직 테스트 코드 작성 * Test: 테스트 코드 수정 테스트가 일관성을 유지하도록 저장시간을 지정 * Feat: API 응답 객체 생성 * Feat: REST Docs 의존성 추가 및 설정 작성 * Feat: 채팅 컨트롤러 작성 및 예외처리 로직 추가 * Test: 채팅 컨트롤러단 테스트 코드 작성 및 문서화 코드 추가 * Docs: 채팅 API 문서 생성 * CI/CD 구축을 위한 세팅 (#4) * Refactor: 테스트환경과 서비스 환경 분리를 위해 Redis 설정 파일 수정 * Feat: 도커파일 작성 * Feat: 테스트 코드 환경 DB 세팅용 도커컴포즈 파일 작성 * Chore: 빌드 시 1개의 jar 파일만 생성하도록 그래들 수정 * Test: 테스트 코드용 yml파일 작성 * Test: 테스트 yml을 사용하도록 코드 추가 * CI/CD 구축을 위한 수정 (#6) * Refactor: 테스트환경과 서비스 환경 분리를 위해 Redis 설정 파일 수정 * Feat: 도커파일 작성 * Feat: 테스트 코드 환경 DB 세팅용 도커컴포즈 파일 작성 * Chore: 빌드 시 1개의 jar 파일만 생성하도록 그래들 수정 * Test: 테스트 코드용 yml파일 작성 * Test: 테스트 yml을 사용하도록 코드 추가 * Feat: .dockerignore 파일 추가 * Refactor: .properties -> .yml 로 파일 확장자 변경 가독성 증가를 위해 파일 확장자 변경 * Feature: 채팅 기능 추가 및 기타 코드 수정 (#8) * Refactor: Redis 설정 클래스 수정 `@Value` 로 값을 불러들여 빈을 만드는 대신 설정파일을 자동으로 읽어 빈으로 만드는 방법으로 수정 * Feat: 채팅방 UUID로 채팅방 상세 정보를 검색하는 API 추가 * Test: 새로운 API 테스트 코드 작성 * Docs: API 문서 최신화 * Refactor: 테스트용 설정 파일 수정 레디스 설정 수정에 따라 설정 파일 수정 --- .dockerignore | 5 + src/docs/asciidoc/index.adoc | 5 + .../kaboochat/chat/config/RedisConfig.java | 28 +--- .../chat/controller/ChatController.java | 14 ++ src/main/resources/application.properties | 1 - src/main/resources/application.yml | 3 + src/main/resources/static/docs/index.html | 137 ++++++++++++++++-- .../chat/controller/ChatControllerTest.java | 35 +++++ src/test/resources/application.yml | 9 +- 9 files changed, 193 insertions(+), 44 deletions(-) create mode 100644 .dockerignore delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/application.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3a14305 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +/docker-compose-chat-test-db.yml +/Dockerfile +/.gradle/ +/build/ +/.idea/ diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 81688f4..7d3782b 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -21,6 +21,11 @@ operation::chat-controller-test/create-room-fail-test[snippets="http-response,re operation::chat-controller-test/find-by-username-test[snippets="http-request,query-parameters"] ==== 응답 operation::chat-controller-test/find-by-username-test[snippets="http-response,response-fields"] +=== 채팅방 상세 정보 조회 API +==== 요청 +operation::chat-controller-test/find-chat-room-details-test[snippets="http-request,query-parameters"] +==== 응답 +operation::chat-controller-test/find-chat-room-details-test[snippets="http-response,response-fields"] === 채팅방 채팅내역 조회 API ==== 요청 operation::chat-controller-test/find-chat-message-test[snippets="http-request,query-parameters"] diff --git a/src/main/java/kaboo/kaboochat/chat/config/RedisConfig.java b/src/main/java/kaboo/kaboochat/chat/config/RedisConfig.java index cd4276b..71ee3c1 100644 --- a/src/main/java/kaboo/kaboochat/chat/config/RedisConfig.java +++ b/src/main/java/kaboo/kaboochat/chat/config/RedisConfig.java @@ -1,10 +1,8 @@ package kaboo.kaboochat.chat.config; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; @@ -13,6 +11,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; import kaboo.kaboochat.chat.domain.redis.RedisSubscriber; +import lombok.RequiredArgsConstructor; /** * Redis 설정 클래스 @@ -26,29 +25,10 @@ * @since : 2024/08/17 */ @Configuration +@RequiredArgsConstructor public class RedisConfig { - private final String redisHost; - private final int redisPort; - - public RedisConfig(@Value("${REDIS_HOST}") String redisHost, @Value("${REDIS_PORT}") int redisPort) { - this.redisHost = redisHost; - this.redisPort = redisPort; - } - - /** - * RedisConnectionFactory 빈 생성 - *

- * Lettuce 클라이언트를 사용하여 Redis 서버와의 연결을 생성하고 관리합니다. - *
- * 이 빈은 Redis 서버와의 연결을 생성하고, 다른 Redis 관련 빈들이 이 연결을 사용하여 Redis와 통신하게 됩니다. - *

- * @return RedisConnectionFactory LettuceConnectionFactory 인스턴스 - */ - @Bean - public RedisConnectionFactory redisConnectionFactory() { - return new LettuceConnectionFactory(redisHost, redisPort); - } + private final RedisConnectionFactory redisConnectionFactory; /** * RedisTemplate 빈 생성 @@ -106,7 +86,7 @@ public RedisMessageListenerContainer redisMessageListener( ChannelTopic channelTopic ) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); - container.setConnectionFactory(redisConnectionFactory()); // Redis 연결 팩토리 설정 + container.setConnectionFactory(redisConnectionFactory); // Redis 연결 팩토리 설정 container.addMessageListener(listenerAdapterChatMessage, channelTopic); // 리스너와 채널을 컨테이너에 등록 return container; } diff --git a/src/main/java/kaboo/kaboochat/chat/controller/ChatController.java b/src/main/java/kaboo/kaboochat/chat/controller/ChatController.java index 3a833c2..1a41eed 100644 --- a/src/main/java/kaboo/kaboochat/chat/controller/ChatController.java +++ b/src/main/java/kaboo/kaboochat/chat/controller/ChatController.java @@ -46,6 +46,20 @@ public ResponseEntity>> findChatRoomListByUse .body(ApiResponse.success(responses)); } + /** + * 채팅방의 상세 정보를 불러옵니다. + * + * @param roomUUID 채팅방 UUID + * @return 채팅방 상세 정보 + */ + @GetMapping("/rooms/details") + public ResponseEntity> findChatRoomDetails(String roomUUID) { + ChatRoomResponse response = chatService.findByChatUUID(roomUUID); + + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success(response)); + } + /** * 채팅방을 생성합니다. * diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 52541ec..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=Kaboo-Chat diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..abeabcb --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + application: + name: Kaboo-Chat diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index d3e9b0f..e5c3ad0 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -451,6 +451,7 @@

카부 커뮤니티 채팅 API

  • 채팅방 생성 성공 API
  • 채팅방 생성 실패 API
  • 채팅방 리스트 조회 API
  • +
  • 채팅방 상세 정보 조회 API
  • 채팅방 채팅내역 조회 API
  • @@ -719,15 +720,15 @@
    HTTP response
    "message" : "요청이 성공적으로 처리되었습니다.", "data" : [ { "usernames" : null, - "chatRoomUUID" : "753f04ee-28e6-44c9-be80-1a7620602164", + "chatRoomUUID" : "704569d6-4f67-4113-b240-262d3b478124", "chatRoomName" : "채팅방1" }, { "usernames" : null, - "chatRoomUUID" : "ac7a6331-dbf9-4416-803e-2740cfcb8e35", + "chatRoomUUID" : "aa72340d-9be0-4570-abb6-fa34dbfdd6a8", "chatRoomName" : "채팅방2" }, { "usernames" : null, - "chatRoomUUID" : "e8ef047b-5f7e-468b-8a80-20df87c3590a", + "chatRoomUUID" : "8c203b97-a839-41f2-aacb-fc1c04990b2c", "chatRoomName" : "채팅방3" } ] } @@ -786,14 +787,14 @@
    Response fields
    -

    채팅방 채팅내역 조회 API

    +

    채팅방 상세 정보 조회 API

    요청

    HTTP request
    -
    GET /chat/messages?roomUUID=A1A1-B2B2&page=0&size=10 HTTP/1.1
    +
    GET /chat/rooms/details?roomUUID=AAAA-BBBB-CCCC-DDDD HTTP/1.1
     Host: localhost:8080
    @@ -814,6 +815,114 @@
    Query parameters

    roomUUID

    +

    검색할 채팅방 UUID

    + + + +
    +
    +
    +

    응답

    +
    +
    HTTP response
    +
    +
    +
    HTTP/1.1 200 OK
    +Content-Type: application/json
    +Content-Length: 253
    +
    +{
    +  "success" : true,
    +  "message" : "요청이 성공적으로 처리되었습니다.",
    +  "data" : {
    +    "usernames" : [ "pjh5365", "Justin", "Apple" ],
    +    "chatRoomUUID" : "7555f825-a342-4d77-b6db-4439cebe8e7c",
    +    "chatRoomName" : "채팅방1"
    +  }
    +}
    +
    +
    +
    +
    +
    Response fields
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    success

    Boolean

    성공여부

    message

    String

    응답 메시지

    data

    Object

    채팅방 리스트

    data.usernames

    Array

    참여자 이름 리스트

    data.chatRoomUUID

    String

    채팅방 UUID

    data.chatRoomName

    String

    채팅방 이름

    +
    +
    +
    +
    +

    채팅방 채팅내역 조회 API

    +
    +

    요청

    +
    +
    HTTP request
    +
    +
    +
    GET /chat/messages?roomUUID=A1A1-B2B2&page=0&size=10 HTTP/1.1
    +Host: localhost:8080
    +
    +
    +
    +
    +
    Query parameters
    + ++++ + + + + + + + + + @@ -829,9 +938,9 @@
    Query parameters
    -

    응답

    +

    응답

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -845,34 +954,34 @@ 
    HTTP response
    "username" : "pjh5365", "nickname" : "justin", "message" : "거기는 어때?", - "sendAt" : "2024-08-18T21:01:13.382868" + "sendAt" : "2024-09-01T13:51:00.983825" }, { "username" : "pjh5365", "nickname" : "justin", "message" : "엄청 더워...", - "sendAt" : "2024-08-18T21:01:13.382866" + "sendAt" : "2024-09-01T13:51:00.983822" }, { "username" : "pibber", "nickname" : "park", "message" : "거기 날씨 어때?", - "sendAt" : "2024-08-18T21:01:13.382864" + "sendAt" : "2024-09-01T13:51:00.98382" }, { "username" : "pibber", "nickname" : "park", "message" : "반가워", - "sendAt" : "2024-08-18T21:01:13.38286" + "sendAt" : "2024-09-01T13:51:00.983817" }, { "username" : "pjh5365", "nickname" : "justin", "message" : "안녕", - "sendAt" : "2024-08-18T21:01:13.382837" + "sendAt" : "2024-09-01T13:51:00.983806" } ] }
    -
    Response fields
    +
    Response fields
    ParameterDescription

    roomUUID

    채팅방 UUID

    @@ -933,7 +1042,7 @@
    Response fields
    diff --git a/src/test/java/kaboo/kaboochat/chat/controller/ChatControllerTest.java b/src/test/java/kaboo/kaboochat/chat/controller/ChatControllerTest.java index 397ca21..1753bc4 100644 --- a/src/test/java/kaboo/kaboochat/chat/controller/ChatControllerTest.java +++ b/src/test/java/kaboo/kaboochat/chat/controller/ChatControllerTest.java @@ -84,6 +84,41 @@ void findByUsernameTest() throws Exception { // Then } + @Test + @DisplayName("채팅방 상세 정보 조회") + void findChatRoomDetailsTest() throws Exception { + // Given + ChatRoom chatRoom = ChatRoom.createRoom("채팅방1"); + ChatRoomResponse response = ChatRoomResponse.fromEntity(List.of("pjh5365", "Justin", "Apple"), chatRoom); + given(chatService.findByChatUUID("AAAA-BBBB-CCCC-DDDD")).willReturn(response); + + // When + mockMvc.perform(get("/chat/rooms/details") + .queryParam("roomUUID", "AAAA-BBBB-CCCC-DDDD")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("요청이 성공적으로 처리되었습니다.")) + .andExpect(jsonPath("$.data").exists()) + .andDo(print()) + .andDo(document("{class-name}/{method-name}/", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + queryParameters( + parameterWithName("roomUUID").description("검색할 채팅방 UUID") + ), + responseFields( + fieldWithPath("success").description("성공여부"), + fieldWithPath("message").description("응답 메시지"), + fieldWithPath("data").description("채팅방 리스트"), + fieldWithPath("data.usernames").description("참여자 이름 리스트"), + fieldWithPath("data.chatRoomUUID").description("채팅방 UUID"), + fieldWithPath("data.chatRoomName").description("채팅방 이름") + ))); + + // Then + } + @Test @DisplayName("채팅방 생성") void createRoomTest() throws Exception { diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index cc92137..06fc633 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -14,10 +14,9 @@ spring: ddl-auto: create # 테스트용 도커 컨테이너의 설정에 맞춘다. data: + redis: + host: redis # 도커 네트워크가 아닌 호스트의 주소를 사용 + port: 6379 mongodb: - host: mongodb + host: mongodb # 도커 네트워크가 아닌 호스트의 주소를 사용 port: 27017 - -# 테스트용 도커 컨테이너의 설정에 맞춘다. -REDIS_HOST: redis -REDIS_PORT: 6379