ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

728x90

<๋ชฉ์ฐจ>

    Spring

    ๐Ÿ‘ Web Socket Dependency ์ถ”๊ฐ€

    ํ•ด๋‹น ํŒจํ‚ค์ง€์— stom๋„ ๋‚ด์žฅ๋˜์–ด์žˆ๋‹ค.

    dependencies {
    	implementation 'org.springframework.boot:spring-boot-starter-websocket'
    }

     

    ๐Ÿ‘ Web Socket Config ์„ค์ •

    โœ”๏ธ ํ—ˆ์šฉํ•˜๋Š” origin์— vue adress๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

    package com.project.mnm.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.messaging.simp.config.MessageBrokerRegistry;
    import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
    import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
    import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
    
    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    
        @Override
        public void configureMessageBroker(MessageBrokerRegistry config) {
            config.enableSimpleBroker("/send");
        }
    
        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry) {
            registry.addEndpoint("/").setAllowedOrigins("http://localhost:8080").withSockJS();
        }
    }

     

     

    ๐Ÿ‘ Chatting Model

    package com.project.mnm.domain;
    
    import lombok.*;
    
    import javax.persistence.*;
    import java.sql.Timestamp;
    
    @Builder
    @Getter
    @Setter
    @ToString
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity
    @Table(name = "chattings")
    public class Chatting {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private long id;
        @OneToOne
        @JoinColumn(name = "uid")
        private User user;
        @Column(name = "message")
        private String message;
        @Column(name = "send_at")
        private Timestamp sendAt;
        @Column(name = "is_request")
        private Boolean isRequest;
    }

     

    ๐Ÿ‘ Chatting Service

    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ฑ„ํŒ… ๋‚ด์šฉ์„ ์ €์žฅํ•œ๋‹ค.

    package com.project.mnm.service;
    
    import com.project.mnm.domain.Chatting;
    import com.project.mnm.domain.User;
    import com.project.mnm.repository.ChattingRepository;
    import com.project.mnm.repository.UserRepository;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Service;
    
    @RequiredArgsConstructor
    @Service
    public class ChattingService {
        private final UserRepository userRepository;
        private final ChattingRepository chattingRepository;
    
        public Chatting chattingHandler(Chatting chatting) {
            User user = userRepository.findById(chatting.getUser().getId())
                    .orElseThrow(() -> new IllegalArgumentException("๊ฐ€์ž…๋˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž์ž…๋‹ˆ๋‹ค."));
    
            chatting.setUser(user);
    
            return chattingRepository.save(chatting);
        }
    }

     

    ๐Ÿ‘ Chatting Controller

    โœ”๏ธ /receive๋กœ ์ฑ„ํŒ… ๋‚ด์šฉ์„ ๋ฐ›๋Š”๋‹ค.

    โœ”๏ธ /send๋กœ ์ฑ„ํŒ… ๋‚ด์šฉ์„ ๋ณด๋‚ธ๋‹ค.

    package com.project.mnm.controller;
    
    import com.project.mnm.domain.Chatting;
    import com.project.mnm.service.ChattingService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.messaging.handler.annotation.MessageMapping;
    import org.springframework.messaging.handler.annotation.SendTo;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class ChattingController {
        @Autowired
        private ChattingService chattingService;
    
        @MessageMapping("/receive")
        @SendTo("/send")
        public Chatting chattingHandler(Chatting chatting) {
            return chattingService.chattingHandler(chatting);
        }
    }

     

    Vue

    ๐Ÿ‘ Web Socket, Stomp ์„ค์น˜

    โœ”๏ธ sockjs-client๋Š” ์›น์†Œ์ผ“ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์„ค์น˜ํ•œ๋‹ค. 

    โœ”๏ธ webstomp-client๋Š” stomp์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์„ค์น˜ํ•œ๋‹ค.

    (test_front) D:\mnm\project-mnm-test-front>npm i webstomp-client sockjs-client

     

    ๐Ÿ‘ ChattingView.vue

    <template>
        <v-container>
            <v-expansion-panel>
            <v-expansion-panel-header><h1>์ฑ„ํŒ…</h1></v-expansion-panel-header>
            <v-expansion-panel-content>
            <v-card class="mt-2">
                <v-card-title><h2>์ฑ„ํŒ… ํ…Œ์ŠคํŠธ</h2></v-card-title>
                <v-card-text>
                          <v-alert
                            dense
                            type="info"
                            color="teal lighten-3"
                        >๋‘๊ฐœ ์ด์ƒ์˜ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„์›Œ ๊ฐ„๋‹จํ•œ ์ฑ„ํŒ…์„ ํ™•์ธํ•ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค.</v-alert>
                        <v-container>
                            uid : 
                            <input
                                v-model="uid"
                                type="text"
                            >
                            message : 
                            <input
                                v-model="message"
                                type="text"
                                @keyup="sendMessage"
                            >
                            <div
                                class="mt-2"
                                v-for="(item, idx) in recvList"
                                :key="idx"
                            >
                                <v-card
                                    class="mt-2 mb-2"
                                    color="teal lighten-3"
                                    dark
                                    max-width="400"
                                >
                                    <v-card-text>
                                        <div>uid : {{ item.user.id }}</div>
                                        <div>{{ item.message }}</div>
                                        <div>{{ item.sendAt }}</div>
                                    </v-card-text>
                                </v-card>
                            </div>
                        </v-container>
                </v-card-text>
            </v-card>
            </v-expansion-panel-content>
        </v-expansion-panel>
        </v-container>
    </template>
    
    <script lang="js">
    import Stomp from 'webstomp-client'
    import SockJS from 'sockjs-client'
    
    export default {
        data() {
            return {
                uid: '',
                message: '',
                recvList: []
                }
        },
        created() {
            this.connect() // ChattingView.vue ์ƒ์„ฑ ์‹œ ์†Œ์ผ“ ์—ฐ๊ฒฐ ์‹œ๋„
        },
        methods: {
            sendMessage(e) {
                if(e.keyCode === 13 && this.uid !== '' && this.message !== '') {
                    this.send();
                    this.message = '';
                }
            },
            send() {
                console.log("Send message:" + this.message);
                if (this.stompClient && this.stompClient.connected) {
                    const msg = { 
                    user: {
                        id: this.uid
                    },
                    message: this.message,
                    sendAt: Date.now(),
                    isRequest: false,
                    };
                    this.stompClient.send("/receive", JSON.stringify(msg), {});
                }
            }, 
            connect() {
                const serverURL = "http://localhost:5050"
                let socket = new SockJS(serverURL);
                this.stompClient = Stomp.over(socket);
                console.log(`์†Œ์ผ“ ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ฃผ์†Œ: ${serverURL}`)
                this.stompClient.connect(
                    {},
                    frame => {
                    this.connected = true;
                    console.log('์†Œ์ผ“ ์—ฐ๊ฒฐ ์„ฑ๊ณต', frame);
                    this.stompClient.subscribe("/send", res => {
                        console.log('๊ตฌ๋…์œผ๋กœ ๋ฐ›์€ ๋ฉ”์‹œ์ง€ ์ž…๋‹ˆ๋‹ค.', res.body);
                        this.recvList.push(JSON.parse(res.body))
                    });
                    },
                    error => {
                    console.log('์†Œ์ผ“ ์—ฐ๊ฒฐ ์‹คํŒจ', error);
                    this.connected = false;
                    } 
                );               
            }
        }
    }
    </script>

     

    ๐Ÿ‘ ์ฑ„ํŒ… ํ…Œ์ŠคํŠธ

    ์ฐฝ์„ ์—ฌ๋Ÿฌ๊ฐœ ๋„์›Œ์„œ ์ฑ„ํŒ…์„ ํ…Œ์ŠคํŠธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋„ ์ฑ„ํŒ… ๋‚ด์šฉ์ด ์ž˜ ์ €์žฅ๋˜๋Š” ๋ชจ์Šต์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

     


    Ref.

    vue spring boot ์›น์†Œ์ผ“ ์ฑ„ํŒ… ๋งŒ๋“ค๊ธฐ (velog.io)

     

    728x90
    ๋Œ“๊ธ€
    ๊ณต์ง€์‚ฌํ•ญ
    ์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€