본문 바로가기
○ WEB/19.07 BoostCourse_DB연결 웹앱

[웹기초 BE_Servlet] 9. 서블릿 컴파일 및 실행 / Servlet이란? / 작성방법 / 라이프 싸이클 / Request, Response 객체 이해 / doGet() / doPost()

by 0ver-grow 2019. 7. 8.
반응형

핵심 개념

  • 자바 웹 어플리케이션
  • HttpServlet
  • Dynamic Web Project

자바 웹 프로젝트 만들기

Perspective는 2가지

 

Java Perspective에선

File > Project > Web > Dynamic Web Project

JAVA EE Perspective에선

File > Project > Dynamic Web Project

우선 project name은 firstweb으로 한다.

java web application은 혼자실행이 안된다. 반드시 WAS안에 있어야한다.

우리가 사용할 WAS는 톰캣!

WAS가 웹앱이 실행되도록 도와주기에 Target runtime을 설정해줘야한다.

 

new Runtime > Apache Tomcat v8.5 > Next 클릭!

 Browse를 눌러서 apache-tomcat이 있는 폴더를 지정하고 Finish클릭해주면 자바 웹앱이 생성된다.

방금 만든 firstweb의 tree를 살펴보자.

웹앱을 구성하기 위해선 다음 디렉토리가 필요하단 것을 알 수 있다.

자바 웹 앱 생성

등록한 웹앱 프로젝트에 서블렛을 등록해보자

참고로, URL요청을 처리하는 프로그램을 서블렛이라 한다.

 

서블렛을 생성해보자!

package, Class name을 입력한 뒤 Next를 눌러준다.

HelloServlet class가 WAS에 배포될때 사용될 이름이 URL mappings에 기본으로 

이클립스는 런타임으로 설정된 WAS에 다음규칙으로 URL이 실행되도록 설정한다.

http://localhost:8080/{projectname}/{URL Mapping value}

현재는

http://localhost:8080/firstweb/HelloServelet

 

Next를 클릭하자

 

 

 

 

 

현재는 HelloWorld만 출력할 것이므로

일단 doGet에만 체크하고 Finish를 눌러주자.

 

앞서 HTTP 메서드에서

웹 브라우저가 get method방식으로 요청을 보낼 때

서블릿에 doGet Method가 호출된다.

Get메서드는 브라우저가 문서를 요청할 때 사용함

 

 

 

 

 

 

 

이제 HelloServlet에 코드를 작성해보자

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8"); // 응답결과는 UTF-8로
		PrintWriter out = response.getWriter(); // PrintWritter는 뭔가를 쓸 때 사용하기 좋음
		out.print(("<h1>Hello servlet </h1>"));
		
	}

톰캣을 실행해보자.

Finish버튼을 누르면 톰캣이 실행되고 있다는 콘솔이 나타나며 실행된 화면이 이클립스에 나타난다.

만약 이클립스 내장형 웹이 아니라 다른 웹에서 보고싶다면 설정을 바꿔주면 된다.

생각해보기

  1. 어떤 과정을 거쳐서 브라우져에 'Hello World' 가 출력될까요?

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

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

 

1. firstweb이라는 Dynamic WebProject를 생성

2. 해당 프로젝트에서 HelloServelet이란 서블릿클래스를 작성

3. 작성된 서블릿은 이클립스 내부에서 설정한 런타임, 톰캣에 의해 동작

4. 이후 웹 브라우저를 이용해서 톰캣 서버에 URL요청

5. HelloServlet실행결과를 웹브라우저에서 확인

< Servlet 이란? >

프로그램을 수행해서 페이지를 동적으로 만들어 주는 서블릿

 

학습 목표

  1. 자바 웹 어플리케이션의 구조를 이해한다.
  2. 서블릿에 대하여 이해한다.

핵심 개념

  • 자바 웹 어플리케이션
  • 서블릿

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

WAS에 설치(deploy)되어 동작하는 어플리케이션입니다.

이전에 firstweb프로젝트에 의해 만들어진 것이 자바 웹앱이다.

자바 웹 어플리케이션에는 HTML, CSS, 이미지, 자바로 작성된 클래스(Servlet도 포함됨, package, 인터페이스 등), 각종 설정 파일 등이 포함됩니다.

 

 

자바 웹 어플리케이션의 폴더 구조

웹 앱은 혼자서 동작하지 않고 WAS에 의해 동작된다.

자바 웹앱 폴더 구조에선 반드시 WEB-INF폴더가 있어야 된다. 이 폴더 안에는 web.xml이란 파일이 존재(서블릿3.0미만에선). 배포 기술자, 웹앱에 대한 정보를 전부 가지고 있음.

 

 

이클립에서 실행된 Dynamic Web Project

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

(오른쪽) 이클립스가 내부적으로 톰캣을 사용하기에 톰캣이 설치된 폴더의 디렉토리와 유사하게 구성되어 있다.

 

 

서블릿이란?

자바 웹 어플리케이션의 구성요소 중 동적인 처리를 하는 프로그램의 역할입니다.

서블릿을 정의해보면

- 서블릿(servlet)은 WAS에 동작하는 JAVA 클래스입니다. 

- 서블릿은 HttpServlet 클래스를 상속받아야 합니다.

- 서블릿과 JSP로부터 최상의 결과를 얻으려면, 웹 페이지를 개발할 때 이 두 가지(JSP, 서블릿)를 조화롭게 사용해야 합니다.

예를 들어, 웹 페이지를 구성하는 화면(HTML)은 JSP로 표현하고, 복잡한 프로그래밍은 서블릿으로 구현합니다.

 

생각해보기

  1. 동적인 페이지가 필요한 경우는 어떤 것일까요? 

< Servlet 작성 방법 >

핵심 개념

  • HttpServlet
  • web.xml

버전에 따른 Servlet 작성 방법은 2가지!

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

  • web.xml 파일을 사용하지 않습니다.
  • 자바 어노테이션(annotation)을 사용합니다.
  • 앞에서 실습했던 first web에서 사용합니다.

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

  • servlet을 등록할 때 web.xml 파일에 등록합니다.

 

<우선 Servlet 3.0 spec 이상에서 사용하는 방법에 대해 알아보자!>

- Servlet 3.1 spec으로 exam31라는 프로젝트를 생성

- 해당 프로젝트에 1부터 10까지 출력하는 TenServlet을 작성

- 요청은 http://localhost:8080/exam31/ten 으로 동작하도록 설정

 

Dynamic Web Project 생성하기

2.x버전과 3.x버전의 가장 큰 차이점은 서블릿을 web.xml에 직접등록하느냐(2.x버전) 어노테이션방법을 이용하느냐(3.x버전)이다. 그래서 현재 버전에선 필수는 아니다. 그.러.나. 나중에 스프링 등을 사용할 때 3.x버전으로 서블릿을 만들었어도 다른 설정 부분을 web.xml에 추가해야할 필요가 있기에 그때는 반드시 web.xml을 생성해야한다.

 

이제 서블릿을 만들자

(가운데 사진)

URL Mapping은 서블릿 파일을 요청할 때 요청할 이름(주소값)을 지정하는 부분!

이 부분을 ten으로 수정해주자.

왜냐하면 앞서 요청을 http://localhost:8080/exam31/ten에 하기로 했기때문이다.

그리고 Next를 눌러주자

(오른쪽 사진)

doPost에는 체크해제해주고 위처럼만 체크해주자.

 

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

이미 응답할 페이지를 만들어서 가지고 있지않고 요청(여기선 ten이란 요청)이 들어왔을 때 이 프로그램(서블릿)이 실행되면서 응답할 코드를 만들어내고 그 코드로 응답.

 

코드 작성하기

 

클라이언트가 요청할 때 서버는 req를 받아내는 객체와 req하는 객체 2개를 자동 생성.

/**
 * Servlet implementation class TenServlet
 */
@WebServlet("/ten") /* 어노테이션 존재함. 현재 요청하는 주소값이 ten으로 되어있으나 여기서 바로 수정가능 */
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 {
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		out.print("<h1>1-10 print! </h1>");
		for(int i = 1; i <= 10; i++) {
			out.print(i+"<br>");
		}
		out.close();
	}

}

setContentType은 브라우저가 받은 파일이 이미지인지 텍스트인지 등을 인식할 수 있게해주는 것

getWriter에는 PrintWriter이란 메서드가 있다.

 

작성할 서블릿 실행하기

실행됐을 때의 콘솔화면

< Servlet 3.0 spec 미만에서 사용하는 방법에 대해 알아보자! >

- servlet 2.5 spec으로 exam25라는 프로젝트생성

- 해당 프로젝트에 1부터 10까지 출력하는 TenServlet을 작성

- http://localhost:8080/exam25/ten 으로 동작하도록 설정

 

Dynamic Web Project 생성하코드에 

(중간사진) Generate web.xml를 체크했기에 (오른쪽사진) web.xml이 생성됨

Servlet 생성하기

코드에 대해 설명해주기

3.x버전과 달리 TenServlet.java파일에는 /ten이란 어노테이션이 없고 대신에 web.xml에 존재한다.

어노테이션이 없는 대신 (오른쪽사진) 파란색 부분처럼 web.xml에 내용이 추가되었다.

 

web.xml, 어노테이션의 동작법

서블릿은 요청이 들어왔을 때 반드시 서블릿 이름으로 요청하진 않기에 이 web.xml은 클라이언트가 요청할 때 입력한 /ten이란 URL로 요청을 하게 되면 이 URL mapping(servlet-mapping)에서 찾아낸다. 여기서 찾지 못하면 404라는 페이지가 보인다. 만약에 존재한다면 servlet-name이란 엘리먼트를 확인한다. servlet-name라는 이름을 가지고 실제 servlet이란 태그안에서 똑같은 이름의 servlet-name 있는지를 확인하고 class인 exam이라는 패키지 내부의 servlet-name을 실행한다. 이처럼 해당 서블릿을 반드시 등록해야만 찾아서 실행시킬 수 있다. 이 동작을 3.0이상 서블릿버전에서 어노테이션이 대신해준다.

 

정리.

1. 클라이언트가 http://localhost:8080/exam25/ten 으로 요청

2. web.xml의 servlet-mapping 에서 /ten을 찾는다.

3. 그리고 동일 태그에서 servlet-name을 찾아 TenServlet이란 것을 확인한다.

4. servlet태그에서 TenServlet이란 servlet-name을 찾아 실행한다.

 

요청하는 주소값을 바꾸고 싶다면 <url-pattern> /주소값 <url-pattern> 해주면된다.

예시. <url-pattern> /test <url-pattern>

 

3.0 이상의 spec에서 작성한 코드와 동일하게 작성하고 실행해주면 된다.

 

< Servlet 라이프 싸이클 >

들어가기 전에

어떤 객체의 생성부터 소멸까지의 과정을 라이프 사이클(Life Cycle)라고 합니다.

 

핵심 개념

  • init
  • service
  • destory

서블릿이 when 생성되고 when what 메서드들이 how 호출되는지 알아보자

LifecycleServlet

HttpServlet의 3가지 메소드를 오버라이딩

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

서블릿 클래스를 만들어보자

 

언제 생성되고 언제 소멸되는지를 알아보기 위해 

init, destroy, service 메서드를 override해준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

그리고 Finish를 눌러주면

 

생성자, init(), destroy(), servise() 총 4개의 메서드가 보인다.

서블릿이 언제 생성되고 이런 메서드들이 언제 호출되는지 콘솔창으로 확인하기 위해 다음과 같이 입력해준다.

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

	/**
	 * @see Servlet#init(ServletConfig)
	 */
	public void init(ServletConfig config) throws ServletException {
		System.out.println("init 호출!!");
		// TODO Auto-generated method stub
	}

	/**
	 * @see Servlet#destroy()
	 */
	public void destroy() {
		System.out.println("destroy 호출!!");
		// TODO Auto-generated method stub
	}

	/**
	 * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("service 호출!!");
		// TODO Auto-generated method stub
	}

}

(빨간줄) 해당 URL로 클라이언트가 서버한테 요청 -> 서버는 이 URL을 받아서 Lifecycle이란 URL Mapping이 LifecycleServlet이란 것을 확인 -> LifecycleServlet이 해당 클래스가 메모리에 존재하는지 체크 -> 존재하지 않으면 객체를 생성, 메모리에 올림

 

최초로 지금 LifecycleServlet을 호출했기에 서버는 메모리에 없다고 판단하였고

 

 

 

(옆사진_파란부분) 그래서 LifecycleServlet을 생성하게 된 것! 그래서 생성자에 넣어준 메시지("LifecycleServlet 생성")가 출력(위사진_파란줄)된 것을 확인할 수 있었던 것!

 

 

 

 

 

브라우저 새로고침을 눌러주면 service메서드만 동작되어 호출되는 것을 확인할 수 있다.

 

왜냐하면 서블릿은 서버에 서블릿객체를 여러 개 만들지 않기 때문이다. 요청이 들어올 때 마다 생성을 반복하지 않고 요청된 객체가 메모리에 있는지만을 체크하여 있다면 service라는 메서드만 호출한다.

 

 

 

 

아직까지 destroy객체가 실행되지 않았다. 그렇다면 언제 실행되는걸까?

이를 확인하기 위해 서블릿을 수정해보자

간단하게 출력내용만 바꿔준 뒤 Run as를 해주면

public LifecycleServlet() {
    	System.out.println("LifecycleServlet 수정");
        // TODO Auto-generated constructor stub
    }

destroy가 실행된 것을 확인할 수 있다. 서블릿이 수정됐기에 메모리에 올라간 객체는 더 이상 사용할 수 없기 때문.

즉, 웹앱이 갱신되거나 WAS종료시 destroy메서드가 호출된다는 것을 알 수 있다.

 

다시 Run as를 해주면 (오른쪽사진) 다시 객체를 생성하고 init(), service()를 호출하는 부분을 수행한단 것을 확인할 수 있다.

 

 

 

 

 

 

 

 

 

< 이론 설명 > 

init() 메서드 : 서블릿이 처음 호출됐을 때

service() 메서드 : 객체가 메모리에 있다면 호출. 응답해야 되는 모든 내용은 이 메서드에 구현

destroy() 메서드 : 웹앱이 갱신되거나 WAS가 종료될 때 호출

 

WAS는 서블릿의 요청을 받는다 -> 해당 서블릿이 메모리에 있는지 확인 -> 없다면 해당 서블릿 클래스를 메모리에 올림 즉, 객체가 생성되는 작업이라 생성자실행 후 init()메서드가 실행됨 -> service() 메서드 호출되어 실행. 

 

doGet메서드만으로 service()가 가능했던이유

service()메서드없이 doGet메서드만으로도 정상적인 실행이 가능했던 이유?

service()메서드는 HttpServlet의 service()라는 메서드로 이미 구현된 상태이다.

내가 만든 클래스가 service()라는 메서드를 갖고 있지 않다면, 부모 클래스의 service()메서드가 실행된다.

즉, doGet()메서드만 오버라이드한 경우에도 WAS는 service()만 호출하기에 내 클래스에서 service()를 오버라이드하지 않았다면 내 서블릿의 부모인 HttpServlet의 service()메서드가 실행

 

HttpServlet의 service()메서드 구현은 어떻게 되어있는걸까?

service(request, response) 메소드

HttpServlet의 service메소드는 템플릿 메소드 패턴으로 구현합니다.

  • 클라이언트의 요청이 GET일 경우에는 자신이 가지고 있는 doGet(request, response)메소드를 호출
  • 클라이언트의 요청이 Post일 경우에는 자신이 가지고 있는 doPost(request, response)를 호출

service()메서드가 이렇게 동작했기에 doGet()메서드만으로 응답결과로 보내줄 수 있었던 것!

 
LifecycleServlet 수정 실습

  • Service(request, response)메소드 주석처리
  • HttpServlet의 doGet(request, response)메소드 오버라이딩
  • HttpServlet의 doPost(request, response)메소드 오버라이딩

1. Service()메서드를 주석처리해주고 doGet(), dePost()메서드를 Override해주자

2. 다음과 같이 doGet(), doPost()를 작성해주자

	import java.io.PrintWriter;
    
    @Override
	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();
	}

혹시라도 PrintWriter에 오류(PrintWriter cannot be resolved to a type)발생했다면

import java.io.PrintWriter; 해주면 해결된다. 빨간엑스박스를 더블클릭하면 import할 수 있다.

 

이제 Run on Server를 해주면 브라우저에 나타난다.

그런데 inner Web Browser에서는 안뜬다 에러발생

이때는 웹 브라우저를 변경하면 된다. Window탭 > Web Browser > Crome

 

doGet()과 doPost() 응답 설명

doGet()에 대한 응답내용

일단 출력된 브라우저에서 소스보기를 눌러서 소스를 확인해보면

doGet()메서드에서 print에다가 문자열로 넣었던 코드들이 그대로 응답 결과로 넘어간 것을 알 수 있다.

 

submit을 눌렀을 때 action주소로 보내게된다. 이 때 메서드는 post값으로 넣게 된다.

 

다시 설명하면

다음처럼 자료를 입력해주고 ok버튼(type='submit'))을 눌러주면 URL(action='/firstweb/LifecycleServlet')로 요청해준다. 이 때의 메서드는 바로 method='post' 이다.

즉, 이 요청이 들어가면 doPost()메서드가 실행된다.

 

다음과 같은 순서로 doPost()가 진행된다.

입력한 name값에 따라 응답이 달라진다.

동일한 코드임에도 입력한 값에 따라 응답 결과가 달라진다.

이를 동적페이지라고 한다.

 

반응형