BackEnd/Servlet

10장. 서블릿의 필터와 리스너 기능

제이드Jade 2022. 2. 18. 18:22

10.1 서블릿 속성과 스코프

<서블릿 속성>

: 세가지 서블릿 API 클래스 HttpServletRequest, ServletContext, HttpSession에 저장되는 객체(정보)

=> 세개 모두 ~Attribute()로 바인딩된 속성을 조작

 

<서블릿 스코프>

: 바인딩된 속성에 대한 접근 범위

  • HttpServletRequest =>리퀘스트 스코프(해당 요청/응답 에서만 접근 가능)
  • ServletContext => 애플리케이션 스코프(애플리케이션 전체 접근 가능)
  • HttpSession => 세션 스코프(브라우저에서만 접근 가능)

* 기능 : 로그인 상태 유지 기능, 장바구니 기능, MVC의 Model과 View의 데이터 전달 기능

 

 

10.2 서블릿의 여러 가지 URL 패턴

URL 패턴 : 서블릿의 매핑 이름, 반드시 /로 시작해야 함

 

URL 패턴 유형(우선순위 대로)

1) 정확히 일치 : ex. /first/test

2) 디렉터리만 일치 : ex. /first/*

3) 확장자만 일치 : ex. *.do

4) /* : 모든 요청 URL 패턴

 

getRequestURI, getRequestURL, getContextPath, getServletPath

 

 

10.3 Filter API

필터 : 요청 직후/응답 직전에 미리 여러가지 작업을 처리하는 기능

- 클라이언트-필터-서블릿

- 반복되는 작업을 필터에서 처리하면 편함(ex. 한글 인코딩)

 

용도에 따라 요청필터와 응답 필터가 있음(chain.doFilter() 전후로 나뉨)

- 요청 필터에서 주로 처리 : 사용자 인증 및 권한 검사 / 요청 관련 로그 작업 / 인코딩

- 응답 필터에서 주로 처리 : 응답 결과에 대한 암호화 작업 / 서비스 시간 측정

 

필터 관련 API

: Filter, FilterChain, FilterConfig

 

<Filter 인터페이스에 선언된 메서드>

- init() : 필터 생성 시 컨테이너에 의해 호출, 초기화 작업

- doFilter() : 요청/응답 시

- destroy() : 필터 소멸 시, 종료작업 수행

 

<FilterConfig의 메서드>

- getFilterName() : 필터 이름 반환

- getInitParameter(String name) : name에 대한 값 반환

- getServletContext() : 서블릿 컨텍스트 객체를 반환

 

<사용자 정의 Filter>

- 반드시 Filter 인터페이스를 구현

- 추상메서드 init(), doFilter(), destroy() 을 구현해줘야 함

- 필터 매핑 해줘야 함(@WebFilter("/*") )

 

New>Filter에서 필터 생성

chain.doFilter() 위쪽에 위치한 코드는 요청 필터, 아래에 위치한 코드는 응답 필터 기능 수행

@WebFilter("/*")
public class EncoderFilter implements Filter {

	ServletContext context;

	public void destroy() {
		System.out.println("destroy() 호출");
	}


	//브라우저에서 요청하면 호출
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		request.setCharacterEncoding("utf-8");
		System.out.println("Context 정보 : "+((HttpServletRequest)request).getContextPath()+
				"\n URI 정보 : "+((HttpServletRequest)request).getRequestURI()+"\n 물리적 경로 : "
				+request.getRealPath(((HttpServletRequest)request).getRequestURI()));
		long begin=System.currentTimeMillis();
		chain.doFilter(request, response); //다음 필터로 넘김. 밑에 코드는 응답 하면서 실행
		long end=System.currentTimeMillis();
		System.out.println("작업 시간 : "+(end-begin)+"ms");
	}

	//브라우저에 주소 입력하면 호출
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("init() 호출");

		context=fConfig.getServletContext();
	}

}

 

 

10.4 여러 가지 서블릿 관련 Listener API

 

서블릿 Listener : 서블릿에서 발생하는 이벤트에 대해 적절한 처리를 해줌

서블릿 관련 LIstener 추상 메서드 기능
ServletContextListener
  • contextInitialized()
  • contextDestroyed()
Context 객체의 생성/소멸 이벤트 발생 시 처리한다.
ServletContextAttributeListener
  • attributeAdded()
  • attributeRemoved()
  • attributeReplaced()
Context 객체에 속성 추가/제거/수정 이벤트 발생 시 처리한다.
HttpSessionListener
  • sessionCreated()
  • sessionDestroyed()
Session 객체의 생성/소멸 이벤트 발생 시 처리한다.
HttpSessionAttributeListener
  • attributeAdded()
  • attributeRemoved()
  • attributeReplaced()
Session 객체에 속성 추가/제거/수정 이벤트 발생 시 처리한다.
HttpSessionActivationListener
  • sessionDidActivate()
  • sessionWillPassivate()
세션의 활성화/비활성화 이벤트 발생 시 처리한다.
HttpSessionBindingListener
  • valueBound()
  • valueUnbound()
* 세션에 바인딩/언바인딩된 객체를 알려 주는 이벤트 발생 시 처리한다.
* 리스너 중 유일하게 핸들러에 @WebListener 등록하지 않아도 된다.
ServletRequestListener
  • requestInitialized()
  • requestDestroyed()
클라이언트의 Request 이벤트 발생 시 처리한다.
ServletRequestAttributeListener
  • attributeAdded()
  • attributeRemoved()
  • attributeReplaced()
Request 객체에 속성 추가/제거/수정 이벤트 발생 시 처리한다.

 

HttpSessionBindingListener 이용해 로그인 접속자수 표시

LoginTest 서블릿

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()) {
    session.setAttribute("loginUser", loginUser); //loginImpl의 valueBound메서드 호출
}

String data="안녕하세요! <br> 로그인하셨습니다<br><br>"; 
data+="<head><script type='text/javascript'> setTimeout('history.go(0);',5000)</script></head>"; //history.go(0)==새로고침, setTimeout(코드,시간)
data+="<html><body>";				 
data+="아이디 : "+loginUser.user_id;
data+="<br>총 접속자수 : "+LoginImpl.total_user;
data+="</html></body>";

out.print(data);

 

LoginImpl > 리스너 구현하는 클래스(이벤트 핸들러)

public class LoginImpl implements HttpSessionBindingListener {
//loginImpl 객체 자체를 바인딩/언바인딩 해야 이벤트 발생하는 것임
String user_id;
String user_pw;
static int total_user=0;


public LoginImpl(String user_id, String user_pw) {
    this.user_id = user_id;
    this.user_pw = user_pw;
}

@Override
public void valueBound(HttpSessionBindingEvent event) { //세션에 바인딩 시 메서드 호출됨
    System.out.println("사용자 접속");
    ++total_user;
}

@Override
public void valueUnbound(HttpSessionBindingEvent event) { //세션에 소멸 시 메서드 호출 됨
    System.out.println("사용자 접속 해제");
    total_user--;
}

 

 

HttpSessionListener 이용해 로그인 접속자수&모든 로그인ID 표시하고 로그아웃 구현

- 세션 생성

- 컨텍스트에 사용자 아이디 저장

- 로그아웃 구현 : 세션 소멸시키기 이벤트

** HttpSessionListener에서는 기본 생성자를 꼭 만들어야 함에 주의

 

- loginImpl

@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("세션 생성");
		++total_user;
    }

    public void sessionDestroyed(HttpSessionEvent se)  {  //세션 소멸 시 이벤트
		System.out.println("세션 소멸");
		total_user--;
    }
	
}

- loginTest

@WebServlet("/login")
public class LoginTest extends HttpServlet {

	List user_list=new ArrayList(); //do메서드에 넣지 않는다
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request,response);
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request,response);
	}
	protected void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out=response.getWriter();
		
		HttpSession session=request.getSession();
		ServletContext ctx=getServletContext();
		
		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()) {
			user_list.add(user_id);
			ctx.setAttribute("user_list", user_list);
		}
		
		String data="안녕하세요! <br> 로그인하셨습니다<br><br>";
		data+="<html><body>";
		data+="아이디 : "+loginUser.user_id;
		data+="<br>총 접속자수 : "+LoginImpl.total_user;
		data+="<br>접속 아이디 : <br>";
		
		List list=(ArrayList)ctx.getAttribute("user_list");
		
		for(int i=0;i<list.size();i++) 
		{
			data+=list.get(i)+"<br>";
		}
		data+="<a href='logout?user_id="+user_id+"'>로그아웃</a></html></body>";
		
		out.print(data);
		}
	}

-logoutTest

@WebServlet("/logout")
public class LogoutTest 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();
		
		HttpSession session=request.getSession();
		ServletContext ctx=getServletContext();
		
		String user_id=request.getParameter("user_id");
		
		session.invalidate();
		
		List list=(List) ctx.getAttribute("user_list");
		ctx.removeAttribute("user_list");
		list.remove(user_id);
		ctx.setAttribute("user_list", list);
		
		String data="로그아웃했습니다<br><br>"; 
		
		out.print(data);
	}

}