servletContext
: 톰캣이 동작을 실행하는 순간 공간이 생성되고 종료되기 전까지 계속 저장하기때문에 가장 수명이 길다.
HttpSession
: 브라우저 - 서버 연결.
브라우저를 켜놓고 작업하면 그동안 존재.
너무 오랫동안 반응이 없으면 session을 종료시킨다.(타임아웃)
servletRequest
: 매 요청마다 생성, 응답전까지 유지됨
주소를 쳤을 때(요청), 브라우저로 돌아오는 것(응답)
포워드, 인클루드 서블릿끼리 데이터 공유할 때 적합
jspContext
: jsp파일 페이지를 실행하는 동안 존재. 가장 적은 수명
servlet context를 활용하면 connection 객체를 생성 후 공유해서 쓸 수 있다.
Servlet Context
web.xml
:AppInitServlet 추가
<?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_3_1.xsd" version="3.1">
<display-name>_12_JDBCServlet_ServletInitParam</display-name>
<!-- 컨텍스트 초기화 매개변수(모든 서블릿에서 공유가능) -->
<context-param>
<param-name>driver</param-name>
<param-value>com.mysql.cj.jdbc.Driver</param-value>
</context-param>
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost/studydb?serverTimezone=UTC</param-value>
</context-param>
<context-param>
<param-name>username</param-name>
<param-value>study</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>study</param-value>
</context-param>
<!-- 서블릿 선언 주소와 매핑하지 않는다. 외부에 의해서 호출되지 않고 시작하자마자 실행되는 서블릿이기 때문이다. -->
<!-- 주소와 매핑을 안해서 브라우저 요청에 의해서 호출되는 것이 아니라
웹어플리케이션이 시작하면 최초로 실행되는 서블릿이다. -->
<servlet>
<servlet-name>AppInitServlet</servlet-name>
<servlet-class>spms.servlets.AppInitServlet</servlet-class>
<!-- 시작하면 1순위로 올려라. 1순위가 여러개면 위에서부터 아래 순서대로 호출 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Servlet 2.4이전에는 '컨텍스트 초기화 매개변수' 다음에 위치해야 한다
2.4 이상부터는 순서에 관계 없다 -->
<!-- 필터 설정 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>spms.filters.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 모든 주소에 필터를 적용 -->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!-- 들어오는 모든 것에 이 필터를 적용 (특정 주소만 주면 그 주소만 ) -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
AppInitServlet
: 가장 먼저 시작될 것이고, init()메서드를 실행할 것이다.
package spms.servlets;
import java.sql.Connection;
import java.sql.DriverManager;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@SuppressWarnings("serial")
public class AppInitServlet extends HttpServlet {
// 웹 어플리케이션 시작시 DB접속 객체를 만들어서 ServletContext 영역에 공유한다
@Override
public void init() throws ServletException {
System.out.println("AppInitServlet 준비...");
try {
ServletContext sc = this.getServletContext();
String driver = sc.getInitParameter("driver");
String url = sc.getInitParameter("url");
String id = sc.getInitParameter("id");
String pwd = sc.getInitParameter("password");
Class.forName(driver);
Connection conn = DriverManager.getConnection(url,id,pwd);
// servlet context 영역에 conn 객체를 저장
// 이곳에 저장하면 웹 어플리케이션 가동 내내 모든 서블릿/jsp에서 사용가능하다.
sc.setAttribute("conn", conn);
} catch(Exception e){
e.printStackTrace();
}
}
// 웹 어플리케이션 종료시 DB접속 객체가 연결되어 있다면 종료한다
@Override
public void destroy() {
System.out.println("AppInitServlet 마무리...");
super.destroy();
ServletContext sc = this.getServletContext();
Connection conn = (Connection)sc.getAttribute("conn");
try {
// 객체가 존재하고, 닫히지 않았다면 연결 종료
if(conn != null && conn.isClosed()==false) {
conn.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
MemberListServlet
직접 db에 연결할 필요 없이 공유하여 사용
package spms.servlets;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
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;
@SuppressWarnings("serial")
@WebServlet("/member/list")
public class MemberListServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Connection conn = null; // MySQL 연결담당
Statement stmt = null; // SQL문 담당
ResultSet rs = null; // SELECT문의 결과 담당
final String sqlSelect = "SELECT mno,mname,email,cre_date" + "\r\n" +
"FROM members" + "\r\n" +
"ORDER BY mno ASC";
// Servelet Context 영역에 공유한 conn 객체를 가져와서 사용하겠다
ServletContext sc = this.getServletContext();
conn = (Connection)sc.getAttribute("conn");
try {
stmt = conn.createStatement();
rs = stmt.executeQuery(sqlSelect);
resp.setContentType("text/html; charset=UTF-8"); // 먼저 호출
/* members테이블의 전체 인원을 저장하기 위해
* ArrayList를 사용한다
* */
ArrayList<Member> members = new ArrayList<Member>();
// DB로부터 members테이블의 정보를 가져와서 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")));
}
// jsp에 전달하기 위해 request의 공유 공간에 저장한다
req.setAttribute("members", members);
// jsp로 request를 전달한다
RequestDispatcher rd =
req.getRequestDispatcher("/member/MemberList.jsp");
rd.include(req, resp);
/* jsp로 전달(위임)하는 방식 2가지
* 1) forward : 제어권을 아예 넘겨준다(네가 알아서 처리해라)
* 2) include : 실행할 동안 제어권을 줬다가 처리가 끝나면 다시 넘겨 받는다
* (마지막 확인은 내가 처리한다)
* */
}catch(Exception e) {
//throw new ServletException(e);
req.setAttribute("error", e);
RequestDispatcher rd = req.getRequestDispatcher("/Error.jsp");
rd.forward(req, resp);
}finally {
try {
if(rs!=null)
rs.close();
}catch(Exception e) {
e.printStackTrace();
}
try {
if(stmt!=null)
stmt.close();
}catch(Exception e) {
e.printStackTrace();
}
//여기서 connection을 닫아버리면 list에서 한번 쓰고 닫아 버리는 것이므로 없애줘야 한다.
/* try {
if(conn!=null)
conn.close();
}catch(Exception e) {
e.printStackTrace();
}
*/
}
}
}
HttpSession
LoginServlet
package spms.servlets;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/auth/login")
@SuppressWarnings("serial")
public class LoginServlet extends HttpServlet {
// 주소로 요청이 들어오면 login form을 보여준다.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// "사용자에게 화면을 전송해줘" jsp로 넘긴다
RequestDispatcher rd = req.getRequestDispatcher("/auth/LogInForm.jsp");
rd.forward(req, resp);
}
}
LogInForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<body>
<h2>사용자 로그인</h2>
<form action = "login" method="post">
이메일 : <input type="text" name="email"><br/>
암호 : <input type="password" name="password"><br/>
<input type="submit" value="로그인"><br/>
</form>
</body>
</html>
LogInFail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- 2초 후에 login 화면으로 이동 -->
<meta http-equiv="Refresh" content="2;url=login">
<title>로그인 실패</title>
</head>
<body>
<p>
로그인 실패입니다<br/>
이메일 또는 암호가 일치하지 않습니다<br/>
잠시 후에 다시 로그인 화면으로 갑니다
</p>
</body>
</html>
LoginServlet
로그인 폼의 method=post이므로 doPost 메서드 추가
해당 id가 있으면 vo객체에 저장 후 session객체에 저장 => 타임아웃이 걸리기 전까지 로그인상태로 공유객체가 존재
package spms.servlets;
import java.io.IOException;
import java.sql.*;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
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 javax.servlet.http.HttpSession;
import spms.vo.Member;
@WebServlet("/auth/login")
@SuppressWarnings("serial")
public class LogInServlet extends HttpServlet {
/* 주소로 접근을 하면
* 로그인 폼을 보여준다
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// "사용자에게 화면을 전송해줘" jsp로 넘긴다
RequestDispatcher rd =
req.getRequestDispatcher("/auth/LogInForm.jsp");
rd.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Connection conn= null;
PreparedStatement stmt = null;
ResultSet rs = null;
String sqlLogIn = "SELECT mname,email " + "\r\n" +
"FROM members WHERE " + "\r\n" +
"email=? AND pwd=?";
try {
ServletContext sc = this.getServletContext();
conn = (Connection)sc.getAttribute("conn");
stmt = conn.prepareStatement(sqlLogIn);
stmt.setString(1, req.getParameter("email"));
stmt.setString(2, req.getParameter("password"));
rs = stmt.executeQuery();
// 결과가 존재한다면 '로그인 성공!'
if(rs.next()) { //rs.next()
Member member = new Member().setEmail(rs.getString("email"))
.setName(rs.getString("mname"));
// 로그인 정보를 session 공유 공간에 저장한다
// 세션 timeout 전까지는 보관된다
HttpSession session = req.getSession();
session.setAttribute("member", member);
/*
현재 경로가 /auth/login이므로
../ auth 경로 위로 이동하고
다시 거기서부터 /member/list로 진입
*/
resp.sendRedirect("../member/list");
}
// 로그인 실패
else {
RequestDispatcher rd = req.getRequestDispatcher("/auth/LogInFail.jsp");
rd.forward(req, resp);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
if(rs != null)
rs.close();
}catch(Exception e) {
e.printStackTrace();
}
try {
if(stmt != null)
stmt .close();
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
결과
로그인 성공시
로그인 실패시
Header.jsp
로그아웃 추가
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="spms.vo.Member" %>
<%
Member member = (Member)session.getAttribute("member");
%>
<div style="background-color:#00008b; color:white; height:20px; padding:5px;">
SPMS(Simple Project Management System)
<span style="float:right;">
<%=member.getName() %>
<a style="color:white;" href="<%=request.getContextPath() %>/auth/logout">
로그아웃
</a>
</span>
</div>
LogOutServlet
package spms.servlets;
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 javax.servlet.http.HttpSession;
@WebServlet("/auth/logout")
@SuppressWarnings("serial")
public class LogOutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.invalidate(); // 세션 저장 객체 무효화
// 다시 로그인 화면으로 이동
resp.sendRedirect("login");
}
}
결과
Action tag
jsp:useBean 이용하여 request 공간에서 객체 가져오기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 페이지 지시자 추가. java의 import와 동일 -->
<%@ page import="spms.vo.Member" %>
<%@ page import="java.util.ArrayList" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 목록</title>
</head>
<body>
<jsp:include page="/Header.jsp" />
<h1>회원 목록</h1>
<p><a href='add'>신규 회원</a></p>
<!-- request공간에서 members이름의 객체를 가져와라 -->
<jsp:useBean id="members"
scope="request"
class="java.util.ArrayList"
type="java.util.ArrayList<spms.vo.Member>"/>
<%
/*
ArrayList<Member> members =
(ArrayList<Member>)request.getAttribute("members");
*/
for(Member member : members){
%>
<%=member.getNo() %>,
<a href='update?no=<%=member.getNo() %>'>
<%=member.getName() %>
</a>,
<%=member.getEmail() %>,
<%=member.getCreatedDate() %>
<a href='delete?no=<%=member.getNo() %>'>
[삭제]
</a>
<br/>
<%
}
%>
<jsp:include page="/Tail.jsp" />
</body>
</html>