본문 바로가기
교재 실습/자바 웹 개발 워크북

20. GenericServlet의 사용 (2)

by Jint 2022. 1. 14.

CalculatorServlet 클래스를 배치 및 테스트하기 위해 web.xml에 배치 정보를 추가한다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>web03</display-name>
  
  <!-- 서블릿 배치 정보 -->
  
  <!-- 서블릿 선언 -->
  <!-- 
  servlet : 서블릿 별명과 서블릿 클래스명 설정
  servlet-name : 서블릿 별명, 클래스 이름일 필요가 없고 공백을 포함해도 된다.
  servlet-class : 패키지 이름을 포함한 서블릿 클래스명 (QName = Fully qualified name = 패키지명 + 클래스명)
   -->
  <servlet>
  	<servlet-name>Hello</servlet-name>
  	<servlet-class>lesson03.servlets.HelloWorld</servlet-class>
  </servlet>
  <servlet>
  	<servlet-name>Calculator</servlet-name>
  	<servlet-class>lesson03.servlets.CalculatorServlet</servlet-class>
  </servlet>
  
  
  <!-- 서블릿을 URL과 연결 -->
  <!-- 
  servlet-mapping : 서블릿과 URL을 매핑
  servlet-name : servlet태그에서 정의한 서블릿 별명
  url-pattern : 서블릿을 요청할 때 클라이언트가 사용할 URL 설정('/'는 컨텍스트 루트, 즉 웹 애플리케이션 루트를 의미)
   -->
  <servlet-mapping>
  	<servlet-name>Hello</servlet-name>
  	<url-pattern>/Hello</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  	<servlet-name>Calculator</servlet-name>
  	<url-pattern>/calc</url-pattern>
  </servlet-mapping>
  
  
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    <welcome-file>default.htm</welcome-file>
  </welcome-file-list>
  
</web-app>

이후 톰캣 서버를 실행시키고, 만약 서버가 켜져 있었다면 재시작 한다. web.xml의 변경사항을 적용하려면 웹 애플리케이션을 다시 시작해야 한다. 이후 웹 브라우저를 띄워 주소창에 http://localhost:9999/web03/calc?a=20&b=30를 입력한다. 서블릿 URL과 a와 b의 매개변수 값을 붙여 GET 요청을 보낸다.

그림 1 (CalculatorServlet 실행 결과)

서블릿의 실행 결과다(그림 1).

 

GET 요청은 매개변수 값이 URI에 포함되기 때문에 setCharacterEncoding()으로 문자집합을 설정할 수 없다. 서블릿 컨테이너에서 권고하는 지침에 따라 URI 인코딩 형식을 설정한다. 톰캣 서버에서 URI 인코딩을 설정하는 방법이다.

그림 2 (톰캣 실행 환경의 server.xml)

Servers 프로젝트의 톰캣 실행 환경 폴더에서 server.xml 파일을 연다(그림 2).

<Connector connectionTimeout="20000" port="9999" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>

Connector 태그에 URIEncoding 속성을 추가하고 값은 UTF-8로 설정한다. 서블릿 컨테이너마다 설정 방법이 달라서 반드시 사용문서를 확인하자.

 

Servlet 3.0 사양부터 web.xml 대신 어노테이션으로 서블릿 배치 정보를 설정할 수 있다. @WebServlet 어노테이션을 이용하여 서블릿의 배치 정보를 작성한다.

package lesson03.servlets;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

/*
@WebServlet 어노테이션의 주요 속성

name : 서블릿의 이름을 설정하는 속성. 기본값은 빈 문자열("").
@WebServlet(name="서블릿이름")

urlPatterns : 서블릿의 URL 목록을 설정하는 속성. 속성값은 String 배열, 기본값은 빈 배열({}).
- 서블릿에 대해 1개의 URL을 설정하는 경우
@WebServlet(urlPatterns="/calc")	//일반적인 문자열로 표기 가능
@WebServlet(urlPatterns={"/calc"})	//중괄호를 사용하여 배열 표기
- 서블릿에 대해 여러개의 URL을 설정하는 경우
@WebServlet(urlPatterns={"/calc","calc.do","calculator.action"})

value : urlPatterns과 같은 용도. 어노테이션 문법에서는 속성 이름이 'value'인 경우 속성 이름 없이 값만 설정할 수 있다.
만약 value 속성 외에 다른 속성의 값도 함께 설정한다면 value 속성 이름 생략 불가.
@WebServlet(value="/calc")
@WebServlet("/calc")	//속성명 'value' 생략 가능
@WebServlet(value="/calc",name="Calculator")	//가능
@WebServlet("/calc",name="Calculator")			//오류
*/
@WebServlet("/calc")
public class CalculatorServlet extends GenericServlet {
	
	private static final long serialVersionUID = 1L;
	
	//GenericServlet 추상클래스 : 하위 클래스에게 공통의 필드와 메소드를 상속해 주고자 존재한다.
	//Servlet 인터페이스를 구현 받아 init(), destroy(), getServletConfig(), getServletInfo() 메소드를 미리 구현한 클래스이다.
	//GenericServlet 추상클래스 상속받은 클래스는 Servlet 인터페이스의 메소드 중 service()만 구현하면 된다.

	@Override
	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		
		/* 
		ServletRequest 클래스 : 클라이언트의 요청 정보를 다룰 때 사용
		↓ 주요 메소드
		getRemoteAddr() : 서비스를 요청한 클라이언트의 IP 주소 반환
		getScheme() : 클라이언트가 요청한 URI 형식 Scheme 반환 (URL에서 ':'문자 전에 오는 값 반환)
					  URL의 스킴이란 자원을 식별하는 최상위 분류 기호다. http와 https(프로토콜), ftp와 news(프로토콜X) 등이 있다.
		getProtocol() : 요청 프로토콜의 이름과 버전을 반환 ex) HTTP/1.1
		getParameterNames() : 요청 정보에서 매개변수 이름만 추출하여 반환
		getParameterValues() : 요청 정보에서 매개변수 값만 추출하여 반환
		getParameterMap() : 요청 정보에서 매개변수들을 Map 객체에 담아서 반환
		setCharacterEncoding() : POST 요청의 매개변수에 대해 문자 집합을 설정. 기본값은 ISO-8859-1.
								 매개변수의 문자 집합을 정확히 지정해야 제대로 변환된 유니코드 값을 반환하고
								 매개변수의 문자 집합을 지정하지 않으면 무조건 ISO-8859-1이라 가정하고 유니코드로 변환.
								 주의할 점은 getParameter()를 호출하기 전에 이 메소드를 호출해야만 적용된다.
		getParameter() : GET, POST 요청으로 들어온 매개변수 값을 꺼낼 때 사용
		
		ServletResponse 클래스 : 응답과 관련된 기능을 제공
		↓ 주요 메소드
		setContentType() : 출력할 데이터의 인코딩 형식과 문자 집합 지정.
						   클라이언트에게 출력할 데이터의 정보를 알려 그 형식에 맞게 올바르게 화면에 출력(Rendering)하게 함.
						   (HTML이면 태그규칙에 맞춰 출력, XML이면 각 태그를 트리노드로 표현, text/plain이면 출력할 데이터가 텍스트이고
						   별도의 메타정보가 없는 순수한 텍스트임 지정)
						   (setContentType("text/plain;chartset=UTF-8")처럼 출력 데이터의 문자 집합을 지정할 수 있지만
						   인자형식이 올바르지 않으면 웹 브라우저는 서버에서 받은 결과를 화면에 출력하는 대신 파일 저장 대화 창을 띄우므로
						   잘못된 값을 입력하지 않도록 유의해야 한다)
	    setCharacterEncoding() : 출력할 데이터의 문자 집합 지정. 기본값은 ISO-8859-1.
	    						 (유니코드 값을 UTF-8로 설정하면 한글 출력 가능. 문자 집합을 지정하지 않으면 모든 문자를 영어로 간주하여
	    						 한글은 '?'문자로 변환되어 출력된다.)
	    getWriter() : 클라이언트로 출력할 수 있도록 스트림 객체 반환 PrintWriter 클래스로 데이터를 넣을 수 있다.
	    			  (setContentType()이나 setCharacterEncoding()가 먼저 호출된 이후 호출되어야 정상적으로 유니코드가 지정된
	    			  문자 집합으로 변환된다)
	    getOutputStream() : 이미지나 동영상과 같은 바이너리 데이터 출력
		*/
		
		int a = Integer.parseInt( request.getParameter("a") );
		int b = Integer.parseInt( request.getParameter("b") );

		response.setContentType("text/plain");
		response.setCharacterEncoding("UTF-8");
		PrintWriter writer = response.getWriter();
		writer.println("a=" + a + "," + "b=" + b + "의 계산결과 입니다.");
		writer.println("a + b = " + (a + b));
		writer.println("a - b = " + (a - b));
		writer.println("a * b = " + (a * b));
		writer.println("a / b = " + ((float)a / (float)b));
		writer.println("a % b = " + (a % b));
		
	}
	
}

@WebServlet 어노테이션으로 서블릿의 URL 정보를 설정했다. 이후 DD 파일(web.xml)에서 CalculatorServlet에 대한 서블릿 정의와 URL 매핑을 설정하는 부분을 주석처리 한다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>web03</display-name>
  
  <!-- 서블릿 배치 정보 -->
  
  <!-- 서블릿 선언 -->
  <!-- 
  servlet : 서블릿 별명과 서블릿 클래스명 설정
  servlet-name : 서블릿 별명, 클래스 이름일 필요가 없고 공백을 포함해도 된다.
  servlet-class : 패키지 이름을 포함한 서블릿 클래스명 (QName = Fully qualified name = 패키지명 + 클래스명)
   -->
  <servlet>
  	<servlet-name>Hello</servlet-name>
  	<servlet-class>lesson03.servlets.HelloWorld</servlet-class>
  </servlet>
  <!--
  <servlet>
  	<servlet-name>Calculator</servlet-name>
  	<servlet-class>lesson03.servlets.CalculatorServlet</servlet-class>
  </servlet>
  -->
  
  <!-- 서블릿을 URL과 연결 -->
  <!-- 
  servlet-mapping : 서블릿과 URL을 매핑
  servlet-name : servlet태그에서 정의한 서블릿 별명
  url-pattern : 서블릿을 요청할 때 클라이언트가 사용할 URL 설정('/'는 컨텍스트 루트, 즉 웹 애플리케이션 루트를 의미)
   -->
  <servlet-mapping>
  	<servlet-name>Hello</servlet-name>
  	<url-pattern>/Hello</url-pattern>
  </servlet-mapping>
  <!--
  <servlet-mapping>
  	<servlet-name>Calculator</servlet-name>
  	<url-pattern>/calc</url-pattern>
  </servlet-mapping>
  -->
  
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    <welcome-file>default.htm</welcome-file>
  </welcome-file-list>
  
</web-app>

 

톰캣 서버를 재시작 후 주소창에 http://localhost:9999/web03/calc?a=20&b=30를 다시 입력한다.

그림 1 (CalculatorServlet 실행 결과)

서블릿의 실행 결과다(그림 1). 실행 결과는 이전과 같다.

 

 

마지막으로 서블릿 프로그래밍을 정리하자면, 웹 서버가 실행시키는 프로그램을 '웹 애플리케이션'이라고 하고 웹 서버와 웹 애플리케이션 사이에 데이터를 주고받기 위한 규칙을 'CGI(Common Gateway Interface)'라고 한다. 그래서 보통 웹 애플리케이션을 'CGI 프로그램'이라고 부른다.

 

특히 자바로 만든 웹 애플리케이션을 서블릿이라고 하는데 서블릿 명칭은 Server와 Applet의 합성어다. 즉 '클라이언트에게 서비스를 제공하는 작은 단위의 서버 프로그램'이라는 뜻이다.

 

서블릿 컨테이너는 서블릿의 생성과 실행, 소멸까지 서블릿의 생명주기를 관리하는 프로그램이다. 클라이언트로부터 요청이 들어오면 서블릿 컨테이너는 호출 규칙에 따라 서블릿의 메소드를 호출한다. 서블릿 호출 규칙은 javax.servlet.Servlet 인터페이스에 정의되어 있어서 서블릿을 만들 때 반드시 Servlet 인터페이스를 구현해야 한다.

 

Servlet라는 규칙 외에 JSP를 만들고 실행하는 규칙, EJB(Enterprise JavaBeans)라는 분산 컴포넌트에 관한 규칙, 웹 서비스에 관한 규칙 등 기업용 애플리케이션 제작에 필요한 기술들의 사양을 정의한 것을 Java EE라고 한다. Java EE는 기능 확장이 쉽고, 이기종 간의 이식이 쉽고, 신뢰성과 보안성이 높고, 트랜잭션 관리와 분산 기능을 쉽게 구현할 수 있는 기술을 제공한다.

 

Java EE 사양에 따라 개발된 서버를 'Java EE 구현체(Implementation)' 또는 'WAS(Web Application Server)'라고 부른다. 상용 제품으로 JEUS(티맥스소프트), WebLogic(오라클), WebSphere(IBM), JBoss Enterprise(레드헷) 등이 있고, 오픈소스로는 Geronimo(아파치재단), GlassFish(오라클), JBoss Community(레드헷) 등이 있다.

 

Java EE 사양 중에서 Servlet과 JSP 기술만 구현한 서버를 '서블릿 컨테이너'라 한다. Tomcat(아파치재단), Resin(Caucho), Jetty(오픈 프로젝트)가 이에 해당한다.

 

서블릿 라이브러리는 서블릿을 좀 더 편리하게 개발할 수 있도록 javax.servlet.GenericServlet 추상 클래스를 제공한다. GenericServlet 추상 클래스는 Servlet 인터페이스에 선언된 메소드 중 service()를 제외한 나머지 메소드를 모두 구현하였다. 따라서 GenericServlet 추상 클래스를 상속받으면 service() 구현에만 집중하면 되기 때문에 좀더 쉽고 편리하게 개발할 수 있다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

댓글