서버 플랫폼

쓰레드 풀 기반 서버, 이벤트 기반 서버 모델의 구조적 차이를 알아봅니다. Node.js 외에 Python, Ruby 또 Apache, Nginx 및 PHP, JSP, ASP 등 다른 플랫폼들과 그 차이에 대해서 알아봅니다. 그리고 웹 서버를 구성하는 다양한 기술 스택에 대해서 소개합니다.
서버 플랫폼, 백엔드, 웹 서버 스택, 쓰레드 풀, 이벤트 기반 서버


Node.js

쓰레드 풀쓰레드 풀

쓰레드 풀(Thread Pool) 기반 서버 모델

전통적인 소켓 서버 프로그램(웹 서버가 아니더라도)의 구조는 각 요청에 대해서 응답을 처리 할 때 멀티 쓰레딩을 구현하는 쓰레드 풀(Thread Pool) 기반 모델을 취하고 있습니다. 이는 일반적으로 OS의 파일 및 네트워킹 I/O API가 오랜 시간이 걸리는 작업이며 CPU를 많이 쉬게하는 Blocking API인데서 출발한 자연스러운 구조입니다.

쓰레드 풀 기반 모델의 핵심은 CPU가 쉬는 시간이 없도록 최대한 착취하는 것입니다. 즉, 파일이나 네트워킹 I/O에서 CPU가 하드웨어의 응답을 기다리는 유휴 시간에도 CPU를 최대한 착취 할 수 있도록 여러 쓰레드를 운영하는 것입니다.

메모리 엑세스와 네트워킹의 차이는 백만배메모리 엑세스와 네트워킹의 차이는 백만배

쓰레드 풀 기반의 서버에서는 대기열(Queue)를 두고, 도착하는 요청들을 대기열에 저장 해둔 후, 여유가 생기면 저장된 순서대로, 미리 생성해둔 쓰레드에 요청을 할당합니다.

이때 쓰레드를 요청마다 매번 생성하고 소멸시키는 것보다 적정 수의 쓰레드를 만들어 두고 재활용하는 것이 효율적이기 때문에 쓰레드 풀을 이용해 멀티쓰레딩을 구현합니다. 이렇게 멀티쓰레딩을 통해서 서버는 최대한 많은 요청을 동시에 처리 할 수 있습니다.

멀티쓰레딩시 공유 메모리 문제멀티쓰레딩시 공유 메모리 문제

이런 훌륭한 쓰레드 풀 기반의 서버 모델을 비관적으로 생각해본다면, 쓰레드 생성 및 전환에 필요한 자원이 적지 않으며, Blocking I/O를 수행 중인 쓰레드는 (더 착취해야 하는데) 결국엔 대기 상태에 놓일 수 밖에 없다는 점입니다.

그리고 무엇보다도 쓰레드 간에 공유되는 데이터(메모리)의 접근 순서를 보장하거나, 각 쓰레드간의 데이터를 동기화하는 등에서 야기되는 동시성에 관한 문제는 코딩 난이도 및 구조를 극적으로 복잡하게 만듭니다.

이벤트 기반 서버 모델

여기서 OS에서 제공하는 비동기 I/O API를 사용한다면, 이벤트 기반의 새로운 프로그래밍 모델을 생각해볼 수 있습니다. Node.js는 I/O 작업시 단일한 메인 쓰레드에서 OS의 비동기 I/O API를 호출한 뒤, 이벤트 시스템을 통해서 API의 실행 결과를 콜백으로 처리합니다. (OS에서 비동기 API를 제공하지 않는 경우엔 내부적으로 쓰레드 풀을 이용합니다.)

Node.js I/O 모델Node.js I/O 모델

Node.js는 위 구조를 통해 유휴 쓰레드에 낭비되는 자원을 줄이며, 서버의 효율성을 높힐 수 있습니다. 또한 복잡한 멀티쓰레딩을 코드로 구현 할 필요가 없기에 C/C++, Java 등으로 구현되는 전통적인 멀티 쓰레딩 서버 모델에 비해서 진입 장벽이 낮으며, 높은 생산성을 가질 수 있습니다.

20% 정도 향상된 Throughput20% 정도 향상된 Throughput

그래서 이벤트 루프 기반의 서버 모델이 최고? 라고 생각 할 수 있겠습니다. 하지만 위 그래프나, 여러 실험에서 시사하는 바에 따르면 어마어마한 성능 차이를 보이지는 않습니다. 또한 싱글 쓰레드 기반이기에 갖는 단점도 존재하며, 메인 쓰레드나 콜백에서 CPU 점유가 높은 경우엔 쓰레드 풀 모델에 비해 나쁜 성능을 낼 수도 있겠습니다.

Node.js 플랫폼의 주요한 장점을 다시 정리하자면, 코드 상에서 쓰레드를 직접 제어하지 않고도 높은 효율의 네트워크 어플리케이션을 작성 할 수 있으며, JavaScript 생태계와 상부 상조하며 거대한 오픈소스 생태계를 꾸려가고 있다는 점입니다.

비동기 코드의 패턴

추가로 비동기 코드에서 자주 볼 수 있는 패턴을 한번 보겠습니다.

Callback Hell

step1(function (value1) {
  // Use value1
  step2(function (value2) {
    // Use value2
    step3(function (value3) {
      // Use value3
      step4(function (value4) {
        // Use value4
        step5(function (value5) {
          // Use value5
        });
      });
    });
  });
});

콜백으로 이어지는 비동기 방식의 코드는 본질적으로 위와 같이 가독성이 떨어지는 문제를 야기 할 수 밖에 없습니다. 하지만 이러한 문제는 비단 Node.js만의 문제는 아니며, 비동기 방식의 프로그래밍을 지원하는 Java, C/C++, C#, Python 등 어느 플랫폼에서든 비동기 코드를 사용 할 때 직면하게 되는 문제입니다.

JavaScript에서는 ES6의 Promise, 나아가 ES7의 await/async를 통해서 코드의 가독성과 생산성을 높힐 수 있습니다. 다른 플랫폼에도 Promise와 비슷하게 Future, Task 등 상응하는 객체들이 존재하며, async/await 문법은 비동기 프로그래밍을 지원하는 여러 최신 언어에서 지원하고 있습니다.

async/await

async function(request, response) {
  try {
    let user = await User.get(request.user);
    let notebook = await Notebook.get(user.notebook);
    response.send(await doSomethingAsync(user, notebook));
  } catch(err) {
    response.send(err);
  }
}

Python, Ruby

웹 백엔드를 작성 할 때 Node.js와 자주 비교되는 플랫폼으로 Python과 Ruby가 있습니다. 두 언어 모두 객체지향, 함수형 언어의 특성을 지니고 있는 비한 특성을 가진 스크립트 언어이며, 웹 서버 개발에 두루 이용됩니다.

Python

Python DjangoPython Django

Python은 웹과 AI 및 데이터 과학 분야에서 풍부한 생태계를 갖추고 있는 언어입니다. 웹 서버 프레임워크로는 Django; 장고가 있습니다.

명시적인 Python 코드

from datetime import datetime
from dateutil.relativedelta import relativedelta
new_time = datetime.now() + relativedelta(months=1)

Ruby

Ruby on RailsRuby on Rails

Ruby는 간결함과 생산성을 강조한 기교가 뛰어난 언어입니다. 웹 서버 프레임워크로는 Ruby on Rails; RoR가 있습니다.

표현력있는 Ruby 코드

require 'active_support/all'
new_time = 1.month.from_now

두 언어 모두 Node.js와 마찬가지로 웹 개발에 많이 사용되며, OS 위에서 작동하는 스크립트 언어입니다. 두 플랫폼 모두 방대한 코어 모듈을 갖고 있으며, 독자적인 생태계와 커뮤니티를 갖추고 있습니다. 두 플랫폼 중 무엇이 좋고 나쁘다라고 구분하기보단, 그 플랫폼의 생태계에 따라 적절한 쓰임새를 판단하시길 바랍니다.

지금까지 Node.js로 웹 서버를 작성하는 데 Express.js를 이용했습니다만(1MB 미만의 초경량 프레임워크입니다.), Rails나 Django가 제공하는 완성도있는 구조와 고수준으로 추상화된 API들을 보면, 상당히 많은 고생을 하면서 웹 서버를 작성해 온 셈입니다.

추가로 비동기 I/O가 빈번한 네트워크 어플리케이션의 관점에서 생각해보면, Node.js의 이벤트 기반 비동기 I/O 모델이 생산성과 효율성에서 Python이나 Ruby보다 강점을 가질 수 있겠습니다.

Apache, Nginx

Apache

Apache Web ServerApache Web Server

Apache httpd는 아파치 재단에서 만든 오픈소스 웹 서버를 말합니다. 흔히 apache 또는 httpd로 불리기도 합니다.

Node.js 등으로 작성한 웹 서버처럼 특정 비지니스 로직에 따라 작동하는 단일한 프로그램과는 다르게, 기본적으로 정적 파일 서버 역할을 수행하면서, 다양한 모듈을 설정하며 다용도로 운영 할 수 있는 웹 서버 데몬(머신에서 항상 실행되는 백그라운드 프로세스)이라고 생각 할 수 있겠습니다.

아파치가 제공하는 몇가지 모듈을 소개하면 다음과 같습니다.

  • mod_rewrite
    요청 URL의 패턴을 매칭해 리디렉션 등의 응답을 수행
  • mod_proxy
    인트라넷에 프록시 서버를 구성하거나, 접속 도메인이나 포트에 따라서 다른 호스트나 프로세스로의 연결을 중개 (리버스 프록시)
  • mod_userdir
    http://example.com/~USER/ 하위의 요청을 /home/USER/public_html/을 기반으로 정적으로 응답
  • mod_cache
    응답 바디를 메모리에 캐싱하여 응답 속도를 높히거나, 응답 헤더에 자동으로 캐시 정책을 추가하여 트래픽을 절약
  • mod_deflate
    응답 바디를 압축하여 트래픽을 절약
  • mod_ssl
    HTTPS 설정

위 외에도 인터넷에 수 많은 아파치 모듈들이 오픈소스로 제공되고 있습니다. 이런 모듈들을 이용하면 설정파일을 편집하는 것만으로 다양한 위치의 정적 파일들을 제공하는 웹 서버를 구성하거나, 네트워크간의 프록시 연결이나 SSL 설정 등을 손쉽게 구성 할 수 있습니다. 이 때문에 아파치는 웹 서버, 프록시/중계 및 로드 밸런싱 등 다양한 쓰임새를 가집니다.

예를 들어, 이 워크샵 웹 서비스도 Node.js 프로세스(8443 포트에서 Listening)를 동일한 호스트에서 작동중인 Apache(80 포트 및 443 포트에서 Listening)를 통해서 workshop.benzen.io:433 으로 연결하고 있습니다. 또한 workshop.benzen.io:80에 대한 요청을 workshop.benzen.io:433으로 리디렉션합니다. 또한 kim.benzen.io로 들어오는 요청에는 mod_php 모듈을 통해 특정 경로의 PHP 스크립트를 해석하고 있습니다.

codeflow 구 버전에 대한 얘기입니다.

이처럼 Apache는 여러 웹 서비스들의 창구 역할을 하는 웹 서버로 구성 할 수 있습니다. 이와 같이 특정 프로그램/스크립트의 창구 역할로 구동되는 웹 서버를 WAS (Web Application Server), 또 이러한 방식의 인터페이스를 CGI (Common Gate Interface)라고 합니다.

Nginx

NGINXNGINX

Nginx 역시 Apache와 비슷한 역할을 수행하는 오픈소스 웹 서버입니다. 마찬가지로 다양한 모듈을 설정하여 웹 서버를 구성하게됩니다.

웹서버 점유율웹서버 점유율

Nginx는 Apache보다 뒤늦게 등장한 만큼 아직 점유율이 떨어지지만 지속적으로 Apache의 점유율을 따라잡고 있습니다. Apache와의 주요한 차이는 Nginx가 Node.js처럼 이벤트 기반으로 설계되었다는 점입니다. 이 때문에 쓰레드 풀 기반의 Apache보다 (특정 부분에서) 높은 성능을 보입니다.

IIS

IIS GUI 콘솔IIS GUI 콘솔

이외에도 MS에서 제공하는 Windows용 웹 서버인 IIS 역시 비슷한 역할을 하는 웹 서버입니다.

PHP, JSP, ASP

위에서 다룬 웹 서버들은 Node.js 등으로 작성한 소켓 프로그램에 연결 할 수도 있지만, 대표적으로 PHP, JSP, ASP라는 웹 프로그래밍 언어에 연결됩니다.

Script LanguagesScript Languages

PHP

<html>
   <head>
      <title>Online PHP-7 Script Execution</title>      
   </head>
   
   <body>
      
      <?php
         echo "<h1>Hello, PHP-7!</h1>";
      ?>
   
   </body>
</html>

JSP

<html>
   <head><title>Hello World</title></head>
   
   <body>
      Hello World!<br/>
      <%
         out.println("Your IP address is " + request.getRemoteAddr());
      %>
   </body>
</html>

ASP

<% @Page Language="C#" %>

<script runat="server">
private void convertoupper(object sender, EventArgs e)
{
  string str = mytext.Value;
  changed_text.InnerHtml = str.ToUpper();
}
</script>

<html>
   <head> 
      <title> Change to Upper Case </title> 
   </head>
   
   <body>
      <h3> Conversion to Upper Case </h3>
      
      <form runat="server">
         <input runat="server" id="mytext" type="text" />
         <input runat="server" id="button1" type="submit" value="Enter..." OnServerClick="convertoupper"/>
         
         <hr />
         <h3> Results: </h3>
         <span runat="server" id="changed_text" />
      </form>
      
   </body>
   
</html>

위 예시 코드들처럼 PHP, JSP, ASP는 모두 템플릿 방식의 스크립트 언어(컴파일도 가능)입니다. 웹 생태계가 발전하면서 동적인 웹이 등장하던 시기에 템플릿 방식으로 인기를 끌며 발전해온 역사 깊은 언어들입니다.

Apache, Nginx, IIS, Tomcat 등의 WAS에 결합된 모듈이 http://example.org/file.php 같은 요청을 받으면 실시간으로 SOME_PATH/file.php를 해석하여 응답하게 됩니다.

Laravel의 MVC 패턴Laravel의 MVC 패턴

이후 디자인 패턴과 언어가 발전하면서 뷰와 데이터 및 라우팅 등의 로직이 모두 함께 뒤섞인 구조에서 탈피하며, 모듈화 및 MVC 등의 디자인 패턴이 강조되어, 이제는 현대의 웹 환경에 걸맞는 견고하고 거대한 웹 어플리케이션을 작성하기에 훌륭한 플랫폼들입니다.

웹 서버 플랫폼 점유율웹 서버 플랫폼 점유율

인터넷에 존재하는 수 많은 웹 사이트의 대부분이 위 세가지 플랫폼 위에 작성되었다고 봐도 좋겠습니다. Node.js나 Python 등의 OS 위에서 구현되는 언어로 작성하는 standalone 웹 서버와 비교해보면 PHP, JSP, ASP로 작성되는 웹 서버에서 네트워킹은 고도로 추상화되어있으며, 비지니스 로직과 뷰(템플릿)를 작성하는 데 초점이 맞추어져 있습니다. 이 때문에 로우 레벨의 언어들에 비해 손이 덜가며, 진입 장벽이 낮고, 풍부한 생태계를 갖추고 있습니다. (반대로 확장성이 떨어진다고 볼 수도 있겠습니다.)

다양한 기술 스택들

이름구성
LAMPLinux (or Windows) + Apache (or Nginx) + MySQL (or Any DBMS) + PHP
ASP.NETIIS (or Apache, Nginx) + ASP.NET
JSPApache (or Nginx) + Tomcat (or JBoss) + JSP
MEANMongoDB(?) + Express.js(?) + Angular.js(?) + Node.js
RoRRuby + Rails
DjangoPython + Django

LAMP

LAMP StackLAMP Stack

LAMP 스택을 이용하면, 오픈 소스를 기반으로 저렴하게 서버 환경을 구성하고, Laravel 같은 생산성 높은 PHP 웹 프레임워크를 도입 할 수 있겠습니다.

ASP.NET

ASP.NETASP.NET

.NET 플랫폼에 익숙하며 Windows 환경에서 웹 서버를 구성해야 한다면, 막강한 IDE(Visual Studio)와 견고한 MVC 프레임워크를 제공하는 ASP.NET 으로 웹 서버를 작성 할 수 있겠습니다.

JAVA, Scala

JAVA Web Server FrameworksJAVA Web Server Frameworks

JAVA의 견고한 객체지향과 라이브러리들을 기반으로, JSP에 Spring 과 같은 프레임워크를 도입하거나, 혹은 복잡한 분산 서버 시스템을 구축해야한다면 Scala 기반의 Play 프레임워크를 도입해 볼 수 도 있겠습니다.

Django, Rails

Django vs RailsDjango vs Rails

DjangoRails 같은 준비된 웹 서버 프레임워크를 이용해 빠르게 고성능의 standalone 서버를 작성 할 수도 있습니다.

MEAN

MEAN StackMEAN Stack

또는 MEAN이라 이름 붙은, Node.js 기반의 풀 스택을 도입 할 수도 있겠습니다. 물론 Node.js를 쓴다고 꼭 Express.js 같은 경량 웹 프레임워크나 MongoDB 류의 NoSQL, 또 클라이언트에 Angular.js와 같은 SPA 프레임워크를 도입 할 필요는 없습니다.

Node.js

Node.js Web Server FrameworkNode.js Web Server Framework

Node.js 생태계에도 Express.js 같은 마이크로 프레임워크 외에 방대한 기능을 제공하는 Sails.js , LoopBack, Meteor 등의 완성형 웹 서버 프레임워크 및 다양한 편의 도구들을 포함한 패키지들이 있습니다.

목차
7. 다른 플랫폼으로
저자

김동욱

개발 경력 약 10년, 전문 분야는 웹 및 각종 응용 소프트웨어 플랫폼입니다. Codeflow를 운영하고 있습니다.

2018년 04월 09일 업데이트

지원되지 않는 웹 브라우저거나 예기치 않은 오류가 발생했습니다.