소켓 이론 1/3 · 완전 기초 — IP·포트·서버·클라이언트
소켓이 무엇인지부터 시작합니다. 서버가 포트에서 기다리고 클라이언트가 IP와 포트로 연결해 바이트를 주고받는 한 번의 TCP 대화를 따라갑니다.

목차
소켓 통신은 프로그램 두 개가 네트워크로 데이터를 주고받는 방법입니다. 채팅 앱이 메시지를 보내고, 게임 클라이언트가 위치를 알리고, 웹 서버가 응답을 돌려주는 밑바닥에는 모두 비슷한 흐름이 있습니다.
처음부터 socket, bind, listen, accept 같은 함수 이름을 외울 필요는 없습니다.
먼저 아래 한 문장을 이해하면 됩니다.
서버가 포트에서 기다리고, 클라이언트가 IP와 포트로 찾아가 연결한 뒤, 양쪽이 소켓을 통해 바이트를 주고받는다.
처음이라면 이 순서로 보세요
- 먼저 IP·포트·소켓 세 단어의 역할만 구분합니다.
- 데모에서 기본값 그대로 통신 시작을 눌러 한 번 끝까지 봅니다.
- 그다음 이 글의
socket → bind → listen → accept함수 이름을 흐름에 붙입니다.
첫 번째 관람에서는 바이트 수나 함수 이름을 외우지 않아도 됩니다. 누가 먼저 기다리고, 누가 찾아가며, 데이터가 어느 방향으로 움직이는지만 확인하면 충분합니다.
소켓은 프로그램의 통신 창구입니다
소켓은 운영체제가 프로그램에 내어 주는 데이터 입출력 창구입니다. 프로그램은 네트워크 카드나 TCP 패킷을 직접 다루는 대신 소켓에 데이터를 쓰고, 소켓에서 도착한 데이터를 읽습니다.
파일을 열면 파일 객체를 통해 읽고 쓰듯이, 네트워크 연결을 열면 소켓을 통해 읽고 쓴다고 생각하면 됩니다.
클라이언트 프로그램 → 소켓 ⇄ 네트워크 ⇄ 소켓 ← 서버 프로그램
소켓은 인터넷에만 쓰이지 않습니다. 같은 컴퓨터 안의 두 프로그램도
127.0.0.1이라는 자기 자신용 주소를 통해 소켓으로 통신할 수 있습니다.
IP는 컴퓨터, 포트는 프로그램을 찾습니다
서버에 접속하려면 보통 두 값이 필요합니다.
192.168.0.20 : 8080
└── IP 주소 └── 포트 번호
- IP 주소는 네트워크에서 어느 컴퓨터로 갈지 정합니다.
- 포트 번호는 그 컴퓨터 안의 어느 서버 프로그램에 전달할지 정합니다.
한 컴퓨터에서는 웹 서버, 데이터베이스, 게임 서버가 동시에 실행될 수 있습니다. IP만으로는 셋 중 누구에게 데이터를 줄지 알 수 없으므로 포트가 필요합니다.
자주 보는 주소도 구분해 두면 좋습니다.
| 주소 | 뜻 |
|---|---|
127.0.0.1 | 지금 이 컴퓨터 자신. 다른 컴퓨터에서는 접근할 수 없음 |
192.168.x.x | 같은 공유기 안에서 흔히 쓰는 사설 IP 주소 |
0.0.0.0 | 서버가 모든 네트워크 인터페이스에서 받겠다는 bind용 표현. 접속 목적지로 쓰는 주소는 아님 |
데모의 기본 목적지는 127.0.0.1:8080입니다. 클라이언트와 서버가 같은 컴퓨터에서
실행되는 가장 단순한 경우입니다. 다른 컴퓨터를 고르면 클라이언트는
192.168.0.20:8080으로 접속합니다. 흐름은 같고 목적 IP만 달라집니다.
서버와 클라이언트는 역할 이름입니다
서버와 클라이언트는 기계 종류가 아니라 그 순간의 역할입니다.
- 서버는 알려진 IP·포트에서 먼저 기다립니다.
- 클라이언트는 그 IP·포트로 연결을 시작합니다.
노트북도 서버 프로그램을 실행하면 서버가 될 수 있고, 큰 서버 컴퓨터도 다른 API에 접속할 때는 클라이언트가 됩니다. 하나의 프로그램이 두 역할을 모두 맡기도 합니다.
한 번의 TCP 대화는 이렇게 흘러갑니다
1. 서버가 포트에서 기다립니다
서버 프로그램을 먼저 켭니다. 서버는 운영체제에 “이 컴퓨터의 8080번 포트로 오는 TCP 연결은 내가 받겠다”고 등록하고 기다립니다.
이때 있는 것은 듣는 소켓입니다. 아직 특정 클라이언트와 대화하는 소켓은 없습니다.
2. 클라이언트가 목적지를 정합니다
클라이언트는 서버의 IP와 포트를 알아야 합니다. 같은 컴퓨터라면
127.0.0.1:8080, 다른 컴퓨터라면 그 서버 컴퓨터의 실제 IP와 8080을 사용합니다.
3. TCP 연결을 만듭니다
클라이언트가 연결을 요청하면 TCP는 양쪽이 준비됐는지 확인합니다. 연결이 성립한 뒤 클라이언트와 서버 양쪽에 연결된 소켓이 생깁니다.
서버에는 이제 역할이 다른 소켓이 둘입니다.
- 듣는 소켓은 8080번 포트에서 다음 연결을 계속 기다립니다.
- 연결된 소켓은 방금 들어온 클라이언트 한 명과 데이터를 주고받습니다.
4. 요청을 보냅니다
클라이언트가 안녕하세요!라는 문자열을 소켓에 씁니다. 네트워크에서 실제로 이동하는
것은 글자가 아니라 UTF-8 같은 규칙으로 인코딩된 바이트입니다.
"안녕하세요!" → ec 95 88 eb 85 95 ... → 네트워크 전송
5. 서버가 읽고 응답합니다
운영체제는 도착한 바이트를 소켓의 수신 버퍼에 둡니다. 서버 프로그램이 그 바이트를
읽고 반가워요!라는 응답을 같은 연결로 돌려보냅니다.
TCP 연결은 양방향입니다. 클라이언트만 보내는 통로가 아니라 어느 쪽이든 읽고 쓸 수 있습니다.
6. 연결을 닫습니다
대화가 끝나면 양쪽의 연결된 소켓을 닫습니다. 서버의 듣는 소켓은 계속 살아 있으므로 서버 프로그램을 다시 켜지 않아도 다음 클라이언트를 받을 수 있습니다.
TCP는 메시지가 아니라 바이트 흐름입니다
입문 단계에서 꼭 알아둘 부분입니다. TCP는 보낸 순서대로 바이트를 전달하지만, 애플리케이션의 “메시지 한 개” 경계를 그대로 보존한다고 약속하지는 않습니다.
클라이언트가 HELLO와 WORLD를 두 번 보냈다고 서버의 recv()도 반드시 두 번에
나뉘어 반환되는 것은 아닙니다. HELLOWORLD로 한 번에 읽힐 수도 있고 여러 조각으로
나뉠 수도 있습니다.
그래서 실제 프로토콜은 다음 중 하나로 메시지 경계를 정합니다.
- 줄바꿈 같은 구분자를 붙입니다.
- 앞에 본문 크기를 적은 길이 헤더를 붙입니다.
- HTTP나 WebSocket처럼 이미 정해진 프로토콜 형식을 사용합니다.
데모는 첫 개념에 집중하려고 요청 하나와 응답 하나가 깔끔하게 도착하는 모습으로 단순화했습니다.
TCP와 UDP는 무엇이 다를까요?
소켓을 만들 때 통신 방식도 정합니다.
| TCP | UDP | |
|---|---|---|
| 연결 | 먼저 연결함 | 프로토콜 연결 없이 바로 보냄 |
| 데이터 | 순서 있는 바이트 스트림 | 메시지 단위 데이터그램 |
| 손실 처리 | TCP가 재전송 | 필요하면 프로그램이 처리 |
| 주로 쓰는 곳 | 웹, 채팅, 데이터베이스, 파일 전송 | 음성·영상, 게임 상태, DNS |
TCP가 항상 좋고 UDP가 항상 빠르다는 뜻은 아닙니다. 정확한 전달과 연결 상태가 중요하면 TCP가 편하고, 일부 손실보다 최신 데이터와 짧은 지연이 중요하면 UDP가 잘 맞을 수 있습니다. 이번 데모는 흐름을 이해하기 쉬운 TCP만 다룹니다.
함수 이름은 흐름 뒤에 붙이면 됩니다
개념을 실제 코드 이름에 대응하면 다음과 같습니다.
서버
import socket
server = socket.socket() # TCP 통신 창구 만들기
server.bind(("127.0.0.1", 8080)) # 8080번 포트 담당
server.listen() # 연결 기다리기
conn, address = server.accept() # 클라이언트 한 명의 대화 소켓
data = conn.recv(1024) # 바이트 읽기
conn.sendall("반가워요!".encode("utf-8")) # 문자열을 바이트로 바꿔 쓰기
conn.close() # 이 대화 닫기
클라이언트
import socket
client = socket.socket()
client.connect((server_ip, 8080))
client.sendall("안녕하세요!".encode("utf-8"))
reply = client.recv(1024).decode("utf-8")
client.close()
언어가 바뀌어도 서버 대기 → 클라이언트 연결 → 읽기·쓰기 → 종료라는 구조는 같습니다. 이 연결을 방화벽 안쪽 PC가 먼저 여는 방향 원리는 아웃바운드 소켓에서, Python·Node.js·Go·Rust가 여러 연결을 실행하는 방식의 차이는 소켓 런타임 비교에서 이어집니다.
처음 만나는 문제 세 가지
- Connection refused — 그 IP·포트에서 기다리는 서버가 없습니다. 서버가 켜졌는지, 포트가 맞는지 확인합니다.
- Address already in use — 다른 프로그램이 이미 그 포트를 사용하고 있습니다.
- 연결은 됐는데 응답이 없음 — 서버가 읽을 메시지 경계를 기다리거나, 상대가 응답을 보내지 않았거나, timeout 없이 계속 기다리는 상황일 수 있습니다.
실제 서버에는 연결·읽기·쓰기 timeout, 보낼 데이터가 밀릴 때의 backpressure, 동시 연결 수 제한, 오류 로그와 정상 종료 처리가 필요합니다. 언어 선택만으로 이런 운영 장치가 자동으로 생기지는 않습니다.
데모에서 확인할 것
- 통신 시작을 누르고 서버가 먼저 8080번 포트에서 기다리는지 봅니다.
- IP와 포트가 각각 컴퓨터와 프로그램을 가리키는 장면을 확인합니다.
안녕하세요!가 문자열에서 바이트로 바뀌어 이동하는 모습을 봅니다.- 응답이 같은 연결에서 반대 방향으로 돌아오는지 봅니다.
- 대화 소켓을 닫아도 서버의 듣는 소켓은 계속 살아 있는지 확인합니다.
- 다른 컴퓨터와 상태 확인을 골라 목적지와 메시지만 바뀌고 전체 흐름은 같은지 비교합니다.
연결을 만들 때 TCP가 안에서 주고받는 세 번의 인사는 TCP 3-way handshake 랩에서 볼 수 있고, 바이트에 포트·IP·MAC 주소가 어떻게 붙는지는 네트워크 계층 랩에서 이어집니다.
소켓 이론 2단계로 이어가려면 안쪽 PC가 먼저 연결하는 아웃바운드 소켓을 보세요. “연결을 시작한 방향”과 “데이터가 오가는 방향”이 왜 다른지 확인합니다.
참고
이 데모의 IP는 문서용 예시이고 실제 네트워크 연결을 만들지 않는 시뮬레이션입니다. 패킷 분할·재전송·흐름 제어·혼잡 제어·FIN 종료 과정은 화면에서 생략했습니다.
자료: RFC 9293 — Transmission Control Protocol, Python socket 공식 문서, Linux socket(2)

