1. 설치 및 사용
socket.io는 클라이언트 웹브라우저와 스켓 통신을 하는 모듈입니다. WebSocket에서는 통신에 필요한 대부분의 기능을 직접 구현해야 하지만 socket.io는 이미 여러 가지 기능이 만들어져 있으므로 사용하기만 하면 됩니다. 기존 WebSocket을 기반으로 하여 몇 가지 편의성을 추가한 거라고 생각하면 되겠습니다.
특히 개별적인 이벤트를 통한 메세지송수신기능과 특정 클라이언트를 구별하는 id기능을 제공해주므로 활용도가 높은 편입니다. 하지만 각 클라이언트의 구별이 필요 없는 환경이라면 굳이 socket.io를 사용할 이유는 없습니다.
socket.io는 이전에 만들어둔 WebSocket을 기반으로 socket.io를 적용해 보고자 합니다. 그러니 해당 포스팅에서 설명된 모든 파일이 존재해야 합니다.
[Server/node.js] - [node.js] WebSocket
socket.io는 아래 방법으로 설치할 수 있습니다.
npm i socket.io |
socket.io를 설치하고 나면 통신을 담당할 모듈을 다음과 같이 생성합니다. 해당 파일은 socket 디렉토리에 socket.js인데 기존의 모든 내용을 지우고 아래 내용으로 교체해 주시면 됩니다.
const socketIO = require('socket.io');
module.exports = (server) => {
const io = socketIO(server, { path: '/socket.io' });
io.on('connection', (socket) => {
const req = socket.request;
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
console.log('클라이언트 연결 : ', ip, socket.id);
socket.on('server', (data) => {
console.log(data);
socket.emit('client','server to client');
});
socket.on('disconnect', () => {
console.log('클라이언트 종료 : ', ip, socket.id);
});
socket.on('error', (error) => {
console.error(error);
});
});
}
socket.io에서 io를 생성할때 path 부분이 추가되었는데 이것은 나중에 클라이언트의 html페이지의 설정이랑 일치해야 합니다. 일단 이 부분은 '/socket.io'라고 넣어줍니다.
클라이언트가 연결되면 connection이벤트를 통해 받을 수 있으며 request는 socket객체에서 가져올 수 있습니다. ip는 socket.io에서는 req.ip로 가져올 수 없고 req.connection.remoteAddress로 지정해야 합니다.
socket.io에서 클라이언트와 연결이 수립되면 각 클라이언트마다 고유의 id값을 자동으로 부여하며 id는 socket.id를 통해 특정 클라이언트를 구분할 수 있게 됩니다.
socket이벤트에서 'server'는 클라이언트에서 서버로 메시지를 보내는 이벤트 입니다. 클라이언트에서 서버로 메세지를 전송할때 'server'라는 이벤트로 보내면 서버에서도 'server'이벤트로 메세지를 받을 수 있는 것입니다. socket.io에서 메세지 전송 이벤트는 다음과 같이 다수의 이벤트를 생성해 클라이언트와 필요한 이벤트로 메세지를 주고 받을 수 있습니다.
클라이언트에 메시지를 보낼때는 emit()함수를 사용하는데 이때도 메세지를 전송할 이벤트 이름을 'client'처럼 임의로 지정할 수 있고 메세지를 전송하면 해당 이벤트를 가진 클라이언트가 메세지를 받게 되는 구조입니다.
socket.on('server', (data) => {
console.log(data);
socket.emit('client','server to client');
});
socket.on('server1', (data) => {
console.log(data);
socket.emit('client','server to client');
});
socket.on('server2', (data) => {
console.log(data);
socket.emit('client','server to client');
});
그 외 클라이언트와의 연결 종료는 disconnect로 오류는 error로 이벤트를 받아 처리합니다.
서버를 위와 같이 만들었으면 클라이언트에서 사용할 웹페이지를 만들어야 합니다. 기존의 index.html을 아래와 같이 수정합니다.
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
const socket = io.connect('http://192.168.137.1:80', {
path: '/socket.io',
transports: ['websocket']
});
socket.on('client', (data) => {
console.error(data);
});
</script>
</head>
<body>
<button onClick="javascript: socket.emit('server', 'client to server');" >시작</button>
</body>
</html>
/socket.io/socket.io.js는 서버와의 연결이 이루어지면 socket.io에서 알아서 넣어주는 부분이므로 해당 스크립트 코드만 위와 같이 추가해 주면 됩니다.
socket.io에서 서버의 연결은 connect() 함수를 사용하며 'http://[IP]:[Port]'형식으로 연결을 시도합니다. path는 서버에서의 path와 동일해야 하며 transpots를 통해 websocket으로 통신할 것임을 지정합니다. 만약 transports부분을 따로 지정하지 않으면 1차적으로 polling방식을 통한 연결을 시도하고 polling이 불가능하다면 websocket을 통한 통신을 시도합니다.
서버에서 클라이언트 쪽으로 수신되는 메시지는 'client'이벤트를 통해 받도록 되어 있는데 위에서 언급한 대로 해당 이베트는 서버와 마찬가지로 어떤 이름이든 다수의 수신 이벤트를 생성할 수 있고 해당 특정한 이벤트를 통해 메시지를 수신받을 수 있습니다.
socket.io에서 클라이언트와 서버가 메시지를 주고받을때 오해하지 말아야할 부분은 서버에서 클라이언트로 메세지를 전송할대 특정 클라이언트에서 메세지를 수신받는 경우는 서바가 해당 클라이언트한테만 개별적으로 메세지를 보내는 구조가 아닌 전체로 메세지를 json방식으로 뿌리고 메세지를 받는 클라이언트는 해당 json을 분해해 자신에게 보내진 메세지인지 확인한 후 자신의 메세지는 받아서 처리하고 그렇지 않은 클라이언트는 메세지를 무시하는 방식입니다.
일반 응용프로그램의 소켓은 정해진 클라이언트에만 패킷을 전달하는데 이와는 다른 통신방식이므로 오해가 없어야 합니다.
마지막으로 클라이언트가 서버와 통신할 때를 보면 2나 3과 같은 메시지를 주고받는 경우가 있는데 이건 서버가 해당 클라이언트의 접속 유지 여부를 확인하기 위한 것입니다.
2. 방송 및 특정 클라이언트 메시지 전송
서버에서 클라이언트에게 데이터를 보낼 때 특정 클라이언트에게만 데이터를 보내려면 socket대신 io를 바로 사용해야 합니다. (socket은 io.socket에서 io가 생략된 것입니다.)
io.to(socket.id).emit('client','server to client');
위와 같이 하면 socket.id에 해당하는 클라이언트만 서버에서 보낸 메시지를 받게 됩니다.
또는 메시지를 발신한 클라이언트를 제외하고 다른 모든 클라이언트에게 메세지를 보내는 경우도 있는데 한사람이 다른사람 모두에게 메세지를 보내는 형태로는 다음과 같이 처리합니다.
socket.broadcast.emit('client', 'message');
메세지를 보내는 당사자는 메세지 수신자들 중에서 알아서 제외되므로 신경 쓰지 않아도 됩니다.
3. 네임스페이스
socket.io는 네임스페이스라는 것을 지원합니다. 클라이언트가 서버와 묶이는 그룹이라고 생각하면 됩니다. 서버에서 네임스페이스를 사용하려면 io.of()를 통해 원하는 네임스페이스로 바꿔줘야 합니다. 그리고 생성되는 객체를 io대신 동작하도록 해야 합니다.
const io = socketIO(server, { path: '/socket.io' });
const ns = io.of('/ns');
ns.on('connection', (socket) => {
클라이언트에서는 특정 네임스페이스로 접속하려면
const socket = io.connect('http://192.168.137.1:80/ns', {
path: '/socket.io',
transports: ['websocket']
});
처럼 연결할 주소 뒤에 네임스페이스를 붙여주면 됩니다. 이렇게 서버가 클라이언트가 네임스페이스로 묶이면 서버에서 전송되는 모든 메시지는 같은 네임스페이스에 접속된 사용자들만 보게 됩니다.
const ns = io.of('/ns');
ns.on('connection', (socket) => {
const req = socket.request;
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
console.log('클라이언트 연결 : ', ip, socket.id);
socket.on('server', (data) => {
console.log(data);
socket.emit('client', 'server to client'); // /ns 네임스페이스만 메세지 확인
});
socket.on('disconnect', () => {
console.log('클라이언트 종료 : ', ip, socket.id);
});
socket.on('error', (error) => {
console.error(error);
});
});
4. 그룹
네임스페이스보다는 작은 개념으로 네임스페이스 안에서도 클라이언트를 특정 그룹에 묶어줄 수 있습니다. 클라이언트를 그룹으로 묶는 건 join() 함수로 가능하며 들어갈 그룹명을 지정해 주면 됩니다. 아래 예제는 접속한 클라이언트를 group이라는 그룹으로 묶어주도록 합니다.
socket.join('group');
서버에서 특정 그룹에 메시지를 전송하려면
ns.to('group').emit('client', 'server to client');
to를 사용해 그룹을 지정해 주면 됩니다. ns는 위에서 생성한 네임스페이스 서버인데 io처럼 전체 서버여도 무관합니다. 특정 클라이언트에게 메시지를 발신할때와 비슷한데 클라이언트의 socket.id를 사용하느냐 그룹명을 사용하느냐의 차이일 뿐입니다.
따라서 메세지를 발신하는 클라이언트를 제외하고 그룹 전체에 발신하는 경우도 broadcast를 그대로 사용하면 됩니다.
socket.broadcast.to('group').emit('client', 'server to client');
그룹을 빠져나가려면 leave()를 사용합니다.
socket.leave('group');
참고로 네임스페이스에서 그룹과 그룹에 속한 클라이언트를 확인하려면 adapter.rooms로 확인하면 되는데
io.of('/ns').adapter.rooms
서버가 특정 네임스페이스가 아닌 전체('/')인 경우는 아래와 같은 방법으로 확인해야 합니다.
io.adapter.rooms
'Server > node.js' 카테고리의 다른 글
[node.js] CORS (0) | 2021.03.10 |
---|---|
[node.js] WebSocket (0) | 2021.03.09 |
[node.js] express-rate-limit(DOS공격 방어하기) (2) | 2021.03.09 |
[node.js] JWT 인증 (0) | 2021.03.09 |
[node.js] mariaDB CRUD (시퀄라이즈) (0) | 2021.03.08 |