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

45. 데이터 보관소 (1)

by Jint 2022. 6. 15.

서블릿들이 서로 협력하여 작업을 수행할 때, 데이터를 공유하는 방법을 알아본다. 서블릿 기술은 데이터를 공유하기 위한 방안으로 4가지 종류의 데이터 보관소를 제공한다. 각각의 데이터 보관소는 공유 범위를 기준으로 구분된다(그림 1).

그림 1 (네 가지 객체 보관소)

① ServletContext 보관소는 웹 애플리케이션이 시작될 때 생성되어 웹 애플리케이션이 종료될 때까지 유지된다. 이 보관소에 데이터를 보관하면 웹 애플리케이션이 실행되는 동안에는 모든 서블릿이 사용할 수 있다. JSP에서는 application 변수를 통해 이 보관소를 참조할 수 있다.
② HttpSession 보관소는 클라이언트의 최초 요청 시 생성되어 브라우저를 닫을 때까지 유지된다. 보통 로그인할 때 이 보관소를 초기화하고, 로그아웃하면 이 보관소에 저장된 값들을 비운다. 따라서 이 보관소에 값을 보관하면 서블릿이나 JSP 페이지에 상관없이 로그아웃하기 전까지 계속 값을 유지할 수 있다. JSP에서는 session 변수를 통해 이 보관소를 참조할 수 있다.
③ ServletRequest 보관소는 클라이언트의 요청이 들어올 때 생성되어, 클라이언트에게 응답할 때까지 유지된다. 이 보관소는 포워딩이나 인클루딩하는 서블릿들 사이에서 값을 공유할 때 유용하다. JSP에서는 request 변수를 통해 이 보관소를 참조할 수 있다.
④ JspContext 보관소는 JSP 페이지를 실행하는 동안만 유지된다. JSP에서는 pageContext 변수를 통해 이 보관소를 참조할 수 있다.

 

보관소에 값을 넣고 빼내는 방법은 모두 같다. 다음의 메서드를 호출하여 값을 저장하고 조회할 수 있다.

보관소 객체.setAttribute(키, 값); //값 저장
보관소 객체.getAttribute(값);     //값 조회

보관소 객체의 사용법이 Map 객체의 put()과 get() 메서드와 유사하다. 실습을 통해 각 보관소의 사용법을 익혀 본다.

 

1. ServletContext의 활용

ServletContext 객체는 웹 애플리케이션이 시작될 때 생성되어, 웹 애플리케이션이 종료될 때까지 유지되는 객체다. 이 객체에 값을 보관하면 웹 애플리케이션이 종료될 때까지 모든 서블릿(JSP)이 이용할 수 있다. 즉 어떤 객체를 웹 애플리케이션이 실행되는 동안 모든 서블릿들과 공유하고 싶다면 이 보관소에 저장하면 된다.

 

지금까지 데이터베이스를 사용하는 서블릿들은 모두 호출될 때마다 데이터베이스 커넥션을 생성했다. 이 데이터베이스 커넥션 객체를 웹 애플리케이션이 시작될 때 생성하여 ServletContext에 저장하겠다. 이렇게 하면 데이터베이스를 이용하는 모든 서블릿은 ServletContext에서 DB 커넥션 객체를 얻을 수 있다.

 

- 공유 자원을 준비하는 서블릿 작성

먼저 웹 애플리케이션이 시작될 때 데이터베이스 커넥션 객체를 준비하는 서블릿을 작성한다. web05 프로젝트의 spms.servlets 패키지에 AppInitServlet 클래스를 생성한다. 물론 서블릿이 되어야 하기 때문에 HttpServlet을 상속받는다.

package spms.servlets;

import java.sql.Connection;
import java.sql.DriverManager;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

@SuppressWarnings("serial")
public class AppInitServlet extends HttpServlet {

	@Override
	public void init(ServletConfig config) throws ServletException {
		System.out.println("AppInitServlet 준비…");
		super.init(config);
		try {
			ServletContext sc = this.getServletContext();
			Class.forName(sc.getInitParameter("driver"));
			Connection conn = DriverManager.getConnection(
						sc.getInitParameter("url"),
						sc.getInitParameter("username"),
						sc.getInitParameter("password"));
			
			sc.setAttribute("conn", conn);
		} catch(Throwable e) {
			throw new ServletException(e);
		}
	}
	
	@Override
	public void destroy() {
		System.out.println("AppInitServlet 마무리...");
		super.destroy();
		Connection conn = (Connection)this.getServletContext().getAttribute("conn"); 
		try {
			if (conn != null && conn.isClosed() == false) {
				conn.close();
			}
		} catch (Exception e) {}
	}
	
}

지금까지 서블릿을 만들 때, service()를 직접 오버라이딩 하거나 또는 doGet(), doPost()를 오버라이딩 했다. 하지만, 이번 서블릿은 클라이언트에서 호출할 서블릿이 아니므로 이런 메서드들을 오버라이딩 하지 않는다. 대신 init() 메서드를 오버라이딩 하였다. init() 메서드는 서블릿 객체가 생성될 때 딱 한 번 호출되기 때문에 공유 자원을 준비하는 코드가 놓이기에 최적의 장소라 할 수 있다.

 

init() 메서드를 살펴보면 첫 번째 라인은 웹 애플리케이션이 시작될 때 이 메서드가 호출된다는 것을 확인하기 위한 콘솔창 출력문이다.

System.out.println("AppInitServlet 준비…");

init() 메서드의 두 번째 라인은 오버라이딩 하기 전의 메서드를 호출한다. 슈퍼 클래스 HttpServlet으로부터 상속받은 init()의 기능을 그대로 수행하겠다는 의미이다. 즉 상속받은 기능은 그대로 두고 새로운 작업을 추가하는 것이다.

super.init(config);

그리고 나머지 코드들은 데이터베이스 커넥션을 준비하는 명령어이다. 여기서 중요한 것은 데이터베이스 커넥션 객체를 준비한 다음, 모든 서블릿들이 사용할 수 있도록 ServletContext 객체에 저장하는 일이다.

sc.setAttribute("conn", conn);

"conn"이라는 키를 사용하여 저장했기 때문에 꺼낼 때도 이 키를 사용하여 꺼내면 된다.

 

destroy() 메서드는 서블릿이 언로드 될 때 호출된다. 서블릿이 언로드 되는 때는 관리자가 톰캣 서버를 종료하거나, 웹 애플리케이션이 종료하거나, 해당 서블릿을 다시 로딩할 때이다. AppInitServlet 클래스의 destroy() 메서드를 살펴보면 init() 메서드에서 준비했던 DB 커넥션 객체를 해제시키고 있다. 즉 데이터베이스와의 연결을 끊는 것이다.

if (conn != null && conn.isClosed() == false) {
	conn.close();
}

물론 destroy() 메서드가 호출되었음을 표시하기 위해 콘솔창으로 출력하는 것도 포함하였다.

System.out.println("AppInitServlet 마무리...");
super.destroy();

destroy() 메서드도 init() 메서드처럼 오버라이딩 되기 전의 상속받은 메서드를 호출함으로써 원래의 마무리 작업은 유효하게 처리할 것이다.

 

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

 

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

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

freelec.co.kr

댓글