Socket.IO
in Web
본 글은 네이버 부스트캠프 과정에서 학습한 내용의 게시글입니다.
1. 배경지식
1.1. Socket 통신
1.1.1. Socket이란
- 프로세스가 드넓은 네트워크 세계로 데이터를 내보내거나 혹은 그 세계로부터 데이터를 받기 위한 실제적인 창구 역할을 수행
- 소켓은 떨어져 있는 두 호스트를 연결해주는 도구로써 인터페이스의 역할을 함
- 프로세스가 데이터를 보내거나 받기 위해서는 반드시 소켓을 열어서 소켓에 데이터를 써보내거나 소켓으로부터 데이터를 읽어들여야 함
- 데이터를 주고 받을 수 있는 구조체로 소켓을 통해 데이터 통로가 만들어짐
- 프로토콜, IP 주소, 포트 넘버로 정의됨
- 역할에 따라 서버 소켓, 클라이언트 소켓으로 구분
1.1.2. 프로토콜
- 원래 의미는 외교상의 언어로써 의례나 국가간의 약속을 의미
- 통신에서는 어떤 시스템이 다른 시스템과 통신을 원활하게 수용하도록 해주는 통신 규약, 약속
1.1.3. IP 주소
- 전 세계 컴퓨터에 부여된 고유의 식별 주소
1.1.4. 포트
- 네트워크 상에서 통신하기 위해서 호스트 내부적으로 프로세스가 할당받아야 하는 고유한 숫자
- 한 호스트 내에서 네트워크 통신을 하고 있는 프로세스를 식별하기 위해 사용되는 값
- 같은 호스트 내에서 서로 다른 프로세스가 같은 포트 넘버를 가질 수 없음
- 즉, 같은 컴퓨터 내에서 프로그램을 식별하는 번호
1.2. 소켓 통신의 흐름
1.2.1. 흐름도(Flow Chart)
1.2.2. 서버 (Server)
- 클라이언트 소켓의 연결 요청을 대기하고, 연결 요청이 오면 클라이언트 소켓을 생성하여 통신이 가능하게 함
- socket() 함수를 이용하여 소켓 생성
- bind() 함수로 ip와 port 번호 설정
- listen() 함수로 클라이언트의 점근 요청에 수신 대기열을 만들어 몇 개의 클라이언트를 대기 시킬지 결정
- accept() 함수를 사용하여 클라이언트와의 연결을 기다림
1.2.3. 클라이언트(Client)
- 실제로 데이터 송수신이 일어남
- socket() 함수로 가장 먼저 소켓을 염
- connect() 함수를 이용하여 통신할 서버의 서정된 ip와 port 번호에 통신을 시도
- 통신 시도 시, 서버가 accept 함수를 이용하여 클라이언트의 socket descriptor(파일 또는 소켓을 지칭하기 위해 부여한 숫자)를 반환
- 이를 통해 클라이언트와 서버가 서로 read(), write()를 하며 통신 (이 과정을 반복)
1.2.4. 소켓 종류
- TCP (스트림)
- 양방향으로 바이트 스트림을 전송, 연결 지향성
- 오류 수정, 전송 처리, 흐름제어 보장
- 송신된 순서에 따라 중복되지 않게 데이터를 수신 → 오버헤드가 발생
- 소량의 데이터보다 대량의 데이터 전송에 적합 → TCP를 사용
- UDP (데이터그램)
- 비연결형 소켓
- 데이터의 크기에 제한이 있음
- 확실하게 전달이 보장되지 않음, 데이터가 손실돼도 오류가 발생하지 않음
- 실시간 멀티미디어 정보를 처리하기 위해 주로 사용
- ex) 전화
1.3. HTTP 통신과 Socket 통신 비교
1.3.1. HTTP 통신
- Client의 요청(Request)이 있을 때만 서버가 응답(Response)하여 해당 정보를 전송하고 곧바로 연결을 종료하는 방식
- Client가 요청을 보내는 경우에만 Server가 응답하는 단방향 통신
- Server로부터 응답을 받은 후에는 연결이 바로 종료됨
- 실시간 연결이 아닌, 필요한 경우에만 Server로 요청을 보내는 상황에 유용
- 요청을 보내 Server의 응답을 기다리는 어플리케이션의 개발에 주로 사용
1.3.2. Socket 통신
- Server와 Client가 특정 Port를 통해 실시간으로 양방향 통신을 하는 방식
- Server와 Client가 계속 연결을 유지하는 양방향 통신
- server와 Client가 실시간으로 데이터를 주고받는 상황이 필요한 경우에 사용
- 실시간 동영상 Streaming이나 온라인 게임 등과 같은 경우에 자주 사용
2. WebSocket
2.1. 서론
2.1.1. WebSocket이란
- Transport protocol의 일종
- 웹 버전의 TCP 또는 Socket이라고 이해하면 됨
- 서버와 클라이언트 간에 Socket Connection을 유지해서 언제든 양방향 통신 또는 데이터 전송이 가능하도록 하는 기술
- Real-time web application 구현을 위해 널리 사용되어지고 있음
- SNS 어플리케이션, LoL과 같은 멀티플레이어 게임, 구글 Doc, 증권거래, 화상채팅 등
2.1.2. 사용 이유
- 웹 어플리케이션에서 기존의 서버와 클라이언트 간의 통신은 대부분 HTTP를 통해 이루어졌으며 HTTP는 Request/Response 기반의 Stateless Protocol임
- 즉, 서버와 클라이언트 간의 Socket connection같은 영구적인 연결이 되어있지 않고 클라이언트 쪽에서 필요할 때 Request를 할때만 서버가 Response하는 방식으로 통신이 진행되는 단방향 통신임
- 이 경우 서버쪽 데이터가 업데이트 되더라도 클라이언트 쪽의 화면은 Refresh하지 않는 한 변경된 데이터가 업데이트되지 않는 문제가 발생함
- 이러한 문제는 일반적인 웹 어플리케이션에서는 기존의 Long polling이나 Ajax를 사용하여 어느정도 해결이 가능하지만 데이터의 빠른 업데이트가 아주 중요한 요소 중에 하나인 어플리케이션에서는 실시간 업데이트가 아주 중요하기 때문에 Web Socket이 아주 중요한 기술로 사용되고 있음
- 즉, 서버와 클라이언트 간의 Socket connection같은 영구적인 연결이 되어있지 않고 클라이언트 쪽에서 필요할 때 Request를 할때만 서버가 Response하는 방식으로 통신이 진행되는 단방향 통신임
- Stateful Protocol이기 때문에 클라이언트와 한 번 연결이 되면 계속 같은 라인을 사용해서 통신하기 때문에 HTTP 사용시 필요없이 발생되는 HTTP와 TCP 연결 트래픽을 피할 수 있음
- HTTP와 같은 포트(80)를 사용하기 때문에 기업용 어플리케이션에 적용할 대 방화벽은 재설정하지 않아도 됨
2.1.3. 작동 원리
- HTTP 프로토콜을 통해 서버와 클라이언트 간의 WebSocket 연결
- 연결이 정상적으로 이루어진다면 서버와 클라이언트 간에 WebSocket 연결이 이루어지고 일정 시간이 지나면 HTTP 연결은 자동으로 끊어짐
2.1.4. HTTP 통신 방법과 WebSocket의 차이
- WebSocket 프로토콜은 접속 확립에 HTTP를 사용하지만, 그 후 통신은 WebSocket 독자의 프로토콜로 이루어짐
- header가 상당히 작아 overhead가 적음
- 장시간 접속을 전제로 하기 때문에, 접속한 상태라면 클라이언트나 서버로부터 데이터 송신이 가능
- 데이터의 송신과 수신에 각각 커넥션을 맺을 필요가 없어 하나의 커넥션으로 데이터를 송수신 할 수 있음
3. Socket.io
3.1. 서론
3.1.1. Socket.io란
- 양방햔 통신을 하기위해 웹소켓 기술을 활용하는 라이브러리
- Node.js를 위한 강력한 Cross-platform WebSocket API
- ex) 자바스크립트와 jQuery의 관계
- node.js의 웹 소켓 구현체 중 일종
- socket.io가 같은 기능을 구현하더라도 약간 느리지만, 많은 편의성을 제공
- Java, C++, Python 등 여러 언어들의 라이브러리 또한 지원
3.1.2. WebSocket과 Socket.io 비교
WebSocket
- HTML5 웹 표준 기술
- 매우 빠르게 작동하며 통신할 때 아주 적은 데이터를 이용함
- 이벤트를 단순히 듣고, 보내는 것만 가능함
Socket.io
- 표준 기술이 아니며, 라이브러리임
- 소켓 연결 실패 시 fallback을 통해 다른 방식으로 알아서 해당 클라이언트와 연결을 시도함
- 방 개념을 이용해 일부 클라이언트에게만 데이터를 전송하는 브로드캐스팅이 가능함
3.1.3. Socket.io 장점
ws
- 기본에 충실한 편
Socket.io
- 보다 다양한 기능 제공
- ex) room이라는 기능을 이용해 여러 개의 채팅방을 만들 수 있고 소켓에 연결된 전체 클라이언트에게 broadcast를 보낼 수 있다거나, room 별로 broadcast를 보낼 수 있음
- 이러한 기능들을 보다 쉽고 직관적으로 제공
- HTML5 WebSocket은 오래된 브라우저의 경우 지원하지 않는 경우가 있음
- 브라우저 간 호환이나 이전 버전 호환을 고려할 수 있음
- socket.io는 채팅방에 특화된 라이브러리
- 만일 양방향 통신 기술이 필요한데 채팅방이 아닌 다른 서비스를 구현한다고 한다면 socket.io는 맞지 않을수 있음
3.2. 사용
3.2.1. 설치
npm i socket.io
3.2.2. 이벤트 통신
- 소켓IO 의 메소드의 특징은 클라이언트에서 발생하는 이벤트는 개발자가 임의로 설정할 수 있다는 점
- 이벤트명은 문자열로 지정
- 직접 이벤트를 발생시킬 수 있음
// 해당 이벤트를 받고 콜백함수를 실행
socket.on("받을 이벤트 명", (msg) => {});
// 이벤트 명을 지정하고 메세지를 보낸다.
socket.emit("전송할 이벤트 명", msg);
3.2.3. 메시지 수신
// 접속된 모든 클라이언트에게 메시지를 전송한다
io.emit("event_name", msg);
// 메시지를 전송한 클라이언트에게만 메시지를 전송한다
socket.emit("event_name", msg);
// 메시지를 전송한 클라이언트를 제외한 모든 클라이언트에게 메시지를 전송한다
socket.broadcast.emit("event_name", msg);
// 특정 클라이언트에게만 메시지를 전송한다
io.to(id).emit("event_name", data);
3.2.4. 메시지 송신
// 클라이언트와 소켓IO 연결됬는지 안됬는지 이벤트 실행. (채팅방에 누가 입장하였습니다/퇴장하였습니다 )
io.on("connection/disconnection", (socket) => {});
// 클라이언트에서 지정한 이벤트가 emit되면 수신 발생
socket.on("event_name", (data) => {});
3.3. 서버 설정
3.3.1. app 서버 생성
const app = require("express")();
const server = app.listen(8005, () => {});
3.3.2. 소켓IO에 서버 정보 넘겨주고 구동
const SocketIO = require("socket.io");
// 서버 연결, path는 프론트와 일치시켜준다.
const io = SocketIO(server, { path: "/socket.io" });
3.3.3. 소켓 연결 성공 시 이벤트 통신
//* 웹소켓 연결 시
io.on("connection", (socket) => {
//* 연결 종료 시
socket.on("disconnect", () => {
console.log("클라이언트 접속 해제", ip, socket.id);
clearInterval(socket.interval);
});
//* 에러 시
socket.on("error", (error) => {
console.error(error);
});
//* 클라이언트로부터 메시지 수신
socket.on("reply", (data) => {
// reply라는 이벤트로 송신오면 메세지가 data인수에 담김
console.log(data);
});
//* 클라이언트로 메세지 송신
socket.emit("news", "Hello Socket.IO"); // news라는 이벤트로 문자열을 포함하여 송신
});
3.4. 클라이언트 설정
3.4.1. socket.io 모듈 스크립트 로드
- socket.io 모듈은 내부적으로 “루트/socket.io” 경로에 socket.io.js 파일을 자동으로 등록해둠
- 결과적으로 위 코드는 socket.io모듈이 자동으로 생성해둔 http://127.0.0.1:8005/socket.io/socket.io.js 에 접근하여 JS 스크립트를 불러오게 됨
- JS스크립트에서 소켓IO 객체를 뽑아 클라이언트에서도 소켓 통신을 할수 있게 되는 것
<script src="/socket.io/socket.io.js"></script>
3.4.2. socket.io 객체 생성 및 연결
- 연결할 서버 경로 및 옵션 설정
- path 옵션: 이 경로를 통해 각종 통신을 수행하며, node.js상에서 설정한 path와 동일하게 지정해야 함
- transports 옵션: socket.io는 처음에 polling 연결을 시도하고, 웹소켓이 지원되는 브라우저인 경우, ws통신으로 이행한다.: 처음부터 ws로 통신하고자 할 경우, transports 옵션 값을 [‘websocket’]으로 추가 설정해주면 됨
<!-- 익스프레스 서버와 소켓 서버가 연결이 되면, 소켓IO 서버에서 js파일을 넣어준다 -->
<script src="/socket.io/socket.io.js"></script>
<script>
// 위의 socket.io.js에서 뽑아 쓴다.
const socket = io.connect('http://localhost:8005', { // ws:// 를 안쓰고 http를 쓴다
path: '/socket.io', // 서버 path와 일치시켜준다
transports: ['websocket']
});
</script>
3.4.3. 송수신 이벤트 처리 (연결/종료/에러/데이터 수신 등)
<!-- 익스프레스 서버와 소켓 서버가 연결이 되면, 소켓IO 서버에서 js파일을 넣어준다 -->
<script src="/socket.io/socket.io.js"></script>
<script>
// 위의 socket.io.js에서 뽑아 쓴다.
const socket = io.connect('http://localhost:8005', { // ws:// 를 안쓰고 http를 쓴다
path: '/socket.io', // 서버 path와 일치시켜준다
// transports: ['websocket']
});
// 서버로부터 메세지 수신
socket.on('news', function (data) {
console.log(data);
// 서버에게 메세지 송신
socket.emit('reply', 'Hello Node.JS');
});
</script>