9강 개념 중심 ⏱ 약 25분

 

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강에서 클라이언트 처리 코드에 바로 적용할 수 있습니다.