📂 오늘 배운 내용
- 사용자 지정 이벤트
- Room 개념과 함수
- 시작하기 (서버, 클라이언트)
JS문법
- `Object.keys`, `Object.values`
- 더하기 할당 연산자(+=) 개념 및 활용
Socket.io 라이브러리
- websocket 기반으로 만들어진 라이브러리라서 cdn 가져올 예정
- websocket과의 차이점
- 사용자 지정 이벤트가 있다는 거!
- 클라이언트, 서버 메서드 차이가 없음
- websocket은 클라이언트에서 이벤트 감지하려면 addEventListenr, 서버는 on
- 웹 소켓은 데이터 전달할 때 JSON 데이터 변환이 필요했지만, socket.io는 그런 처리가 필요없음 👍
- 특징 1) 양방향 2) 자동 재연결
기본 이벤트 : connection, disconnect, disconnecting, error
- connection : 클라이언트-서버 연결 시 발생. 기본 메서드라서 emit 없이 작성 가능
- disconnecting : 클라이언트가 연결을 해제하려는 경우 발생
사용자 지정 이벤트
- emit : 이벤트 만들고 데이터를 보냄
- on : 이벤트 받는 쪽
내가 만들고 싶은 이름('hello')으로 이벤트를 만들 수 있음! 이벤트를 만들고 보내는 건 서버/클라이언트 상관없다.
위 이미지에서는 클라이언트가 emit()으로 메시지를 보낼 때, 마지막 인자로 콜백함수를 전달하고 있음 (서버에서 실행하고 응답 가능)
Socket.IO의 Room
클라이언트
- socket : 하나의 클라이언트
- io : 여러개의 socket = 여러개의 클라이언트
- broadcast: 나를 제외한 클라이언트
Room 개념
- 기본적으로 모든 클라이언트는 `socket.id`를 가진 고유한 연결을 갖는다
- `socket.join(room)` 호출 시 해당 클라이언트가 특정 방에 속하게 됨
- 방에 속한 클라이언트들끼리 `io.to(room).emit()`을 이용해 같은 방에 있는 사람들에게만 메시지 전송 가능
함수
// 1. 클라이언트 특정 방에 추가
socket.join('roomA')
console.log(socket.rooms); // Set {'소켓ID', 'roomA'}
// 2. 클라이언트 특정 방에서 제거
socket.leave('roomA')
// 3. 특정 방에 있는 모든 클라이언트에게 이벤트, 데이터 발송
io.to('roomA').emit('message','hello, RoomA!')
// 4. 특정 방에 있는 모든 클라이언트에게 이벤트, 데이터 발송 (송신자 클라이언트 제외)
socket.to('roomA').emit('message', 'hello, RoomA!')
socket.broadcast.to('roomA').emit('message', 'A new user has joined the room.')
// 5. 송신자 제외 서버에 연결된 모든 클라이언트에게 이벤트, 데이터 발송
sockt.broadcast.emit('message', 'A new user has joined the room.')
// 6. 송신자 포함 모든 클라이언트에게 이벤트, 데이터 발송
io.socket.emit('message', 'hello, RoomA!')
// 7.특정 방에 속한 클라이언트들의 정보 확인
const roomInfo = io.sockets.adapter.rooms.get(room)
console.log(roomInfo) //방에 속한 클라이언트 ID 정보 출력
- 서버에서 활용
- `get` : 이 방에 누가 있나~
- `io.sockets.adpater.rooms` : 현재 존재하는 모든 방과 방에 속한 소켓 정보 관리하는 객체
- roomInfo에는 해당 방에 연결된 클라이언트 소켓 ID들이 저장된 Set 객체(중복이 없는 리스트)가 들어간다.
예제
io.on('connection', (socket) => {
console.log(`클라이언트 연결됨: ${socket.id}`);
// 특정 방(room1)에 클라이언트 입장
socket.join('room1');
console.log(`${socket.id}가 room1에 입장`);
// 현재 room1 방에 속한 클라이언트 정보 가져오기
const roomInfo = io.sockets.rooms.get('room1');
console.log('현재 room1에 속한 클라이언트:', roomInfo);
});
예제 코드의 실행 결과 예시 :
클라이언트 연결됨: WtGHJdXcNk0X7N6LAAAA
WtGHJdXcNk0X7N6LAAAA가 room1에 입장
현재 room1에 속한 클라이언트: Set { 'WtGHJdXcNk0X7N6LAAAA' }
시작하기
서버
설치
npm i socket.io
예제
const express = require('express');
const http = require('http'); // node.js 기본 http모듈
const app = express();
const server = http.createServer(app); //HTTP 서버 생성 (Express앱 기반)
const io = require('socket.io')(server); //socket.io 서버 생성(HTTP서버와 연결)
const PORT = 8080;
/* middleware */
// ...
/* api 라우팅 */
// ...
/* socket */
io.on('connection', (socket)=>{
//...
})
/* 서버 실행 */
server.listen(PORT, () => {
console.log(`http://localhost:${PORT}`);
});
socket.io의 소켓은 HTTP모듈로 생성된 서버에서만 동작한다.
`const io = require('socket.io')(server);` : HTTP 기반으로 만들어진 서버를 인자로 전달해서 io 소켓 만들어주기 (소켓 연결 설정)
클라이언트
CDN
<!-- socket.io CDN -->
<script src="/socket.io/socket.io.js"></script>
`io()` 함수 : 소켓 생성하고, 서버와의 연결을 설정
<script>
const socket = io();
//....
</script>
예제 on과 emit 사용해보기
1~2 클라이언트 > 서버
3 서버 > 클라이언트
실시간 채팅방을 구현하면서 사용했던 JS문법을 정리하고자한다.
먼저 닉네임 중복 검사 기능을 구현하기 위해 소켓 연결 전 클라이언트에서 받은 사용자 닉네임 저장 정보를 담을 수 있는 빈 객체를 만들었다. 여기에 저장된 정보와 클라이언트의 Input창에 입력된 이름을 비교하기 위해 객체의 메서드를 사용했다.
JS Object.keys , Object.values
Object.keys(obj) : 객체의 속성(key)을 배열로 반환한다.
const object1 = {
a: 'somestring',
b: 42,
c: false,
};
console.log(Object.keys(object1));
// Expected output: Array ["a", "b", "c"]
Object.values(obj) : 객체의 값들을 배열로 반환한다.
const object1 = {
a: 'somestring',
b: 42,
c: false,
};
console.log(Object.values(object1));
// Expected output: Array ["somestring", 42, false]
더하기 할당 연산자(+=) 활용
더하기 할당 연산자를 활용해서 닉네임 목록 갱신 기능을 구현했다. 더하기 할당은 오른쪽 피연산자의 값을 변수에 더한 결과를 다시 변수에 할당한다. 두 피연산자의 타입이 연산자의 동작을 결정하며, 덧셈 또는 문자열 연결이 가능하다.
더하기 할당 (+=) 기본 예제
let a = 0;
let b = '소켓';
console.log((a += 3)); // 3
console.log((b += ' 어렵다!!'));// "소켓 어렵다!!"
활용 : 닉네임 목록 갱신 기능
[app.js]
// 닉네임2. 닉네임 중복 검사, 입장 실패/성공에 따른 처리
socket.on('checkNick', (nickname) => {
// nickname = nickInput.value
// nickInfo[socket.id] = nickname;
console.log(nickInfo); //{ LzSvKL19UkErW1c8AAAD: '돌고래' }
console.log(Object.values(nickInfo).indexOf(nickname));
// 닉네임 중복 검사
if (Object.values(nickInfo).indexOf(nickname) > -1) {
socket.emit('error', '이미 존재하는 닉네임입니다.');
} else {
// 현재 클라이언트 닉네임 정보 nickInfo에 저장
nickInfo[socket.id] = nickname;
// ...
// 입장 성공 시 나를 포함한 전체 client에게 전체 닉네임 정보 전달 (사용자 목록 갱신)
io.emit('updateNicks', nickInfo);
}
});
[talk-dm.ejs]
socket.on('updateNicks', (nickInfo) => {
let option = `<option value='all'>전체</option>`;
console.log('nickInfo', nickInfo);
console.log('option', option); //option 태그 그 자체 (문자열)
console.log('option type', typeof option); //string
for (let key in nickInfo) {
// console.log('key', key);
// console.log('value', nickInfo[key]);
option += `<option value='${key}'>${nickInfo[key]}</option>`;
}
select.innerHTML = option; //닉네임 목록에 추가해주기
});
여기서는 option이 문자열이므로 콘솔에 사용자의 닉네임 값이 모두 출력된다. 이 닉네임 값의 목록에 하나의 문자열을 또 추가해서 연결해기 위해 더하기 할당 연산자를 사용했다.
즉, 문자열을 누적하여 <option> 태그를 동적으로 생성하고 있다.
🌐 참고 / 읽을거리
서울시 청년취업사관학교(SeSAC) x 코딩온 웹 개발자 풀스택 과정
32.socket.io
- client : 기본 개념 (1) on과 emit 사용해보기 (2) 채팅 만들기
- client2 : 실습1 buttons(hello, study, bye)
- rooms : 실습2 방이 있는 채팅
32.socket.io-practice [EJS 채팅방 만들기]
- talk : 입장 공지, 기본 채팅 기능 (DM, 닉네임 표시 기능 x)
- talk-dm : 닉네임 입력 후 채팅방 입장(DM, 닉네임 표기 기능 O)
'Today I Learned > SeSAC 웹 2기' 카테고리의 다른 글
Socket.io with React MVC 패턴 채팅 앱 | 15주차(3) (0) | 2025.02.09 |
---|---|
Node.js Web Socket 💬 | 15주차(1) (0) | 2025.02.05 |
TypeScript와 React | 13주차(3) (0) | 2025.01.30 |
Typescript 입문 🥳 | 13주차(2) (0) | 2025.01.24 |
React 상태관리 ContextAPI와 Redux | 13주차(1)하 (0) | 2025.01.23 |