9강. 여러 클라이언트 접속 구조

0. 학습 목표
→ 이번 글에서 무엇을 이해하고, 무엇을 정리하고, 무엇을 확인할지 먼저 정리합니다.
0.1 이번 글에서 다룰 내용
이번 글은 개념 중심 강의입니다.
8강까지 1:1 채팅 서버와 클라이언트를 완성했습니다. 서버가 실행되고, 클라이언트가 접속해 메시지를 주고받는 흐름을 직접 만들었습니다. 그런데 여기서 중요한 질문이 생깁니다.
"사용자가 두 명이면 어떻게 되지?"
현재 서버에 A가 접속해 대화하는 중에 B가 접속을 시도하면,
서버는 A와의 대화를 처리하느라 B를 바로 받기 어렵습니다.
이번 강의에서는 코드를 새로 작성하는 대신 이 질문을 먼저 이해합니다.
- 왜 현재 서버는 한 명만 처리할까?
- 여러 클라이언트를 처리하려면 어떤 구조가 필요할까?
- 왜 접속자 목록과 스레드가 필요할까?
이번 강의에서 이해하는 다중 접속 구조는 최종 채팅 서버의 핵심 뼈대입니다.
chat_server/server.py에서 여러 클라이언트를 동시에 처리하고,
client_manager.py에서 접속자 목록을 관리하는 구조가 모두 이번 강의 개념에서 출발합니다.
지금 모든 내용을 외울 필요는 없습니다. "왜 구조를 바꿔야 하는가"를 먼저 파악하는 것이 목표입니다.

| 구분 | 내용 |
| 이해할 것 | 현재 1:1 서버의 한계와 다중 접속 서버에 필요한 구조 |
| 정리할 것 | 여러 클라이언트를 처리하려면 접속자 목록과 독립적인 처리 흐름(스레드)이 함께 필요하다는 점 |
| 확인할 것 | 왜 스레드가 필요한지, clients 목록과 스레드의 역할 차이를 말로 설명할 수 있는지 |
0.2 앞으로 만들 프로젝트 구조 미리보기
이번 강의에서는 새 파일을 만들거나 기존 파일을 수정하지 않습니다. 8강까지 완성한 구조는 다음과 같습니다.
chat_server/
└── server.py ← 8강까지 완성한 1:1 서버
chat_client/
└── client.py ← 8강까지 완성한 1:1 클라이언트
이번 강의에서 이해하는 개념을 바탕으로 앞으로 만들 구조는 다음처럼 확장됩니다.
chat_server/
├── server.py ← 11강에서 스레드 구조로 수정
└── client_manager.py ← 12강에서 생성 예정 (접속자 목록 관리)
chat_client/
└── client.py ← 이후 강의에서 수정 예정
1. 현재 서버가 한 명만 처리하는 이유 이해하기
→ 8강까지 만든 서버 구조에서 왜 한 명밖에 처리하지 못하는지 코드 흐름으로 확인합니다.
1.1 현재 서버 흐름 확인하기
8강의 서버 코드에는 다음과 같은 흐름이 있었습니다.
client_socket, client_address = server_socket.accept()
while True:
data = client_socket.recv(1024)
# ...클라이언트와 메시지 주고받기
accept()는 클라이언트 접속을 받아들이는 함수입니다.
그런데 accept() 이후 서버는 바로 while True 반복문에 들어가 그 클라이언트와 메시지를 주고받습니다.
서버가 A와 대화하는 반복문 안에 있는 동안은 다시 accept()로 돌아오지 않습니다.

클라이언트 A 접속
↓
accept() → A를 받음
↓
while True: A와 메시지 주고받기 ← 여기에 갇힘
↓
A가 종료 → 반복문 탈출
이 상태에서 B가 접속을 시도하면 서버는 B를 바로 처리하기 어렵습니다.
이것이 순차 처리입니다.
한 작업이 끝난 뒤에야 다음 작업을 처리하는 방식으로, 1:1 채팅을 배우기에는 충분했지만 여러 사람이 동시에 대화하는 채팅방을 만들기에는 부족합니다.
접속이 안 된다고 포트 번호나 코드를 바꿔봐도 해결이 안 되는 이유가 바로 이것입니다. 구조 자체를 바꿔야 합니다.
1.2 현재 구조와 앞으로 만들 구조 비교
| 항목 | 현재 구조 (8강까지) | 앞으로 만들 구조 |
| 접속 가능 인원 | 1명 | 여러 명 |
| 처리 방식 | 순차 처리 | 동시 처리 |
| 클라이언트 목록 | 없음 | clients 리스트 |
| 스레드 사용 | 없음 | 클라이언트마다 1개 |
| 전체 채팅 구현 | 어려움 | 가능 |
현재 구조는 네트워크 프로그래밍 기초를 배우기에 좋은 출발점이었습니다. 이제 그 위에 다중 접속 구조를 더하는 단계입니다.
✔ 확인 기준: 현재 서버가 accept() 이후 while True 안에 들어가는 동안 다른 클라이언트를 바로 처리하지 못한다고 설명할 수 있으면 완료. "accept()를 한 번 실행하면 여러 클라이언트를 동시에 받을 수 있다"고 오해하고 있지 않은지 확인하세요.
2. 다중 접속에 필요한 두 가지 구조 이해하기
→ 접속자 목록(clients 리스트)과 독립적인 처리 흐름(스레드)이 각각 왜 필요한지 이해합니다.
2.1 접속자를 기억하는 구조 — clients 리스트
여러 명을 처리하려면 먼저 접속한 클라이언트들을 기억해야 합니다. 가장 단순한 방법은 리스트입니다.
clients = []
clients.append(client_socket) # 접속할 때마다 추가
클라이언트가 접속할 때마다 리스트에 추가하면 다음처럼 됩니다.
A 접속 → clients = [A]
B 접속 → clients = [A, B]
C 접속 → clients = [A, B, C]
이 목록은 나중에 "A가 메시지를 보내면 B, C에게도 전달하기" 같은 전체 채팅 기능을 만들 때 꼭 필요합니다.
그러나 중요한 점이 있습니다. 목록만 만든다고 동시 처리가 되는 것은 아닙니다. 목록은 접속한 클라이언트를 기억하는 구조이고, 각 클라이언트를 동시에 처리하려면 별도의 처리 흐름이 추가로 필요합니다.
2.2 독립적인 처리 흐름 — 스레드

스레드는 하나의 프로그램 안에서 여러 작업 흐름을 만들 수 있게 해 주는 방법입니다.
현재 서버는 하나의 흐름으로만 움직입니다. 스레드를 사용하면 클라이언트마다 독립적인 처리 흐름을 만들 수 있습니다.
스레드 적용 후:
서버 메인 흐름 → 새 접속을 계속 받음
A 스레드 → A의 메시지를 계속 처리
B 스레드 → B의 메시지를 계속 처리
C 스레드 → C의 메시지를 계속 처리
서버 메인 흐름은 새 접속을 받는 일에만 집중하고, 각 클라이언트의 메시지 처리는 해당 클라이언트 전용 스레드에 맡기는 구조입니다.
2.3 clients 목록과 스레드의 역할 구분
두 가지를 혼동하는 경우가 많습니다. 역할이 다릅니다.
| 구분 | clients 목록 |
스레드 |
| 역할 | 접속한 클라이언트를 기억 | 각 클라이언트를 독립적으로 처리 |
| 없으면 | 누가 접속해 있는지 알 수 없음 | 한 번에 하나의 클라이언트만 처리 가능 |
| 이후 쓰임 | 브로드캐스팅 시 전체 목록 순회 | 각 클라이언트 전담 메시지 처리 |
한 문장으로 정리하면 clients 목록은 기억, 스레드는 처리입니다. 두 가지가 함께 있어야 다중 접속 서버가 됩니다.
✔ 확인 기준: "clients 목록은 기억, 스레드는 처리"를 한 문장으로 설명할 수 있으면 완료. "clients = []만 만들면 동시 처리가 된다"고 오해하고 있지 않은지 확인하세요.
3. 카페 비유로 구조 이해하기
→ 순차 처리와 동시 처리의 차이를 카페 카운터 비유로 이해합니다.
3.1 카페 카운터로 비유하기
순차 처리와 동시 처리의 차이를 카페 카운터로 비유하면 이해하기 쉽습니다.
현재 서버의 상황은 직원 한 명이 손님 A의 주문을 받고, 음료를 만들고, 건네주고 나서야 손님 B를 상대하는 카운터와 같습니다. B, C는 A가 끝날 때까지 기다려야 합니다. 앞으로 만들 구조는 손님이 접속하면 전용 직원(스레드)을 배정받는 카페와 같습니다. A, B, C 각각에게 전용 직원이 붙기 때문에 모두 동시에 서비스를 받을 수 있습니다.

| 카페 비유 | 카페에서의 역할 | 서버에서의 역할 |
| 카운터 관리자 | 새 손님을 계속 맞이함 | 서버 메인 흐름 — 새 클라이언트 접속을 계속 받음 |
| 손님 전용 직원 | 특정 손님 주문만 전담 | 클라이언트 처리 스레드 — 특정 클라이언트 메시지를 처리 |
| 손님 대기표 목록 | 현재 카페에 있는 손님을 기억 | clients 리스트 — 현재 접속한 클라이언트 목록 |
| 손님이 자리 떠남 | 대기표 목록에서 제거 | 클라이언트 연결 종료 → 목록에서 제거 |
3.2 실제 서비스에서의 예
다중 접속 구조는 우리가 매일 쓰는 서비스에서 이미 사용되고 있습니다.
| 서비스 | 내부에서 일어나는 일 |
| 카카오톡 서버 | 수백만 명이 동시에 접속해도 각자의 메시지를 독립적인 처리 흐름으로 분리해 처리 |
| 온라인 게임 서버 | 플레이어마다 독립적인 처리 흐름으로 위치·상태를 관리하고 다른 플레이어에게 전달 |
| 웹 서버 (Nginx 등) | 방문자 요청이 들어올 때마다 독립 처리 흐름을 할당해 동시에 응답 |
우리가 만드는 채팅 서버도 같은 원리입니다. 규모는 다르지만 구조의 핵심은 동일합니다.
✔ 확인 기준: 카운터 관리자(서버 메인 흐름)와 손님 전용 직원(클라이언트 스레드)의 역할 차이를 비유로 설명할 수 있으면 완료.
4. 의사코드로 흐름 비교하기
→ 현재 1:1 서버 흐름과 앞으로 만들 다중 접속 서버 흐름을 의사코드로 나란히 비교합니다.
아래 코드는 실제 Python 코드가 아닌, 개념 설명을 위한 의사코드입니다. 지금 이 코드를 외울 필요는 없습니다. "구조가 어떻게 달라지는가"를 보는 것이 목표입니다.
4.1 현재 1:1 서버 흐름
# 현재 구조 의사코드
서버_소켓_생성()
주소_묶기()
접속_대기()
클라이언트_A = 접속_받기() # accept() 한 번 — 이 아래로 내려오지 않음
while True: # A와의 대화 반복문에 갇힘
A의_메시지_받기()
서버_답장_보내기()
accept()가 반복문 밖에 있어 A와 대화를 시작한 뒤로는 다시 accept()로 돌아오지 않습니다. 이 구조에서 B가 접속하려 해도 서버 코드가 recv()를 기다리는 중이라 B의 접속 신호를 받아줄 위치가 없습니다.
4.2 앞으로 만들 다중 접속 서버 흐름

# 다중 접속 서버 의사코드
clients = [] # 접속자 목록
서버_소켓_생성()
주소_묶기()
접속_대기()
while True: # accept()가 반복됨
새_클라이언트 = 접속_받기() # 계속 새 접속을 받음
clients에_추가(새_클라이언트)
새_클라이언트_전용_처리_흐름_시작(새_클라이언트) # 스레드로 분리
서버의 while True는 이제 특정 클라이언트의 메시지 처리에 묶이지 않고, 새 접속을 계속 받는 역할만 합니다. 각 클라이언트 처리는 전용 스레드 함수로 넘어갑니다.
4.3 클라이언트 송수신 처리
# 클라이언트 전용 처리 흐름 (스레드 함수)
def 클라이언트_처리(client_socket):
while True:
메시지_받기()
필요한_대상에게_전달하기()

이렇게 역할이 분리됩니다.
- 서버 메인 흐름 → 새 접속을 받는 역할.
- 각 클라이언트 처리 흐름 → 메시지를 주고받는 역할.
① 새 클라이언트 접속 받기 (accept())
↓
② 접속한 클라이언트를 목록(clients)에 추가하기
↓
③ 그 클라이언트 전용 처리 흐름(스레드) 시작하기
↓
④ 다시 ①로 돌아가 다음 접속 기다리기 ← 서버가 ③에서 멈추지 않고 바로 돌아옴
✔ 확인 기준: 다중 접속 구조에서 accept()가 while True 안에서 반복되고, 클라이언트 처리는 별도 함수(스레드)로 넘어간다고 설명할 수 있으면 완료. "clients 목록만 추가하면 while True가 자동으로 여러 클라이언트를 처리해 준다"고 오해하고 있지 않은지 확인하세요.
5. 다음 강의로 이어지는 부분
→ 이번 강의 핵심을 정리하고, 10강에서 배울 내용을 예고합니다.
이번 강의에서 이해한 핵심을 정리하면 다음과 같습니다.
- 현재 서버는 accept() 이후 while True에 갇혀 한 명만 처리한다
- 여러 명을 처리하려면 clients 목록(기억)과 스레드(처리)가 함께 필요하다
- clients 목록은 접속자를 기억하는 구조이고, 동시 처리는 스레드가 맡는다
- 서버 메인 흐름은 새 접속을 받는 역할만, 메시지 처리는 각 스레드가 맡는다
- 서버가 accept() 이후 멈추지 않고 계속 새 접속을 받는 것이 핵심 변화다
이번 강의에서 스레드를 바로 작성하지 않은 이유가 있습니다. 스레드는 초급자에게 새로운 개념이라 채팅 코드와 동시에 배우면 한꺼번에 너무 많은 내용이 등장합니다. 다음 순서로 단계를 나눕니다.
| 강의 | 내용 |
| 9강 (이번) | 다중 접속 구조가 왜 필요한지 이해 |
| 10강 | threading 모듈로 스레드를 만들고 실행하는 기본 사용법 |
| 11강 | 스레드를 클라이언트 처리 코드에 직접 적용 |
| 12강 | 접속자 목록 관리 (client_manager.py 생성) |
| 13강 | 전체 채팅 브로드캐스팅 구현 |
이번 강의에서 다음 문장이 자연스럽게 느껴진다면 10강 준비가 된 것입니다.
각 클라이언트는 독립적인 처리 흐름(스레드)으로 담당해야 한다.
→ 다음 강의 (10강): threading 모듈로 스레드를 만들고 실행하는 방법을 익힙니다. 스레드 개념과 기본 사용법을 이해하면 11강에서 클라이언트 처리 코드에 바로 적용할 수 있습니다.