프로세스는 각각의 독립된 식당으로 비교할 수 있다. 각 식당(프로세스)는 자신만의 주방, 직원, 재료를 가지고 있어서 완전히 독립적으로 운영된다. 한 식당이 문을 닫아도 다른 식당은 전혀 영향 받지 않는다. 컴퓨터에서도 각 프로그램(카카오톡, 크롬, 게임 등)이 독립된 메모리 공간을 가진 별개의 프로세스로 실행된다.
한 식당 안에서 일하는 여러 직원들이라고 생각하면 편하다. 주방장, 서빙직원, 계산직원이 같은 주방과 재료를 공유하면서 동시에 다른 일을 처리한다. 마찬가지로 프로그램 안에서 여러 스레드가 같은 메모리를 공유하면서 동시에 다른 작업들을 수행한다. 예를 들어 카카오톡에서 메시지 받기, 이모티콘 로딩, 파일 다운로드가 동시에 일어나는 것이라고 생각하면 좋다.
채팅 메시지를 전송할 때 "안녕하세요"라는 메세지를 보낸다고 생각하고 7계층을 비유하자면
7계층 : 카카오톡 앱에서 "안녕하세요" 입력하기 (응용계층)
6계층 : UTF-8로 인코딩, 필요시 암호화 (표현계층)
5계층 : 카카오톡 서버와의 세션 유지 (세션계층)
4계층 : TCP로 신뢰성 있게 전송, 포트 80 사용 (전송계층)
3계층 : 상대방 서버 IP로 라우팅 경로 설정 (네트워크 계층)
2계층 : 이더넷 프레임으로 포장, MAC 주소로 다음 라우터 지정 (데이터링크 계층)
1계층 : 실제 전기신호로 변환하여 케이블/WIFI로 전송 (물리 계층)
전화 통화로 생각하면 편한데 전화를 걸려면 상대방 번호를 알아야 하고 (IP주소), 상대방이 전화를 받아야 하고(포트 열림), 서로 말하고 들을 수 있어야 한다(양방향 통신). 컴퓨터끼리도 소켓이라는 전화기를 통해 IP주소와 포트번호로 연결하여 데이터를 주고 받는다.
먼저 가장 간단한 형태부터 시작했다. 단 하나의 클라이언트만 처리하는 기본 서버를 만들어 소켓 통신의 기초를 익혔다.
// BasicServer.java - 1명만 처리하는 기본 서버
ServerSocket serverSocket = new ServerSocket(12345);
Socket clientSocket = serverSocket.accept(); // 한 명만 받음
이 단계에서는 소켓의 기본 개념을 이해했다:
ServerSocket
: 클라이언트 연결을 기다리는 서버용 소켓accept()
: 클라이언트가 연결될 때까지 대기하는 블로킹 메서드기본 서버의 한계는 명확했다. 한 번에 한 명만 접속할 수 있다는 것! 이를 해결하기 위해 멀티스레드를 도입했다.
// MultiThreadServer.java - 여러 명 동시 처리
while (true) {
Socket clientSocket = serverSocket.accept();
ClientHandler handler = new ClientHandler(clientSocket);
handler.start(); // 새로운 스레드에서 클라이언트 처리
}
각 클라이언트마다 독립된 스레드를 할당하니, 마치 식당에서 손님마다 전담 직원을 배정하는 것과 같은 효과를 얻었다. 이제 동시에 여러 명이 접속할 수 있게 되었다
개별 에코 서버에서 진짜 채팅 서버로 발전시키려면 브로드캐스팅이 필요했다. 한 명이 보낸 메시지를 다른 모든 사람에게 전달하는 기능이다.
// 모든 클라이언트 목록 관리
private static List<ClientHandler> clients = new ArrayList<>();
// 브로드캐스팅 메서드
public static void broadcast(String message, ClientHandler sender) {
for (ClientHandler client : clients) {
if (client != sender) { // 보낸 사람 제외
client.sendMessage(message);
}
}
}
이제 진짜 채팅의 느낌이 나기 시작했다
마지막으로 실용적인 기능들을 추가해서 완성도를 높였다:
NICK:사용자명
프로토콜로 신원 구별CHAT:메시지
형식으로 일관된 통신NICK:Kangsub → 닉네임을 Kangsub로 설정
CHAT:Hello! → 채팅 메시지 전송
SYSTEM:Kangsub님이 입장했습니다 → 시스템 알림
ChatServer (메인 서버)
├── ServerSocket (포트 12345에서 대기)
├── List<ClientHandler> (연결된 클라이언트 관리)
├── broadcast() (메시지 전파 메서드)
└── ClientHandler 스레드들
├── 각 클라이언트마다 독립적 처리
├── 닉네임 설정 및 검증
└── 채팅 메시지 브로드캐스팅
ClientHandler extends Thread
를 구현하면서 멀티스레드의 동작 원리를 체감clients
리스트)에 안전하게 접근하는 방법가장 기억에 남는 건 telnet에서 백스페이스 문제였다. 오타를 수정하려고 백스페이스를 누르면 화면에서는 지워져 보이지만 실제로는 제어문자가 서버로 전송되어 프로토콜 파싱이 실패했다. 이를 해결하기 위해 입력 문자열 정리 로직을 추가했다:
// 제어문자 제거
inputLine = inputLine.replaceAll("[\\r\\n\\x00-\\x1F\\x7F]", "").trim();
하지만 결국 오타없이 쳐야 한다... ㅋㅋㅋㅋ
단순해 보이는 채팅 서버 하나에도 프로세스/스레드, 네트워크, 소켓 통신 등 컴퓨터 공학의 핵심 개념들이 모두 녹아있다는 걸 깨달았다. 이론으로만 배웠던 OSI 7계층이나 TCP/IP가 실제로 어떻게 작동하는지 직접 체험할 수 있어서 정말 의미 있었던 것 같다.