Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

[JDBC] 예제 소스 정리 - Servlet(서블릿, 필터, 리스너, 멀티파트, 썸네일, 리프레시, 리다이렉트, 쿠키, 세션) 본문

네이버클라우드 AIaaS 개발자 양성과정 1기/DBMS, SQL, JDBC, Servlet

[JDBC] 예제 소스 정리 - Servlet(서블릿, 필터, 리스너, 멀티파트, 썸네일, 리프레시, 리다이렉트, 쿠키, 세션)

끈기JK 2023. 2. 18. 17:40

eomcs-java\eomcs-servlet\app\src\main\java\com\eomcs\web\

 

 

예제 소스 정리

 

 

com.eomcs.web.ex01

 

 

서블릿 만들기 - javax.servlet.Servlet 인터페이스 구현

서블릿 클래스를 만든 후, 서블릿 컨테이너에 등록해야만 사용할 수 있다.
등록 방법 1)
  웹 애플리케이션 배치 파일(web.xml; DD(Deploy Description) 파일)에 서블릿 정보를 등록한다.
  => WEB-INF/web.xml
  => DD File: Deployment Descriptor File
  => 배치 예:
     <servlet>
         <servlet-name>서블릿별명</servlet-name>
         <servlet-class>서블릿 클래스의 전체이름(패키지명 포함)</servlet-class>
     </servlet>

     <servlet-mapping>
         <servlet-name>서블릿별명</servlet-name>
         <url-pattern>클라이언트에서 요청할 때 사용할 URL(/로 시작해야 한다.)</url-pattern>
     </servlet-mapping>
등록 방법 2)
  서블릿 클래스 선언부에 @WebServlet 애노테이션을 붙인다.
  => @WebServlet
     @WebServlet(URL)
     @WebServlet(value=URL)
     @WebServlet(urlPatterns={"URL1", "URL2", ...})

서블릿 실행 방법
=> http://서버주소:포트번호/웹애플리케이션이름/서블릿URL
예) http://localhost:8080/eomcs-java-web/ex01/s01

서블릿 구동 과정
1) 웹 브라우저가 서블릿 실행을 요청한다.
2) 서블릿 컨테이너는 해당 URL의 서블릿 객체를 찾는다.
3.1) 서블릿 객체를 아직 만들지 않았다면,
=> 서블릿 클래스에 대해 인스턴스를 생성한다.
=> 생성자를 호출한다.
=> init()를 호출한다.
=> service()를 호출한다.
3.2) 서블릿 객체가 생성되어 있다면,
=> service()를 호출한다.

만약 웹 애플리케이션이 종료된다면
=> 생성된 모든 서블릿들의 destroy() 메서드를 호출한다.

결론!
=> 특별한 옵션을 주지 않으면 클라이언트가 최초로 요청했을 때 서블릿 인스턴스를 생성한다.
=> 그리고 그 서블릿 인스턴스는 클래스 마다 오직 한 개만 생성된다.
=> init(), destroy()은 오직 한 번만 호출된다.
=> service()는 클라이언트가 요청할 때 마다 호출된다.

주의!
=> 서블릿 인스턴스는 오직 클래스 마다 한 개만 생성된다.
   그래서 모든 클라이언트가 같은 서블릿 인스턴스를 사용한다.
=> 클라이언트마다 구분되어야 할 데이터는
   서블릿 인스턴스 변수에 보관해서는 안된다.
=> 왜?
   인스턴스는 모든 클라이언트가 공유하기 때문이다.

package com.eomcs.web.ex01;

import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex01/first")
public class Servlet01 implements Servlet {

  ServletConfig config;

  public Servlet01() {
    System.out.println("Servlet01()");
  }

  @Override
  public void init(ServletConfig config) throws ServletException {
    // 서블릿 객체를 생성한 후 생성자 다음에 이 메서드가 호출된다.
    // => 서블릿을 실행할 때 사용할 자원을 이 메서드에서 준비한다.
    // => 파라미터로 받은 ServletConfig 객체는 인스턴스 변수에 보관해 두었다가 필요할 때 사용한다.
    // => 각각의 서블릿 클래스마다 객체는 한 개만 생성된다.
    // => 따라서 각 서블릿에 대해 init()는 한 번만 호출된다.
    this.config = config;
    System.out.println("Servlet01.init(ServletConfig)");
  }

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {
    HttpServletRequest req2 = (HttpServletRequest)req;
    HttpServletResponse res2 = (HttpServletResponse)res;
    // 클라이언트가 이 서블릿의 실행을 요청할 때마다 호출된다.
    // 클라이언트가 요청한 작업을 수행한다.
    System.out.println("Servlet01.service(ServletRequest,ServletResponse)");
  }

  @Override
  public void destroy() {
    // 웹 애플리케이션을 종료할 때(서버 종료 포함) 호출된다.
    // => 이 서블릿이 만든 자원을 해제하는 코드를 이 메서드에 둔다.
    System.out.println("Servlet01.destroy()");
  }

  @Override
  public ServletConfig getServletConfig() {
    // 서블릿에서 작업을 수행하는 중에 서블릿 관련 설정 정보를 꺼낼 때 이 메서드를 호출한다.
    // 이 메서드가 리턴하는 값이 ServletConfig 객체인데
    // 이 객체를 이용하면 서블릿 설정 정보를 알 수 있다.
    // 보통 init()의 파라미터 값을 리턴한다.
    System.out.println("Servlet01.getServletConfig()");
    return this.config;
  }

  @Override
  public String getServletInfo() {
    // 서블릿 컨테이너가 관리자 화면을 출력할 때 이 메서드를 호출한다.
    // 이 메서드의 리턴 값은 서블릿을 소개하는 간단한 문자열이다.
    System.out.println("Servlet01.getServletInfo()");
    return "Servlet01";
  }

}

 

 

서블릿 만들기 - javax.servlet.GenericServlet 추상 클래스 상속

GenericServlet 추상 클래스
=> javax.servlet.Servlet 인터페이스를 구현하였다.
=> service() 메서드만 남겨두고 나머지 메서드들은 모두 구현하였다.
=> 따라서 이 클래스를 상속 받는 서브 클래스는 service() 만 구현하면 된다.

@WebServlet 애노테이션
=> web.xml 에 서블릿 정보를 설정하는 대신에 
   이 애노테이션을 사용하여 서블릿을 설정할 수 있다.
=> 이 애노테이션을 활성화시키려면 
   web.xml의 <web-app> 태그에 다음 속성을 추가해야 한다.
     metadata-complete="false"
   속성의 값은 false 여야 한다.

package com.eomcs.web.ex01;

import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

//@WebServlet(urlPatterns={"/ex01/s2","/ex01/ss2","/ex01/sss2"})
//@WebServlet(urlPatterns={"/ex01/s2"})
//@WebServlet(urlPatterns="/ex01/s2")
//@WebServlet(value="/ex01/s2")
@WebServlet("/ex01/s2")
public class Servlet02 extends GenericServlet {

  // GenericServlet 추상 클래스는 java.io.Serialize 인터페이스를 구현하였다.
  // => serialVersionUID 변수 값을 설정해야 한다.
  private static final long serialVersionUID = 1L;

  public Servlet02() {
    System.out.println("Servlet02()");
  }

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {
    System.out.println("Servlet02.service(ServletRequest,ServletResponse)");
  }
}

 

 

서블릿 만들기 - javax.servlet.http.HttpServlet 추상 클래스 상속

HttpServlet 추상 클래스
=> javax.servlet.GenericServlet 추상 클래스를 상속 받았다.
=> service() 메서드도 구현했다.
=> 또한 서블릿 컨테이너가 넘기 파라미터를 
   원래의 타입으로 변환처리한 service() 메서드가 오버로딩 되어 있다.
=> HTTP 프로토콜을 다루려면 GenericServlet을 상속 받지 말고 
   HttpServlet을 상속 받아 서블릿 클래스를 만들라!

package com.eomcs.web.ex01;

import java.io.IOException;
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("/ex01/s3")
public class Servlet03 extends HttpServlet {

  // GenericServlet 추상 클래스가 java.io.Serialize 인터페이스를 구현하였고,
  // HttpServlet 클래스가 GenericServlet 추상 클래스를 상속 받았으니
  // HttpServlet 클래스를 상속 받는 이 클래스도 마찬가지로
  // serialVersionUID 변수 값을 설정해야 한다.
  private static final long serialVersionUID = 1L;

  // Servlet 인터페이스의 service(ServletRequest, ServletResponse)를 오버라이딩 하는 대신에
  // HttpServlet 클래스가 추가한 service(HttpServletRequest, HttpServletResponse)를 오버라이딩 하라.
  // 호출과정:
  // => 웹브라우저
  //   => 톰캣 서버
  //     => Servlet03.service(ServletRequest, ServletResponse) 
  //       => Serlvet03.service(HttpServletRequest, HttpServletResponse)
  @Override
  public void service(HttpServletRequest req, HttpServletResponse res)
      throws ServletException, IOException {
    System.out.println("Servlet03.service(HttpServletRequest,HttpServletResponse)");
  }
}

 

 

서블릿 만들기 - doGet(), doPost() 등

  HTTP 요청 프로토콜에서 특정 명령에 대해 처리하고 싶다면,
  service(HttpServletRequest,HttpServletResponse)를 오버라이딩 하는 대신에
  doGet(), doPost(), doHead() 등을 오버라이딩 하라.
  호출과정:
  => 웹브라우저
    => 톰캣 서버
      => Servlet04.service(ServletRequest, ServletResponse) 호출
        => Serlvet04.service(HttpServletRequest, HttpServletResponse) 호출
          => HTTP 프로토콜의 명령을 분석하여 그 명령을 처리할 메서드를 호출한다.
            => GET 명령: Servlet04.doGet(HttpServletRequest, HttpServletResponse) 호출
            => POST 명령: Servlet04.doPost(HttpServletRequest, HttpServletResponse) 호출
            => HEAD 명령: Servlet04.doHead(HttpServletRequest, HttpServletResponse) 호출
            등 

package com.eomcs.web.ex01;

import java.io.IOException;
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("/ex01/s04")
public class Servlet04 extends HttpServlet {

  private static final long serialVersionUID = 1L;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse res)
      throws ServletException, IOException {
    System.out.println("Servlet04.doGet(ServletRequest,ServletResponse)");
  }
}

 

 

 

com.eomcs.web.ex02

 

 

필터나 리스너를 테스트하기 위한 서블릿
package com.eomcs.web.ex02;

import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex02/s1")
public class Servlet01 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {
    System.out.println("/ex02/s1 서블릿 실행!");
  }
}

 

 

서블릿 컨테이너가 관리하는 컴포넌트
=> 서블릿, 필터, 리스너

필터 만들기
=> javax.servlet.Filter 인터페이스 규칙에 따라 작성한다.

필터 배포하기
=> DD 파일(web.xml)에 설정하기
     <!-- 필터 등록 -->
     <filter>
         <filter-name>f01</filter-name>
         <filter-class>com.eomcs.web.ex02.Filter01</filter-class>
     </filter>

     <!-- 필터를 적용할 URL 설정 -->
     <filter-mapping>
         <filter-name>f01</filter-name>
         <url-pattern>/ex02/*</url-pattern>
     </filter-mapping>
=> 애노테이션으로 설정하기
     @WebFilter(URL)

필터의 용도
=> 서블릿을 실행하기 전후에 필요한 작업을 수행
=> 서블릿 실행 전
- 웹브라우저가 보낸 암호화된 파라미터 값을 서블릿으로 전달하기 전에 암호 해제하기
- 웹브라우저가 보낸 압축된 데이터를 서블릿으로 전달하기 전에 압축 해제하기
- 서블릿의 실행을 요청할 권한이 있는지 검사하기
- 로그인 사용자인지 검사하기
- 로그 남기기
=> 서블릿 실행 후
- 클라이언트로 보낼 데이터를 압축하기
- 클라이언트로 보낼 데이터를 암호화시키기

package com.eomcs.web.ex02;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

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

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    // 웹 애플리케이션을 시작할 때 필터 객체를 생성한다.
    // 이 메서드는 필터 객체를 생성한 후 자동으로 호출된다.
    // 필터가 사용할 자원을 이 메서드에서 준비한다.
    // => 웹 애플리케이션을 시작할 때 필터는 자동 생성된다.
    System.out.println("Filter01.init()");
  }

  @Override
  public void destroy() {
    // 웹 애플리케이션을 종료할 때 호출된다.
    // init()에서 준비한 자원을 해제한다.
    System.out.println("Filter01.destroy()");
  }

  @Override
  public void doFilter(
      ServletRequest request,
      ServletResponse response,
      FilterChain chain)
          throws IOException, ServletException {
    // 요청이 들어 올 때 마다 호출된다.
    // => 단 필터를 설정할 때 지정된 URL의 요청에만 호출된다.
    // => 서블릿이 실행되기 전에 필터가 먼저 실행된다.
    // => 서블릿을 실행한 후 다시 필터로 리턴한다.
    System.out.println("Filter01.doFilter() : 시작");

    // 다음 필터를 실행한다.
    // 만약 다음 필터가 없으면,
    // 요청한 서블릿의 service() 메서드를 호출한다.
    // service() 메서드 호출이 끝나면 리턴된다.
    chain.doFilter(request, response);

    // 체인에 연결된 필터나 서블릿이 모두 실행된 다음에
    // 다시 이 필터로 리턴될 것이다.
    System.out.println("Filter01.doFilter() : 종료");
  }
}

 

 

package com.eomcs.web.ex02;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

//@WebListener
public class Listener01 implements ServletContextListener {

  public Listener01() {
    System.out.println("Listener01() 호출됨!");
  }

  @Override
  public void contextInitialized(ServletContextEvent sce) {
    // 웹 애플리케이션이 시작될 때 호출된다.
    System.out.println("Listener01.contextInitialized()");
  }

  @Override
  public void contextDestroyed(ServletContextEvent sce) {
    // 웹 애플리케이션이 종료될 때 호출된다.
    System.out.println("Listener01.contextDestroyed()");
  }
}

 

 

필터나 리스너를 테스트하기 위한 서블릿
package com.eomcs.web.ex02;

import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex02/a/s2")
public class Servlet02 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {
    System.out.println("/ex02/a/s2 서블릿 실행!");
  }
}

 

 

서블릿 컨테이너가 관리하는 컴포넌트
=> 서블릿, 필터, 리스너

필터 만들기
=> javax.servlet.Filter 인터페이스 규칙에 따라 작성한다.

필터 배포하기
=> DD 파일(web.xml)에 설정하거나 애노테이션으로 설정하면 된다.
=> 다음과 같이 애노테이션으로 할 수도 있다.

필터 실행 순서
=> 필터의 실행 순서를 임의로 조정할 수 없다.
=> 필터를 정의할 때 순서에 의존하는 방식으로 프로그래밍 하지 말라.
=> 필터의 실행 순서에 상관없이 각 필터가 독립적으로 동작하도록 작성하라.

package com.eomcs.web.ex02;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

//@WebFilter("/ex02/a/*")
public class Filter02 implements Filter {

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    // 필터 객체를 생성한 후 제일 처음으로 호출된다.
    // 필터가 사용할 자원을 이 메서드에서 준비한다.
    System.out.println("Filter02.init()");
  }

  @Override
  public void destroy() {
    // 웹 애플리케이션이 종료될 때 호출된다.
    // init()에서 준비한 자원을 해제한다.
    System.out.println("Filter02.destroy()");
  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    // 요청이 들어 올 때 마다 호출된다.
    // => 단 필터를 설정할 때 지정된 URL의 요청에만 호출된다.
    // => 서블릿이 실행되기 전에 필터가 먼저 실행된다.
    // => 서블릿을 실행한 후 다시 필터로 리턴한다.
    System.out.println("Filter02.doFilter() : 시작");

    // 다음 필터를 실행하거나 요청한 서블릿을 실행하려면 다음 코드를 반드시 실행해야 한다.
    chain.doFilter(request, response);

    // 체인에 연결된 필터나 서블릿이 모두 실행된 다음에 다시 이 필터로 리턴될 것이다.
    System.out.println("Filter02.doFilter() : 종료");
  }
}

 

 

리스너 만들기
=> 서블릿 컨테이너 또는 서블릿, 세션 등의 객체 상태가 변경되었을 때 보고 받는 옵저버
=> "Observer" 디자인 패턴이 적용된 것이다.
=> ServletContextListener
   - 서블릿 컨테이너를 시작하거나 종료할 때 보고 받고 싶다면 이 인터페이스를 구현하라.
=> ServletRequestListener
   - 요청이 들어오거나 종료될 때 보고 받고 싶다면 이 인터페이스를 구현하라.
=> HttpSessionListener
   - 세션이 생성되거나 종료될 때 보고 받고 싶다면 이 인터페이스를 구현하라.
=> XxxListener
   - 기타 다양한 인터페이스가 있다. 문서를 참고하라.

리스너 배포하기
=> DD 파일(web.xml)에 설정하거나 애노테이션으로 설정하면 된다.

리스너의 용도
=> 서블릿 컨테이너나, 세션 등이 특별한 상태일 때 필요한 작업을 수행한다.
=> ServletContextListener
   - 웹 애플리케이션을 시작할 때 Spring IoC 컨테이너 준비하기
   - 웹 애플리케이션을 시작할 때 DB 커넥션 풀 준비하기
   - 웹 애플리케이션을 종료할 때 DB 커넥션 풀에 들어 있는 모든 연결을 해제하기
=> ServletRequestListener
   - 요청이 들어 올 때 로그 남기기

package com.eomcs.web.ex02;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;

//@WebListener
public class Listener02 implements ServletRequestListener {

  public Listener02() {
    System.out.println("Listener02() 호출됨!");
  }

  @Override
  public void requestInitialized(ServletRequestEvent sre) {
    // 요청이 들어 왔을 때 호출된다.
    System.out.println("Listener02.requestInitialized()");
    HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
    System.out.println("클라이언트 IP: " + request.getRemoteAddr());
    System.out.println("요청 URL: " + request.getServletPath());
  }

  @Override
  public void requestDestroyed(ServletRequestEvent sre) {
    // 요청 처리를 완료할 때 호출된다.
    System.out.println("Listener02.requestDestroyed()");
  }
}

 

 

 

com.eomcs.web.ex03

 

 

클라이언트로 출력하기
package com.eomcs.web.ex03;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex03/s1") // http://localhost:8080/web/ex03/s1
public class Servlet01 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 출력 스트림을 꺼내기 전에
    // 출력할 때 사용할 문자표(charset)를 지정하지 않으면
    // 리턴 받은 출력 스트림은 기본 문자표 ISO-8859-1 을 사용한다.
    // 즉 자바의 유니코드 문자를 ISO-8859-1 문자표에 따라 변환하여 출력한다.
    // 자바(Unicode2;UTF-16) ===> 출력문자(ISO-8859-1)
    PrintWriter out = res.getWriter();

    // 다음 영어 유니코드 문자는 ISO-8859-1 문자표에 있기 때문에 제대로 변환된다.
    out.println("Hello!");

    // 그러나 다음 유니코드 문자는 ISO-8859-1 문자표에 없기 때문에
    // 없다는 의미에서 '?' 문자표 바뀌어 출력된다.
    out.println("안녕하세요!");
    out.println("こんにちは");
    out.println("您好");
    out.println("مع السلامة؛ إلى اللقاء!");
  }
}

 

 

클라이언트로 출력하기 - 한글 깨짐 현상 처리
package com.eomcs.web.ex03;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex03/s2")
public class Servlet02 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 한글 깨짐 처리하기
    // => 출력 스트림을 꺼내기 전에
    //    출력 스트림이 사용할 문자표(charset)를 지정하라.
    // => 반드시 출력 스트림을 얻기 전에 설정해야 한다.
    //      res.setContentType("MIME Type;charset=문자표이름");
    //
    res.setContentType("text/plain;charset=UTF-8"); // UCS2(UTF-16) ==> UTF-8
    PrintWriter out = res.getWriter();

    out.println("Hello!");

    // 한글이나 아랍문자, 중국문자, 일본문자는
    // UTF-8 문자표에 정의되어 있기 때문에
    // UTF-8 문자로 변환할 수 있다.
    out.println("안녕하세요!");
    out.println("こんにちは");
    out.println("您好");
    out.println("مع السلامة؛ إلى اللقاء!");

    // MIME Type : Multi-purpose Internet Mail Extension
    // => 콘텐트의 형식을 표현
    // => 콘텐트타입/상세타입
    // => 예) text/plain, text/css, text/html 등
    // => 웹 브라우저는 콘텐트를 출력할 때 서버가 알려준 MIME 타입을 보고
    //    어떤 방식으로 출력할 지 결정한다.
  }
}

 

 

클라이언트로 출력하기 - HTML 출력하기
package com.eomcs.web.ex03;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex03/s3")
public class Servlet03 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // HTML 출력할 때 MIME 타입에 HTML을 지정하지 않으면
    // 웹 브라우저는 일반 텍스트로 간주하여 출력한다.
    res.setContentType("text/html;charset=UTF-8"); // UTF-16 ==> UTF-8
    PrintWriter out = res.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head><title>servlet03</title></head>");
    out.println("<body><h1>안녕하세요</h1></body>");
    out.println("</html>");
  }
}

 

 

클라이언트로 출력하기 - 바이너리 데이터 출력하기
package com.eomcs.web.ex03;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex03/s4")
public class Servlet04 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // photo.jpeg 파일의 실제 경로 알아내기
    // 1) 서블릿의 환경 정보를 다루는 객체를 먼저 얻는다.
    ServletContext ctx = req.getServletContext();

    // 2) ServletContext를 통해 웹 자원의 실제 경로를 알아낸다.
    // => getRealPath(현재 웹 애플리케이션의 파일 경로) : 실제 전체 경로를 리턴한다.
    String path = ctx.getRealPath("/photo.jpeg");
    System.out.println(path);

    FileInputStream in = new FileInputStream(path);

    // 바이너리를 출력할 때 MIME 타입을 지정해야 웹 브라우저가 제대로 출력할 수 있다.
    // => 웹 브라우저가 모르는 형식을 지정하면 웹 브라우저는 처리하지 못하기 때문에
    //    그냥 다운로드 대화상자를 띄운다.
    res.setContentType("image/jpeg");

    OutputStream out = res.getOutputStream();

    int b;
    while ((b = in.read()) != -1) {
      out.write(b);
    }

    out.flush(); // 버퍼 데코레이터에 보관된 데이터를 클라이언트로 방출한다.
    out.close();
    in.close();
  }
}

 

 

 

com.eomcs.web.ex04

 

 

클라이언트가 보낸 데이터 읽기 - GET 요청 데이터 읽기
package com.eomcs.web.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s1")
public class Servlet01 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // GET 요청
    // - 웹 브라우저에 URL을 입력한 후 엔터를 치면 GET 요청을 보낸다.
    // - 웹 페이지에서 링크를 클릭하면(자바스크립트 처리하지 않은 상태) GET 요청을 보낸다.
    // - 웹 페이지의 폼(method='GET' 일 때)에서 전송 버튼을 클릭하면 GET 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/web/ex04/test01.html 실행
    //

    // 웹 브라우저가 보낸 데이터 읽기
    // ServletRequest.getParameter("파라미터이름")
    //
    String name = req.getParameter("name");
    String age = req.getParameter("age");

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.printf("이름=%s\n", name);
    out.printf("나이=%s\n", age);
  }
}

웹브라우저에서 웹서버의 자원을 요청하는 방법
1) 서블릿 클래스를 실행하고 싶을 때
=> 서블릿 클래스의 실제 위치:
   톰캣배치폴더/wtpwebapps/eomcs-java-web/WEB-INF/classes/com/eomcs/web/ex04/Servlet01.class
=> 요청:
   해당 서블릿을 서버에 등록할 때 사용한 URL을 지정해야 한다.
   http://localhost:9999/eomcs-java-web/ex04/s1

2) HTML, CSS, JavaScript, JPEG 등 정적 파일을 받고 싶을 때
=> 정적 파일의 실제 위치:
   톰캣배치폴더/wtpwebapps/eomcs-java-web/ex04/test01.html
=> 요청:
   http://localhost:9999/eomcs-java-web/ex04/test01.html

3) /WEB-INF/ 폴더에 있는 정적 파일을 받고 싶을 때
=> 정적 파일의 실제 위치:
   톰캣배치폴더/wtpwebapps/eomcs-java-web/WEB-INF/ex04/test01.html
=> 요청:
   /WEB-INF 폴더 아래에 있는 파일은 클라이언트에서 요청할 수 없다!
   웹 애플리케이션의 정보를 두는 폴더이기 때문이다.

HTTP 요청 형식

method sp request-URI sp http_version CRLF
*((general header | request header | entity header) CRLF)
CRLF
message-body

GET 요청 HTTP 프로토콜 예)
=> GET 요청은 데이터를 request-URI에 붙여서 보낸다.
=> request-URI 
   예) /java-web/ex04/s1?name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20
   서블릿 URL : /java-web/ex04/s1
   데이터(Query String) : ?name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20
=> 데이터 형식
   이름=값&이름=값&이름=값
=> request-URI는 URI 규칙에 따라 인코딩되어야 한다.   

URL 인코딩(=퍼센트 인코딩)
- URI 작성 규칙에 따라 데이터를 변환하는 것.
- 문법
  원래 코드를 4비트 단위로 짤라서 문자로 간주하고 문자 코드로 인코딩 한다.


URI 작성 규칙
- RFC 3986 규약에 따라 URL을 작성해야 한다.
- URL 비예약어(unreserved)인 경우 그대로 사용할 수 있다.
  예) 영어 대.소문자, -, _, ., ~
- URL 문법에서 사용하기 위해 예약된 키워드 및 기타 언어의 문자를 데이터로 작성할 때는 URL 인코딩해야 한다.
  예약어 => !, *, : , ; ,#, /, @ 등
  기타 문자 => 한글, 한자, 일본어 등
               즉 8비트 값이 음수일 경우 URL 인코딩 대상이 된다.

URL 인코딩 및 디코딩 과정
[웹 브라우저]
1. 사용자가 웹 브라우저 주소 창에 입력한 값
   1) 문자(사람 눈에 보이는 상태) 
        => ?name=AB가각&age=20
      코드(메모리에 저장된 상태) 
        => 3F 6E 61 6D 65 3D 41 42 
           B0 A1 
           B0 A2 
           26 61 67 65 3D 32 30
      * Windows 인 경우 한글은 MS949 문자표에 따라 코드 값으로 저장된다.
        그래서 한글 '가각' 은 B0 A1 B0 A2 로 저장된 것이다.

2. 사용자가 입력한 값에서 URL 규약에 벗어나는 문자에 대해 URL 인코딩을 수행
   1) MS949를 웹 페이지 기본 문자표(UTF-8)에 따라 변환(메모리에 저장된 상태)
        => 3F 6E 61 6D 65 3D 41 42 
           EA BO 80 
           EA BO 81 
           26 61 67 65 3D 32 30  
   2) URL 인코딩 코드(메모리에 저장된 상태)  
        => 3F 6E 61 6D 65 3D 41 42 
           25 45 41 25 42 30 25 38 30 
           25 45 41 25 42 30 25 38 31 
           26 61 67 65 3D 32 30  
        => URL 인코딩 대상
           EA => 25 45 41  (사람 눈에 보이는 문자: %EA)
           B0 => 25 42 30  (사람 눈에 보이는 문자: %B0)
           80 => 25 38 30  (사람 눈에 보이는 문자: %80)
           EA => 25 45 41  (사람 눈에 보이는 문자: %EA)
           B0 => 25 42 30  (사람 눈에 보이는 문자: %B0)
           81 => 25 38 31  (사람 눈에 보이는 문자: %81)
           내부적으로 저장되는 코드 값을 사람 눈에 보이는 문자화시켰다.
        => URL 인코딩 후 생성된 코드 값을 보라.
           모든 코드가 7bit로 표현 가능한 US-ASCII 코드이다.
           즉 정수 값으로 바꾼다면 모두 양수가 된다.

      문자(사람 눈에 보이는 상태) 
        => ?name=AB%EA%B0%80%EA%B0%81&age=20
        

[톰캣 서버]
3. 웹브라우저로부터 받은 데이터를 URL 디코딩
   1) URL 디코딩 코드(메모리에 저장된 상태)
        => 3F 6E 61 6D 65 3D 41 42 
           EA BO 80    <---- 한글 '가'의 UTF-8 코드
           EA BO 81    <---- 한글 '가'의 UTF-8 코드 
           26 61 67 65 3D 32 30  
        => URL 디코딩 대상
           25 45 41(%EA) => EA
           25 42 30(%BO) => B0
           25 38 30(%80) => 80
           25 45 41(%EA) => EA
           25 42 30(%BO) => B0
           25 38 31(%81) => 81

4. UTF-8 코드를 자바 String에서 사용할 UTF-16 코드 값으로 변환
   (단, 톰캣 서버에 클라이언트가 보낸 데이터가 UTF-8이라고 설정되어 있어야 한다.
    설정이 되어 있지 않으면 톰캣 서버는 클라이언트가 보낸 데이터를  ISO-8859-1 이라 간주하고 
    UTF-16으로 변환할 것이다.)
   1) UTF-16로 변환한 코드(메모리에 저장된 상태)
        => 00 3F 00 6E 00 61 00 6D 00 65 00 3D 00 41 00 42 
           AC 00    <---- 한글 '가'의 UTF-16 코드  
           AC 01    <---- 한글 '각'의 UTF-16 코드 
           00 26 00 61 00 67 00 65 00 3D 00 32 00 30  
      문자(사람 눈에 보이는 상태) 
        => ...?name=AB가각&age=20

5. 만약 톰캣 서버에 클라이언트가 보낸 데이터가 UTF-8 일 것이라고 설정되어 있지 않다면 
   클라이언트가 보낸 데이터가 ISO-8859-1로 인코딩 되었다고 가정하고 다음과 같이 변환할 것이다.
   1) UTF-16로 변환한 코드(메모리에 저장된 상태)
        => 00 3F 00 6E 00 61 00 6D 00 65 00 3D 00 41 00 42 
           00 EA 00 BO 00 80   <------ 화면에 출력하면 깨진 문자가 보일 것이다. 
           00 EA 00 B0 00 81   <------ 화면에 출력하면 깨진 문자가 보일 것이다.
           00 26 00 61 00 67 00 65 00 3D 00 32 00 30  
      문자(사람 눈에 보이는 상태) 
        => ...?name=AB가각&age=20


톰캣 서버에 GET 요청으로 받은 데이터가 UTF-8 임을 설정하는 방법
톰캣 7 이하 버전에서는 conf/server.xml 파일에서 다음과 같이 설정해야 한다.
   <Connector connectionTimeout="20000" port="8080" 
              protocol="HTTP/1.1" redirectPort="8443"
              URIEncoding="UTF-8"  <------ 이 속성을 추가하라!          
    />
주의!
=> 톰캣 서버가 아닌 다른 서블릿 컨테이너는 사용 안내서를 확인해보라!

HTTP GET 요청 예)
------------------------------------------------------------------------------
GET /java-web/ex04/s1?name=AB%EA%B0%80%EA%B0%81&age=20 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, likeGecko) Chrome/73.0.3683.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6
빈 줄


HTTP 응답 프로토콜
=> 형식
status-line(HTTP프로토콜 상태코드 간단한문구) CRLF
*(general header | response header | entity header) CRLF
CRLF
message-body

=> 예:

HTTP 응답 예)
------------------------------------------------------------------------------
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 27
Date: Thu, 28 Mar 2019 05:46:08 GMT
    <== 빈 줄
이름=홍길동
나이=20


URI (Uniform Resource Identifier)
=> 웹 자원의 위치를 가리키는 식별자
=> 종류
   URL(Uniform Resource Locator)
     scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
     예) http://localhost:8080/ex04/s1?name=홍길동&age=20

   URN(Uniform Resource Name)
     <URN> ::= "urn:" <NID> ":" <NSS>
     예) urn:lex:eu:council:directive:2010-03-09;2010-19-UE

 

 

 

클라이언트가 보낸 데이터 읽기 - POST 요청 데이터 읽기
package com.eomcs.web.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s2")
public class Servlet02 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // POST 요청
    // - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/web/ex04/test02.html 실행
    //

    // 웹 브라우저가 보낸 데이터 읽기
    // => 데이터를 읽을 때는 GET 요청과 POST 요청이 같다.
    //      ServletRequest.getParameter("파라미터이름")
    //
    // => POST 요청으로 보낸 데이터는 서블릿 컨테이너 측에서 영어(ISO-8859-1)라고 간주한다.
    //    그래서 한글 코드 값도 영어라고 간주하고 UCS2(UTF-16) 문자 코드로 변환하기 때문에
    //    getParameter() 가 리턴한 한글은 깨진 한글이다.
    //
    // 클라이언트가 보낸 한글을 읽을 때 깨지는 문제 해결?
    // => 다음 코드의 주석을 풀고 테스트 해보라!
    //    정상적으로 잘 출력될 것이다.
    req.setCharacterEncoding("UTF-8");

    // => 원리
    //    getParameter()를 최초로 호출하기 전에 먼저
    //    클라이언트 보낸 데이터의 인코딩 형식이 어떤 문자표로 되어 있는지 알려줘야 한다.
    // => 주의!
    //    반드시 getParamter()를 최초로 호출하기 전이어야 한다.
    //    한 번 getParameter()를 호출한 후에는 소용없다.
    //

    String age = req.getParameter("age");
    String name = req.getParameter("name");

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.printf("이름=%s\n", name);
    out.printf("나이=%s\n", age);
    out.println("-------------------");

    char[] chars = name.toCharArray();
    for (char c : chars) {
      out.printf("%04x\n", (int) c);
    }
  }
}

HTTP 요청 형식
method sp request-URI sp http_version CRLF
*((general header | request header | entity header) CRLF)
CRLF
message-body

POST 요청 HTTP 프로토콜 예)
=> POST 요청은 데이터를 message-body에 붙여서 보낸다.
=> 데이터 형식과 URL 인코딩은 GET 요청과 같다.
=> 예)

POST /java-web/ex04/s2 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 33
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:8080
Upgrade-Insecure-Requests:1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel MacOS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,
Referer:http://localhost:8080/java-web/ex04/test02.html
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6
빈 줄
name=AB%EA%B0%80%EA%B0%81&age=20


URL 인코딩 및 디코딩 과정
[웹 브라우저]
1. 사용자가 입력 폼에 값을 입력하면
   1) 문자(사람 눈에 보이는 상태) 
        => AB가각
      코드(메모리에 저장된 상태) 
        => 6E 61 6D 65 3D 41 42 
           B0 A1 
           B0 A2 
           26 61 67 65 3D 32 30
      * Windows 인 경우 한글은 MS949 문자표에 따라 코드 값으로 저장된다.
        그래서 한글 '가각' 은 B0 A1 B0 A2 로 저장된 것이다.

2. 사용자가 입력한 값에서 URL 규약에 벗어나는 문자에 대해 URL 인코딩을 수행
   1) MS949를 웹 페이지 기본 문자표(UTF-8)에 따라 변환(메모리에 저장된 상태)
        => 6E 61 6D 65 3D 41 42 
           EA BO 80 
           EA BO 81 
           26 61 67 65 3D 32 30  
   2) URL 인코딩 코드(메모리에 저장된 상태)  
        => 6E 61 6D 65 3D 41 42 
           25 45 41 25 42 30 25 38 30 
           25 45 41 25 42 30 25 38 31 
           26 61 67 65 3D 32 30  
        => URL 인코딩 대상
           EA => 25 45 41  (사람 눈에 보이는 문자: %EA)
           B0 => 25 42 30  (사람 눈에 보이는 문자: %B0)
           80 => 25 38 30  (사람 눈에 보이는 문자: %80)
           EA => 25 45 41  (사람 눈에 보이는 문자: %EA)
           B0 => 25 42 30  (사람 눈에 보이는 문자: %B0)
           81 => 25 38 31  (사람 눈에 보이는 문자: %81)
           내부적으로 저장되는 코드 값을 사람 눈에 보이는 문자화시켰다.
        => URL 인코딩 후 생성된 코드 값을 보라.
           모든 코드가 7bit로 표현 가능한 US-ASCII 코드이다.
           즉 정수 값으로 바꾼다면 모두 양수가 된다.

      문자(사람 눈에 보이는 상태) 
        => name=AB%EA%B0%80%EA%B0%81&age=20
        

[톰캣 서버]
3. 웹브라우저로부터 받은 데이터를 URL 디코딩
   1) URL 디코딩 코드(메모리에 저장된 상태)
        => 3F 6E 61 6D 65 3D 41 42 
           EA BO 80    <---- 한글 '가'의 UTF-8 코드
           EA BO 81    <---- 한글 '가'의 UTF-8 코드 
           26 61 67 65 3D 32 30  
        => URL 디코딩 대상
           25 45 41(%EA) => EA
           25 42 30(%BO) => B0
           25 38 30(%80) => 80
           25 45 41(%EA) => EA
           25 42 30(%BO) => B0
           25 38 31(%81) => 81

4. 클라이언트가 보낸 데이터가 UTF-8로 되어 있다고 알려 주지 않으면,
   getParameter()의 리턴 값은 다음과 같이 한글이 깨진 문자열을 리턴한다.
   1) UTF-16로 변환한 코드(메모리에 저장된 상태)
        => 00 3F 00 6E 00 61 00 6D 00 65 00 3D 00 41 00 42 
           00 EA 00 BO 00 80   <------ 화면에 출력하면 깨진 문자가 보일 것이다. 
           00 EA 00 B0 00 81   <------ 화면에 출력하면 깨진 문자가 보일 것이다.
           00 26 00 61 00 67 00 65 00 3D 00 32 00 30  
      문자(사람 눈에 보이는 상태) 
        => AB...

5. 다음과 클라이언트가 보낸 데이터가 UTF-8로 되어 있음 알려주면 
      req.setCharacterEncoding("UTF-8")
   getParameter()의 리턴 값은 정상적으로 UTF-16 으로 변환된 문자열을 리턴한다.
   1) UTF-16로 변환한 코드(메모리에 저장된 상태)
        => 00 3F 00 6E 00 61 00 6D 00 65 00 3D 00 41 00 42 
           AC 00    <---- 한글 '가'의 UTF-16 코드  
           AC 01    <---- 한글 '각'의 UTF-16 코드 
           00 26 00 61 00 67 00 65 00 3D 00 32 00 30  
      문자(사람 눈에 보이는 상태) 
        => AB가각

GET 요청 vs POST 요청
0) 데이터 전송 방식
=> GET
   - request uri(URL 주소)에 데이터를 포함한다.
     예) /java-web/ex04/s1?name=AB%EA%B0%80%EA%B0%81&age=20
=> POST
   - message body에 데이터를 포함한다.
     예) name=AB%EA%B0%80%EA%B0%81&age=20
1) 전송 데이터 용량
=> GET
   - 대부분의 웹서버가 request-line과 헤더의 크기를 8KB로 제한하고 있다.
   - 따라서 긴 게시글과 같은 큰 용량의 데이터를 GET 방식으로 전송할 수 없다.
=> POST
   - HTTP 요청 헤더 다음에 message-body 부분에 데이터를 두기 때문에
     용량의 제한 없이 웹 서버에 전송할 수 있다.
   - 즉 웹 서버가 제한하지 않는 한 전송 데이터의 크기에 제한이 없다.
   - 웹 서버가 제한한다?
     특정 사이트에서는 게시글의 크기나 첨부파일의 크기에 제한을 둔다.
=> 용도
   - 게시글 조회나 검색어 입력 같은 간단한 데이터 전송에는 GET 요청으로 보내고
   - 게시글 등록이나 첨부파일 같은 큰 데이터 전송에는 POST 요청으로 보낸다.

2) 바이너리 데이터 전송
=> GET
   - request-URI가 텍스트로 되어 있다.
     따라서 바이너리 데이터를 request-URI에 붙여서 전송할 수 없다.
   - 그럼에도 꼭 GET 요청으로 바이너리 데이터를 보내고자 한다면?
     바이너리 데이터를 텍스트로 변환하면 된다.
     예를 들어 바이너리 데이터를 Base64로 인코딩하여 텍스트를 만든 후에
     GET 요청 방식대로 이름=값 으로 보내면 된다.
   - 그래도 결국 용량 제한 때문에 바이너리 데이터를 GET 요청으로 전송하는 것은 바람직하지 않다.
=> POST
   - 이 방식에서도 이름=값 형태로는 바이너리 값을 전송할 수 없다.
   - multipart 형식을 사용하면 바이너리 데이터를 보낼 수 있다.
   - 보통 파일 업로드를 구현할 때 이 multipart 전송 방식으로 사용한다.

3) 보안
=> GET
   - URL에 전송 데이터가 포함되어 있기 때문에
     사용자 아이디나 암호 같은 데이터를 GET 방식으로 전송하는 것은 위험하다.
   - 웹 브라우저는 주소 창에 입력한 값을 내부 캐시에 보관해 두기 때문이다.
   - 그러나 게시물 번호 같은 데이터는 URL에 포함되어야 한다.
     그래야 다른 사람에게 URL과 함께 데이터를 보낼 수 있다.
=> POST
   - mesage-body 부분에 데이터가 있기 때문에
     웹 브라우저는 캐시에 보관하지 않는다.
   - 또한 주소 창에도 보이지 않는다.
   - 사용자 암호 같은 데이터를 전송할 때는 특히 이 방식으로 보내는 것이 바람직 하다.
     즉 보내는 데이터를 웹 브라우저의 캐시 메모리에 남기고 싶지 않을 때는 POST 방식을 사용한다.
   - 꺼꾸로 특정 페이지를 조회하는 URL일 경우 POST 방식을 사용하면
     URL에 조회하려는 정보의 번호나 키를 포함할 수 없기 때문에
     이런 상황에서는 POST 방식이 적절하지 않다.
     오히려 GET 방식이 적합하다.

 

 

 

클라이언트가 보낸 데이터 읽기 - 파일 업로드 처리하기
ge com.eomcs.web.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s3")
public class Servlet03 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // POST 요청으로 파일 전송하기
    // - 파일을 첨부하여 서버에 전송한다.
    // - multipart/form-data 형식으로 데이터를 전송하지 않으면
    //   첨부 파일의 데이터는 받을 수 없다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test03.html 실행
    //

    req.setCharacterEncoding("UTF-8");

    String age = req.getParameter("age");
    String name = req.getParameter("name");
    String photo = req.getParameter("photo");

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.printf("이름=%s\n", name);
    out.printf("나이=%s\n", age);
    out.printf("사진=%s\n", photo);

    // GET 요청이나 일반 POST 요청을 한 경우에는
    // 파일이 이름만 넘어오고 파일 데이터는 넘어오지 않는다.
    //
    // 파일의 데이터를 전송하려면,
    // <form> 태그에 enctype 속성을 "multipart/form-data"로 설정해야 한다.
    //
    // 단 멀티파트 형식으로 데이터가 넘어온 경우에는
    // getParameter()로 그 값을 꺼낼 수 없다.
  }
}

1) GET 요청 예:

GET /eomcs-java-web/ex04/s3?name=AB%EA%B0%80%EA%B0%81&age=20&photo=actors.jpg HTTP/1.1
Host: 192.168.1.10:9999
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/80.0.3987.149 Safari/537.36
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.1.10:9999/eomcs-java-web/ex04/test03.html
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6,cs;q=0.5
Connection: keep-alive

2) 일반 POST 요청 예:
form의 기본 데이터 전송 형식은 "application/x-www-form-urlencoded"이다.
즉 "이름=값&이름=값" 형태로 전송한다.
다음 요청 프로토콜에서 "Content-Type" 헤더를 확인해 보라!

POST /eomcs-java-web/ex04/s3 HTTP/1.1
Host: 192.168.1.10:9999
Content-Length: 50
Pragma: no-cache
Cache-Control: no-cache
Origin: http://192.168.1.10:9999
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/80.0.3987.149 Safari/537.36
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.1.10:9999/eomcs-java-web/ex04/test03.html
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6,cs;q=0.5
Connection: keep-alive

name=ABC%EA%B0%80%EA%B0%81&age=20&photo=actors.jpg

3) 멀티파트 POST 요청 예:

POST /eomcs-java-web/ex04/s3 HTTP/1.1
Host: 192.168.1.10:9999
Content-Length: 248900
Pragma: no-cache
Cache-Control: no-cache
Origin: http://192.168.1.10:9999
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryT1G23U6fYMK0zZxx
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/80.0.3987.149 Safari/537.36
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.1.10:9999/eomcs-java-web/ex04/test03.html
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6,cs;q=0.5
Connection: keep-alive

------WebKitFormBoundaryT1G23U6fYMK0zZxx
Content-Disposition: form-data; name="name"

AB가각
------WebKitFormBoundaryT1G23U6fYMK0zZxx
Content-Disposition: form-data; name="age"

20
------WebKitFormBoundaryT1G23U6fYMK0zZxx
Content-Disposition: form-data; name="photo"; filename="actors.jpg"
Content-Type: image/jpeg

바이너리데이터...
------WebKitFormBoundaryT1G23U6fYMK0zZxx--

 

 

 

멀티파트 파일 업로드 처리하기 - apache 라이브러리 사용
package com.eomcs.web.ex04;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

// 멀티파트 형식으로 전송된 데이터는
// 별도의 처리과정이 필요하다.
// 이 일을 대신 해주는 라이브러리가 있다.
// => Apache 소프트웨어 재단(ASF)에서 제공하는 fileupload 라이브러리이다.
// => search.maven.org 에서 'commons-fileupload' 검색한다.
// => 라이브러리 정보를 build.gradle 에 추가한다.
// => 'gradle eclipse'를 실행하여 라이브러리를 가져온다.
// => 이클립스 프로젝트를 refresh 한다.
//
@WebServlet("/ex04/s4")
public class Servlet04 extends GenericServlet {

  private static final long serialVersionUID = 1L;
  private String uploadDir;

  @Override
  public void init() throws ServletException {
    // init(ServletCondig)가 호출될 때 이 메서드를 호출한다.
    // 파일을 저장할 디렉토리 경로를 준비한다.
    this.uploadDir = this.getServletContext().getRealPath("/upload");
  }

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 멀티파트 형식으로 보낸 첨부 파일 데이터를 읽는 방법
    // => Content-Type 헤더에 지정한 구분자를 사용하여 각 파트를 분리한 다음
    //    데이터를 읽는다.
    // => 문제는 기존에 제공하는 getParameter()로는 멀티파트 형식으로 전송된
    //    데이터를 읽을 수 없다.
    // => 방법?
    // 1) 개발자가 직접 멀티파트 형식을 분석하여 데이터를 추출한다.(X)
    // 2) 외부 라이브러리를 사용한다.
    //    - apache.org 사이트에서 제공하는 멀티파트 데이터 분석기를 사용한다.
    //    - 실무에서 예전에 많이 사용했다.
    // 3) Servlet 3.0 부터 제공하는 기능을 이용한다.
    //    - 실무에서는 아직도 apache.org에서 제공하는 라이브러리를 계속 사용하는 곳도 있다.
    //      그래서 Servlet 3.0에서 제공하는 방법뿐만아니라 
    //      2) 번 방법도 알고 있어야 한다.
    // 4) Spring WebMVC를 사용한다면 해당 프레임워크에서 제공하는 기능을 이용한다.
    //    - Spring WebMVC를 설명할 때 실습하겠다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test04.html 실행
    //
    //
    // 멀티파트 데이터를 처리할 때는 다음의 인코딩 설정이 적용되지 않는다.
    // req.setCharacterEncoding("UTF-8");
    //
    //
    // 멀티파트 형식의 데이터 처리하기
    // 1) Apache 라이브러리 가져온다.
    // - mvnrepository.com에서 apache fileupload 라이브러리 검색한다.
    // - build.gradle에 라이브러리 정보 추가한다.
    // - '$ gradle eclipse' 실행하여 이클립스 설정 파일을 갱신한다.
    // - 이클립스 IDE에서 프로젝트 정보를 갱신한다.
    // 2) Apache commons-fileupload 문서에 따라 코딩한다.
    //
    // DiskFileItemFactory
    // => 각 파트 데이터를 분석하여 
    //    - 파라미터이름과 값을 추출한다.
    //    - 파일인 경우 임시 폴더에 저장한다.
    //    - 그런 후 FileItem 객체에 분석한 정보를 담아서 리턴한다. 
    // => ServletFileUpload 객체의 일을 도와준다.
    DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();

    // ServletFileUpload
    // => 클라이언트가 보낸 멀티 파트 형식의 HTTP 요청 프로토콜을 분석하는 일을 한다.
    // => 생성자에 주입된 FileItemFactory 객체를 사용하여 
    //    각 파트의 데이터를 사용하기 좋게 FileItem 객체로 만든다.
    ServletFileUpload multipartDataHandler = new ServletFileUpload(fileItemFactory);

    // => 분석한 데이터를 보관할 맵 객체를 준비한다.
    HashMap<String, String> paramMap = new HashMap<>();

    try {
      // parseRequest()
      // => 클라이언트가 보낸 멀티 파트 데이터를 읽어서 FileItem 객체 배열로 뽑아내는 일을 한다.
      List<FileItem> parts = multipartDataHandler.parseRequest((HttpServletRequest) req);

      for (FileItem part : parts) {
        if (part.isFormField()) {
          // 파트의 데이터가 일반 데이터라면
          paramMap.put(part.getFieldName(), // 클라이언트가 보낸 파라미터 이름
              part.getString("UTF-8") // 파라미터의 값. 값 꺼낼 때 인코딩을 지정해야 한다.
              );

        } else {
          // 파트의 데이터가 파일이라면
          // => upload/ 디렉토리에 파일을 저장한다.

          // 업로드 파일을 저장할 때 사용할 파일명을 준비한다.
          // => 원래의 파일명을 사용하지 않는다.
          // => 다른 클라이언트가 같은 이름의 파일을 업로드 하면 기존 파일을 덮어쓸 수 있기 때문이다.
          String filename = UUID.randomUUID().toString();

          // 전체 파일 경로를 준비한다.
          // => /java-web/upload/파일명
          File file = new File(this.uploadDir + "/" + filename);
          System.out.println(file.getCanonicalPath());

          // 임시 폴더에 저장된 파일을 지정된 파일 경로로 옮긴다.
          part.write(file);

          paramMap.put(part.getFieldName(), // 클라이언트가 보낸 파라미터 이름
              filename // 파일 이름
              );
        }
      }

      res.setContentType("text/html;charset=UTF-8");
      PrintWriter out = res.getWriter();
      out.println("<html>");
      out.println("<head><title>servlet04</title></head>");
      out.println("<body><h1>파일 업로드 결과</h1>");
      out.printf("이름=%s<br>\n", paramMap.get("name"));
      out.printf("나이=%s<br>\n", paramMap.get("age"));
      out.printf("사진=%s<br>\n", paramMap.get("photo"));
      out.printf("<img src='../upload/%s'><br>\n", paramMap.get("photo"));
      out.println("</body></html>");

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

 

 

멀티파트 파일 업로드 처리하기 - Servlet 3.0의 기본 라이브러리 사용
package com.eomcs.web.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.UUID;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;

// 멀티파트 형식의 데이터를 처리할 서블릿으로 선언하라.
// 1) DD 파일(web.xml)에 설정하기
// <servlet>
//   <servlet-name>ex04.Servlet05</servlet-name>
//   <servlet-class>com.eomcs.web.ex04.Servlet05</servlet-class>
//   <multipart-config>
//     <max-file-size>10000000</max-file-size>
//   </multipart-config>
// </servlet>
//
// 2) 애노테이션으로 설정하기
//
@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebServlet("/ex04/s5")
public class Servlet05 extends GenericServlet {

  private static final long serialVersionUID = 1L;
  private String uploadDir;

  @Override
  public void init() throws ServletException {
    this.uploadDir = this.getServletContext().getRealPath("/upload");
  }

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 테스트
    // - http://localhost:8080/java-web/ex04/test05.html 실행
    //

    // Servlet 3.0의 멀티파트 처리 기능을 이용할 때는
    // 원래 하던대로 클라이언트가 보낸 데이터의 인코딩을 지정하라.
    req.setCharacterEncoding("UTF-8");

    // 파라미터로 받은 ServletRequest를 원래의 타입으로 변환하라.
    HttpServletRequest httpReq = (HttpServletRequest) req;

    res.setContentType("text/html;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("<html>");
    out.println("<head><title>servlet04</title></head>");
    out.println("<body><h1>파일 업로드 결과</h1>");

    // 일반 폼 데이터를 꺼낼 때는 원래 하던 방식대로 값을 꺼낸다.
    out.printf("이름=%s<br>\n", httpReq.getParameter("name"));
    out.printf("나이=%s<br>\n", httpReq.getParameter("age"));

    // 파일 데이터는 getPart()를 이용한다.
    Part photoPart = httpReq.getPart("photo");
    if (photoPart.getSize() > 0) {
      // 파일을 선택해서 업로드 했다면,
      String filename = UUID.randomUUID().toString();
      photoPart.write(this.uploadDir + "/" + filename);
      out.printf("사진=%s<br>\n", filename);
      out.printf("<img src='../upload/%s' height='50'><br>\n", filename);
      out.printf("<img src='../upload/%s'><br>\n", filename);
    }
    out.println("</body></html>");
  }
}

 

 

클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우
package com.eomcs.web.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s6")
public class Servlet06 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    req.setCharacterEncoding("UTF-8");

    // POST 요청
    // - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test06.html 실행
    //

    // 1) 서로 다른 이름으로 값을 보낼 경우
    // => 예) genre1=on&genre2=on&genre4=on
    // => 다음과 같이 각각의 이름에 대해 값을 꺼내 확인해야 한다.
    String genre1 = req.getParameter("genre1");
    String genre2 = req.getParameter("genre2");
    String genre3 = req.getParameter("genre3");
    String genre4 = req.getParameter("genre4");
    String genre5 = req.getParameter("genre5");
    String genre6 = req.getParameter("genre6");

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("선택한 장르:");
    if (genre1 != null) {
      out.println("로맨틱");
    }
    if (genre2 != null) {
      out.println("스릴러");
    }
    if (genre3 != null) {
      out.println("호러");
    }
    if (genre4 != null) {
      out.println("드라마");
    }
    if (genre5 != null) {
      out.println("액션");
    }
    if (genre6 != null) {
      out.println("SF");
    }
  }
}

 

 

클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우
package com.eomcs.web.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s6_2")
public class Servlet06_2 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    req.setCharacterEncoding("UTF-8");

    // POST 요청
    // - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test06.html 실행
    //

    // 2) 같은 이름으로 값을 보낼 경우
    // => 예) genre=1&genre=2&genre=4
    // => 다음과 같이 한 번에 값을 리턴 받는다.
    String[] genres = req.getParameterValues("genre");
    String[] genreData = {"", "로맨틱", "스릴러", "호러", "드라마", "액션", "SF"};

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("선택한 장르:");
    for (String genre : genres) {
      out.println(genreData[Integer.parseInt(genre)]);
    }
    // 같은 값을 여러 개 입력 받아야 하는 경우
    // 같은 이름을 사용하라.
    // 그러면 위와 같이 한 번에 배열로 그 값들을 받을 수 있다.
    // 배열로 받으면 반복문을 이용하여 보다 쉽고 간결하게 처리할 수 있다.
  }
}

 

 

클라이언트가 보낸 데이터 읽기 - 빈 값과 null
package com.eomcs.web.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s7")
public class Servlet07 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    req.setCharacterEncoding("UTF-8");

    // POST 요청
    // - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test07.html 실행
    //

    // 파라미터 이름만 넘어갈 때 getParameter()의 리턴 값은 빈 문자열 객체이다.
    // null 이 아니다.
    // 입력 상자에 값을 입력하지 않아도 빈 문자열이 서버에 전송된다는 것이다.
    // => 예) a=aaa&b=
    //
    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();

    out.printf("a = %s\n", req.getParameter("a"));
    out.printf("b = %s\n", req.getParameter("b"));

    // 파라미터 이름 자체가 없으면 getParameter()는 null을 리턴한다.
    out.printf("c = %s\n", req.getParameter("c"));
  }
}

 

 

썸네일 이미지 만들기
package com.eomcs.web.ex04;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.UUID;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import net.coobird.thumbnailator.ThumbnailParameter;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.Thumbnails.Builder;
import net.coobird.thumbnailator.geometry.Positions;
import net.coobird.thumbnailator.name.Rename;

@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebServlet("/ex04/s8")
public class Servlet08 extends GenericServlet {

  private static final long serialVersionUID = 1L;
  private String uploadDir;

  @Override
  public void init() throws ServletException {
    this.uploadDir = this.getServletContext().getRealPath("/upload");
  }

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 테스트
    // - http://localhost:8080/java-web/ex04/test08.html 실행
    //

    req.setCharacterEncoding("UTF-8");

    HttpServletRequest httpReq = (HttpServletRequest) req;

    res.setContentType("text/html;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("<html>");
    out.println("<head><title>servlet04</title></head>");
    out.println("<body><h1>파일 업로드 결과</h1>");

    // 일반 폼 데이터를 원래 하던 방식대로 값을 꺼낸다.
    out.printf("이름=%s<br>\n", httpReq.getParameter("name"));
    out.printf("나이=%s<br>\n", httpReq.getParameter("age"));

    // 파일 데이터는 getPart()를 이용한다.
    Part photoPart = httpReq.getPart("photo");
    if (photoPart.getSize() == 0) {
      out.println("</body></html>");
      return;
    }

    // 파일을 선택해서 업로드 했다면,
    String filename = UUID.randomUUID().toString();
    photoPart.write(this.uploadDir + "/" + filename);

    // 원본 사진을 가지고 특정 크기의 썸네일 이미지를 만들기
    // 1) 썸네일 이미지를 생성해주는 자바 라이브러리 추가
    // => mvnrepository.com에서 thumbnailator 라이브러리 검색
    // => build.gradle 에 추가
    // => '$ gradle eclipse' 실행
    // => eclise IDE에서 프로젝트 리프래시

    // 2) 썸네일 이미지 만들기
    // => 원본 이미지 파일이 저장된 경로를 알려주고
    // 어떤 썸네일 이미지를 만들어야 하는지 설정한다.
    Builder<File> thumbnailBuilder = Thumbnails.of(this.uploadDir + "/" + filename);
    thumbnailBuilder.size(20, 20);
    thumbnailBuilder.outputFormat("jpg");
    thumbnailBuilder.toFiles(Rename.PREFIX_DOT_THUMBNAIL);

    Builder<File> thumbnailBuilder2 = Thumbnails.of(this.uploadDir + "/" + filename);
    thumbnailBuilder2.size(20, 20);
    thumbnailBuilder2.crop(Positions.CENTER);
    thumbnailBuilder2.outputFormat("jpg");
    thumbnailBuilder2.toFiles(new Rename() {
      @Override
      public String apply(String name, ThumbnailParameter param) {
        return name + "_20x20";
      }
    });

    Thumbnails.of(this.uploadDir + "/" + filename)
    .size(80, 80)
    .outputFormat("jpg")
    .crop(Positions.CENTER)
    //.toFiles(Rename.PREFIX_DOT_THUMBNAIL);
    .toFiles(new Rename() {
      @Override
      public String apply(String name, ThumbnailParameter param) {
        return name + "_80x80";
      }
    });

    Thumbnails.of(this.uploadDir + "/" + filename)
    .size(160, 160)
    .outputFormat("jpg")
    .crop(Positions.CENTER)
    //.toFiles(Rename.PREFIX_DOT_THUMBNAIL);
    .toFiles(new Rename() {
      @Override
      public String apply(String name, ThumbnailParameter param) {
        return name + "_160x160";
      }
    });

    out.printf("사진=%s<br>\n", filename);
    out.printf("<img src='../upload/thumbnail.%s.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s_20x20.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s_80x80.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s' height='80'><br>\n", filename);
    out.printf("<img src='../upload/%s_160x160.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s'><br>\n", filename);
    out.println("</body></html>");
  }
}

 

 

 

com.eomcs.web.ex05

 

 

HTTP 프로토콜 다루기 - HttpServletRequest와 GET/POST
package com.eomcs.web.ex05;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex05/s1")
public class Servlet01 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 테스트
    // - http://localhost:8080/web/ex05/test01.html 실행
    //

    // HTTP 프로토콜로 통신을 하는 서블릿 컨테이너는
    // service() 메서드를 호출할 때
    // ServletRequest의 파라미터 값으로 HttpServletRequest를 만들어 전달한다.
    // ServletResponse의 파라미터 값으로 HttpServletResponse를 만들어 전달한다.
    // 따라서 service() 메서드의 파라미터 값은 원래
    // HttpServletRequest와 HttpServletResponse이다.
    // 이들 객체에는 HTTP 프로토콜을 다루는 메서드가 추가되어 있다.
    // 따라서 HTTP 프로토콜을 다루고 싶다면 파라미터 값을
    // 원래의 타입으로 변환하라!
    HttpServletRequest httpReq = (HttpServletRequest) req;
    HttpServletResponse httpRes = (HttpServletResponse) res;

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();

    // HttpServletRequest에는 HTTP 프로토콜의 요청 방식을 리턴하는 메서드가 있다.
    // => HttpServletRequest.getMethod()
    //
    if (httpReq.getMethod().equals("GET")) {
      out.println("GET 요청입니다.");

    } else if (httpReq.getMethod().equals("POST")) {
      out.println("POST 요청입니다.");

    } else {
      out.println("이 서블릿이 다루지 못하는 요청 방식입니다.");
    }

  }
}

 

 

HTTP 프로토콜 다루기 - MyHttpServlet 클래스를 활용하기
package com.eomcs.web.ex05;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex05/s2")
public class Servlet02 extends MyHttpServlet {

  private static final long serialVersionUID = 1L;

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 테스트
    // - http://localhost:8080/web/ex05/test02.html 실행
    //
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    if (request.getMethod().equals("GET")) {
      out.println("GET 요청입니다.");

    } else if (request.getMethod().equals("POST")) {
      out.println("POST 요청입니다.");

    } else {
      out.println("이 서블릿이 다루지 못하는 요청 방식입니다.");
    }

  }
}

 

 

package com.eomcs.web.ex05;

import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 서블릿 클래스를 만들 때 HTTP 프로토콜을 쉽게 다룰 수 있도록
// service(HttpServletRequest,HttpServletResponse) 메서드를 추가하였다.
// 따라서 GenericServlet을 상속 받아 서블릿을 만들기 보다
// 이 클래스를 상속 받아 서블릿을 만든다면,
// 훨씬 편하게 service()를 구현할 수 있다.
//
@SuppressWarnings("serial")
public abstract class MyHttpServlet extends GenericServlet {

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {
    // 서블릿 컨테이너가 이 메서드를 호출하면

    // => 파라미터 값을 원래의 타입으로 변환한다.
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    // => 오버로딩한 service()를 호출한다.
    this.service(request, response);
  }

  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 웹브라우저가 요청하면
    // => 서블릿 컨테이너가 service(ServletRequest,ServletResponse)를 호출한다.
    // => service(ServletRequest,ServletResponse)는
    //    다시 오버로딩한 이 메서드를 호출한다.
    //    현재 이 메서드는 아무런 일을 하지 않는다.
    //    이 클래스를 상속 받는 서브 클래스 쪽에서 이 메서드를 오버라이딩 하면 된다.
  }

}

 

 

HTTP 프로토콜 다루기 - MyHttpServlet2 클래스를 활용하기
package com.eomcs.web.ex05;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex05/s3")
public class Servlet03 extends MyHttpServlet2 {

  private static final long serialVersionUID = 1L;


  // HTTP GET 요청에 응답하고 싶다면
  // 상속 받은 메서드 중에서 doGet()을 오버라이딩 한다.
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 테스트
    // - http://localhost:8080/web/ex05/test03.html 실행
    //
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("doGet() => GET 요청입니다.");
  }

  // HTTP POST 요청에 응답하고 싶다면
  // 상속 받은 메서드 중에서 doPost()을 오버라이딩 한다.
  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 테스트
    // - http://localhost:8080/java-web/ex05/test03.html 실행
    //
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("doPost() => POST 요청입니다.");
  }
}

 

 

package com.eomcs.web.ex05;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// MyHttpServlet 클래스를 사용하여 서블릿을 만드는 것도 편하지만,
// 여기에다가 HTTP 요청 방식에 따라 메서드를 구분해 놓는다면
// 서브 클래스를 만들기가 더 편리할 것이다.
//
@SuppressWarnings("serial")
public abstract class MyHttpServlet2 extends GenericServlet {

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {
    // 서블릿 컨테이너가 이 메서드를 호출하면

    // => 파라미터 값을 원래의 타입으로 변환한다.
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    // => 오버로딩한 service()를 호출한다.
    this.service(request, response);
  }

  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // HTTP 요청 방식에 따라 메서드를 분리하여 호출한다.
    String httpMethod = request.getMethod();

    switch (httpMethod) {
      case "GET":
        doGet(request, response);
        return;
      case "POST":
        doPost(request, response);
        return;
      case "PUT":
        doPut(request, response);
        return;
      case "HEAD":
        doHead(request, response);
        return;
      default:
        error(request, response);
    }
  }


  // 서브 클래스에서 오버라이딩 할 메서드라면 private으로 선언하지 말라.
  // => 서브 클래스에서 이 메서드를 오버라이딩 하지 않으면 오류를 출력하도록 한다.
  // => 즉 클라이언트의 이런 요청을 처리하라면 서브 클래스에서 그 요청 방식에 해당하는 메서드를
  // 오버라이딩 하면 된다.
  // => 클라이언트 요청이 들어오면
  // 서블릿 컨테이너
  // ==> service(ServletRequest,ServletResponse) 호출
  //     ==> service(HttpServletRequest,HttpServletResponse) 호출
  //         ==> doXxx(HttpServletRequest,HttpServletResponse) 호출
  //
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    error(request, response);
  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    error(request, response);
  }

  protected void doPut(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    error(request, response);
  }

  protected void doHead(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    error(request, response);
  }

  private void error(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    request.setCharacterEncoding("UTF-8");
    PrintWriter out = response.getWriter();
    out.println("해당 HTTP 요청을 처리할 수 없습니다.");
  }
}

 

 

HTTP 프로토콜 다루기 - HttpServlet 클래스를 활용하기
package com.eomcs.web.ex05;

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("/ex05/s4")
public class Servlet04 extends HttpServlet {

  private static final long serialVersionUID = 1L;

  // HTTP GET 요청에 응답하고 싶다면
  // 상속 받은 메서드 중에서 doGet()을 오버라이딩 한다.
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 테스트
    // - http://localhost:8080/web/ex05/test04.html 실행
    //
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("GET 요청입니다.");
  }

  // HTTP POST 요청에 응답하고 싶다면
  // 상속 받은 메서드 중에서 doPost()을 오버라이딩 한다.
  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 테스트
    // - http://localhost:8080/java-web/ex05/test04.html 실행
    //
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("POST 요청입니다.");
  }
}

 

 

 

com.eomcs.web.ex06

 

 

load on startup - 서블릿 객체 자동 생성하기(애노테이션으로 설정하기)
package com.eomcs.web.ex06;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 클라이언트가 실행을 요청하지 않아도 서블릿을 미리 생성하고 싶다면,
// loadOnStartup 프로퍼티 값을 지정하라.
//     loadOnStartup=실행순서
// 미리 생성할 서블릿이 여러 개 있다면, loadOnStartup에 지정한 순서대로 생성한다.
// 언제 서블릿을 미리 생성하는가?
// => 서블릿이 작업할 때 사용할 자원을 준비하는데 시간이 오래 걸리는 경우
//    웹 애플리케이션을 시작시킬 때 미리 서블릿 객체를 준비한다.
//    예) DB 연결, 소켓 연결, 필요한 환경 변수 로딩, 스프링 IoC 컨테이너 준비 등
//
//@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()");
  }
}

 

 

load on startup - 서블릿 객체 자동 생성하기(web.xml에 설정하기)
package com.eomcs.web.ex06;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 클라이언트가 실행을 요청하지 않아도 서블릿을 미리 생성하고 싶다면,
// 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()");
  }
}

 

 

서블릿 초기화 파라미터 - 애노테이션으로 설정하기
package com.eomcs.web.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 = "1111")})
@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 = "1111";

    // 위의 코드처럼 언제든 변경될 수 있는 값을
    // 소스코드에 직접 작성하는 방식은 좋지 않다.
    // 해결책?
    // => 이런 변경 값들은 외부에 두는 것이 관리에 편하다.
    // => 값이 바뀌더라도 소스를 변경할 필요가 없다.
    // => 보통 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.xml에서 설정하기
package com.eomcs.web.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"));
  }
}

 

webapp\WEB-INF\web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  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">

<!-- 컨텍스트(웹 애플리케이션 환경) 초기화 파라미터 
  => Servlet, Listener, Filter 에서 모두 사용할 수 있다.
-->

  <context-param>
    <param-name>jdbc2.driver</param-name>
    <param-value>org.mariadb.jdbc.Driver</param-value>
  </context-param>
  <context-param>
    <param-name>jdbc2.url</param-name>
    <param-value>jdbc:mariadb://localhost:3306/studydb</param-value>
  </context-param>
  <context-param>
    <param-name>jdbc2.username</param-name>
    <param-value>study</param-value>
  </context-param>
  <context-param>
    <param-name>jdbc2.password</param-name>
    <param-value>1111</param-value>
  </context-param>

 

 

컨텍스트 초기화 파라미터 - web.xml에서 설정하기
package com.eomcs.web.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"));
  }
}

 

 

 

com.eomcs.web.ex07

 

 

포워딩(forwarding) - 서블릿 실행을 위임하기
package com.eomcs.web.ex07;

import java.io.IOException;
import java.io.PrintWriter;
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;

@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=*
    //
    // 주의!
    // => + 연산을 수행하지 못한다.
    // 왜? 서버에 전송될 때 '+'문자가 아닌 공백(' ')문자가 전달되기 때문이다.
    // => + 연산자를 파라미터 값으로 보내고 싶다면 URL 인코딩 해야 한다.
    // => 왜? + 문자는 URL에서 한 칸의 공백을 의미한다.
    //    즉 getParamter("op")의 리턴 값이 공백(" ") 이다.
    // => + 문자의 URL 인코딩 값은?
    // %2b
    // => 따라서 + 연산을 파라미터 값으로 보내려면
    // http://localhost:8080/java-web/ex07/s1?a=100&b=200&op=%2b
    //
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    // 명심!
    // => 서블릿의 PrintWriter 객체를 통해 값을 출력하면
    //    클라이언트로 바로 출력되는 것이 아니라,
    //    내부에 마련된 버퍼로 출력된다.
    // => 그럼 출력 결과는 언제?
    //    - service() 메서드 호출이 끝났을 때 버퍼의 내용이 클라이언트로 출력된다.
    // => 만약 버퍼가 꽉 차면, service() 메서드 호출이 끝나기 전에 자동으로 출력된다.
    //
    // 따라서 다음 출력코드는 버퍼로 값을 출력한 것이다.
    // 아직 클라이언트로 보낸 상태가 아니다.
    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);
  }
}

 

 

포워딩(forwarding) - 서블릿 실행을 위임하기
package com.eomcs.web.ex07;

import java.io.IOException;
import java.io.PrintWriter;
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;

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

 

 

포워딩(forwarding) - 서블릿 실행을 위임하기
package com.eomcs.web.ex07;

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("/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!!!");
  }
}

 

 

인클루딩(including) - 다른 서블릿의 작업을 포함시키기
package com.eomcs.web.ex07;

import java.io.IOException;
import java.io.PrintWriter;
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;

@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("---------------------------------------");
  }
}

 

 

@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);

    // 이 메서드의 호출이 완료되면 이전 서블릿으로 되돌아 간다.
  }
}

 

 

@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);

    // 이 메서드의 호출이 완료되면 이전 서블릿으로 되돌아 간다.
  }
}

 

 

@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("해당 연산자를 지원하지 않습니다.");

    // 이 메서드의 호출이 완료되면 이전 서블릿으로 되돌아 간다.
  }
}

 

 

 

com.eomcs.web.ex08

 

 

리프래시 - 클라이언트에게 다른 URL을 요청하라는 명령
package com.eomcs.web.ex08;

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("/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
    //
    // 리프래시
    // => 서버로부터 응답을 받고 "내용을 출력"한 후
    //    지정된 시간이 경과되면 특정 URL을 자동으로 요청하도록 만들 수 있다.
    // => 보통 웹 페이지를 자동으로 이동시키고 싶을 때 사용한다.
    // => 예
    //    예1: 로그인 후 메인페이지로 자동 이동
    //    예2: 메일을 전송한 후 메일 목록 페이지로 자동 이동
    //    예3: 게시글 등록한 후 게시글 목록으로 자동 이동
    //    예4: 결제 완료 후 결제 상태 페이지로 자동 이동
    //
    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() 메서드의 호출이 끝나면
    // 비로서 응답 프로토콜에 맞춰
    // 클라이언트로 헤더와 내용을 전송한다.
  }
}


// HTTP 응답 프로토콜 예:
//
// HTTP/1.1 200
// Refresh: 3;url=s100 <--- 웹 브라우저는 이 헤더의 정보에 따라 다시 요청한다.
// Content-Type: text/plain;charset=UTF-8
// Content-Length: 28
// Date: Tue, 07 Apr 2020 06:46:25 GMT
// Keep-Alive: timeout=20
// Connection: keep-alive
//
// 안녕하세요! - /ex08/s1

 

 

리프래시 II - 클라이언트에게 다른 URL을 요청하라는 명령
package com.eomcs.web.ex08;

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("/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
    //
    // 리프래시
    // => 서버로부터 응답을 받고 내용을 출력한 후 특정 URL을 자동으로 요청하도록 만들 수 있다.
    // => 보통 웹 페이지를 자동으로 이동시키고 싶을 때 사용한다.
    // => 예 
    //    예1: 로그인 후 메인페이지로 자동 이동
    //    예2: 메일을 전송한 후 메일 목록 페이지로 자동 이동
    //    예3: 게시글 등록한 후 게시글 목록으로 자동 이동
    //    예4: 결제 완료 후 결제 상태 페이지로 자동 이동
    //

    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>

 

 

리다이렉트 - 응답할 때 콘텐트를 보내지 않는다. 바로 다른 페이지를 요청하라고 명령한다.
package com.eomcs.web.ex08;

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("/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
    //
    // 리다이렉트
    // => 클라이언트의 요청을 받은 후 콘텐트를 보내는 대신
    //    다른 페이지의 URL을 알려줄 때 사용한다.
    // => 웹 브라우저는 응답 받는 즉시 해당 페이지를 요청한다.
    //    웹 서버로부터 콘텐트를 받지 않았기 때문에 어떤 것도 출력하지 않는다.
    //    바로 다른 페이지로 이동한다.
    // => 리프래시와 달리 서버는 콘텐트(message-body)를 보내지 않는다.
    // => 사용 예:
    //    - 로그인 후 로그인 결과를 출력하지 않고 즉시 메인 화면으로 보내고 싶을 때
    //    - 결제완료 후 결과를 출력하지 않고 즉시 결제 상태 페이지로 보내고 싶을 때
    // => 리다이렉트 HTTP 응답 프로토콜
    //
    //    HTTP/1.1 302 <----- 리다이렉트 응답 상태 코드
    //    Location: s100 <----- 리다이렉트 URL
    //    Content-Type: text/html;charset=UTF-8
    //    Content-Length: 0
    //    Date: Tue, 02 Apr 2019 03:38:45 GMT
    //    빈 줄
    //         <---- 콘텐트를 보내지 않는다. 즉 message-body가 없다.
    //
    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");

    // 리다이렉트를 하는 순간 이전까지 버퍼로 출력된 내용은 모두 버려진다.
    // 왜? 리다이렉트는 클라이언트로 콘텐트를 보내지 않는다.
    //
    // 만약 출력한 내용이 버퍼를 꽉 채워서 자동으로 응답을 했다면 어떻게 되나요?
    // => 이미 응답했기 때문에 리다이렉트는 동작되지 않는다.
  }
}

// redirect HTTP 응답 프로토콜
// HTTP/1.1 302 <---- 요청한 자원이 다른 URL에 있음을 표시한다.
// Location: s100 <---- 다른 URL의 주소를 알려준다.
// Content-Type: text/html;charset=UTF-8
// Content-Length: 0
// Date: Tue, 07 Apr 2020 07:05:34 GMT
// Keep-Alive: timeout=20
// Connection: keep-alive
//
// <---내용을 보내지 않는다.

 

 

리프래시 또는 리다이렉트 최종 요청 페이지
package com.eomcs.web.ex08;

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("/ex08/s100")
@SuppressWarnings("serial")
public class Servlet100 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("반가워요 - /ex08/s100");
  }
}

 

 

 

com.eomcs.web.ex09

 

 

보관소에 값 넣기
package com.eomcs.web.ex09;

import java.io.IOException;
import java.io.PrintWriter;
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("/ex09/s1")
@SuppressWarnings("serial")
public class Servlet01 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex09/s1
    //

    // 1) ServletContext 보관소에 값 넣기
    // => ServletContext 객체는 웹 애플리케이션이 시작될 때 생성된다.
    ServletContext sc = this.getServletContext();
    sc.setAttribute("v1", "aaa");

    // 2) HttpSession 보관소에 값 넣기
    // => HttpSession 객체는 웹 브라우저에서
    //    '세션 아이디(예:고객번호, 스탬프 카드)'를 제공하지 않으면,
    //    getSession()을 호출할 때 생성된다.
    //    즉 이 요청을 한 클라이언트의 HttpSession 객체가 없다면 만들어준다.
    // => 웹 브라우저에서 '세션 아이디'를 제공하면,
    //    getSession()을 호출할 때 기존에 생성했던 세션 객체를 리턴한다.
    //    즉 이미 이 클라이언트를 위해 만든 객체가 있다면 그 객체를 리턴한다.
    HttpSession session = request.getSession();
    session.setAttribute("v2", "bbb");

    // 3) ServletRequest 보관소에 값 넣기
    // => ServletRequest 객체는 클라이언트가 요청할 때마다 생성된다.
    request.setAttribute("v3", "ccc");

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("보관소에 값을 넣었습니다. - /ex09/s1");

  }
}

 

 

보관소에 저장된 값 꺼내기
package com.eomcs.web.ex09;

import java.io.IOException;
import java.io.PrintWriter;
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("/ex09/s2")
@SuppressWarnings("serial")
public class Servlet02 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex09/s2
    //

    // 1) ServletContext 보관소에 저장된 값 꺼내기
    ServletContext sc = this.getServletContext();
    String v1 = (String) sc.getAttribute("v1");

    // 2) HttpSession 보관소에 저장된 값 꺼내기
    // => 이 요청을 한 클라이언트의 HttpSession 객체가 없다면 만들어준다.
    // => 이미 이 클라이언트를 위해 만든 객체가 있다면 그 객체를 리턴한다.
    HttpSession session = request.getSession();
    String v2 = (String) session.getAttribute("v2");

    // 3) ServletRequest 보관소에 저장된 값 꺼내기
    String v3 = (String) request.getAttribute("v3");

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("보관소에 저장된 값 꺼내기 - /ex09/s2");
    out.printf("v1 = %s\n", v1);
    out.printf("v2 = %s\n", v2);
    out.printf("v3 = %s\n", v3);

  }
}

 

 

보관소에 값 넣기 - forward/include 서블릿끼리 ServletRequest 공유하는 것 테스트
package com.eomcs.web.ex09;

import java.io.IOException;
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("/ex09/s11")
@SuppressWarnings("serial")
public class Servlet11 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex09/s11
    //

    // 1) ServletContext 보관소에 값 넣기
    ServletContext sc = this.getServletContext();
    sc.setAttribute("v1", "xxx");

    // 2) HttpSession 보관소에 값 넣기
    // => 이 요청을 한 클라이언트의 HttpSession 객체가 없다면 만들어준다.
    // => 이미 이 클라이언트를 위해 만든 객체가 있다면 그 객체를 리턴한다.
    HttpSession session = request.getSession();
    session.setAttribute("v2", "yyy");

    // 3) ServletRequest 보관소에 값 넣기
    request.setAttribute("v3", "zzz");

    // 주목!
    // => 포워드(인클루드 포함)할 때 이 서블릿이 파라미터로 받은
    // ServletRequest와 ServletResponse를 전달한다.
    // => 따라서 포워드/인클루드 서블릿들은 응답을 완료할 때까지 이 객체들을 공유하는 것이다.
    //
    request.getRequestDispatcher("s12").forward(request, response);

  }
}

 

 

package com.eomcs.web.ex09;

import java.io.IOException;
import java.io.PrintWriter;
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("/ex09/s12")
@SuppressWarnings("serial")
public class Servlet12 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex09/s11
    //

    // 1) ServletContext 보관소에 저장된 값 꺼내기
    ServletContext sc = this.getServletContext();
    String v1 = (String) sc.getAttribute("v1");

    // 2) HttpSession 보관소에 저장된 값 꺼내기
    // => 이 요청을 한 클라이언트의 HttpSession 객체가 없다면 만들어준다.
    // => 이미 이 클라이언트를 위해 만든 객체가 있다면 그 객체를 리턴한다.
    HttpSession session = request.getSession();
    String v2 = (String) session.getAttribute("v2");

    // 3) ServletRequest 보관소에 저장된 값 꺼내기
    String v3 = (String) request.getAttribute("v3");

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("보관소에 저장된 값 꺼내기 - /ex09/s12");
    out.printf("v1 = %s\n", v1);
    out.printf("v2 = %s\n", v2);
    out.printf("v3 = %s\n", v3);

  }
}

 

 

 

com.eomcs.web.ex10

 

 

쿠키(cookie) 보내기
package com.eomcs.web.ex10;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex10/s1")
@SuppressWarnings("serial")
public class Servlet01 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex10/s1
    //

    // 쿠키
    // => 웹서버가 웹브라우저에게 맡기는 데이터이다.
    // => 응답할 때 응답 헤더에 포함시켜 보낸다.
    // => 웹브라우저는 응답헤더로 받은 쿠키 데이터를 보관하고 있다가
    //    지정된 URL을 요청할 때 요청 헤더에 포함시켜 웹 서버에게 쿠키를 다시 보낸다.
    // => HTTP 응답 프로토콜
    //
    // HTTP/1.1 200
    // Set-Cookie: name=hong <---- Set-Cookie 헤더에 '이름=값' 형태로 쿠키를 보낸다.
    // Set-Cookie: age=20
    // Set-Cookie: working=true
    // Set-Cookie: name2=홍길동 <---- URL 인코딩 하지 않은 상태
    // Set-Cookie: name3=%ED%99%8D%EA%B8%B8%EB%8F%99 <---- URL 인코딩한 예
    // Content-Type: text/plain;charset=UTF-8
    // Content-Length: 35 Date: Wed, 03 Apr 2019 01:03:37 GMT
    // ...

    // 쿠키 생성
    // => 이름과 값으로 생성한다.
    // => 쿠키의 유효기간을 설정하지 않으면 웹브라우저가 종료될 때 까지 유지된다.
    //    웹브라우저를 종료하면 유효기간이 지정되지 않은 쿠키는 모두 삭제된다.
    // => 쿠키의 사용범위를 지정하지 않으면 현재 경로에 한정한다.
    //    쿠키를 보낼 때의 URL이 /ex10/s1 이라면,
    //    웹브라우저는 /ex10/* 경로를 요청할 때만 웹서버에게 쿠키를 보낸다.
    Cookie c1 = new Cookie("name", "hong");
    // 프로토콜 예 => Set-Cookie: name=hong


    // => 값은 반드시 문자열이어야 한다.
    //    만약 문자열이 아닌 값을 보내려면 Base64와 같은 인코딩 기법을 이용하여
    //    바이너리 데이터를 문자화시켜서 보내야 한다.
    Cookie c2 = new Cookie("age", "20");
    // 프로토콜 예 => Set-Cookie: age=20

    Cookie c3 = new Cookie("working", "true");
    // 프로토콜 예 => Set-Cookie: working=true

    // => 또한 값은 반드시 ISO-8859-1 이어야 한다.
    //    만약 UTF-8을 보내고 싶다면 URL 인코딩 같은 기법을 사용하여
    //    ASCII 코드화시켜 보내야 한다.
    Cookie c4 = new Cookie("name2", "홍길동");
    // 프로토콜 예 => Set-Cookie: name2=홍길동

    Cookie c5 = new Cookie("name3", URLEncoder.encode("홍길동", "UTF-8"));
    // 프로토콜 예 => Set-Cookie: name3=%ED%99%8D%EA%B8%B8%EB%8F%99

    // 쿠키를 응답 헤더에 포함시키기
    response.addCookie(c1);
    response.addCookie(c2);
    response.addCookie(c3);
    response.addCookie(c4);
    response.addCookie(c5);

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("/ex10/s1 - 쿠키 보냈습니다.");
  }
}

 

 

받은 쿠키(cookie) 읽기
package com.eomcs.web.ex10;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex10/s2")
@SuppressWarnings("serial")
public class Servlet02 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex10/s2
    //

    // 클라이언트 보낸 쿠키 읽기
    // => 요청 헤더에 포함된 쿠키를 읽는다.
    // => HTTP 요청 프로토콜

    // GET /java-web/ex10/s2 HTTP/1.1
    // Host: localhost:8080
    // Connection: keep-alive
    // Cookie: name=hong; age=20; working=true; name2=홍길동; name3=%ED%99%8D%EA%B8%B8%EB%8F%99
    // ...
    //


    // 쿠키 꺼내기
    // => 쿠키를 이름으로 한 개씩 추출할 수 없다.
    // => 한 번에 배열로 받아야 한다.
    // => 요청 헤더에 쿠키가 없으면 리턴 되는 것은 빈 배열이 아니라 null이다.
    // => 따라서 무조건 반복문을 돌리면 안된다.
    //
    Cookie[] cookies = request.getCookies();

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    if (cookies != null) {
      for (Cookie c : cookies) {
        // 쿠키 값이 'URL 인코딩'한 값이라면
        // 개발자가 직접 디코딩 해서 사용해야 한다.
        // 쿠키 값에 대해서는 서버가 자동으로 디코딩 해주지 않는다.
        out.printf("%s=%s,%s\n",
            c.getName(),
            c.getValue(),
            URLDecoder.decode(c.getValue(), "UTF-8"));
      }
    }

  }
}

 

 

쿠키(cookie) - 유효기간 설정하기
package com.eomcs.web.ex10;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex10/s11")
@SuppressWarnings("serial")
public class Servlet11 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex10/s11
    //

    // 쿠키 유효기간
    // => 유효기간을 설정하면 웹브라우저는 그 기간 동안 보관하고 있다가
    //    웹서버에게 쿠키를 보낸다.
    // => HTTP 응답 프로토콜
    //
    // HTTP/1.1 200
    // Set-Cookie: v1=aaa
    // Set-Cookie: v2=bbb; Max-Age=30; Expires=Wed, 08-Apr-2020 02:41:43 GMT
    // Set-Cookie: v3=ccc; Max-Age=60; Expires=Wed, 08-Apr-2020 02:42:13 GMT
    // Content-Type: text/plain;charset=UTF-8
    // Content-Length: 36
    // Date: Wed, 08 Apr 2020 02:41:13 GMT
    // Keep-Alive: timeout=20
    // Connection: keep-alive

    // 유효기간을 설정하지 않으면 웹브라우저가 실행되는 동안에만 웹서버에게 쿠키를 보낸다.
    // => 웹 브라우저는 메모리에 쿠키를 보관한다.
    Cookie c1 = new Cookie("v1", "aaa");

    // 유효기간을 설정하면 웹브라우저를 종료해도 삭제되지 않는다.
    // 단 유효기간이 지나면 웹서버에 보내지 않고 삭제한다.
    // => 웹 브라우저는 로컬 디스크에 쿠키를 보관한다.
    Cookie c2 = new Cookie("v2", "bbb");
    c2.setMaxAge(30); // 쿠키를 보낸 이후 30초 동안만 유효

    Cookie c3 = new Cookie("v3", "ccc");
    c3.setMaxAge(60); // 쿠키를 보낸 이후 60초 동안만 유효

    // 쿠키를 응답 헤더에 포함시키기
    response.addCookie(c1);
    response.addCookie(c2);
    response.addCookie(c3);

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("/ex10/s11 - 쿠키 보냈습니다.");
  }
}

 

 

쿠키(cookie) 읽기 - 유효기간이 설정된 쿠키 읽기
package com.eomcs.web.ex10;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex10/s12")
@SuppressWarnings("serial")
public class Servlet12 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex10/s12
    //

    // 클라이언트 보낸 쿠키 읽기
    // => 요청 헤더에 포함된 쿠키를 읽는다.
    // => HTTP 요청 프로토콜
    /*
     * GET /java-web/ex10/s2 HTTP/1.1 Host: localhost:8080 Connection: keep-alive ... Cookie:
     * name=hong; age=20; working=true; name2=홍길동; name3=%ED%99%8D%EA%B8%B8%EB%8F%99
     */

    // 쿠키 꺼내기
    // => 쿠키를 이름으로 한 개씩 추출할 수 없다.
    // => 한 번에 배열로 받아야 한다.
    // => 요청 헤더에 쿠키가 한 개라도 없으면 리턴 되는 것은 null이다.
    Cookie[] cookies = request.getCookies();

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    if (cookies != null) {
      for (Cookie c : cookies) {
        out.printf("%s=%s\n", c.getName(), c.getValue());
      }
    }

  }
}

 

 

쿠키(cookie) - 쿠키 사용 범위 지정하기
package com.eomcs.web.ex10;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex10/s21")
@SuppressWarnings("serial")
public class Servlet21 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex10/s21
    //

    // 쿠키 사용범위
    // => 쿠키의 사용 범위를 지정하지 않으면 쿠키를 발행한 URL 범위에 한정된다.
    //    즉 같은 URL로 요청할 때만 쿠키를 보낸다.
    // => 예)
    //    서버에서 쿠키를 발행한 URL : /ex10/s21
    //    클라이언트가 쿠키를 보내는 URL : /ex10/*
    //    클라이언트가 쿠키를 보내지 않는 URL  : /ex10/* 이외의 모든 URL
    // => HTTP 응답 프로토콜
    //
    // HTTP/1.1 200
    // Set-Cookie: v1=aaa
    // Set-Cookie: v2=bbb; Path=/eomcs-java-web/ex10/a
    // Set-Cookie: v3=ccc; Path=/eomcs-java-web
    // Content-Type: text/plain;charset=UTF-8
    // Content-Length: 36
    // Date: Wed, 08 Apr 2020 02:51:12 GMT
    // Keep-Alive: timeout=20
    // Connection: keep-alive
    //
    // 사용 범위를 지정하지 않은 쿠키
    // => 쿠키를 발급한 서블릿과 같은 경로이거나 하위 경로의 서블릿을 요청할 때만
    //    웹 브라우저가 서버에 쿠키를 보낸다.
    // => 기본: /web/ex10
    Cookie c1 = new Cookie("v1", "aaa");

    // 사용 범위 지정
    // => 쿠키를 발급한 서블릿의 경로에 상관없이 지정된 경로의 서블릿을 요청할 때
    // 웹 브라우저가 서버에 쿠키를 보낸다.
    Cookie c2 = new Cookie("v2", "bbb");
    c2.setPath("/web/ex10/a");

    Cookie c3 = new Cookie("v3", "ccc");
    c3.setPath("/web");

    // 어~ 왜 쿠키의 경로를 적을 때 웹 애플리케이션 루트(컨텍스트 루트)까지 적나요?
    // => 쿠키 경로는 서블릿 컨테이너가 사용하는 경로가 아니다.
    // => 웹 브라우저가 사용하는 경로다.
    // => 웹 브라우저에서 '/' 은 서버 루트를 의미한다.
    // => 따라서 웹 브라우저가 사용하는 경로를 지정할 때는 조심해야 한다.
    // '/'가 서버 루트를 의미하기 때문이다.
    // => 그래서 쿠키의 경로를 지정할 때는 웹 애플리케이션 루트(컨텍스트 루트)를 정확하게 지정해야 한다.


    // 쿠키를 응답 헤더에 포함시키기
    response.addCookie(c1);
    response.addCookie(c2);
    response.addCookie(c3);

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("/ex10/s21 - 쿠키 보냈습니다.");
  }
}

 

 

쿠키(cookie) 읽기 - 사용 범위가 지정된 쿠키 읽기
package com.eomcs.web.ex10;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex10/s22")
@SuppressWarnings("serial")
public class Servlet22 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex10/s22
    //

    Cookie[] cookies = request.getCookies();

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    if (cookies != null) {
      for (Cookie c : cookies) {
        out.printf("%s=%s\n", c.getName(), c.getValue());
      }
    }

  }
}

 

 

package com.eomcs.web.ex10;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex10/a/b/c/s23")
@SuppressWarnings("serial")
public class Servlet23 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex10/a/b/c/s23
    //

    Cookie[] cookies = request.getCookies();

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    if (cookies != null) {
      for (Cookie c : cookies) {
        out.printf("%s=%s\n", c.getName(), c.getValue());
      }
    }

  }
}

 

 

package com.eomcs.web.ex10;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/ex10_1/s24")
@SuppressWarnings("serial")
public class Servlet24 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex10_1/s24
    //

    Cookie[] cookies = request.getCookies();

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    if (cookies != null) {
      for (Cookie c : cookies) {
        out.printf("%s=%s\n", c.getName(), c.getValue());
      }
    }

  }
}

 

 

 

com.eomcs.web.ex11

 

 

세션(session)
package com.eomcs.web.ex11;

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("/ex11/s1")
@SuppressWarnings("serial")
public class Servlet01 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex11/s1
    //

    // 세션
    // => 클라이언트를 식별하는 기술이다.
    // => HTTP 프로토콜은 Stateless 방식으로 통신을 한다.
    //    즉 연결한 후 요청하고 응답을 받으면 연결을 끊는다.
    //    그래서 서버는 클라이언트가 요청할 때 마다 누구인지 알 수 없다.
    // => 이를 해결하기 위해
    //    클라이언트가 접속하면 웹 서버는 그 클라이언트를 위한
    //    고유 번호를 발급(쿠키 이용)한다.
    //    이 고유 번호를 '세션 아이디'라 부른다.
    // => 웹 브라우저는 세션 아이디를 쿠키에 보관해 두었다가
    //    그 서버에 요청할 때 마다 세션 아이디를 보낸다.
    //    왜? 세션 아이디는 쿠키이다.
    // => 세션 아이디를 쿠키로 보낼 때 유효기간을 설정하지 않았기 때문에
    //    웹 브라우저를 종료하면 세션 아이디 쿠키는 삭제된다.
    // => 세션 아이디 쿠키의 사용 범위는 웹 애플리케이션이다.
    //    예) /eomcs-java-web
    //    따라서 같은 웹 애플리케이션의 서블릿을 실행할 때는
    //    무조건 세션 아이디를 보낸다.
    //
    // 세션 아이디는 언제 발급하는가?
    // => 새 세션을 생성할 때 세션 아이디를 발급한다.
    //
    // 언제 새 세션을 생성하는가?
    // => 세션이 없거나 유효기간이 지난 경우, 
    //    request.getSesssion()을 호출할 때 생성한다.
    // => 새로 세션을 생성하면 그 세션 ID를 쿠키로 웹브라우저에게 보낸다.
    // => 세션이 없는 경우?
    //    클라이언트가 세션 아이디를 보내지 않을 때
    // => 세션 유효기간이 지난 경우?
    //    세션을 생성한 후 서버에 설정된 시간이 지나도록
    //    클라이언트의 요청이 않으면
    //    서버는 세션을 무효화시킨다.
    // => HTTP 응답 프로토콜
    //
    // HTTP/1.1 200
    // Set-Cookie: JSESSIONID=5801C115615A2C9074AC0B78E31C5F21; Path=/eomcs-java-web; HttpOnly
    // Content-Type: text/plain;charset=UTF-8
    // Content-Length: 44
    // Date: Wed, 08 Apr 2020 03:10:58 GMT
    // Keep-Alive: timeout=20
    // Connection: keep-alive

    // 세션 생성하기 : getSession() 호출
    //
    // 1) 클라이언트가 세션 아이디를 쿠키로 전송
    //    - 서버에서는 해당 아이디의 세션을 찾는다.
    //    - 있으면, 그 세션을 리턴한다.
    //    - 있는데 세션의 유효 기간이 지났다면, 새로 세션을 만들어 리턴한다.
    //    - 없다면, 새로 세션을 만들어 리턴한다.
    // 2) 클라이언트가 세션 아이디를 보내지 않은 경우
    //    - 서버는 새 세션을 만들어 리턴한다.
    //
    // 새로 세션을 만들면, 무조건 응답할 때 새 세션의 아이디를 쿠키로 보낸다.
    //
    HttpSession session = request.getSession();

    // 세션에 데이터 보관하기
    session.setAttribute("v1", "aaa");

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("/ex11/s1 - 세션을 생성하였습니다!");
  }
}

 

 

세션(session) - 세션에서 값 꺼내기
package com.eomcs.web.ex11;

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("/ex11/s2")
@SuppressWarnings("serial")
public class Servlet02 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex11/s2
    //

    // 세션 아이디 보내기
    // => 서버로부터 쿠키를 통해 받은 세션 아이디는
    //    웹 브라우저가 해당 서버에 요청할 때마다
    //    쿠키로 세션 아이디를 보낸다.
    // => HTTP 요청 프로토콜
    //
    // GET /eomcs-java-web/ex11/s2 HTTP/1.1
    // Host: localhost:9999
    // Connection: keep-alive
    // ...
    // Cookie: JSESSIONID=AAEEF5A0E55596AB47FF69573B179CB5 <--- 세션아이디

    HttpSession session = request.getSession();
    // 1) 클라이언트가 세션 아이디를 보냈다면,
    //    => 그 아이디에 해당하는 세션 객체가 유효하다면,
    //       => 기존의 HttpSession 객체를 리턴한다.
    //    => 그 아이디에 해당하는 세션 객체가 유효하지 않다면,(서버에서 정한 시간 동안 요청이 없었다면)
    //       => 2) 번으로 간다. 
    // 2) 클라이언트가 세션 아이디를 보내지 않았다면,
    //    => 새 HttpSession 객체를 생성하여 리턴한다.
    //    => 응답 할 때 응답프로토콜에 새로 생성한 HttpSession 객체의 세션ID를 쿠키로 보낸다.

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    // 세션에서 값 꺼내기
    // => 웹 브라우저를 종료하면 이전에 /ex11/s1 을 실행했을 때
    //    서버로부터 받은 세션 아이디 쿠키가 삭제된다.
    // => 그런 후에 웹 브라우저에서 이 서블릿을 요청하면
    //    getSession() 메서드는 새 세션 객체를 생성한 후 리턴한다.
    //    따라서 새 세션에는 v1 이라는 이름으로 저장된 값이 없기 때문에
    //    null을 출력할 것이다.
    //
    out.printf("v1=%s\n", session.getAttribute("v1"));
  }
}

 

 

세션(session)의 활용
package com.eomcs.web.ex11;

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("/ex11/s11")
@SuppressWarnings("serial")
public class Servlet11 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("<html><head><title>페이지1</title></head><body>");
    out.println("<form action='s12' method='post'>");
    out.println("이름: <input type='text' name='name'><br>");
    out.println("<button>다음</button>");
    out.println("</form>");
    out.println("</body></html>");
  }
}

 

 

세션(session)의 활용 - 페이지2
package com.eomcs.web.ex11;

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("/ex11/s12")
@SuppressWarnings("serial")
public class Servlet12 extends HttpServlet {

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    request.setCharacterEncoding("UTF-8");

    // 세션을 준비한다.
    HttpSession session = request.getSession();

    // 클라이언트가 보낸 데이터를 세션에 보관한다.
    session.setAttribute("name", request.getParameter("name"));

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("<html><head><title>페이지2</title></head><body>");
    out.println("<form action='s13' method='post'>");
    out.println("나이: <input type='text' name='age'><br>");
    out.println("<button>다음</button>");
    out.println("</form>");
    out.println("</body></html>");
  }
}

 

 

세션(session)의 활용 - 페이지3
package com.eomcs.web.ex11;

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("/ex11/s13")
@SuppressWarnings("serial")
public class Servlet13 extends HttpServlet {

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 세션을 준비한다.
    HttpSession session = request.getSession();

    // 클라이언트가 보낸 데이터를 세션에 보관한다.
    session.setAttribute("age", request.getParameter("age"));

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("<html><head><title>페이지3</title></head><body>");
    out.println("<form action='s14' method='post'>");
    out.println("전화: <input type='text' name='tel'><br>");
    out.println("<button>다음</button>");
    out.println("</form>");
    out.println("</body></html>");
  }
}

 

 

세션(session)의 활용 - 페이지4
package com.eomcs.web.ex11;

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("/ex11/s14")
@SuppressWarnings("serial")
public class Servlet14 extends HttpServlet {

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 세션을 준비한다.
    HttpSession session = request.getSession();

    // 클라이언트가 보낸 데이터를 세션에 보관한다.
    String tel = request.getParameter("tel");

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("<html><head><title>페이지4</title></head><body>");

    // 이전 서블릿에서 세션에 보관했던 값을 꺼내기
    // => 이것이 세션 보관소를 사용하는 이유이다.
    // => 여러 서블릿이 수행한 작업을 공유할 수 있다.
    // => 단 같은 클라이언트일 경우에만 사용할 수 있다.
    //
    out.printf("이름: %s<br>\n", session.getAttribute("name"));
    out.printf("나이: %s<br>\n", session.getAttribute("age"));

    out.printf("전화: %s<br>\n", tel);
    out.println("</body></html>");
  }
}

 

 

세션(session)의 타임아웃 설정
package com.eomcs.web.ex11;

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("/ex11/s21")
@SuppressWarnings("serial")
public class Servlet21 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    HttpSession session = request.getSession();

    // 세션의 유효 시간(초)을 설정한다.
    // => 클라이언트가 요청하는 순간부터 세션 시간을 카운트 한다.
    // => 만약 10초 이내에 클라이언트의 요청이 없으면
    //    10초를 초과하는 순간 HttpSession 객체를 무효화시킨다.
    //    (어떤 서블릿을 요청하는 상관없다)
    // => 10초 이내에 다시 클라이언트 요청이 있다면
    //    카운트를 0부터 다시 시작한다.
    // => 예) 인터넷 뱅킹 에서 보통 세션 타임아웃을 10분으로 설정한다.
    //        클라이언트가 어떤 링크를 누르면 카운트는 다시 10분으로 초기화된다.
    session.setMaxInactiveInterval(10);

    session.setAttribute("v1", "aaa");

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("/ex11/s21 실행함!");
  }
}

 

 

세션(session)의 타임아웃 설정 테스트
package com.eomcs.web.ex11;

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("/ex11/s22")
@SuppressWarnings("serial")
public class Servlet22 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // setMaxInactiveInterval() 메서드로 지정된 시간 동안
    // 서버에 아무런 요청을 하지 않으면 해당 세션은 무효화 된다.
    // 무효화된 세션은 사용할 수 없기 때문에
    // getSession()은 새 세션을 만들어 리턴한다.
    //
    HttpSession session = request.getSession();

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.printf("v1=%s\n", session.getAttribute("v1"));
  }
}

 

 

세션(session) 무효화시키기
package com.eomcs.web.ex11;

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("/ex11/s31")
@SuppressWarnings("serial")
public class Servlet31 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    HttpSession session = request.getSession();
    session.setAttribute("v1", "aaa");

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("/ex11/s31 실행함!");
  }
}

 

 

세션(session) 무효화시키기 - 값 꺼내기
package com.eomcs.web.ex11;

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("/ex11/s32")
@SuppressWarnings("serial")
public class Servlet32 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 클라이언트가 세션 아이디 쿠키를 서버에 보냈다 하더라도
    // 해당 세션이 무효화 되었다면
    // getSession()은 새 세션 객체를 만들어 리턴한다.
    //
    HttpSession session = request.getSession();

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.printf("v1=%s\n", session.getAttribute("v1"));
  }
}

 

 

세션(session) 무효화시키기 - 세션 무효화하기
package com.eomcs.web.ex11;

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("/ex11/s33")
@SuppressWarnings("serial")
public class Servlet33 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    HttpSession session = request.getSession();
    session.invalidate(); // 세션을 무효화시킨다.

    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("세션을 무효화시켰습니다.");
  }
}

 

 

 

com.eomcs.web.ex12

 

 

서블릿 배치: 1) web.xml 에 배치 정보를 등록
package com.eomcs.web.ex12;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Servlet01 extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {

    resp.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    out.println("안녕하세요!");
  }
}

 

web.xml

 <servlet>
    <servlet-name>ex12.s01</servlet-name>
    <servlet-class>com.eomcs.web.ex12.Servlet01</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ex12.s01</servlet-name>
    <url-pattern>/ex12/s01</url-pattern>
  </servlet-mapping>

 

 

서블릿 배치: 2) 애노테이션으로 배치 정보를 등록
package com.eomcs.web.ex12;

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("/ex12/s02")
public class Servlet02 extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {

    resp.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    out.println("안녕하세요!");
  }
}

 

 

서블릿 배치: 3) 객체를 직접 생성하여 등록 : 웹 애플리케이션을 시작할 때
package com.eomcs.web.ex12;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Servlet03 extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {

    resp.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    out.println("안녕하세요!");
  }
}

 

 

package com.eomcs.web.ex12;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.annotation.WebListener;

@WebListener
public class Listener01 implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent sce) {
    System.out.println("웹애플리케이션 시작!!");
    System.out.println("서블릿 배치!");

    // 1) 서블릿 정보를 관리하는 객체를 꺼낸다.
    ServletContext sc = sce.getServletContext();

    // 2) 서블릿 컨테이너에 등록할 서블릿 객체를 준비한다.
    Servlet03 servlet = new Servlet03();

    // 3) ServletContext 객체를 통해 서블릿 객체를 등록한다.
    Dynamic 서블릿설정정보 = sc.addServlet("ex12.s03", servlet);

    // 4) 등록된 서블릿의 배치 정보를 설정한다.
    서블릿설정정보.addMapping("/ex12/s03");

  }
}