Skip to content

Conversation

@lsy1307
Copy link
Contributor

@lsy1307 lsy1307 commented Jul 23, 2025

관련 이슈

작업 내용

  • build.gradle 의존성을 추가하였습니다.
  • ErrorCode를 추가하였습니다.
  • config를 작성하였습니다.
  • 규혁님 코드를 좀 참고했습니다! 이미지 크기는 임의로 10MB까지로 설정하고 그에 따라 버퍼 크기와 Timeout을 설정하였습니다.

특이 사항

리뷰 요구사항 (선택)

아직 STOMP에 대한 이해가 부족해서... 코드에 문제가 있으면 반드시 알려주세요!
이미지 크기 제한은 논의가 필요해 보입니당

- build.gradle 의존성 추가
- ErrorCode 추가
- config 작성
@lsy1307 lsy1307 self-assigned this Jul 23, 2025
@lsy1307 lsy1307 requested a review from wibaek as a code owner July 23, 2025 14:11
@lsy1307 lsy1307 added the 기능 label Jul 23, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 23, 2025

"""

Walkthrough

  1. 빌드 설정 변경
     - build.gradle 파일에 org.springframework.boot:spring-boot-starter-websocket 의존성이 추가되어, Spring Boot에서 WebSocket 기능을 사용할 수 있도록 설정되었습니다.

  2. STOMP 이벤트 리스너 추가
     - StompEventListener 클래스가 새로 도입되어, WebSocket STOMP 세션의 연결 및 해제 이벤트를 감지하고, 활성 세션 ID를 스레드 안전한 Set에 관리하며, 이벤트 발생 시마다 세션 ID와 전체 세션 수를 로그로 남깁니다.

  3. STOMP 메시지 인터셉터 추가
     - StompHandler 클래스가 추가되어, ChannelInterceptor를 구현합니다.
     - 이 클래스는 STOMP의 CONNECTSUBSCRIBE 명령을 가로채 JWT 토큰을 검증하고, 구독 시 roomId를 추출하여 사용자의 구독 시도를 로그로 남깁니다.
     - 토큰 검증 실패나 roomId 추출 실패 시 커스텀 예외를 발생시키며, 구독 권한 체크를 위한 위치에 주석이 추가되어 있습니다.

  4. WebSocket 및 STOMP 구성 클래스 추가
     - StompWebSocketConfig 클래스가 신설되어, WebSocket 및 STOMP 메시지 브로커를 설정합니다.
     - /connect 엔드포인트를 등록하고, SockJS와 동적 CORS 설정을 적용하며, /publish/topic 경로 설정, 하트비트 및 스레드풀 관련 설정값을 프로퍼티로 주입받아 적용합니다.
     - 인바운드/아웃바운드 채널에 스레드풀 및 인터셉터를 등록하고, 전용 스레드풀 태스크 스케줄러로 하트비트를 관리합니다.

  5. 에러 코드 추가
     - ErrorCode enum에 소켓 관련 에러 코드 두 개(UNAUTHORIZED_SUBSCRIBE, INVALID_ROOM_ID)가 추가되어, 각각 구독 권한 없음(403)과 잘못된 roomId(400)에 대응합니다.

  6. WebSocket 설정 프로퍼티 추가
     - StompProperties라는 새로운 구성 프로퍼티 클래스가 추가되어, WebSocket 관련 스레드풀과 하트비트 설정을 타입 안전하게 관리합니다.

  7. 설정 파일 확장
     - application.ymlwebsocket 섹션이 추가되어, 인바운드/아웃바운드 스레드풀 크기와 하트비트 간격이 명시적으로 설정되었습니다.

  8. 서브프로젝트 커밋 업데이트
     - src/main/resources/secret 파일 내 서브프로젝트 커밋 참조가 최신 커밋으로 변경되었습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20분

  • 변경 범위는 새로운 WebSocket/STOMP 기능 도입과 관련된 여러 클래스 및 설정 추가로, 중간 정도의 복잡도와 검토 시간이 예상됩니다.
  • 신규 클래스의 로직, 토큰 검증, 예외 처리, 스레드풀 및 하트비트 설정 등 세부 동작을 꼼꼼히 확인해야 합니다.
    """

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 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 f6f9672 and 6ebfec7.

📒 Files selected for processing (1)
  • src/main/resources/secret (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/resources/secret
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
✨ Finishing Touches
🧪 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.
    • @coderabbitai modularize this function.
  • 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.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

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: 8

🧹 Nitpick comments (3)
build.gradle (1)

67-68: WebSocket 및 STOMP 의존성 추가가 적절합니다!

  1. spring-boot-starter-websocket: Spring Boot의 WebSocket 지원을 위한 핵심 의존성
  2. stomp-websocket: STOMP 프로토콜 클라이언트 기능 제공

다만 stomp-websocket 의존성에 버전을 명시하는 것을 권장합니다:

-    implementation 'org.webjars:stomp-websocket'
+    implementation 'org.webjars:stomp-websocket:2.3.4'
src/main/java/com/example/solidconnection/chat/config/StompEventListener.java (1)

13-34: STOMP 세션 이벤트 리스너 구현이 깔끔합니다!

  1. ConcurrentHashMap.newKeySet() 사용으로 스레드 안전성 확보
  2. 연결/해제 이벤트 로깅으로 세션 상태 추적 가능
  3. Spring 이벤트 리스너 패턴을 올바르게 활용

세션 관리 기능을 확장할 경우를 대비해 다음과 같은 개선사항을 고려해볼 수 있습니다:

// 세션 정보를 더 상세히 관리하고 싶다면
private final Map<String, SessionInfo> sessionDetails = new ConcurrentHashMap<>();

// 또는 세션 정리 스케줄러 추가
@Scheduled(fixedRate = 300000) // 5분마다
public void cleanupStaleSession() {
    // 비활성 세션 정리 로직
}
src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java (1)

31-45: 스레드 풀 설정에 대한 검토가 필요합니다

현재 스레드 풀 설정:

  1. Inbound: 코어 6, 최대 12, 큐 용량 1000
  2. Outbound: 코어 6, 최대 12, 큐 용량 미지정

다음 사항을 고려하세요:

  • 예상 동시 접속자 수에 따른 적절한 크기 산정
  • Outbound 채널의 큐 용량 설정 추가
  • PR에서 언급된 10MB 이미지 제한과의 연관성

부하 테스트를 통해 적절한 값을 산정하고, Outbound 채널에도 큐 용량을 설정하세요:

 registration.taskExecutor()
         .corePoolSize(6)
-        .maxPoolSize(12);
+        .maxPoolSize(12)
+        .queueCapacity(1000);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 60ee7b9 and e8383b1.

📒 Files selected for processing (7)
  • build.gradle (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/StompEventListener.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/StompHandler.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/WebSocketConfig.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/WebSocketHandler.java (1 hunks)
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1 hunks)
🔇 Additional comments (5)
src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1)

114-116: WebSocket 구독 권한 에러 코드 추가가 적절합니다!

  1. HTTP 403 상태 코드 사용이 구독 권한 거부에 적합함
  2. 한글 메시지가 명확하고 일관성 있음
  3. 새로운 "socket" 섹션으로 체계적으로 분류됨

STOMP 핸들러에서의 JWT 토큰 검증 실패 시 활용될 것으로 보입니다.

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

17-23: 세션 관리 구현이 적절합니다!

  1. 스레드 안전한 ConcurrentHashMap.newKeySet() 사용
  2. 연결 시 세션 추가 및 로깅 처리가 올바름
  3. 세션 ID 로깅으로 디버깅 지원

36-41: 연결 해제 처리가 깔끔합니다!

세션 정리와 상태 로깅이 적절하게 구현되어 있습니다. CloseStatus 정보까지 로깅하여 연결 해제 원인 추적이 가능합니다.

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

11-16: WebSocket 설정 클래스 구조가 적절합니다!

  1. @Configuration@EnableWebSocket 어노테이션 올바른 사용
  2. WebSocketConfigurer 인터페이스 구현으로 표준 패턴 준수
  3. 의존성 주입을 통한 핸들러 연결이 깔끔함

24-31: 실제 이미지 전송 요구사항 및 메모리 영향 검토 요청.
현재 WebSocket 버퍼 크기가 10MB로 하드코딩되어 있습니다.
이 크기가 이미지 전송을 위한 실제 요구사항을 충족하는지 확인해 주세요.
동시에 여러 연결이 발생했을 때 전체 메모리 사용량이 어떻게 변하는지 검토 부탁드립니다.
큰 메시지 전송 시 네트워크 타임아웃이나 연결 불안정 현상이 발생하지 않는지도 함께 살펴봐 주세요.
운영 환경별로 유연하게 조정할 수 있도록 설정 값을 외부화하는 것을 추천합니다.

변경 가이드라인을 아래에 정리합니다.

  1. 실제 요구사항 확인
     - 이미지 1회 전송 시 최대 크기를 측정합니다.
  2. 메모리 사용량 분석
     - 동시 연결 수 × 버퍼 크기를 기반으로 예상 메모리 사용량을 계산합니다.
  3. 네트워크 안정성 테스트
     - 최대 크기 메시지 전송 시 타임아웃 또는 연결 끊김 여부를 점검합니다.
  4. 설정 외부화
    java  @Value("${websocket.max-text-buffer-size:1048576}") // 1MB 기본값  private int maxTextBufferSize;    @Value("${websocket.max-binary-buffer-size:1048576}")  private int maxBinaryBufferSize;    @Value("${websocket.session-timeout:300000}")  private long sessionTimeout;  

Comment on lines 30 to 44
if(StompCommand.CONNECT == accessor.getCommand()){
log.info("connect요청시 토큰 유효성 검증");

String bearerToken = accessor.getFirstNativeHeader("Authorization");

if(bearerToken == null || !bearerToken.startsWith("Bearer ")) {
throw new IllegalArgumentException("Authorization header missing or invalid format");
}

String token = bearerToken.substring(7);

Claims claims = jwtTokenProvider.parseClaims(token);

log.info("토큰 검증 성공, 유저 인증 완료 - 사용자: {}", claims.getSubject());
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

인증 정보를 SecurityContext에 저장해야 합니다

CONNECT 시 JWT 토큰 검증은 성공적으로 수행되고 있지만, 인증된 사용자 정보를 Spring Security의 SecurityContext나 STOMP 세션에 저장하지 않고 있습니다. 이로 인해 후속 메시지 처리 시 사용자 컨텍스트를 활용할 수 없습니다.

다음과 같이 인증 정보를 저장하는 것을 권장합니다:

 Claims claims = jwtTokenProvider.parseClaims(token);
 
 log.info("토큰 검증 성공, 유저 인증 완료 - 사용자: {}", claims.getSubject());
+
+// 사용자 정보를 STOMP 세션 속성에 저장
+accessor.getSessionAttributes().put("userEmail", claims.getSubject());
+accessor.setUser(() -> claims.getSubject());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if(StompCommand.CONNECT == accessor.getCommand()){
log.info("connect요청시 토큰 유효성 검증");
String bearerToken = accessor.getFirstNativeHeader("Authorization");
if(bearerToken == null || !bearerToken.startsWith("Bearer ")) {
throw new IllegalArgumentException("Authorization header missing or invalid format");
}
String token = bearerToken.substring(7);
Claims claims = jwtTokenProvider.parseClaims(token);
log.info("토큰 검증 성공, 유저 인증 완료 - 사용자: {}", claims.getSubject());
}
if (StompCommand.CONNECT == accessor.getCommand()) {
log.info("connect요청시 토큰 유효성 검증");
String bearerToken = accessor.getFirstNativeHeader("Authorization");
if (bearerToken == null || !bearerToken.startsWith("Bearer ")) {
throw new IllegalArgumentException("Authorization header missing or invalid format");
}
String token = bearerToken.substring(7);
Claims claims = jwtTokenProvider.parseClaims(token);
log.info("토큰 검증 성공, 유저 인증 완료 - 사용자: {}", claims.getSubject());
// 사용자 정보를 STOMP 세션 속성에 저장
accessor.getSessionAttributes().put("userEmail", claims.getSubject());
accessor.setUser(() -> claims.getSubject());
}
🤖 Prompt for AI Agents
In src/main/java/com/example/solidconnection/chat/config/StompHandler.java
around lines 30 to 44, after successfully validating the JWT token, you need to
store the authenticated user information in the Spring Security SecurityContext.
To fix this, create an Authentication object using the user details extracted
from the token claims, then set this Authentication into the
SecurityContextHolder. This ensures that the user context is available for
subsequent message handling.

String roomId = destination.split("/")[2];
log.info("사용자 {} 가 룸 {} 구독 시도", email, roomId);

// todo: room 검증로직 구현
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

TODO: 채팅방 접근 권한 검증 구현 필수

채팅방 구독 시 사용자가 해당 채팅방에 접근 권한이 있는지 검증하는 로직이 누락되어 있습니다. 이는 보안상 중요한 이슈입니다.

채팅방 접근 권한 검증 로직을 구현하시겠습니까? 아니면 이를 추적하기 위한 새 이슈를 생성할까요?

🤖 Prompt for AI Agents
In src/main/java/com/example/solidconnection/chat/config/StompHandler.java at
line 67, the code lacks the implementation for verifying if a user has access
rights to a chat room upon subscription. To fix this, implement a validation
method that checks the user's permissions against the chat room ID before
allowing the subscription to proceed. This method should be called at the
indicated line to enforce access control and prevent unauthorized access.

Comment on lines 24 to 29
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/publish");
registry.enableSimpleBroker("/topic")
.setHeartbeatValue(new long[]{15000, 15000});
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

메시지 크기 제한 설정이 필요합니다

PR에서 언급된 10MB 이미지 크기 제한이 여기에 구성되어 있지 않습니다. STOMP 메시지 크기 제한을 설정해야 합니다.

다음과 같이 메시지 크기 제한을 추가하세요:

 @Override
 public void configureMessageBroker(MessageBrokerRegistry registry) {
     registry.setApplicationDestinationPrefixes("/publish");
     registry.enableSimpleBroker("/topic")
-            .setHeartbeatValue(new long[]{15000, 15000});
+            .setHeartbeatValue(new long[]{15000, 15000})
+            .setTaskScheduler(taskScheduler()); // 필요시 스케줄러 추가
+    
+    // 메시지 크기 제한 설정 (10MB)
+    registry.setCacheLimit(1024 * 1024 * 10);
 }

또한 WebSocketTransportRegistration을 통해 더 세밀한 제어가 가능합니다:

@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
    registration.setMessageSizeLimit(10 * 1024 * 1024); // 10MB
    registration.setSendBufferSizeLimit(10 * 1024 * 1024);
    registration.setSendTimeLimit(20 * 1000); // 20초
}
🤖 Prompt for AI Agents
In
src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java
around lines 24 to 29, the STOMP message size limit is not configured, which is
necessary to enforce the 10MB image size restriction mentioned in the PR. Add a
configureWebSocketTransport method to the class that sets the message size limit
and send buffer size limit to 10MB (10 * 1024 * 1024 bytes) and the send time
limit to 20 seconds (20 * 1000 milliseconds) using the
WebSocketTransportRegistration parameter.

Comment on lines 20 to 22
registry.addHandler(webSocketHandler, "/connect")
.setAllowedOriginPatterns("*"); // todo: 사용하는 도메인으로 한정
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Origin 패턴 보안 설정을 우선적으로 개선해야 합니다.

현재 모든 Origin을 허용하는 설정("*")은 보안상 위험합니다:

  1. CSRF 공격 위험성
  2. 의도하지 않은 도메인에서의 WebSocket 연결 허용
  3. 프로덕션 환경에서 심각한 보안 취약점

다음과 같이 개선을 권장합니다:

// 개발 환경과 프로덕션 환경을 분리
@Value("${websocket.allowed-origins:http://localhost:3000,https://yourdomain.com}")
private String[] allowedOrigins;

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry.addHandler(webSocketHandler, "/connect")
            .setAllowedOrigins(allowedOrigins);
}
🤖 Prompt for AI Agents
In src/main/java/com/example/solidconnection/chat/config/WebSocketConfig.java
around lines 20 to 22, the WebSocket handler currently allows all origins with
setAllowedOriginPatterns("*"), which poses security risks. Replace this with a
configurable allowedOrigins array injected via @Value from application
properties, and use setAllowedOrigins(allowedOrigins) instead. This change
restricts allowed origins to trusted domains, improving security by preventing
CSRF and unauthorized WebSocket connections.

Comment on lines 26 to 34
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
log.info("Received message : {}", payload);
for(WebSocketSession s : sessions) {
if(s.isOpen()) {
s.sendMessage(new TextMessage(payload));
}
}
}
Copy link

@coderabbitai coderabbitai bot Jul 23, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

메시지 브로드캐스팅 로직에 대한 검토가 필요합니다.

현재 구현은 모든 연결된 세션에 메시지를 브로드캐스팅하는데, 이는 다음과 같은 우려사항이 있습니다:

  1. 보안 및 프라이버시: 모든 사용자가 다른 사용자의 메시지를 받을 수 있음
  2. 성능: 대량의 세션이 있을 때 성능 저하 가능성
  3. 비즈니스 로직: 실제 채팅 애플리케이션에서는 특정 방이나 사용자에게만 메시지 전송이 필요

다음과 같은 개선을 권장합니다:

// 예시: 룸 기반 메시징
private final Map<String, Set<WebSocketSession>> roomSessions = new ConcurrentHashMap<>();

protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    try {
        String payload = message.getPayload();
        log.info("Received message : {}", payload);
        
        // JSON 파싱하여 대상 룸이나 사용자 확인
        // 권한 검증 로직 추가
        // 특정 대상에게만 메시지 전송
        
    } catch (Exception e) {
        log.error("Error handling message", e);
        // 에러 응답 전송
    }
}
🤖 Prompt for AI Agents
In src/main/java/com/example/solidconnection/chat/config/WebSocketHandler.java
around lines 26 to 34, the current handleTextMessage method broadcasts incoming
messages to all connected sessions, which raises security, privacy, and
performance concerns. Refactor this method to implement room-based messaging by
maintaining a concurrent map of room IDs to session sets. Parse the incoming
message payload to identify the target room or user, add authorization checks to
ensure message delivery only to permitted recipients, and send the message only
to sessions within the specified room. Also, add exception handling to log
errors and send error responses as needed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

저도 이게 궁금했어요. 전체 session 에 대해서 for 문을 돌면
서버와 연결된 모든 세션에 브로드케스트될 것 같아요.

제가 잘못이해하는 부분이 있다면 말씀해주세요.!

Copy link

Choose a reason for hiding this comment

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

@nayonsoso 맞습니다! 이해하신 것이 정확해요.

현재 코드의 handleTextMessage 메서드에서:

for(WebSocketSession s : sessions) {
    if(s.isOpen()) {
        s.sendMessage(new TextMessage(payload));
    }
}

이 부분은 정말로 서버에 연결된 모든 세션에 브로드캐스트하고 있어요.

즉, 사용자 A가 메시지를 보내면 → 서버에 연결된 사용자 B, C, D... 모든 사용자가 그 메시지를 받게 되는 구조입니다.

1. 현재 구현의 문제점들:

  • 보안: 모든 사용자가 다른 사용자의 메시지를 볼 수 있음
  • 성능: 사용자가 많아질수록 하나의 메시지가 모든 연결에 전송됨
  • 비즈니스 로직: 실제 채팅앱에서는 특정 방이나 상대방에게만 메시지를 보내야 함

2. 개선이 필요한 부분:

  • 방(room) 기반 메시징 시스템
  • 메시지 대상 식별 로직
  • 권한 검증

잘못 이해하신 부분은 전혀 없어요. 오히려 중요한 문제점을 정확하게 파악하신 거예요! 🐰

Copy link
Contributor Author

@lsy1307 lsy1307 Jul 24, 2025

Choose a reason for hiding this comment

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

이 부분은 우선 삭제하고 config가 아니라 서비스로 옮기면서 로직 수정하도록 하겠습니다!

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 Jul 25, 2025

Choose a reason for hiding this comment

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

아마 제 코드 참고해서 작성하다가 추가하신 거 같은데 stomp없이 순수 웹소켓으로 한 번 먼저 구현해보고싶어서 작성해놓았었습니다!
거기서 삭제하면 테스트해본 게 아까워서 @component만 주석처리해놓았었는데 설명을 조금 달아놓을 걸 그랬네요 ㅎㅎ..
참고

Copy link
Collaborator

@nayonsoso nayonsoso left a comment

Choose a reason for hiding this comment

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

안녕하세요 수연님!

궁금한 내용 위주로 코멘트를 달았습니다.
저도 소켓에 대해서는 잘 알지 못하는 부분들이 많은데, 이 PR에서 같이 이야기하며 답을 찾아갈 수 있으면 좋겠습니다~


이미지 크기 제한은 논의가 필요해 보입니당

저도 이번에 GPT와 Claude한테 물어봤습니다.

image image

제 생각에는 10MB vs 5MB 사이에서 선택하면 될 것 같아요.

  • 우리 서비스의 규모를 생각한다면 5MB
  • 혹시 모르는 경우)까지 대비하려 한다면 10MB

제 포트폴리오가 4MB 정도더라고요. (pdf 약 10장)
교환학생과 관련된 정보를 pdf 10 장보다 큰 파일로 공유할 것 같진 않습니다.
이걸 기준으로 생각해봤을 때, 5MB 도 괜찮을 것 같습니다. 🧐
5MB를 넘어가는 상황에 대한 최선은 "업로드할 수 있는 파일의 크기를 초과했습니다"라고 예외를 응답하는게 최선이 아닐까 싶습니다.!


❗️그리고 추가된 파일들에 대해서 cmd + shift + option + L 로 포매팅해주세요❗️

Comment on lines 18 to 22
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/connect")
.setAllowedOriginPatterns("*"); // todo: 사용하는 도메인으로 한정
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/connect")
.setAllowedOriginPatterns("*"); // todo: 사용하는 도메인으로 한정
}
private final CorsProperties corsProperties;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
List<String> strings = corsProperties.allowedOrigins();
String[] allowedOrigins = strings.toArray(String[]::new);
registry.addHandler(webSocketHandler, "/connect")
.setAllowedOrigins(allowedOrigins);
}

이렇게 하면 어떨까요?
그럼 application-variable.yml 에 있는 cors.allowed-origins 에 있는 목록을 가져올 수 있습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

오호 그 부분은 확인 못했네요 반영하겠습니다~!

@Slf4j
public class WebSocketHandler extends TextWebSocketHandler {

private final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();
Copy link
Collaborator

Choose a reason for hiding this comment

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

ConcurrentHashMap.newKeySet(); 이라는 자료구조를 선택하신 이유가 있으신가요?

동시성 문제를 방지하기 위한 다른 Set 컬렉션들 중에서,
e.g. Collections.synchronizedSet(new HashSet<>());

특히나 ConcurrentHashMap.newKeySet() 을 선택하신 이유가 궁금해요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ConcurrentHashMap.newKeySet()이 segment lock을 활용하여 실시간성에서 이점이 크다고 생각했고요 제가 알기로 읽기에 최적화 되어있다고 알고 있습니다! 혹시 잘못 알고 있는 거라면 알려주시면 감사하겠습니당

Copy link
Contributor

Choose a reason for hiding this comment

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

아마 근데 Set 내에 WebSocketSession는 thread-safe하지 않은 것으로 알고 있어서 이 부분에 대해서 테스트를 좀 진행해보면 좋을 거 같습니다!

Copy link
Contributor

Choose a reason for hiding this comment

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

저도 Collections.synchronizedSet 이건 성능저하가 너무 심해서 레거시인 것으로 알고 있긴 한데 확인 한 번 해보겠습니다

Comment on lines 19 to 23
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
log.info("Connected : {}", session.getId());
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

이번 구현에서 수연님이 여러군데에 로깅을 하셨더라고요.
로깅은 어떤 기준으로 추가하셨나요?

Copy link
Contributor Author

@lsy1307 lsy1307 Jul 24, 2025

Choose a reason for hiding this comment

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

규혁님 코드를 활용하면서 로깅을 그대로 채용했는데요 connect/disconnect과정의 모든 과정에서 로그를 확인할 수 있도록 하셨더라구요. 클로드에서 실시간 서비스는 문제가 생기지 않도록 계속 모니터링 하는 게 중요하다고 해서 괜찮다고 생각하여 그대로 유지하였습니다 혹시 문제가 될까요...?

Copy link
Contributor

Choose a reason for hiding this comment

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

저는 테스트용이었어서 그냥 잘 작동하는지 확인하기 위해 많은 곳에 로그를 찍어놨었습니다 ㅎㅎ.. 정말 필요한 부분만 찍으셔도 좋을 거 같습니다!
만약 다 필요하다면 그거에 대한 기준만 정리해봐도 의미가 있을 거 같습니다!

Copy link
Collaborator

@nayonsoso nayonsoso Jul 26, 2025

Choose a reason for hiding this comment

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

로그가 너무 많으면 어떤 단점이 있는지는 이미 알고계시리라 생각합니다.!

  • 디스크 공간 낭비
  • 비용 증가 (클라우드 저장 시)
  • 중요 로그 묻힘 등등..

(물론 지금 서버가 최적의 로그만 남기고 있는가? 라고 물으신다면 그건 아니지만요.. 😅
리팩터링해야한다고 생각하고 있습니다!)


실시간 서비스는 문제가 생기지 않도록 계속 모니터링 하는 게 중요하다고 해서 괜찮다고 생각하여 그대로 유지하였습니다 혹시 문제가 될까요...?

이 말씀도 일리가 있지만, "연결이 수립되었음 / 해제되었음 / 메세지 받음"에 대해 모두 로그를 남기는 것은 많다고 생각해요.
예를 들어 연결 수립/해제 같은 "연결 히스토리"는 엔진엑스 access.log 에도 기록되고 있을것이고요.
메시지를 받았다는 것은 데이터베이스에도 기록되는 것이라 생각했어요!

우리가 집중적으로 모니터링할 부분이 생긴다면 그때 추가하거나,
문제가 발생했을 때만 로그를 찍는 방법은 좋을 것 같다는 생각입니다~

Comment on lines 26 to 34
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
log.info("Received message : {}", payload);
for(WebSocketSession s : sessions) {
if(s.isOpen()) {
s.sendMessage(new TextMessage(payload));
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

저도 이게 궁금했어요. 전체 session 에 대해서 for 문을 돌면
서버와 연결된 모든 세션에 브로드케스트될 것 같아요.

제가 잘못이해하는 부분이 있다면 말씀해주세요.!

Comment on lines 31 to 38
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(stompHandler)
.taskExecutor()
.corePoolSize(6)
.maxPoolSize(12)
.queueCapacity(1000);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

이런 설정값들을 선택하신 이유가 궁금해요! 표준이 있는건가요?


그리고 너무 원칙주의적일수도 있는데요,
저는 매직넘버를 보면 "상수화 or 변수화"를 못참겠더라고요 ㅠ.ㅠ
참고 : https://dev-dmsgk.tistory.com/30

이 부분을 private static final 로 상수화, 혹은 application-variable.yml 에 환경변수로 관리할 수 있을 것 같아요.
제 생각에는 application-variable.yml 로 관리하는게 좋을 것 같은데 수연님 생각은 어떠신가요?

Copy link
Contributor Author

@lsy1307 lsy1307 Jul 24, 2025

Choose a reason for hiding this comment

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

100명 이하일 때 추천하는 설정을 채용했는데 저도 따로 관리하는 게 좋을 것 같습니다! 수정해놓을게용

Copy link
Contributor

Choose a reason for hiding this comment

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

매직넘버를 지양하는 건 아주 좋다고 생각합니다 ㅎㅎ

Comment on lines 39 to 44
String token = bearerToken.substring(7);

Claims claims = jwtTokenProvider.parseClaims(token);

log.info("토큰 검증 성공, 유저 인증 완료 - 사용자: {}", claims.getSubject());
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

음.. 사실 인증과 관련된 부분은 스프링 시큐리티에서 웹소켓에 대한 편의 기능들을 제공하는것으로 알아요.
그것들을 잘 사용한다면 이런 중복코드가 많이 줄어들 것이라 생각돼요.

하지만 문제는 ....
스프링 시큐리티 - 웹소켓에 대한 러닝커브인 것습니다. 🫠
제가 스프링 시큐리티에 대해서 관련 부분을 더 알아보고 수정할 수 있을 것 같아요! (시간이 필요하긴 합니다 🥲)

제가 성혁님 코드리뷰할 때도 말씀드렸는데,
다음주 월요일 개발 회의 시간에 제가 스프링 시큐리티 특강(?)을 해드릴게요.
이 PR에서는 인증과 관련된 부분은 잠시 뒤로하고, "웹소켓 연결 설정"에 대한 것들만 집중하여 리뷰 드리겠습니다!!!
우선 이게 머지되어야 다른 소켓 관련 기능들도 진행 가능할테니까요.
그래도 괜찮을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵 알겠습니다! 감사합니다

Copy link
Contributor

Choose a reason for hiding this comment

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

제가 알기로 Spring Security는 HTTP 요청 기반 필터 체인에서만 작동해서 웹소켓 관련해서 검증이 안되는 것으로 알고있습니다!
혹시 다른 방식을 찾아보신 게 있으신가요?

Copy link
Collaborator

Choose a reason for hiding this comment

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

제가 알기로 Spring Security는 HTTP 요청 기반 필터 체인에서만 작동해서 웹소켓 관련해서 검증이 안되는 것으로 알고있습니다!
혹시 다른 방식을 찾아보신 게 있으신가요?

저도 정확히는 모르지만 스프링 시큐리티에서 여차저차 처리를 하면,
"HTTP 세션에 저장된 SecurityContext를 WebSocket 세션으로 옮겨오는" 것이 가능하다고 합니다.
이게 된다면 위와 같은 코드들은 없어도 된다 생각했어요 💡

Copy link
Contributor

Choose a reason for hiding this comment

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

오! 되면 너무 신기하고 편해지겠네요! 새로 또 배우네요 ㅎㅎ

@nayonsoso nayonsoso changed the title feat: Stomp Config 추가 feat: STOMP Config 추가 Jul 23, 2025
Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

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

고생하셨습니다 ! 저도 WebSocket과 STOMP를 빠삭하게 알고 있는 게 아니라서 ... 알고 있는 지식 내로 의견 달았습니다 ..!!

build.gradle Outdated
implementation 'org.hibernate.validator:hibernate-validator'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.782'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.webjars:stomp-websocket'
Copy link
Member

Choose a reason for hiding this comment

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

  • What went wrong:
    Execution failed for task ':compileJava'.
    Could not resolve all files for configuration ':compileClasspath'.
    Could not find org.webjars:stomp-websocket:.
    Required by:
    project :

빌드 에러가 발생하는데, 아마 implementation 'org.webjars:stomp-websocket' 때문인 것 같습니다. 제 경험 상으론 해당 의존성은 없어도 STOMP 까지 잘 동작했던 거로 기억합니다 !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵 감사합니다!

public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/publish");
registry.enableSimpleBroker("/topic")
.setHeartbeatValue(new long[]{15000, 15000});
Copy link
Member

Choose a reason for hiding this comment

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

애플리케이션 실행 시 아래 오류가 발생합니다.

Caused by: java.lang.IllegalArgumentException: Heartbeat values configured but no TaskScheduler provided
at org.springframework.util.Assert.isTrue(Assert.java:122)
at org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler.startInternal(SimpleBrokerMessageHandler.java:273)
at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.start(AbstractBrokerMessageHandler.java:211)
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:179)
... 13 common frames omitted

만약 하트비트를 설정하신다면, 오류 메시지 내용처럼 TaskScheduler 빈을 생성하신 후, 등록해주시면 될 것 같습니다 !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

확인했습니다.... 이걸 확인안했네요 ㅜㅜ


@Component
@Slf4j
public class WebSocketHandler extends TextWebSocketHandler {
Copy link
Member

Choose a reason for hiding this comment

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

저희는 STOMP를 통해 WebSocket을 구현하고 있고, STOMP는 내부적으로 SimpleBrokerMessageHandler 를 사용하기 때문에 WebSocketHandler는 없어도 잘 동작할 것 같습니다.

이 점은 저도 애매하기에, 잘못 알고 있는 점이 있다면 말씀해주세요 !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵 성혁님께서 말씀해주신 게 맞습니다! 수정할게용

@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
Copy link
Member

Choose a reason for hiding this comment

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

(WebSockerHandler 리뷰의 연장선입니다.)

WebSocketConfig 도 마찬가지로 저희는 STOMP를 사용하기 때문에 없어도 잘 동작할 듯 합니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이것도요!

- 불필요한 의존성 삭제
- 불필요한 파일 삭제
- 매직넘버 application-variable에서 관리
- 에러코드 추가
- 중복 코드 함수화
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: 0

♻️ Duplicate comments (1)
src/main/java/com/example/solidconnection/chat/config/StompHandler.java (1)

29-33: 인증 정보를 세션에 저장하는 로직이 여전히 누락되어 있습니다

토큰 검증은 성공적으로 수행되고 있지만, 인증된 사용자 정보를 STOMP 세션 속성에 저장하지 않고 있어요. 이로 인해 후속 메시지 처리 시 사용자 컨텍스트를 활용할 수 없습니다.

다음과 같이 개선해보세요:

 if (StompCommand.CONNECT.equals(accessor.getCommand())) {
     log.info("CONNECT 요청 - 토큰 검증 시작");
     Claims claims = validateAndExtractClaims(accessor, AUTHENTICATION_FAILED);
     log.info("토큰 검증 성공 - 사용자: {}", claims.getSubject());
+    
+    // 사용자 정보를 STOMP 세션 속성에 저장
+    accessor.getSessionAttributes().put("userEmail", claims.getSubject());
+    accessor.setUser(() -> claims.getSubject());
 }
🧹 Nitpick comments (1)
src/main/java/com/example/solidconnection/chat/config/StompHandler.java (1)

35-46: 구독 권한 검증 로직 구현이 시급합니다

코드 중복 문제는 잘 해결되었지만, 채팅방 접근 권한 검증이 여전히 TODO 상태로 남아있어요. 이는 보안상 중요한 이슈입니다.

현재 누구나 임의의 채팅방을 구독할 수 있는 상태인데, 다음 중 하나를 선택해주세요:

  1. 지금 당장 권한 검증 로직을 구현하기
  2. 별도 이슈로 등록하여 추후 작업 계획 수립하기

채팅방 접근 권한 검증 로직을 구현하는 코드를 생성해드릴까요?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e8383b1 and a874ae8.

📒 Files selected for processing (4)
  • build.gradle (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/StompHandler.java (1 hunks)
  • src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java (1 hunks)
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • build.gradle
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
  • src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java
🧰 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.
src/main/java/com/example/solidconnection/chat/config/StompHandler.java (1)

Learnt from: nayonsoso
PR: #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.

🧬 Code Graph Analysis (1)
src/main/java/com/example/solidconnection/chat/config/StompHandler.java (2)
src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java (1)
  • Slf4j (22-88)
src/main/java/com/example/solidconnection/chat/config/StompEventListener.java (1)
  • Component (13-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/main/java/com/example/solidconnection/chat/config/StompHandler.java (3)

1-24: 클래스 구조가 깔끔하게 정리되었네요!

이전 버전 대비 개선된 점들을 확인할 수 있습니다:

  1. 적절한 어노테이션 사용으로 Spring 컴포넌트 등록
  2. Lombok을 활용한 생성자 주입 패턴 적용
  3. 로깅을 위한 @slf4j 어노테이션 추가

51-58: 코드 중복 문제가 훌륭하게 해결되었습니다!

이전 버전에서 CONNECT와 SUBSCRIBE에서 중복되던 토큰 검증 로직을 깔끔하게 별도 메서드로 추출했네요.

특히 좋은 점들:

  1. ErrorCode를 파라미터로 받아 유연한 예외 처리 가능
  2. 메서드명이 역할을 명확히 표현
  3. 단일 책임 원칙을 잘 준수

60-69: 방어적 프로그래밍이 완벽하게 적용되었습니다!

이전 버전에서 지적된 배열 인덱스 검증 문제가 모두 해결되었어요:

  1. null 체크로 NPE 방지
  2. 배열 길이 검증으로 IndexOutOfBoundsException 방지
  3. topic 경로 형식 검증으로 잘못된 destination 차단
  4. 적절한 CustomException 처리

안전하고 견고한 코드로 개선되었습니다!

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.

코멘트 다른분들이 남겨놓으신 거에 추가로 더 남겨놓았습니다!
추가로 잘 연결되는 거 같긴 한데 이걸 테스트코드로도 작성해야할지(작성할 수는 있는지...)는 잘 모르겠네요... 고생하셨습니다!
stomp는 테스트하기가 힘들더라구요

Comment on lines 20 to 38
@Value("${websocket.thread-pool.inbound.core-pool-size:6}")
private int inboundCorePoolSize;

@Value("${websocket.thread-pool.inbound.max-pool-size:12}")
private int inboundMaxPoolSize;

@Value("${websocket.thread-pool.inbound.queue-capacity:1000}")
private int inboundQueueCapacity;

@Value("${websocket.thread-pool.outbound.core-pool-size:6}")
private int outboundCorePoolSize;

@Value("${websocket.thread-pool.outbound.max-pool-size:12}")
private int outboundMaxPoolSize;

@Value("${websocket.heartbeat.server-interval:15000}")
private long heartbeatServerInterval;

@Value("${websocket.heartbeat.client-interval:15000}")
Copy link
Contributor

Choose a reason for hiding this comment

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

스크린샷 2025-07-25 오후 7 12 47

저희 코드 일부 화면인데요! 이런식으로 @ConfigurationProperties를 활용하면 @value를 사용하는 것보다 코드를 좀 더 깔끔하게 작성할 수 있습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

저도 ConfigurationProperties 코멘트를 다려했었습니다 ㅎㅎ
설정과 관련된 클래스로, 관련 값들을 묶어서 정말로 중요한 코드에 집중하게 해주더라고요!
수연님께서 전에 사용한적이 없으셨다면, 이번에 알아두셔도 좋을 것 같습니다~

참고로 우테코에서 현직자분들께 코드리뷰를 받을 때,
대단한 기업에 다니셨던 멘토님들도 ConfigurationProperties 방법을 추천하셨어요!

lsy1307 added 2 commits July 27, 2025 22:04
- 로그 삭제
- @ConfigurationProperties 사용을 위한 파일 추가
- application.yml에 websocket 값 추가
count:
scheduling:
delay: 3000
websocket:
Copy link
Member

Choose a reason for hiding this comment

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

현재 WebSocket 관련 설정값이 test/resources/application.yml 에 작성되어 있는데, main/resources/application.yml 에 적어주어야 할 듯 합니다 ..!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아니 분명 테스트랑 main 둘 다 올렸는데 merge 과정에서 날아간 것 같네요............. ㅜㅜㅜ 다시 올릴게용

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정 완료했습니다!

import org.springframework.web.socket.messaging.SessionDisconnectEvent;

@Component
@Slf4j
Copy link
Member

Choose a reason for hiding this comment

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

로그 없으니까 @Sl4fj 는 없애도 될 것 같습니다 !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵넵!

Copy link
Collaborator

Choose a reason for hiding this comment

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

아직 반영이 안된 것 같습니다. push 하셨는지 확인해주세요~ 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정 완료했습니다!


@Component
@RequiredArgsConstructor
@Slf4j
Copy link
Member

Choose a reason for hiding this comment

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

여기 @Sl4fj 도 같은 이유입니다 !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

확인했어용!

Copy link
Collaborator

Choose a reason for hiding this comment

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

아직 반영이 안된 것 같습니다. push 하셨는지 확인해주세요~ 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정 완료했습니다!

Copy link
Collaborator

@nayonsoso nayonsoso left a comment

Choose a reason for hiding this comment

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

수연님 어려운 부분이었을텐데 수고 많으셨습니다 💐
flyway 버전 충돌 때문에 build 가 실패하는 것 같은데, 최신 develop 과 merge or rebase 하시면 해결될것이라 생각합니다~

import org.springframework.web.socket.messaging.SessionDisconnectEvent;

@Component
@Slf4j
Copy link
Collaborator

Choose a reason for hiding this comment

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

아직 반영이 안된 것 같습니다. push 하셨는지 확인해주세요~ 👀


@Component
@RequiredArgsConstructor
@Slf4j
Copy link
Collaborator

Choose a reason for hiding this comment

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

아직 반영이 안된 것 같습니다. push 하셨는지 확인해주세요~ 👀

Copy link
Collaborator

@nayonsoso nayonsoso Jul 28, 2025

Choose a reason for hiding this comment

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

이 파일 전체에도 포매팅이 필요해보입니다 🏃‍♀️
만약 다른 개발자가 main.java 패키지 전체를 포매팅했을 때, 이 파일까지 함께 변경된다면
그 변경이 본인의 관심사와는 무관한 부분임에도 불구하고 PR에 포함될 것입니다.
이런 경우엔 불필요한 diff가 발생해서 리뷰나 히스토리 추적에 혼란을 줄 수 있다고 생각합니다~
그래서 미리 포매팅을 맞춰두는 게 좋을 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

분명 모든 파일에 포매팅을 했는데........ 빼먹은 것 같습니다 담부터 잘 확인 할게용 ㅜㅜ

long clientInterval
) {}
}

Copy link
Collaborator

@nayonsoso nayonsoso Jul 28, 2025

Choose a reason for hiding this comment

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

파일 끝 개행이 두줄 되어있는 것 같습니다 😲

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정 완료했습니다!

Comment on lines 34 to 38
registration.interceptors(stompHandler)
.taskExecutor()
.corePoolSize(stompProperties.threadPool().inbound().corePoolSize())
.maxPoolSize(stompProperties.threadPool().inbound().maxPoolSize())
.queueCapacity(stompProperties.threadPool().inbound().queueCapacity());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
registration.interceptors(stompHandler)
.taskExecutor()
.corePoolSize(stompProperties.threadPool().inbound().corePoolSize())
.maxPoolSize(stompProperties.threadPool().inbound().maxPoolSize())
.queueCapacity(stompProperties.threadPool().inbound().queueCapacity());
InboundProperties inboundProperties = stompProperties.threadPool().inbound();
registration.interceptors(stompHandler)
.taskExecutor()
.corePoolSize(inboundProperties.corePoolSize())
.maxPoolSize(inboundProperties.maxPoolSize())
.queueCapacity(inboundProperties.queueCapacity());

변수 분리하는건 어떨까요?
stompProperties.threadPool().inbound()가 조금 긴데, 세번이나 있는게 중복코드이기도 하고,
실수로 stompProperties.threadPool().outbound()가 포함되어있더라도 파악하기 어려울 수 있을 것 같아요.

변수로 분리하면 그런 실수를 방지할 수 있을 것 같습니다~


  • stompProperties.threadPool().outbound()와
  • stompProperties.heartbeat() 도 마찬가지입니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

좋은 아이디어 감사합니다! 반영하였습니다 : )


// socket
UNAUTHORIZED_SUBSCRIBE(HttpStatus.FORBIDDEN.value(), "구독 권한이 없습니다."),
INVALID_ROOMID(HttpStatus.BAD_REQUEST.value(), "경로의 roomId가 잘못되었습니다."),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
INVALID_ROOMID(HttpStatus.BAD_REQUEST.value(), "경로의 roomId가 잘못되었습니다."),
INVALID_ROOM_ID(HttpStatus.BAD_REQUEST.value(), "경로의 roomId가 잘못되었습니다."),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정 완료했습니다!

- application-variable.yml에 설정값 추가
- code formatting, 오타 수정
- 불필요한 @Sl4fj제거
- StompWebSocketConfig 변수 분리
Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

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

어노테이션 제거 확인했습니다 ! 시크릿 파일만 변경사항 적용해주세요 !!
정말 고생하셨습니다~!! 👍

Copy link
Collaborator

@nayonsoso nayonsoso left a comment

Choose a reason for hiding this comment

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

이 PR머지하시기 전에 secret 레포의 application-variable.yml 업데이트하시고

🔥submodule 해시 변경하는🔥 커밋 포함한 후 머지해주세요!!

@lsy1307 lsy1307 merged commit 69a0bc9 into solid-connection:develop Jul 31, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Stomp config 구성

4 participants