실시간 통신: Socket

wldud·2026년 6월 12일

우리가 웹 서비스르 만들 때 가장 많이 사용하는 통신 방식은 HTTP입니다.

예를 들어 프론트엔드 개발을 하면서 아래와 같은 코드를 자주 쓸겁니다.

const response = await fetch('/api/users')
const {data} = await axios.get('/api/messages')

이 방식은 기본적으로 다음과 같은 흐름을 가집니다.

클라이언트 -> 서버 : 요청
서버 -> 클라이언트 : 응답

즉, 클라이언트가 먼저 요청해야 서버가 응답할 수 있습니다. 그래서 이런 구조는 목록 조회, 로그인 등과 같은 기능과는 아주 잘 맞습니다.

하지만 채팅과 같은 서비스를 생각해보면 상대방이 메시지를 보냈을 때, 내 화면에 바로 메시지가 떠야 합니다.

그런데 HTTP 방식만 사용한다면 브라우저는 서버에게 계속 물어봐야 합니다.

새 메시지 있음?
없음

있음?
없음

있음?
있음!

그러면 계속해서 물어봐야하는건데, 되게 비효율적이죠? 그래서 등장한 개념이 실시간 통신입니다.

기존 HTTP:
클라이언트가 요청해야 서버가 응답

실시간 통신:
서버가 필요한 순간 클라이언트에게 데이터를 보냄

실시간처럼 보이게 만들기 위해서 여러가지 방법이 있습니다.

Polling

Polling은 일정 시간마다 서버에게 계속 물어보는 방식입니다.

setInterval(() => {
  fetch('/api/messages')
}, 1000)

위에 코드는 1초마다 서버에게 메시지 목록을 요청합니다.

장점: 구현이 쉽다.

단점: 새로운 데이터가 없어도 계속 요청을 보내기 때문에 서버의 네트워크에 부담이 생긴다.

사용자가 많아지면 문제가 커집니다.

그래서 Polling은 간단한 기능에는 사용가능하지만, 실시간 서비스에 한계가 있습니다.

Long Polling

일반 Polling은 서버가 바로 응답한다면, Long Polling은 서버가 바로 응답하지 않고 기다립니다.

클라이언트: 새 메시지 있어?
서버: 잠깐 기다려봐.
...
...
서버: 이제 메시지 생겼어.

서버는 새로운 데이터가 생길 때까지 요청을 붙잡고 있다가, 데이터가 생기면 응답합니다.

응답을 받은 클라이언트는 다시 서버에 요청을 보냅니다.

장점: Polling보다 요청 횟수를 줄일 수 있다

단점: 여전히 HTTP 요청과 응답 구조 사용. 완전한 양방향 실시간 통신이라고 보기 어려움

SS

SSE는 Server-Sent Events의 약자

서버가 클라이언트에게 이벤트를 보내는 방식입니다.

클라이언트 → 서버: 연결 요청
서버 → 클라이언트: 데이터 계속 전송
const eventSource = new EventSource('/api/events')

eventSource.onmessage = (event) => {
  console.log(event.data)
}

SSE의 특징은 서버에서 클라이언트 방향으로만 데이터를 보내는 단방향 통신이다.

그래서 실시간 알림, 주식 가격 표시 등등에 어울립니다.

하지만 채팅처럼 클라이언트도 서버에게 자주 메시지를 보내야 하는 서비스에는 WebSocket이 더 적합합니다.

WebSocket

WebSocket은 실시간 양방향 통신을 위한 대표적인 기술입니다.

연결 → 계속 유지 → 양방향 데이터 전송

WebSocket은 처음부터 WebSocket으로 시작하지 않습니다. 처음에는 HTTP 요청으로 시작합니다..

클라이언트가 서버에게

나 HTTP 연결을 WebSocket으로 바꾸고 싶어.

이를 Handshake라고 합니다. 요청에는 다음과 같은 의미가 담깁니다.

GET /chat
Upgrade: websocket
Connection: Upgrade

서버가 이를 허용하면 101 Switching Protocols 응답을 보냅니다.

이후부터 HTTP가 아니라 WebSocekt 연결로 바뀝니다.

1. 처음에는 HTTP 요청
2. 서버가 WebSocket 업그레이드 허용
3. 연결이 WebSocket으로 전환
4. 이후 양방향 통신

Socket이란?

Socket은 쉽게 네트워크 통신을 위한 문입니다.

컴퓨터끼리 통신을 하려면 주소(IP주소, 포트 번호)가 필요합니다. ex. 192.168.0.10:3000

IP 주소는 어느 컴퓨터인지 찾기 위한 주소이고, 포트 번호는 그 컴퓨터 안에서 어떤 프로그램과 통신하맂 구분하는 번호입니다.

즉, Socket은 이런 IP와 포트를 기반으로 프로그램 간 통신을 가능하게 해주는 통신 창구라고 볼 수 있습니다.

Socket과 WebSocekt의 차이

Socket과 WebSocket은 비슷해보이지만 같은 말은 아닙니다.

Socket은 더 넓은 개념입니다. 운영체제 수준에서 네트워크 통신을 위해 사용하는 기본 개념입니다.

WebSocket은 웹 환경에서 브라우저와 서버가 실시간 양방향 통신을 하기 위한 프로토콜입니다.

WebSocket은 Socket이라는 넓은 개념 안에서 웹에 특화된 방식이라고 볼 수 있습니다.

프론트엔드에서 WebSocket 사용하기

브라우저에는 WebSocket API가 기본으로 있습니다.

const socket = new WebSocket('ws://localhost:3000')

socket.onopen = () => {
  console.log('연결됨')
}

socket.onmessage = (event) => {
  console.log('서버에서 온 메시지:', event.data)
}

socket.send('안녕하세요')

socket.onclose = () => {
  console.log('연결 종료')
}
onopen: 연결 성공
onmessage: 서버에서 메시지를 받음
send: 서버로 메시지를 보냄
onclose: 연결 종료
onerror: 에러 발생

프론트 개발할 때 연결 뿐만 아니라

  • 연결이 끊겼을 때 재연결 할 것인가?
  • 사용자가 페이지를 나가면 연결을 닫을 것인가?
  • 메세지 형식은 어떻게 정할 것인지?

등등 신경써야 합니다.

조금 더 깊게 보면, Socket은 어디서 동작할까?

여기서 중요한 점은 Socket이 프론트엔드에서만 쓰이는 개념은 아니라는 점입니다.

Socket은 운영체제가 프로그램에게 제공하는 네트워크 통신 기능입니다.

예를 들어 브라우저가 서버에 요청을 보내거나, Node.js 서버가 클라이언트의 요청을 받거나, 게임 서버가 사용자와 실시간으로 데이터를 주고받을 때 내부적으로는 운영체제의 Socket을 사용합니다.

서버 프로그램이 클라이언트의 요청을 받을 때는 대략 이런 흐름으로 동작합니다.

1. socket()    
통신할 수 있는 소켓을 만든다.
  
2. bind()    
특정 IP와 포트 번호에 소켓을 연결한다.
  
3. listen()    
클라이언트가 접속하기를 기다린다.  

4. accept()    
클라이언트의 연결을 수락한다. 
 
5. read() / write()    
데이터를 읽고 쓴다.  

6. close()    
연결을 종료한다.

즉, 서버가 3000번 포트에서 요청을 기다린다는 것은 운영체제에게 “3000번 문으로 들어오는 요청을 내가 받을게”라고 등록하는 것과 비슷합니다.

그리고 클라이언트가 접속하면 서버는 그 연결을 위한 새로운 소켓을 만들고, 그 소켓을 통해 데이터를 주고받습니다.

서버 소켓 
- 접속을 기다리는 역할  

연결 소켓 
- 실제 클라이언트와 데이터를 주고받는 역할

이렇게 보면 Socket은 단순히 “실시간 통신 기술”이라기보다, 네트워크 통신의 가장 기본적인 통로라고 볼 수 있습니다.

TCP Socket과 UDP Socket

Socket은 대표적으로 TCP Socket과 UDP Socket으로 나눌 수 있습니다.

TCP Socket은 연결을 맺고 데이터를 안정적으로 주고받는 방식입니다.

데이터가 순서대로 도착하는지, 중간에 빠진 데이터는 없는지 확인하면서 통신합니다.

그래서 HTTP, WebSocket, 파일 전송, 채팅처럼 데이터가 정확하게 도착하는 것이 중요한 곳에서 많이 사용됩니다.

반대로 UDP Socket은 연결을 계속 유지하지 않고 데이터를 빠르게 보내는 방식입니다.

데이터가 일부 유실될 수 있지만 속도가 빠릅니다.

그래서 온라인 게임, 음성 통화, 영상 스트리밍처럼 약간의 손실보다 빠른 전달이 더 중요한 곳에서 사용됩니다.

정리하면 이렇게 볼 수 있습니다.

TCP 
- 연결 지향 
- 안정적 
- 순서 보장 
- 데이터 유실 최소화 
- HTTP, WebSocket, 채팅 등에 사용  

UDP 
- 비연결 지향 
- 빠름 
- 일부 데이터 유실 가능 
- 게임, 음성, 영상 등에 사용

WebSocket은 TCP 위에서 동작한다

그리고 WebSocket은 보통 TCP 위에서 동작합니다.

우리가 프론트엔드에서 아래처럼 코드를 작성하면,

tsx const socket = new WebSocket('ws://localhost:3000')

브라우저 내부에서는 대략 이런 흐름으로 통신이 이루어집니다.

React 코드
 ↓ 
브라우저 WebSocket API
 ↓ 
WebSocket 프로토콜
 ↓ 
TCP
 ↓ 
운영체제 Socket
 ↓ 
네트워크

즉, 프론트엔드 개발자가 직접 운영체제의 Socket을 다루지는 않지만, 실제 통신은 그 아래에서 운영체제의 Socket을 통해 이루어집니다.

0개의 댓글