본문 바로가기
교재 실습/자바 웹 개발 워크북

46. 데이터 보관소 (2)

by Jint 2022. 6. 16.

- 서블릿 배치와 <load-on-startup> 태그

AppInitServlet 클래스의 배치 정보를 작성한다.

DD파일(web.xml)에 다음과 같이 AppInitServlet 클래스의 배치 정보를 추가한다.

	<!-- 서블릿 선언 -->
	<servlet>
		<servlet-name>AppInitServlet</servlet-name>
		<servlet-class>spms.servlets.AppInitServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

컨텍스트 매개변수 태그 아래에 서블릿을 선언하는 태그를 추가했다. 여기서 주목할 사항은 <load-on-startup> 태그이다.

<load-on-startup>1</load-on-startup>

서블릿 객체는 클라이언트의 최초 요청시 생성된다. 클라이언트가 단 한 번도 요청하지 않으면 그 서블릿은 생성되지 않는다. 그러나 AppInitServlet 클래스처럼 다른 서블릿이 생성되기 전에 미리 준비 작업을 해야 하는 서블릿이라면, 클라이언트 요청이 없더라도 생성되어야 한다. <load-on-startup> 태그는 바로 그런 용도의 서블릿을 배치할 때 사용한다. 서블릿을 배치할 때 <load-on-startup>태그를 지정하면, 해당 서블릿은 웹 애플리케이션이 시작될 때 자동으로 생성된다. 이 태그의 값은 생성 순서이다. 생성 순서가 같은 서블릿이 여러 개 있을 경우, 먼저 선언된 것이 먼저 생성된다.

 

※ URL 매핑이 없는 서블릿

web.xml을 보면 AppInitServlet 클래스에 대해 <servlet-mapping> 태그가 없다. 즉 AppInitServlet 클래스에 대해 URL을 지정하지 않았다. 이렇게 URL 매핑 정보가 없는 서블릿은 클라이언트에서 실행을 요청할 수 없다. AppInitServlet 클래스처럼 다른 서블릿을 위해 준비 작업을 수행하는 서블릿인 경우 URL을 지정하지 않는다.

 

- ServletContext에 저장된 DB 커넥션 사용

이제 서블릿에서 DB 커넥션을 직접 준비할 필요가 없이 ServletContext 보관소에 저장된 DB 커넥션 객체를 꺼내 쓰면 된다.

회원 목록을 처리하는 MemberListServlet 클래스를 수정한다.

package spms.servlets;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;

import javax.servlet.GenericServlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import spms.vo.Member;

//UI 출력 코드를 제거하고, UI 생성 및 출력을 JSP에게 위임한다.
@WebServlet("/member/list")
public class MemberListServlet extends HttpServlet {//extends GenericServlet
	private static final long serialVersionUID = 1L;
	
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		//데이터베이스 관련 코드를 위한 try ~ catch 블록 (JDBC API 사용시 예외가 발생할 수 있기 때문)
		try {
			//JDBC 드라이버를 로딩하고 데이터베이스 연결 시 컨텍스트 초기화 매개변수에서 해당 정보 가져와 처리.
			ServletContext sc = this.getServletContext();
			//DB 커넥션 준비하는 코드 삭제
			/*Class.forName(sc.getInitParameter("driver"));
			conn = DriverManager.getConnection(
						sc.getInitParameter("url"),
						sc.getInitParameter("username"),
						sc.getInitParameter("password"));*/
			//ServletContext에 저장된 DB 커넥션 사용
			conn = (Connection) sc.getAttribute("conn");
			stmt = conn.createStatement();
			rs = stmt.executeQuery(
					"SELECT MNO,MNAME,EMAIL,CRE_DATE" + 
					" FROM MEMBERS" +
					" ORDER BY MNO ASC");
			response.setContentType("text/html; charset=UTF-8");
			/*
			PrintWriter out = response.getWriter();
			out.println("<html><head><title>회원목록</title></head>");
			out.println("<body><h1>회원목록</h1>");
			out.println("<p><a href='add'>신규 회원</a></p>");
			while(rs.next()) {
				out.println(
					rs.getInt("MNO") + "," +		//getInt(1)
					"<a href='update?no=" + rs.getInt("MNO") + "'>" + //회원 상세정보 출력 서블릿 URL 추가
					rs.getString("MNAME") + "</a>," +	//getString(2)
					rs.getString("EMAIL") + "," + 	//getString(3)
					//rs.getDate("CRE_DATE") + "<br>" //getDate(4)
					rs.getDate("CRE_DATE") + 
					"<a href='delete?no=" + rs.getInt("MNO") + "'>[삭제]</a><br>"
				);
			}
			out.println("</body></html>");
			*/
			ArrayList<Member> members = new ArrayList<Member>();
			//데이터베이스에서 회원 정보를 가져와 Member에 담는다.
			//그리고 Member객체를 ArrayList에 추가한다.
			while(rs.next()) {
				members.add(new Member().setNo(rs.getInt("MNO"))
										.setName(rs.getString("MNAME"))
										.setEmail(rs.getString("EMAIL"))
										.setCreatedDate(rs.getDate("CRE_DATE"))	);
			}
			//request에 회원 목록 데이터 보관한다.
			request.setAttribute("members", members);
			//JSP로 출력을 위임한다.
			RequestDispatcher rd = request.getRequestDispatcher("/member/MemberList.jsp");
			rd.include(request, response); //인클루딩
		} catch (Exception e) {
			//throw new ServletException(e);
			request.setAttribute("error", e); //예외 객체를 request에 보관
			RequestDispatcher rd = request.getRequestDispatcher("/Error.jsp");
			rd.forward(request, response); //포워딩
		} finally {
			try {if (rs != null) rs.close();} catch(Exception e) {}
			try {if (stmt != null) stmt.close();} catch(Exception e) {}
			//DB 커넥션 객체를 서블릿에서 관리하지 않으므로 삭제
			//try {if (conn != null) conn.close();} catch(Exception e) {}
		}
	}
}

MemberListServlet 클래스에서 DB 커넥션을 준비하는 코드를 삭제했다.

/*Class.forName(sc.getInitParameter("driver"));
conn = DriverManager.getConnection(
			sc.getInitParameter("url"),
			sc.getInitParameter("username"),
			sc.getInitParameter("password"));*/

DB 커넥션 객체를 서블릿에서 관리하지 않으므로 finally 블록에 있는 con.close() 명령문도 삭제한다.

//try {if (conn != null) conn.close();} catch(Exception e) {}

이제 서블릿은 ServletContext 보관소에서 DB 커넥션을 꺼낸다.

//ServletContext에 저장된 DB 커넥션 사용
conn = (Connection) sc.getAttribute("conn");

 

톰캣 서버를 재시작하고, http://localhost:9999/web05/member/list를 테스트한다(그림 1).

그림 1 (AppInitServlet 클래스의 자동 실행)

톰캣 서버의 콘솔창에 출력된 내용을 보면, AppInitServlet 클래스의 init() 메서드가 호출되었음을 확인할 수 있다. init() 메서드가 호출되었다는 것은 AppInitServlet 객체가 생성되었다는 의미이다. 웹 브라우저의 실행 결과는 이전과 같이 정상적으로 출력된다(그림 2).

그림 2 (MemberListServlet 클래스의 실행 결과)

나머지 서블릿(MemberAddServlet, MemberDeleteServlet, MemberUpdateServlet)들도 MemberListServlet 클래스처럼 변경했다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

'교재 실습 > 자바 웹 개발 워크북' 카테고리의 다른 글

48. 데이터 보관소 (4)  (0) 2022.06.19
47. 데이터 보관소 (3)  (0) 2022.06.18
45. 데이터 보관소 (1)  (0) 2022.06.15
44. 포워딩과 인클루딩 (2)  (0) 2022.06.14
43. 포워딩과 인클루딩 (1)  (0) 2022.06.13

댓글