SMALL
소켓(Socket)이란?
소켓(Socket)은 네트워크 상에서 돌아가는 두 개의 프로그램 간 양방향 통신의 개념이라고 생각하시면 됩니다.
즉, 프로세스가 소켓을 열고, 소켓에 데이터를 넣어 보내거나 데이터를 읽어드리는 것을 의미한다.
구현
1.설치
npm install socket.io-client
npm install express socket.io
2. 서버 구축
server.js
* 이렇게만 하면 cors 문제가 생기니 아래 cors 문제 해결 방안도 같이 봐야합니다
const express = require('express')
const http = require('http')
const { Server } = require('socket.io')
const app = express()
const server = http.createServer(app)
const io = new Server(server);
io.on('connection', (socket) => {
console.log(`user ${socket.id} is connected`)
socket.on('message', data =>{
socket.broadcast.emit('message:received', data)
})
socket.on('disconnect', () => {
console.log(`user ${socket.id} left`)
})
})
server.listen(3000, () => {
console.log('3000 서버 오픈')
})
3. 화면 구축
App.vue
<template>
<div v-if="!joined" class="parent-container">
<div class="name-container">
<input type="text" class="user-name" v-model="currentUser"/>
<button class="join-button" @click="join()">Join</button>
</div>
</div>
<div v-if="joined">
<div class="list-container">
<div v-for="message in messages" :key="message.id">
<b>
{{ message.user }}
</b>
: {{ message.text }}
</div>
</div>
<div class="text-input-container">
<textarea
v-model="text"
class="text-message"
v-on:keyup.enter="sendMessage"
></textarea>
</div>
</div>
</template>
<script>
import io from 'socket.io-client'
export default {
data(){
return {
joined:false,
currentUser:'',
text:'',
messages:[],
}
},
methods:{
join(){
this.joined = true
this.socketInstance = io('http://localhost:3000')
this.socketInstance.on(
"message:received", (data) => {
this.messages = this.messages.concat(data)
}
)
},
sendMessage(){
this.addMessage()
this.text = ''
},
addMessage(){
const message = {
id : new Date().getTime(),
text : this.text,
user: this.currentUser
};
this.messages = this.messages.concat(message);
this.socketInstance .emit('message', message)
}
},
name: 'ChatApp',
}
</script>
<style scoped>
.parent-container{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
position: fixed;
padding-top: 150px;
}
.name-container{
display: flex;
flex-direction: column;
width: 200px;
}
.user-name {
height: 30px;
font-size: 20px;
padding: 5px;
margin-bottom: 5px;
text-align: center;
font-weight: bold;
}
.join-button{
height: 30px;
font-size: 20px;
}
.text-input-container{
height: 100vh;
}
.text-message{
width: 100%;
position: absolute;
bottom: 0px;
height: 70px;
padding: 10px;
box-sizing: border-box;
}
</style>
실행
새 터미널 띄운 후 'node server.js' 입력
node 서버 터미널은 그대로 두고 새 터미널 띄운 후 'npm run serve' 입력
결과 화면
SMALL
에러 (cors)
실행을 시켜보니 프론트 개발자라면 피해갈 수 없는 숙명의 cors 문제가 발생하였다.
'has been blocked by cors policy no 'access-control-allow-origin' header is present on the requested'
해결 방법 1 : cors origin 전체 허용
아까 작성한 server.js 에서 특정 코드를 아래와 같이 origin이 전체 허용하도록 수정하였더니 정상적으로 작동 하였다.
* 수정 전
const io = new Server(server);
* 수정 후
const io = new Server(server, {
cors: {
origin: "*"
}
});
* 하지만 출처가 명확하지 않은 곳에서도 연결을 시도할 수 있으므로 실무에서는 사용하면 위험하다
해결 방법 2 : transports 속성 명시
아까 작성한 App.vue에서 소켓 연동 시켜주는 부분에 transports의 속성을 'websocket'으로 명시해주면 정상 동작한다.
* 수정 전
this.socketInstance = io('http://localhost:3000')
* 수정 후
this.socketInstance = io('http://localhost:3000',{
transports: ["websocket"]
})
최종 결과 화면
이미지와 같이 창을 두개 띄워서 다른 user로 접속 후 한쪽씩 채팅하며 정상동작하는 것을 확인할 수 있다.
* 최종 코드 - server.js
const express = require('express')
const http = require('http')
const { Server } = require('socket.io')
const app = express()
const server = http.createServer(app)
const io = new Server(server, {
cors: {
origin: "*"
}
});
io.on('connection', (socket) => {
console.log(`user ${socket.id} is connected`)
socket.on('message', data =>{
socket.broadcast.emit('message:received', data)
})
socket.on('disconnect', () => {
console.log(`user ${socket.id} left`)
})
})
server.listen(3000, () => {
console.log('3000 서버 오픈')
})
* 최종 코드 - App.vue
<template>
<div v-if="!joined" class="parent-container">
<div class="name-container">
<input type="text" class="user-name" v-model="currentUser"/>
<button class="join-button" @click="join()">Join</button>
</div>
</div>
<div v-if="joined">
<div class="list-container">
<div v-for="message in messages" :key="message.id">
<b>
{{ message.user }}
</b>
: {{ message.text }}
</div>
</div>
<div class="text-input-container">
<textarea
v-model="text"
class="text-message"
v-on:keyup.enter="sendMessage"
></textarea>
</div>
</div>
</template>
<script>
import io from 'socket.io-client'
export default {
data(){
return {
joined:false,
currentUser:'',
text:'',
messages:[],
}
},
methods:{
join(){
this.joined = true
this.socketInstance = io('http://localhost:3000')
this.socketInstance.on(
"message:received", (data) => {
this.messages = this.messages.concat(data)
}
)
},
sendMessage(){
this.addMessage()
this.text = ''
},
addMessage(){
const message = {
id : new Date().getTime(),
text : this.text,
user: this.currentUser
};
this.messages = this.messages.concat(message);
this.socketInstance .emit('message', message)
}
},
name: 'ChatApp',
}
</script>
<style scoped>
.parent-container{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
position: fixed;
padding-top: 150px;
}
.name-container{
display: flex;
flex-direction: column;
width: 200px;
}
.user-name {
height: 30px;
font-size: 20px;
padding: 5px;
margin-bottom: 5px;
text-align: center;
font-weight: bold;
}
.join-button{
height: 30px;
font-size: 20px;
}
.text-input-container{
height: 100vh;
}
.text-message{
width: 100%;
position: absolute;
bottom: 0px;
height: 70px;
padding: 10px;
box-sizing: border-box;
}
</style>
LIST
'Front > Vue.js' 카테고리의 다른 글
[Vue.js] input 사용법 및 예제 (0) | 2023.06.14 |
---|---|
[Vue.js] 이벤트 핸들링(@click & @change) 사용법 및 예제 (0) | 2023.06.13 |
[Vue.js] select 사용법 및 예제 (0) | 2023.06.12 |
[Vue.js] 버튼(button) 사용법 및 예제 (0) | 2022.09.23 |
[Vue.js] Array(배열), Object(객체)에서 sort함수 활용 방법 및 예제 (0) | 2022.09.22 |