[부스트코스] Servlet
ETC./study

[부스트코스] Servlet

뉴비뉴 2019. 6. 13.

6) HelloWorld 서블릿 컴파일 및 실행하기

  • 서블릿은 URL 요청을 처리하는 프로그램이다.
  • 이클립스는 runtime으로 설정된 WAS에 다음과 같은 규칙의 URL로 서블릿을 실행하도록 설정한다.
    http://localhost:8080/{프로젝트이름}/{URL Mapping 값}
  • HTTP 프로토콜 학습 시 HTTP request에 첫 번째 줄에 메소드라는 것이 있다.
    메소드에는 GET, POST, PUT, DELETE 등이 있다. 웹 브라우저가 GET 메소드 방식으로 요청을 보낼 때 서블렛에 doGet() 메소드가 호출된다.
  • GET 메소드는 웹 브라우저가 서버에게 문서를 요청할 때 사용하는 방식이다.

 

Hello World가 나오는 과정

[ 웹브라우저 <-> 웹서버( 아파치 ) <-> WAS( 톰캣 ) <-> 웹어플리케이션 ]

우선 HelloServlet.java 파일은 HelloServlet.class로 변환됩니다.  그리고 톰캣( 서블릿컨테이너 ) 가 HelloServlet.class를 Servlet 규칙을 통해 실행하고 결과를 웹서버에 전달합니다. 그리고 웹서버는 http통신 프로토콜과 함께 해당 정보를 브라우저에 뿌려줍니다.

 

1) Servlet 이란?

자바 웹 애플리케이션(Java Web Application)

  • WAS에 설치되어 동작하는 어플리케이션
  • 자바 웹 어플리케이션에는 HTML, CSS, 이미지, 자바로 작성된 클래스(Servlet도 포함됨, package, 인터페이스 등), 각종 설정 파일 등이 포함된다.
  • 서블릿은 WAS에서 동작하는 Java 클래스이다.
  • 서블릿은 HttpServlet 클래스를 상속 받아야 한다.
  • 서블릿과 JSP로 부터 최상의 결과를 얻으려면, 웹 페이즈를 개발할 때 이 두가지(JSP, 서블릿)를 조화롭게 사용해야 한다. 예: 웹 페이지를 구성하는 화면(HTML)은 JSP로 표현하고, 복잡한 프로그래밍은 서블릿으로 구현

WEB-INF/web.xml 웹 어플리케이션에 대한 정보들을 다 가지고 있다. 3.0 미만에서는 필수로 갖고 있어야 했다.

WEB-INF/lib/.jar 파일

WEB-INF/classes/java 패키지, class들

 

이클립스에서 실행된 Dynamic Web Project

  • 이클립스에서 Dynamic Web Project의 Servlet을 실행하면, 해당 프로젝트가 이클립스가 관리하는 .metadata 폴더 아래에 자바 웹 어플리케이션 폴더 구조로 만들어져 실행된다.

2) Servlet 작성 방법

1. Servlet 3.0 spec 이상에서 사용하는 방법

  • web.xml파일을 사용하지 않음
  • 자바 어노테이션(annontation)을 사용
  • 앞에서 실습했던 firstweb에서 사용

Servlet 3.0 이상

2. Servlet 3.0 spec 미만에서 사용하는 방법

  • Servlet을 등록할 때 web.xml 파일에 등록

Servlet 3.0 미만

이클립스에서 웹 다이나믹 프로젝트 생성 시 Content root는 http://localhost:8080/exam31/ten 부분이다.

3점대는 어노테이션 방법을 이용하기 때문에 굳이 생성해 줄 필요가 없다. 그런데 나중에 스프링 같은 것들을 사용하게 될 때에는 서블릿을 3점대로 만들었다 하더라도 다른 설정 부분을 web.xml에 추가해야 할 필요가 있기 때문에 그때는 반드시 web.xml을 생성해주어야 한다.

 

서블릿은 동적으로 응답 결과를 만들어낸다.

동적이라는 것은 이미 응답할 페이지를 만들어서 가지고 있는게 아니라 요청이 들어왔을 때 이 프로그램이 실행되면서 다시 이야기하면 ten 이라고 요청을 하면 이 서블릿이 실행이되면서 응답할 코드를 만들어내고 그 때 그 코드로 응답을 하게 하는 것이다.

 

클라이언트가 요청하면 서버는 응답한다.

이 때 서블릿은 요청을 받아내는 객체와 응답을 하기 위한 객체 두 개를 자동으로 만들어낸다.

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

요청에 대한 정보들은 모두 HttpServletRequest 안에 추상화 시켜 가지고 있을 것이고,

응답에 대한 정보들은 HttpServletResponse 안에 가지고 있다.

 

1~10까지 출력

PrintWriter out = response.getWriter();
out.println("<h1>1-10까지 출력!!<h1>");
for(int i = 1 ; i <= 10 ; i++) {
		out.println(i+"<br>");

해당 부분을 수정하면 URL 주소를 변경할 수 있다.
현재는 Ten으로 되어 있기 때문에 http://localhost:8080/exam31/Ten 로 나오는 걸 확인할 수 있다.

 

Servlet 3.0 spec 미만에서 사용하는 방법

  • 실습
  • Servlet 2.5 spec으로 exam25라는 프로젝트를 생성
  • 해당 프로젝트에 1부터 10까지 출력하는 TenServlet을 작성
  • http://localhost:8080/exam25/ten으로 동작하도록 설정

HttpServlet을 상속받고 있는 TenServlet이 생성되었는데 3.0 이상 버전과의 차이점은 Class 위 쪽에 붙어있던 어노테이션이 보이지 않는다.

 * Servlet implementation class TenServlet
 */
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.getWriter().append("Served at: ").append(request.getContextPath());
	}

}

 

 

 

서블릿은 요청이 들어왔을 때 반드시 서블릿 이름으로 요청하지는 않기 때문에 web.xml은 클라이언트가 요청할 때 이런 URL로 요청하게 되면 Servlet-name이 같은 서블릿을 찾아서 실제 class인 exam 이라는 패키지 안에 있는 TenServlet을 실행시켜달라 

 

<url-pattern>/ten</url-pattern> 에서 ten을 찾지 못하면 404 페이지가 리턴된다. 만약 존재한다면

<servlet-name>TenServlet<servlet-name>이라는 엘리먼트를 확인한다. 똑같은 이름의 Servlet-name이 있는지 확인

url 변경 시 url-pattern 부분의 /ten을 변경해주면 된다.

web.xml 변경 시 서버를 restart 해주어야 한다.

 

3) Servlet 라이프 싸이클

클라이언트가 Servlet에 요청을 하면, Servlet은 바로 호출되지 않는다.

Servlet은 객체를 생성하고 초기화 작업을 거친 후, 요청을 처리하는 생명 주기를 갖고 있습니다.

1. 요청이 오면, Servlet 클래스가 로딩되어 요청에 대한 Servlet 객체가 생성된다.

2. 서버는 init() 메소드를 호출해서 Servlet을 초기화 한다.

3. service() 메소드를 호출해서 Servlet이 브라우저의 요청을 처리하도록 한다.

4. service() 메소드는 특정 HTTP 요청(GET,POST 등)을 처리하는 메소드 (doGet(), doPost() 등)를 호출합니다.

5. 서버는 destroy() 메소드를 호출하여 Servlet을 제거한다.

 

LifecycleServlet 작성

  • 서블릿 생명주기를 확인할 수 있는 LifecycleServlet을 작성한다.
  • HttpServlet의 3가지 메소드를 오버라이딩
    init()
    service(request, response)
    destroy()

public class Lifecycleservlet extends HttpServlet {
	private static final long serialVersionUID = 1L;  

    public Lifecycleservlet() {
        System.out.println("LifecycleServlet 생성!!");
        
    }

	public void init(ServletConfig config) throws ServletException {
		System.out.println("init 호출!!");
	}

	public void destroy() {
		System.out.println("destroy 호출!!");
	}

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("service 호출!!");
	}

}

LifecycleServlet 생성!!
init 호출!!

service 호출!!

 

destroy 는 아직 호출되지 않고 있다.

 

하지만 여기서 웹 사이트를 새로고침을 하게 되면

service만 호출하게 된다.

 

LifecycleServlet 생성!!
init 호출!!

service 호출!!

 

LifecycleServlet 생성!!
init 호출!!

service 호출!!

service 호출!!

 

서블릿은 서버에 서블릿 객체를 여러 개 만들지 않는다.

요청이 여러 번 들어오면 매번 생성하고 이런 일들을 반복하는게 아니라

실제 요청된 객체가 메모리에 있는지 없는지 확인해서

있다면 Service라는 메소드만 호출된다.

그래서 한번 호출된 이후에는 계속 Service만 호출되고 있는것을 확인할 수 있다.

 

그렇다면 destroy()는 언제 생성될까?

 

public void init(ServletConfig config) throws ServletException {
		System.out.println("init test 호출!!");
	}

서블릿이 수정 되었기 때문에 지금 메모리에 올라가 있는 서블릿 객체는 더 이상 사용될 수 없다.

destroy 호출!!

다시 브라우저를 새로고침하니 처음부터 객체를 생성하고, init을 호출하고 service를 호출하는 걸 확인할 수 있다.

 

서블릿이 처음 호출되면 init() --> service()

destroy()는 항상 호출되는것이 아니라 웹 애플리케이션이 갱신되거나 WAS가 종료될 때 호출이 된다.

  • WAS는 서블릿 요청을 받으면 해당 서블릿이 메모리에 있는지 확인한다.
  • if(메모리에 없음){
      해당 서블릿 클래스를 메모리에 올림
      init() 메소드를 실행
    }
  • service() 메소드를 실행
  • WAS가 종료되거나, 웹 어플리케이션이 새롭게 갱신 될 경우 destroy() 메소드가 실행됨

service(request, response) 메소드

  • HttpServlet의 service메소드는 템플릿 메소드 패턴으로 구현
    - 클라이언트의 요청이 GET일 경우에는 자신이 가지고 있는 doGet(request, response) 메소드를 호출
    - 클라이언트의 요청이 Post일 경우에는 자신이 가지고 있는 doPost(request, response)를 호출
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		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();
	}

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

 

form method='post' action='/firstweb/LifecycleServlet' // 이 주소로 요청을 해줘

input type='submit' value='ok' // ok 버튼이 눌렸을 때

action이 작동될 때 post라는 값으로 넣어줘라 라고 의미하면 된다.

 

4) Request, Response 객체 이해하기

1. 웹 브라우저에 URL을 입력하고 Enter를 입력하면 Domain과 포트 번호를 이용해서 서버에 접속

2. 그리고 나서 path 정보, 클라이언트의 IP, 클라이언트의 다양한 정보를 포함한 요청정보를 서버에 전송

HttpServletRequest 객체에는 요청할 때 가지고 들어온 다양한 정보들을 담는다.

HttpServletResponse 요청을 보낸 클라이언트에게 전송하기 위해 정보를 담을 수 있는 객체를 생성해주는 것이다.

위 객체를 서블릿에게 전달한다.

이렇게 전달한 객체는 service(), doGet(), doPost() 같은 메서드에 파라미터로 전달되서 사용하게 된다.

HttpServletRequest

  • http 프로토콜의 request 정보를 서블릿에게 전달하기 위한 목적으로 사용
  • 헤더정보, 파라미터, 쿠키, URI, URL 등의 정보를 읽어 들이는 메소드를 가지고 있다.
  • Body의 Stream을 읽어 들이는 메소드를 가지고 있다.
  • 요청할 때 가지고 있는 정보들을 모두 메서드로 담는다.

HttpServletResponse

  • WAS는 어떤 클라이언트가 요청을 보냈는지 알고 있고, 해당 클라이언트에게 응답을 보내기 위한 HttpServletResponse 객체를 생성하여 서블릿에게 전달
  • 서블릿은 해당 객체를 이용하여 content type, 응답코드, 응답메시지 등을 전송

헤더 정보 읽어 들이기

  • 웹 브라우저가 요청 정보에 담아서 보내는 header 값을 읽어 들여 브라우저 화면에 출력한다.

request가 갖고 있는 메서드 중에 getHeaderNames()라는 메서드가 있다.

이 메서드는 모든 헤더 이름을 문자열 Enumeration 객체로 반환해준다.

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		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>");
	}

accept : image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
accept-language : ko-KR
cache-control : no-cache
ua-cpu : AMD64
accept-encoding : gzip, deflate
user-agent : Mozilla/5.0 (Windows NT 6.2; Win64; x64; Trident/7.0; rv:11.0) like Gecko
host : localhost:8080
connection : Keep-Alive

 

와 같은 결과값이 나오게된다.

 

파라미터 읽어 들이기

  • URL주소의 파라미터 정보를 읽어 들여 브라우저 화면에 출력한다.
  • http://localhost:8080/firstweb/param?name=kim&age=5
    : ? 뒤에 부분을 파라미터라고 한다.
    : &기준점을 사용하면 더 많은 파리미터를 전달할 수 있다.
    : 앞에 = 이라는 것을 기준으로 앞에 있는 것은 파라미터 이름, 뒤에 있는 것은 파라미터 값이다.
    ex) 이름은 kim, 나이는 5
  • getParameter("파라미터name");
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	response.setContentType("text/html");
	PrintWriter out = response.getWriter();
	out.println("<html>");
	out.println("<head><title>form</head></title>");
	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 : null
age : null

이라는 결과가 나온다 왜냐하면 http://localhost:8080/firstweb/param 에 파라미터 정보가 없기 때문이다.

파라미터 정보를 추가하고 실행하면 추가 된 정보가 출력되는 것을 확인할 수 있을것이다.

input 태그 안의 값들도 똑같이 파라미터로 넘어오기 때문에 input 태그를 사용해도 된다.

 

그외의 요청정보 출력

  • URI, URL, PATH, Remote host 등에 대한 정보 출력
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	response.setContentType("text/html");
	PrintWriter out = response.getWriter();
	out.println("<html>");
	out.println("<head><title>info</head></title>");
	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>");
}

uri : /firstweb/info
url : http://localhost:8080/firstweb/info
contentPath : /firstweb
remoteAddr : 0:0:0:0:0:0:0:1 // IPv6의 경우

 

getRemoteAddr() 메서드가 출력해주는 클라이언트의 주소 값이다.

클라이언트가 요청할 때 이 클라이언트의 주소 같은 부분이 나온다.

로컬 컴퓨터에서 접속하였기 때문에 0:0:~이런식으로 나온다.

원격 컴퓨터가 접속했다면 원격컴퓨터의 IP가 출력된다.

 

url 부분은 StringBuffer로 지정해주었는데 그 이유는 url의 경우 문자열 데이터가 계속해서 변경이 되어야 하기 때문에 이럴 때에는 String 보다는 StringBuffer를 사용하기를 권장한다.

 

내용정리

파트1. 웹 프로그래밍 기초 - 웹프론트엔드

  • 프론트엔드와 백엔드의 역할과 관계
  • HTML로 웹페이지 구조(계층적) 설계
  • CSS 레이아웃에 필요한 속성과 활용방법

파트2. 윕 프로그래밍 기초 - 웹백엔드

  • 웹 개발에 대한 이해
  • 개발 환경 설정(JDK, 톰캣, 이클립스)
  • 서블릿(Servlet) (동적인 웹페이지)

 

댓글

💲 추천 글