프로그래밍 개발 공부

[개발 실무] 서버는 요청을 처리하지 않는다 — 요청 처리 구조(스레드/이벤트 기반 서버 모델 이해)

wikys 2026. 3. 26. 12:52
1. 도입 : "서버가 요청을 처리한다"는 흔한 오해
웹 개발을 시작하면 가장 먼저 마주하는 문장이 "클라이언트가 요청하면 서버가 처리하여 응답한다"입니다. 하지만 시스템의 깊은 곳을 들여다보면 실제로 서버 시스템 자체가 직접 요청을 처리하는 것은 아닙니다. 서버는 단지 클라이언트의 요청을 받아서 '처리할 구조'에 넘기는 중계자 역할을 할 뿐입니다.
 
이 글에서는 서버의 실제 동작 원리와 대표적인 요청 처리 구조인 스레드(Thread) 기반이벤트(Event) 기반 서버 모델을 비교 분석하여 최적의 서버 아키텍처를 선택하는 기준을 제시합니다.
 

--------------------------------------------------------------------------------

2. 서버의 진짜 역할 : 대표와 직원 비유
서버의 역할을 회사에 비유해 보겠습니다. 회사의 대표(서버)는 외부에서 들어오는 업무(요청)를 직접 처리하지 않고 직원(처리 단위)에게 분배합니다. 콜센터의 대표 번호가 전화를 받아 빈 상담원에게 연결해 주는 것과 같습니다.
 
클라이언트가 요청을 보내면 서버는 이를 받아 스레드(Thread)나 이벤트 루프(Event Loop)라는 실제 작업자에게 넘기고, 작업이 완료되면 그 결과를 응답으로 반환하는 흐름을 가집니다. 그렇다면 이 실제 '작업자'들의 처리 방식은 어떻게 구성되어 있을까요?
 

--------------------------------------------------------------------------------

3. 스레드(Thread) 기반 서버 모델 : 1요청 1작업자
스레드 기반 모델은 요청이 들어올 때마다 하나의 스레드(작업자)를 할당하는 가장 직관적인 방식입니다. 과거부터 웹 서버의 표준으로 불린 Apache(아파치) 서버의 Prefork/Worker 모델이 대표적입니다.
  • 특징 및 장점 : 주문 하나당 전담 요리사 한 명을 배정하는 것과 같습니다. 각 요청이 독립된 스레드에서 실행되므로 구조가 직관적이고 구현이 쉽습니다. 특히 복잡한 데이터 연산이 필요한 CPU 중심 작업에 유리합니다.
  • 치명적인 단점 (C10K 문제) : 스레드는 결코 공짜가 아닙니다. 클라이언트 동시 접속이 1만 명으로 늘어나면 스레드도 1만 개가 필요해지며(C10K 문제), 이로 인해 스택 메모리 사용량이 폭증하게 됩니다. 또한, 한정된 CPU 코어에서 수많은 스레드를 번갈아 실행하기 위해 컨텍스트 스위칭(Context Switching) 비용이 막대하게 발생하여 시스템 성능이 급격히 저하됩니다. CPU 캐시 오염(Cache Pollution) 및 TLB 초기화와 같은 하드웨어 레벨의 오버헤드가 발생해 서버를 마비시킬 수 있습니다.
 

--------------------------------------------------------------------------------

4. 이벤트(Event) 기반 서버 모델 : 소수 정예의 순환 처리
스레드 기반의 무거운 한계와 C10K 문제를 극복하기 위해 등장한 것이 바로 이벤트(Event) 기반 서버 모델입니다. Nginx나 Node.js가 이 방식을 사용하여 대용량 트래픽을 처리합니다.
  • 개념 및 특징 : 스레드를 무한정 만들지 않고, 하나 또는 소수의 스레드(이벤트 루프)가 여러 요청을 순환하며 처리합니다. 한 명의 뛰어난 요리사가 물이 끓는 시간을 낭비하지 않고 그사이에 다른 요리를 진행하는 것과 같습니다.
  • 기다림(I/O)의 최적화 : 파일 읽기나 외부 DB 조회처럼 '기다리는 시간(Non-blocking I/O)'에 스레드를 멈춰두지 않고 즉시 다른 요청을 처리합니다. 매우 적은 리소스로도 수만 개의 커넥션을 동시에 유지할 수 있어 고속 처리가 가능합니다.
  • 단점 : 다수의 요청을 비동기로 처리하는 흐름 관리가 복잡하여 코드 이해가 어려울 수 있으며, 단일 흐름 내에서 지나치게 무거운 CPU 연산이 실행될 경우 전체 이벤트 루프가 멈춰 다른 요청까지 지연될 수 있습니다.
 

--------------------------------------------------------------------------------

5. 핵심 비교 : 기다리는 시간을 어떻게 처리할 것인가?
결국 두 모델의 가장 큰 차이는 "기다리는 시간(I/O 대기)을 어떻게 처리하느냐"에 있습니다.
  • 스레드 기반 : I/O 작업 시 스레드가 멈춰서 대기(Blocking)하며, 동시 처리를 위해 사람(스레드)을 계속 추가합니다. CPU 연산 중심 작업에 적합합니다.
  • 이벤트 기반 : 대기 시간을 잘게 쪼개서 100% 활용하며, 멈춤 없이(Non-blocking) 들어오는 이벤트를 순환 처리합니다. 대규모 네트워크 통신 등 I/O 중심 작업에 고도의 확장성을 발휘합니다.
 

--------------------------------------------------------------------------------

6. 입문자가 흔히 하는 5가지 오해
서버 구조를 설계할 때 초보 개발자들이 가장 자주 빠지는 착각은 다음과 같습니다.
  1. 서버 메인 프로세스 자체가 직접 요청을 처음부터 끝까지 처리한다고 생각한다.
  2. 요청 1개는 반드시 1개의 독립적인 실행 흐름(스레드)과 일치한다고 여긴다.
  3. 동시 처리를 위해 스레드를 많이 생성할수록 무조건 서버가 빠르다고 착각한다 (컨텍스트 스위칭 비용 간과).
  4. 이벤트 기반 모델을 여러 작업이 '물리적으로 동시에(병렬로) 실행'되는 것으로 오해한다.
  5. 성능 최적화에서 연산 속도보다 "기다리는 시간(I/O)"의 처리 방식이 얼마나 중요한지 놓친다.
 

--------------------------------------------------------------------------------

7. 결론 : 서버 아키텍처 관점의 전환
서버는 단순한 연산 기계가 아닙니다. 요청을 직접 처리하는 존재가 아니라, 요청을 적절한 방식(스레드 또는 이벤트 흐름)으로 처리하도록 분배하는 거대한 시스템입니다. 이 핵심 개념을 명확히 이해하면 단순한 작업자(코더)를 넘어 시스템 설계자의 시점을 갖출 수 있습니다.
 
Node.js가 왜 싱글 스레드임에도 불구하고 비동기 작업에 강력한지, Nginx가 어떻게 수만 명의 동시 접속자를 가볍게 처리하는지 그 해답은 바로 이 이벤트 기반 아키텍처 차이에 있습니다. 구현하려는 서비스의 특성이 CPU 연산 위주인지, I/O 통신 작업 위주인지 파악하고 그에 맞는 서버 모델을 선택하는 것이 성공적인 백엔드 시스템 설계의 첫걸음입니다.
 
반응형