Skip to content

Conversation

@thezz9
Copy link
Collaborator

@thezz9 thezz9 commented Jun 25, 2025

작업 내용

  • 멀티 서버 환경에서 SSE는 적절하지 않아 WebSocket 기반으로 싹 다 전환했습니다.

참고 사항

Honeycam 2025-06-25 10-13-25

Summary by CodeRabbit

  • New Features
    • 코드 제출 결과 및 채점 상태가 실시간 WebSocket(STOMP) 메시지로 전송됩니다.
    • 제출 시 세션 키를 반환하며, 클라이언트는 해당 키로 WebSocket 구독을 통해 채점 진행 상황을 확인할 수 있습니다.
    • 테스트케이스 초기화, 개별 테스트케이스 결과, 최종 결과, 오류 등 다양한 실시간 이벤트가 제공됩니다.
  • Refactor
    • 기존 SSE 기반 스트리밍 방식이 WebSocket 기반 구조로 전면 교체되었습니다.
    • 내부 데이터 구조 및 응답 DTO가 WebSocket 메시지 전송에 맞게 일원화되었습니다.
  • Bug Fixes
    • 실행 시간(executionTime) 관련 필드가 일관되게 long 타입으로 변경되어 단위 및 정밀도가 통일되었습니다.
  • Style
    • 제출 테스트 페이지 UI 및 CSS가 WebSocket 흐름에 맞게 개선되었습니다.
  • Chores
    • 불필요한 SSE 관련 코드 및 DTO, 저장소가 제거되었습니다.

@coderabbitai
Copy link

coderabbitai bot commented Jun 25, 2025

Walkthrough

이 변경사항은 코드 제출 및 채점 결과의 실시간 스트리밍 방식을 SSE에서 WebSocket(STOMP) 기반으로 전환합니다. 이를 위해 이벤트 기반 구조가 도입되어, 제출, 채점, 에러 등 다양한 이벤트가 서비스, 퍼블리셔, 리스너를 통해 처리됩니다. DTO, 엔티티, 컨트롤러, 프론트엔드 템플릿 등 관련된 모든 계층이 WebSocket 이벤트 흐름에 맞게 리팩토링되었습니다.

Changes

파일/경로 요약 변경 요약
.../dto/event/*, .../dto/event/payload/* 제출, 채점, 에러, 테스트케이스 초기화 등 다양한 이벤트 및 페이로드 DTO 신설
.../dto/response/submission/*, .../dto/submission/model/*, .../domain/submission/dto/*, .../domain/submission/model/*, .../domain/submission/service/*, .../domain/submission/exception/code/* 기존 SSE/REST 기반 응답 DTO 및 도메인 모델 필드 타입(long/double 등) 및 메서드 시그니처 일괄 변경, 일부 레코드/클래스/필드/메서드 삭제 또는 추가
.../port/EmitterStore.java, .../sse/InMemoryEmitterStore.java SSE Emitter 관련 인터페이스 및 구현체 삭제
.../port/SubmissionEventService.java, .../port/ProblemEventService.java 이벤트 퍼블리셔 인터페이스 신설
.../service/SubmissionService.java SSEEmitter 사용 제거, 이벤트 기반 구조로 대대적 리팩토링, 반환값 및 주요 흐름 변경
.../presentation/submission/SubmissionController.java SSE 기반 submitCodeStream → WebSocket 기반 submitCodeStream으로 변경, 반환 타입 및 API 문서 변경
.../infrastructure/event/dto/submission/*, .../infrastructure/event/listener/*, .../infrastructure/event/publisher/* WebSocket 메시지 DTO, 이벤트 리스너, 퍼블리셔, 메시지 서비스 등 WebSocket 이벤트 처리 컴포넌트 신설 및 수정
.../infrastructure/event/listener/RedisJudgeQueueConsumer.java, .../infrastructure/event/publisher/RedisJudgeQueueProducer.java SubmissionMessage 필드명 emitterKey → sessionKey로 변경, 관련 로직 수정
.../infrastructure/judge0/Judge0ResponseMapper.java 실행시간 단위 변환(초→밀리초), 출력값 정규화 등 내부 로직 개선
.../infrastructure/persistence/repository/submission/impl/UserProblemResultRepositoryImpl.java 저장/수정 메서드 반환타입 void→엔티티로 변경
.../resources/templates/submit-test.html 프론트엔드: SSE→WebSocket 구조로 JS, UI, 흐름 전체 리팩토링, 코드리뷰 기능 제거

Sequence Diagram(s)

sequenceDiagram
actor User
participant SubmissionController
participant SubmissionService
participant SubmissionEventService
participant SubmissionEventListener
participant StompMessageService
User->>SubmissionController: POST /problems/{id}/submit-ws
SubmissionController->>SubmissionService: enqueueCodeSubmission()
SubmissionService->>SubmissionEventService: publishInitTestcases(...)
SubmissionService->>SubmissionEventService: publishTestcaseUpdate(...)
SubmissionService->>SubmissionEventService: publishFinalResult(...)
SubmissionService->>SubmissionEventService: publishSubmissionError(...)
SubmissionEventService->>SubmissionEventListener: (Spring Event)
SubmissionEventListener->>StompMessageService: sendInitTestcases(...)
SubmissionEventListener->>StompMessageService: sendTestcaseResultUpdate(...)
SubmissionEventListener->>StompMessageService: sendFinalResult(...)
SubmissionEventListener->>StompMessageService: sendError(...)
StompMessageService-->>User: WebSocket 메시지 전송 (/topic/submission/{sessionKey}/...)
Loading

Possibly related PRs

  • fix : submission-stream #66: SubmissionService의 비동기 예외 처리 및 예외 알림 로직 개선과 관련, 본 PR의 이벤트 기반 에러 처리 구조와 코드 레벨에서 밀접하게 연결됨.

Suggested labels

enhancement

Suggested reviewers

  • minjee2758
  • pokerbearkr
  • Kimminu7
  • chat26666
  • NCookies

Poem

🐇
WebSocket 바람 타고
채점 소식 실시간으로
토끼 귀 쫑긋, 이벤트 춤추네
SSE는 안녕, 이벤트는 반가워
코드의 흐름 따라
토끼도 깡총깡총
오늘도 채점 성공!

✨ Finishing Touches
  • 📝 Generate Docstrings

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

🧹 Nitpick comments (10)
src/main/java/org/ezcode/codetest/infrastructure/event/config/RedisStreamConfig.java (1)

65-65: 예외 정보 로깅 레벨 변경에 대한 검토가 필요합니다.

예외 정보를 log.error에서 log.info로 변경했는데, 이로 인해 디버깅이 어려워질 수 있습니다. Redis Consumer Group 초기화 중 발생하는 예외가 정상적인 시나리오(이미 존재하는 그룹)인 경우가 많다면 적절하지만, 예상치 못한 오류를 놓칠 위험이 있습니다.

더 명확한 로깅을 위해 다음과 같이 수정하는 것을 고려해보세요:

-            log.info("예외 발생: {}, 메시지: {}", e.getClass(), e.getMessage());
+            log.debug("Redis Consumer Group 초기화 중 예외 발생: {}, 메시지: {}", e.getClass(), e.getMessage());
src/main/java/org/ezcode/codetest/application/submission/dto/response/submission/SubmissionDetailResponse.java (1)

24-25: 타입 일관성 개선은 좋으나 문서 업데이트가 필요합니다.

executionTime의 타입을 Long으로 변경한 것은 엔티티와의 일관성을 위해 적절합니다. 다만 스키마 설명에서 여전히 "실행 시간 (s)"로 표기되어 있는데, Long 타입으로 변경된 후 단위가 여전히 초인지 밀리초로 변경되었는지 확인하고 문서를 업데이트해 주세요.

스키마 설명을 다음과 같이 업데이트하는 것을 고려해 보세요:

-    @Schema(description = "실행 시간 (s)", example = "0.129")
+    @Schema(description = "실행 시간 (ms)", example = "129")
src/main/java/org/ezcode/codetest/application/submission/dto/event/TestcaseListInitializedEvent.java (1)

7-17: 이벤트 레코드 설계가 적절하며 정적 팩토리 메서드 간소화를 고려해 보세요.

테스트케이스 목록 초기화를 위한 이벤트 레코드가 잘 설계되었습니다. 불변 구조와 명확한 필드명이 적절합니다.

정적 팩토리 메서드 from()이 단순히 생성자를 호출하기만 하므로, 현재 구현에서는 생략하거나 추가적인 검증 로직이 필요한 경우에만 유지하는 것을 고려해 보세요.

더 간단한 구현을 원한다면 정적 팩토리 메서드를 제거할 수 있습니다:

-    public static TestcaseListInitializedEvent from(String sessionKey, List<InitTestcaseListPayload> payload) {
-        return new TestcaseListInitializedEvent(sessionKey, payload);
-    }
src/main/java/org/ezcode/codetest/infrastructure/event/publisher/RedisJudgeQueueProducer.java (1)

21-21: 로그 메시지가 더 이상 정확하지 않습니다.

SSE에서 WebSocket으로 전환되었으므로 로그 메시지를 업데이트해야 합니다.

-        log.info("[SSE enqueue] emitterKey: {}", submissionMessage.sessionKey());
+        log.info("[WebSocket enqueue] sessionKey: {}", submissionMessage.sessionKey());
src/main/java/org/ezcode/codetest/application/submission/dto/event/TestcaseEvaluatedEvent.java (1)

12-18: 정적 팩토리 메서드가 필요한지 검토해보세요.

record의 생성자와 동일한 역할을 하는 정적 팩토리 메서드가 추가적인 가치를 제공하는지 확인해보세요. 단순히 생성자 호출을 래핑하는 것이라면 불필요할 수 있습니다.

-    public static TestcaseEvaluatedEvent of(String sessionKey, TestcaseResultPayload payload) {
-        return new TestcaseEvaluatedEvent(
-            sessionKey,
-            payload
-        );
-    }

대신 직접 생성자를 사용하는 것을 고려해보세요:

new TestcaseEvaluatedEvent(sessionKey, payload)
src/main/java/org/ezcode/codetest/domain/submission/model/SubmissionAggregator.java (1)

17-19: 평균 계산에서 정밀도 손실 가능성 검토

정수 나눗셈으로 변경되면서 소수점 이하 값이 버려질 수 있습니다. 실행 시간 평균에서 정밀도가 중요한지 비즈니스 요구사항을 확인해보세요.

만약 정밀도가 중요하다면 다음과 같이 개선할 수 있습니다:

-public long averageExecutionTime() {
-    return count == 0 ? 0L : totalExecutionTime / count;
-}
+public long averageExecutionTime() {
+    return count == 0 ? 0L : Math.round((double) totalExecutionTime / count);
+}
src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/InitTestcaseListPayload.java (1)

28-28: 하드코딩된 상태값을 상수로 분리하세요.

"채점 중" 문자열이 하드코딩되어 있습니다. 상태값을 상수나 enum으로 관리하여 유지보수성을 향상시키는 것이 좋겠습니다.

+ private static final String GRADING_STATUS = "채점 중";
+ 
public record InitTestcaseListPayload(
    // ...
) {
    public static List<InitTestcaseListPayload> from(ProblemInfo info) {
        return IntStream.rangeClosed(1, info.getTestcaseCount())
            .mapToObj(seq -> {
                Testcase tc = info.testcaseList().get(seq - 1);
                return new InitTestcaseListPayload(
                    seq,
                    tc.getInput(),
                    tc.getOutput(),
-                   "채점 중"
+                   GRADING_STATUS
                );
            })
            .toList();
    }
}
src/main/java/org/ezcode/codetest/presentation/submission/SubmissionController.java (1)

65-69: 반환 타입을 단순화하세요.

단일 세션 키를 반환하기 위해 HashSet<String>을 사용하는 것은 부자연스럽습니다. 더 명확하고 직관적인 반환 타입을 사용하는 것이 좋겠습니다.

- public ResponseEntity<HashSet<String>> submitCodeStream(
+ public ResponseEntity<Map<String, String>> submitCodeStream(
    @Parameter(description = "제출할 문제 ID", required = true) @PathVariable Long problemId,
    @RequestBody @Valid CodeSubmitRequest request,
    @AuthenticationPrincipal AuthUser authUser
) {
-   HashSet<String> set = new HashSet<>();
-   set.add(submissionService.enqueueCodeSubmission(problemId, request, authUser));
-   return ResponseEntity
-       .status(HttpStatus.OK)
-       .body(set);
+   String sessionKey = submissionService.enqueueCodeSubmission(problemId, request, authUser);
+   return ResponseEntity
+       .status(HttpStatus.OK)
+       .body(Map.of("sessionKey", sessionKey));
}
src/main/java/org/ezcode/codetest/infrastructure/event/publisher/SubmissionEventPublisher.java (1)

19-37: 디버깅을 위한 로깅 추가를 고려해보세요.

이벤트 발행 시점을 추적하기 위한 디버그 레벨 로깅을 추가하는 것을 고려해보세요. 특히 WebSocket 기반 실시간 통신에서는 이벤트 흐름 추적이 중요할 수 있습니다.

+ import lombok.extern.slf4j.Slf4j;

+ @Slf4j
@Component
@RequiredArgsConstructor
public class SubmissionEventPublisher implements SubmissionEventService {

    @Override
    public void publishInitTestcases(TestcaseListInitializedEvent event) {
+       log.debug("Publishing testcase list initialized event for session: {}", event.sessionKey());
        publisher.publishEvent(event);
    }
    
    // 다른 메소드들도 유사하게 로깅 추가
src/main/java/org/ezcode/codetest/infrastructure/event/publisher/StompMessageService.java (1)

75-105: WebSocket 메시지 전송 메서드들이 잘 구현되었습니다

각 이벤트 타입별로 명확한 토픽 구조를 가지고 있어 클라이언트에서 구독하기 쉽습니다. 다만, sessionKey의 유효성 검증이나 예외 처리를 추가하면 더 안정적일 것 같습니다.

sessionKey 검증을 추가하면 더 안정적일 것입니다:

 public void sendInitTestcases(String sessionKey, List<InitTestcaseListResponse> dataList) {
+    if (sessionKey == null || sessionKey.isBlank()) {
+        log.warn("Invalid sessionKey provided for sendInitTestcases");
+        return;
+    }
 
     messagingTemplate.convertAndSend(
         "/topic/submission/" + sessionKey + "/init",
         dataList
     );
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 735bf5d and ef61b21.

📒 Files selected for processing (44)
  • src/main/java/org/ezcode/codetest/application/submission/dto/event/SubmissionErrorEvent.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/event/SubmissionJudgingFinishedEvent.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/event/TestcaseEvaluatedEvent.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/event/TestcaseListInitializedEvent.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/InitTestcaseListPayload.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/SubmissionFinalResultPayload.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/TestcaseResultPayload.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/response/submission/FinalResultResponse.java (0 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/response/submission/JudgeResultResponse.java (0 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/dto/response/submission/SubmissionDetailResponse.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/model/JudgeResult.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/model/SubmissionContext.java (2 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/port/EmitterStore.java (0 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/port/ProblemEventService.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/port/QueueProducer.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/port/SubmissionEventService.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/submission/service/SubmissionService.java (4 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/dto/AnswerEvaluation.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/dto/SubmissionData.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/dto/SubmitProcessResult.java (0 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/exception/code/SubmissionExceptionCode.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/model/SubmissionAggregator.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/model/TestcaseEvaluationInput.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/model/entity/Submission.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/repository/UserProblemResultRepository.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/submission/service/SubmissionDomainService.java (4 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/config/RedisStreamConfig.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/SubmissionMessage.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/response/ErrorWsResponse.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/response/InitTestcaseListResponse.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/response/JudgeResultResponse.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/response/SubmissionFinalResultResponse.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/listener/RedisJudgeQueueConsumer.java (2 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/listener/SubmissionEventListener.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/publisher/ProblemEventPublisher.java (2 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/publisher/RedisJudgeQueueProducer.java (2 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/publisher/StompMessageService.java (2 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/event/publisher/SubmissionEventPublisher.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/judge0/Judge0ResponseMapper.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/openai/OpenAIMessageBuilder.java (2 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/submission/impl/UserProblemResultRepositoryImpl.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/sse/InMemoryEmitterStore.java (0 hunks)
  • src/main/java/org/ezcode/codetest/presentation/submission/SubmissionController.java (3 hunks)
  • src/main/resources/templates/submit-test.html (1 hunks)
💤 Files with no reviewable changes (5)
  • src/main/java/org/ezcode/codetest/application/submission/dto/response/submission/FinalResultResponse.java
  • src/main/java/org/ezcode/codetest/domain/submission/dto/SubmitProcessResult.java
  • src/main/java/org/ezcode/codetest/application/submission/port/EmitterStore.java
  • src/main/java/org/ezcode/codetest/application/submission/dto/response/submission/JudgeResultResponse.java
  • src/main/java/org/ezcode/codetest/infrastructure/sse/InMemoryEmitterStore.java
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (51)
src/main/java/org/ezcode/codetest/application/submission/port/ProblemEventService.java (1)

5-9: 인터페이스 설계가 잘 되어 있습니다.

단일 책임 원칙을 따르며, 이벤트 발행을 위한 명확한 메서드 시그니처를 제공합니다. SSE에서 WebSocket 기반 아키텍처로의 전환에 적합한 설계입니다.

src/main/java/org/ezcode/codetest/application/submission/port/QueueProducer.java (1)

3-3: 패키지 경로 변경에 따른 일관성 확인이 필요합니다.

SubmissionMessage의 패키지 경로가 변경되고 emitterKey에서 sessionKey로 필드명도 변경되었다고 하니, 전체 코드베이스에서 해당 클래스를 사용하는 모든 곳이 일관되게 업데이트되었는지 확인해야 합니다.

다음 스크립트를 실행하여 모든 참조가 올바르게 업데이트되었는지 확인하세요:

#!/bin/bash
# SubmissionMessage 클래스 사용처 확인
rg -A 3 "SubmissionMessage" --type java

# emitterKey 사용이 남아있는지 확인
rg "emitterKey" --type java

# sessionKey 사용 확인
rg "sessionKey" --type java
src/main/java/org/ezcode/codetest/infrastructure/event/publisher/ProblemEventPublisher.java (1)

13-13: 인터페이스 구현이 올바르게 되어 있습니다.

ProblemEventService 인터페이스를 정확히 구현하고 있으며, 기존 로직은 그대로 유지하면서 이벤트 기반 아키텍처에 적합하게 리팩토링되었습니다.

src/main/java/org/ezcode/codetest/application/submission/model/JudgeResult.java (1)

10-10: 실행 시간 타입 변경으로 인한 정밀도 손실 검토가 필요합니다.

executionTime 필드가 double에서 long으로 변경되었는데, 이는 소수점 정밀도를 잃을 수 있습니다. 실행 시간이 밀리초 단위로 측정되고 있다면 문제없지만, 더 정밀한 측정이 필요한 경우 데이터 손실이 발생할 수 있습니다.

다음 스크립트를 실행하여 관련 클래스들의 타입 일관성을 확인하세요:

#!/bin/bash
# executionTime 필드를 사용하는 모든 클래스 확인
rg -A 5 -B 5 "executionTime" --type java

# JudgeResult를 사용하는 코드에서 타입 호환성 확인
ast-grep --pattern 'new JudgeResult($$$)'
src/main/java/org/ezcode/codetest/infrastructure/event/listener/RedisJudgeQueueConsumer.java (2)

8-8: 패키지 경로 변경 확인됨

SubmissionMessage의 패키지 경로가 새로운 이벤트 기반 구조에 맞게 변경되었습니다.


30-30: emitterKey에서 sessionKey로 일관성 있게 변경됨

SSE 기반 시스템에서 WebSocket 기반 시스템으로 전환하면서 "emitterKey"를 "sessionKey"로 변경한 것이 적절합니다. 변경사항이 일관성 있게 적용되었습니다.

다른 관련 클래스들에서도 동일하게 변경되었는지 확인해보겠습니다:

#!/bin/bash
# Description: emitterKey 사용이 남아있는지 확인하고 sessionKey로 변경되었는지 검증

# emitterKey 사용 확인
echo "=== emitterKey 사용 확인 ==="
rg -A 3 -B 3 "emitterKey"

echo -e "\n=== sessionKey 사용 확인 ==="
rg -A 3 -B 3 "sessionKey"

Also applies to: 38-38

src/main/java/org/ezcode/codetest/domain/submission/exception/code/SubmissionExceptionCode.java (1)

20-20: 알 수 없는 오류 처리를 위한 예외 코드 추가

새로운 이벤트 기반 아키텍처에서 일반적인 예외를 처리하기 위한 UNKNOWN_ERROR 코드 추가가 적절합니다. HTTP 500 상태 코드와 명확한 에러 메시지가 설정되어 있습니다.

src/main/java/org/ezcode/codetest/domain/submission/dto/SubmissionData.java (1)

30-30: 실행 시간 타입의일관성 있는 변경

executionTime 필드를 double에서 long으로 변경한 것이 적절합니다. 실행 시간은 일반적으로 밀리초 단위의 정수로 표현하는 것이 더 정확하고 일관성 있습니다.

다른 관련 클래스들에서도 실행 시간 타입이 일관성 있게 변경되었는지 확인해보겠습니다:

#!/bin/bash
# Description: 실행 시간 관련 필드들의 타입 일관성 확인

# executionTime 필드를 가진 클래스들 검색
echo "=== executionTime 필드 타입 확인 ==="
rg -A 2 -B 2 "executionTime"

# 혹시 남아있는 double 타입 실행시간 필드 확인
echo -e "\n=== double 타입 실행시간 필드 확인 ==="
rg -A 2 -B 2 "double.*[Ee]xecution[Tt]ime|[Ee]xecution[Tt]ime.*double"
src/main/java/org/ezcode/codetest/domain/submission/model/TestcaseEvaluationInput.java (1)

16-16: 실행 시간 타입 통일

다른 도메인 클래스들과 일관성을 유지하기 위해 executionTime 타입을 long으로 변경한 것이 적절합니다. 이는 시스템 전반의 데이터 타입 일관성을 개선합니다.

src/main/java/org/ezcode/codetest/domain/submission/dto/AnswerEvaluation.java (1)

6-18: DTO 단순화를 통한 관심사 분리

expectedOutputactualOutput 필드를 제거하고 평가 결과만 유지하도록 단순화한 것이 적절합니다. 새로운 이벤트 기반 아키텍처에서는 출력 데이터가 이벤트 페이로드나 응답 DTO에서 처리되므로, 이 클래스는 순수하게 평가 결과만 담당하게 되어 관심사 분리가 개선되었습니다.

src/main/java/org/ezcode/codetest/domain/submission/model/entity/Submission.java (1)

54-54: 실행 시간 데이터 타입 일관성 개선이 적절합니다.

executionTime 필드와 생성자 매개변수의 타입을 Double에서 Long으로 변경한 것이 코드베이스 전반의 타입 일관성을 높이는 좋은 개선사항입니다.

다만 기존 데이터와의 호환성을 확인해 주세요.

다음 스크립트로 관련 타입 변경이 일관되게 적용되었는지 확인하세요:

#!/bin/bash
# Description: executionTime 필드의 타입 일관성 확인

# executionTime 관련 모든 사용처 검索
rg -A 3 -B 3 "executionTime.*Double"

# 데이터베이스 스키마 변경사항 확인  
fd -e sql | xargs rg -l "execution_time"

Also applies to: 61-61

src/main/java/org/ezcode/codetest/application/submission/dto/event/SubmissionJudgingFinishedEvent.java (1)

5-12: 이벤트 레코드 설계가 우수합니다.

WebSocket 기반 이벤트 드리븐 아키텍처로의 전환을 위한 이벤트 레코드가 잘 설계되었습니다. record를 사용한 불변 데이터 구조와 명확한 필드명(sessionKey, payload)이 적절합니다.

src/main/java/org/ezcode/codetest/domain/submission/repository/UserProblemResultRepository.java (1)

15-17: 저장소 인터페이스 개선이 우수합니다.

메서드들이 void 대신 UserProblemResult를 반환하도록 변경된 것은 다음과 같은 장점이 있습니다:

  • JPA 저장소 관례에 부합
  • 함수형 프로그래밍 패턴 지원
  • 메서드 체이닝 가능
  • 저장/업데이트된 엔티티에 대한 접근 제공

이러한 변경사항은 코드의 유연성과 가독성을 향상시킵니다.

src/main/java/org/ezcode/codetest/infrastructure/event/publisher/RedisJudgeQueueProducer.java (2)

6-6: LGTM! 패키지 재구성이 적절하게 반영되었습니다.

새로운 패키지 구조가 이벤트 기반 아키텍처를 잘 반영하고 있습니다.


23-23: LGTM! emitterKey에서 sessionKey로의 일관된 네이밍 변경이 잘 적용되었습니다.

WebSocket 기반 아키텍처에 맞는 명확한 네이밍입니다.

src/main/java/org/ezcode/codetest/infrastructure/judge0/Judge0ResponseMapper.java (4)

15-15: LGTM! 실행 시간을 밀리초 단위로 일관되게 변환하는 좋은 개선입니다.

시스템 전반에서 시간 표현의 일관성을 보장합니다.


23-34: LGTM! 출력 정규화 로직이 잘 통합되었습니다.

각 출력 소스별로 적절한 처리가 이루어지고 있습니다.


42-44: LGTM! 시간 변환 로직이 정확합니다.

Math.round()를 사용하여 정확한 반올림 처리가 되고 있습니다.


46-50: LGTM! 출력 정규화 로직이 적절합니다.

trailing newline과 공백을 제거하여 일관된 출력 형식을 보장합니다.

src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/SubmissionMessage.java (2)

1-1: LGTM! 패키지 재구성이 적절합니다.

submission 관련 DTO들을 별도 패키지로 분리하여 구조가 더 명확해졌습니다.


5-5: LGTM! WebSocket 아키텍처에 맞는 네이밍 변경입니다.

emitterKey에서 sessionKey로 변경하여 의미가 더 명확해졌습니다.

src/main/java/org/ezcode/codetest/application/submission/dto/event/TestcaseEvaluatedEvent.java (1)

5-11: LGTM! 이벤트 기반 아키텍처에 적합한 깔끔한 record 설계입니다.

불변 객체로 설계되어 있고 필드 구성이 명확합니다.

src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/SubmissionFinalResultPayload.java (2)

5-15: LGTM! 불변 설계와 명확한 필드 구성이 우수합니다.

final 필드와 Lombok을 적절히 활용한 깔끔한 설계입니다.


16-21: LGTM! 정확성 판단 로직이 적절합니다.

totalCount == passedCount로 정확성을 판단하는 비즈니스 로직이 명확하고 직관적입니다.

src/main/java/org/ezcode/codetest/infrastructure/openai/OpenAIMessageBuilder.java (3)

17-17: 프롬프트 포맷팅 지시사항 개선이 적절합니다

볼드체 변환 지시사항 추가로 WebSocket을 통해 전달되는 AI 응답의 일관성이 향상될 것입니다.


67-74: 명시적 개행 문자 추가로 포맷팅 일관성 확보

정답 케이스에서 명시적 \n 문자 추가와 탭 들여쓰기 지시사항으로 응답 포맷이 더욱 일관되게 생성될 것입니다.


81-84: 오답 케이스 포맷팅 개선 확인

오답 케이스에서도 일관된 포맷팅 지시사항이 적용되어 전체적인 응답 품질이 향상되었습니다.

src/main/java/org/ezcode/codetest/application/submission/model/SubmissionContext.java (2)

3-3: 이벤트 기반 아키텍처에 맞는 DTO 변경 확인

FinalResultResponse에서 SubmissionFinalResultPayload로의 변경이 SSE에서 WebSocket 전환에 적절하게 대응됩니다.


36-41: 반환 타입 변경 및 제거된 메서드 확인 필요

toFinalResult 메서드의 반환 타입이 새로운 payload 클래스로 올바르게 변경되었습니다. 다만 getProcessedCount() 메서드가 제거되었는데, 다른 곳에서 사용되고 있지 않은지 확인이 필요합니다.

다음 스크립트로 제거된 getProcessedCount() 메서드의 사용처를 확인해주세요:

#!/bin/bash
# 제거된 getProcessedCount() 메서드의 사용처 확인
rg -A 3 "getProcessedCount" --type java
src/main/java/org/ezcode/codetest/domain/submission/model/SubmissionAggregator.java (1)

5-5: 실행 시간 데이터 타입 일관성 개선 확인

double에서 long으로의 타입 변경이 시스템 전반의 타입 일관성을 향상시킵니다.

Also applies to: 11-11

src/main/java/org/ezcode/codetest/application/submission/port/SubmissionEventService.java (1)

8-17: 이벤트 기반 아키텍처를 위한 잘 설계된 인터페이스

제출 과정의 각 단계별 이벤트를 명확하게 분리하여 정의했으며, 메서드명이 직관적이고 책임이 명확히 구분되어 있습니다. SSE에서 WebSocket으로의 전환에 적합한 추상화입니다.

src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/response/ErrorWsResponse.java (1)

7-23: WebSocket 에러 응답을 위한 잘 구조화된 DTO

Record 클래스 사용과 Swagger 문서화, 그리고 SubmissionExceptionCode로부터의 정적 팩토리 메서드까지 모든 구성 요소가 적절합니다. WebSocket 기반 에러 처리에 필요한 정보를 완전히 포함하고 있습니다.

src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/TestcaseResultPayload.java (1)

6-30: 잘 설계된 페이로드 레코드입니다.

테스트케이스 결과를 캡슐화하는 레코드가 깔끔하게 구현되었습니다. 정적 팩토리 메서드 fromEvaluation을 통해 JudgeResultAnswerEvaluation 데이터를 적절히 결합하고 있으며, 실행 시간을 long 타입으로 사용하는 것도 적절합니다.

src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/response/SubmissionFinalResultResponse.java (1)

7-30: 잘 구현된 WebSocket 응답 DTO입니다.

Swagger 어노테이션을 통한 API 문서화가 잘 되어 있고, 정적 팩토리 메서드를 통해 페이로드에서 응답 DTO로의 변환이 깔끔하게 구현되었습니다. SSE에서 WebSocket 기반으로의 전환에 적합한 구조입니다.

src/main/java/org/ezcode/codetest/infrastructure/event/listener/SubmissionEventListener.java (1)

19-48: 이벤트 기반 아키텍처가 잘 구현되었습니다.

Spring의 @EventListener를 활용하여 제출 관련 이벤트들을 WebSocket 메시징으로 변환하는 구조가 깔끔합니다. 각 이벤트 핸들러가 단일 책임을 가지고 있으며, 페이로드를 응답 DTO로 변환하여 WebSocket을 통해 전송하는 로직이 명확합니다. SSE에서 WebSocket으로의 전환 목적에 적합한 설계입니다.

src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/submission/impl/UserProblemResultRepositoryImpl.java (1)

26-34: 리포지토리 메서드 시그니처가 개선되었습니다.

저장/수정 메서드가 엔티티를 반환하도록 변경된 것은 좋은 개선입니다. 이를 통해 호출자가 관리 상태의 엔티티(예: 생성된 ID, 업데이트된 타임스탬프 등)에 접근할 수 있어 더 나은 사용성을 제공합니다. 일반적인 리포지토리 패턴에 잘 부합합니다.

src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/InitTestcaseListPayload.java (1)

21-24: 스크립트가 결과를 반환하지 않아, ripgrep을 이용해 보다 폭넓게 ProblemInfo 구현부를 확인해 보겠습니다. 아래 스크립트를 실행해주세요.

#!/bin/bash
# 1. ProblemInfo 클래스 선언 위치 확인
rg -n 'class ProblemInfo' -A 20

# 2. getTestcaseCount() 메서드 구현부 검색
rg -n 'getTestcaseCount\s*\(' -A 5 src/main/java

# 3. testcaseList() 메서드 구현부 검색
rg -n 'testcaseList\s*\(' -A 5 src/main/java
src/main/java/org/ezcode/codetest/presentation/submission/SubmissionController.java (1)

44-53: API 문서가 잘 작성되었습니다.

WebSocket 구독 경로와 사용법이 명확히 문서화되어 있어 클라이언트 개발자가 쉽게 이해할 수 있습니다.

src/main/java/org/ezcode/codetest/domain/submission/service/SubmissionDomainService.java (3)

31-58: 함수형 스타일로의 리팩토링이 우수합니다.

OptionalmaporElseGet을 사용한 함수형 스타일 구현이 깔끔하고 읽기 좋습니다. 메소드가 UserProblemResult를 반환하도록 변경된 것도 호출하는 쪽에서 결과를 활용하기에 더 유용합니다.


91-91: 실행 시간 타입 변경이 일관성을 개선합니다.

executionTimedouble에서 long으로 변경한 것은 밀리초 단위의 정수 표현으로 타입 일관성을 향상시킵니다.


112-118: 도메인 서비스 메소드들의 반환 타입 개선.

createUserProblemResultmodifyUserProblemResult 메소드가 생성/수정된 엔티티를 반환하도록 변경된 것은 호출자가 결과를 즉시 사용할 수 있어 유용합니다.

src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/response/JudgeResultResponse.java (2)

8-30: WebSocket 응답 DTO가 잘 설계되었습니다.

@Schema 어노테이션을 통한 API 문서화가 예시와 함께 명확하게 작성되어 있고, record 구조로 불변성이 보장됩니다. 필드명과 타입도 적절합니다.


31-40: 정적 팩토리 메소드 구현이 우수합니다.

TestcaseResultPayload에서 JudgeResultResponse로의 변환 로직이 깔끔하게 캡슐화되어 있습니다. 이러한 패턴은 변환 로직의 재사용성과 가독성을 높입니다.

src/main/java/org/ezcode/codetest/infrastructure/event/publisher/SubmissionEventPublisher.java (1)

13-39: 이벤트 퍼블리셔가 깔끔하게 구현되었습니다.

Spring의 ApplicationEventPublisher를 활용한 어댑터 패턴이 잘 적용되었습니다. 각 이벤트 타입별로 메소드가 명확히 분리되어 있어 역할이 명확합니다.

src/main/java/org/ezcode/codetest/application/submission/service/SubmissionService.java (4)

76-91: 세션 키 생성 로직이 적절합니다

사용자 ID와 UUID를 조합한 세션 키 생성 방식은 충돌 가능성이 매우 낮고, WebSocket 세션 식별에 적합합니다.


116-118: 타임아웃 값 증가에 대한 설명이 필요합니다

타임아웃이 60초에서 100초로 증가했습니다. 이는 복잡한 문제나 다중 테스트케이스 처리를 위한 것으로 보이나, 구체적인 이유를 문서화하면 좋겠습니다.


107-122: 이벤트 기반 아키텍처 전환이 잘 구현되었습니다

SSE에서 WebSocket 이벤트 기반으로의 전환이 깔끔하게 처리되었습니다. 초기화, 진행 상황, 최종 결과의 이벤트 발행 흐름이 명확합니다.


163-191: 비동기 테스트케이스 실행 로직이 적절합니다

seqId 추가로 테스트케이스별 추적이 가능해졌고, 이벤트 발행을 통한 실시간 업데이트가 잘 구현되었습니다.

src/main/java/org/ezcode/codetest/infrastructure/event/dto/submission/response/InitTestcaseListResponse.java (1)

9-32: WebSocket 응답 DTO가 잘 설계되었습니다

Java record를 활용한 불변 DTO 설계가 적절하고, Swagger 문서화도 충실합니다. 팩토리 메서드들이 간결하고 명확합니다.

src/main/resources/templates/submit-test.html (2)

40-48: 전역 변수 사용이 적절합니다

stompClientsessionKey를 전역 변수로 관리하는 것은 단일 페이지 애플리케이션에서 적절한 선택입니다.


110-115: 테스트케이스 업데이트 UI가 잘 구현되었습니다

각 테스트케이스의 상태에 따라 CSS 클래스를 변경하고 실행 시간과 메모리 사용량을 표시하는 로직이 명확합니다.

Comment on lines +17 to +22
private static SubmissionExceptionCode resolveCode(Throwable t) {
if (t instanceof SubmissionException se) {
return (SubmissionExceptionCode) se.getResponseCode();
}
return SubmissionExceptionCode.UNKNOWN_ERROR;
}
Copy link

@coderabbitai coderabbitai bot Jun 25, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

안전하지 않은 캐스팅을 수정해주세요.

19번 라인의 (SubmissionExceptionCode) se.getResponseCode() 캐스팅이 안전하지 않습니다. getResponseCode()SubmissionExceptionCode를 반환한다는 보장이 없어 ClassCastException이 발생할 수 있습니다.

다음과 같이 안전한 캐스팅으로 수정하는 것을 권장합니다:

-    private static SubmissionExceptionCode  resolveCode(Throwable t) {
+    private static SubmissionExceptionCode resolveCode(Throwable t) {
         if (t instanceof SubmissionException se) {
-            return (SubmissionExceptionCode) se.getResponseCode();
+            var responseCode = se.getResponseCode();
+            return responseCode instanceof SubmissionExceptionCode ? 
+                (SubmissionExceptionCode) responseCode : SubmissionExceptionCode.UNKNOWN_ERROR;
         }
         return SubmissionExceptionCode.UNKNOWN_ERROR;
     }
📝 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
private static SubmissionExceptionCode resolveCode(Throwable t) {
if (t instanceof SubmissionException se) {
return (SubmissionExceptionCode) se.getResponseCode();
}
return SubmissionExceptionCode.UNKNOWN_ERROR;
}
private static SubmissionExceptionCode resolveCode(Throwable t) {
if (t instanceof SubmissionException se) {
var responseCode = se.getResponseCode();
return responseCode instanceof SubmissionExceptionCode ?
(SubmissionExceptionCode) responseCode : SubmissionExceptionCode.UNKNOWN_ERROR;
}
return SubmissionExceptionCode.UNKNOWN_ERROR;
}
🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/application/submission/dto/event/SubmissionErrorEvent.java
lines 17 to 22, the cast on line 19 from se.getResponseCode() to
SubmissionExceptionCode is unsafe and may cause ClassCastException. To fix this,
avoid direct casting and instead check if the returned object is an instance of
SubmissionExceptionCode before casting, or modify the method to return
SubmissionExceptionCode directly if possible, ensuring type safety.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

나중에 SubmissionException으로 JudgeClient에서 다 랩핑해서 넘길 거야

Copy link

Choose a reason for hiding this comment

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

@thezz9 아, 그렇다면 현재 코드는 그대로 두시고 향후 JudgeClient에서 모든 예외를 SubmissionException으로 래핑해서 넘겨주시면 이 캐스팅 문제가 자연스럽게 해결되겠네요! 👍


✏️ Learnings added
Learnt from: thezz9
PR: ezcode-my/backend#98
File: src/main/java/org/ezcode/codetest/application/submission/dto/event/SubmissionErrorEvent.java:17-22
Timestamp: 2025-06-25T01:24:30.754Z
Learning: JudgeClient에서 모든 예외를 SubmissionException으로 래핑해서 넘길 계획이므로, SubmissionErrorEvent.resolveCode() 메서드의 캐스팅은 안전할 예정임.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +87 to +96
stompClient.connect(
{},
() => {
const base = `/topic/submission/${sessionKey}`;
stompClient.subscribe(`${base}/init`, msg => handleInit(JSON.parse(msg.body)));
stompClient.subscribe(`${base}/case`, msg => handleCase(JSON.parse(msg.body)));
stompClient.subscribe(`${base}/final`, msg => handleFinal(JSON.parse(msg.body)));
},
err => console.error('STOMP 연결 오류:', err)
);
Copy link

@coderabbitai coderabbitai bot Jun 25, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

에러 이벤트 구독이 누락되었습니다

서버에서 /error 토픽으로 에러 메시지를 전송하지만, 클라이언트에서 구독하지 않고 있습니다.

에러 처리를 위한 구독을 추가하세요:

 stompClient.connect(
     {},
     () => {
         const base = `/topic/submission/${sessionKey}`;
         stompClient.subscribe(`${base}/init`, msg => handleInit(JSON.parse(msg.body)));
         stompClient.subscribe(`${base}/case`, msg => handleCase(JSON.parse(msg.body)));
         stompClient.subscribe(`${base}/final`, msg => handleFinal(JSON.parse(msg.body)));
+        stompClient.subscribe(`${base}/error`, msg => handleError(JSON.parse(msg.body)));
     },
     err => console.error('STOMP 연결 오류:', err)
 );

에러 핸들러도 추가해야 합니다:

function handleError(error) {
    const errorDiv = document.createElement('div');
    errorDiv.style.color = 'red';
    errorDiv.textContent = `오류 발생: ${error.message || '알 수 없는 오류'}`;
    judgeEl.appendChild(errorDiv);
    if (stompClient && stompClient.connected) {
        stompClient.disconnect();
    }
}
🤖 Prompt for AI Agents
In src/main/resources/templates/submit-test.html around lines 87 to 96, the
client is missing a subscription to the server's /error topic for error
messages. Add a stompClient.subscribe call for the
/topic/submission/${sessionKey}/error topic inside the connect callback, and
pass a handler that parses the message and calls a new handleError function.
Implement the handleError function as described, creating a red-colored div
displaying the error message, appending it to judgeEl, and disconnecting
stompClient if connected.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

오키~

Copy link

Choose a reason for hiding this comment

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

@thezz9 네! 에러 처리 추가하시면 WebSocket 통신이 더 안정적으로 작동할 거예요. 구현하시다가 궁금한 점 있으시면 언제든 말씀해 주세요! 😊

@thezz9 thezz9 merged commit 2ce16d7 into dev Jun 25, 2025
2 checks passed
@thezz9 thezz9 deleted the feat/submission-ws branch June 25, 2025 02:15
@thezz9 thezz9 added the enhancement New feature or request label Jun 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants