- 패킷 경계와 메시지 구분 처리2026년 06월 25일
- 소쩍소쩍이
- 작성자
- 2026.06.25.:33
딕셔너리를 JSON 메시지로 보내기
채팅 메시지를 딕셔너리로 표현하면 메시지의 종류와 내용을 명확하게 구분할 수 있다.
하지만 딕셔너리는 Python 안에서 사용하는 데이터 구조이기 때문에 그대로 네트워크로 보낼 수 없다. 네트워크로 보내려면 JSON 문자열로 변환한 뒤 바이트로 바꾸어 전송해야 한다.
보낼 때와 받을 때의 변환 흐름
보낼 때:
딕셔너리 → json.dumps() → JSON 문자열 → + "\n" → encode("utf-8") → sendall()
받을 때:
recv() → decode("utf-8") → buffer에 추가 → "\n" 기준으로 분리 → json.loads() → 딕셔너리sendall()을 사용하는 이유
이전에는 send()를 사용했지만, 이번에는 sendall()을 사용한다.
# 이전 방식 client_socket.send(message.encode("utf-8")) # 이번 방식 sock.sendall(data.encode(ENCODING))send()는 데이터를 커널 송신 버퍼에 복사하는 함수이다. 버퍼에 자리가 부족하면 일부만 복사하고 끝날 수도 있다.
sendall()은 데이터를 전부 보낼 때까지 내부적으로 반복 시도한다. 그래서 메시지를 안정적으로 보내기 위해 sendall()을 사용하는 것이 좋다.
핵심
send()는 일부만 보낼 가능성이 있지만, sendall()은 전달한 데이터 전체를 보내려고 끝까지 시도한다.Protocol 만들기
protocol.py 파일을 만들어 메시지 송신과 수신 규칙을 함수로 분리한다.
import json ENCODING = "utf-8" DELIMITER = "\n"상수 의미 ENCODING 문자열을 바이트로 변환할 때 사용하는 인코딩 방식 DELIMITER 메시지 끝을 표시하는 구분자. 여기서는 \n 사용 send_message() 함수 추가
def send_message(sock, message): # 송신을 함수로 분리 json_text = json.dumps(message, ensure_ascii=False) # 딕셔너리 → JSON 문자열 data = json_text + DELIMITER # 끝에 구분자 \n 추가 sock.sendall(data.encode(ENCODING)) # send() → sendall()로 변경send_message() 함수는 딕셔너리를 JSON 문자열로 바꾸고, 메시지 끝에 구분자인 \n을 붙인 뒤 UTF-8 바이트로 변환해 전송한다.
주의
json.dumps()에서 ensure_ascii=False를 넣지 않으면 한글이 유니코드 이스케이프 형태로 변환된다.# ensure_ascii=True (기본값) — 한글이 이스케이프됨 json.dumps({"content": "안녕하세요"}) # '{"content": "\\uc548\\ub155\\ud558\\uc138\\uc694"}' # ensure_ascii=False — 한글이 그대로 유지됨 json.dumps({"content": "안녕하세요"}, ensure_ascii=False) # '{"content": "안녕하세요"}'receive_messages() 함수 추가
📄 receive_messages() 함수 코드
def receive_messages(sock, buffer): # buffer를 넘겨받는 수신 함수 data = sock.recv(1024) if not data: return buffer, [], False # recv()가 빈 바이트 → 연결 끊김 신호 buffer += data.decode(ENCODING) # 받은 조각을 buffer에 누적 messages = [] while DELIMITER in buffer: # \n이 있는 동안 반복해서 꺼낸다 raw_message, buffer = buffer.split(DELIMITER, 1) # \n 하나만 기준으로 자르고 나머지는 buffer에 남김 if raw_message.strip(): message = json.loads(raw_message) # JSON 문자열 → 딕셔너리 messages.append(message) return buffer, messages, True # (남은 buffer, 완성된 메시지 목록, 연결 유지)receive_messages()는 recv()로 받은 조각을 바로 json.loads()에 넣지 않는다. 먼저 buffer에 누적하고, 구분자인 \n이 있을 때만 완성된 메시지로 판단해 하나씩 꺼낸다.
① 받은 조각을 buffer에 계속 모은다
↓
② \n이 있을 때만 앞에서부터 하나씩 잘라낸다
↓
③ 아직 \n이 없는 나머지는 buffer에 남겨 둔다
↓
④ 완성된 JSON 문자열만 json.loads()로 딕셔너리로 바꾼다if not data의 의미
상대가 연결을 닫으면 recv()는 빈 바이트를 돌려준다. 이는 더 들어올 데이터가 없다는 연결 종료 신호이다.
그래서 세 번째 반환값을 False로 돌려 호출한 쪽이 반복을 멈추게 한다.
buffer.split(DELIMITER, 1)의 의미
split("\n")처럼 인자 없이 쓰면 모든 \n을 한 번에 잘라버린다. 이 경우 미완성 조각까지 분리될 수 있다.
반면 split(DELIMITER, 1)은 가장 앞의 \n 하나만 기준으로 자르고, 나머지는 다시 buffer로 돌려준다. 이 방식을 while 반복과 함께 사용하면 완성된 메시지만 하나씩 안전하게 꺼낼 수 있다.
buffer = '{"type":"chat","content":"A"}\n{"type":"chat","content":"B"}\n' 1회 while: raw_message = '{"type":"chat","content":"A"}' buffer = '{"type":"chat","content":"B"}\n' 2회 while: raw_message = '{"type":"chat","content":"B"}' buffer = ''반환값 의미 buffer 아직 \n이 오지 않아 처리되지 않은 남은 데이터 messages 이번 recv()에서 완성된 메시지 딕셔너리 목록 True / False True면 연결 유지, False면 연결 끊김 흔한 오류와 해결 방법
오류 상황 원인 및 해결 방법 ModuleNotFoundError: No module named 'protocol' (서버) chat_server/에 protocol.py가 없음. 같은 폴더에 만들었는지 확인한다 ModuleNotFoundError: No module named 'protocol' (클라이언트) chat_client/에 protocol.py 복사본이 없음. chat_client/protocol.py를 만든다 json.decoder.JSONDecodeError 완성되지 않은 JSON을 json.loads()에 전달했음. \n으로 분리된 raw_message만 json.loads()에 넣는지 확인한다 메시지가 다른 클라이언트에 전달되지 않음 send_message() 대신 기존 send()를 섞어 사용했을 수 있음. 서버와 클라이언트 모두 send_message()로 통일한다 한 메시지가 가끔 둘로 쪼개져 보임 서버와 클라이언트의 protocol.py 내용이 다를 수 있음. 특히 DELIMITER 값이 같은지 확인한다 한글이 \uc548\ub155 형태로 출력됨 ensure_ascii=False가 없음. json.dumps() 호출 시 추가한다 ConnectionRefusedError 서버가 실행되지 않았음. 서버를 먼저 실행한다 확인 기준
서로의 메시지가 정상 전달되고, 서버에 딕셔너리 형태로 출력되면 완료이다.
send_message()가 메시지 끝에 \n을 붙이는지, receive_messages()가 while 반복으로 메시지를 하나씩 꺼내는지 확인하면 된다.정리
딕셔너리는 네트워크로 바로 보낼 수 없기 때문에 JSON 문자열로 변환해야 한다.
보낼 때는 json.dumps() → 구분자 추가 → encode() → sendall() 순서로 처리한다.
받을 때는 recv() → decode() → buffer 누적 → 구분자 기준 분리 → json.loads() 순서로 처리한다.
이 구조를 protocol.py로 분리하면 서버와 클라이언트가 같은 메시지 규칙을 안정적으로 사용할 수 있다.'Server - Client' 카테고리의 다른 글
패킷 경계란? (0) 2026.06.25 JSON 기반 메시지 타입 설계 (0) 2026.06.23 왜 메시지 규칙이 필요한가 (0) 2026.06.23 전체 채팅 브로드캐스팅 (0) 2026.06.23 접속자 목록 관리 (0) 2026.06.23 다음글이전글이전 글이 없습니다.댓글