[Servlet] 서블릿 필터 & 리스너

sang·2024년 3월 5일

Attribute

서블릿 속성
세가지 서블릿 API 클래스에 저장되는 객체

  • ServletContext 객체
  • HttpSession 객체
  • HttpServletRequest 객체

setAttribute(String name, Object value): 속성 바인딩하기
getAttribute(String name): 속성 가져오기
removeAttribute(String name): 속성 제거하기

Scope

서블릿 스코프
서블릿 API에 바인딩된 속성에 대한 접근 범위

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

pro10/src/sec01/ex01/SetAttribute.java

package sec01.ex01;
...

@WebServlet("/set")
public class SetAttribute extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();
    
    String ctxMesg = "context에 바인딩됩니다.";
    String sesMesg = "session에 바인딩됩니다.";
    String reqMesg = "request에 바인딩됩니다.";
 
    ServletContext ctx = getServletContext();
    HttpSession session = request.getSession();
    /* 바인딩 */
    ctx.setAttribute("context", ctxMesg);
    session.setAttribute("session", sesMesg);
    request.setAttribute("request", reqMesg);
    out.print("바인딩을 수행합니다.");
  }
}

scope 확인 실습

pro10/src/sec01/ex01/GetAttribute.java

package sec01.ex01;
...

@WebServlet("/get")
public class GetAttribute extends HttpServlet{
  public void doGet(HttpServletRequest request , HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();
    
    ServletContext ctx = getServletContext();
    HttpSession sess = request.getSession();
    /* 바인딩 값 가져오기 */
    String ctxMesg = (String)ctx.getAttribute("context");
    String sesMesg = (String)sess.getAttribute("session"); //브라우저마다 다른 세션
    String reqMesg = (String)request.getAttribute("request"); //바인딩 요청 '/set'과 다른 새로운 요청
    
    out.print("context값 : " + ctxMesg + "<br>");
    out.print("session값 : " + sesMesg + "<br>"); //다른 브라우저로 요청 시 null
    out.print("request값 : " + reqMesg + "<br>"); //null
  }
}



요청 매핑명에 따른 서블릿 실행

pro10/src/sec02/ex01/TestServlet1.java
전부 (디렉터리 이름 + 매핑 이름) 일치

선택(요청)한 서블릿 실행

package sec02.ex01;
...

@WebServlet("/first/test")
public class TestServlet1 extends HttpServlet {
  public 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();
    String url = request.getRequestURL().toString();
    String mapping = request.getServletPath();
    String uri = request.getRequestURI();
    
    out.println("<html>");
    out.println("<head>");
    out.println("<title>Test Servlet1</title>");
    out.println("</head>");
    out.println("<body bgcolor='green'>");
    out.println("<b>TestServlet1</b><br>");
    out.println("<b>컨텍스트 이름 : "+ context + "</b><br>");
    out.println("<b>전체 경로 : "+ url +"<b><br>");
    out.println("<b>매핑 이름 : "+mapping+"<b><br>");
    out.println("<b>URI : " + uri + "<b>");
    out.println("</body>");
    out.println("</html>");
    out.close();
  }
}

pro10/src/sec02/ex01/TestServlet2.java
디렉터리 이름 일치

해당 디렉터리 서블릿 실행

package sec02.ex01;
...

@WebServlet("/first/*")
public class TestServlet2 extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws SevletException, IOException {
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();
    
    String context = request.getContextPath();
    String url = request.getRequestURL().toString();
    String mapping = request.getServletPath();
    String uri = request.getRequestURI();
    
    out.println("<html>");
    out.println("<head>");
    out.println("<title>Test Servlet2</title>");
    out.println("</head>");
    out.println("<body bgcolor='yellow'>");
    out.println("<b>TestServlet2</b><br>");
    out.println("<b>컨텍스트 이름 : "+ context + "</b><br>");
    out.println("<b>전체 경로 : "+ url +"<b><br>");
    out.println("<b>매핑 이름 : "+mapping+"<b><br>");
    out.println("<b>URI : " + uri + "<b>");
    out.println("</body>");
    out.println("</html>");
    out.close();
  }
}

pro10/src/sec02/ex01/TestServlet3.java
확장자 일치

확장자명에 맞는 서블릿 실행

package sec02.ex01;
...

@WebServlet("*.do") /* 모든 요청: @WebServlet("/*") */
public class TestServlet2 extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws SevletException, IOException {
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();
    
    String context = request.getContextPath();
    String url = request.getRequestURL().toString();
    String mapping = request.getServletPath();
    String uri = request.getRequestURI();
    
    out.println("<html>");
    out.println("<head>");
    out.println("<title>Test Servlet2</title>");
    out.println("</head>");
    out.println("<body bgcolor='red'>");
    out.println("<b>TestServlet3</b><br>");
    out.println("<b>컨텍스트 이름 : "+ context + "</b><br>");
    out.println("<b>전체 경로 : "+ url +"<b><br>");
    out.println("<b>매핑 이름 : "+mapping+"<b><br>");
    out.println("<b>URI : " + uri + "<b>");
    out.println("</body>");
    out.println("</html>");
    out.close();
  }
}


Filter

브라우저에서 서블릿으로 요청/응답할 때 미리 관련된 작업을 처리하는 기능
필터 사용 시 모든 요청이 필터를 거침
서블릿에서 요청/응답 필터 기능은 동일한 필터가 수행함
각 서블릿에서 반복적으로 처리해야하는 작업에 사용
ex) 한글 인코딩

  • 요청 필터
    사용자 인증 및 권한 검사
    요청 시 요청 관련 로그 작업
    인코딩

  • 응답 필터
    응답 결과에 대한 암호화
    서비스 시간 측정


Filter API

  • javax.servlet.Filter
    컨테이너에서 호출하여 작업 수행

    • destroy(): 필터 소멸 시 호출 -> 종료
    • doFilter(): 요청/응답 시 호출 -> 기능 수행
    • init(): 필터 생성 시 호출 -> 초기화
  • javax.servlet.FilterConfig

  • javax.servlet.FilterChain

    • getFilterName(): 필터 이름 반환
    • getInitParameter(String name): 매개변수 name 값 반환
    • getServletContext(): 서블릿 컨텍스트 객체 반환

사용자 정의 Filter

필터를 각 요청에 맞게 적용하기 위해 필터 매핑 필요
Filter 인터페이스 구현 필수

필터 매핑 방법:

  • 애너테이션 이용
  • web.xml 설정

pro10/WebContent/login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인창</title>
</head>
<body>
  <form name="frmLogin" method="post" action="login" encType="utf-8">
      이름 :<input type="text" name="user_name"><br>
      비밀번호:<input type="password" name="user_pw"><br>
      <input type="submit" value="로그인">
      <input type="reset" value="다시입력">
  </form>
</body>
</html>

prro10/src /sec03/ex01/LoginTest.java

package sec03.ex01;
...
@WebServlet("/login")
public class LoginTest extends HttpServlet {
  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_name = request.getParameter("user_name");
    String user_pw = request.getParameter("user_pw");
    out.println("<html><body>");
    out.println("이름는 " + user_name +"<br>");
    out.println("비밀번호는 "+user_pw +"<br>");
    out.println("</body></html>");
  }
}

필터 생성

1. 패키지 -> Filter 메뉴
2. class name: 필터명(EncoderFilter) 입력
3. Filter mappings: 입력한 필터명 선택
4. Pattern: /*

요청 필터 기능 - 인코딩

pro10/src/sec03/ex01/EncoderFilter.java

package sec03.ex01;
...

@WebFilter("/*") //필터 애너테이션
public class EncoderFilter implements Filter {
  ServletContext context;
  public void init(FilterConfig fConfig) throws ServletException {
  System.out.println("utf-8 인코딩............");
    context = fConfig.getServletContext();
  }

  // 실제 필터 기능 구현 메소드
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException { 
    System.out.println("doFilter 호출");
    
    request.setCharacterEncoding( "utf-8" ); //인코딩
    
    String context= ((HttpServletRequest)request).getContextPath();
    String pathinfo = ((HttpServletRequest)request).getRequestURI();
    String realPath = request.getRealPath( pathinfo);
    
    String mesg = " Context 정보:" + context
                   + "\n URI 정보 : " + pathinfo
                   + "\n 물리적 경로: " + realPath;
    System.out.println(mesg);
    
    chain.doFilter( request, response ); //다음 필터로 전달
  }

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

응답 필터 기능 - 서비스 시간 측정

pro10/src/sec03/ex01/EncoderFilter.java

package sec03.ex01;
...

@WebFilter("/*") //필터 애너테이션
public class EncoderFilter implements Filter {
  ServletContext context;
  public void init(FilterConfig fConfig) throws ServletException {
  System.out.println("utf-8 인코딩............");
    context = fConfig.getServletContext();
  }

  // 실제 필터 기능 구현 메소드
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException { 
    System.out.println("doFilter 호출");
    
    request.setCharacterEncoding( "utf-8" ); //인코딩
    
    tring path = ((HttpServletRequest)request).getContextPath();
    String pathinfo = ((HttpServletRequest)request).getRequestURI();
    String realPath = request.getRealPath( pathinfo);
    
    String mesg = " Context 정보:" + context
          + "\n URI 정보 : " + pathinfo
          + "\n 물리적 경로: " + realPath;
    System.out.println(mesg);
    
    long begin = System.currentTimeMillis(); //요청 필터 - 요청 처리 전 시각
    chain.doFilter( request, response ); //다음 필터로 전달 (응답)
    long end = System.currentTimeMillis(); //응답 필터 - 요청 처리 후 시각
    System.out.println("작업 시간:"+(end-begin)+"ms");
  }

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


Listener

서블릿 리스너
서블릿에서 발생하는 이벤트에 대해 적절한 처리 수행
사용 시 애너테이션을 이용해 리스너 등록 필요 - HttpSessionBindingListener 제외

ServletContextAtributeListener

Context 객체 속성 이벤트 처리

  • attributeAdded()
  • atributeRemoved()
  • attributeReplaced()

ServletRequestListener

클라이언트의 요청 이벤트 처리

  • requestInitialized()
  • requestDestroyed()

ServletRequestAtributeLitener

요청 객체 속성 이벤트 처리

  • attributedAdded()
  • attributedRemoved()
  • attributedReplaced()

HttpSessionAttributeListener

세션 속성 이벤트 처리

  • attributeAdded
  • attributeRemoved
  • attributeReplaced

ServletContextListener

컨텍스트 객체 이벤트 처리

  • contextInitialized()
  • contextDestroyed()

HttpSessionActivationListener

세션 활성화/비활성화 이벤트 처리

  • sessionDidActivate()
  • sessionWillPassivate()

HttpSessionBindingListener

세션에 바인딩/언바인딩된 객체를 알려주는 이벤트 처리
리스너 등록 불필요

  • valueBound()
  • valueUnbound()

pro10/WebContent/login2.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인창</title>
</head>
<body>
  <form name="frmLogin" method="post" action="login" 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>

pro10/src/sec04/ex01/LoginTest.java

package sec04.ex02;
...
@WebServlet("/login")
public class LoginTest extends HttpServlet {
  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();
    
    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); }
    
    out.println("<head>");
    out.println("<script type='text/javascript'>");
    out.println("setTimeout('history.go(0);', 5000)"); //현재 접속자 수 5초마다 재요청
    out.println("</script>");
    out.println("</head>");
    out.println("<html><body>");
    out.println("아이디는 " + loginUser.user_id +"<br>");
    out.println("총 접속자수는"+LoginImpl.total_user +"<br>");
    out.println("</body></html>");
  }
}

pro10/src/sec04/ex01/LoginImpl.java

HttpSessionBindingListener 이벤트 리스너 구현

package sec04.ex01;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.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;
  }

  @Override
  // 세션 바인딩(저장) 시 접속자수 증가
  public void valueBound(HttpSessionBindingEvent arg0) {
    System.out.println("사용자 접속");
    ++total_user;
  }
  
  @Override
  // 세션 소멸 시 접속자수 감소
  public void valueUnbound(HttpSessionBindingEvent arg0) {
    System.out.println("사용자 접속 해제");
    total_user--;
  }
}

HttpSessionListener

세션 객체 이벤트 처리

  • sessionCreated()
  • sessionDestroyed()

pro10/src/sec04/ex02/LoginTest.java

package sec04.ex02;
...
@WebServlet("/login")
public class LoginTest extends HttpServlet {
  ServletContext context = null;
  List user_list = new ArrayList(); //접속자 아이디 리스트

  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding( "utf-8" );
    response.setContentType("text/html;charset=utf-8");
    context=getServletContext();
    PrintWriter out = response.getWriter();
    
    HttpSession session=request.getSession();
    String user_id = request.getParameter("user_id");
    String user_pw = request.getParameter("user_pw");
    
    /* 이벤트 핸들러 생성 & ServletContext 객체에 바인딩 */
    LoginImpl loginUser = new LoginImpl(user_id, user_pw);
    if(session.isNew()) { // 최초 로그인 시
      session.setAttribute("loginUser", loginUser);
      user_list.add(user_id); // 접속자 아이디를 리스트에 저장
      context.setAttribute("user_list",user_list); // context 객체 속성으로 저장
    }
    
    out.println("<html><body>");
    out.println("아이디는 " + loginUser.user_id +"<br>");
    out.println("총 접속자 수는" + LoginImpl.total_user +"<br><br>"); //세션 바인딩 이벤트 처리
    out.println("접속 아이디:<br>");

    List list = (ArrayList) context.getAttribute("user_list");
    for(int i = 0; i < list.size(); i++) {
      out.println(list.get(i)+"<br>"); //접속자 아이디 출력
    }
    out.println("<a href='logout?user_id="+user_id+"'>로그아웃 </a>"); //서블릿 로그아웃 처리
    out.println("</body></html>");
  }
}   

pro10/src/sec04/ex02/LogoutTest.java

package sec04.ex02;
...
@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 {
    request.setCharacterEncoding( "utf-8" );
    response.setContentType("text/html;charset=utf-8");
    context=getServletContext();
    PrintWriter out = response.getWriter();
    
    HttpSession session = request.getSession();
    String user_id = request.getParameter("user_id"); //로그아웃 할 아이디
    session.invalidate();
    
    /* 접속자 리스트에서 제거 */
    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>로그아웃했습니다.");
  }
}

pro10/src/sec04/ex02/LoginImpl.java

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;
  }

  @Override
  //세션 생성 처리
  public void sessionCreated(HttpSessionEvent arg0) {
    System.out.println("세션 생성");
    ++total_user; //접속자 수 증가
  }
  
  @Override
  //세션 소멸 처리
  public void sessionDestroyed(HttpSessionEvent arg0) {
    System.out.println("세션 소멸");
    --total_user; //접속
  }
}


*자바 웹을 다루는 기술

profile
CS 메모장

0개의 댓글