프로그램개발/ServerSide(PHP,Node.js)

Node.js, PHP/JSP/ASP 및 Apache/Nginx에 대한 비교

크레도스 2017. 7. 13. 11:25

출처 : https://okky.kr/article/403922


Node.js, PHP, JSP 등 흔히 쓰시는 언어나 서버 스택에 대해서 좀 더 넓은 시야에서 비교를 해보고자 합니다.


Node.js 외에 Python, Ruby 또 Apache, Nginx 및 PHP, JSP, ASP 등 다른 플랫폼들과 그 차이에 대해서 알아봅니다. 그리고 웹 서버를 구성하는 다양한 기술 스택에 대해서 소개합니다.





Node.js
Thread Pool

Thread Pool

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

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

비동기와 쓰레드에 대한 이해가 명확하지 않으면 4.8 동기와 비동기, Thread  챕터를 복습하시길 바랍니다.
CPU가 얼마나 쉬길래요?

CPU가 얼마나 쉬길래요?

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

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

골치 아픈 멀티쓰레딩

골치 아픈 멀티쓰레딩

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

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

Node.js I/O 모델

Node.js I/O 모델

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

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

20% 정도 처리가 빠르다?

20% 정도 처리가 빠르다?

그래서 이벤트 루프 기반의 서버 모델이 최고라는 건가? 라고 생각 할 수 있겠습니다. 하지만 위 그래프나, 여러 실험에서 시사하는 바에 따르면 어마어마한 성능 차이를 보이지는 않습니다. 또한 싱글 쓰레드 기반이기에 갖는 단점도 존재하며, 메인 쓰레드나 콜백에서 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 가 있습니다. 두 언어 모두 객체지향, 함수형 언어의 특성을 지니고 있는 비슷하면서도 대비되는 특성을 가진 스크립트 언어이며, 웹 서버 개발에 두루 이용됩니다.

Ruby on Rails

Ruby on Rails

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

표현력있는 Ruby 코드
require 'active_support/all'
new_time = 1.month.from_now
Python Django

Python Django

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

명시적인 Python 코드
from datetime import datetime
from dateutil.relativedelta import relativedelta
new_time = datetime.now() + relativedelta(months=1)

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

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

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

Apache, Nginx
Apache Web Server

Apache 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 스크립트를 해석하고 있습니다.

이처럼 Apache는 여러 웹 서비스들의 컨테이너 역할을 하는 웹 서버로 구성 할 수 있습니다.

Nginx

Nginx

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

웹 서버 점유율

웹 서버 점유율

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

IIS GUI 콘솔

IIS GUI 콘솔

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

PHP, JSP, ASP

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

Script Languages

Script 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 등의 웹 서버에 결합된 모듈이 http://example.org/file.php  같은 요청을 받으면 실시간으로 SOME_PATH/file.php를 해석하여 응답하게 됩니다.

MVC 패턴

MVC 패턴

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

인터넷의 웹 서버 플랫폼 점유율

인터넷의 웹 서버 플랫폼 점유율

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

다양한 기술 스택들
이름구성
LAMPLinux (or Windows) + Apache (or Nginx) + MySQL (or Any DBMS) + PHP
ASP.NET IIS (or Apache, Nginx) + ASP.NET 
JSPApache (or Nginx) + Tomcat (or JBoss) + JSP
MEANMongoDB + Express.js + Angular.js + Node.js
RoRRuby + Rails
DjangoPython + Django

웹 백엔드 스택

LAMP Stack

LAMP Stack

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

ASP.NET

ASP.NET 

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

JAVA Web Server Frameworks

JAVA Web Server Frameworks

JAVA의 견고한 객체지향과 라이브러리들을 기반으로, JSP에 Spring 과 같은 프레임워크를 도입하거나, 혹은 JVM 위에서 작동하는 Scala 기반의 Play  프레임워크를 도입해 볼 수 도 있겠습니다.

Django vs Rails

Django vs Rails

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

MEAN Stack

MEAN Stack

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

Node.js Web Server Framework

Node.js Web Server Framework

Node.js 생태계에도 방대한 기능을 제공하는 Sails.js LoopBack Meteor  등의 훌륭한 웹 서버 프레임워크 및 다양한 편의 도구들을 포함한 패키지들이 있습니다.

위 내용은 모두 웹 서버에 대한 소개입니다. 이 점에 유의하시길 바랍니다.