6장. 서블릿 기초

톰캣과 같은 WAS가 처음 나왔을 때 웹브라우저 요청을 스레드 방식으로 처리하는 기술이 서블릿. 모든 웹 프로그램은 6~7장의 기능을 뼈대로 하여 동작

 

 

6.1 서블릿의 세가지 기본 기능

서블릿 응답과 요청 수행 API

요청 API : javax.servlet.http.HttpServletRequest 클래스

응답 API : javax.servlet.http.HttpServletResponse 클래스

ex)

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 

1. 클라이언트가 서블릿에 요청

2. 톰캣 컨테이너가 요청을 받아 사용자의 요청이나 응답에 대한 HttpServletRequest 객체와 HttpServletResponse 객체를 만듦(요청에 대한 정보를 HttpServletRequest 객체 속성으로 담아서 메서드로 전달)

3. 서블릿의 doGet()/doPost() 메서드를 호출하며 이 객체를 전달

 

<HttpServletRequest의 메서드들>

 

반환형 메서드    기능
boolean authenticate(HttpServletResponse response) 현재 요청한 사용자가 ServletContext 객체에 대한 인증을 하기 위한 컨테이너 로그인 메커니즘을 사용한다
String changeSessionId() 현재 요청과 연관된 현재 세션의 id를 변경
String getContextPath() 요청한 컨텍스트를 가리키는 URI 반환
Cookie[] getCookies() 클라이언트가 현재의 요청과 함께 보낸 쿠키 객체 배열
String getHeader(String name) 특정 요청에 대한 헤더 정보를 문자열로 반환
Enumeration
<String>
getHeaderNames() 현재 요청에 포함된 헤더의 name 속성을 enumeration으로 반환
String getMethod() 현재 요청이 GET, POST, PUT 방식 중 어떤 HTTP 요청인지 반환
String getRequestURI() 요청한 URL의 컨텍스트 이름과 파일 경로 반환
String getServletPath() 요청한 URL에서 서블릿이나 JSP 이름을 반환
HttpSession getSession() 현재의 요청과 연관된 세션 반환(없으면 새로 만들어서 반환)

 

<HttpServletResponse의 메서드들>

반환형 메서드 기능
void addCookie(Cookie cookie) 응답에 쿠키를 추가
void addHeader(String name, String value) name과 value를 헤더에 추가
String encodeURL(String url) 클라이언트가 쿠키를 지원하지 않을 때 세션 id를 포함한 특정 URL을 인코딩한다
Collection
<String>
getHeaderNames() 현재 응답의 헤더에 포함된 name을 반환
void sendRedirect(String location) 클라이언트에게 리다이렉트 응답을 보낸 후 특정 URL로 다시 요청함
String getPathInfo() 클라이언트가 요청 시 보낸 URL과 관련된 추가 경로 정보 반환

출처 : https://park-story.tistory.com/28

 

 

6.2 <form> 태그 이용해 서블릿에 요청하기

  • 서블릿과 JSP는 HTML,CSS,자바스크립트들과 자신의 기능을 추가해서 서로 연동하여 동작.
  • 특히 사용자의 요청은 HTML의 <form> 태그나 자바스크립트로부터 전송 받아서 처리

* <form>, <input> : 클랑언트에서 서버로 데이터를 저송하는 기능 담당

 

웹브라우저에서 여러 가지 입력 서식을 이용해 전송 클릭 => 서블릿으로 데이터가 전송

서블릿은 여러가지 메서드를 이용해서 데이터를 받아 옴

 

ex) 로그인창의 HTML 코드 

<form name="frmLogin" method="get" action="login"  encType="UTF-8">
   아이디  :<input type="text" name="user_id"><br>
     비밀번호:<input type="password" name="user_pw" ><br>
    <input type="submit" value="로그인">  <input type="reset" value="다시입력">
</form>

 

실 데이터는 input 태그의 name 속성의 값과 실제 입력한 데이터가 쌍으로 전송 됨, 서블릿은 이 데이터를 받아옴

 

<form> 태그의 속성

  • name : <form> 태그의 이름, 여러 개의 <form>을 구분하는 역할(자바스크립트에서 <form>태그에 접근할 때 자주 사용)
  • method : 데이터 전송 방법 지정(GET-디폴트 / POST)
  • action : 데이터를 전송할 서블릿(매핑 이름 지정)이나 JSP를 지정
  • encType : 전송할 데이터의 encoding 타입을 지정. 파일을 업로드할 때는 multipart/form-data로 지정

 

 

6.3 서블릿에서 클라이언트의 요청을 얻는 방법

<form> 태그로 전송된 데이터를 받아 오는 메서드

메서드 기능
String getParameter(String name) name에 대한 전송된 값을 받아오는 데 사용
String[] getParameterValues(String name) name에 대해 여러 개의 값을 받아 올 때 사용(배열)
Enumeration getParametersNames() name 값을 모를 때 사용

출처 : https://park-story.tistory.com/28

 

ex) LoginServlet.java

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("utf-8");				//전송된 데이터를 utf-8로 인코딩
    String user_id=request.getParameter("user_id");
    String user_pw=request.getParameter("user_pw");
    System.out.println("아이디:"+user_id);
    System.out.println("비밀번호:"+user_pw);
}

 

<하나의 name으로 여러 개의 값을 서블릿으로 요청하기>

  • 주로 checkbox를 많이 사용 >> checked된 checkbox만 서블릿으로 넘겨짐
  • 하나의 name으로 여러개의 input 타입을 선언하면 서블릿으로 전송할 때 배열로 전송된다. 

ex)

<form name="frmInput" method="get" action="input">
아이디: <input type="text" name="user_id"><br>
  비밀번호 : <input type="password" name="user_pw"><br>
<input type="checkbox" name=subject value="java" checked>자바
<input type="checkbox" name=subject value="C언어">C언어
<br><br>
 <input type="submit" value="전송">
 <input type="reset" value="초기화">
</form>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	...
    String[] subject=request.getParameterValues("subject");
		for(String str:subject) {
			System.out.println("선택한 과목 : "+str);
		}
        }

 

일일이 name의 값을 기억하기 힘들 때는 getParameterName() 메서드 사용

ex)

Enumeration enu=request.getParameterNames();
while(enu.hasMoreElements()) {
    String name=(String)enu.nextElement();			//name 속성 값을 가져 옴
    String[] values=request.getParameterValues(name);
    for(String value : values) {
        System.out.println("name="+name+",value="+value);
    }
}

 

 

6.4 서블릿의 응답 처리 방법

  • doGet() / doPost() 메서드 안에서 처리
  • HttpServletResponse 객체 이용
  • setContentType()을 이용해서 클라이언트에게 전송할 데이터 종류(MINE-TYPE)을 지정
  • 클라이언트와 서블릿 통신은 자바 I/O의 스트림을 이용

클라이언트 <-> 서블릿 : 서블릿은 데이터 입력 받고, 네트워크로 데이터를 출력(클라로 전송)

 => 자바 I/O 스트림 클래스의 입출력 기능을 이용하면 쉽게 웹 애플리케이션의 네트워크 기능 구현 가능

 

  • 서버에서 클라로 데이터를 전송할 때는 어떤 종류의 데이터를 전송하는지 웹 브라우저에 알려줘야 함 >> 브라우저가 더 빠르게 처리 가능
  • 톰캣 컨테이너에서 미리 제공하는 여러 가지 전송 데이터 종류(=MINE-TYPE) 중 하나를 지정해서 전송

 

MINE-TYPE 예

  • HTML로 전송 > text/html  ( 웹 브라우저는 기본적으로 HTML만 인식하므로 대부분의 데이터 타입이 이거임)
  • 일반 텍스트 > text/plain
  • XML > application/xml

새로운 종류의 데이터를 지정하고 싶으면 CATALINA_HOME\conf\web.xml에 추가

 

<서블릿이 클라이언트에 응답하는 과정>

1. setContentType()을 이용해 MINE-TYPE를 지정

2. 데이터를 출력할 PrintWriter 객체를 생성

3. 출력 데이터를 HTML 형식으로 만듦

4. PrintWriter의 print()/println()을 이용해 데이터 출력

 

출력예제)

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=utf-8"); //응답할 데이터 종류 설정(html)
    PrintWriter out=response.getWriter(); //클라로 데이터를 출력해 줄 출력스트림 printWriter 객체

    String id=request.getParameter("user_id");
    String pw=request.getParameter("user_pw");

    String data="<html> <body>";
    data+="아이디 : "+id+"<br>패스워드 :"+pw;
    data+="</body> </html>";

    out.print(data); //HTML 태그 문자열을 웹 브라우저로 출력
}

 

계산기예제) >> 화면과 기능을 서블릿에 한번에 정의

public class CalcServlet extends HttpServlet {
	private static float USD_RATE = 1124.70F;
	private static float JPY_RATE = 10.113F;
	private static float CNY_RATE = 163.30F;
	private static float GBP_RATE = 1444.35F;
	private static float EUR_RATE = 1295.97F;
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");
		
		PrintWriter pw = response.getWriter();
		
        //첫화면에서 계산 눌렀을 때, 즉 두번째 브라우저 화면
		String command = request.getParameter("command");
		String won = request.getParameter("won");
		String operator = request.getParameter("operator");

		if (command != null && command.equals("calculate")) {
			String result = calculate(Float.parseFloat(won), operator); //계산 메서드 실행
			pw.print("<html><font size=10>변환결과</font><br>");
			pw.print("<html><font size=10>" + result + "</font><br>");
			pw.print("<a href='/pro06/calc'>환율 계산기 </a>"); //command 속성의 값으로 지정x
			return;
		}
		
        //첫화면(처음에는 command가 null이므로 위의 코드는 수행 x)
		pw.print("<html><title>환율계산기</title>");
		pw.print("<font size=5>환율 계산기</font><br>");
		pw.print("<form  name='frmCalc' method='get'  action='/pro06/calc'  />  ");
		pw.print("원화: <input type='text' name='won' size=10  />  ");
		pw.print("<select name='operator' >"); //셀렉트 박스@@ 선택된 애를 operator로 전송
		pw.print("<option value='dollar'>달러</option>"); //셀렉트 박스 후보들
		pw.print("<option value='en'>엔화</option>");
		pw.print("<option value='wian'>위안</option>");
		pw.print("<option value='pound'>파운드</option>");
		pw.print("<option value='euro'>유로</option>");
		pw.print("</select>");
		pw.print("<input type='hidden' name='command' value='calculate'  />  ");
         //hidden 태그 : name에 value값 넣기(브라우저에는 안보임), 변수 정의와 비슷
		pw.println("<input type='submit' value='변환'  />");
		pw.println("</form>");
		pw.print("</html>");
		pw.close();
	}

	private static String calculate(float won, String operator) {
		String result = null;
		if (operator.equals("dollar")) {
			result = String.format("%.6f", won / USD_RATE);
		} else if (operator.equals("en")) {
			result = String.format("%.6f", won / JPY_RATE);
		} else if (operator.equals("wian")) {
			result = String.format("%.6f", won / CNY_RATE);
		} else if (operator.equals("pound")) {
			result = String.format("%.6f", won / GBP_RATE);
		} else if (operator.equals("euro")) {
			result = String.format("%.6f", won / EUR_RATE);
		}
		return result;
	}
}

 

 

6.5 웹 브라우저에서 서블릿으로 데이터 전송하기

 

GET 방식 POST 방식
- 브라우저 > 서블릿 전송 : URL 뒤에 전송하는 데이터가 보임(name=value 형태, 여러개는 &로 구분)
- 보안 취약
- 전송할 수 있는 데이터 최대 255자
- 디폴트 전송 방식
- URL에 직접 입력해서 전송할 수도 있음
- 보안과 관련 없는 간단한 데이터를 주로 전송
- 서블릿에서는 doGet()을 이용해 처리
- 브라우저 > 서블릿 전송 : TCP/IP 프로토콜 데이터의 body 영역에 숨겨진 채 전송됨(주소창에 데이터가 보이지 않음)
- 보안에 유리
- 전송 데이터의 용량이 무제한
- 서블릿에서는 또 다시 가져오는 작업을 해야하므로 처리 속도가 GET보다 느림
- 보안과 관련된 데이터를 전송하는데 많이 사용 됨
- 서블릿에서는 doPost()을 이용해 처리

 

<form> 태그의 method 속성으로 방식 설정

브라우저에서 전송되는 방식과 메서드(do~)를 잘 일치 시켜야 하고(doHandle()로 퉁치기 가능) 오로지 이 메서드에서만 사용할 수 있음

 

 

6.6 GET 방식과 POST 방식 요청 동시에 처리하기

doGet과 doPost 에 doHandle()를 호출헤서 이 doHandle() 안에 로직을 구현하면 GET/POST 구분 없이 쓸 수 있음

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doHandle(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doHandle(request,response);
}
protected void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    System.out.println("doHanlde메서드 호출");
}

 

 

6.7 자바스크립트로 서블릿에 요청하기

전송 데이터에 대해 유효성 검사(ex. id/pw입력 유무) >> 자바스크립트로

따라서 <form> 태그에서 서블릿으로 요청하지 않고, 자바스크립트 함수에서 서블릿에 요청하기

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
 function fn_validate(){
	 var frmLogin=document.frmLogin;	//form 태그의 name속성으로 <form>태그 객체 받아오기
	 var user_id=frmLogin.user_id.value;
	 var user_pw=frmLogin.user_pw.value;
	 
	 if ((user_id.length==0 || user_id=="") || (user_pw.length==0 || user_pw == "")){
		 alert("아이디와 비밀번호는 필수입니다.")
	 } else{
		 frmLogin.method="post";
		 frmLogin.action="login5";
		 frmLogin.submit(); // 자바스크립트에서 서블릿으로 전송
	 }
 }
</script>

<title>로그인 창</title>
</head>

<body>
 <form name="frmLogin" method="post" action="login" encType="UTF-8">
  아이디 : <input type="text" name="user_id"><br>
  비밀번호 : <input type="password" name="user_pw"><br>
 <input type="button" onClick="fn_validate()" value="로그인">
 <input type="reset" value="다시입력">
 <input type="hidden" name="user_address" value="서울시 성북구"/>
 </form>
</body>
</html>

 

 

6.8 서블릿을 이용한 여러가지 실습 예제

 WebContent 내가 아닌 그 안의 폴더 안에 넣으면 프로젝트 명도 명시 해줘야 함

ex)

 <form name="frmLogin" method="get" action="/pro06/guguTest3" encType="UTF-8">

넘겨받은 데이터를 int형 변수에 저장하기 >> 파싱 : Integer.parseInt("name")

ex)

int dan=Integer.parseInt(request.getParameter("dan"));

 

** 지금까지 한것을 토대로 보면 서블릿 응답 기능은 결국 웹 애플리케이션 화면을 구현하는 기능이다.

    현재는 서블릿으로 화면을 구현하지는 않지만 서블릿이 처음 나왔을 때는 이런식으로 응답 기능을 이용해 화면을 구현함