본문 바로가기
국비학원

[국비지원] KH 정보교육원 78일차

by 도전하는 개발자 2022. 7. 20.

kh day 078

이번주 공지사항
 - 금주 목요일 능력단위평가 시행예정! 빠지지 마세요~

이번주 훈련방향
가. Servlet/JSP에서 Servlet 완료 
    -Servlet 핵심클래스, JDBC연동, DataSource 연동, Servlet 고급 (MVC패턴)
나. JSP 초급 완료
   - JSP 스크립팅 요소 (선언태그, Scriptlet, Expression 태그)
   - 웹 어플레케이션 예외처리
다. 진도가 빠르면 스프링을 위한 선행요소기술로 MyBatis SQL Mapper Framework 나간다

---

ServletContextListener 이어서 나가자!

 

@Log4j2
@NoArgsConstructor

//@WebListener // web.xml에서 해보자
public class ServletContextListenerImpl implements ServletContextListener {
	
	
//	public ServletContextListenerImpl() {
//		log.trace("Default Constructor invoked.");
//		
//	} // default constructor

	@Override
	public void contextInitialized(ServletContextEvent e)  { 
		log.trace("contextInitialized({}) invoked.", e);
		
		ServletContext sc = e.getServletContext();
		
		// web.xml에 등록된 컨텍스트 파라미터 획득
		String driver = sc.getInitParameter("driver");
		String jdbcUrl = sc.getInitParameter("jdbcUrl");
		String user = sc.getInitParameter("user");
		String pass = sc.getInitParameter("pass");
				
		log.info("\t+ driver : {}", driver);
		log.info("\t+ jdbcUrl : {}", jdbcUrl);
		log.info("\t+ user : {}", user);
		log.info("\t+ pass : {}", pass);	
		
		// Application scope에 올리자!
		sc.setAttribute("driver", driver);
		sc.setAttribute("jdbcUrl", jdbcUrl);
		sc.setAttribute("user", user);
		sc.setAttribute("pass", pass);
		
		} // contextInitialized

	@Override
    public void contextDestroyed(ServletContextEvent e)  { 
    	log.trace("contextDestroyed({}) invoked.", e);
    	
		ServletContext sc = e.getServletContext();    
		
		// 파괴될 때 내리자! 
		sc.removeAttribute("driver");
		sc.removeAttribute("jdbcUrl");
		sc.removeAttribute("user");
		sc.removeAttribute("pass");
    } // contextDestroyed
	
} // end class


ServletContextListenerImpl.java

WAS 실행시키고 로그 확인
Q) 생성자가 언제 호출되나?  
-> WAS가 완전히 올라오기 전에 호출된다 (웹 어플리케이션이 디플로이 되기 전에 호출)
-> 결론 : 리스너 객체는 WAS 안의 웹 어플리케이션들이 올라오기 전에 먼저 생성된다.

WAS를 내리고 로그확인
Q) contextDestroyed가 언제 호출되나?
-> WAS가 완전히 내려가기 전에 호출된다
-> 따라서 자원객체 해제를 여기에다가 해준다.

* 어노테이션 말고 web.xml 파일에 리스너를 등록해보자
  web.xml에 <listener> 태그를 사용하여 등록한다. 
  주의할 점은 <context-param> 태그 다음에 지정해야 된다.




* 서블릿을 위한 파라미터는 이닛파람
  컨텍스트를 위한 파라미터는 컨텍스트파람
  컨텍스트 나오면 웹 어플리케이션을 떠올리세요

 - 서버 올렸을때 콘솔창

 - 서버 내렷을때 콘솔창

--

 

@Log4j2
@NoArgsConstructor

@WebServlet("/TTT")
public class TTTServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	private Connection conn;

	// 1. App.Scope에 공유되어 있는 JDBC 연결정보를 이용해서,
	// JDBC Connection 1개를 생성하여, 필드로 저장
	@Override
	public void init(ServletConfig config) throws ServletException {
		log.trace("init({}) invoked.", config);
		super.init(config); // 지우면 큰일! 앞에다 넣어주자
		
		try { // JDBC Connection을 얻으라
			
			ServletContext sc = config.getServletContext();	
			
			// 리스너에서 올린 JDBC 연결정보 값을 얻어서 필드에 저장하자
			String driver = (String) sc.getAttribute("driver");
			String jdbcUrl = (String) sc.getAttribute("jdbcUrl");
			String user = (String) sc.getAttribute("user");
			String pass = (String) sc.getAttribute("pass"); 
			
			// 잘 받아서 저장했는지 찍어서 확인해보자			
			log.info("\t+ dirver : {}", driver);
			log.info("\t+ jdbcUrl : {}", jdbcUrl);
			log.info("\t+ user : {}", user);
			log.info("\t+ pass : {}", pass);
			
			// JDBC programming
			Class.forName(driver);
			this.conn = DriverManager.getConnection(jdbcUrl, user, pass);
			log.info("\t+ this.conn : {}", this.conn);
			
			// 연결객체가 null인지 여부 검증 (2가지 방법)
			Objects.requireNonNull(this.conn); 
			// assert this.conn != null;
			
		} catch (Exception e) {
			throw new ServletException(e);
		} // try-catch
		
	} // init 
       
	// 2. 필드에 저장되어 있는 JDBC Connection을 이용하여 현재 날짜와 시간을 응답문서로 출력
    @Override
	protected void service(HttpServletRequest req, HttpServletResponse res) 
			throws ServletException, IOException {
    	log.trace("revice(req, res) invoked.");
    	
    	String now = null;
    	
		try { // 현재날짜와 시간정보를 얻는 SQL을 수행하고 응답문서로 출력
			String sql = "Select to_char(current_date, 'yyyy/mm/dd - hh24:mi:ss') AS now From dual";
			PreparedStatement pstmt = conn.prepareStatement(sql);
			ResultSet rs = pstmt.executeQuery();
			
			if (rs.next()) {
				now = rs.getString("now");
			} // if
			
			// 응답문서생성
			res.setContentType("text/html; charset=utf8");
			@Cleanup
			PrintWriter out = res.getWriter();
			
			out.println("<h1>" + now + "</h1>");
			
			out.flush();
			
		} catch (Exception e) {
			throw new IOException(e);
		} // try-catch
    	    	
	} // service

    // 3. 필드에 저장되어있는 JDBC Connection 자원을 해제
	@Override
	public void destroy() {
		log.trace("destroy() invoked.");
		
		try { // 필드에 있는 JDBC Connection 해제
			// 롬복 @Cleanup은 메소드 블록 안에서만 쓸 수 있으므로 사용불가
			// try-with-resources는 final이어야 쓸 수 있으므로 사용불가
			if(this.conn != null && !this.conn.isClosed()) {
				this.conn.close();
			} // if 
		} catch(Exception e) {;;}		
		
		// super.destroy(); // 얘는 지워도 됨	
	} // destroy

} // end class


TTTServlet.java - 강사님의 예제다

* studyproject의 JDBCExample 1~7 다시 한번 확인해봐야한다

 - 서버 올렸을때 콘솔창

- 서버 내렷을때 콘솔창


---

4. Filter API

클라이언트인 웹 브라우저에서 서블릿으로 요청하면, 웹 컴포넌트인 서블릿이 요청을 받아서 작업을 처리하고 결과 HTML 형식으로 작성하여 웹 브라우저에게 응답 처리한다. 이때, 서블릿이 요청 받기 전과 결과를 웹 브라우저에게 응답하기 전에 특정 작업을 수행할 수 있도록 Filter API를 사용할 수 있다. 

즉, 웹 컴포넌트가 실행되기 전의 선처리(pre-processing) 작업과 응답되기 전의 후처리(post-processing) 작업을 수행하는 API이다. 다수의 Filter를 체인(Chain)처럼 묶어서 적용시킬 수도 있으며, 선처리 작업의 필터를 요청필터(Request Filter)라고 하고 후처리 작업의 필터를 응답필터(Response Filter)라고 한다. 선처리 작업의 대표적인 적용 예는 한글인코딩 및 보안관련 작업 등이고, 후처리 작업은 압축 및 데이터 변환작업 등이다. 

다음은 Filter를 적용한 아키텍처이다.



---

필터를 생성하자!

 

 

@Log4j2
@NoArgsConstructor

// @WebFilter("/*") // 이렇게 쓰면 어떤 servlet이던 이 필터를 통과함
// @WebFilter({ "/Lifecycle1", "/Lifecycle2" }) 
// 이렇게 쓰면 /Lifecycle1, /Lifecycle2만 이 필터를 통과함
// web.xml에서 수동으로 등록해보자!

public class MyFilter 
	extends HttpFilter 
	implements Filter {
       
//	public MyFilter() {
//        super(); // 부모 클래스의 생성자를 가장 먼저 호출해야한다.
//        log.trace("Default Constructor invoked.");
//    } // default constructor

    
    @Override
	public void init(FilterConfig config) throws ServletException {
		log.trace("init({}) invoked.", config);
	} // init


    @Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
			throws IOException, ServletException {		
		log.trace("doFilter(req, res, {}) invoked.", chain);
		
		// 1. 요청에 대한 선처리 (Pre-processing)
		log.info("\t+ Pre-processing for request");
		
//		boolean isValid = false;
//		
//		if(!isValid) {
//			res.setContentType("text/html; charset=utf8");
//			@Cleanup
//			PrintWriter out = res.getWriter();
//			out.println("<h1> Bad request. </h1>");
//			out.flush();
//			return;
//		} // if		
		
		// 요청(request)에 대한 선처리와, 응답(response)에 대한 후처리를 제어하는 호출
    	// chain 객체를 이용해 request, response를 넘긴다
		chain.doFilter(req, res); 

		// 2. 요청에 대한 후처리 (Post-processing)
		log.info("\t+ Post-processing for response");
	} // doFilter


    @Override
	public void destroy() {
		log.trace("destroy() invoked.");
	} // destroy
    
} // end class


MyFilter.java
- 서버를 올리고 호출로그를 확인해보자


선처리 로그가 먼저 나오고
후처리 로그가 나중에 나온다

---

이번엔 어노테이션 말고 직접 web.xml에 등록해보자



---

* 자바 언어 1권은 마치고 나머지는 백앤드에 몰입해보세요
백엔드는 교재랑 예제를 반복해서 보고 해보세요

---

5. url-pattern 개요
서블릿 맵핑과 필터 맵핑은 web.xml 파일 및 어노테이션을 사용하여 설정한다. 
이때 공통적으로 사용하는 값이 url-pattern이다. 
url-pattern은 웹 브라우저에서 클라이언트가 요청하는 URL 값의 패턴에 따라서, 
서버의 어떤 웹 컴포넌트가 실행될지를 결정하는 방법이다

1) 디렉토리 패턴



2) 확장자 패턴

 

 

@Log4j2
@NoArgsConstructor

@WebServlet("*.do")
public class ExtensionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

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

} // end class


ExtensionServlet.java

---

* getInitParameter()와 getAttribute() 차이가 뭔가요?

1) request.getInitParameter(전송파라미터이름) 
 - 지정된 이름의 전송파라미터 값을 획득 (문자열)

2) 공유데이터 영역에 접근할 수 있는 객체.getAttribute(공유속성이름)
 - ServletContext.getAttribute("name"); 
   : App.scope의 name 공유 속성값을 획득
 - HttpSession.getAttribute("name"); 
   : Sess.scope의 name 공유 속성값을 획득
 - HttpServletRequest.getAttribute("name"); 
   : Req.scope의 name 공유 속성값을 획득
 - page.getAttribute("name"); 
   : Page.scope의 name 공유 속성값을 획득