WAS (Web Application Server)는 어떻게 동작 하는가?
지난 수십 년 동안 소프트웨어 개발의 주요 추세는 웹 애플리케이션이었습니다.
웹 애플리케이션의 급속한 성장은 모두 다 인터넷을 사용하는 시대가 되었고, 클라우드 컴퓨팅이 급속히 발전해가면서 당연한 것이 되었습니다.
웹 응용 프로그램이 등장하기 전의 소프트웨어는 일반적으로 개별 컴퓨터나 로컬 네트워크에 설치되고 실행되었습니다. 이런 시대에는 개별 컴퓨터에 소프트웨어를 직접 설치해야 하며, 여러 컴퓨터 간에 데이터와 리소스를 공유하려면 많은 어려움이 있었습니다. 원격 접속도 쉽지 않았습니다.
하지만 최근에는 업무 자동화에 필요한 B2B 프로그램, 인터넷 뱅킹 및 물건을 구매하기 위한 B2C 프로그램까지 인터넷 웹 애플리케이션을 사용하지 않는 시스템이 없을 정도입니다.
이 글에서는 인터넷 환경의 웹 애플리케이션을 서비스하기 위해서 어떤 기술이 필요한지 기본 개념을 설명하고, 기술에 대한 이해를 위해서 간단한 WAS 를 직접 구현해 보면서 알아보려고 합니다.
먼저 WAS (Web Application Server), Java EE, HTTP Protocol, 개념들을 활용해 사용자 브라우저에서 서버 간의 처리 방법을 직접 구현하는 예제를 이번 챕터에서 설명하겠습니다.
1. WAS(Web Application Server)란 무엇인가?
웹 애플리케이션 서버는 웹 응용 프로그램의 개발 및 배포를 위한 플랫폼을 제공하는 서버 측 소프트웨어입니다.
기본적으로 HTTP Web 서버를 포함하고 있으며 HTTP Protocol을 기반으로 작동합니다.
널리 사용되는 일부 웹 애플리케이션 서버에는 Apache Tomcat, Eclipse Jetty, JBoss EAP, Oracle Weblogic, Tmax JEUS 및 Microsoft 의 IIS가 포함됩니다.
어떤 웹 애플리케이션 서버를 선택할 것인지는 어떤 운영체제를 사용할 것인지, Java 기반의 애플리케이션을 사용한다면, Java EE의 모든 기능을 사용해야하는지, 서블릿, JSP만 있으면 되는지에 따라 선택하면 됩니다.
국내 환경에서는 레거시 환경의 EJB 또는 JMS 시스템을 사용하는 업무 시스템이라면 Java EE를 모두 만족하는 상용 WAS 제품을 선택해야 하며, 서비스가 중단되면 안되는 중요한 시스템이라면 보안, 안정성, 기술 지원을 위해서 오픈소스 WAS보다는 상용 제품을 선택해야 합니다.
그중 Apache Tomcat은 Java EE 사양 중 일부만 구현된 오픈소스 엔진입니다.
반면 JBoss EAP, Oracle Weblogic, Tmax JEUS 의 경우는 Java EE 모든 사양이 구현된 인증된 유료 제품입니다.
(JBoss EAP 는 일반적인 유료 제품과는 달리 오픈소스 라이센스를 가지고 있는 제품이지만 구독하여 기술 지원받을 수 있음)
따라서 요구 사항에 따라 적절하게 선택하여 사용할 수 있습니다.
- 사양
- Expression Language (EL)
- Servlet
- Java Server Pages (JSP)
- WebSocket
- Enterprise Java Bean (EJB)
- Java Persistence API (JPA)
- Java Transaction API (JTA)
- Java Message Service (JMS)
- ETC Java EE
- 기타
- Apache Tomcat (Jetty 등)
- OContent-Type
- O
- O
- O
- –
- –
- –
- –
- –
- –
- JBoss EAP (Weblogic, JEJUS)
- O
- O
- O
- O
- O
- O
- O
- O
- O
- O
2. HTTP Protocol 이란 무엇인가?
HTTP Protocol은 HTML 문서와 같은 정적 리소스들을 가져올 수 있도록 해주는 프로토콜입니다.
HTTP는 웹에서 이루어지는 모든 데이터 교환의 기초이며, 클라이언트-서버 프로토콜이기도 합니다.
메시지 형식 및 전송 방법과 웹 서버 및 브라우저가 다양한 명령에 대한 응답으로 수행해야 하는 작업을 정의합니다.
특징으로는 서버에 연결하고, 요청해서 응답받으면 연결을 끊어지는 Stateless 방식으로 많은 사용자가 접속하더라도 최소한의 연결로 많은 요청을 처리할 수 있습니다.
위와 같이 WAS 는 HTTP Procotol 을 기반으로 수많은 기능이 구현된 서버입니다.
WAS 가 사용자 브라우저에서 웹 페이지 또는 리소스를 요청하면 서버에서 어떤 동작이 일어나고 응답을 주는지 예제를 통해서 살펴보겠습니다.
브라우저는 HTTP 프로토콜에 의해 정의된 형식으로 요청 메시지를 보내고, 서버는 요정 메시지를 분석하여 필요에 따라 일련의 작업 후 결과를 HTML 형태로 브라우저에 돌려줍니다.
브라우저는 응답받은 HTML을 해석하여 화면을 출력합니다.
아래 소스 [SimpleHTTPServer1] 는 간단한 Socket 프로그램으로 브라우저의 요청을 받아 처리하는 로직입니다.
public class SimpleHTTPServer1 {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8080); <===== ❶
while (true) { <===== ❷
Socket client = server.accept();
try (BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream())) {
int oneInt = -1;
while(-1 != (oneInt = in.read())){
System.out.print((char)oneInt); <===== ❸
}
String response = "HTTP/1.1 200 OKnnHello, World!";
out.print(response);
out.flush();
}
client.close();
}
}
}
❶ 은 8080 포트로 서버에서 받아 주겠다는 의미 입니다.
❷ 사용자 브라우저에서 요청이 올 때까지 대기하고 있다가 요청이 들어오면 ❸ 은 사용자 브라우저에서 받은 정보를 출력합니다.
[그림 1] 과 같이 브라우저에서 “/” 에 “name”, age” 파라미터를 GET 으로 요청했을 경우 서버에서 출력되는 정보는 아래와 같습니다.
- ❶ 영역
- HTTP Method : GET
- URI : /
- Query String : name=openmaru&age=10
- HTTP version : HTTP/1.1
- 나머지 영역
- HTTP Header 영역
GET /?name=openmaru&age=10 HTTP/1.1 <===== ❶
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
[그림 2] 과 같이 브라우저에서 “/” 에 “name”, age” 파라미터를 POST로 요청했을 경우 서버에서 출력되는 정보는 아래와 같습니다.
GET일 때와 달리 ❶ 영역의 HTTP Method 정보가 POST이고, ❷ 영역의 Content-Type 헤더 값이 application/x-www-form-urlencoded 인 것을 확인할 수 있습니다.
그렇다면 Form 형태로 파라미터를 전달받았다는 것이고 파라미터 정보는 ❸ 영역인 메시지 바디에 존재 합니다.
규칙은 아래와 같고, GET일 때와 다르게 헤더 영역 이후 공백이 오게 되고, 다음 라인부터 메시지 바디 영역이 됩니다.
메시지 바디 영역의 끝은 헤더 “Content-Length” 길이입니다.
첫 번째 행
헤더 영역 N행
공백
메시지 바디
POST / HTTP/1.1 <===== ❶
Host: 127.0.0.1:8080
Connection: keep-alive
Content-Length: 20
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded <===== ❷
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
name=openmaru&age=10 <===== ❸
이렇게 브라우저에서 HTTP GET, POST를 서버에 요청하면 서버에서 어떻게 출력되는지 알아보았습니다.
이러한 규칙을 통해서 클라이언트, 서버 간 요청과 응답을 주고받는 것이고 이러한 명세들이 HTTP Protocol에 정의되어 있습니다.
앞에서 브라우저에서 요청된 정보를 확인했다면 이번에는 서버에서 처리 이후 사용자에게 돌려주는 응답에 대해서 알아보겠습니다.
위 예제를 실행해 보면 이상하게도 요청 후 브라우저에 응답이 종료되지 않고 결과도 출력되지 않는 모습을 보았을 것입니다.
이유는 요청된 Stream 즉 요청 메시지를 읽고 출력하였으나 메시지 끝부분을 판단하고 반복문(while) 문을 빠져나오는 구문이 없기 때문입니다.
public class SimpleHTTPServer2 {
... 생략 ...
Map headers = new HashMap<>();
String line;
while (!(line = in.readLine()).equals("")) { <===== ❶
System.out.println(line);
setHeaders(headers, line);
}
if ("application/x-www-form-urlencoded".equals(headers.get("Content-Type"))) { <===== ❷ String messageBody = getMessageBody(in, headers); System.out.println("Request Message Body ====================>");
System.out.println(messageBody);
System.out.println("====================");
}
out.println("HTTP/1.1 200 OK"); <===== ❹
out.println("Content-Type: text/html;charset=UTF-8"); <===== ❺
out.println(); <===== ❻
out.println("Hello, World!"); <===== ❼
out.flush(); <===== ❽
}
client.close();
}
}
private static void setHeaders(Map headers, String line) { <===== ❾
String[] parts = line.split(": ");
if (parts.length == 2) {
headers.put(parts[0], parts[1]);
}
}
private static String getMessageBody(BufferedReader in, Map headers) throws IOException {
int contentLength = Integer.parseInt(headers.get("Content-Length")); <===== ❸
char[] body = new char[contentLength];
in.read(body, 0, contentLength);
String messageBody = new String(body);
return messageBody;
}
}
수정된 부분은 Stream 을 라인 단위로 읽는 중에 ❶ “공백” 라인이 나오면 중지하도록 했으며, ❷ Content-Type 이 Form 형식(application/x-www-form-urlencoded)이라면 “공백” 라인 이후부터 ❸ Content-Length 길이만큼 메시지 바디를 얻어 오게 수정하였습니다.
즉 Content-Length 헤더 정보는 메시지 바디로 받을 메시지의 길이 정보이고 ❾ 함수를 통해서 가동되었습니다.
그리고 응답 메시지가 HTML 형식이라고 사용자 브라우저에 알려 줄 수 있는 ❺ Response Header Content-Type 값을 추가하였습니다.
응답 메시지의 규칙은 다음과 같습니다.
❹
HTTP Version
HTTP 응답 상태코드
HTTP 응답 상태 메시지
❺ 응답 헤더 정보
❻ 개행
❼ 응답 본문
❽ 사용자 브라우저에 출력
이제 브라우저에서 받은 응답을 확인하면 소스 ❹~❽ 영역에 의해 [그림3, 4] 와 같습니다.
General 영역은 요청 URL, Method 정보, 응답 코드 등을 표현합니다.
Request Headers 영역은 브라우저가 서버로 보낸 정보이고, Response Headers 영역은 서버에서 브라우저로 받은 정보입니다.
Headers Tab
General
- Request URL : 요청 서버 + GET Parameter 정보
- Request Method : HTTP Method 정보 (GET, POST, PUT, DELETE 등)
- Status Code : 요청 후 받은 응답 결과 (성공/실패 여부)
– 응답 메시지 중 첫 번째 정보로 판단
Request Headers
- Accept : 클라이언트에서 응답받아 처리할 수 있는 형식
– 상황에 따라 응답 결과를 HTML 이 아닌 JSON 형식으로 받고 싶을때 이 값을 활용 - Accept-Encoding : 클라이언트에서 처리할 수 있는 압축 형식
– 일반적으로 최근의 WEB 환경은 응답 메시지의 양이 많으므로 메시지를 압축하여 전송 - Content-Type : 요청 정보의 메시지 형태로 서버에서 메시지 바디를 파싱하는데 필요
– form : application/x-www-form-urlencoded
– json : application/json 등
– Response Headers
Payload Tab
- 컨텐츠 바디 영역
Response Tab
- 서버에서 보낸 응답 본분을 표시합니다.
어떻게 보면 간단한 문자열 규칙을 해석하고 처리하는 것이 WAS 의 기본적인 기능입니다.
정의된 규칙 맞게 요청 메시지를 분석하고 그에 알맞게 처리하는 로직을 만들면 되는 것입니다.
하지만 설명하지 않은 요청/응답 Header 값에서도 알 수 있듯이 HTTP 및 웹 애플리케이션 서버가 구현해야 하는 기능은 무수히 많습니다.
이러한 정의는 아래 링크에서 확인할 수 있습니다.
https://www.w3.org/Protocols/
https://javaee.github.io/javaee-spec/
마치며
지금까지 WAS 에 대한 개념을 설명하고 간단하게 요청/응답하는 HTTP 서버를 살펴보았는데요.
WAS 는 웹 애플리케이션의 실행 및 관리를 위한 필수적인 기능을 제공하기에 이에 대한 이해는 매우 중요한 부분일 것입니다.
다음 챕터에서는 WAS 의 서블릿에 대해서 자세히 알아보며 더욱 알찬 내용을 소개해 드리겠습니다😉
이구용 (ddakker@openmaru.io)
오픈마루 연구소
Lead Developer
IT 운영자들이 반드시 알아야 하는 클라우드 네이티브 기술
/in Kubernetes, OpenShift, Red Hat, 발표자료/by 실장 님IT 생존을 위한 진정한 클라우드 기술! 이젠 클라우드 네이티브를 꼭 알아야 합니다.
/in Kubernetes, OpenShift, Red Hat, 발표자료/by 실장 님2022년 9월 클라우드 네이티브 세미나 자료 다운로드
/in Seminar, 발표자료, 오픈소스/by 실장 님