Skip to content

Conversation

@whqtker
Copy link
Member

@whqtker whqtker commented Aug 7, 2025

관련 이슈

작업 내용

WebSocket + STOMP 관련 테스트 코드와 메시지 송신 테스트를 작성하였습니다.
#423 이 머지되면 rebase 하고 머지하겠습니다 !

특이 사항

ArgumentCaptor 라는 걸 처음 써 봤는데요, 어색한 부분이 있다면 말씀해주세요 ~
WebSocket + STOMP 통합 테스트는 chat 도메인 하위가 아니라 따로 작성하였습니다.

리뷰 요구사항 (선택)

추가할 테스트가 있는지

@whqtker whqtker self-assigned this Aug 7, 2025
@whqtker whqtker requested a review from Gyuhyeok99 as a code owner August 7, 2025 04:05
@whqtker whqtker added the 테스트 Added tests label Aug 7, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 7, 2025

Walkthrough

  1. 새로운 WebSocket 핸드셰이크 및 인증 구조 도입
      - CustomHandshakeHandlerWebSocketHandshakeInterceptor가 추가되어, WebSocket 세션에서 Principal(인증 정보) 할당과 핸드셰이크 단계에서 인증 여부 검증이 커스터마이즈되었습니다.
  2. WebSocket 엔드포인트 설정 개선
      - StompWebSocketConfig에 커스텀 핸드셰이크 핸들러와 인터셉터가 등록되어, "/connect" 엔드포인트에서 인증된 사용자만 접속이 가능하도록 설정이 강화되었습니다.
  3. STOMP 핸들러 인증 방식 리팩토링
      - StompHandler는 JWT 직접 파싱 대신 Principal을 활용하여 인증 검증을 수행하도록 변경되었고, 채팅방 구독 시 정규표현식을 이용해 채팅방 ID를 추출 및 검증합니다.
  4. 채팅 메시지 송신 기능 추가
      - ChatMessageController와 DTO(ChatMessageSendRequest, ChatMessageSendResponse)가 신설되어, 인증된 사용자가 채팅방에 메시지를 전송할 수 있는 기능이 구현되었습니다.
  5. 채팅 서비스 메시지 송신 로직 구현
      - ChatService에 메시지 송신 메서드가 추가되고, SimpMessageSendingOperations를 활용해 실시간 메시지 브로드캐스트가 가능해졌습니다.
      - 채팅방 참여자 검증 메서드가 public으로 변경되어 외부에서 활용할 수 있게 되었습니다.
  6. 보안 설정 강화
      - /connect/** 경로에 대해 인증을 요구하는 규칙이 추가되어, WebSocket 연결 시 인증이 필수화되었습니다.
  7. 테스트 코드 확장
      - ChatServiceTest에 채팅 메시지 송신 및 예외 상황 테스트가 추가되었고,
      - WebSocketStompIntegrationTest를 통해 WebSocket/STOMP 인증 및 핸드셰이크 성공/실패 케이스의 통합 테스트가 신설되었습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

Suggested reviewers

  • Gyuhyeok99

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4391ba and eae5e66.

📒 Files selected for processing (1)
  • src/test/java/com/example/solidconnection/chat/service/ChatServiceTest.java (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/com/example/solidconnection/chat/service/ChatServiceTest.java
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
src/main/java/com/example/solidconnection/chat/config/CustomHandshakeHandler.java (1)

14-26: WebSocket 세션 Principal 할당 로직이 안전하게 구현되었습니다!

구현의 장점들:

  1. 안전한 타입 체크: instanceof로 Principal 타입 확인 후 캐스팅
  2. 적절한 폴백: 기본 동작으로 안전하게 처리
  3. 명확한 역할: 핸드셰이크 인터셉터에서 설정한 "user" 속성 활용

아래와 같이 코드를 약간 간소화할 수 있습니다:

 Object userAttribute = attributes.get("user");

-if (userAttribute instanceof Principal) {
-    Principal principal = (Principal) userAttribute;
-    return principal;
-}
+if (userAttribute instanceof Principal principal) {
+    return principal;
+}

 return super.determineUser(request, wsHandler, attributes);
src/main/java/com/example/solidconnection/chat/config/WebSocketHandshakeInterceptor.java (1)

20-25: 로깅 추가로 디버깅을 개선해보세요.

핸드셰이크 실패 시 로깅을 추가하면 디버깅에 도움이 될 것 같습니다.

        if (principal != null) {
            attributes.put("user", principal);
            return true;
        }

+        log.debug("WebSocket handshake failed: no authenticated principal found");
        return false;
src/test/java/com/example/solidconnection/websocket/WebSocketStompIntegrationTest.java (1)

67-113: 추가 테스트 케이스 고려사항

현재 테스트는 핸드셰이크만 검증하고 있습니다. STOMP 구독과 메시지 전송에 대한 통합 테스트도 추가하면 더 완전한 커버리지를 얻을 수 있을 것 같습니다.

STOMP 구독 및 메시지 전송 테스트 코드를 생성해드릴까요?

src/test/java/com/example/solidconnection/chat/service/ChatServiceTest.java (1)

376-423: 테스트 커버리지 추가 고려사항

현재 테스트는 기본적인 성공/실패 케이스를 다루고 있습니다. 다음과 같은 추가 테스트 케이스를 고려해보시기 바랍니다:

  1. 빈 메시지 내용 전송 시 검증
  2. 존재하지 않는 채팅방에 메시지 전송
  3. 메시지 길이 제한 검증

추가 테스트 케이스 구현을 도와드릴까요?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f4a24a and d4391ba.

📒 Files selected for processing (11)
  • src/main/java/com/example/solidconnection/chat/config/CustomHandshakeHandler.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/StompHandler.java (2 hunks)
  • src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/WebSocketHandshakeInterceptor.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/controller/ChatMessageController.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/dto/ChatMessageSendRequest.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/dto/ChatMessageSendResponse.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/service/ChatService.java (5 hunks)
  • src/main/java/com/example/solidconnection/security/config/SecurityConfiguration.java (2 hunks)
  • src/test/java/com/example/solidconnection/chat/service/ChatServiceTest.java (4 hunks)
  • src/test/java/com/example/solidconnection/websocket/WebSocketStompIntegrationTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: nayonsoso
PR: solid-connection/solid-connect-server#400
File: src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java:20-22
Timestamp: 2025-07-23T18:07:08.348Z
Learning: SockJS configuration should be added to STOMP endpoints using .withSockJS() for better browser compatibility and network environment support, even if frontend doesn't initially use SockJS client libraries.
📚 Learning: sockjs configuration should be added to stomp endpoints using .withsockjs() for better browser compa...
Learnt from: nayonsoso
PR: solid-connection/solid-connect-server#400
File: src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java:20-22
Timestamp: 2025-07-23T18:07:08.348Z
Learning: SockJS configuration should be added to STOMP endpoints using .withSockJS() for better browser compatibility and network environment support, even if frontend doesn't initially use SockJS client libraries.

Applied to files:

  • src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java
  • src/test/java/com/example/solidconnection/websocket/WebSocketStompIntegrationTest.java
🔇 Additional comments (20)
src/main/java/com/example/solidconnection/security/config/SecurityConfiguration.java (2)

9-9: 임포트 순서 정리 완료!

코드 정리가 잘 되었습니다.


65-65: WebSocket 엔드포인트 보안 설정 추가가 적절합니다

/connect/** 경로에 대한 인증 요구사항 추가가 올바르게 구현되었습니다. 다음과 같은 장점들이 있습니다:

  1. 보안 강화: WebSocket 연결 시 인증된 사용자만 접근 가능
  2. 올바른 순서: admin 규칙보다 먼저 배치되어 적절한 보안 필터 체인 구성
  3. 통합성: 새로 추가된 WebSocket 핸드셰이크 인터셉터와 완벽하게 연동
src/main/java/com/example/solidconnection/chat/dto/ChatMessageSendRequest.java (1)

6-12: 채팅 메시지 요청 DTO 구현이 깔끔합니다!

다음과 같은 좋은 구현 특징들이 있습니다:

  1. 적절한 유효성 검증:
    • @NotNull로 필수값 검증
    • @Size(max = 500)로 메시지 길이 제한
  2. 명확한 에러 메시지: 사용자 친화적인 한국어 메시지 제공
  3. 모던 자바 활용: Record 클래스로 간결한 구조

채팅 메시지 전송 기능의 견고한 기반이 될 것 같습니다.

src/main/java/com/example/solidconnection/chat/dto/ChatMessageSendResponse.java (1)

5-19: 채팅 메시지 응답 DTO 설계가 우수합니다!

구현의 장점들을 정리하면:

  1. 적절한 필드 선택:
    • messageId: 메시지 식별을 위한 필수 정보
    • content: 메시지 내용
    • senderId: 발신자 식별 정보
  2. 팩토리 메서드 패턴: from() 메서드로 도메인 객체에서 깔끔한 변환
  3. 캡슐화: 도메인 객체의 내부 구조를 숨기고 필요한 정보만 노출

WebSocket을 통한 실시간 메시지 브로드캐스트에 최적화된 구조입니다.

src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java (2)

25-26: WebSocket 인증 컴포넌트 의존성 주입이 적절합니다

새로운 인증 관련 컴포넌트들이 올바르게 추가되었습니다:

  • WebSocketHandshakeInterceptor: 핸드셰이크 시 인증 정보 추출
  • CustomHandshakeHandler: WebSocket 세션의 Principal 할당

32-36: STOMP 엔드포인트 설정이 보안과 호환성을 모두 고려하여 완벽합니다!

엔드포인트 구성의 우수한 점들:

  1. 보안 강화:

    • addInterceptors(webSocketHandshakeInterceptor): 핸드셰이크 시 인증 정보 검증
    • setHandshakeHandler(customHandshakeHandler): Principal 할당으로 세션 인증 상태 관리
  2. 호환성 보장:

    • .withSockJS(): 다양한 브라우저와 네트워크 환경 지원 (이전 리뷰 학습 내용 반영 완료)
  3. CORS 설정: 허용된 오리진 적절히 구성

인증된 사용자만 WebSocket에 연결할 수 있는 견고한 구조가 완성되었습니다.

src/main/java/com/example/solidconnection/chat/controller/ChatMessageController.java (1)

21-31: 메시지 전송 로직이 깔끔하게 구현되었습니다.

WebSocket 엔드포인트 매핑과 검증 로직이 적절히 구성되어 있고, 채팅 서비스와의 연동도 명확합니다.

src/main/java/com/example/solidconnection/chat/config/WebSocketHandshakeInterceptor.java (1)

15-26: 핸드셰이크 인터셉터 로직이 올바르게 구현되었습니다.

  1. 인증된 Principal 추출 및 저장
  2. 인증되지 않은 사용자의 연결 차단

이 구조가 WebSocket 보안을 적절히 강화하고 있습니다.

src/test/java/com/example/solidconnection/websocket/WebSocketStompIntegrationTest.java (3)

79-96: 인증된 사용자 테스트가 체계적으로 구성되었습니다.

  1. JWT 토큰 생성
  2. Authorization 헤더 설정
  3. 핸드셰이크 성공 검증
  4. 전송 에러 부재 확인

테스트 로직이 명확하고 검증 항목이 적절합니다.


98-112: 인증 실패 테스트가 정확히 구현되었습니다.

  1. Authorization 헤더 없이 연결 시도
  2. HTTP 401 Unauthorized 예외 발생 확인
  3. 전송 에러 발생 검증

예외 처리와 에러 상황 테스트가 잘 구성되어 있습니다.


55-57: SockJS 설정 적용 확인 – 리뷰 지적 무시 요망

  1. 확인된 설정
    • StompWebSocketConfig.java의 registry.addEndpoint("/connect") 호출 체인에 이미 .withSockJS()가 포함되어 있습니다.
  2. 결론
    • SockJS 구성은 이미 적용되어 있어 추가 설정이 필요 없습니다.

Likely an incorrect or invalid review comment.

src/main/java/com/example/solidconnection/chat/config/StompHandler.java (2)

33-36: Principal 인증 검증 로직이 일관성 있게 적용되었습니다.

CONNECT와 SUBSCRIBE 모두에서 Principal 존재 여부를 확인하는 것이 보안상 적절합니다.

Also applies to: 40-43


25-25: 정규표현식을 사용한 roomId 추출이 깔끔합니다.

이전 split 방식보다 정규표현식을 사용한 패턴 매칭이 더 안전하고 명확한 검증을 제공합니다.

Also applies to: 62-67

src/test/java/com/example/solidconnection/chat/service/ChatServiceTest.java (3)

71-72: SimpMessagingTemplate 모킹 설정이 적절합니다.

WebSocket 메시지 전송을 테스트하기 위한 MockBean 추가가 올바르게 구성되었습니다.


376-410: 채팅방 참여자 메시지 전송 테스트가 체계적으로 구성되었습니다.

  1. 메시지 전송 요청 생성
  2. chatService.sendChatMessage 호출
  3. ArgumentCaptor로 destination과 payload 검증
  4. 메시지 내용과 발신자 ID 확인

테스트 구조가 명확하고 검증 항목이 완전합니다.


412-422: 비참여자 메시지 전송 제한 테스트가 정확합니다.

채팅방 참여자가 아닌 사용자의 메시지 전송 시도 시 CHAT_PARTICIPANT_NOT_FOUND 예외가 발생하는 것을 올바르게 검증하고 있습니다.

src/main/java/com/example/solidconnection/chat/service/ChatService.java (4)

13-14: 새로운 DTO 임포트 추가 승인

채팅 메시지 송신 기능을 위한 ChatMessageSendRequestChatMessageSendResponse DTO 임포트가 적절하게 추가되었습니다.


29-29: WebSocket 메시징을 위한 의존성 주입 적절함

  1. @Lazy 애노테이션과 SimpMessageSendingOperations 의존성 추가가 올바르게 구현되었습니다
  2. 순환 의존성 방지를 위한 @Lazy 사용이 적절합니다

Also applies to: 32-32, 45-45


110-115: 메서드 가시성 변경 승인

validateChatRoomParticipant 메서드가 private에서 public으로 변경되었습니다. 이는 StompHandler에서 채팅방 구독 권한 검증을 위해 필요한 적절한 변경사항입니다.


47-59: 검증 완료: 명시적 생성자 구현이 올바르게 적용되었습니다.

  1. Lombok 어노테이션 제거
  2. 필드 초기화 확인
    • 선언된 모든 final 필드가 생성자 내부에서 정확히 초기화됩니다.

변경사항이 의도대로 반영되었으므로 추가 검토는 필요하지 않습니다. 😊

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

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

저도 ArgumentCaptor는 처음보는데 sendChatMessage를 테스트하기가 쉽지 않았네요.. 간단한 리뷰만 남겨놓아서 Approve해놓겠습니다!

ArgumentCaptor<String> destinationCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ChatMessageSendResponse> payloadCaptor = ArgumentCaptor.forClass(ChatMessageSendResponse.class);

Mockito.verify(simpMessagingTemplate).convertAndSend(destinationCaptor.capture(), payloadCaptor.capture());
Copy link
Contributor

Choose a reason for hiding this comment

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

Mockito.verify(simpMessagingTemplate).convertAndSend(destinationCaptor.capture(), payloadCaptor.capture());

이부분만 import static org.mockito.BDDMockito.verify;

Mocktio가 아닌 BDDMocito로 변경해주실 수 있나요?

Copy link
Member Author

Choose a reason for hiding this comment

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

변경했습니다 ! Mockito 대신 BDDMockito 를 import하도록 변경했습니다
eae5e66

@whqtker whqtker closed this Aug 11, 2025
@whqtker whqtker force-pushed the test/427-websocket-test branch from eae5e66 to 5a2ad88 Compare August 11, 2025 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

테스트 Added tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: WebSocket 관련 테스트 코드 작성

2 participants