열심히 끝까지

디바이스 융합 자바(Java) day48 - 리스너(Listener)(초기화 매개변수),필터(Filter) 본문

디바이스 융합 자바(Java)기반 풀스택 개발자 양성과정(수업내용)

디바이스 융합 자바(Java) day48 - 리스너(Listener)(초기화 매개변수),필터(Filter)

노유림 2022. 8. 18. 17:31

[오늘 수업]

V : 커스텀태그, JSTL, EL식
M : JDBC, MYSQL, ORACLE, TRANJECTION, 리스너
C : 필터

>> 오늘의 수업 : 리스너, 필터
    - 서블릿 상위 클래스 아래에 리스너, 필터 존재
    - 각자 모델과 컨트롤러에서 사용(주로 활용되는 공간)

[리스너] : 초기화 매개변수(키워드)
- JSTL에서 사용한 경험 존재
- 현업, 포폴에서 많이 등장하는 개념은 아니라서 설명을 안했지만 
  다른 사람의 소스코드에도 나오기 때문에 익혀 놓을 것
   >> 질문도 나왔고해서 포함 시켜서 설명
- 조심해야 하는 단어 : "초기화 매개변수"
   >> 초기화 매개변수 
             : 프로그램을 동작을 시키거나 수행을 시킬 때, 꼭 필요한 정보가 존재(ex ) url, id, pw, db...(경로))               
               일반적으로는 소스코드에 "하드코딩"을 해왔음
               그런데 이런 정보들을 프로젝트 중간에 쉽게 변경될 수 있음
               이런 "정보"들을 별도로 "환경설정 파일(.xml)"에 보관하는 것이 일반적

               "환경설정 파일에 담긴 정보 == 초기화 매개변수"
              
             : 하드코딩 
                     ex ) 
                           int N=5; // 이 자리에 데이터의 개수를 입력
                           int[] data=new int[N];
                           for(int i=0; i<data.length; i++){
                                   syso(data[i]);
                           }

             : 보관은 다음과 같이 진행
               <init-param></init-param>  ->  ServletConfig 
                       : 해당 서블릿에서 사용 가능
               <context-param></context-param>  ->  ServletContext 
                       : 동일한 웹 어플리케이션 내에서 모든 서블릿에서 사용 가능
                       : 내부에 
                          <param-name>(이름)</param-name>
                          <param-value>(값)</param-value>
                        존재

          ( 블로그나 교재에 잘 안나오는 내용, 꼭 기억할 것 )
             ★ .xml  ->  @ (어노테이션,애너테이션)
                  : .xml파일을 어노테이션으로 바꾸는 일이 잦아짐
                  : 대부분이 .xml의 상위버전인 어노테이션으로 바꾸려는 것

         >> 과거에 설정파일은 xml에 들어잇엇는데 스프링이라는 웹 프레임워크 등장
              여기에서 채택하는 것이 어노테이션
              그래서 xml을 어노테이션으로 바꾸는 작업을 자주 함
              그걸 신입한테 시킨다고..ㅋㅋㅋ(작대기)

                  : 컴파일을 시도할 때, 
                      > 어떤 어노테이션(오버라이딩)이 있었는지 별도로 기억
                      > 특정 기능을 수행할 때, 
                         그에 해당하는 어노테이션이 있는지 가장 먼저 확인(=동적바인딩)
                         or
                         특정 순간에 반응(모니터링, 감지)하는 어노테이션 존재, 
                         해당 순간에 기능을 수행
                         
       >> WEB-INF 하위에 web.xml 파일 생성(꼭 그렇게 해야 함)
                 : web.xml 은 WEB-INF 폴더 하위에 존재
                 : 서버(tomcat : 톰캣)가 시작될 때 참조하는 환경설정 파일
                 : 최상위 태그로 "<web-app></web-app>" 사용  

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app>

	<!-- 기본적인 값 명시 -->
	<context-param>
		<param-name>name</param-name>
		<param-value>timo</param-value>
	</context-param>
	<context-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
		<!-- 인코딩을 UTF-8이라고 명시 / 인코딩 정보 입력 가능 -->
	</context-param>

	<!-- errorPage="error/error(이 부분 이름 바뀌면서 명시).jsp"라고 명시 했었음 -->
	<!-- 초기화 매개변수에서는 이렇게 명시 -->
	<error-page> <!-- 고쳐야하는 에러가 발생 시, 출력하는 방법 -->
		<exception-type>java.lang.Throwable</exception-type>
		<location>/error/error.jsp</location>
	</error-page>
	
	<error-page> <!-- 없는 페이지, 삭제된 게시글 등... -->
		<exception-type>404</exception-type>
		<location>/error/error404.jsp</location>
	</error-page>

</web-app>

>> 초기설정------------------------------------------------

package test;

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;

/**
 * Servlet implementation class TestServlet2
 */
@WebServlet("/apple")
public class TestServlet2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public TestServlet2() {
        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("apple");
	}

	/**
	 * @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);
	}

}

>> 따로 설정--------------------------------------------------

package test;

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

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

/**
 * Servlet implementation class TestServlet
 */
@WebServlet(urlPatterns={"/TestServlet"}, initParams={@WebInitParam(name="msg1",value="HELLO"),@WebInitParam(name="msg2",value=":D")})
// URL DEFAULT 설정 제거하고 직접 설정
// (urlPatterns={"/TestServlet"}, initParams={})
// initParam에 대한 이야기 >  이름, 변수를 저장 가능, 여러 개 가능
// ("/TestServlet") 단축되어 있음(Default 값)
// URL 매핑 설정이란?
// 브라우저에서 어떤 URL 요청에 대해 해당 서블릿을 서비스할지를 결정
public class TestServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public TestServlet() { // 기본생성자가 꼭 필요해서 기본생성자 제공
        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("test");
		doPost(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 글자 깨짐을 방지하기 위한 용도
		response.setContentType("text/html; charset=UTF-8");
		
		PrintWriter out = response.getWriter();
		
		out.println("<HTML>");
		out.println("<BODY>");
		
		// 서블릿 초기화 매개변수 -> @(어노테이션)
		out.println("<H1>초기화 매개변수 - @(어노테이션 사용)</H1>");
		out.println("msg1 : " + getInitParameter("msg1")+"<BR>");
		out.println("msg2 : " + getInitParameter("msg2"));
		
		out.println("<HR>");
		
		// 웹 어플리케이션 초기화 매개변수 -> web.xml
		out.println("name : " + getServletContext().getInitParameter("name") + "<BR>");
		out.println("encoding : " + getServletContext().getInitParameter("encoding"));
		
		out.println("</BODY>");
		out.println("</HTML>");
	}

}
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>초기화 매개변수 설정</title>
</head>
<body>

<!-- Servlet에서 꺼내보기 -->
name : <%=getServletContext().getInitParameter("name") %><br>
<!-- 어플리케이션에서 꺼내보기 -->
encoding : <%=application.getInitParameter("encoding") %>

</body>
</html>

 

>> 본격적인 [리스너]

   >> 리스너
        : 특수한 형태의 서블릿
        : 특정 동작을 모니터링(감지)해서 기능을 자동 호출
        : 어떤 동작을 감지할 지 선택할 수 있음
       >> 초기화 매개변수와 엮겨서 사용

※ 전체 흐름
    1. 톰캣 서버 시작
    2. web.xml 참조 
          + 프로젝트에 작성되어 있는 @(어노테이션) 스캔
    3. @WebListener(리스너 클래스)가 발견되면 
        어떤 동작에 대해 해당 리스너 클래스를 자동호출해야하는지 별도로 기억

    1. xxx.jsp를 요청
    2. 톰캣 시작됨
    3. 리스너가 모니터링 하다가 자동 호출됨 -> xxx.jsp 요청 완료(브라우저에 출력)

1. Servlet context
  1-1. Lifecycle : 웹 서버 어플리케이션 컨텍스트가 시작되거나 소멸되는 것을 감지
  1-2. Change to attribute : 어플리케이션 스코프에 속성 추가, 변경 설정 가능
2. HTTP session
  2-1 Lifecycle : 세션이 생성됬니? 장바구니 새로 만들어!
  2-2 Change to attribute : session 스코프에 속성 변경 설정 가능
3. Servlet request
  3-1 Lifecycle
  3-2 Change to attribute

package test;

public class Member {
	private String name;
	private int score;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	public Member(String name, int score) {
		this.name=name;
		this.score=score;
	}
}
package test;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * Application Lifecycle Listener implementation class TestListener
 *
 */
@WebListener
public class TestListener implements ServletContextListener {

    /**
     * Default constructor. 
     */
    public TestListener() {
        // TODO Auto-generated constructor stub
    }

	/**
     * @see ServletContextListener#contextDestroyed(ServletContextEvent)
     */
    public void contextDestroyed(ServletContextEvent sce)  { 
         // TODO Auto-generated method stub
    }

	/**
     * @see ServletContextListener#contextInitialized(ServletContextEvent)
     */
    public void contextInitialized(ServletContextEvent sce)  { 
         ServletContext sc=sce.getServletContext();
         Member member=new Member("홍길동",100);
         sc.setAttribute("member", member);
         // sc.setAttribute("객체명", 객체);
         System.out.println("TestListener : contextInitialized() : 톰캣 시작이 감지됨");
    }
	
}

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

${member.name}학생 : ${member.score} 점

</body>
</html>

 

★★★우리들의 포트폴리오에서는...★★★
    - 샘플 데이터
    - 웹 크롤링

 

>> 크롤링해오기..?
class A{ // 웹 크롤링
         public ArrayList<VO> funcA(){
         }
}

class Listener{
         A a = new A();
         ArrayList<VO> datas=a.funcA();

         DAO dao = new DAO();
         if(dao.funcB(datas)){
                syso("성공");
         }
         else{
                syso("실패");
         }
}

class DAO{
         public boolean funcB(ArrayList<VO> datas){
                 // DB에 크롤링한 데이터를 INSERT하는 로직
         }
}

 

-> 1) 리스너 클래스의 메서드가 동작
   => 웹 크롤링 수행
   => DB에 저장

class A{ // 웹 크롤링
   public ArrayList<VO> funcA(){
   }
}

class Listener{
   DAO dao=new DAO();
   if(dao.funcC()){
      // 크롤링 데이터가 이미 존재함
      return;
   }

   A a=new A();
   ArrayList<VO> datas=a.funcA();

   if(dao.funcB(datas)){
      syso("성공");
   }
   else{
      syso("실패");
   }
}

class DAO{
   public boolean funcB(ArrayList<VO> datas){
      // DB에 크롤링한 데이터를 INSERT하는 로직
      // +) 혹시, 크롤링 데이터가 이미 있다면, false 반환
   }
   public boolean funcC(){
      // 크롤링 데이터가 존재하는지안하는지 알려주는 메서드
   }
}

1. Listener에 집어넣어서 Crawling
2. 애초에 funcB() 자체에 크롤링 데이터 유무를 확인하는 로직 존재

항상 좋은 코드는 없고 본인이 왜 그렇게 썼는지 이유를 설명할 수 있으면 ok!


[필터] : 정수기, 에어컨
     - 특정 요청에만 반응하는 특수한 형태의 서블릿
     - 기존의 요청정보를 탈취(뺏어옴)해서 자신의 작업을 처리하고(doFilter())
       아무 일도 없었던 것 처럼 원래대로 요청정보를 다시 진행시킴
     - 사용처
      : 인증
        인가
        로깅
        데이터 변환
        인코딩
        국제화(다국어 처리, 번역)에 사용


★★★우리들의 포트폴리오에서는...★★★
    - C
    - 인코딩 관려 설정

※ 필터 동작 순서
   1. 웹서버(톰캣) 시작
   2. 필터 클래스 init() 동작
   3. 사용자가 서비스를 자유롭게 이용
   4. 그러던 중에, 특정 요청을 수행하면,
   5. 필터가 반응 : doFilter()

 

package test;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;

/**
 * Servlet Filter implementation class EncFilter
 */
@WebFilter("*.jsp")
public class EncFilter extends HttpFilter implements Filter {

	private String encoding;
    /**
     * Default constructor. 
     */
    public EncFilter() {
    	super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see Filter#destroy()
	 */
	public void destroy() {
		// TODO Auto-generated method stub
	}

	/**
	 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// TODO Auto-generated method stub
		// place your code here
		// request.setCharacterEncoding("UTF-8"); 
		// 하드코딩 : 잘 안바뀌는 부분은 하드코딩으로 해 놓기도 한다.(하지만 교육중이니..)
		request.setCharacterEncoding(this.encoding); 
		// web.xml(환경설정파일)에 저장되어 있던 초기화 매개변수로 교체
		System.out.println("doFilter() 동작 완료");

		// pass the request along the filter chain
		chain.doFilter(request, response);
	}

	/**
	 * @see Filter#init(FilterConfig)
	 */
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("필터 클래스 최초 초기화 완료");
		
		this.encoding=fConfig.getServletContext().getInitParameter("encoding");
	}

}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<form action="NewFile2.jsp" method="post">
	<input type="text" name="msg">
	<input type="submit" value="확인">
</form>

</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>msg : ${param.msg}</h1>
<!-- param.msg로 안하고 msg로 하면 에러 안나고 null값으로만 들어감 - 놓치지 말 것! -->

</body>
</html>