BLC Project 소켓 연결

한강섭·2025년 6월 24일

BLC 프로젝트

목록 보기
1/9
post-thumbnail

프론트엔드 소켓(채팅) 연결


연결 계획 단계별 정리

Step 1: REST API 연결

  1. Game Store의 fetchGameDetail 응답 매핑 확인
  2. Chat Store의 fetchChatRooms 응답 매핑 확인
  3. 백엔드 서버 실행 후 API 테스트

Step 2: 더미데이터 제거

  1. loadInitialMessages, startMessageSimulation 삭제
  2. 하드코딩된 메시지들 제거
  3. 빈 채팅방 상태로 만들기

Step 3: 소켓 연결

  1. connectToGame에서 socket.io 연결
  2. 메시지 수신 이벤트 처리
  3. 연결 상태 관리

Step 4: 메시지 전송

  1. sendMessage에서 REST API 호출
  2. 에러 처리 추가
  3. 사용자 정보 연동

Step 5: 테스트 및 디버깅

  1. 브라우저 개발자 도구에서 소켓 연결 확인
  2. 메시지 송수신 테스트
  3. 여러 브라우저 탭에서 실시간 동기화 확인

Step1 작업 완료 요약

목표: REST API 연결 및 더미데이터 → 실제 데이터 전환

첫 번째 단계에서는 기존의 하드코딩된 더미 데이터를 제거하고 백엔드 REST API와 실제 연결했습니다. Game Store와 Chat Store의 fetchGameDetail, fetchChatRooms 메소드를 수정하여 http://localhost:8080/api/games/{gameId}/api/chat-rooms 엔드포인트를 호출하도록 구현했고, API 호출 실패 시에는 폴백으로 더미 데이터를 사용하도록 예외 처리를 추가했습니다.

주요 수정사항 및 문제 해결

GameDetail.vue에 디버그 정보 섹션을 추가하여 API 응답 데이터를 실시간으로 확인할 수 있게 했고, 환경 변수를 VUE_APP_API_BASE_URL로 올바르게 수정했습니다. 가장 중요한 부분은 백엔드 API 응답 구조가 예상과 달랐던 점을 발견하고 해결한 것입니다. 백엔드에서 {homeCode: "키움", awayCode: "두산", gameDateTime: "..."}와 같은 플랫 구조로 데이터를 보내는 것을 확인하고, 프론트엔드 코드를 실제 API 스펙에 맞춰 수정했습니다.

달성 결과 및 다음 단계

이제 프론트엔드가 백엔드와 성공적으로 연결되어 실제 경기 데이터를 받아와서 화면에 표시할 수 있게 되었습니다. /games/1 페이지에서 키움 vs 두산 경기 정보가 API에서 가져온 실제 데이터로 표시되고, 디버그 섹션에서 전체 응답 구조를 확인할 수 있습니다. 다음 단계에서는 더미 메시지 시뮬레이션을 완전히 제거하고 실제 Socket.io 연결을 구현하여 실시간 채팅 기능을 완성할 예정입니다.


Step 2 작업 완료 요약 - 시행착오와 해결 과정

목표와 첫 번째 시행착오

Step 2의 목표는 더미 데이터를 제거하고 실시간 Socket 통신을 구현하는 것이었습니다. 처음에는 Node.js에서 주로 사용하는 Socket.io 방식으로 접근했지만, 백엔드 코드를 확인해보니 Spring Boot에서 사용하는 STOMP + SockJS 방식이었다는 것을 발견했습니다. 이때 중요한 깨달음은 백엔드의 WebSocket 구현 방식에 따라 프론트엔드 연결 방법이 달라진다는 점이었습니다. Socket.io는 Node.js 생태계의 커스텀 프로토콜이고, STOMP는 Spring Boot의 표준 WebSocket 프로토콜이라는 차이를 이해하게 되었습니다.

기술적 해결 과정과 연결 실패 문제

Chat Store를 STOMP 방식으로 전면 개편하면서 @stomp/stompjs 라이브러리를 도입하고, 백엔드의 @MessageMapping("/chat.sendMessage/{gameId}"), @SendTo("/topic/game/{gameId}") 구조에 맞춰 메시지 전송 및 구독 로직을 구현했습니다. 하지만 첫 번째 연결 시도에서 ws://localhost:8080/chat-socket 연결 실패가 발생했습니다. 백엔드 로그를 확인해보니 서버는 정상 작동 중이었고, 문제는 프론트엔드에서 순수 WebSocket(ws://)으로 연결하려 했지만 백엔드는 SockJS 래퍼 방식으로 설정되어 있었다는 점이었습니다.

최종 해결과 성공

sockjs-client 라이브러리를 추가하고 연결 방식을 brokerURL: 'ws://...'에서 webSocketFactory: () => new SockJS('http://...')로 변경했습니다. 핵심은 Spring Boot의 .withSockJS() 설정 때문에 SockJS 래퍼를 통해서만 연결이 가능하다는 점이었고, HTTP 프로토콜로 SockJS 인스턴스를 생성한 후 STOMP 클라이언트에 전달하는 방식으로 해결했습니다. 결과적으로 실시간 WebSocket 연결에 성공했고, 이제 사용자가 메시지를 보내면 백엔드에서 자동으로 DB 저장과 동시에 모든 연결된 클라이언트에게 실시간 브로드캐스트가 가능한 구조가 완성되었습니다. 다음 단계는 사용자 인증 연동과 실제 다중 사용자 테스트입니다.


Step 3 작업 완료 요약

데이터 구조 통합

프론트엔드에서 messageContent, messageType, team 필드로 전송했지만 백엔드 DTO는 content, type, teamId 필드를 기대하고 있어서 Column 'message_content' cannot be null 오류가 발생했습니다. 이를 백엔드 DTO 구조에 맞춰 수정하고 팀 정보를 home/away 문자열에서 teamId: 1/2 숫자로 변환하는 로직을 추가했습니다. 백엔드의 @SendTo("/topic/game/{gameId}") 어노테이션 덕분에 메시지 저장과 실시간 브로드캐스트가 자동으로 처리되어, 사용자가 메시지를 전송하면 DB 저장 후 같은 게임을 구독한 모든 사용자에게 자동으로 실시간 전송하는 구조가 완성되었습니다.


Step 4 작업 완료 요약

Vue 반응성 문제 해결 및 실시간 시스템 완성

실시간 메시지 수신은 정상적으로 작동했지만 화면에 표시되지 않는 Vue 반응성 문제가 발생했습니다. Console에서는 메시지 수신 로그가 확인되고 새로고침하면 메시지가 보였지만, 실시간으로는 화면이 업데이트되지 않았습니다. 원인은 this.homeMessages.push(newMessage) 방식으로 배열을 직접 수정했을 때 Vue의 Proxy 기반 반응성 시스템이 변화를 감지하지 못한 것이었습니다. this.homeMessages = [...this.homeMessages, newMessage] 방식으로 새 배열을 생성하도록 수정하여 Vue가 변화를 감지할 수 있게 만들었습니다. 결과적으로 새로고침 없이 실시간으로 다중 사용자 간 메시지 송수신이 가능한 완전한 채팅 시스템이 구축되었고, 팀별 메시지 분리, 실시간 동기화, 사용자 구분까지 모든 핵심 기능이 정상 작동하는 상태가 되었습니다.


Step 5 테스트 화면

profile
기록하고 공유하는 개발자

2개의 댓글

comment-user-thumbnail
2025년 6월 25일

신기해요

1개의 답글