[spring-vue] 웹소켓으로 채팅 구현하기 (5) - vue 화면 설명

xxx-sj·2023년 11월 12일
0

웹소켓

목록 보기
5/5

화면을 개발하면서 생각보다 기록해두어야 할 부분이 많다고 생각하여 추가로 글을 작성합니다.

프로젝트 생성부분은 프로젝트 생성 부분을 참고해주세요

📘vue.config.js

먼저 vue.config 부분입니다. spring과 vue를 같이 개발시 npm run serve로 front server를 띄울 때 back-end server와 통신을 하기위해 설정합니다.

const { defineConfig } = require('@vue/cli-service')

const host = "localhost"; //back-end host
const port = "8081"; //back-end port
module.exports = defineConfig({
  transpileDependencies: true,

  outputDir: "../back-end/src/main/resources/static", //webpack build 시 결과물 위치


  devServer: {
    hot: true,
    proxy: {
      //http://localhost:8080/api/ -> http://localhost:8081/api/
      '/api/': {
        target: `http://${host}:${port}`,
        changeOrigin: true,
      },

      '/ws/': {
        target: `ws://${host}:${port}`,
        changeOrigin: false,
        ws: true,
      }
    }
  }
})

> outputDir path 설정은 resources/static 이거나, classpath 내 public 폴더도 가능합니다 참고

여기서 주의해서 봐야하는 부분은 proxy 부분입니다. 작성한 부분을 자세히 보자면

vue에서 /api/로 보내는 요청은 target 으로 변경되어 요청됩니다.
즉, 현재 front-server가 8080으로 떠있다면 axios를 이용해 localhost:8080/api/** 로의 요청은 localhost:8081/api/** 로 변경됩니다.

다음 /ws/또한 같은 의미로 사용됩니다. /ws/는 vue에서 웹소켓을 이용 할 때 사용합니다.

📘AXIOS 사용 시

front에서 back-end와 통신 시 axios를 많이 사용하실텐데요,여기서 주의해야할 점은 fetch할 때 input 부분입니다. 처음 input 부분에 다음과 같이 입력했는데 오류가 발생했었습니다.

fetch("http://localhost:8081/api/v1/room, ...)

개발환경에서 개발 시 위에서 설정한 대로

fetch("http://localhost:8080/api/v1/room,) 또는
fetch("/api/v1/room", )

으로 사용하셔야 합니다.

배포 후 빌드 시에는 localhost:8081로 설정되어있어야 합니다

📘STOMP

📗Client 객체 생성

해당 프로젝트에서 사용한 @stomp/stompjs 사용법은 다음과 같습니다.

const client = new Client({//config});
config 부분에는 어떤 속성들이 있는지 확인해봅시다. Client를 따라 들어가보면
와 같은 생성자가 있는 것을 확인할 수 있습니다.
저희가 여기서 봐야하는 부분은 생성자의 인자에 있는 StompConfig 부분입니다.

  • StompConfig
export class StompConfig {
  /**
   * See [Client#brokerURL]{@link Client#brokerURL}.
   */
  public brokerURL?: string;

  /**
   * See [Client#stompVersions]{@link Client#stompVersions}.
   */
  public stompVersions?: Versions;

  /**
   * See [Client#webSocketFactory]{@link Client#webSocketFactory}.
   */
  public webSocketFactory?: () => any;

  /**
   * See [Client#connectionTimeout]{@link Client#connectionTimeout}.
   */
  public connectionTimeout?: number;

  /**
   * See [Client#reconnectDelay]{@link Client#reconnectDelay}.
   */
  public reconnectDelay?: number;

  /**
   * See [Client#heartbeatIncoming]{@link Client#heartbeatIncoming}.
   */
  public heartbeatIncoming?: number;

  /**
   * See [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}.
   */
  public heartbeatOutgoing?: number;

  /**
   * See [Client#splitLargeFrames]{@link Client#splitLargeFrames}.
   */
  public splitLargeFrames?: boolean;

  /**
   * See [Client#forceBinaryWSFrames]{@link Client#forceBinaryWSFrames}.
   */
  public forceBinaryWSFrames?: boolean;

  /**
   * See [Client#appendMissingNULLonIncoming]{@link Client#appendMissingNULLonIncoming}.
   */
  public appendMissingNULLonIncoming?: boolean;

  /**
   * See [Client#maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}.
   */
  public maxWebSocketChunkSize?: number;

  /**
   * See [Client#connectHeaders]{@link Client#connectHeaders}.
   */
  public connectHeaders?: StompHeaders;

  /**
   * See [Client#disconnectHeaders]{@link Client#disconnectHeaders}.
   */
  public disconnectHeaders?: StompHeaders;

  /**
   * See [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}.
   */
  public onUnhandledMessage?: messageCallbackType;

  /**
   * See [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}.
   */
  public onUnhandledReceipt?: frameCallbackType;

  /**
   * See [Client#onUnhandledFrame]{@link Client#onUnhandledFrame}.
   */
  public onUnhandledFrame?: frameCallbackType;

  /**
   * See [Client#beforeConnect]{@link Client#beforeConnect}.
   */
  public beforeConnect?: () => void | Promise<void>;

  /**
   * See [Client#onConnect]{@link Client#onConnect}.
   */
  public onConnect?: frameCallbackType;

  /**
   * See [Client#onDisconnect]{@link Client#onDisconnect}.
   */
  public onDisconnect?: frameCallbackType;

  /**
   * See [Client#onStompError]{@link Client#onStompError}.
   */
  public onStompError?: frameCallbackType;

  /**
   * See [Client#onWebSocketClose]{@link Client#onWebSocketClose}.
   */
  public onWebSocketClose?: closeEventCallbackType;

  /**
   * See [Client#onWebSocketError]{@link Client#onWebSocketError}.
   */
  public onWebSocketError?: wsErrorCallbackType;

  /**
   * See [Client#logRawCommunication]{@link Client#logRawCommunication}.
   */
  public logRawCommunication?: boolean;

  /**
   * See [Client#debug]{@link Client#debug}.
   */
  public debug?: debugFnType;

  /**
   * See [Client#discardWebsocketOnCommFailure]{@link Client#discardWebsocketOnCommFailure}.
   */
  public discardWebsocketOnCommFailure?: boolean;

  /**
   * See [Client#onChangeState]{@link Client#onChangeState}.
   */
  public onChangeState?: (state: ActivationState) => void;
}

간단하게 저희가 사용하는 것은 brokerURL, onConnect, onDisconnect, onStompError, onWebSocketClose, onWebSocketError 등이 있을 것 같습니다.
정의해야 하는 속성과 함께 client config를 완성시키면 다음과 같습니다.

 this.websocketClient = new Client({
	brokerURL: "ws://localhost:8080/ws/init",
    onConnect: () => {
    this.websocketClient.subscribe(`/sub/room/${this.currentRoom.id}`, msg => {
            this.messages.push(msg.body);
          });

          this.websocketClient.publish({
            destination: `/pub/room/${this.currentRoom.id}/entered`,
            body: JSON.stringify({message: `${this.textMessage}`, writer: "user1"}),
          });

    },
    onDisconnect: (frame) => {},
    onStompError: (frame) => {},
    onWebSocketClose: (_) => {}
    onWebSocketError: (_) => {},
});

추가로 마지막에 client.activate()를 호출해주셔야 합니다.

📗subscribe 하기

클라이언트를 생성했다면 다음으로는 subscribe입니다. subscribe은 다음과 같습니다.

client.subscribe(destination, callback=(message:IMessage) => {}, headers=[key:String])

프로젝트에서 사용한 코드는 다음과 같습니다.

this.websocketClient.subscribe(`/sub/room/${this.currentRoom.id}`, msg => {
            this.messages.push(msg.body);
          })

destination = /sub/room/${this.currentRoom.id}
callback = msg => { this.messages.push(msg.body); }

📗publish 하기

sub가 비슷합니다.


client.publish(params: IPublishParams) == 
client.publish({destination: "/queue/test", body: "Hello, STOMP"});
---
export interface IPublishParams {
  /**
   * destination end point
   */
  destination: string;
  /**
   * headers (optional)
   */
  headers?: StompHeaders;
  /**
   * body (optional)
   */
  body?: string;
  /**
   * binary body (optional)
   */
  binaryBody?: Uint8Array;
  /**
   * By default, a `content-length` header will be added in the Frame to the broker.
   * Set it to `true` for the header to be skipped.
   */
  skipContentLengthHeader?: boolean;
}

프로젝트에서 사용한 코드는 다음과 같습니다.

const body = {message: `${this.textMessage}`, writer: "user1"}

this.websocketClient.publish({
        destination: `/pub/room/${this.currentRoom.id}`,
        body: JSON.stringify(body),
      });

📗disconnect하기

disconnect는 activate 와 반대로 deactivate 를 사용하면 됩니다.

this.websocketClient.deactivate();

전체 코드는 github에서 확인 가능합니다.

profile
틀려도 일단 기록하자

0개의 댓글