← 목록으로
네트워크
#소켓#TCP#WebSocket#실시간통신#131회
최종 업데이트 · 2026-09-27

소켓(Socket) 통신

1. 개요

가. 정의

소켓(Socket) 은 네트워크상에서 프로세스 간 통신을 위한 양 끝점(Endpoint) 으로, IP 주소와 포트 번호의 조합으로 식별된다. 소켓 통신은 이 소켓을 통해 응용 프로그램이 데이터를 송수신하는 방식으로, 운영체제가 TCP/IP 스택을 표준 API로 감싸 제공하는 프로세스 간 통신(IPC)의 네트워크 확장이다.

소켓을 이해하는 핵심은 '응용 프로그램이 복잡한 네트워크를 다루기 위한 추상화된 창구'라는 점이다. TCP/IP의 내부 동작(패킷 분할·재조립, 라우팅, 순서 보장, 재전송, 흐름 제어, 혼잡 제어)은 대단히 복잡하지만, 개발자는 socket()·bind()·connect()·send()·recv()라는 표준 인터페이스만 알면 이 복잡성을 몰라도 통신할 수 있다. 이는 마치 전화기를 쓸 때 교환망의 내부 동작을 몰라도 되는 것과 같은 추상화의 힘이다.

소켓은 두 개의 좌표로 통신 상대를 특정한다. IP 주소로 '인터넷상의 어느 컴퓨터(호스트)인지'를, 포트 번호로 '그 컴퓨터의 어느 프로그램(프로세스)인지'를 지정한다. 예를 들어 웹 서버는 보통 80번(HTTP)·443번(HTTPS) 포트에서 소켓을 열어 클라이언트의 연결을 기다린다. IP:포트는 흔히 '건물 주소:호실'에 비유되며, TCP 통신에서 하나의 연결은 (출발지 IP, 출발지 포트, 목적지 IP, 목적지 포트, 프로토콜)의 5-튜플(5-tuple) 로 유일하게 식별된다. 이 5-튜플 개념 덕분에 한 서버가 동일한 80번 포트로도 수만 개의 클라이언트 연결을 서로 구분해 동시에 처리할 수 있다.

나. 등장 배경 및 필요성

1980년대 초 BSD 유닉스가 네트워크 프로그래밍을 위해 버클리 소켓(Berkeley Socket) API 를 도입한 것이 소켓의 기원이다. 그 이전에는 네트워크 하드웨어·프로토콜마다 통신 코드를 새로 작성해야 했으나, 소켓이 파일 입출력(read/write)과 유사한 통일된 디스크립터 모델로 네트워크 통신을 추상화하면서 이식성 있는 네트워크 애플리케이션 개발이 가능해졌다. 서로 다른 기기의 프로그램이 통신하려면 ① 상대를 특정하고 ② 데이터를 안정적으로 주고받으며 ③ 운영체제·언어에 무관하게 동작하는 표준 방법이 필요한데, 소켓은 이 세 요구를 모두 충족하는 사실상의 표준(de facto standard)이 되었다. 오늘날 웹·메신저·게임·IoT·스트리밍 등 거의 모든 네트워크 애플리케이션이 소켓 위에서 동작한다.

다. 특징

소켓 통신은 양방향(bidirectional) 이며, 연결형 소켓의 경우 한 번 연결을 수립하면 명시적으로 닫기 전까지 상태를 유지(stateful) 한다. 또한 운영체제 커널이 버퍼링·재전송·흐름 제어를 담당하므로 응용 계층은 데이터의 의미에만 집중할 수 있다. 반면 이 추상화 아래에서 실제로는 전송 계층 프로토콜(TCP/UDP)의 특성이 그대로 드러나므로, 개발자는 어떤 소켓 유형을 선택하느냐에 따라 신뢰성과 성능의 트레이드오프를 직접 책임져야 한다.

2. 소켓의 구조와 식별 체계

flowchart TB
  subgraph APP["응용 계층 (Application)"]
    A1["응용 프로그램"]
  end
  subgraph OS["운영체제 커널 (OS Kernel)"]
    S["소켓 (Socket Descriptor)"]
    SB["송신 버퍼 / 수신 버퍼"]
    TCP["전송 계층 (TCP / UDP)"]
    IP["네트워크 계층 (IP)"]
  end
  NIC["NIC (물리 계층)"]
  A1 -->|"socket() / send() / recv()"| S
  S --> SB --> TCP --> IP --> NIC
  NIC -->|"네트워크"| NET(("인터넷"))
  style S fill:#e8f0fe,stroke:#2f6fed

소켓은 응용 프로그램과 운영체제 커널의 네트워크 스택 사이에 위치하는 경계면이다. 응용 프로그램이 socket()을 호출하면 커널은 통신에 필요한 자료구조를 만들고 소켓 디스크립터(파일 디스크립터의 일종) 라는 정수 핸들을 돌려준다. 이후 프로그램은 이 정수 하나로 네트워크 통신을 파일처럼 다룬다. 유닉스 계열에서 "모든 것은 파일이다"라는 철학이 네트워크까지 확장된 결과이며, 이 통일성 덕분에 select/poll 같은 다중화 함수가 파일과 소켓을 동일하게 취급할 수 있다.

소켓 디스크립터가 파일 디스크립터와 같은 자원 풀을 쓴다는 점은 실무에서 구체적 제약으로 나타난다. 리눅스에서 프로세스가 동시에 열 수 있는 디스크립터 수는 기본값이 흔히 1024(ulimit -n)로 잡혀 있어, 이 값을 올리지 않으면 동시 연결이 1024를 넘는 순간 accept()가 EMFILE 오류로 실패한다. 따라서 대량 연결 서버는 ulimit·fs.file-max 같은 커널 한계를 수십만 단위로 상향하고, 연결 누수(닫지 않은 소켓)가 없는지 모니터링해야 한다. 이는 소켓이 단순한 개념이 아니라 운영체제 자원 관리와 직결된 실체임을 보여 준다.

소켓의 식별 체계에서 특히 중요한 것은 송신 버퍼와 수신 버퍼의 존재다. send()가 반환되었다고 데이터가 상대에게 도착한 것이 아니라 커널의 송신 버퍼에 복사되었을 뿐이며, 실제 전송·재전송·순서 보장은 커널의 TCP 구현이 비동기로 수행한다. 이 때문에 소켓 프로그래밍에서는 "부분 전송(partial write)"과 "부분 수신(partial read)"을 반드시 고려해야 한다. 예컨대 10KB를 send()해도 버퍼 상황에 따라 4KB만 처리될 수 있으므로, 응용은 반환값을 확인하며 반복 전송하는 루프를 작성해야 한다. 이 지점을 소홀히 하면 대용량 전송에서 데이터 누락처럼 보이는 버그가 발생한다.

구성요소 역할 실무적 함의
소켓 디스크립터 통신 종단점을 가리키는 정수 핸들 프로세스당 열 수 있는 수(ulimit -n)가 동시 연결 상한을 좌우
IP 주소 호스트(컴퓨터) 식별 NAT·다중 인터페이스 환경에서 바인딩 주소 선택 필요
포트 번호(0~65535) 프로세스(서비스) 식별 0~1023은 well-known 포트(권한 필요)
송·수신 버퍼 커널의 데이터 임시 저장소 버퍼 크기(SO_RCVBUF 등)가 처리량에 영향
5-튜플 연결의 유일 식별자 한 포트로 다수 연결 동시 처리 근거

3. 소켓 통신 절차와 유형

sequenceDiagram
  participant C as 클라이언트
  participant S as 서버
  Note over S: socket() → bind() → listen()
  S->>S: accept() 로 연결 대기(블로킹)
  C->>C: socket()
  C->>S: connect() (TCP 3-way handshake)
  S-->>C: accept() 반환(연결 소켓 생성)
  loop 데이터 교환
    C->>S: send() / recv()
    S->>C: send() / recv()
  end
  C->>S: close() (4-way handshake)
  S->>S: close()

TCP 소켓 통신의 절차는 서버 준비 → 연결 수립 → 데이터 교환 → 종료의 4단계로 이뤄진다. 서버는 먼저 socket()으로 소켓을 만들고, bind()로 자신의 IP·포트를 결합한 뒤, listen()으로 연결 요청을 받을 수 있는 상태(대기 큐 생성)로 전환하고, accept()에서 클라이언트의 연결을 기다린다. 여기서 중요한 설계 포인트는 accept()가 새로운 연결 소켓을 별도로 반환한다는 점이다. 즉 listen 소켓은 연결을 받아들이는 '접수 창구'로 계속 남고, 실제 데이터 통신은 클라이언트마다 새로 생성된 연결 소켓이 담당한다. 이 구조가 한 서버가 다수 클라이언트를 동시에 서비스할 수 있는 근거다.

클라이언트는 socket() 후 connect()로 서버에 연결을 요청하며, 이 과정에서 TCP의 3-way handshake(SYN → SYN/ACK → ACK) 가 일어난다. 연결이 수립되면 양쪽이 send()/recv()로 데이터를 주고받고, 통신이 끝나면 close()가 4-way handshake(FIN/ACK 왕복) 를 통해 연결을 정리한다. 이때 서버 측에는 TIME_WAIT 상태가 일정 시간(보통 2*MSL) 남는데, 이는 지연 도착 패킷을 처리하기 위한 안전장치이나, 짧은 연결을 대량으로 맺는 서버에서는 TIME_WAIT 소켓이 누적되어 포트 고갈을 유발할 수 있다. 실무에서는 SO_REUSEADDR 옵션이나 연결 재사용(keep-alive)으로 이를 완화한다.

소켓은 사용하는 전송 프로토콜에 따라 두 유형으로 나뉜다. 스트림 소켓(TCP) 은 연결을 수립하고 데이터의 순서·신뢰성을 보장하며 경계 없는 바이트 스트림을 제공한다. 스트림이라는 성질 때문에 응용이 보낸 메시지 경계가 보존되지 않아, 여러 메시지가 한 번에 붙어 오거나(쪼개져 오거나) 하는 현상이 생긴다. 이를 처리하려면 길이 프리픽스나 구분자 같은 자체 프레이밍(framing) 규약이 필요하다. 데이터그램 소켓(UDP) 은 연결 없이 개별 메시지를 빠르게 보내지만 순서·도착을 보장하지 않는 대신, 메시지 경계는 보존된다.

소켓의 동작 방식에는 또 하나의 중요한 축이 있다. recv()처럼 데이터가 도착할 때까지 스레드를 멈추는 블로킹(blocking) 소켓과, 즉시 반환하고 준비 여부만 알려주는 논블로킹(non-blocking) 소켓의 구분이다. 블로킹 방식은 코드가 직관적이지만 연결마다 스레드를 점유해 확장성이 떨어지고, 논블로킹 방식은 이벤트 루프와 결합해 소수 스레드로 대량 연결을 처리할 수 있으나 상태 관리가 복잡해진다. 이 선택이 뒤에서 다룰 대규모 서버의 I/O 모델과 직결된다. 또한 SO_KEEPALIVE(유휴 연결 생존 확인), TCP_NODELAY(Nagle 알고리즘 비활성화로 소량 데이터 지연 제거), SO_REUSEADDR(주소 재사용) 같은 소켓 옵션을 통해 동일한 소켓 API 위에서도 지연·처리량·자원 사용을 세밀하게 튜닝한다. 예컨대 실시간 게임 서버는 TCP_NODELAY를 켜 작은 패킷이 버퍼에 모였다 늦게 전송되는 것을 막는다.

유형 프로토콜 특징 대표 용도
스트림 소켓 TCP 연결지향, 신뢰성·순서 보장, 바이트 스트림 웹·파일 전송·DB·메신저
데이터그램 소켓 UDP 비연결, 빠르나 비신뢰, 메시지 경계 보존 실시간 영상·게임·DNS·VoIP
RAW 소켓 IP/ICMP 등 하위 계층 직접 접근 ping·트레이스라우트·보안도구

4. TCP/UDP 소켓과 WebSocket — 비교 및 사례

TCP 소켓이 전송 계층을 직접 다루는 저수준 통신이라면, WebSocket 은 웹 브라우저 환경을 위한 상위 계층 기술이다. 브라우저는 보안·정책상 임의의 TCP 소켓을 열 수 없으므로, 실시간 양방향 통신을 위한 표준으로 WebSocket(RFC 6455)이 등장했다. WebSocket은 처음에 일반 HTTP GET 요청으로 시작한 뒤, Upgrade: websocket 헤더로 프로토콜을 전환(핸드셰이크)하고, 이후에는 하나의 지속 연결(같은 TCP 연결) 위에서 서버와 클라이언트가 자유롭게(full-duplex) 프레임 단위 메시지를 주고받는다. 이 덕분에 서버가 클라이언트에게 먼저 데이터를 보내는 '서버 푸시'가 가능해, 채팅·실시간 알림·주식 시세·협업 편집(구글 독스류) 같은 실시간 웹 서비스를 구현한다.

구체적 사례로 비교하면, 온라인 게임은 수십 ms의 지연도 체감되므로 위치·움직임 데이터를 UDP로 보내고(일부 유실은 다음 패킷으로 보정), 결제·아이템 거래처럼 정확성이 중요한 데이터는 TCP로 처리하는 하이브리드 구조를 흔히 쓴다. 화상회의(WebRTC) 역시 미디어는 UDP 기반(SRTP)으로, 시그널링은 신뢰성이 필요해 TCP/WebSocket으로 나눈다. 증권 시세 대시보드는 초당 수천 건의 시세 갱신을 서버가 밀어줘야 하므로 WebSocket이 폴링 대비 지연과 서버 부하를 크게 낮춘다. 이처럼 데이터의 지연 민감도와 정확성 요구가 프로토콜 선택을 좌우한다.

구분 TCP 소켓 WebSocket
계층 전송 계층(TCP) 직접 응용 계층(HTTP 위 업그레이드)
연결 socket→connect→3-way handshake HTTP 핸드셰이크 후 Upgrade
통신 양방향 바이트 스트림 양방향 full-duplex(프레임 단위)
환경 서버·네이티브 앱 웹 브라우저 포함 전 영역
프레이밍 응용이 직접 구현 프로토콜이 프레임 제공
용도 일반 네트워크 앱 웹 실시간(채팅·알림·시세)

5. 소켓 통신과 HTTP 통신 비교

소켓 통신과 HTTP는 연결 유지 방식이 근본적으로 다르다. HTTP는 요청-응답 후 처리를 완료하는 무상태(Stateless) 지향 방식이라, 원칙적으로 서버가 먼저 클라이언트에게 말을 걸 수 없다. 실시간이 필요하면 클라이언트가 반복 요청하는 폴링(polling) 이나 롱폴링에 의존하는데, 이는 불필요한 요청·헤더 오버헤드와 지연을 유발해 비효율적이다. 다만 HTTP도 내부적으로는 TCP 소켓 위에서 동작하며, HTTP/1.1의 keep-alive, HTTP/2의 멀티플렉싱은 연결 재사용으로 이 오버헤드를 줄여 왔다. 소켓 통신(특히 WebSocket)은 연결 자체를 유지해 양방향·실시간 통신을 자연스럽게 지원한다는 점이 핵심 차이다.

실무에서는 셋 사이의 중간 대안도 활발히 쓰인다. 서버가 클라이언트로 한 방향 푸시만 필요하면 SSE(Server-Sent Events) 가 HTTP 위에서 경제적으로 동작하고, 진정한 양방향·저지연이 필요하면 WebSocket을, 단순 요청-응답은 REST(HTTP)를 선택하는 식이다. 이 판단은 성능뿐 아니라 방화벽·프록시 통과성, 재연결·인증 처리의 복잡도까지 함께 고려해야 한다.

정량적으로 보면 차이가 뚜렷하다. 예컨대 1초마다 갱신되는 데이터를 1만 명에게 폴링으로 제공하면 초당 1만 건의 HTTP 요청이 발생하고 매 요청마다 수백 바이트의 헤더가 반복 전송되지만, WebSocket은 최초 핸드셰이크 한 번 뒤 필요할 때만 수 바이트 프레임으로 밀어주므로 네트워크·서버 부하가 수십 배 이상 줄어든다. 반대로 하루 몇 번만 조회하는 단순 정보라면 지속 연결을 유지하는 비용이 오히려 낭비이므로 HTTP 요청-응답이 정답이다. 즉 '무엇이 우월한가'가 아니라 '갱신 빈도·양방향성·연결 수'라는 워크로드 특성에 맞추는 것이 핵심이다.

구분 소켓 통신(WebSocket) HTTP 통신
연결 지속 연결(상태 유지) 요청-응답 후 종료(무상태)
방향 양방향(서버 푸시 가능) 단방향(클라이언트 시작)
실시간성 높음 낮음(폴링 필요)
오버헤드 초기 핸드셰이크 후 프레임 최소 매 요청 헤더 반복
용도 실시간·양방향 웹 문서·REST API

6. 심화: 대규모 실시간 서비스와 고성능 소켓 처리

대규모 실시간 서비스에서 소켓의 진짜 과제는 수많은 동시 연결을 어떻게 효율적으로 유지·처리하느냐이다. 이는 고전적인 C10K 문제(한 서버가 1만 개 동시 연결을 감당하는 문제)로 정식화되었고, 오늘날 C10M(천만) 수준까지 논의된다. 초기의 '연결마다 스레드/프로세스' 모델은 연결 수만큼 메모리와 문맥 전환 비용이 선형 증가해 확장에 한계가 있었다. 이를 해결한 것이 이벤트 기반 I/O 다중화(I/O multiplexing) 다. 리눅스의 epoll, BSD/macOS의 kqueue, 윈도우의 IOCP는 하나의 스레드가 수만 개 소켓의 상태 변화를 커널로부터 효율적으로 통지받아 처리하게 해 준다. Nginx·Node.js·Redis가 소수 스레드로 대량 연결을 감당하는 비결이 바로 이 이벤트 루프 모델이다. 최근 리눅스의 io_uring(5.1+)은 시스템 콜 오버헤드마저 줄이는 비동기 I/O 인터페이스로, 고성능 서버·프록시에서 채택이 확대되고 있다.

이벤트 루프 모델의 위력은 자원 사용량에서 극명하게 드러난다. 연결마다 스레드를 쓰는 모델은 스레드 스택만으로도 연결당 수백 KB~수 MB를 소비해 1만 연결이면 수 GB의 메모리가 필요하지만, 이벤트 루프는 연결을 소켓 디스크립터와 소량의 상태 객체로만 관리해 같은 하드웨어에서 훨씬 많은 연결을 감당한다. 다만 이벤트 루프는 단일 스레드에서 콜백이 오래 점유되면 전체가 멈추므로, CPU 집약 작업은 워커 스레드/프로세스로 분리하는 설계가 병행되어야 한다. 이처럼 소켓 처리 모델의 선택은 곧 서버의 비용 구조와 장애 특성을 결정한다.

WebSocket 기반 서비스를 여러 대의 서버로 스케일아웃(scale-out) 할 때는 새로운 문제가 생긴다. 지속 연결은 특정 서버 인스턴스에 고정(고정 세션)되므로, A 서버에 연결된 사용자에게 B 서버가 받은 메시지를 전달하려면 서버 간 메시지 전파가 필요하다. 실무에서는 Redis Pub/Sub, Kafka 같은 메시지 브로커를 백플레인으로 두어 인스턴스 간 이벤트를 팬아웃하고, 로드밸런서에서 sticky session이나 일관 해싱으로 연결을 분배한다. 또한 유휴 연결이 방화벽·NAT에 의해 끊기지 않도록 주기적 핑/퐁(heartbeat) 을 보내고, 끊긴 연결은 지수 백오프로 재연결하는 등 연결 수명주기 관리가 서비스 품질을 좌우한다. 카카오톡·슬랙·디스코드 같은 대규모 메신저가 이러한 아키텍처의 대표 사례다.

보안 측면에서는 소켓 통신에 TLS를 적용해(TCP는 TLS, WebSocket은 wss://) 도청·변조를 막고, WebSocket 핸드셰이크 시 Origin 검증과 토큰 기반 인증(JWT 등)으로 무단 연결과 CSWSH(교차 사이트 WebSocket 하이재킹)를 방지해야 한다. 또한 연결당 자원 상한, 메시지 크기 제한, 속도 제한(rate limiting)으로 자원 고갈형 DoS에 대비한다.

최근에는 소켓 통신의 지형 자체가 변하고 있다. QUIC(HTTP/3의 전송 기반) 는 UDP 소켓 위에 연결·신뢰성·암호화(TLS 1.3 내장)를 재구현해, TCP의 3-way handshake와 TLS 핸드셰이크를 하나로 합치고 연결 마이그레이션(네트워크 전환 시에도 연결 유지)을 지원한다. 이는 "신뢰성은 반드시 TCP"라는 오랜 전제를 깨고, UDP 소켓을 실시간·신뢰성 양쪽에서 활용하는 방향으로 응용 개발의 무게중심을 옮기고 있다. 또한 서버리스·엣지 환경이 확산되면서, 장시간 지속 연결을 전제로 한 전통적 WebSocket 대신 서버 측이 상태를 최소화하고 관리형 WebSocket 게이트웨이(예: API Gateway의 WebSocket 지원)에 연결 관리를 위임하는 패턴도 늘고 있다.

7. 고려사항 및 시사점

  1. 요구 특성에 맞는 통신 방식 선택이 아키텍처의 출발점이다. 단순 요청-응답은 HTTP/REST가 간단하고, 실시간 양방향은 WebSocket, 서버 단방향 푸시는 SSE, 지연 민감·소량 손실 허용은 UDP가 적합하다. 기술사 관점에서는 성능만이 아니라 개발·운영 복잡도, 프록시 통과성까지 포함한 종합 판단이 필요하다.

  2. 동시 연결 확장성은 I/O 모델이 결정한다. 대규모 서비스는 스레드-퍼-커넥션이 아닌 epoll/kqueue/IOCP 기반 이벤트 루프, 나아가 io_uring을 검토해야 하며, 연결 유지 서비스는 메시지 브로커·고정 세션·백플레인 설계를 함께 가져가야 한다.

  3. 신뢰성과 속도의 트레이드오프를 데이터 단위로 분리 적용하는 것이 현실적이다. 게임·화상회의처럼 한 서비스 안에서도 미디어는 UDP, 제어·거래는 TCP로 나누는 하이브리드 설계가 일반적이며, 이는 QUIC(HTTP/3)처럼 UDP 위에 신뢰성을 재구현하는 최신 흐름과도 연결된다.

  4. 연결 수명주기와 장애 복원력을 반드시 설계에 반영해야 한다. TIME_WAIT·포트 고갈, 좀비 연결, NAT 타임아웃, 부분 전송 등은 소규모에서는 드러나지 않다가 트래픽이 커지면 장애로 번지므로, heartbeat·재연결·백프레셔·타임아웃 정책을 표준화해야 한다.

  5. 보안 내재화(security by design) 가 필수다. 평문 소켓은 지양하고 TLS/wss를 기본으로, 인증·Origin 검증·속도 제한을 통신 계층에 내장해 실시간 채널이 공격 표면이 되지 않도록 해야 한다.

  6. 관측 가능성(observability) 확보가 운영의 전제다. 지속 연결은 요청-응답과 달리 문제가 조용히 누적되므로, 활성 연결 수·연결 수명·재연결율·메시지 지연·버퍼 점유 같은 지표를 상시 계측하고 임계치 알람을 걸어야 조기에 이상을 포착할 수 있다. 이는 SRE 관점의 SLO 관리와도 자연스럽게 연결된다.

참고자료


한 줄 요약: 소켓은 IP·포트로 식별되는 통신 양 끝점으로 TCP(신뢰)·UDP(속도) 소켓과 실시간 양방향 WebSocket이 있으며, 지속·양방향인 소켓 통신은 요청-응답·무상태인 HTTP와 대비되어 실시간 서비스에 적합하고, 대규모 확장은 epoll·io_uring 등 이벤트 기반 I/O와 메시지 브로커·보안 설계가 관건이다.