75~76일차 Servlet 개념과 요청과 응답 조금

쿠우·2022년 7월 13일
0

WAS에는 3가지의 컨테이너로 구성되어있다
Servlet - 서블릿 관리
JSP - JSP 관리
EJB (Enterprise JavaBeans)

https://doozi316.github.io/web/2020/09/13/WEB26/
WAS를 왜 사용하는지 컨테이너는 무엇인지 예쁘게 잘 정리된 블로그

서블릿 컨테이너의 역할

  • 웹서버와의 통신 지원
  • 서블릿 생명주기(Life Cycle) 관리
  • 멀티쓰레드 지원 및 관리
  • 선언적인 보안 관리

-서블릿(Servlet)이란? (컨테이너에 의해 관리된다.)

서블릿은 웹 컨테이너에 의해서 관리되며, 다양한 클라이언트 요청에 의해서 동적인 콘텐츠
(content)로 응답 가능한 자바 기반의 웹 컴포넌트이다.

  • 클라이언트의 요청에 의해서 동적으로 실행된다.

  • 서블릿은 반드시 웹 컨테이너에 의해서 관리되며, 자바 스레드로 동작되기 때문에 효율
    적으로 사용이 가능하다

  • 서블릿의 응답결과는 일반적으로 HTML 형식으로 서비스된다.

  • MVC 패턴을 적용하여 웹 어플리케이션을 개발한다면, 서블릿이 아닌 JSP에서 HTML 코드를 작성하게
    된다

-서블릿 아키텍처 및 핵심 API!!!

실행 순서/동작 구조
-Java EE 기반에서는 프로그램을 컨테이너가 제어한다.
-개발자가 아닌 제 3자가 실행흐름을 제어하는 것을 IoC(Inversion of Control) 제어의 역전이라 한다.

1.클라이언트로부터 요청을 받음
-요청받은 페이지가 서블릿이면 서블릿 컨테이너로 처리를 넘김, WEB-INF에서 서블릿을 찾아서 실행준비

2.최초의 요청 여부 판단
-서블릿 컨테이너에서 해당 요청이 최초의 요청인지 아닌지 판단한다.

3.서블릿 객체 생성
-서블릿 컨테이너에서 최초의 요청에 해당해서 서블릿을 메모리에 로딩하고 객체를 생성

4.init() 메소드 실행
-객체가 생성된 다음 호출되는 메소드 Servlet 인터페이스에 선언되어잇고 고 아래에 구현되어있다.
-서블릿 객체의 초기화 작업이 구현되어있다.

5.service() 메소드 실행
-서블릿의 요청 순서에 상관없이 요청이 있을때 마다 실행
-실제 서블릿에서 처리해야 되는 내용이 구현되어있다.

API

-서블릿을 구현할 때 해당 API만 상속하면 웹에서 동작하는 기본조건 갖추는게 가능

-모든 서블릿은 HttpServlet을 상속 받아야 한다.(아래의 상속 구조를 보자)

GenericServlet클래스와 HttpServlet 클래스의 차이점은 둘 다 추상클래스이지만
service() 메소드가 HttpServlet에서는 구현되어있다

-servlet 프로젝트 생성

-매핑(URI별칭) = URI 경로를 설정 하고(웹에서 보았을 때 서블릿이 보이면 안됌.)
-service방식으로 생성(어떤 방식이든 비즈니스 로직을 처리하고 응답문서를 생성 전송)

service는 service()를 오버라이드 한다.
클라이언트로부터의 요청이 올 때마다 다른 thread가 뜨면서 이 메소드를 실행.



프로젝트 만드는 이 부분 안넣으려다가.. 다른 사람도 보는 사람 있을까봐..
doget은 get방식으로 요청응답 할 때 dopost는 post방식으로 요청응답 할 때 사용된다

-기본적인 예제

-알아본 것
(1) 응답 할 때 일일히 찍기어려우니까 JSP를 이용한다
(2)예외에 대해서 웹에서 예외처리가 따로있으니 그 형식에 맞게끔 예외를 형성해서 ServletException 으로 예외처리 될 수 있게 던져라
(3) 자원의 생성과 종료를 느껴봐라

package org.zerock.myapp.servlet;

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;

import lombok.Cleanup;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Log4j2
@NoArgsConstructor

// 매핑되어있는 부분
@WebServlet("/Hello")
public class HelloServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		log.info("찍어라 제발");
		
		try {
		
		response.setCharacterEncoding("utf8");
		
		@Cleanup("close")
		PrintWriter out = response.getWriter();

		//하나씩 찍기는 불가능하다.
		out.print("<html><head></head><body>");
		out.print("<h1>Hello world.</h1>");
		out.print("</body></html>");
		
		
		out.flush();
		
		} catch(Exception e) {
			throw new ServletException(e);
		} // try-catch
		
	}// service
	
}// end class

Deployment Assembly에 Maven라이브러리가 빠졌는지 확인하세요
그거 때문에 오류가 났었습니다.
(deploy assembly는 임시 배포 공간을 마련하는 것/ Maven라이브러리가 없으면 여러 라이브러리 관리가 안되서 오류)

-라이프 사이클을 알아보는 예제

-ServletConfig 의 클래스를 찍어본다.
(1)라이프 사이클
-init 생성단계(자원객체를 얻는 부분)
-service 준비단계()
-destory 파괴단계 ()


package org.zerock.myapp.servlet;

import java.io.IOException;
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;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Log4j2
@NoArgsConstructor

// Servlet 의 Lifecycle과 그 callback 메소드를 이해하자!
//@WebServlet({ "/Lifecycle1", "/Lifecycle2" })//  --> web.xml에서 mapping을 해주기위해 주석처리
public class LifecycleServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	public void init(ServletConfig config) throws ServletException { // 사전처리작업
		log.trace("init({}) invoked.", config);
		
		// 요청처리에 필요한 각 종 자원객체의 획득(DataSource)
	}// init

	@Override
	public void destroy() {											// 사후처리작업
		log.trace("destroy({}) invoked.");
		
		// init 단계에서 획득했던 자원객체들을 해제하는 작업
	}// destroy

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) 
			throws ServletException, IOException {
		log.trace("service({},{}) invoked)",req,res);
		
		
	} // service

}// end class

(2)web.xml 을통해 servlet mapping

<?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_4_0.xsd"
  id="WebApp_ID" 
  version="4.0">

  <display-name>servlet01</display-name>

  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
  </welcome-file-list>

  <!-- Lifecycle servlet registration -->

  <servlet>
    <servlet-name>LifecycleServlet</servlet-name>
    <servlet-class>org.zerock.myapp.servlet.LifecycleServlet</servlet-class>
  </servlet>
 이 부분 매핑부분
  <servlet-mapping>
    <servlet-name>LifecycleServlet</servlet-name>
    <url-pattern>/Lifecycle1</url-pattern>
    <url-pattern>/Lifecycle2</url-pattern>
  </servlet-mapping>


</web-app>

어노테이션을 통해 매핑이 가능하지만 web.xml을 통해 매핑가능
web.xml을 통해 매핑하는게 좋은 점은 여러개의 서블릿을 한번에
정리되어있는 상태에서 확인 할 수 있기 때문에 유지보수성이 올라간다.

-@WebServlet에 대해서

(value = url-pattern / 별칭이다. 어노테이션 속성에는 별칭이 가능)
속성명을 기재하지 않으면 기본속성값에 들어간다.


package org.zerock.myapp.servlet;

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

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Log4j2
@NoArgsConstructor
			// name 생략가능	 url-pattern = { "/xxx", "/yyy" }
@WebServlet(name="MyServlet", value = { "/xxx", "/yyy" }) // 원소가 하나이면 중괄호 생략가능
public class HelloSevelet2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
       

	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		
		log.info("HelloServlet 요청");
		
	}// doGet

}// end class

-서블릿 응답처리 예제


package org.zerock.myapp.servlet;

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;

import lombok.Cleanup;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@Log4j2
@NoArgsConstructor

@WebServlet("/Response")
public class ResponseServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		log.trace("service(req, res) invoked");

		try {
          // MIME타입 설정
          response.setContentType("text/html; charset = UTF-8");

          // 자바 I/O
          //@Cleanup
          PrintWriter out = response.getWriter();

          try(out){
          // html 작성 및 출력 -> JSP에서 해야함
          out.print("<html><body>");
          out.print("ResponseServlet 요청성공");
          out.print("</body></html>");

          out.flush();

          } // try-with-resources

		}catch(Exception e) {
			throw new ServletException(e);
		}// try-catch 
		
	}// service
} //end class

-init(), destroy() 말고 다른 방법(안쓴다함 알고만 있자)

  • @PreDestroy과 destroy() 성능과 기능 차이가 없다. 규약에 따른 라이프사이클에 따라 destroy()를 사용하는 게 좋다.
  • 위의 문제의 해결 방법을 찾기 위해서 구글링해 보니 Java 9 부터 PostConstruct 가 deprecated
    그리고, maven 에 javax.annotation-api 를 추가해 주면 된다
@WebServlet("/PostPre")
public class PostPreServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		log.info("service(req,res) invoked");
		
	}// service

	@Override
	public void init(ServletConfig config) throws ServletException { // 사전처리작업
		log.trace("init({}) invoked.", config);
		
		// 요청처리에 필요한 각 종 자원객체의 획득(DataSource)
	}// init

	@Override
	public void destroy() {											// 사후처리작업
		log.trace("destroy({}) invoked.");
		
		// init 단계에서 획득했던 자원객체들을 해제하는 작업
	}// destroy
	
	
	@PostConstruct
	public void initMethod() {
		log.info("@PostConstruct");
	}// PostConstruct 
	//init과 성능과 기능 차이가 없다. 규약에 따른 라이프사이클에 따라 init을 사용하는 게 좋다.
	
	
	
	@PreDestroy
	public void clean() {
		log.info("@PreDestroy");
	}// PreDestory


}// end class

-요청 왔을때서블릿에서 파라미터 처리

해당 3개에 대해 예제를 통해 확인했다.


postman이라는 것 사용

  • Postman은 개발한 API를 테스트하고, 테스트 결과를 공유하여 API 개발의 생산성을 높여주는 플랫폼
    = API 개발을 보다 빠르고 쉽게 구현 할 수 있도록 도와주며, 개발된 API를 테스트하여 문서화 또는 공유 할 수 있도록 도와 주는 플랫폼
  • 사용법은 get방식이면 params통해 쿼리스트링을 만들고 post방식이면 body를 통해 작성

-GET방식으로 요청을 받고 처리하는 과정

@WebServlet("/Login")
public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		log.info("서비스시작");
		
		try {	
			// Step.1 전송파라미터 획득
			String userid = request.getParameter("userid");
			String passwd = request.getParameter("passwd");
			
			log.info("\t userid: {} , passwd: {}" , userid, passwd);
			
			// Step.2 에코(echo)
			response.setContentType("text/html; charset=UTF-8");
			
			@Cleanup
			PrintWriter out = response.getWriter();
			
			out.print("<html><body>");
			out.print("아이디값: " +userid+"<br>");
			out.print("비밀번호값: " + passwd + "<br>" );
			out.print("</body></html>");
			
			out.flush();
			
		} catch (Exception e) {
			throw new ServletException(e);
		}// try-catch
	}// service

}// end class

postman을 사용한 결과 ▼

-POST방식으로 왔을때 처리방법

@WebServlet("/Sport")
public class SportServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		log.info("서비스시작");
		
		try {	
			// Step.1 전송파라미터 획득
			request.setCharacterEncoding("utf8");
			
			String[] sports = request.getParameterValues("sports");
			String gender = request.getParameter("gender");
			
			log.info("\t sports: {} , gender: {}" , sports, gender);
			
			// Step.2 에코(echo)
			response.setContentType("text/html; charset=UTF-8");
			
			@Cleanup
			PrintWriter out = response.getWriter();
			
			out.print("<html><body>");	
			out.print("<ul>");	
			
			for(String sport: sports) {
				out.print("<li>좋아하는 운동:" + sport +"</li>");			
			}//for
			
			out.print("<li> 성별 : " +gender+ "</li>");
			out.print("</ul>");				
			out.print("</body></html>");
			
			out.flush();
			
		} catch (Exception e) {
			throw new ServletException(e);
		}// try-catch
	}// service
}// end class

postman을 사용한 결과 ▼

1번은 해당 속성 자체가 없을 때 (아래의 예제에서는 속성이 없어도 빈문자열로 나옴)(생각을 가두지마라)
2번은 속성에 값이 없을 때 (아래의 예제에서는 속성이 없어도 빈문자열로 나옴)(생각을 가두지마라)
3번은 화면 나온대로

-몇 개인지 무슨 값으로 왔는지 모르는 요청값에 대하여 처리과정

		이부분이 중요하다. *실전에서는 한HTML 파일에 전송파라메터가 수십개다 
			// 제네릭 인터페이스다. 제네릭 요소를 포함한다.
			Enumeration<String> enu = request.getParameterNames();
@WebServlet("/Member")
public class MemberServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
      

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		log.trace("서비스 시작");
	
		try {
			
			// Step.1 전송파라미터 획득 
			// (주의) getParameter 메소드 수행전에,
			// 반드시 request.setCharacterEncoding("utf8") 로 인코딩 형식을 지정해줘라
			request.setCharacterEncoding("utf8");
			
			// 제네릭 인터페이스다. 제네릭 요소를 포함한다.
			Enumeration<String> enu = request.getParameterNames();
			
			
			// Step.2 응답화면 생성 및 전송 
			response.setContentType("text/html; charset=utf-8");
			
			@Cleanup
			PrintWriter out = response.getWriter();
			
			out.print("<html><body><ul>");
			while(enu.hasMoreElements()) {
				String paramName = enu.nextElement();
				String paramValue = request.getParameter(paramName);
				System.out.println( String.format("<li> %s : %s </li>" , paramName, paramValue));
				out.println( String.format("<li> %s : %s </li>" , paramName, paramValue));
			}// while
			
			out.print("</ul></body></html>");
			
			out.flush();
		} catch (Exception e) {
			throw new ServletException(e);
		}// try- catch
		
		
	}// service

끝!

profile
일단 흐자

0개의 댓글