8장. 서블릿 확장 API 사용하기

8.1 서블릿 포워드 기능 사용하기

웹 애플리케이션은 여러 기능을 담당하는 서블릿끼리 또는 서블릿과 JSP를 연동해서 작업하는 경우가 많음

포워드 : 하나의 서블릿에서 다른 서블릿이나 JSP와 연동하는 방법

 

<포워드 기능이 사용되는 용도>

  • 요청에 대한 추가 작업을 다른 서블릿에게 수행하게 함
  • 요청에 포함된 정보를 다른 서블릿이나 JSP와 공유
  • 요청에 정보를 포함시켜 다른 서블릿에 전달
  • 모델2 개발 시 서블릿에서 JSP로 데이터를 전달

 

 

8.2 서블릿의 여러 가지 포워드 방법

1. redirect 방법

   ㄴ HttpServletResponse 객체의 sendRedirect("포워드할 서블릿/JSP") 메서드 이용

   ㄴ 웹 브라우저에 재요청하는 방식

2. Refresh 방법

   ㄴ HttpServletResponse 객체의 addHeader()메서드 이용

       >> response.addHeader("Refresh", 경과시간(초); url="요청할 서블릿/JSP");

   ㄴ 웹 브라우저에 재요청하는 방식

3. location 방법

   ㄴ 자바스크립트 location 객체의 href 속성 이용 >> location.href='요청할 서블릿/JSP"

   ㄴ 자바스크립트에서 재요청하는 방식

4. dispatch 방법

   ㄴ 일반적으로 포워딩 기능을 지칭

   ㄴ 서블릿이 직접 요청하는 방식

   ㄴ RequestDispatcher 클래스의 forward() 메서드 이용 

       >>> RequestDispatcher dis=request.getRequestDispatcher("포워드할 서블릿/JSP");

              dis.forward(request,response);

 

  • 1,2,3은 서블릿 > 웹브라우저 > 다른 서블릿/JSP에게 요청

 >>> 첫번째 서블릿으로 요청(주소창에 첫번째 서블릿매핑 이름 으로 치면) 자동으로 두번째 서블릿 주소와 함께 화면이 나옴

  • 4는 서블릿>다른 서블릿 (직접)

 >>> 웹 브라우저의 주소가 변경되지 않음, 즉 클라이언트에서는 포워드가 진행되었는지 알 수 없음

 

<1. redirect를 이용한 포워딩>

  • 요청 > 첫 번째 서블릿은 sendRedirect()를 이용해 두 번째 서블릿을 브라우저를 통해 요청 > 브라우저는 sendRedirect() 메서드가 지정한 두번째 서블릿을 다시 요청
  • 다른 서블릿을 호출하면서 원하는 데이터를 GET 방식으로 전달할 수 있음(첫번째에서 서블릿 뒤에 ?로 구분하여 name=value입력하고 두번째에서 getParameter로 받음)

 

<2. refresh 포워딩>

  • 요청 > 첫번째 서블릿이 addHeader()로 두 번째 서블릿을 브라우저를 통해 요청 > 브라우저는 addHeader()가 지정한 두 번째 서블릿을 다시 요청
  • ex) response.addHeader("Refresh","1;url=second"); // 웹 브라우저에 1초 후 서블릿 second로 재요청

 

<3. location 포워딩>

첫번째 서블릿

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out=response.getWriter();
    out.print("<script type='text/javascript'>");
    out.print("location.href='second'");
    out.print("</script>");
}

 

 

8.3 dispatch를 이용한 포워드 방법

  • 요청 > 첫번째 서블릿은 RequestDispatcher를 이용해 두 번째 서블릿으로 포워드
  • 모델2방식, 스트럿츠, 스프링 프레임워크에서 포워딩할 때 사용

ex)

RequestDispatcher dispatch=request.getRequestDispatcher("second");
dispatch.forward(request, response);;
  • redirect처럼 ?뒤에 name=value를 추가해서 GET 방식으로 데이터를 전송할 수 있다

 

 

8.4 바인딩

  • 앞서 GET방식으로 데이터를 전달하는 방법은 대량의 정보를 전송하는 것은 불편 >> 바인딩 이용
  • 바인딩 : 두 개를 하나로 묶는 것, 데이터를 서블릿 관련 객체에 저장하는 방법
  • HttpServletRequest, HttpSession, ServletContext 객체에서 사용 됨
  • 저장된 데이터는 프로그램 실행 시 서블릿이나 JSP에서 공유하여 사용
  • 모델2, 스트럿츠, 스프링 프레임워크로 구현하는 웹 프로그램이 사용하는 방법

 

<서블릿 객체에서 사용되는 바인딩 관련 메서드>

  • setAttribute(String name, Object obj) : 데이터를 각 객체에 바인딩(ex. address-서울시 성북구)
  • getAttribute(String name) : 객체에 바인딩 된 데이터를 name으로 가져옴
  • removeAttribute(String name) : 객체에 바인딩 된 데이터를 name으로 제거

 

* HttpServletRequest 바인딩을 이용한 redirect 포워딩 ==> X

* 첫번째에 request.setAttribute하고 두번째에 request.getAttribute하면 제대로 데이터가 전송되지 않음,

 >> because, 첫번째 서블릿이 받는 요청과 두번째 서블릿이 받는 요청은 서로 다른 요청이기 때문이다. 

 

HttpServletRequest 바인딩을 이용한 dispatch 포워딩 ==> O

첫 번째 서블릿에서 두 번째 서블릿으로 전달되는 요청이 브라우저를 거치지 않고 바로 전달되어 같은 요청임

 

//first
request.setAttribute("address", "서울시 성북구");
RequestDispatcher dispatch=request.getRequestDispatcher("second");
dispatch.forward(request, response);

/////////////////////
//second
String address=(String)request.getAttribute("address");

 

~두 서블릿 간 회원 정보 조회 바인딩 실습~

MemberServlet.java

protected void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ...
    List<MemberVO> memberList=dao.listMembers();
    request.setAttribute("membersList", memberList);
    RequestDispatcher dispatch=request.getRequestDispatcher("viewMembers");
    dispatch.forward(request, response);
    }

 

ViewServlet.java

protected void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ...
    List membersList=(List)request.getAttribute("membersList");
    out.print("<html><body>");
    out.print("<table border=1><tr align='center' bgcolor='lightgreen'>");
    out.print("<td>아이디</td><td>비밀번호</td><td>이름</td><td>이메일</td><td>가입일</td><td >삭제</td></tr>");


     for(int i=0;i<membersList.size();i++) {
         MemberVO memberVO=(MemberVO)membersList.get(i);

         String id=memberVO.getId();
         String pwd=memberVO.getPwd();
         String name=memberVO.getName();
         String email=memberVO.getEmail();
         Date joinDate=memberVO.getJoinDate();

         out.print("<tr><td>"+id+"</td><td>"+
                    pwd+"</td><td>"+
                    name+"</td><td>"+
                    email+"</td><td>"+
                    joinDate+"</td><td>"
                    +"<a href='/pro07/member3?command=delMember&id="+id+"'> 삭제 </a></td></tr>");	
     }
     out.print("</table></body></html>");
     out.print("<a href='/pro08/memberForm.html'>새 회원 등록하기</a>");
}

* 7장과 다르게 db 데이터 받아오는 서블릿과 화면에 응답하는 서블릿(추후에 분화되어 발전된 것이 JSP)을 분리

 

 

8.5 ServletContext와 ServletConfig 사용법

 

ServletContext 클래스

  • getServletContext()로 ServletContext 객체를 가져옴
  • 서블릿과 컨테이너 간의 연동을 위해 사용
  • 톰캣 컨테이너 실행 시 각 컨텍스트(웹 애플리케이션)마다 한 개의 ServletContext 객체를 생성
  • 톰캣 컨테이너가 종료하면 소멸
  • 웹 애플리케이션이 실행되면서 애플리케이션 전체의 공통 자원이나 정보를 미리 바인딩해서 서블릿이 공유하여 사용
  • 서블릿에서 파일 접근 / 자원 바인딩 / 로그 파일 / 컨텍스트에서 제공하는 설정 정보 제공

 

<ServletContext  메서드>

메서드 기능
getAttribute(String name) - 주어진 name(없으면 null)을 이용해 바인딩된 value를 가져옴
getAttributeNames() - 바인딩된 속성들의 name 반환
getContext(String uripath) - 지정한 uripath에 해당하는 객체를 반환
getInitParameter(String name) - name에 해당되는 매개변수(없으면 null)의 초기값 반환
getInitParameterNames() - 컨텍스트의 초기화 관련 매개변수들의 이름들을 Enumeration 타입으로 반환
- 매개변수가 없으면 null 반환
getMajorVersion() - 서블릿 컨테이너가 지원하는 주요 서블릿 API 버전 반환
getRealPath(String path) - 지정한 path에 해당되는 실제 경로 반환
getResource(String path) - 지정한 path에 해당되는 Resource 반환
getResourceAsStream(String path) - 지정한 path에 해당되는 파일 반환
getServerInfo() - 현재 서블릿이 실행되고 있는 서블릿 컨테이너의 이름과 버전 반환
getServletContextName() - 해당 어플리케이션의 배치 관리자가 지정한 ServletContext에 대한 웹 어플리케이션 이름 반환
log(String msg) - 로그 파일에 로그를 기록
removeAttribute(String name) - 해당 name으로 ServletContext에 바인딩된 객체를 제거함
setAttribute(String name, Object object)  - 해당 name으로 객체를 바인딩
setInitParameter(String name, String value) - 주어진 name으로 value를 컨텍스트 초기화 매개변수로 설정

 

~바인딩 실습~

바인딩하는 cset.java

...
ServletContext context=getServletContext();

List member=new ArrayList();
member.add("이순신");
member.add(20);

context.setAttribute("member", member);

 

바인딩 데이터 가져오는 cget.java

...
ServletContext context=getServletContext();
List member=(ArrayList)context.getAttribute("member");

String name=(String)member.get(0);
int age=(Integer)member.get(1);

>> ServletContext에 바인딩된 데이터는 모든 서블릿이 접근 가능한 것을 알 수 있음

 

 

ServletContext의 매개변수 설정 기능(바인딩 미리 선언)

  • WebContent>Web-INF>web.xml에서 <web-app>내 <context-param> 태그 안에 <param-name>, <param-value>를 이용해 하위 메뉴 항목을 설정 >>> 서블릿에서 getInitParameter("paramName")으로 가져옴

ex) 

web.xml

<context-param>
 <param-name> menu_member</param-name>
 <param-value>회원등록 회원조회 회원수정</param-value>
</context-param>
<context-param>
 <param-name> menu_order</param-name>
 <param-value>주문조회 주문등록 주문수정 주문취소</param-value>
</context-param>
<context-param>
 <param-name> menu_goods</param-name>
 <param-value>상품조회 상품등록 상품수정 상품삭제</param-value>
</context-param>

서블릿.xml

ServletContext context=getServletContext();
String menu_member=context.getInitParameter("menu_member");
String menu_order=context.getInitParameter("menu_order");
String menu_goods=context.getInitParameter("menu_goods");

out.print("<html><body> <table border=1 cellspacing=0><tr>메뉴 이름</tr>");
out.print("<tr><td>"+menu_member+"</tr></td>");
out.print("<tr><td>"+menu_member+"</tr></td>");
out.print("<tr><td>"+menu_member+"</tr></td>");
out.print("</table></body></html>");

 

 

<ServletContext의 파일 입출력 기능>

폴더에 바인딩 이름 작성 > getResourceAsStream으로 파일 읽어드리기 > 버퍼리더 선언 > 토큰분리 후 저장

 

- /WEB-INF/bin/init.txt

회원등록 회원조회 회원수정, 주문조회 주문등록 주문수정 주문취소, 상품조회 상품등록 상품수정 상품삭제

 

-servlet

//서블릿 객체를 얻은 후
..
InputStream is=context.getResourceAsStream("/WEB-INF/bin/init.txt"); //파일 읽어드리기
BufferedReader buffer=new BufferedReader(new InputStreamReader(is));
...
while((menu=buffer.readLine())!=null) {
    StringTokenizer tokens=new StringTokenizer(menu,","); //콤마로 토큰 분리
    menu_member=tokens.nextToken();
    menu_order=tokens.nextToken();
    menu_goods=tokens.nextToken();
}

 

 

ServletConfig

  • 각 Servlet 객체에 대해 생성됨, 서블릿끼리 공유 불가
  • Servlet과 동일하게 생성&소멸
  • ServletConfig 인터페이스를 GenericServlet 클래스가 실제로 구현(5장 참고)
  • 기능 : ServletContext 객체를 얻는 기능 / 서블릿에 대한 초기화 작업 기능

 

서블릿에서 사용할 설정 정보를 읽어 들어와 초기화하기

방법1) @WebServlet 애너테이션 사용

방법2) web.xml에 설정하는 방법

 

방법1]

@WebServlet 구성 요소들

  • urlPatterns : 서블릿 요청 매핑 이름
  • name : 서블릿 이름
  • loadOnStartup : 컨테이너 실행 시 서블릿이 로드되는 순서 지정
  • initParams : @WebInitParam 애너테이션을 이용해서 매개변수를 추가하는 기능
  • description : 서블릿에 대한 사용

 

이클립스에서 @WebServlet 값들 설정하기

서블릿 생성 시 URL mappings 설정하는 창

> 위에 Initialzation parameters 추가하여 이름-값 입력

> 매핑 값들 입력

 

결과~ urlPatterns를 이용해서 매핑이름 여러개, initParams의 @WebInitParam을 이용해 매개변수 여러개 설정

@WebServlet(
    urlPatterns = { 
            "/sInit", 
            "/sInit2"
    }, 
    initParams = { 
            @WebInitParam(name = "email", value = "admin@jweb.com"), 
            @WebInitParam(name = "tel", value = "010-1111-2222")
    })
public class InitParamServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out=response.getWriter();

    String email=getInitParameter("email");		//컨텍스트의 메서드가 아니라 그냥 바로 가져옴
    String tel=getInitParameter("tel");

    out.print("<html><body>email: "+email+"<br>휴대전화 : "+tel+"<br>"+"</body></html>");
}

 

방법2) web.xml를 이용해 매개변수 지정

web.xml에서 <web-app>안에

<servlet>
  	<servlet-name>sinit</servlet-name>
  	<servlet-class>sec06.ex01.initParamServlet</servlet-class>
  	 <init-param>
  	  <param-name>email</param-name>
  	  <param-value>admmin@jweb.com</param-value>
  	 </init-param>
  	 <init-param>
  	  <param-name>tel</param-name>
  	  <param-value>010-111-2222</param-value>
  	 </init-param>
 </servlet>
 <servlet-mapping>
...

 

 

8.6 load-on-startup 기능 사용하기

서블릿은 브라우저에서 최초 요청 시 init() 메서드를 실행한 후 메모리에 로드되어 기능을 수행해서 최초 요청에선 실행 시간 길어짐 >> 이 단점을 보완한 기능이 load-on-startup

 

load-on-startup 특징

  • 지정한 숫자가 0보다 크면 톰캣 컨테이너가 실행될 때 미리 서블릿을 실행
  • 지정한 숫자는 우선순위를 의미, 작은 숫자부터 먼저 초기화
  • 방법 : 1) 애너테이션 이용 2)web.xml에 설정

 

방법1) @WebServlet에 loadOnStartup로 등록

@WebServlet(name = "loadConfig", urlPatterns = { "/loadConfig" },loadOnStartup=1)
public class LoadAppConfig extends HttpServlet {
private ServletContext context;

public void init(ServletConfig config) throws ServletException { 
//톰캣 실행 시 이것만 실행, 요청을해야 doGet실행
    System.out.println("init호출");
    context=config.getServletContext(); //config이다
    String menu_member=context.getInitParameter("menu_member");
    String menu_order=context.getInitParameter("menu_order");
    String menu_goods=context.getInitParameter("menu_goods");	//web.xml에서 읽어드림
    context.setAttribute("menu_member", menu_member);
    context.setAttribute("menu_order", menu_order);
    context.setAttribute("menu_goods", menu_goods);
    //컨텍스트 객체에 바인딩 >> 요청 시 web.xml(파일)보다 객체에서 가져오는게 더 빠름
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    System.out.println("doGet호출");
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out=response.getWriter();

    String menu_member=(String)context.getAttribute("menu_member"); //객체에서 가져오기
    String menu_order=(String)context.getAttribute("menu_order");
    String menu_goods=(String)context.getAttribute("menu_goods");
    ...

 

방법2) web.xml에 설정하기

  <servlet>
   <servlet-name>...</servlet-name>
   <servlet-class>...</servlet-class>
   <load-on-startup>1</load-on-startup>
  </servlet>

'BackEnd > Servlet' 카테고리의 다른 글

10장. 서블릿의 필터와 리스너 기능  (0) 2022.02.18
9장. 쿠키와 세션 알아보기  (0) 2022.02.17
7장. 서블릿 비즈니스 로직 처리  (0) 2022.02.15
6장. 서블릿 기초  (0) 2022.02.14
5장. 서블릿 이해하기  (0) 2022.02.13