본문 바로가기

공부/웹 프로그래밍

1. 웹 프로그래밍 기초 - #5. Servlet - BE

본 게시물의 내용은 edwith 부스트코스 - 웹 프로그래밍 강의를 수강한 내용을 바탕으로 한다. 들은 내용을 정리하고, 복습에 용이하도록 정리해놓은 게시물이다.

이번 차시의 강의는 다음과 같다. 배경색이 들어간 부분이 이번 게시글에서 다룰 내용이다. 

 

1). Servlet이란?

2). Servelt 작성 방법

3). Servlet 라이프 사이클

4). Request, Response 객체 이해하기

 

 

1). Servlet이란?

자바 웹 어플리케이션

자바 웹 어플리케이션은 WAS에 설치되어 동작하는 어플리케이션을 말한다. 자바 웹 어플리케이션은 HTML, CSS, 이미지, JAVA로 작성된 클래스(Servlet, package, 인터페이스  등) 등 각종 설치파일이 포함된다. 쇼핑몰, 까페, 블로그 등 아주 포괄적이다. 

 

웹 어플의 폴더 구조

WEB-INF폴더의 구성에 대해서 알아보면, 다음과 같다. 

  • web.xml 파일(배포 기술자)
  • 웹 어플에 대한 정보를 가진다
  • lib 폴더: 각종 자료 파일들이 들어감
  • Classes 폴더: 실제 클래스, servlet파일, html, css, js등의 코드들이 들어간다.

 

Servlet

Servlet은 WAS에서 작동하는 JAVA 클래스로, 자바 웹 어플리케이션의 구성요소중, 동적인 처리를 하는 프로그램의 역할을 한다. 이를 사용하기 위해선 HttpServlet 클래스를 상속받아야 한다. Servlet과 jsp를 조화롭게 사용하는 것이 중요하다. 예를 들면, 웹 페이지를 구성하는 화면은 jsp를 사용하고, 복잡한 프로그램이은 Servlet으로 구현한다.

 

 

2). Servelt 작성 방법

Servlet의 작성 방법은 다음과 같이 버젼에 따라 2가지로 나뉜다.

  • 3.0 이상: web.xml에 직접 등록하지 않고, annotation을 이용
  • 3.0 미만: web.xm에 직접 등록해서 사용

사실 버젼에 따라 무엇이 달라지는지는 아직 잘 모르겠다. 그래서 비교는 못하겠고, 3.1버젼으로 1부터 10까지를 출력하는 코드를 작성해봤다. 

package exam;

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

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class TenServlet
 */
@WebServlet("/ttt")
public class TenServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public TenServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */ 
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		out.print("<h1>1-10까지 출력</h1>");
		
		for (int i = 1; i < 11; i++) {
			out.print(i+"<br>");
		}
		out.close();
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

이렇게 간단하게 서브렛의 작성 방법에 대해 알아보았다. 

 

 

3). Servlet 라이프 사이클

Servlet클래스를 사용하려면, HttpServlet이 3가지 메소드를 오버라이딩해야 한다. 그 메소드 3가지는 다음과 같다

  • init()
  • service(request, response)
  • destroy()

Servlet 생명주기

WAS는 서블릿 요청을 받으면, 해당 서블릿이 메모리에 있는 지 확인한다. 만약 메모리에 해당 서블릿이 없다면, 해당 서블릿을 메모리에 올리고(객체를 생성하고), init() 메소드를 실행한다. 그리고 나서, service()메소드를 실행한다. 

 

서브렛의 라이프사이클에 대해 알아보기 위해, LifeCycleServlet.java를 작성해볼 것이다. 

package examples;

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

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class LifeCycleServlet
 */
@WebServlet("/LifeCycleServlet")
public class LifeCycleServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public LifeCycleServlet() {
        super();
        // TODO Auto-generated constructor stub
        System.out.println("LifeCycleServlet 생성!!");
    }

	public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
        System.out.println("init test 호출!!");

	}

	public void destroy() {
        System.out.println("destroy()메소드 호출!!");
		// TODO Auto-generated method stub
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<head><title>form</title></head>");
		out.println("<body>");
		out.println("<form method='post' action='/firstweb/LifeCycleServlet'>");
		out.println("name : <input type='text' name='name'><br>");
		out.println("<input type='submit' value='ok'><br>");                                                 
		out.println("</form>");
		out.println("</body>");
		out.println("</html>");
		out.close(); 
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		String name = request.getParameter("name");
		out.println("<h1> hello " + name + "</h1>");
		out.close();
	}

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
        System.out.println("service()메소드 호출!!");
	}

}

코드를 보면, init()메소드가 실행될 때, service메소드가 실행될 때, destroy()메소드가 실행될 때 해당 메소드가 호출되었음을 알 수 있도록 프린트문을 추가했다. 그리고 실행시켜본 결과, 콘솔에는 위와 같이 init(), service()메소드가 호출된 것을 볼 수 있었다. 그런데 destroy()메소드는 실행되지 않았다. 왜일까?

왜냐하면, destroy()메소드는 WAS가 종료되거나, 웹 어플리케이션이 새롭게 갱신될 경우 실행되는 메소드이기 때문이다. 따라서 콘솔창에는 나타나지 않았다.  

이를 통해서, 하나의 서블릿이 init(), service(), destory() 순으로 진행된다는 것을 알 수 있다. 이는 곧, 객체가 만들어져서 메모리에 올라가고, 원하는 기능을 한 뒤, 메모리에서 사라짐을 의미한다. 이해를 돕기 위해 아래 그림을 첨부하였다. 

서블릿의 생명주기

 

 

4). Request, Response 객체 이해하기

서버와 클라이언트의 통신

request와 response객체를 보기 전에, 우리는 앞서 강의에서 클라이언트와 서버가 통신하는 과정을 살펴본 적이 있다.

서버와 클라이언트의 통신

위 그림에서, 요청은 request이고, 응답은 response라 할 수 있다. 또한, 웹 브라우저가 클라이언트이고, 서버가 WAS라 할 수 있다. 좀 더 자세한 과정은 다음과 같다.

  • 웹 브라우저에 URL을 입력하고, 엔터를 치면 웹 브라우저는 도메인과 포트 번호를 이용해 서버에 접속한다. 
  • 이 때, path정보, clien ip등의 다양한 정보를 포함한 요청(request)를 서버에 보낸다. 
  • 클라이언트로부터 요청이 들어오면, WAS는 
  • HttpServletRequest, HttpServletResponse라는 객체를 생성해서, request객체에는 요청할 때 가져온 정보를 담고, response객체에는 현재 접속한 클라이언트에게 전송하기 위한 정보를 담는다. 
  • 이렇게 생성된 두 객체를 요청 정보에 있는 path로 매핑된 서블릿에게 전달한다. 
  • 이 두 객체는 메소드(doGet, doPost등 Servlet에 구현한 메소드)에 파라미터로 전달돼서 사용한다. 

 

Request

  • 요청 정보를 전달하기 위해 사용한다. 
  • 헤더, 파라미터, 쿠키 등을 읽어들이는 메소드를 가지고 있다(get).
  • Body의 stream을 읽어들이는 메소드도 있다. 

 

Response

  • WAS는 response객체를 생성하여 서블릿에게 전달한다
  • 서블릿은 해당 객체를 이용, content타입이나 응답 코드, 응답 메시지등을 전송한다. 

 

실습

HeaderServlet.java를 통해 request객체가 담고 있는 요청의 헤더 정보들을 출력해보자.

package examples;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class HeaderServlet
 */
@WebServlet("/Header")
public class HeaderServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HeaderServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<head><title>form</title></head>");
		out.println("<body>");
		
		Enumeration<String> headerNames = request.getHeaderNames();
		while(headerNames.hasMoreElements()) {
			String headerName = headerNames.nextElement();
			String headerValue = request.getHeader(headerName);
			out.println(headerName+ " : " + headerValue + "<br>");
		}
		
		out.println("</body>");
		out.println("</html>");
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

위의 결과에서, 요청 헤더의 이름 headerName, headerValue를 get메소드로 읽어옴으로써 헤더가 어떤 정보를 가지고 있는지를 확인했다. cpu, cache-control, encoding정보 등이 담겨 있다. 

 

ParameterServlet.java를 통해서, 어떻게 변수와 값이 넘어가는 지를 살펴보자. 

package examples;

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

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ParameterServlet
 */
@WebServlet("/Param")
public class ParameterServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ParameterServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<head><title>form</title></head>");
		out.println("<body>");
		
		String name = request.getParameter("name");
		String age = request.getParameter("age");
		
		out.println("name: " + name + "<br>");
		out.println("age: " + age + "<br>");
		
		out.println("</body>");
		out.println("</html>");
		
	}

}

위 코드에서, 우리는 name과 age라는 변수의 값을 출력하기를 원했다. 그러나 해당 코드를 실행시키니 name과 age는 null로 출력되었다. 이러한 이유는 아주 간단하다. 변수에 값을 넘겨주지 않았기 때문이다. url뒤에 ?변수이름=변수의값 일 덧붙여서 넣어보자. 이 경우에는, ?&name=kim&age=5 를 덧붙인다.

위 처럼 url에 결과를 덧붙여서 엔터를 치면, 다음과 같이 우리가 전달한 값이 페이지에 반영되는것을 볼 수 있다. 이 부분은 응용해야 할 부분이 많으므로 기억해두자. 

 

InfoServlet.java를 통해서 request의 다양한 정보들을 알아보자.

package examples;

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

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class InfoServlet
 */
@WebServlet("/Info")
public class InfoServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public InfoServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<head><title>info</title></head>");
		out.println("<body>");
		
		String uri = request.getRequestURI();
		StringBuffer url = request.getRequestURL();
		String contentPath = request.getContextPath();
		String remoteAddr = request.getRemoteAddr();
		
		out.println("uri: " + uri + "<br>");
		out.println("url: " + url +  "<br>");
		out.println("contentPath : " + contentPath + "<br>");
		out.println("remoteAddr : " + remoteAddr + "<br>");
		
		out.println("</body");
		out.println("<html>");
		
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		
	}

}

요청이 들어오면, doGet() 메소드에서 파라미터로 받은 request의 정보를 바탕으로 정보를 출력한다. 

uri, url, 경로, ip주소 등이 출력되는 것을 볼 수 있다.

 

이러한 점들을 잘 숙지한다면, 요청이 들어왔을 때 이 요청에 포함된 정보를 바탕으로 그에 맞는 response를 주는 방법 또한 고려해볼 수 있다고 생각한다. 

 

fe와 be를 둘다 해봤는데, fe는 좀 재미가 없는것 같다. 그래도 fe또한 필수적인 요소라 생각하기에 열심히 해봐야겠다. 이로써 1. 웹 프로그래밍 기초를 모두 수강했으니, 이제 프로젝트를 진행해야겠다.