LESSON5. 웹 애플리케이션의 구성 요소
웹 애플리케이션은 기본적으로 클라이언트와 서버가 HTTP를 통해 통신을 함으로써 구성이 됩니다. 하지만 서버는 한 개가 아니라 논리적으로는 웹 서버, 애플리케이션 서버, 데이터베이스 서버 이렇게 세 종류로 구분됩니다. 물론 그 규모와 특성에 따라 그 개수가 달라지겠지만요. 웹 애플리케이션을 만들기 위해선 이 세 가지 서버를 알아두어야 합니다. 왜냐하면 만약 잘못되면 어디서 잘못되었는지 알 수 있기 때문입니다.
데이터베이스 서버
먼저 데이터베이스 서버에 대해 설명을 할게요. 그 이전에 데이터베이스가 무엇이고 왜 필요한지를 알아야합니다. 데이터베이스는 컴퓨터상에 대량의 정보를 축적해 효율적으로 검색할 수 있게 한 소프트웨어를 말하는데요. 그 필요성에 대해선 이전에 살펴보았던 피자 주문 예제를 통해 살펴봅시다. 피자 주문이 1~2명이면 종이로 된 주문표로 충분하겠지만 만약 수십장이 넘어가면 종이 주문표로 과연 관리가 될까요? 이리저리 움직이다가 종이 한장이라도 잃어버리면 그 손님에게서 컴플레인이 오고 난리통이 될 것입니다. 하지만 데이터베이스란 것이 있으면 여기에 모든 주문을 기록해두고 관리까지 가능합니다.
기본적인 데이터베이스 조작에 관련해선 CRUD로 알아두시면 편합니다. 생성(Create), 읽기(Read), 갱신(Update), 삭제(Delete)의 앞 스펠링을 따서 만든 것인데요. 이 4가지로 대부분의 데이터베이스 정보를 조작할 수 있습니다. 생성을 통해 주문을 입력할 수 있구요. 읽기를 통해 주문했던 기록을 불러올 수 있습니다. 그리고 갱신을 통해서는 주문 기록에서 변경하고 싶은 것들을 바꿀 수 있구요. 삭제는 기록을 없애버릴 수 있는 것입니다. 이러한 데이터베이스의 구현과 처리에 특화된 소프트웨어가 있는데요. 이것을 데이터베이스 관리 시스템(DBMS, Database Management System)이라고 합니다. 실제 이 소프트웨어 내에서 CRUD 명령어를 SQL(Structed Query Language)라는 언어를 통해 전달하는데요. 데이터베이스에 특화된 언어로 이전에 CRUD를 CREATE, SELECT, UPDATE, DELETE와 관련된 여러 SQL명령어를 조합하여 사용합니다. 데이터베이스에 관련된 부분은 하나의 책이 있을만큼 방대하고 실제로 설계하고 사용하는 작업이 만만치는 않습니다. 그래서 데이터베이스에 대한 대략적인 설명은 여기까지 할게요.
자! 이제 다시 데이터베이스 서버로 돌아가서 왜 웹 서버와 분리를 하는 걸까요? 물론 소규모 애플리케이션의 경우 웹 서버 내에 데이터베이스가 들어있을 수도 있습니다. 웹 서버에 데이터베이스를 설치만 하면 되거든요. 하지만 분리를 하는 이유는 데이터베이스가 커졌을 때(정보의 양이 엄청 많이 늘어났을 때) CPU에 부담을 줘 다운되는 사태를 방지하기 위함입니다. 아무래도 DB 서버를 따로 두면 그만큼 처리가 분산되고(웹 서버에서 처리할 것은 웹 서버 컴퓨터에서, DB서버에서 처리할 부분은 DB서버 컴퓨터에서) 그만큼 부하가 줄어들 것입니다.
또 한 가지 이유가 더 있다면 기존 시스템과의 결합 때문입니다. 사실 기업에선 웹이 확산되기 전부터 데이터베이스를 활용해왔는데요. 그렇기 때문에 DB서버를 따로 두어왔습니다. 굳이 웹 서버와 붙이면 큰 장점이 없는데 웹 서버에 붙여서 기존 시스템을 모두 변경해야할 필요가 없었던 것이죠.
웹 애플리케이션과 데이터베이스의 통신의 경우 DB 접속용 라이브러리(자바의 경우 JDBC)가 표준으로 제공되기 때문에 애플리케이션 개발자가 프로토콜을 의식할 필요는 거의 없습니다. 알아서 해주니까요.
애플리케이션 서버
애플리케이션 서버를 설명하기 앞서 이전에 CGI, 서블릿, JSP에 관해 정리를 못한 부분이 있는데요. 이 부분을 간략히 먼저 정리해볼게요.
WWW의 발전과 함께 사용자들은 동적인 콘텐츠에 대한 요구가 생겨났습니다. 기존 HTML의 경우 정적인 컨텐츠다 보니 미리 준비된 컨텐츠 밖에 제공이 안되었거든요. 그렇다고 매번 개발자가 변화에 맞춰 HTML을 갱신할 수도 없구요. 그래서 기존에 웹서버에선 준비된 콘텐츠를 클라이언트의 요청에 응해 보내주는 일만 했었는데 거기에서 더 나아가 웹 서버내에서 작동하는 프로그램을 만들어 사람 대신 콘텐츠를 만들어내도록 생각한 것이죠. 그래서 고안된 것이 CGI(Common Gateway Interface)입니다. CGI에서는 웹 서버가 클라이언트로 받은 요청을 웹 서버상에서 작동하는 프로그램에 보냅니다. 프로그램은 요청을 참조해 HTML을 만든 다음 웹 서버로 다시 돌려보내고, 웹 서버는 HTML을 마치 준비되어 있다는 듯이 웹 애플리케이션에 보내는 것이죠.
하지만 웹 애플리케이션가 요구되는 기능의 규모가 커지고 복잡해짐에 따라 CGI도 문제점에 직면했습니다. 기존 언어가 규모가 크고 복잡한 애플리케이션에 적합하지 않을 뿐더러 CGI를 통한 프로세스 성능이 너무 느린 것이죠1. 그 때쯤 개발된 것이 자바(Java)입니다. 자바는 웹 애플리케이션을 위해 개발된 언어는 아니지만 그 당시 요구에 따라 서블릿(Servlet)이라는 웹 애플리케이션 개발을 지원하는 기능을 제공했습니다. 서블릿을 사용한 웹 애플리케이션은 CGI처럼 새로운 프로세스를 매번 기동할 필요가 없어 고속으로 작동한다는 이점이 있었습니다.
대규모 개발에 적합한 자바에서 동적 콘텐츠 제공을 가능하게 만든 서블릿도 제공되자 개발자 사이에선 또 다른 점이 문제점으로 떠올랐습니다. 그것은 서블릿 내에서 HTML을 만들어서 보내는데 그렇다 보니 서블릿 내에서 사용자 요청을 처리하는 명령어와 HTML이 섞여 문제가 발생한다는 것입니다. 예를들어 디자인을 담당하는 웹 디자이너가 HTML을 수정하려고 서블릿을 수정하다가 다른 서버 사이드 명령어를 건들수도 있고 출력되는 HTML도 다른 명령어랑 섞이다보니 어떤건지 상상이 안간다는 것입니다. 그래서 고안된 것이 JSP(Java Server Pages)입니다. JSP에서는 HTML 코드 내에 동적으로 출력하고 싶은 부분을 <%, %>
로 묶고 그 안에 자바 프로그램을 기술할 수 있습니다. 이를 통해 개발자는 자바 코드 나오는 부분에만 집중할 수 있고, 웹 디자이너는 HTML부분에만 집중해 실수할 여지를 줄이는 것입니다. 서블릿은 자바 코드 속에 HTML을 집어넣었다면, JSP는 HTML 속에 자바 코드를 넣은 것입니다.
다시 돌아와서 웹 서버 내에서 CGI대신 JavaVM을 통해 서블릿/JSP가 실행되고 있는 모습을 살펴보겠습니다.
서블릿과 JSP는 JavaVM위에서 작동됩니다. JavaVM도 웹 서버나 데이터베이스와 같은 하나의 프로세스인데요. 사실 이 안을 자세히 살펴보면 애플리케이션 서버2라고 하는 소프트웨어가 안에서 작동하고 있으며, 이 애플리케이션 서버가 서블릿이나 JSP를 작동시키는 구조입니다. 이렇게 CGI와 같이 하나의 프로세스인데 왜 이것이 더 좋은지 의문점이 들 수 있는데요. CGI의 경우 앞서 말씀드렸듯이 프로세스가 요청에 따라 기동하고 이후에 새로운 요청이 들어오면 다시 기동시켜야 합니다. 하지만 JavaVM내 애플리케이션 서버는 항상 프로세스가 실행되고 있기 때문에 재사용한다는 점에서 차이가 있습니다.
그럼 웹 서버와 애플리케이션 서버가 어떻게 연동을 하는지 궁금하실텐데요. 딱히 표준은 없다고 합니다. 그럼 가장 대표적으로 사용되고 있는 아파치 HTTP서버와 톰캣(Tomcat)의 조합으로 연동 과정을 설명해볼게요.
연동 표준이 딱히 없다보니 톰캣의 경우 아파치용으로 mod_jk라고 하는 연동 모듈을 사용합니다. 연동 과정은 우선 아파치에 도착한 HTTP 요청을 mod_jk가 톰캣으로 전송하고, 톰캣은 웹 애플리케이션으로 전달합니다. 웹 애플리케이션에서 처리를 끝내고 결과를 애플리케이션으로 다시 되돌려준 다음 mod_jk에 결과를 돌려주어, 최종적으로 아파치가 웹 브라우저로 HTTP 응답을 되돌려줍니다. ajp13 프로토콜은 HTTP대신 쓰는 톰캣 고유의 프로토콜로 효율이 더 좋아 사용되고 있습니다.
이렇게 연동을 하게 되면 주로 HTML파일이나 이미지, 동영상 등 정적 콘텐츠는 웹 서버상에 배치하고, 동적 컨텐츠인 웹 애플리케이션은 애플리케이션 서버에서 담당하여 분담을 하는데요. 그렇다보니 HTTP 요청을 모두 웹 서버에서 우선 받아들이고 애플리케이션이 담당하는 것은 따로 넘겨보내야 합니다. 이 구분을 URL을 통해서 합니다.
worker.list = worker1
worker.worker1.host = localhost
worker.worker1.port = 8009
worker.worker1.type=ajp13
위는 아파치와 톰캣 간의 연동 설정인데요. 애플리케이션 서버 톰캣을 worker1
로 정의하고 처리하려는 주체는 localhost
(나)로 설정한 후 프로토콜로 ajp13을 사용하는 것을 볼 수 있습니다.
JkWorkerFile /usr/local/apache2/conf/workers.properties
JkLogFile /usr/local/apache2/logs/mod_jk.log
JkLogLevel info
JKMount /webtext/pentomino/* worker1
또 다른 아파치와 톰캣 간의 연동 설정 파일인데요. 여기서 주목해야할 부분은 4번째 줄입니다. URL이 /webtext/pentomino/
로 시작하는 모든 요청에 대해 worker1로 전송하는 것입니다. 이런 설정을 통해 애플리케이션 서버로 넘겨줄 요청을 처리하는 것이죠.
웹 서버와 애플리케이션 서버를 다음과 같이 분리를 할 수도 있습니다.
이렇게 분리하면 이점이 뭘까요? 처리과정은 이전과 동일하지만 이렇게 분리해놓으면 다른 노드(컴퓨터)에서 작동시킬 수 있다는 장점이 있습니다. 위에서 localhost
대신 다른 노드의 IP를 설정하면 그 노드에서 작업처리의 호스트가 될 수 있는 것입니다. 또 대용량 작업 처리에도 유리합니다. 애플리케이션 서버에 비하면 웹 서버의 작업량은 더 작은데요. 애플리케이션 서버에서는 데이터베이스 처리도 하고 HTML등 많은 작업이 이루어지니까요. 근데 그렇다고 웹 서버가 마냥 일이 적은 것만은 아닙니다. 하나의 웹 페이지를 표시하려면 아시겠지만 HTML 이외에도 CSS, JavaScript, 각종 플래시 파일 등 다양한 정적 콘텐츠들이 있기 때문입니다3. 그렇기 때문에 이렇게 분리를 통해 처리량은 가벼운 대신 많은 횟수의 정적 콘텐츠에 대한 요청은 웹 서버에서, 횟수는 적으나 처리량이 무거운 동적 콘텐츠에 대한 요청은 애플리케이션 서버에서 담당할 수 있도록 하는 것입니다.
여러 톰캣에서 작동시키기 위해선 worker.list = worker1, worker2
로 바꾸고 JKMount /webtext/* worker1
, JKMount /nazejava/* worker2
로 설정함으로써 URL에 따라 요청을 분담할 수 있습니다. 이것 역시 애플리케이션 서버를 분리함으로써 얻는 유연함이죠. 사실 애플리케이션 서버에서 웹 서버 기능을 가지고 있기 때문에 단독으로 웹 서버로 작동시킬 수 있긴한데 아파치에서 제공하는 기능을 최소한 밖에 이용할 수 밖에 없기 때문에 규모가 크지 않을 때나 테스트 환경에서만 주로 활용됩니다.
웹 시스템의 삼층 구성
웹 시스템의 규모나 용도에 따라 각 서버의 프로세스를 노드 하나에 두느냐, 아니면 여러 노드에 분산해 배치하느냐가 달라집니다. 보통은 각 컴퓨터가 보유한 리소스를 각 서버 역할에 최대한 충실히 활용할 수 있도록 각 서버를 별개의 노드에 배치합니다.
위 설명을 통해 서브 사이드에서 웹 서버, 애플리케이션 서버, 데이터베이스 서버 이렇게 세 가지 구성요소로 나뉘는지 이해가 되셨나요? 규모에 작으면 노드 하나에 세 가지 모두 합칠 수도 있겠지만 규모가 커짐에 따라 이렇게 역할별로 분산하여 처리하여야 안전성이나 고장 대비에 수월합니다. 그러한 측면에서 이 세 가지 구성요소를 잘 기억해두는 것이 좋겠죠?
'Book > programming' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 05장. 연관관계 매핑 기초 (0) | 2018.12.26 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 03장. 영속성 관리 (0) | 2018.12.18 |
프로가 되기 위한 웹기술 입문2 (0) | 2018.11.08 |
프로가 되기 위한 웹기술 입문1 (0) | 2018.11.07 |
스프링 입문을 위한 자바 객체지향의 원리와 이해6 (2) | 2018.10.27 |