WebSocket 실시간 통신 서비스 구축: 프론트엔드와 백엔드 분리 버전

백엔드 구성

의존성 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

WebSocket 설정 클래스

package com.example.websocket.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfiguration {

    @Bean
    public ServerEndpointExporter endpointExporter() {
        return new ServerEndpointExporter();
    }
}

WebSocket 서버 엔드포인트

package com.example.websocket.endpoint;

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/api/ws/connect/{userId}")
@Component
public class NotificationEndpoint {

    private static final ConcurrentHashMap<String, NotificationEndpoint> connections = new ConcurrentHashMap<>();
    
    private Session clientSession;
    private String userId;

    @OnOpen
    public void handleOpen(Session session, @PathParam("userId") String userId) {
        this.userId = userId;
        this.clientSession = session;
        connections.put(userId, this);
        
        try {
            sendResponse("connected");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @OnClose
    public void handleClose() {
        connections.remove(this.userId);
    }

    @OnMessage
    public void handleMessage(String message, Session session) {
        System.out.println("수신 메시지: " + message + " from " + session.getId());
    }

    @OnError
    public void handleError(Session session, Throwable throwable) {
        throwable.printStackTrace();
    }

    public void sendResponse(String response) {
        try {
            if (this.clientSession != null && this.clientSession.isOpen()) {
                this.clientSession.getBasicRemote().sendText(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void pushToUser(String targetUser, String notification) {
        NotificationEndpoint endpoint = connections.get(targetUser);
        if (endpoint != null) {
            endpoint.sendResponse(notification);
        }
    }
}

프론트엔드 구성

Vue 컴포넌트 (ChatView.vue)

<template>
  <div class="notification-panel">
    <button @click="transmitMessage('https://example.com 방문')">메시지 전송</button>
    <div v-for="(msg, index) in receivedMessages" :key="index">
      {{ msg }}
    </div>
  </div>
</template>

<script>
export default {
  name: 'RealTimeNotifications',
  data() {
    return {
      receivedMessages: [],
      socketConnection: null
    }
  },
  mounted() {
    this.establishConnection()
  },
  beforeDestroy() {
    if (this.socketConnection) {
      this.socketConnection.close()
    }
  },
  methods: {
    establishConnection() {
      const wsUrl = `ws://localhost:서버포트/api/ws/connect/${사용자ID}`
      
      if (!window.WebSocket) {
        console.error('브라우저가 WebSocket을 지원하지 않습니다')
        this.$message.error('WebSocket 미지원으로 푸시 기능 사용 불가!')
        return
      }

      this.socketConnection = new WebSocket(wsUrl)
      
      this.socketConnection.onopen = () => {
        this.transmitMessage('초기 연결 완료')
        console.log('WebSocket 연결 성공')
      }
      
      this.socketConnection.onmessage = (event) => {
        this.receivedMessages.push(event.data)
      }
      
      this.socketConnection.onerror = (error) => {
        console.error('연결 오류:', error)
      }
      
      this.socketConnection.onclose = () => {
        console.log('연결 종료됨')
      }
    },
    
    transmitMessage(content) {
      if (this.socketConnection && this.socketConnection.readyState === WebSocket.OPEN) {
        this.socketConnection.send(content)
      }
    }
  }
}
</script>

태그: websocket Spring Boot Vue.js 실시간 통신 서버-클라이언트 통신

7월 31일 05:36에 게시됨