이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

게시글

강좌13 - NodeJS - 2년 전 등록 / 2달 전 수정

Socket.io Server API

설정하기, 전체 채팅, 1대1 채팅, 그룹 채팅
조회수:
0
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

안녕하세요. 이번 시간에는 Socket.io Server API에 대해 알아보겠습니다. 지난 시간에는 간단하게 한 사용자로부터 받은 데이터를 전체 사용자들에게 뿌려줬는데요. 이번 시간에는 그 데이터를 특정 사용자에게만 뿌린다든가, 특정 그룹에게만 뿌린다든가, 아니면 나 자신을 제외한 사람에게만 뿌리는 방법에 대해 알아보겠습니다. 채팅방이나 메신저를 만들 때 참 적합한 API인 것 같습니다. Socket.io의 사용법부터 알아보고 싶으시다면 지난 강좌부터 보시면 됩니다.

Socket.io 홈페이지의 설명이 부실하기 때문에 제가 좀 정리해보았습니다. 다음 코드에서부터 시작합니다. 서버에서 작성해줍니다.

io.on('connection', (socket) => {
  socket.on('disconnect', () => {
    console.log('disconnected');
  });
});

잘 보시면 iosocket이라는 변수가 있습니다. io는 socket.io 패키지를 import한 변수고, socket은 커넥션이 성공했을 때 커넥션에 대한 정보를 담고 있는 변수입니다. socket 변수를 사용해 서버에서 이벤트 리스너를 등록하면 됩니다. 몇 개의 이벤트는 이미 예약되어 있는데요. 일례로 disconnect 이벤트는 클라이언트와의 연결이 끊어졌을 때 발생합니다.

클라이언트에서는 socket.io 클라이언트 스크립트를 넣고 다음과 같이 보내고 받으면 됩니다.

var socket = io.connect('서버 주소');
socket.on('서버에서 받을 이벤트명', function(데이터) {
  // 받은 데이터 처리
  socket.emit('서버로 보낼 이벤트명', 데이터);
});

설정

설정은 io.configure()io.set()이 아닙니다. 많은 인터넷 블로그에서 이렇게 설명을 하는데 이것은 예전 API입니다. 처음에 socket.io에 주소를 연결할 때 설정도 함께 연결하면 됩니다. 설정 가능한 부분은 공식 홈페이지에 잘 나와 있습니다.

io('주소', { 설정명: '설정값' }); // 클라이언트 설정
SocketIo(server, { 설정명: '설정값' }); // 서버 설정

Socket.io는 웹소켓말고 폴링도 같이 지원하는데 순수한 웹소켓만 사용하고 싶다면 클라이언트에서 다음과 같이 합니다. 

io('주소', { transports: ['websocket'] });

Socket.io는 익스프레스처럼 미들웨어를 사용할 수 있습니다. 다음은 Socket.io와 express-session과 연동하는 예시입니다. socket 객체에는 req와 res 객체가 다 들어 있습니다.

io.use((socket, next) => {
  require('express-session')(옵션)(socket.request, socket.request.res, next);
});

여기서부터는 서버 상의 API입니다. 클라이언트는 socket.onsocket.emit만 있으면 됩니다. 

전체

전체라고 함은 클라이언트에서 socket.on('이벤트명', 콜백)를 등록해 해당 이벤트가 오기를 기다리는 페이지 전부를 의미합니다. (사실 이벤트리스너를 등록하지 않아도 연결된 모든 클라이언트에 전달됩니다. 다만 이벤트리스너가 없기 때문에 무시됩니다) 전체에게 메세지를 보내는 방법은 지난 시간에 이미 했습니다. 서버에 요청을 보냈던 자신에게도 메세지가 다시 온다는 특징이 있습니다. 제 홈페이지의 예를 들자면, 새로운 포스트를 등록하거나 수정했을 경우 모든 사람에게 포스팅의 정보가 전송됩니다.

io.emit('이벤트명', 데이터);

또는

io.sockets.emit('이벤트명', 데이터);

해도 됩니다.

네임스페이스

Socket.io에는 네임스페이스라는 것도 존재합니다. 전체에게 메시지를 보내는 것은 사실 / 네임스페이스에게 메시지를 보내는 것입니다. 네임스페이스를 바꾸려면 서버에서는 of 메서드를 사용합니다.

const chat = io.of('/chat');
chat.on('connection', (socket) => {
  ...
});

클라이언트에서는 연결 시 주소를 바꿔줘야합니다. 주소 뒤에 네임스페이스를 붙여줍니다.

io.connect('주소/chat', 설정);

위와 같이 네임스페이스를 바꾸고 네임스페이스별로 따로 이벤트를 연결할 수 있습니다. 이렇게 하는 이유는 필요한 사람들에게만 메시지를 보내기 위함입니다.

예를 들어 기본값인 / 네임스페이스를 사용한다고 생각해봅시다. 채팅방 목록이 새로 갱신되든, 채팅 메시지가 오가든 모든 사람들에게 모든 이벤트가 전송됩니다. 채팅방 안에 들어있는 사람은 새로운 채팅 메시지만 받으면 되지 채팅방 목록이 갱신되는 것까지는 알 필요 없습니다. 대기실에 있는 사람들도 채팅방 안의 메시지가 오고 가는 것을 알 필요가 없고요. 따라서 네임스페이스를 분리하여 같은 네임스페이스에 연결된 사람들끼리만 웹소켓 통신을 하는 것입니다.

io.of(네임스페이스).emit('이벤트명', 데이터);

위의 방식으로 같은 네임스페이스의 사람들에게 데이터를 보낼 수 있습니다.

나를 제외한 전체

나를 제외한 전체에게 메세지를 보내는 방법입니다. io를 사용하지 않고 socket 안에 있는 broadcast 객체를 사용합니다. 제 홈페이지에서는 댓글을 달 때 자신을 제외한 모두에게 실시간으로 전송됩니다. 자신의 댓글은 서버를 거치지 않고 바로 클라이언트 상에서 처리됩니다.

socket.broadcast.emit('이벤트명', 데이터);

특정인

특정 한 사람에게 메세지를 보낼 수 있습니다. 귓속말같은 것이나 1대1 채팅을 구현할 때 사용하면 좋겠죠?

io.to(소켓아이디).emit('이벤트명' 데이터);

sockets 객체를 함수처럼 사용하여 인자로 소켓아이디를 제공하면 그 소켓아이디를 가진 사람에게만 메세지가 전달됩니다. 자신의 소켓아이디는 socket.id를 통해 얻을 수 있습니다. 다른 사람에게 메세지를 보내려면 그 사람의 소켓 아이디가 필요한데 이 때는 데이터에 자신의 소켓 아이디를 넣어 전달해줘서 다른 사람들이 자신에게 메시지를 보낼 수 있도록 허용하면 됩니다.

특정 그룹

특정 그룹에게도 메세지를 보낼 수 있습니다. 대신 사람들을 그 그룹에 먼저 들어야가 합니다. 네임스페이스보다는 작은 개념입니다. 네임스페이스 안에 그룹을 만드는 셈입니다.

socket.join(방의 아이디); // 그룹에 들어가기
socket.leave(방의 아이디); // 그룹 떠나기

방의 아이디는 단순히 문자열일 뿐이라서 아무 거나 생성해주면 됩니다. 예를 들면 방의 아이디가 'room01'일 경우 두 명 이상이 'room01'에 join하면 그룹 채팅을 할 수 있습니다. 메세지는 그룹 전체에게 보내는 방식과 나를 제외한 그룹 전체에게 보내는 두 가지 방식이 있습니다.

io.to(방의 아이디).emit('이벤트명', 데이터); // 그룹 전체
socket.broadcast.to(방의 아이디).emit('이벤트명', 데이터); // 나를 제외한 그룹 전체

이제 좀 정리가 된 것 같네요. 응용하는 것은 여러분의 자유입니다. 

그룹의 목록과 그룹 안의 소켓들을 확인하는 방법은 다음과 같습니다.

io.adapter.rooms
io.of(네임스페이스).adapter.rooms
socket.adapter.rooms

한가지 팁을 드리자면, 위의 방법대로 참여 인원 수나 방의 수를 구하는 것이 불안정하기 때문에 서버 상에서 배열을 만들어 방의 아이디를 모아두는 것이 편할 것 같습니다. 그리고 방 안에는 참여한 사람들의 소켓 아이디를 넣어두고요.

[
  { _id: 'room01', members: ['zero_id', 'aero_id']},
  { _id: 'room02', members: ['nero_id', 'hero_id']},
]

이런 식으로요. 서버가 꺼지지 않는 이상 저 배열은 데이터베이스처럼 동작할 겁니다. 이 방식은 서버의 메모리에 저장하는 것이기 때문에 날아갈 위험이 있습니다. 나중에는 저 부분을 데이터베이스와 연결하여 방의 목록과 멤버를 영구적으로 저장하면 됩니다.

이제 위의 API를 사용해서 채팅방을 만들어보세요!

투표로 게시글에 관해 피드백을 해주시면 많은 도움이 됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지

댓글

3개의 댓글이 있습니다.
3달 전
음..사용자가 방에 들어왔는지 어떻게아나요... 채팅방을 만들어서 방안에 있는사람에게 보낼려고하는데 사용자의 socket.Id가 room에 들어왔는지 체크하는 방법 알려주세요,....
3달 전
socket.set('room',function(){})이게 없어졋다고 하는데 대신할 방법 없나요?
3달 전
제가 현재 이 부분 책을 쓰고 있는데 한 번 확인해보겠습니다.
2달 전
socket.adapter.rooms[방 아이디] 안에 사용자 소켓 목록들이 들어 있습니다.
3달 전
항상 잘보고 있습니다.
socket.js에서 DB의 특정값이 바뀐것을 어떻게 detecting하나요? 또는 외부 서버에서 받은 post data를 브라우저에게 알려줄려면 어떻게 해줘야하나요? 대화나 댓글이 아니라.. 예를들면

외부서버가 post or get으로 1000이라는 숫자를 넘겨준다면 index.js(route)에서 그 값을 받을 것이고, socket.js에게 이 값을 전달해줘야지 emit을 해줄 수 있을텐데 초보라서.. 잘 이해가 안됩니다. 하루종일 이리저리 해봤지만 실패해서 이렇게 질문드립니다...
3달 전
socket을 라우터로 import(또는 require)해오세요.
3달 전
app.js 에서
socketio = require('socket.io');
socket = require('socket.js');

server = http.createServer(app);
var io = new sokcetio(server);
socket(io);

하는데

index.js에서 import or require한다면
server를 하나 더 만들어야하는거 아닌가요?
3달 전
일단 socket.js가 뭔가요? 직접 만드신 건가요? Server를 리콰이어하는게 아니라 모듈로 만든 후 소켓 객체를 리콰이어하시면 됩니다. io나 sockets요
3달 전
이전글 socket.io로 실시간업데이트 포스팅에서 socket.js 만드신것 그대로 만들었습니다. 그래서 app(이전 포스팅에서는 server).js에서 위 작업을 했습니다.
3달 전
app.set('socket', socket)으로 익스프레스랑 연결한 후 라우터에서 req.app.get('socket')으로 꺼낼 수도 있어요
6일 전
저도 지금 이문제로 골머리중. app.get으로 꺼내면 io.emit은 날라 가는데 io.to('socketid') 는 안날라감. ㅠ.ㅠ. 왜 안날라 가는지 모르겠음.
6일 전
올바른 socketId가 맞나요? 네임스페이스도 살펴보세요~
6일 전
아... socket.id가 서버가 계속 다시 시작되면서 바뀌었었네요. 감사합니다.
3달 전
1