서블릿의 속성(Attribute)이란?
HttpServletRequest, HttpSession, ServletContext 객체에 바인딩되는 객체(정보)
서블릿의 스코프(Scope)란?
서블릿에 바인딩된 속성이 유지되는 범위(라이프사이클)
request
스코프 : 한번 넘어간 페이지/서블릿까지 사용 가능session
스코프 : 같은 브라우저(세션id) 에서 사용 가능application
스코프 : 서버가 종료될 때까지 사용 가능URL 패턴이란?
서블릿에 매핑할 때 사용되는 이름, /(슬래시) 로 시작해야 함
정확한 이름까지 일치, 디렉터리 일치, 확장자 이름 일치 등 여러 가지 URL 패턴을 지정 가능
getContextPath()
: 컨텍스트 이름 (/프로젝트(컨텍스트)명)
getRequestURL()
: 전체 URL (http://~) (StringBuffer 타입, 캐스팅 필요)
getServletPath()
: 해당 서블릿에 매핑한 URL 패턴
getRequestURI()
: URI (/컨텍스트명~)
package sec02.ex01;
import java.io.IOException;
import java.io.PrintWriter;
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("/first/test")
public class TestServlet1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
// 컨텍스트 이름 가져오기 : /~
String context = request.getContextPath();
// URL 이름 가져오기 (StringBuffer 타입, 캐스팅 필요) : http://~
String url = request.getRequestURL().toString();
// 서블릿 매핑 이름 가져오기 : 지정한 URL 패턴
String mapping = request.getServletPath();
// URI 가져오기 : /컨텍스트명부터~
String uri = request.getRequestURI();
out.print("<html><head><title>Test Servlet1</title></head>");
out.print("<body bgcolor='lightgreen'>");
out.print("TestServlet1 입니다.<br>");
out.print("컨텍스트 이름 : "+context+"<br>");
out.print("전체 경로 : "+url+"<br>");
out.print("매핑 이름 : "+mapping+"<br>");
out.print("URI : "+uri+"<br>");
out.print("</body></html>");
}
}
URL패턴을 각각 다르게 설정하여 동일한 내용의 서블릿을 2개 더 작성
TestServlet1
: /first/test
TestServlet2
: /first/*
TestServlet3
: *.do
💻 실행 결과
요청 URL에 따라 다른 서블릿으로 연결되는 것을 확인 가능
필터(Filter)란?
브라우저에서 서블릿에 요청하거나 서블릿에서 브라우저에 응답할 때, 요청/응답 전에 여러 가지 작업을 먼저 처리하는 기능
ex) 한글 인코딩 등 여러 서블릿에서 공통으로 수행하는 작업을 필터로 만들어 사용
Filter 인터페이스의 메서드
doFilter()
: 필터의 주요 기능을 수행, 실행할 조건이 되면 자동으로 호출됨
init()
: 필터 생성 시 초기화 작업
destroy()
: 필터 소멸 시 종료 작업
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="login" method="post" encType="utf-8">
이름 : <input type="text" name="user_id"><br>
비밀번호 : <input type="password" name="user_pw"><br>
<input type="submit" value="로그인">
<input type="reset" value="다시 입력">
</form>
</body>
</html>
package sec03.ex01;
import java.io.IOException;
import java.io.PrintWriter;
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("/login")
public class LoginTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 필터 처리할 부분 주석 처리하기
// request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
String user_id = request.getParameter("user_id");
String user_pw = request.getParameter("user_pw");
out.print("<html><body>");
out.print("아이디 : "+user_id+"<br>");
out.print("비밀번호 : "+user_pw+"<br>");
out.print("</body></html>");
}
}
package sec03.ex01;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebFilter("/*") // 모든 요청이 해당 필터를 거치게 함
public class EncoderFilter extends HttpFilter implements Filter {
ServletContext context;
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("utf-8 인코딩......");
// 서블릿 컨텍스트 가져오기 (FilterConfig의 메서드)
context = fConfig.getServletContext();
}
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
/* 참고 : 필터가 HttpFilter 클래스를 상속받으면
doFilter() 의 매개변수를 HttpServletRequest, HttpServletResponse 타입으로
가질 수 있음 (원래는 매개변수 타입에 Http 없음) */
System.out.println("doFilter 호출");
request.setCharacterEncoding("utf-8");
String context = request.getContextPath();
String pathinfo = request.getRequestURI();
String realPath = this.context.getRealPath(pathinfo);
String mesg = " Context 정보 : " + context + "\n URI 정보 : " + pathinfo + "\n 물리적 경로 : " + realPath;
System.out.println(mesg);
long begin = System.currentTimeMillis();
// doFilter() : 다음 필터가 있으면 다음 필터 실행하고, 없으면 서블릿으로 넘김
// 이 위의 내용은 요청 필터 기능 (요청을 서블릿에서 처리하기 전 실행)
chain.doFilter(request, response);
// 이 아래의 내용은 응답 필터 기능 (서블릿에서 처리 후 웹 브라우저에 응답하기 전 실행)
long end = System.currentTimeMillis();
System.out.println("작업 시간 : "+(end-begin)+"ms");
}
public void destroy() {
System.out.println("destroy 호출");
}
}
💻 실행 결과
필터를 적용하면 요청받은 데이터가 utf-8로 인코딩됨. 아래는 콘솔 출력 내용
doFilter 호출
Context 정보 : /pro10
URI 정보 : /pro10/login.html
물리적 경로 : D:\JYJ\.metadata\.plugins\
org.eclipse.wst.server.core\tmp0\wtpwebapps\pro10\pro10\login.html
작업 시간 : 1ms
이벤트 리스너(Listener)란?
특정 이벤트가 발생했을 때 그 이벤트를 감지하고 처리하는 인터페이스
이벤트 처리에 필요한 리스너 인터페이스를 구현해 사용
HttpSessionBindingListener
는 HttpSession
에 속성이 바인딩/언바인딩 될 때 작동하는 리스너로, 이 인터페이스를 구현한 클래스의 객체가 HttpSession
에 바인딩/언바인딩될 때 자동으로 valueBound()
/ valueUnbound()
메서드가 호출된다. 해당 메서드는 추상 메서드로, 구현 시 내용을 필수로 작성해야 한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="login" method="post" encType="utf-8">
아이디 : <input type="text" name="user_id"><br>
비밀번호 : <input type="password" name="user_pw"><br>
<input type="submit" value="로그인">
<input type="reset" value="다시 입력">
</form>
</body>
</html>
package sec04.ex01;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
// 리스너로 사용하기 위해 HttpSessionBindingListener 인터페이스 구현
public class LoginImpl implements HttpSessionBindingListener {
String user_id;
String user_pw;
// 접속 중인 사용자 수를 카운트할 변수
static int total_user = 0;
// 생성자
public LoginImpl() {}
public LoginImpl(String user_id, String user_pw) {
this.user_id = user_id;
this.user_pw = user_pw;
}
// valueBound(), valueUnbound() 추상 메서드 오버라이딩
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("사용자 접속");
++total_user;
}
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("사용자 접속 해제");
total_user--;
}
}
이 리스너 객체를 속성으로 가진 세션은 이 객체를 바인딩/언바인딩할 때 valueBound()
/ valueUnbound()
메서드를 자동으로 호출함
package sec04.ex01;
import java.io.IOException;
import java.io.PrintWriter;
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("/login")
public class LoginTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
String user_id = request.getParameter("user_id");
String user_pw = request.getParameter("user_pw");
// 이벤트 리스너 기능을 할 LoginImpl 객체 생성
LoginImpl loginUser = new LoginImpl(user_id, user_pw);
if(session.isNew()) {
// 새로 생성된 세션이면,
// 새로 생성한 이벤트 리스너(HttpSessionBindingListener)를 세션에 바인딩
session.setAttribute("loginUser", loginUser);
// 리스너가 바인딩되어서 LoginImpl 클래스 안의 valueBound() 메서드가 자동 호출됨
}
out.print("<html><head>");
// 자바스크립트로 5초마다 자동 새로고침(요청) 설정하기
out.print("<script type='text/javascript'>");
out.print("setTimeOut('history.go(0);', 5000)");
out.print("</script>");
out.print("<body>");
out.print("아이디 : "+loginUser.user_id+"<br>");
out.print("총 접속자 수 : "+LoginImpl.total_user+"<br>");
out.print("</body></html>");
}
}
💻 실행 결과
다른 브라우저에서 로그인하면 접속자 수(리스너의 static 변수) 가 1씩 늘어남, 같은 브라우저에서 다시 로그인하면 아이디는 변경되지만 세션에 바인딩이 일어나지 않으므로 접속자 수는 그대로 유지됨
HttpSessionListener
는 HttpSession
가 생성/소멸될 때 작동하는 리스너로, 이 인터페이스를 구현한 클래스의 객체가 HttpSession
에 등록되면 해당 세션이 생성, 소멸될 때 자동으로 sessionCreated()
/ sessionDestroyed()
메서드가 호출된다. 해당 메서드는 추상 메서드로, 구현 시 내용을 필수로 작성해야 한다.
위 실습에서는 로그인 후 접속자 수가 증가하는 것만 확인했지만 이번에는 로그인 후 접속자 목록 표시, 로그아웃하면 접속자 수가 감소하는 것까지 확인
package sec04.ex02;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class LoginImpl implements HttpSessionListener {
String user_id;
String user_pw;
static int total_user = 0;
public LoginImpl() {
}
public LoginImpl(String user_id, String user_pw) {
this.user_id = user_id;
this.user_pw = user_pw;
}
public void sessionCreated(HttpSessionEvent se) {
System.out.println("세션 생성");
// 세션 생성 시 접속자 수 1 증가
++total_user;
}
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("세션 소멸");
// 세션 소멸 시 접속자 수 1 감소
--total_user;
}
}
package sec04.ex02;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
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;
@WebServlet("/login")
public class LoginTest extends HttpServlet {
ServletContext context = null;
/* 주의 : 서블릿 클래스의 멤버 변수는 클라이언트끼리 같은 저장 공간을 공유함 */
List user_list = new ArrayList();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
context = getServletContext();
HttpSession session = request.getSession();
String user_id = request.getParameter("user_id");
String user_pw = request.getParameter("user_pw");
LoginImpl loginUser = new LoginImpl(user_id, user_pw);
if(session.isNew()) {
// 새로 생성된 세션이면(최초 로그인이면)
// 새로 생성한 이벤트 리스너(HttpSessionListener)를 세션에 바인딩
// 리스너가 등록되고 세션이 새로 생성되면,
// LoginImpl 클래스 안의 sessionCreated() 메서드가 자동 호출됨
session.setAttribute("loginUser", loginUser);
// 접속자 id를 ArrayList에 저장하고 ServletContext의 속성으로 저장
user_list.add(user_id);
context.setAttribute("user_list", user_list);
}
out.print("<html><body>");
out.print("아이디 : "+loginUser.user_id+"<br>");
// 세션에 바인딩 이벤트 처리 후 총 접속자 수 표시
out.print("총 접속자 수 : "+LoginImpl.total_user+"<br><br>");
out.print("접속 아이디 : <br>");
// ServletContext에 바인딩한 ArrayList 가져와 출력하기
List list = (ArrayList) context.getAttribute("user_list");
for(int i=0; i<list.size(); i++) {
out.println(list.get(i)+"<br>");
}
out.print("<a href='logout?user_id="+user_id+"'>로그아웃</a>");
out.print("</body></html>");
}
}
package sec04.ex02;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
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;
@WebServlet("/logout")
public class LogoutTest extends HttpServlet {
ServletContext context;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doHandle(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doHandle(request, response);
}
private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
context = getServletContext();
HttpSession session = request.getSession();
// 요청에서 넘어온 삭제할 아이디 값
String user_id = request.getParameter("user_id");
// 세션 소멸시키기
session.invalidate();
// 서블릿 컨텍스트에서 유저 목록 가져와 삭제하고 다시 set하기
List user_list = (ArrayList) context.getAttribute("user_list");
user_list.remove(user_id);
context.removeAttribute("user_list");
context.setAttribute("user_list", user_list);
out.println("<br>로그아웃했습니다.");
}
}
💻 실행 결과
다른 브라우저에서 로그인하면 접속자 수가 변경되고 로그아웃 시 아이디가 접속자 목록에서 삭제되며 접속자 수가 변경된 것을 확인 가능