- 서블릿 프로그래밍
- Init Parameter 다루는 방법
- Context Parameter 다루는 방법
- 필터, 서블릿 배치 방법(복습)
- 포워드/인클루드, 리프래시/리다이렉트 다루는 방법
- 절대 경로와 상대 경로의 이해
Servlet 정리
servlet-app 프로젝트
ㄴ eomcs.servlet.ex06 패키지 생성
=>
ㄴ /Users/사용자명/git/eomcs-java/eomcs-servlet/app/src/main/java/com/eomcs/web/ex05 에 존재하는 파일 모두 복사하기
=>
ㄴ 인텔리제이에서 생성한 패키지 eomcs.servlet.ex06 에 복사한 파일 모두 붙여넣기 > [OK] 선택
load on startup - 서블릿 객체 자동 생성하기(애노테이션으로 설정하기)
클라이언트가 실행을 요청하지 않아도 서블릿을 미리 생성하고 싶다면,
loadOnStartup 프로퍼티 값을 지정하라.
loadOnStartup=실행순서
미리 생성할 서블릿이 여러 개 있다면, loadOnStartup에 지정한 순서대로 생성한다.
서블릿을 미리 생성하는 경우?
=> 서블릿이 작업할 때 사용할 자원을 준비하는데 시간이 오래 걸리는 경우
웹 애플리케이션을 시작시킬 때 미리 서블릿 객체를 준비하는 것이 좋다.
예) DB 연결, 소켓 연결, 필요한 환경 변수 로딩, 스프링 IoC 컨테이너 준비 등
Servlet01.java (ex06)
// load on startup - 서블릿 객체 자동 생성하기(애노테이션으로 설정하기)
package eomcs.servlet.ex06;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 클라이언트가 실행을 요청하지 않아도 서블릿을 미리 생성하고 싶다면,
// loadOnStartup 프로퍼티 값을 지정하기.
//@WebServlet(value="/ex06/s1", loadOnStartup=1)
@SuppressWarnings("serial")
public class Servlet01 extends HttpServlet {
@Override
public void init() throws ServletException {
// 클라이언트가 서블릿의 실행을 요청할 때 서블릿 객체가 없으면 생성된다.
// 서블릿 객체가 생성될 때 이 메서드가 호출된다.
System.out.println("/ex06/s1 => init()");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("/ex06/s1 => service()");
}
}
WEB-INF > web.xml
ㄴ 해당 코드 복사
=>
WEB-INF > web.xml
ㄴ 복사한 코드 붙여넣기
ㄴ 해당 코드 복사하기
=>
WEB-INF > web.xml
ㄴ 복사한 코드 붙여넣기
=>
WEB-INF > web.xml
ㄴ 경로 알맞게 적어주기
=>
ㄴ App 실행
=>
ㄴ localhost:8888/ex06/s1 요청
=>
ㄴ 클라이언트가 서블릿의 실행을 요청할 때 서블릿 객체가 없으면 생성되고, 서블릿 객체가 생성될 때 init 메서드가 호출되므로 콘솔 창에서 "/ex06/s1 => init()" 이 출력됨을 확인할 수 있음 (init() 메서드가 실행됐다는 뜻)
=>
ㄴ 페이지 새로고침 해주기
=>
ㄴ 페이지 새로고침 해줄 경우 service() 메서드가 호출됨을 확인
ㄴ init() 메서드는 서버 실행하고 처음 한 번만 호출됨을 확인
=>
WEB-INF > web.xml
ㄴ <load-on-startup>1</load-on-startup> 이용하여 자동 로딩 되도록 해줌
ㄴ <load-on-startup> 요소의 값으로는 양의 정수가 사용되며, 값이 작을수록 초기화 순서가 빠름
ㄴ 값이 작은 Servlet부터 초기화됨
ㄴ 값이 0인 경우는 웹 컨테이너가 초기화 순서를 결정하게 됨
=>
ㄴ 서버 실행 시 /ex06/Servlet01 의 init() 이 자동 로딩 됨을 확인
ㄴ loaclhost:8888/ex06/s1 호출하기
=>
ㄴ loaclhost:8888/ex06/s1 호출 시 /ex06/Servlet01 의 service() 메서드 호출됨을 확인
load on startup - 서블릿 객체 자동 생성하기(web.xml에 설정하기)
Servlet02.java (ex06)
// load on startup - 서블릿 객체 자동 생성하기(web.xml에 설정하기)
package eomcs.servlet.ex06;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 클라이언트가 실행을 요청하지 않아도 서블릿을 미리 생성하고 싶다면,
// loadOnStartup 프로퍼티 값을 지정하라.
// <servlet>
// ...
// <load-on-startup>1</load-on-startup>
// </servlet>
// 미리 생성할 서블릿이 여러 개 있다면, loadOnStartup에 지정한 순서대로 생성한다.
// 언제 서블릿을 미리 생성하는가?
// => 서블릿이 작업할 때 사용할 자원을 준비하는데 시간이 오래 걸리는 경우
// 웹 애플리케이션을 시작시킬 때 미리 서블릿 객체를 준비한다.
// 예) DB 연결, 소켓 연결, 필요한 환경 변수 로딩, 스프링 IoC 컨테이너 준비 등
//
@SuppressWarnings("serial")
public class Servlet02 extends HttpServlet {
@Override
public void init() throws ServletException {
// 클라이언트가 서블릿의 실행을 요청할 때 서블릿 객체가 없으면 생성된다.
// 서블릿 객체가 생성될 때 이 메서드가 호출된다.
System.out.println("/ex06/s2 => init()");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("/ex06/s2 => service()");
}
}
WEB-INF > web.xml
ㄴ /ex06/Servlet01 관련 설정 주석 처리 해준 후 /ex06/Servlet02 관련 설정 추가 해주기
=>
ㄴ App 실행
=>
ㄴ 브라우저에서 localhost:8888/ex06/s2 호출
=>
ㄴ /ex06/Servlet02 의 service() 메서드가 호출됨을 확인
Servlet01.java
ㄴ 애노테이션 주석 풀어주기
=>
ㄴ 서버 실행 시 /ex06/s1 과 /ex06/s2 의 init() 메서드 모두 자동 로딩 됨을 확인
서블릿 초기화 파라미터 - web.xml에서 설정하기
Servlet04.java (ex06)
// 서블릿 초기화 파라미터 - web.xml에서 설정하기
package eomcs.servlet.ex06;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
// 서블릿이 사용할 값을 DD 설정으로 지정할 수 있다.
// => web.xml 에 지정하였다.
@SuppressWarnings("serial")
public class Servlet04 extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("ex06/s4 => init()");
ServletConfig config = this.getServletConfig();
System.out.printf("driver=%s\n", config.getInitParameter("jdbc.driver"));
System.out.printf("url=%s\n", config.getInitParameter("jdbc.url"));
System.out.printf("username=%s\n", config.getInitParameter("jdbc.username"));
System.out.printf("password=%s\n", config.getInitParameter("jdbc.password"));
}
}
WEB-INF > web.xml
ㄴ /ex06/Servlet02 설정 복사하여 /ex06/Servlet04 설정 추가해주기
=>
WEB-INF > web.xml
ㄴ <init-param> 태그를 이용하여 설정 추가해주기
=>
Servlet04.java (ex06)
ㄴ ServletConfig 의 getInitParameter() 메서드를 이용하도록 함
=>
ㄴ 서블릿 초기화 파라미터 값 설정됨을 확인
서블릿 초기화 파라미터 - 애노테이션으로 설정하기
Servlet03.java (ex06)
// 서블릿 초기화 파라미터 - 애노테이션으로 설정하기
package eomcs.servlet.ex06;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
// 서블릿이 사용할 값을 DD 설정으로 지정할 수 있다.
//
//@WebServlet(
// value = "/ex06/s3",
// loadOnStartup = 1,
// initParams = {
// @WebInitParam(name = "jdbc.driver", value = "org.mariadb.jdbc.Driver"),
// @WebInitParam(name = "jdbc.url", value = "jdbc:mariadb://localhost/studydb"),
// @WebInitParam(name = "jdbc.username", value = "study"),
// @WebInitParam(name = "jdbc.password", value = "******")})
@SuppressWarnings("serial")
public class Servlet03 extends HttpServlet {
// ServletConfig config;
//
// @Override
// public void init(ServletConfig config) throws ServletException {
// // 서블릿 객체가 생성될 때 뭔가 준비하는 작업을 해야 한다면,
// // 보통 이 메서드를 오버라이딩 할 것이다.
// //
// // 이 메서드가 호출될 때 넘어오는 값(config)은 나중에 사용할 수 있도록
// // 보통 인스턴스 필드에 보관해 둔다.
// // 즉 getServletConfig()가 호출될 때 리턴하도록 다음과 같이 코드를 작성한다.
// this.config = config;
//
// // 그런데 init()를 오버라이딩 할 때 마다 이렇게
// // config 객체를 인스턴스 필드에 저장하도록 코딩하는 것은 매우 번거롭다.
// //
// // 이런 불편함을 없애기 위해서 GenericServlet은
// // 미리 이 메서드에 해당 코드를 작성해 두었다.
// // 그리고 추가적으로 파라미터 값을 받지 않는 init()를 호출하도록
// // 구현하였다.
// //
// // 결론?
// // => 그러니 개발자는 서블릿 객체가 생성될 때 뭔가 작업을 수행하고 싶다면,
// // 이 메서드를 직접 오버라이딩 하지 말고,
// // 이 메서드가 호출하는 다른 init()를 오버라이딩 하라!
// //
// //
//
// }
@Override
public void init() throws ServletException {
System.out.println("/ex06/s3 ==> init()");
// 이 객체가 생성될 때 DB에 연결한다고 가정하자!
// DB에 연결하려면 JDBC Driver 이름과 JDBC URL, 사용자 아이디, 암호를
// 알아야 한다.
//
// 그런데 다음과 같이 자바 소스 코드에 그 값을 직접 작성하면,
// 나중에 DB 연결 정보가 바뀌었을 때
// 이 소스를 변경하고 다시 컴파일 해야 하는 번거로움이 있다.
// => 소스에 변할 수 있는 값을 작성하는 것은 바람직하지 않다.
// => 보통 이렇게 값을 직접 작성하는 것을 "하드(hard) 코딩"이라 부른다.
// String jdbcDriver = "org.mariadb.jdbc.Driver";
// String jdbcUrl = "jdbc:mariadb://localhost:3306/studydb";
// String username = "study";
// String password = "******";
// 위의 코드처럼 언제든 변경될 수 있는 값을
// 소스코드에 직접 작성하는 방식은 좋지 않다.
// 해결책?
// => 이런 변경 값들은 외부에 두는 것이 관리에 편하다.
// => 값이 바뀌더라도 소스를 변경할 필요가 없다.
// => 보통 DD 파일(web.xml)에 둔다.
// => 다만 이 예제에서는 애노테이션으로도 설정할 수 있다는 것을
// 보여주기 위해 서블릿 상단에 애노테이션으로 설정하였다.
// => 예)
// @WebInitParam(name = "jdbc.driver", value = "org.mariadb.jdbc.Driver")
// => 애노테이션에 설정된 값을 꺼내기 위해서는 ServletConfig 객체가 필요하다.
//
ServletConfig config = this.getServletConfig();
// 이렇게 @WebInitParam()으로 설정된 값을
// "서블릿 초기화 파라미터"라 부른다.
String jdbcDriver = config.getInitParameter("jdbc.driver");
String jdbcUrl = config.getInitParameter("jdbc.url");
String username = config.getInitParameter("jdbc.username");
String password = config.getInitParameter("jdbc.password");
System.out.println(jdbcDriver);
System.out.println(jdbcUrl);
System.out.println(username);
System.out.println(password);
}
}
=>
ㄴ 해당 코드 주석 풀어주기
=>
WEB-INF > web.xml
ㄴ 필터 객체 등록하기
=>
ㄴ ex06.Filter01.init() 실행됨을 확인
=>
=>
ㄴ /ex06/Filter01 가 호출된 후 /ex06/s2 의 service() 메서드 호출됨
=>
WEB-INF > web.xml
ㄴ 초기화 값 설정
=>
ㄴ 서버 실행만 했을 경우의 초기화 확인
=>
=>
ㄴ web.xml 파일의 Filter01 에서 설정한 값이 출력됨을 확인
ㄴ /ex06/Servlet02 의 service() 메서드 호출됨
필터 초기화 파라미터 : web.xml에서 설정한 값 가져오기
Filter01.java
// 필터 초기화 파라미터 : web.xml에서 설정한 값 가져오기
package eomcs.servlet.ex06;
import javax.servlet.*;
import java.io.IOException;
public class Filter01 implements Filter {
FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
System.out.println("ex06.Filter01.init()");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 필터의 DD 설정으로 지정한 파라미터 값 가져오기
System.out.printf("ex06.Filter01 : encoding=%s\n",
filterConfig.getInitParameter("encoding"));
System.out.printf("ex06.Filter01 : aaa=%s\n",
filterConfig.getInitParameter("aaa"));
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("ex06.Filter01.destroy()");
}
}
// 필터 객체가 사용할 값을 외부 파일인 web.xml 에 둘 수 있다.
// web.xml 예)
// <filter>
// <filter-name>f1</filter-name>
// <filter-class>com.eomcs.web.ex06.Filter01</filter-class>
// <init-param>
// <param-name>encoding</param-name> <== 이렇게 파라미터 이름과
// <param-value>UTF-8</param-value> <== 파라미터 값을 설정할 수 있다.
// </init-param>
// <init-param> <== 여러 개 설정하고 싶다면 이렇게 반복하라!
// <param-name>aaa</param-name>
// <param-value>okok</param-value>
// </init-param>
// </filter>
// <filter-mapping>
// <filter-name>f1</filter-name>
// <url-pattern>/ex05/*</url-pattern>
// </filter-mapping>
//
// 필터에서 web.xml 에 설정된 파라미터 값을 꺼내고 싶다면,
// => FilterConfig.getInitParameter(파라미터명) 메서드를 사용하라.
//
//
=>
Filter01.java
ㄴ web.xml 의 DD 설정대로 파라미터 값 가져오기
=>
WEB-INF > web.xml
ㄴ 해당 코드 모두 주석 처리 해주기
=>
필터 초기화 파라미터 : 애노테이션으로 설정한 값 가져오기
Filter02.java
// 필터 초기화 파라미터 : 애노테이션으로 설정한 값 가져오기
package eomcs.servlet.ex06;
import javax.servlet.*;
import java.io.IOException;
//@WebFilter(
// value="/ex06/*",
// initParams={
// @WebInitParam(name="encoding", value="UTF-8"),
// @WebInitParam(name="aaa", value="nono")
// })
public class Filter02 implements Filter {
FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 필터의 DD 설정으로 지정한 파라미터 값 가져오기
System.out.printf("ex06.Filter02 : encoding=%s\n",
filterConfig.getInitParameter("encoding"));
System.out.printf("ex06.Filter02 : aaa=%s\n",
filterConfig.getInitParameter("aaa"));
chain.doFilter(request, response);
}
}
=>
Filter02.java
ㄴ 해당 코드 주석 해제해주기
=>
ㄴ App 실행 시 /ex06/s1, /ex06/s3 의 init() 메서드가 실행됨을 확인
ㄴ web.xml 에서 DD 설정한 컨텍스트 초기화 파라미터 값 출력됨을 확인
=>
ㄴ 브라우저 리프레시
=>
ㄴ /ex06/Filter02 에 설정된 웹 초기화 파라미터 값 출력됨을 확인
=>
WEB-INF > web.xml
ㄴ 필터와 서블릿에서 공유하는 파라미터 등록하는 부분
컨텍스트 파라미터 가져오기
Filter03.java
// 컨텍스트 파라미터 가져오기
package eomcs.servlet.ex06;
import javax.servlet.*;
import java.io.IOException;
//@WebFilter("/ex06/*")
public class Filter03 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 필터의 DD 설정으로 지정한 파라미터 값 가져오기
System.out.printf("ex06.Filter03 : encoding=%s\n",
request.getServletContext().getInitParameter("encoding"));
System.out.printf("ex06.Filter03 : aaa=%s\n",
request.getServletContext().getInitParameter("aaa"));
chain.doFilter(request, response);
}
}
=>
Filter03.java
ㄴ 해당 코드 주석 해제 해주기
ㄴ Filter03 은 ex06 패키지에 있는 모든 파일과 공유하는 필터임
=>
Filter03.java
ㄴ ServletReqeust 의 getServletContext().getInitParameter 를 이용하여 필터의 DD 설정으로 지정한 파라미터 값 가져오기
=>
ㄴ App 실행
=>
ㄴ localhost:8888/ex06/s2 요청(에러 무시)
=>
ㄴ localhost:8888/ex06/s2 요청 시 Filter03, Filter02 가 호출됨을 확인
ㄴ web.xml 에 설정하는 방법과 애노테이션으로 설정하는 방법 확인
컨텍스트 초기화 파라미터 - web.xml에서 설정하기
Servlet05.java
// 컨텍스트 초기화 파라미터 - web.xml에서 설정하기
package eomcs.servlet.ex06;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
// 서블릿의 DD 설정으로 지정한 초기화 파라미터 값은 그 서블릿에서만 사용할 수 있다.
// 모든 서블릿에서 사용할 값을 설정하려면 컨텍스트 파라미터로 설정해야 한다.
// => web.xml 에 지정하였다.
//
//@WebServlet(value="/ex06/s5", loadOnStartup=1)
@SuppressWarnings("serial")
public class Servlet05 extends HttpServlet {
@Override
public void init() {
// 컨텍스트 초기화 파라미터 값을 꺼내려면 ServletContext 객체가 있어야 한다.
// => 웹 애플리케이션 당 ServletContext 객체는 한 개이다.
// => 따라서 다음 코드 모두 같은 객체를 리턴한다.
ServletContext sc = this.getServletContext();
ServletContext sc2 = this.getServletConfig().getServletContext();
System.out.println(sc == sc2);
System.out.printf("driver=%s\n", sc.getInitParameter("jdbc2.driver"));
System.out.printf("url=%s\n", sc.getInitParameter("jdbc2.url"));
System.out.printf("username=%s\n", sc.getInitParameter("jdbc2.username"));
System.out.printf("password=%s\n", sc.getInitParameter("jdbc2.password"));
}
}
=>
Servlet05.java
ㄴ 애노테이션을 이용하여 설정
=>
WEB-INF > web.xml
ㄴ 컨텍스트 초기화 파라미터 값 설정 코드 복사
=>
WEB-INF > web.xml
ㄴ 복사한 코드 붙여 넣은 후 2로 변경해줌
=>
WEB-INF > web.xml
<?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_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- 컨텍스트 초기화 파라미터 등록 -->
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<context-param>
<param-name>aaa</param-name>
<param-value>오호라!</param-value>
</context-param>
<context-param>
<param-name>jdbc2.driver</param-name>
<param-value>aaa</param-value>
</context-param>
<context-param>
<param-name>jdbc2.url</param-name>
<param-value>bbb</param-value>
</context-param>
<context-param>
<param-name>jdbc2.username</param-name>
<param-value>ccc</param-value>
</context-param>
<context-param>
<param-name>jdbc2.password</param-name>
<param-value>ddd</param-value>
</context-param>
<!-- 필터 객체 등록 -->
<!--
<filter>
<filter-name>ex06.f1</filter-name>
<filter-class>eomcs.servlet.ex06.Filter01</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>aaa</param-name>
<param-value>오호라...</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ex06.f1</filter-name>
<url-pattern>/ex06/*</url-pattern>
</filter-mapping>
-->
<!-- 서블릿 객체 등록 -->
<!--
<servlet>
<servlet-name>s1</servlet-name>
<servlet-class>eomcs.servlet.ex01.Servlet01</servlet-class>
</servlet>
-->
<!-- 서블릿에 경로 설정 -->
<!--
<servlet-mapping>
<servlet-name>s1</servlet-name>
<url-pattern>/ex01/first</url-pattern>
</servlet-mapping>
-->
<!--
<servlet>
<servlet-name>ex06.s2</servlet-name>
<servlet-class>eomcs.servlet.ex06.Servlet02</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ex06.s2</servlet-name>
<url-pattern>/ex06/s2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ex06.s4</servlet-name>
<servlet-class>eomcs.servlet.ex06.Servlet04</servlet-class>
<init-param>
<param-name>jdbc.driver</param-name>
<param-value>aaa</param-value>
</init-param>
<init-param>
<param-name>jdbc.url</param-name>
<param-value>bbb</param-value>
</init-param>
<init-param>
<param-name>jdbc.username</param-name>
<param-value>ccc</param-value>
</init-param>
<init-param>
<param-name>jdbc.password</param-name>
<param-value>ddd</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ex06.s4</servlet-name>
<url-pattern>/ex06/s4</url-pattern>
</servlet-mapping>
-->
</web-app>
=>
Servlet05.java (ex06)
ㄴ ServletContext 의 getServletContext() 메서드를 이용하거나 getServletConfig().getServletContext() 메서드를 이용하여 컨텍스 파라미터 값을 꺼내도록 함
=>
ㄴ true 가 출력됨을 확인할 수 있음
=>
ㄴ ex07 패키지 생성 후 ex06 과 동일하게 파일 가져오기
=>
ㄴ App 실행
=>
포워딩(forwarding) - 서블릿 실행을 위임하기
주의!
=> + 연산을 수행하지 못한다.
왜? 서버에 '+' 문자를 전송하면 URL 디코딩 할 때 '+' 문자가 공백(' ')문자로 변환되기 때문이다.
=> + 연산자를 파라미터 값으로 보내고 싶다면 직접 URL 인코딩 해야 한다.
=> 왜? + 문자는 URL에서 한 칸의 공백을 의미한다.
즉 getParamter("op")의 리턴 값이 공백(" ") 이다.
=> + 문자의 URL 인코딩 값은?
%2b
명심!
=> 서블릿의 PrintWriter 객체를 통해 값을 출력하면
클라이언트로 바로 출력되는 것이 아니라,
내부에 마련된 버퍼로 출력된다.
=> 그럼 출력 결과는 언제?
- service() 메서드 호출이 끝났을 때 버퍼의 내용이 클라이언트로 출력된다.
=> 만약 버퍼가 꽉 차면, service() 메서드 호출이 끝나기 전에 자동으로 출력된다.
Servlet01.java (ex07)
// 포워딩(forwarding) - 서블릿 실행을 위임하기
package eomcs.servlet.ex07;
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;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex07/s1")
@SuppressWarnings("serial")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 테스트 방법:
// => http://localhost:8080/java-web/ex07/s1?a=100&b=200&op=+
// => http://localhost:8080/java-web/ex07/s1?a=100&b=200&op=-
// => http://localhost:8080/java-web/ex07/s1?a=100&b=200&op=*
//
// => + 연산을 파라미터 값으로 보내려면
// http://localhost:8080/java-web/ex07/s1?a=100&b=200&op=%2b
//
response.setContentType("text/plain;charset=UTF-8");
PrintWriter out = response.getWriter();
// 다음 출력코드는 버퍼로 값을 출력한 것이다.
// 아직 클라이언트로 보낸 상태가 아니다.
out.println("더하기 계산 결과:");
String op = request.getParameter("op");
if (!op.equals("+")) {
// 자신의 일이 아니라면 다른 서블릿으로 위임할 수 있다.
// => 요청을 다른 서블릿으로 전달할 수 있다.
// 요청배달자 = request.getRequestDispatcher(다른 서블릿 URL);
RequestDispatcher 요청배달자 = request.getRequestDispatcher("/ex07/s2");
// 이 서블릿이 지금까지 출력한 것은 모두 취소된다.
// => PrintWriter 객체를 통해 출력하는 내용은 즉시 웹 브라우저로 전달되는 것이 아니다.
// 내부 출력 버퍼(보통 8KB 크기)에 보관된다.
// => 서블릿의 service() 메서드 호출이 종료될 때 비로서 버퍼의 내용이
// 웹 브라우저로 전송된다.
// => 물론 그 전에 버퍼가 꽉 차면 자동으로 출력된다.
// => 그래서 다른 서블릿으로 실행을 위임하기 전에
// 이 서블릿이 출력한 내용을 취소할 수 있는 것이다.
요청배달자.forward(request, response);
// 포워딩 한 후 리턴되는지 확인해보자!
// 다음 출력이 서버 콘솔창에 보인다면 포워딩 후에 리턴됨을 알 수 있다.
System.out.println("ex07/Servlet01!!!");
// 그러면 포워딩 서블릿에서 다시 원래 서블릿으로 돌아온 후에
// 클라이언트로 출력한다면?
out.println("어떻게 될까?");
// 리턴된 후에 출력하는 것은 모두 무시된다.
// 따라서 포워딩 후에 리턴되면 하위의 코드를 실행하지 않고
// 바로 종료하도록 다음과 같이 return 명령을 삽입하라!
return;
}
int a = Integer.parseInt(request.getParameter("a"));
int b = Integer.parseInt(request.getParameter("b"));
out.printf("%d + %d = %d\n", a, b, a + b);
}
}
Servlet02.java (ex07)
// 포워딩(forwarding) - 서블릿 실행을 위임하기
package eomcs.servlet.ex07;
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;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex07/s2")
@SuppressWarnings("serial")
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/plain;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("빼기 계산 결과:");
String op = request.getParameter("op");
if (!op.equals("-")) {
// 자신의 일이 아니라면 다른 서블릿으로 위임할 수 있다.
// => 요청을 다른 서블릿으로 전달할 수 있다.
// 요청배달자 = request.getRequestDispatcher(다른 서블릿 URL);
RequestDispatcher 요청배달자 = request.getRequestDispatcher("/ex07/s3");
// 이 서블릿이 지금까지 버퍼로 출력한 것은 모두 취소된다.
// => 엥! 출력된 것이 최소될 수 있나요?
요청배달자.forward(request, response);
System.out.println("ex07/Servlet02!!!");
// 포워딩 한 서블릿을 실행한 후 리턴된다.
// 단 리턴된 후에 출력하는 것은 모두 무시된다.
// 따라서 포워딩 후에 리턴되면 하위의 코드를 실행하지 않고
// 바로 종료하도록 다음과 같이 return 명령을 삽입하라!
return;
}
int a = Integer.parseInt(request.getParameter("a"));
int b = Integer.parseInt(request.getParameter("b"));
out.printf("%d - %d = %d\n", a, b, a - b);
}
}
Servlet03.java (ex07)
// 포워딩(forwarding) - 서블릿 실행을 위임하기
package eomcs.servlet.ex07;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex07/s3")
@SuppressWarnings("serial")
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/plain;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("해당 연산을 수행할 수 없습니다.");
System.out.println("ex07/Servlet03!!!");
}
}
=>
ㄴ + 연산은 + 라고 적으면 연산 불가능함
=>
=>
ㄴ + 연산은 %2b 로 적어줘야 함
=>
Servlet01.java (ex07)
ㄴ getReqeustDispatcher 라는 요청 배달자에 의해 /ex07/s2 로 요청을 전달
ㄴ 요청배달자에 대해 forward 를 실행
=>
Servlet01.java (ex07)
=>
ㄴ - 연산은 정상적으로 실행됨을 확인
=>
ㄴ "어떻게 될까?" 문구는 포워딩 후에 출력하는 것이므로 출력되지 않고 무시됨
ㄴ 서버 콘솔창에 보이는 출력을 통해 포워딩 후에 리턴됨을 알 수 있음
=>
인클루딩(including) - 다른 서블릿의 작업을 포함시키기
Servlet11.java
// 인클루딩(including) - 다른 서블릿의 작업을 포함시키기
package eomcs.servlet.ex07;
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;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex07/s11")
@SuppressWarnings("serial")
public class Servlet11 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 테스트 방법:
// => http://localhost:8080/java-web/ex07/s11?a=100&b=200&op=%2b
// => http://localhost:8080/java-web/ex07/s11?a=100&b=200&op=-
// => http://localhost:8080/java-web/ex07/s11?a=100&b=200&op=*
//
response.setContentType("text/plain;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("계산 결과:");
out.println("---------------------------------------");
String op = request.getParameter("op");
RequestDispatcher 요청배달자 = null;
if (op.equals("+")) {
요청배달자 = request.getRequestDispatcher("/ex07/s11_plus");
} else if (op.contentEquals("-")) {
요청배달자 = request.getRequestDispatcher("/ex07/s11_minus");
} else {
요청배달자 = request.getRequestDispatcher("/ex07/s11_error");
}
// 다른 서블릿을 실행시킨다.
// => forward()는 다른 서블릿으로 위임할 때 현재 서블릿의 출력을 취소한다.
// => include()는 다른 서블릿으로 실행을 위임하더라도
// 현재 서블릿의 실행 결과를 유지한다.
// => 인클루드의 경우 현재 서블릿에서 setContentType()을 설정해야 한다.
// => 포워드는 현재 서블릿에서 설정한 setContentType()이 무시된다.
요청배달자.include(request, response);
// including 서블릿을 실행한 후에 리턴되면
// 현재 서블릿은 계속해서 출력할 수 있다.
// => forwarding 서블릿을 실행한 후에서 리턴되어도
// 현재 서블릿이 출력할 수 없었다.
// 정확히 하면 출력한 것이 모두 무시되었다.
out.println("---------------------------------------");
}
}
Servlet11_plus.java
// 인클루딩(forwarding) - 다른 서블릿의 실행을 포함시키기
package eomcs.servlet.ex07;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex07/s11_plus")
@SuppressWarnings("serial")
public class Servlet11_plus extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 이미 이전 서블릿에서 setContentType()을 호출했기 때문에
// 이 서블릿에서는 할 필요가 없다.
// => forwarding으로 서블릿을 실행할 때는 여기에서 따로 setContentType()을 호출해야 한다.
PrintWriter out = response.getWriter();
int a = Integer.parseInt(request.getParameter("a"));
int b = Integer.parseInt(request.getParameter("b"));
out.printf("%d + %d = %d\n", a, b, a + b);
// 이 메서드의 호출이 완료되면 이전 서블릿으로 되돌아 간다.
}
}
Servlet11_minus.java
// 인클루딩(forwarding) - 다른 서블릿의 실행을 포함시키기
package eomcs.servlet.ex07;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex07/s11_minus")
@SuppressWarnings("serial")
public class Servlet11_minus extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 이미 이전 서블릿에서 setContentType()을 호출했기 때문에
// 이 서블릿에서는 할 필요가 없다.
// => forwarding으로 서블릿을 실행할 때는 여기에서 따로 setContentType()을 호출해야 한다.
PrintWriter out = response.getWriter();
int a = Integer.parseInt(request.getParameter("a"));
int b = Integer.parseInt(request.getParameter("b"));
out.printf("%d - %d = %d\n", a, b, a - b);
// 이 메서드의 호출이 완료되면 이전 서블릿으로 되돌아 간다.
}
}
Servlet11_error.java
// 인클루딩(forwarding) - 다른 서블릿의 실행을 포함시키기
package eomcs.servlet.ex07;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex07/s11_error")
@SuppressWarnings("serial")
public class Servlet11_error extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 이미 이전 서블릿에서 setContentType()을 호출했기 때문에
// 이 서블릿에서는 할 필요가 없다.
// => forwarding으로 서블릿을 실행할 때는 여기에서 따로 setContentType()을 호출해야 한다.
PrintWriter out = response.getWriter();
out.println("해당 연산자를 지원하지 않습니다.");
// 이 메서드의 호출이 완료되면 이전 서블릿으로 되돌아 간다.
}
}
=>
ㄴ ex08 패키지 생성 후 ex06, ex07 과 동일하게 파일 가져오기
들여쓰기 값 설정
ㄴ Editor > Code Style > Java > Tabs and Indents > Indent : 2 로 설정
줄바꿈 설정
ㄴ Editor > Code Style > General > Hard wrap at : 100 으로 설정
리프래시 - 클라이언트에게 다른 URL을 요청하라는 명령
리프래시
=> 서버로부터 응답을 받고 "내용을 출력"한 후
지정된 시간이 경과되면 특정 URL을 자동으로 요청하도록 만들 수 있다.
=> 보통 웹 페이지를 자동으로 이동시키고 싶을 때 사용한다.
=> 예
예1: 로그인 후 메인페이지로 자동 이동
예2: 메일을 전송한 후 메일 목록 페이지로 자동 이동
예3: 게시글 등록한 후 게시글 목록으로 자동 이동
예4: 결제 완료 후 결제 상태 페이지로 자동 이동
Servlet01.java (ex08)
// 리프래시 - 클라이언트에게 다른 URL을 요청하라는 명령
package eomcs.servlet.ex08;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex08/s1")
@SuppressWarnings("serial")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 테스트 방법:
// => http://localhost:8080/java-web/ex08/s1
//
response.setContentType("text/plain;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("안녕하세요! - /ex08/s1");
// 응답 헤더에 Refresh 정보를 추가한다.
//
// 위에서 벌써 클라이언트에게 응답을 했는데 어떻게 응답 헤더를 출력할 수 있나요?
// => 잊지 말자! out.println()이 출력한 것은 출력스트림 버퍼에 보관되어 있다.
// 따라서 아직 클라이언트에게 응답한 상태가 아니다.
// 그래서 다음과 같이 출력을 한 후에 응답 헤더 값을 추가하거나 변경할 수 있는 것이다.
// 메서드 호출이 완료될 때 비로소 클라이언트로 응답헤더와
// 버퍼에 저장된 message-body가 출력된다.
//
// 만약 out.println()/out.printf()/out.print() 등에서 출력한 내용이
// 버퍼를 꽉 채웠다면 어떻게 되나요?
// => 그러면 자동으로 클라이언트에게 응답한다.
// 따라서 일단 클라이언트에게 응답을 하면
// 헤더를 추가하거나 변경하는 코드는 적용되지 않는다.
// 즉 응답을 완료한 후에 헤더 값을 변경하거나 바꿀 수 없기 때문이다.
// 소용이 없다.
//
// 다음은 일부러 버퍼를 채우는 코드이다.
// 버퍼가 꽉차면 자동으로 출력하는 것을 확인해보자!
for (int i = 0; i < 150; i++) {
// 약 40 바이트씩 100번 출력하면 아직 버퍼에 차지 않았기 때문에
// 클라이언트로 출력되지 않는다.
// 따라서 반복문 아래에 있는 응답 헤더 설정이 유효하다.
// 그러나 200번 출력하면 헤더 값과 이전에 출력한 값,
// 그리고 반복문에서 출력한 데이터가 8KB 버퍼를 꽉 채우기 때문에
// 즉시 클라이언트로 응답한다.
// 즉 반복문 다음에 헤더를 설정하기 전에 이미 버퍼 내용이 출력된다.
// 응답이 완료된 후에 응답 헤더의 값을 변경하거나 추가해봐야 소용없다.
//
// out.println(i + " ===> 1234567890123456789012345678901234567890");
}
response.setHeader("Refresh", "3;url=s100");
// 이 service() 메서드의 호출이 끝나면
// 비로서 응답 프로토콜에 맞춰
// 클라이언트로 헤더와 내용을 전송한다.
}
}
=>
ㄴ 해당 코드 주석 처리
=>
ㄴ 현재 페이지가 로딩된 후 3초 뒤에 자동으로 리프레시하고, 그 후에는 s100 페이지로 이동하도록 함
ㄴ 상대 경로 => 3;url=s100
ㄴ 절대 경로 => 3;url=/ex08/s100
=>
ㄴ App 실행
=>
ㄴ localhost:8888/ex08/s1 요청
=>
ㄴ 3초 뒤 리프레시 되며 s100 으로 이동됨을 확인
=>
리프래시 II - 클라이언트에게 다른 URL을 요청하라는 명령
Servlet02.java (ex08)
// 리프래시 II - 클라이언트에게 다른 URL을 요청하라는 명령
package eomcs.servlet.ex08;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex08/s2")
@SuppressWarnings("serial")
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 테스트 방법:
// => http://localhost:8080/java-web/ex08/s2
//
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<html><head>");
// HTML을 출력하는 경우
// 응답 헤더가 아니라 HTML 헤더에 리프래시 명령을 설정할 수 있다.
out.println("<meta http-equiv='Refresh' content='3;url=s100'>");
out.println("</head><body>");
out.println("<h1>안녕하세요! - /ex08/s2</h1>");
out.println("</body></html>");
}
}
// HTTP 응답 프로토콜 예:
//
//HTTP/1.1 200
//Content-Type: text/html;charset=UTF-8
//Content-Length: 119
//Date: Mon, 16 Sep 2019 03:36:11 GMT
//
//<html><head>
//<meta http-equiv='Refresh' content='3;url=s100'>
//</head><body>
//<h1>안녕하세요! - /ex08/s2</h1>
//</body></html>
=>
ㄴ HTML 의 meta 태그를 이용하여 HTML 헤더에 리프레시 명령을 설정
=>
ㄴ localhost:8888/ex08/s2 요청
=>
ㄴ 3초 뒤 리프레시 되며 s100 으로 이동됨을 확인
=>
리다이렉트 - 응답할 때 콘텐트를 보내지 않는다. 바로 다른 페이지를 요청하라고 명령한다.
리다이렉트
=> 클라이언트의 요청을 받은 후 콘텐트를 보내는 대신
다른 페이지의 URL을 알려줄 때 사용한다.
=> 웹 브라우저는 응답 받는 즉시 해당 페이지를 요청한다.
웹 서버로부터 콘텐트를 받지 않았기 때문에 어떤 것도 출력하지 않는다.
바로 다른 페이지로 이동한다.
=> 리프래시와 달리 서버는 콘텐트(message-body)를 보내지 않는다.
=> 사용 예:
- 로그인 후 로그인 결과를 출력하지 않고 즉시 메인 화면으로 보내고 싶을 때
- 결제완료 후 결과를 출력하지 않고 즉시 결제 상태 페이지로 보내고 싶을 때
=> 리다이렉트 HTTP 응답 프로토콜
HTTP/1.1 302 <----- 리다이렉트 응답 상태 코드 (요청한 자원이 다른 URL에 있음을 표시)
Location: s100 <----- 리다이렉트 URL (다른 URL의 주소를 알려줌)
Content-Type: text/html;charset=UTF-8
Content-Length: 0
Date: Tue, 02 Apr 2019 03:38:45 GMT
빈 줄
<---- 콘텐트를 보내지 않는다. 즉 message-body가 없다.
Servlet03.java (ex08)
// 리다이렉트 - 응답할 때 콘텐트를 보내지 않는다. 바로 다른 페이지를 요청하라고 명령한다.
package eomcs.servlet.ex08;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex08/s3")
@SuppressWarnings("serial")
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 테스트 방법:
// => http://localhost:8080/java-web/ex08/s2
//
System.out.println("/ex08/s3");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<html><head>");
out.println("<title>리다이렉트</title>");
out.println("</head><body>");
// 버퍼가 꽉차서 클라이언트로 자동 출력되면
// 리다이렉트가 안먹힌다.
// 왜? 리다이렉트 명령을 응답헤더로 보내기 때문이다.
// 이미 클라이언트로 출력을 완료했는데 어떻게 응답헤더를 보내는가?
// 다음 반복문 주석을 풀라!
for (int i = 0; i < 1000; i++) {
// out.println("안녕하세요! - /ex08/s3<br>");
}
out.println("</body></html>");
// 클라이언트에게 URL을 알려줄 때 상대 경로를 지정할 수 있다.
// forward/include 와 달리 '/'는 컨텍스트 루트(웹 애플리케이션 루트)가 아닌
// 웹 서버 루트를 의미한다.
response.sendRedirect("s100");
// 리다이렉트를 하는 순간 이전까지 버퍼로 출력된 내용은 모두 버려진다.
// 왜? 리다이렉트는 클라이언트로 콘텐트를 보내지 않는다.
//
// 만약 출력한 내용이 버퍼를 꽉 채워서 자동으로 응답을 했다면 어떻게 되나요?
// => 이미 응답했기 때문에 리다이렉트는 동작되지 않는다.
}
}
=>
Servlet03.java (ex08)
ㄴ 해당 코드 주석 처리 해주기
=>
ㄴ 응답 헤더에 콘텐트를 보내지 않고 페이지를 바로 요청함
Postman 이용하기
=>
ㄴ servlet-app 에 Add request 선택하여 ex08/sx 라는 이름의 새로운 request 생성
ㄴ localhost:8888/ex08/s1 를 입력하고 [Send] 선택하여 GET 방식으로 요청
=>
=>
ㄴ Header 확인
'네이버클라우드 > JAVA 웹 프로그래밍' 카테고리의 다른 글
JAVA 63일차 (2023-08-22) 자바 프로그래밍_62. forward/include/refresh/redirect 다루기_개인프로젝트 - 마트 관리 시스템 (0) | 2023.08.23 |
---|---|
인텔리제이 설치 및 설정_윈도우(Windows) 기준 (0) | 2023.08.23 |
JAVA 62일차 (2023-08-21) 자바 프로그래밍_Servlet 정리_ex05 (0) | 2023.08.21 |
JAVA 62일차 (2023-08-21) 자바 프로그래밍_Servlet 정리_ex03~ex04 (0) | 2023.08.21 |
JAVA 62일차 (2023-08-21) 자바 프로그래밍_Servlet 정리_ex01~ex02 (0) | 2023.08.21 |