728x90
목차 : https://okane-on-cliff.tistory.com/239
0. .env.local
VUE_APP_URL=http://localhost:9998
여기에 환경변수를 등록해 처음엔 localhost로 작업하다가 배포할 때 이 코드만 바꾸면 되도록 했다.
1. router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: () => import('@/views/HomeView.vue')
},
{
path: '/ChatView/:roomName/:userId',
name: 'chat',
component: () => import('@/views/ChatView.vue')
}
]
const router = new VueRouter({
routes
})
export default router
특이점은 chatview에서 path에 아이디와 방이름을 넣어 새로고침을 해도 폼이 유지되게 만들었다 이 부분에 대해서는 조치가 필요할 것 같다. url변경으로 다른사람을 쫒아 낼 수 있기 때문에 피해야 될 거 같다.
2. HomeView.vue
<template>
<div>
채팅방 목록
<button @click="makeRoom">방 만들기</button>
<hr>
<div v-if="roomList.length == 0">방없음</div>
<div v-else>
<span class="room" v-for="(room, idx) in roomList" :key="idx" @click="enterChat(room)">
{{room.roomName}}<br>
</span>
</div>
</div>
</template>
<script>
import axios from 'axios'
import Stomp from 'webstomp-client'
import SockJS from 'sockjs-client'
export default {
data() {
return {
roomList: [],
}
},
created() {
this.start();
},
methods: {
start() {
if(this.$store.state.stompClient) this.subscribeCancel();
const socket = new SockJS(process.env.VUE_APP_URL + '/realsocket/websocket');
this.$store.state.stompClient = Stomp.over(socket);
this.$store.state.stompClient.connect(
{},
() => {
console.log("야호 연결성공요")
// 모든 구독을 끊어요
this.subscribeCancel()
const subscribeId = this.$store.state.stompClient.subscribe("/topic/roomList", (e) => {
console.log(e.body)
//이 함수를 써서 vue객체의 룸리스트를 업데이트해요
console.log("방리스트를 업데이트 할게요")
this.updateRoomList();
//e로 던저서 e.body로 받았어요
})
// 구독 정보를 업데이트해요
this.$store.state.subscribeList.push(subscribeId);
this.updateRoomList();
});
},
updateRoomList() {
axios.get(process.env.VUE_APP_URL + '/realsocket/chatingRoomList').then(({data}) => {
this.roomList = data;
})
},
subscribeCancel() {
for(let sub of this.$store.state.subscribeList) {
this.$store.state.stompClient.unsubscribe(sub.id);
}
this.$store.state.subscribeList = [];
},
makeRoom() {
var roomName = prompt("방제목을 입력해주세요");
if (!roomName) return;
var userId = prompt("닉네임을 입력해주세요");
if (!userId) return;
console.log(roomName, userId)
//채팅방이름과 내 닉네임을 가지고 채팅방을 만드는 메서드를 호출해요
axios.post(process.env.VUE_APP_URL + '/realsocket/chatingRoom', {"roomName": roomName, "userId": userId}).then(({data}) => {
console.log(data)
sessionStorage.setItem("roomNumber", data)
// 내가 방을 만든 사실을 계속 방에 있을 구독자들에게 알려줘요
this.$store.state.stompClient.send("/socket/roomList")
// 채팅방으로 이동해요
this.$router.push( {path: '/ChatView/' + roomName + "/" + userId} )
})
},
enterChat(room) {
console.log(room)
var userId = prompt("닉네임을 입력해주세요");
if (!userId) return;
axios.post(process.env.VUE_APP_URL + '/realsocket/enterChat', {"roomNumber": room.roomNumber, "userId": userId}).then(({data}) => {
sessionStorage.setItem("roomNumber", room.roomNumber);
//notification에서 room인스턴스 찾아서 room인스턴스의 users를 return하고 받는 쪽에서는 user를 업데이트
this.$store.state.stompClient.send("/socket/notification/" + sessionStorage.getItem("roomNumber"))
this.$router.push( {path: '/ChatView/' + data + "/" + userId} )
})
}
},
};
</script>
<style>
.room:hover {
cursor: pointer;
}
</style>
페이지가 로드 될 때 마다 socket연결을 하도록 했다. 구독정보도 같이 초기화 했다. 룸리스트를 가져오는 소켓핸들러도 구독되어 있어서 원할 때 룸리스트를 가져올 수 있도록 했다. stomp객체는 store에 저장했는데 아마 사용자가 새로고침은 하지않는 사람이라면 내가 원하는대로 작동 한다. 하지만 이상한 유저도 있다는 생각에 컴포넌트마다 socket연결을 하도록 했다.
컴포넌트가 변하면 구독정보를 룸리스트에서 룸Dto로 바꾸면 되었다. 하지만 convertAndSend 같은걸 있단걸 미리 알았다면 좀더 생각했을 거 같다.
사용자가 채팅방에 들어가는 동작도 HomeView에 넣어서 ChatView에서 새로고침해도 한번만 핸들러에 send되도록 했다.
3. ChatView.vue
<template>
<div>
방이름 : {{this.$route.params.roomName}}<button @click="outChat">나가기</button><br>
참가자 :
<span v-for="(user, idx) in userList" :key="idx">
{{user}}
</span>
<hr>
<input v-model="message" />
<button @click="submitMessage">전송</button>
<hr>
<div>
<div v-for="(m, idx) in messageList" :key="idx">
<div v-if="m.nickname">{{m.nickname}} :: {{m.message}} / {{m.date.getHours()}}시 {{m.date.getMinutes()}}분 {{m.date.getSeconds()}}초</div>
<div v-else>{{m.message}}</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
import Stomp from 'webstomp-client'
import SockJS from 'sockjs-client'
export default {
data() {
return {
userList: [],
message: "",
messageList: [],
routerFlag: false
}
},
created() {
this.start();
},
methods: {
//시작할 땐 stompClient가 있는지 없는지 검사해봐요 새로고침하면 stompClient가 없어지니까요
start() {
//stompClient가 없다면 먼저 stompClient객체를 만들어 줘요
if (!this.$store.state.stompClient) {
const socket = new SockJS(process.env.VUE_APP_URL + '/realsocket/websocket');
this.$store.state.stompClient = Stomp.over(socket);
this.$store.state.stompClient.connect(
{},
() => {
console.log("야호 연결성공요")
this.updateSubscribe();
});
}
//stompClient가 있다면 바로 subscribe를 갱신해요
else {
this.updateSubscribe();
}
},
subscribeCancel() {
for(let sub of this.$store.state.subscribeList) {
this.$store.state.stompClient.unsubscribe(sub.id);
}
this.$store.state.subscribeList = [];
},
updateSubscribe() {
console.log(sessionStorage.getItem("roomNumber"))
axios.post(process.env.VUE_APP_URL + '/realsocket/findRoom', sessionStorage.getItem("roomNumber"), { headers: {'Content-Type': 'text/plain'}}).then(({data}) => {
this.userList = data.users;
// 방리스트에 관한 구독을 끊어요
this.subscribeCancel()
// 들어간 채팅방에 대한 구독을 해요
// 첫 번째 구독은 메세지에 대한 구독이에요
console.log("data.roomNumber : ", data.roomNumber)
const messageSubscribeId = this.$store.state.stompClient.subscribe("/topic/message/" + data.roomNumber, (e) => {
//메세지를 받았어요 메세지는 e.body에 있고 {message: ~~~, nickname: ~~, date: ~~~}에요
var rawData = JSON.parse(e.body);
rawData.date = new Date(rawData.date);
this.messageList.push(rawData);
})
this.$store.state.subscribeList.push(messageSubscribeId);
// 두 번째 구독은 채팅방 입장 퇴장에 대한 구독이에요
const NoteSubscribeId = this.$store.state.stompClient.subscribe("/topic/notification/" + data.roomNumber, (e) => {
console.log("****************************************")
console.log(JSON.parse(e.body));
var newList = JSON.parse(e.body);
var outbound = this.userList.filter(x => !newList.includes(x))
var inbound = newList.filter(x => !this.userList.includes(x))
for (var out of outbound) {
console.log(out, "가 나갔어")
var rawData1 = {
message: out + "가 나갔어요"
}
this.messageList.push(rawData1)
}
for (var inb of inbound) {
console.log(inb, "가 들어왔어")
var rawData2 = {
message: inb + "가 들어왔어요"
}
this.messageList.push(rawData2)
}
//유저 목록을 갱신해요
this.userList = newList;
})
this.$store.state.subscribeList.push(NoteSubscribeId);
});
},
submitMessage() {
// 전달할 값은 {아이디: 내아이디, 메세지: this.message}에요
const data = {
message : this.message,
nickname : this.$route.params.userId,
}
this.$store.state.stompClient.send("/socket/sendMessage/" + sessionStorage.getItem("roomNumber"), JSON.stringify(data), {})
this.message = "";
},
async outing() {
alert('나갈게요')
// 새 핸들러 만들어서 방넘버와 아이디를 주면 방인스턴스의 users에서 아이디 지우고 send()
// 또 뒤로가기는 따로 처리해야 되니까 outChat에 인자 달아서 버튼을 눌러서 실행되는건지 뒤로가기로 실행되는건지 구분
await axios.post(process.env.VUE_APP_URL + '/realsocket/outChat', {"roomNumber": sessionStorage.getItem("roomNumber"), "userId": this.$route.params.userId}).then(() => {
this.$store.state.stompClient.send("/socket/notification/" + sessionStorage.getItem("roomNumber"))
})
},
outChat() {
this.$router.push('/');
}
},
async beforeRouteLeave (to, from, next) {
await this.outing();
next();
}
};
</script>
<style>
</style>
들어올 때 구독정보를 초기화 하고 나갈 때, 메세지를 보낼 때 등을 동작을 정의했다.
728x90