반응형

eomcs-java-web

web

web #ex01

Servlet01 -> Servlet이 가지고 있는 5개의 메소드 // init(), destroy(), service(), getServletInfo(), getServletConfig()

         // init(), destroy(), service() // 3개 // Servlet의 Lifecycle 메서드

         // init() // 생성 // 서블릿 객체를 생성하고 초기 작업을 시행하게 하는 메서드

         // 서블릿을 실행할 때 사용할 자원을 이 메서드에서 준비한다. // 서블릿 객체 생성 직후 1회만 메서드가 실행된다.

         // 파라미터로 받은 ServletConfig 객체는 인스턴스 변수에 보관해 두었다가 필요할 때 사용

         // destroy() // 소멸 // 소멸하기전에 서블릿 객체를 불러 마지막 작업을 수행하게 하는 메서드

         // 웹 애플리케이션이나 서버를 종료할 때 이 서블릿이 만든 자원을 해제할 수 있도록

         // 서블릿 컨테이너가 이 메서드를 호출한다. // 서블릿 객체 소멸 직전 1회만 메서드가 실행된다.

         // init()에서 준비한 자원을 보통 이 메서드에서 해제한다.

         // service() // 실행 // 서블릿 객체를 불러서 클라이언트가 요청한 작업을 수행 시키는 메소드

         // 클라이언트가 이 서블릿의 실행을 요청할 때마다 호출된다.

         // getServletConfig() // 서블릿에서 작업을 수행하는 중에 서블릿 관련 설정 정보를 꺼낼 때 이 메서드를 호출

         // getServletInfo() // 서블릿 컨테이너가 관리자 화면을 출력할 때 이 메서드를 호출

         // Servlet 클래스 사용 //

         // 서블릿 클래스를 만든 후 배치 파일(web.xml; DD 파일)에 서블릿 정보를 등록해야만 실행될 수 있다.

         // WEB-INF/web.xml // DD File // Deployment Descriptor File

         // 서블릿 실행 방법 // http://서버주소:포트번호/웹애플리케이션이름/서블릿URL

         // http://localhost:9999/bitcamp-java-web/ohora/haha

         // 서블릿 구동 과정 // 

         // 1. 웹 브라우저(ClientApp)가 서블릿 실행을 요청한다.

         // 2. 서블릿 컨테이너는 해당 URL의 서블릿 객체를 찾는다.

         // 3.1 서블릿 객체가 없다면,

         // 서블릿 클래스에 대해 인스턴스를 생성 - 서블릿 객체의 생성자를 호출 - init()를 호출 - service()를 호출

         // 3.2 서블릿 객체가 있다면, service()를 호출

         // 4. 웹 애플리케이션이 종료된다면, 생성된 모든 서블릿들의 destroy() 메서드를 호출

         // Servlet 사용 주의 // 

         // 클라이언트마다 구분되어야 할 데이터는 서블릿 인스턴스 변수에 보관해서는 안된다.

         // 서블릿 인스턴스는 오직 클래스 마다 한 개만 생성되어 

         // 모든 클라이언트가 같은 서블릿 인스턴스를 사용 // 인스턴스를 모든 클라이언트가 공유하기 때문

Servlet02 -> GenericServlet 추상 클래스

         // javax.servlet.Servlet 인터페이스를 service() 메서드만 남겨두고 나머지 메서드들은 모두 구현

         // 이 클래스를 상속 받는 서브 클래스는 service() 만 구현하면 된다.

         // GenericServlet 추상 클래스는 java.io.Serialize 인터페이스를 구현하였다.

         // serialVersionUID 변수 값을 설정해야 한다.

Servlet03 -> HttpServlet 추상 클래스 상속

         // doGet(), doPost(), doHead(), doPut() 등을 호출하게 프로그램 되어 있다.

         // service()를 오버라이딩 하는 대신에 doGet(), doPost(), doHead() 등을 오버라이딩 해야 한다.

         // 호출과정 // 

         // 1. 웹브라우저가 톰캣 서버에 요청 

         // 2. 톰캣 서버가 서블릿 객체에 대해 service(ServletRequest, ServletResponse) 호출 

         // 3. service(HttpServletRequest, HttpServletResponse) 호출

         // 4. doGet(HttpServletRequest, HttpServletResponse) 호출

Servlet04 -> @WebServlet 애노테이션

         // web.xml 에 서블릿 정보를 설정하는 대신에 애노테이션을 사용하여 서블릿을 설정할 수 있다.

         // metadata-complete="false" // web.xml의  태그에 false로 속성을 추가해야 한다.

 

web #ex02

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

         // Filter 만들기 // javax.servlet.Filter 인터페이스 규칙에 따라 작성

         // 필터 배포하기 // DD 파일(web.xml)에 설정하거나 애노테이션으로 설정하면 된다.

         // 필터의 용도 // 서블릿을 실행하기 전후에 필요한 작업을 수행

         // 서블릿 실행 전 //

         // ex) 웹브라우저가 보낸 암호화된 파라미터 값을 서블릿으로 전달하기 전에 암호 해제하기

         // ex) 웹브라우저가 보낸 압축된 데이터를 서블릿으로 전달하기 전에 압축 해제

         // ex) 서블릿의 실행을 요청할 권한이 있는지 검사

         // ex) 로그인 사용자인지 검사 // ex) 로그 남기기

         // 서블릿 실행 후 //

         // ex) 클라이언트로 보낼 데이터를 압축하기

         // ex) 클라이언트로 보낼 데이터를 암호화시키기

         // Filter이 가지고 있는 5개의 메소드 // init(), destroy(), doFilter()

         // init() // 필터 객체를 생성한 후 제일 처음으로 호출 // 필터가 사용할 자원을 이 메서드에서 준비

         // 필터 객체 생성 직후 1회만 메서드가 실행된다.

         // destroy() // 웹 애플리케이션이 종료될 때 호출 // init()에서 준비한 자원을 해제

         // 필터 객체 소멸 직후 1회만 메서드가 실행된다.

         // doFilter() // 필터를 설정할 때 지정된 URL의 요청이 들어 올 때 마다 호출 //

         // 서블릿이 실행되기 전에 필터가 먼저 실행 // 서블릿을 실행한 후 다시 필터로 리턴

Filter02 -> 필터 실행 순서

         // 필터의 실행 순서를 임의로 조정할 수 없다.

         // 필터의 실행 순서에 상관없이 각 필터가 독립적으로 동작하도록 작성해야 한다.

Listener01 -> 리스너 만들기 

         // 서블릿 컨테이너 또는 서블릿, 세션 등의 객체 상태가 변경되었을 때 보고 받는 옵저버

         // "Observer" 디자인 패턴 적용

         // ServletContextListener // 

         // 서블릿 컨테이너를 시작하거나 종료할 때 보고 받고 싶다면 이 인터페이스를 구현

         // ServletRequestListener // 

         // 요청이 들어오거나 종료될 때 보고 받고 싶다면 이 인터페이스를 구현

         // HttpSessionListener //

         // 세션이 생성되거나 종료될 때 보고 받고 싶다면 이 인터페이스를 구현

         // XxxListener

         // 기타 다양한 인터페이스가 있어, 문서를 참고

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

         // 리스너의 용도 // 서블릿 컨테이너나, 세션 등이 특별한 상태일 때 필요한 작업을 수행한다.

         // ServletContextListener // 웹 애플리케이션을 시작할 때 Spring IoC 컨테이너 준비하기

         // 웹 애플리케이션을 시작할 때 DB 커넥션 풀 준비하기

         // 웹 애플리케이션을 종료할 때 DB 커넥션 풀에 들어 있는 모든 연결을 해제하기

         // ServletRequestListener // 요청이 들어 올 때 로그 남기기

Listener02 ->리스너 테스트 

 

web #ex03

Servlet01 -> 문자표 지정하기 전 // 기본적으로 아스키 코드(ASCII)로 변환하여 출력

         // 자바(Unicode2;UTF-16) -> 출력문자(ASCII) // 자바코드를 자동적으로 아스키 코드로 변환한다.

Servlet02 -> 한글 깨짐 현상 처리

         // res.setContentType("text/plain;charset=UTF-8"); // text/plain 으로 ContentType 지정 // UTF-16 -> UTF-8

Servlet03 -> HTML 출력하기

         // res.setContentType("text/html;charset=UTF-8"); // text/html으로 ContentType 지정

         // HTML 출력할 때, MIME 타입에 HTML을 지정하지 않으면 웹 브라우저는 일반 텍스트로 간주한다.

Servlet04 ->바이너리 데이터 출력하기

         // /WEB-INF/photo.jpeg 파일의 실제 경로 알아내기

         // 1. 서블릿의 환경 정보를 다루는 객체를 먼저 얻는다.

         // ServletContext ctx = req.getServletContext();

         // 2. ServletContext를 통해 웹 자원의 실제 경로를 알아낸다.

         // getRealPath(현재 웹 애플리케이션의 파일 경로) // 실제 전체 경로를 리턴

         // String path = ctx.getRealPath("/WEB-INF/photo.jpeg");

         // FileInputStream in = new FileInputStream(path);

         // 3. 바이너리를 출력할 때 MIME 타입을 지정해야 웹 브라우저가 제대로 출력할 수 있다.

         // 웹 브라우저가 모르는 형식을 지정하면 웹 브라우저는 처리하지 못하기 때문에 다운로드 대화상자를 띄운다.

 

web #ex04

Servlet01 -> GET 요청 데이터 읽기 // 응답, 요청 프로토콜 https://te-ma.tistory.com/209 에서 상세 확인

         // GET 요청 // 웹 브라우저에 URL을 입력한 후 엔터를 치면 GET 요청을 보낸다.

         // 웹 페이지에서 링크를 클릭하면(자바스크립트 처리하지 않은 상태) GET 요청을 보낸다.

         // 웹 페이지의 폼(method='GET' 일 때)에서 전송 버튼을 클릭하면 GET 요청을 보낸다.

         // 웹 브라우저에서 웹 서버의 자원 요청하는 방법 //

         // 1. 서블릿 클래스를 실행하고 싶을 때

// servlet class 실제 위치 : ex) wtpwebapps/eomcs-java-web/WEB-INF/classes/com/eomcs/web/ex04/servlet01.class

         // 요청시 : http://localhost:9999/eomcs-java-web/ex04/s1

         // 해당 서블릿을 서버에 등록할 때 사용한 URL을 지정해야 한다.

         // 2. HTML, CSS, JavaScript, JPEG 등 정적 파일을 받고 싶을 때

         // 정적 파일 실제 위치 : ex) wtpwebapps/eomcs-java-web/ex04/test01.html

         // 요청 : ex) http://localhost:9999/eomcs-java-web/ex04/test01.html

         // 3. /WEB-INF/ 폴더에 있는 정적 파일을 받고 싶을 때

         // 정적 파일 실제 위치 : ex) wtpwebapps/eomcs-java-web/WEB-INF/ex04/test01.html

         // 요청 : ex) WEB-INF 폴더 아래에 있는 파일은 클라이언트에서 요청할 수 없다.

         // 웹 애플리케이션의 정보를 두는 폴더이기 때문이다.

         // HTTP 요청 프로토콜 // 

         // method sp request-URI sp http_version CRLF // Request-Line 이라고 함.

         // (general header | request header | entity header) CRLF

         // CRLF // CRLF는 텍스트 줄의 끝을 의미

         // message-body

         // method는 GET, POST, PUT, DELETE, TRACE, CONNECT, HEAD, OPTIONS // 8가지이다. // 나머지는 밑에 설명

         // GET 요청은 데이터를 request-URI에 붙여서 보낸다.

         // ex) /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

         // 데이터 형식 // 이름=값&이름=값&이름=값

         // URL 인코딩 // 데이터를 보낼 때 7bit 네트워크 장비를 거치면 8비트 데이터가 깨진다.

         // 이를 방지하고자 보내는 데이터를 7비트로 변환 // 원래 코드 값을 아스키(ASCII) 문자 코드로 변환

         // ASCII 코드는 7비트이기 때문에 데이터를 주고 받을 때 깨지지 않는다.

         // 단 8비트 데이터를 ASCII 코드(7비트)로 변환하면, 1데이터 손실이 일어나게 된다.

         // 따라서 이 손실을 막기 위해, URL인코딩을 해야한다.

         // URL 인코딩이란? 문자 코드의 값을 ASCII 코드화시키는 것

         // http_version // HTTP 버전을 의미

         // "HTTP" "/" 1*DIGIT "." 1*DIGIT 와 같이 정의되는데 현재 HTTP/1.0 HTTP/1.1이 있다.

         // header // header는 general-header, request-header, entity-header // 3가지 종류가 있다.

         // General Header // Cache-Control, Connection, Date, Pragma, Trailer, Transfer-Enco, Upgrade, Via, Warning

         // Request Header // Accept, Accept-Charset, Accept-Encoding, Accept-Language, Authorization, Expect, From,

         // Host, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Max-Forwards,

         // Proxy-Authorization, Range, Referer, TE, User-Agent 등

         // Entity Header // Allow, Content-Encoding, Content-Language, Content-Length, Content-Location,

         // Content-MD5, Content-Range, Content-Type, Expires, Last-Modified, extension-header

         // name : content // 헤더마다 위와 같은 name을 가지고 있고, 해당하는 content를 넣어 표현한다.

         // 각 값들은 공백이나, 탭으로 구분될 수 있고, 각 해더는 CRLF로 구분된다.

GET /java-web/ex04/s1?name=%ED%99%8D%EA%B8%B8%EB%8F%99&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, 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, 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 응답 프로토콜 // 

         // HTTP-Version SP Status-Code SP Reason-Phrase CRLF // status-line이라 함.

         // (general header | response header | entity header) CRLF

         // CRLF

         // message-body

         // Status-Code // 3자리 숫자로 된 상태를 나타내는 코드

         // 1xx // 요청을 받았고, 처리중임을 표현

         // 2xx // 성공했음을 표현

         // 200 // 요청을 정상적으로 처리했음

         // 201 // 요청한 자원을 생성했음

         // 3xx // 요청한 자원이 다른 장소로 옮겨졌음을 표현

         // 301 // 요청한 자원이 다른 장소로 이동 했음

         // 304 // 요청한 자원이 변경되지 않았음. 따라서 기존에 다운로드 받은 것을 그대로 사용하라고 요구함.

         // 4xx // 잘못된 형식으로 요청했음을 표현

         // 401 요청 권한이 없음.

         // 403 // 요청한 자원에 대한 권한이 없어 실행을 거절함.

         // 404 // 요청한 자원을 찾을 수 없음.

         // 5xx // 서버쪽에서 오류가 발생했음을 표현

         // 500 // 서버에서 프로그램을 실행하다가 오류 발생함.

         // header // general header와 Entity header는 요청부분과 동일

         // Response Header // Accept-Ranges, Age, ETag, Location, Proxy-Authenticate, Retry-After,

         // Server, Vary, WWW-Authenticate

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

          // URI (Uniform Resource Identifier) // 웹 자원의 위치를 가리키는 식별자 // URL과 URN이 있다.

          // URL(Uniform Resource Locator) // scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

          // ex) http://localhost:8080/ex04/s1?name=홍길동&age=20

          // URN(Uniform Resource Name) //  <URN> ::= "urn:" <NID> ":" <NSS>

          // ex) urn:lex:eu:council:directive:2010-03-09;2010-19-UE

Servlet02 -> POST 요청 데이터 읽기 

         // POST 요청 // 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.

         // 웹 브라우저가 보낸 데이터 읽기 // 데이터를 읽을 때는 GET 요청과 POST 요청이 같다.

         // ServletRequest.getParameter("파라미터이름") // POST로 보낸 데이터는 영어(ISO-8859-1)로 간주한다

         // 그래서 영어 코드를 자바에서 사용하는 UTF-8로 변환한다.

         // ex) ABC가각 // 웹에서 보낸다고 가정하고 설명함.

         // 실제 웹 브라우저가 "ABC가각" 문자열을 보낼 때 UTF-8 코드를 보낸다. // 414243EAB080EAB081

         // 그런데 서블릿에서는 이 코드 값을 ISO-8859-1 코드라고 간주

      // getParameter()로 값을 꺼내면 UTF-16으로 바꾼 값을 리턴 // 0041 0042 0043 00EA 00B0 0080 00EA 00B0 0081

         // 바이트에 00을 붙여 문자열을 만들어 리턴 // 영어를 2바이트 유니코드로 바꿀 때 앞에 00을 붙이면 되기 때문

         //  ABCê°€ê° // 변환된 코드를 화면에 출력하면 이렇게 나온다.

    req.setCharacterEncoding("UTF-8");

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

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

         // getParameter()를 최초로 호출하기 전에 클라이언트가 보낸 데이터의 인코딩 형식을 먼저 알려줘야 한다.

         // getParameter가 이전에 한번이라도 호출 된 후라면 적용되지 않는다.

         // GET 요청 vs POST 요청

         // 1. 전송 데이터 용량

         // GET // 게시글 조회나 검색어 입력 같은 간단한 데이터 전송

GET /ex01/s1?id=a@naver.com&pwd=1111 HTTP/1.1
HOST :
.
.
.

         // 대부분의 웹서버가 request-line과 헤더의 크기를 8KB로 제한하고 있다.

         // 위 예제처럼 GET은 값이 헤더에 들어가게 된다.

         // 따라서 긴 게시글과 같은 큰 용량의 데이터를 GET 방식으로 전송할 수 없다.

         // POST // 게시글 등록이나 첨부파일 같은 큰 데이터 전송

POST /ex01/s1 HTTP/1.1
HOST: 
.
.
.

id=a@test.com&pwd=1111

         // HTTP 요청 헤더 다음에 message-body 부분에 데이터를 두기 때문에

         // 용량의 제한 없이 웹 서버에 전송할 수 있다.

         // 위의 예제처럼 POST는 값이 message-body에 들어가게 된다.

         // 즉 웹 서버가 제한하지 않는 한 전송 데이터의 크기에 제한이 없다.

         // 웹 서버가 제한 한다? // 특정 사이트에서는 게시글의 크기나 첨부파일의 크기에 제한을 둔다.

         // 2. 바이너리 데이터 전송

         // GET // 바이너리 Data 전송 불가 // 어거지로 가능은 하나 그렇게 안쓴다.

         // request-URI가 텍스트로 되어 있다. // 따라서 바이너리 데이터를 request-URI에 붙여서 전송할 수 없다.

         // URL에 Data가 포함되기 때문이다.

         // 꼭 GET 요청으로 바이너리 데이터를 보내고자 한다면? // 바이너리 데이터를 텍스트로 변환하면 된다. 

         // ex) 바이너리 데이터를 Base64로 인코딩하여 텍스트를 만든 후, GET 요청 방식대로 이름=값으로 보내면 된다.

         // 그래도 결국 용량 제한 때문에 바이너리 데이터를 GET 요청으로 전송하는 것은 바람직하지 않다.

         // POST // multipart 형식으로 보낼 때는 바이너리 Data 전송 가능 // 첨부파일 전송때 사용

         // POST도 이름=값 형태로는 바이너리 값을 전송할 수 없다. // multipart 형식을 사용하면 전송 가능

         // 3. 보안 // 보안이 가장 좋은건 POST가 아니라, HTTPS 방식을 사용하는 것이다.

         // GET // URL에 전송 데이터가 포함되어 있기 때문에

         // 사용자 아이디나 암호 같은 데이터를 GET 방식으로 전송하는 것은 위험하다.

         // 웹 브라우저는 주소 창에 입력한 값을 내부 캐시에 보관해 두기 때문이다.

         // POST // mesage-body 부분에 데이터가 있기 때문에 웹 브라우저는 캐시에 보관하지 않는다.

         // 또한 주소 창에도 보이지 않는다. // 사용자 암호 같은 데이터를 전송할 때는 POST로 보내는 것이 바람직 하다.

         // 보내는 데이터를 웹 브라우저의 캐시 메모리에 남기고 싶지 않을 때는 POST 방식을 사용한다.

         // 특정 페이지를 조회하는 URL일 경우 POST 방식을 사용하면

         // URL에 조회하려는 정보의 번호나 키를 포함할 수 없기 때문에 POST 방식이 적절하지 않다.

         // 4. 쓰임

         // GET // Request URL에 Data가 포함되어 있다. // 조회하는 페이지의 URL을 주고 받을 수 있어 편리하다.

         // ex) /news/detail?no=117 // 뒤의 값이 나오기 때문에 즐겨찾기에 추가가 가능하다.

         // POST // Request URL에 Data가 없다. // 특정 페이지로 바로 이동이 불가능하다.

         // ex) /news/detail // 이 URL에 message body로 값을 보내기 때문에 URL은 고정되어 있다.

         // URL은 계속 같은데, 넘겨지는 값에 따라 달라지기 때문에 북마크가 불가능하다.

Servlet03 -> 파일 업로드 처리하기

    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");
<form action="/eomcs-java-web/ex04/s3" method="post">
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  사진: <input type="file" name="photo"><br>
  <input type="submit" value="전송">
</form>

         // GET 요청으로 파일 전송하기 //

         // GET 으로 파일을 전송할 수 없다. 단지 파일 이름만 전송한다. 

         // POST 요청으로 파일 전송하기 // 

         // POST 요청만 적용해서 보낸다면, form의 데이터 인코딩 형식은 "application/x-www-form-urlencoded"이다.

         // 즉 "이름=값&이름=값&이름=값" 형식으로 데이터를 보낸다. // 첨부한 파일의 데이터를 보내지 못한다.

         // 단지 파일 이름만 전송한다.

<form action="/eomcs-java-web/ex04/s3" enctype="multipart/form-data" method="post">
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  사진: <input type="file" name="photo"><br>
  <input type="submit" value="POST 전송">
</form>

         // POST 요청 + multipart/form-data 형식으로 파일 전송하기 //

         // enctype="multipart/form-data"

         // 파일 업로드할때 꼭 encodingtype을 "multipart/form-data"로 지정해야 한다.

         // multipart/form-data 형식으로 데이터를 전송하지 않으면 첨부 파일의 데이터는 받을 수 없다.

         // 파일을 업로드 하려면, 반드시 multipart/form-data 형식으로 POST 요청해야 한다.

Servlet04 -> apache 라이브러리 사용 // 멀티파트 파일 업로드 처리

         // 멀티파트 형식으로 전송된 데이터는 별도의 처리과정이 필요하다.

         // Apache 재단에서 제공하는 fileupload 라이브러리가 이 별도의 처리과정을 대신 해준다.

        // mvnmvnrepository.com 또는 search.maven.org 접속 - 'commons-fileupload' 검색

        // org.springframework:spring-context // 버전 클릭 // Gradle Groovy DSL 복사

        // - 라이브러리 정보를 dependencies {} 블록에 추가 - 'gradle cleanEclipse' - 'gradle eclipse'

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

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

         // init(ServletCondig)가 호출될 때 이 메서드(init())를 호출한다. // 파일을 저장할 디렉토리 경로를 준비

         // 멀티파트 형식으로 보낸 첨부 파일 데이터를 읽는 방법 //

         // Content-Type 헤더에 지정한 구분자를 사용하여 각 파트를 분리한 다음 데이터를 읽는다.

         // 문제는 기존에 제공하는 getParameter()로는 멀티파트 형식으로 전송된 데이터를 읽을 수 없다.

         // 해결방법 //

         // 1. 개발자가 직접 멀티파트 형식을 분석하여 데이터를 추출한다. // 미친짓이다.

         // 2. apache.org 사이트에서 제공하는 멀티파트 데이터 분석기를 사용 // 과거에는 많이 사용했었음.

         // 3. Servlet 3.0 부터 제공하는 기능을 이용 // 과거에 쓰던 방식에 젖어 이 방법을 모르기도 한다.

         // 4. Spring WebMVC를 사용한다면 해당 프레임워크에서 제공하는 기능을 이용 // 나중에 추후 설명

         // req.setCharacterEncoding("UTF-8"); // 멀티파트 데이터를 처리할 때는 인코딩 설정이 적용되지 않는다.

         // 멀티파트 형식으로 전송된 데이터는 getParameter()로 꺼낼 수 없다.

         // 2번 방법 // 실무에서는 아직도 이 방법을 사용하기 때문에 짚고 넘어간다.

    DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
    ServletFileUpload multipartDataHandler = new ServletFileUpload(fileItemFactory);
    HashMap<String, String> paramMap = new HashMap<>();
    try {
      List<FileItem> parts = multipartDataHandler.parseRequest((HttpServletRequest) req);
      for (FileItem part : parts) {
        if (part.isFormField()) {
          paramMap.put(part.getFieldName(), part.getString("UTF-8"));

         // DiskFileItemFactory // 멀티파트 데이터를 분석하여 FileItem 객체에 담아 줄 공장을 준비

         // ServletFileUpload(fileItemFactory) // 공장 객체를 사용하여 클라이언트가 보낸 데이터를 처리할 객체

         // HashMap<String, String> // 분석한 데이터를 보관할 맵 객체를 준비

         // multipartDataHandler.parseRequest((HttpServletRequest) req); // 

         // 멀티파트 데이터 처리기를 이용하여 클라이언트 요청을 분석

         // part.isFormField() // 파트의 데이터가 일반 데이터라면

         // paramMap.put(part.getFieldName(), part.getString("UTF-8") // 

         // 클라이언트가 보낸 파라미터 이름, 파라미터의 값. 값 꺼낼 때 인코딩을 지정

        } else {
          String filename = UUID.randomUUID().toString();
          File file = new File(this.uploadDir + "/" + filename);
          System.out.println(file.getCanonicalPath());
          part.write(file);
          paramMap.put(part.getFieldName(), filename);
        }
      }

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

         // UUID.randomUUID().toString(); // 업로드 파일을 저장할 때 사용할 파일명을 준비한다. // 원래 파일명 사용 x

         // 다른 클라이언트가 같은 이름의 파일을 업로드 하면 기존 파일을 덮어쓸 수 있기 때문

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

         // part.write(file); // 파일 경로에 업로드 파일을 저장한다.

Servlet05 -> Servlet 3.0의 기본 라이브러리 사용

         // 멀티파트 형식의 데이터를 처리할 서블릿으로 선언

         // 사용방법은 두가지가 있다. // 1. DD 파일(web.xml)에 설정 2. 애노테이션으로 설정

         // 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>
  <servlet-mapping>
    <servlet-name>ex04.Servlet05</servlet-name>
    <url-pattern>/ex04/s5</url-pattern>
  </servlet-mapping>

         // 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 {

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

    Part photoPart = httpReq.getPart("photo");
    String filename = "";
    if (photoPart.getSize() > 0) {
      filename = UUID.randomUUID().toString();
      photoPart.write(this.uploadDir + "/" + filename);
      out.printf("사진=%s<br>\n", filename);
      out.printf("<img src='../upload/%s'><br>\n", filename);
    }
    out.println("</body></html>");
  }
}

         // @MultipartConfig(maxFileSize=값)  // 최대 FileSize를 설정한다.

         // req.setCharacterEncoding("UTF-8"); //

         // Servlet 3.0의 멀티파트 처리 기능을 이용할 때는 클라이언트가 보낸 데이터의 인코딩을 지정해야 한다.

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

         // HttpServletRequest : 요청Data 클라이언트 정보 // HttpServletResponse : 출력스트림, 응답정보 다루는 도구

         // httpReq.getParameter("name") // 일반 폼 데이터를 원래 하던 방식대로 값을 꺼낸다.

         // Part photoPart = httpReq.getPart("photo"); // 파일 데이터는 getPart()를 이용한다.

         // 밑에 반복문은 업로드 한 후에 이름 무작위 부여와 저장에 대한 내용이다. // Servlet04에서 설명함.

Servlet06 -> 여러 개의 데이터를 같은 이름으로 보낸 경우 // 문제

<form action="s6" method="get">
  <input type="checkbox" name="genre1">로맨틱<br>
  <input type="checkbox" name="genre2">스릴러<br>
  <input type="checkbox" name="genre3">호러<br>
  <input type="checkbox" name="genre4">드라마<br>
  <input type="checkbox" name="genre5">액션<br>
  <input type="checkbox" name="genre6">SF<br>
  <input type="submit" value="전송">
</form>

         // HTML에서 서로 다른 이름으로 값을 보낼 경우 각각의 이름에 대해 값을 꺼내 확인해야 한다.

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

Servlet06_2 -> 여러 개의 데이터를 같은 이름으로 보낸 경우 // 해결

         // 같은 값을 여러 개 입력 받아야 하는 경우 같은 이름을 사용하자 // 단 value로 구분하자.

<form action="s6_2" method="get">
  <input type="checkbox" name="genre" value="1">로맨틱<br>
  <input type="checkbox" name="genre" value="2">스릴러<br>
  <input type="checkbox" name="genre" value="3">호러<br>
  <input type="checkbox" name="genre" value="4">드라마<br>
  <input type="checkbox" name="genre" value="5">액션<br>
  <input type="checkbox" name="genre" value="6">SF<br>
  <input type="submit" value="전송">
</form>

         // 그러면 위와 같이 한 번에 배열로 그 값들을 받을 수 있다.

         // 배열로 받으면 반복문을 이용하여 보다 쉽고 간결하게 처리할 수 있다.

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

Servlet07 -> 빈 값과 null

<form action="s7" method="get">
  a: <input type="text" name="a" value="aaaa"><br>
  b: <input type="text" name="b"><br>
  
  c: <input type="checkbox" name="c"><br>
  <input type="submit" value="전송">
</form>

         // a 입력상자에는 값을 넣고, b 입력상자에는 값을 입력하지 않고 전송을 한다.

         // 파라미터 이름만 넘어갈 때 getParameter()의 리턴 값은 빈 문자열 객체이다.

         // 즉 b는 null이 아닌 빈 문자열이 들어 있다.

         // c 체크박스를 체크한 경우와, 체크하지 않은 경우

         // 파라미터 이름 자체가 없으면 getParameter()는 null을 리턴한다.

         // 즉 체크하지 않은 경우에는 null을 리턴한다.

Servlet08 -> 썸네일 이미지 만들기

        // 돈 받고 프로그램 짤거면 사진에 대한 썸네일을 꼭 만들어야 한다. // 선택이 아닌 필수

        // 클라이언트가 요청하지 않아도 3가지 버전은 필수로 만들어 놔야 한다.

        // 1. 원본사진 2. 크기별 썸네일 1, 2 // 3장 

        // 썸네일은 원본 사진의 크기를 단순히 줄인 것이 아니다. // 화질을 떨구어 용량도 떨구어야 한다.

        // mvnmvnrepository.com 또는 search.maven.org 접속 - 'thumbnailator' 검색

        // org.springframework:spring-context // 버전 클릭 // Gradle Groovy DSL 복사

        // - 라이브러리 정보를 dependencies {} 블록에 추가 - 'gradle cleanEclipse' - 'gradle eclipse'

        // 썸네일 만드는 라이브러리는 굉장히 많다. // 검색해서 사용하고 싶은 것 사용해도 된다.

        // 썸네일 이미지 만들기 //

        // ex) Thumbnails.of(this.uploadDir + "/" + filename).size(20, 20).outputFormat("jpg")

        // .toFiles(Rename.PREFIX_DOT_THUMBNAIL);

        // ex) Thumbnails.of(this.uploadDir + "/" + filename) .size(80, 80) .outputFormat("jpg")

        // .toFiles(Rename.PREFIX_DOT_THUMBNAIL);

        // ex) Thumbnails.of(this.uploadDir + "/" + filename) .size(160, 160) .outputFormat("jpg")

        // .toFiles(Rename.PREFIX_DOT_THUMBNAIL);

        // 원본 이미지 파일이 저장된 경로를 알려주고 // 어떤 썸네일 이미지를 만들어야 하는지 설정한다.

        // 이미지 불러올 때 // 위에 ex) 순서대로

    out.printf("<img src='../upload/thumbnail.%s.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s' height='80'><br>\n", filename);
    out.printf("<img src='../upload/%s'><br>\n", filename);

 

web #ex05

Servlet01 -> HttpServletRequest와 GET/POST // MyHttpServlet 상속 전

        // HTTP 프로토콜로 통신을 하는 서블릿 컨테이너는 service() 메서드를 호출할 때

        // ServletRequest의 값으로 HttpServletRequest를, ServletResponse의 값으로 HttpServletResponse를 전달한다.

        // service() 메서드의 파라미터 값은 원래 HttpServletRequest와 HttpServletResponse이다.

        // 이들 객체(HTTP객체)에는 HTTP 프로토콜을 다루는 메서드가 추가되어 있다.

        // 따라서 HTTP 프로토콜을 다루려면 파라미터 값을 HTTP로 변환하여 사용하여야 한다.

HttpServletRequest httpReq = (HttpServletRequest) req; // HttpServletResponse httpRes = (HttpServletResponse) res;

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

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

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

        // HttpServletRequest.getMethod() // HttpServletRequest의 HTTP 프로토콜의 요청 방식을 리턴하는 메서드

        // getMethod()를 사용해서, 요청이 GET인지, POST인지 구분이 가능하다.

Servlet02 -> HttpServletRequest와 GET/POST // MyHttpServlet 상속 후

        // Servlet01의 문제점 // Http 프로토콜을 다루려고 할 때마다, Http로 형변환을 해야 한다.

@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;
    this.service(request, response);
  }

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

        // 서블릿 클래스를 만들 때 HTTP 프로토콜을 쉽게 다룰 수 있도록

        // service(HttpServletRequest,HttpServletResponse) 메서드를 추가하였다.

        // 서블릿 컨테이너가 service(ServletRequest req, ServletResponse res)를 호출하면, 

        // service(HttpServletRequest request, HttpServletResponse response)를 호출하게 만든 것이다.

        // 단, Http를 파라미터로 받는 Service는 아무런 메서드가 구현되어있지 않기 때문에,

        // 상속받아 사용하는 쪽에서 Service를 오버라이딩 해서 사용해야 한다. // Servlet02가 구현

        // GenericServlet을 상속 받아 서블릿을 만들기보다 MyHttpServlet을 상속받아 사용한다면,

        // 편하게 service()를 구현할 수 있다. // MyHttpServlet은 GenericServlet을 상속받는다.

Servlet03 -> HttpServletRequest와 Method // MyHttpServlet02 상속

        // 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;
    this.service(request, response);
  }

  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    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);
    }
  }

  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 요청을 처리할 수 없습니다.");
  }
}

        //미리 MyHttpServlet2에서 Client에서 넘어온 Method를 구분하여 메서드를 구분해 놓는다면,

        // 요청 들어온 Method에 따라, 필요한 MyHttpServlet2에서 만든 메서드를 상속받아 사용하자.

        // 서브 클래스에서 오버라이딩 할 메서드라면 private으로 선언하지 않게 한다.

        // 서브 클래스에서 이 메서드를 오버라이딩 하지 않으면 오류를 출력하도록 한다.

        // 클라이언트 요청이 들어오면 서블릿 컨테이너 - service(ServletRequest,ServletResponse) 호출

// service(HttpServletRequest,HttpServletResponse) 호출 - doXxx(HttpServletRequest,HttpServletResponse) 호출

 @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("GET 요청입니다.");
  }

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("POST 요청입니다.");
  }

        // Servlet03에서는 단순히 만들어놓은 메서드를 용도에 맞게 오버라이딩하여 사용한다.

Servlet04 -> HttpServlet 클래스를 활용하기

        // HttpServlet는 기존 라이브러리에서 이미 MyHttpServlet2처럼 만들어 놓은 클래스이다.

        // Http 프로토콜을 사용할 때에는 HttpServlet을 상속받아, 

        // 요청들어온 Method에 맞게 메서드를 오버라이딩하여 사용하자.

 

web #ex06

Servlet01 -> load on startup // 서블릿 객체 자동 생성 // 애노테이션으로 설정

        // @WebServlet(value = "/ex06/s1", loadOnStartup = 1) // 객체를 미리 만들고 싶은 서블릿에 애노테이션 설정

        // loadOnStartup=실행순서 // 미리 생성할 서블릿이 여러 개 있다면, loadOnStartup에 지정한 순서대로 생성

        // 클라이언트가 실행을 요청하지 않아도 서블릿을 미리 생성할 때, loadOnStartup 프로퍼티 값을 지정한다.

        // 서블릿을 미리 생성하는 경우 //

        // 서블릿이 작업할 때 사용할 자원을 준비하는데 시간이 오래 걸리는 경우

        // 웹 애플리케이션을 시작시킬 때 미리 서블릿 객체를 준비

        // ex) DB 연결, 소켓 연결, 필요한 환경 변수 로딩, 스프링 IoC 컨테이너 준비 등

Servlet02 -> load on startup // 서블릿 객체 자동 생성 // web.xml에 설정

  <servlet>
  <servlet-name>ex06.Servlet02</servlet-name>
  <servlet-class>com.eomcs.web.ex06.Servlet02</servlet-class>
  <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>ex06.Servlet02</servlet-name>
    <url-pattern>/ex06/s2</url-pattern>
  </servlet-mapping>

        // DD파일에 미리 객체를 생성시키고 싶은 서블릿의 정보를 넣는다.

        // 현재 프로젝트(v55_2)에서는 listener를 사용해서 IoC 컨테이너를 준비하지만,

        // Servlet01과 Servlet02처럼 loadOnStartup 방법으로 준비할 수도 있다.

Servlet03 -> 서블릿 초기화 파라미터 // 애노테이션으로 설정

@WebServlet(value = "/ex06/s3", loadOnStartup = 1, initParams = {
    @WebInitParam(name = "jdbc.driver", value = "org.mariadb.jdbc.Driver"),
    @WebInitParam(name = "jdbc.url", value = "jdbc:mariadb://localhost/bitcampdb"),
    @WebInitParam(name = "jdbc.username", value = "bitcamp"), @WebInitParam(name = "jdbc.password", value = "1111") })
@SuppressWarnings("serial")

public class Servlet03 extends HttpServlet {
  @Override
  public void init(ServletConfig config) throws ServletException {
    super.init(config);
  }

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

    ServletConfig config = this.getServletConfig();

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

        // 서블릿이 사용할 값을 애노테이션 설정으로 지정할 수 있다.

        // 서블릿 초기화 파라미터 // @WebInitParam()으로 설정된 값을 말한다.

        // 그러나 위의 코드처럼 언제든 변경될 수 있는 값을 소스코드에 직접 적는 방식은 좋지 않다.

        // 애노테이션으로도 이렇게 설정할 수 있다는 것을 보여주기 위한 예제이다.

        // 보통 DD(web.xml) File에 보관한다. // 애노테이션으로 설정하지말고 DD File에 별도로 관리하자.

Servlet04 -> 서블릿 초기화 파라미터 // web.xml에서 설정

    <servlet>
  <servlet-name>ex06.Servlet04</servlet-name>
  <servlet-class>com.eomcs.web.ex06.Servlet04</servlet-class>
  <init-param>
    <param-name>jdbc.driver</param-name>
    <param-value>org.mariadb.jdbc.Driver</param-value>
  </init-param>
    <init-param>
    <param-name>jdbc.url</param-name>
    <param-value>jdbc:mariadb://localhost:3306/studydb</param-value>
  </init-param>
    <init-param>
    <param-name>jdbc.username</param-name>
    <param-value>study</param-value>
  </init-param>
    <init-param>
    <param-name>jdbc.password</param-name>
    <param-value>1111</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
  </servlet>

        // 별도로 web.xml에 지정하여 사용한다.

  @Override
  public void init() throws ServletException {
    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"));
  }

        // init-param을 사용할 때에는 ServeltConfig를 사용한다.

Servlet05 -> 컨텍스트 초기화 파라미터 // web.xml에서 설정

  <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>
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    ServletContext sc = this.getServletContext();

    resp.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    out.printf("driver=%s\n", sc.getInitParameter("jdbc2.driver"));
    out.printf("url=%s\n", sc.getInitParameter("jdbc2.url"));
    out.printf("username=%s\n", sc.getInitParameter("jdbc2.username"));
    out.printf("password=%s\n", sc.getInitParameter("jdbc2.password"));
  }

        // this.getServletContext(); // 컨텍스트 초기화 파라미터 값을 꺼내려면 ServletContext 객체가 필요하다.

        // context-param을 사용할 때에는 ServletContext를 사용한다.

        // 특정 Servlet에서만 사용한다면 init-param으로 선언한다. // Servlet04처럼

        // 모든 Servlet에서 공통으로 사용한다면 context-param으로 선언한다. // Servlet05

 

web #ex07

Servlet01 -> 포워딩(forwarding) // 다른 Servlet에게 작업을 위임.

    String op = request.getParameter("op");
    if (!op.equals("+")) {
      RequestDispatcher dispatcher = request.getRequestDispatcher("/ex07/s2");
      dispatcher.forward(request, response);
      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));
  }

        // URL에서 +는 공백으로 인식한다. // URL 인코딩시 +는 %2b로 처리한다. 

        // Servlet01, 02, 03 모두 ContentType을 설정해주어야 한다.

        // op 뒤의 값이 "+"가 아니라면, /ex07/s2로 보낸다는 의미이다.

        // ex) localhost:9999/bitcamp-java-web/ex07/s1?a=100&b=200&op=%2b

        // 위 예시를 실행시, URL은 그대로인 상태에서 값이 300이 출력된다. // Servlet01 실행

        // ex) localhost:9999/bitcamp-java-web/ex07/s1?a=100&b=200&op=-

        // 위 예시를 실행시, URL은 그대로인 상태에서 값이 -100이 출력된다. // Servlet02 실행

        // ex) localhost:9999/bitcamp-java-web/ex07/s1?a=100&b=200&op=*

        // 위 예시를 실행시, URL은 그대로인 상태에서 해당 연산을 수행할 수 없습니다.가 출력된다. // Servlet03 실행

        // 즉, URL은 그대로인 상태에서 이미 출력한 결과를 취소하는 것이다.

        // 출력한 것이 취소 될 수 있다 ??? //

        // PrintWriter 객체를 통해 출력하는 내용은 즉시 웹 브라우저로 전달되는 것이 아니라,

        // 내부 출력 버퍼(보통 8KB 크기)에 보관한다.

        // 서블릿의 service() 메서드 호출이 종료될 때 비로서 버퍼의 내용이 웹 브라우저로 전송된다.

        // 물론 그 전에 버퍼가 꽉 차면 자동으로 출력된다.

        // 그래서 다른 서블릿으로 실행을 위임하기 전에 서블릿이 출력한 내용을 취소할 수 있는 것이다.

        // 포워딩은 서블릿을 실행한 후 리턴된다. // Servlet03까지 호출 됐어도, Servlet02, Servel01까지 실행함.

        // 그러나, Servlet03에게 위임을 시켰기 때문에, Servlet02와 Servlet01의 출력 결과를 반영하지 않는다.

        // 즉 Servlet03의 결과만 출력된다. // Servlet02, Servlet01의 결과는 Client에게 출력되지 않는다.

        // 결론, URL에서 작업하는 Servlet이 바뀌어도 출력 결과만 바뀌고 URL은 바뀌지 않는다.

Servlet11 -> 인클루딩(including) // 다른 Servlet 작업을 포함.

        // forward()는 다른 서블릿으로 위임할 때 현재 서블릿의 출력이 취소된다.

        // include()는 다른 서블릿으로 실행을 위임하더라도 현재 서블릿의 실행 결과를 유지한다.

        // forward() 코드의 경우, Servlet01에서 Servlet02로, Servlet02에서 Servlet03으로 넘어간다.

        // Servlet03까지 가면, 해당 Servlet03의 내용만 출력된다.

        // include()의 경우, Servlet11에서 Servlet11_error로, Servlet11_minus로, Servlet11_plus로 넘어간다.

        // Servlet11_xxx로 넘어가더라도, Servlet11의 출력 결과를 유지한다.

        // 인클루드의 경우 이전 서블릿에서 setContentType()을 설정해야 한다. // 최초 한번 설정하면 계속 유지

        // 포워드는 현재 서블릿에서 설정한 setContentType()이 무시된다. // 서블릿 마다 설정을 해주어야 한다.

 

web #ex08

Servlet01 -> 리프래시(refresh) // java 코드로 설정 // 클라이언트에게 다른 URL을 요청하라는 명령 

        // refresh // ex) 결제완료 안내메세지 출력 후 메인페이지 요청

        // 1. Client가 Servlet01에게 요청 // 2. Servlet01이 콘텐트+refresh url을 Client에게 응답

        // 3. 콘텐트 출력

        // 4. Client에서 일정시간 경과 후 Servlet02로 자동 요청 // 5. Servlet02이 Clinet에게 응답

        // redirect // ex) 로그인 후 바로 메인페이지 요청

        // 1. Client가 Servlet01에게 요청 // 2. Servlet01이 redirect url을 Client에게 응답

        // 3. Client에서 응답 받는 즉시 Servlet02로 요청 // 4. Servlet02이 Clinet에게 응답

        // forward // 오류 발생시 오류 처리 서블릿으로 포워딩

        // 1. Client가 Servlet01에게 요청 // 2. Servlet01이 Servlet02에게 forward

        // 3. Servlet02가 Client에게 응답

        // include // 꼬리말 출력을 할 서블릿을 인클루딩

        // 1. Client가 Servlet01에게 요청 // 2. Servlet01이 Servlet02에게 including

        // 3. Servlet02가 Servlet01에게 리턴 // 4. Servlet01가 Client에게 응답

        // refresh //

        // 서버로부터 응답을 받고 내용을 출력한 후 특정 URL을 자동으로 요청하도록 만들 수 있다.

        // 보통 웹 페이지를 자동으로 이동시키고 싶을 때 사용한다.

        // ex) 로그인 후 메인페이지로 자동 이동, 메일을 전송한 후 메일 목록 페이지로 자동 이동

        // ex) 게시글 등록한 후 게시글 목록으로 자동 이동, 결제 완료 후 결제 상태 페이지로 자동 이동

  @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/s1");
    
    for (int i = 0; i < 200; i++) {
      out.println(i + "  ===> 1234567890123456789012345678901234567890");
    }

    response.setHeader("Refresh", "3;url=s100");
  }

        // for문은 없다고 생각하고 이해 // for문은 이후에 넣어서 얘기함

        // response.setHeader("Refresh", "3;url=s100"); // 응답 헤더에 Refresh 정보를 추가한다.

        // 이미 println("안녕하세요...)를 출력한 상태에서 헤더를 어떻게 변경하는가?? //

        // out.println()이 출력한 것은 출력스트림 버퍼에 보관되어 있다.

        // 따라서 아직 클라이언트에게 응답한 상태가 아니다.

        // 그래서 다음과 같이 출력을 한 후에 응답 헤더 값을 추가하거나 변경할 수 있는 것이다.

        // 메서드 호출이 완료될 때 비로소 클라이언트로 응답헤더와 버퍼에 저장된 message-body가 출력된다.

        // "반가워요 - /ex08/s100" // 따라서 3초 후에 Servlet100이 실행 된다. 

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

        // HTTP 응답 프로토콜을 확인해보면 Refresh 응답헤더가 적용 된 것을 확인할 수 있다.

        // Refresh: 3;url=s100 // 웹 브라우저는 이 헤더의 정보에 따라 다시 요청한다.

        // for 반복문을 넣고 돌린다면, 헤더를 추가하거나 변경하는 코드는 적용되지 않는다.

        // 반복문으로 인해 8KB 버퍼가 꽉 차기 때문에 헤더를 설정하기 전에 이미 버퍼 내용이 출력된다.

        // 즉 응답이 완료된다. // 응답을 완료한 다음에 응답 헤더의 값을 변경하거나 추가해도 적용이 되지 않는다.

Servlet02 -> 리프래시(refresh) // HTML 태그로 설정

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

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

    out.println("<html><head>");

    out.println("<meta http-equiv='Refresh' content='3;url=s100'>");

    out.println("</head><body>");
    out.println("안녕하세요! - /ex08/s2");
    out.println("</body></html>");
  }

        // HTML 태그로 설정하는 경우에는, <head> 태그 사이에 넣어야 한다.

Servlet03 -> 리다이렉트(redirect) // 응답할 때 콘텐트를 보내지 않고 바로 다른 페이지를 요청하라고 명령

        // redirect // 

        // 클라이언트의 요청을 받은 후 콘텐트를 보내는 대신 다른 페이지의 URL을 알려줄 때 사용한다.

        // 웹 브라우저는 응답 받는 즉시 해당 페이지를 요청한다.

        // 웹 서버로부터 콘텐트를 받지 않았기 때문에 어떤 것도 출력하지 않는다. // 바로 다른 페이지로 이동

        // 리프래시와 달리 서버는 콘텐트(message-body)를 보내지 않는다.

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

    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 < 4000; i++) {
    out.println("안녕하세요! - /ex08/s3");
     }
     
    out.println("</body></html>");
    response.sendRedirect("s100");
  }
HTTP/1.1 302
Location: s100
Content-Type: text/html;charset=UTF-8
Content-Length: 0
Date: Tue, 07 Apr 2020 07:04:42 GMT
Keep-Alive: timeout=20
Connection: keep-alive

        // 전과 같이 for문이 있으면 redirect 적용되지 않는다. // 이유는 전과 같이 버퍼 때문에 응답이 되어버려서이다.

        // for문이 없다고 가정하고 얘기함.

        // HTTP 응답 프로토콜 //

        // HTTP/1.1 302 // 요청한 자원이 다른 URL에 있음을 표시한다.

        // Location: s100 // 다른 URL의 주소를 알려준다.

        // refresh때와 다르게 안녕하세요! 같은 메세지가 없다.

        // response.sendRedirect("URL"); // 클라이언트에게 URL을 알려줄 때 상대 경로를 지정할 수 있다.

        // forward/include 와 달리 '/'는 컨텍스트 루트(웹 애플리케이션 루트)가 아닌 웹 서버 루트를 의미한다.

        // forward/include 때의 Servlet02, 03의 경우에는 컨텍스트 루트였다.

        // 리다이렉트를 하는 순간 이전까지 버퍼로 출력된 내용은 모두 버려진다.

        // 이유 // 리다이렉트는 클라이언트에게 콘텐트를 보내지 않는다.

        // 단, 버퍼가 꽉 차서 클라이언트에게 응답한 경우에는, 리다이렉트는 적용되지 않는다.

 

web #ex09

Servlet01 -> 보관소에 값 넣기 // ServletContext, HttpSession, ServletRequest

ServletContext sc = this.getServletContext();
    sc.setAttribute("v1", "aaa");
    HttpSession session = request.getSession();
    
    session.setAttribute("v2", "bbb");
    request.setAttribute("v3", "ccc");

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

        // 보관소는 총 3가지가 있다. //

        // ServletContext // this.getServletContext(); // ServletContext 객체는 애플리케이션이 시작될 때 생성된다.

        // HttpSession // request.getSession(); // HttpSession 객체는 웹 브라우저에서

        // '세션 아이디(예:고객번호, 스탬프 카드)'를  제공하지 않으면, getSession()을 호출할 때 생성된다.

        // 웹 브라우저에서 '세션 아이디'를 제공하면, getSession()을 호출할 때 기존에 생성했던 세션 객체를 리턴한다.

        // ServletRequest // request.setAttribute("v3", "ccc"); // ServletRequest 객체는 클라이언트가 요청할 때마다 생성

        // ServletContext와 HttpSession은 객체를 생성 및 load를 하고 사용해야 한다.

        // ServletRequest는 파라미터로 받았기 때문에 단순히 호출하면 된다.

Servlet02 -> 보관소에서 값 꺼내기

        // ServletContext // this.getServletContext(); // Context를 먼저 불러와야 한다.

        // HttpSession // request.getSession(); // ServletRequest에서 Session을 먼저 불러와야 한다.

        // request.getAttribute("v3"); // ServletRequest에서 꺼내면 된다.

Servlet11 -> forward/include 서블릿끼리 ServletRequest 공유하는 것 테스트 // 값 넣기

        // request.getRequestDispatcher("s12").forward(request, response);

        // 포워드(인클루드 포함)할 때 이 서블릿이 파라미터로 받은 ServletRequest와 ServletResponse를 전달한다.

        // 따라서 포워드/인클루드 서블릿들은 응답을 완료할 때까지 이 객체들을 공유하는 것이다.

Servlet12 -> forward/include 서블릿끼리 ServletRequest 공유하는 것 테스트 // 값 꺼내기

 

web #ex10

Servlet01 -> 쿠키(cookie) 보내기

        //  쿠키 // 웹서버가 웹브라우저에게 맡기는 데이터 // 응답할 때 응답 헤더에 포함시켜 보낸다.

        // 웹브라우저는 응답헤더로 받은 쿠키 데이터를 보관하고 있다가 

        // 지정된 URL을 요청할 때 요청 헤더에 포함시켜 웹 서버에게 쿠키를 다시 보낸다.

HTTP/1.1 200 Set-Cookie: name=hong 
Set-Cookie: age=20 
Set-Cookie: working=true 
Set-Cookie: name2=홍길동 // 인코딩 하지 않은 예시
Set-Cookie: name3=%ED%99%8D%EA%B8%B8%EB%8F%99
Content-Type: text/plain;charset=UTF-8 Content-Length: 35 Date: Wed, 03 Apr
2019 01:03:37 GMT

        // HTTP 응답 프로토콜 예시 //

        // HTTP/1.1 200 Set-Cookie: // Set-Cookie 헤더에 '이름=값' 형태로 쿠키를 보낸다. 

        // 값은 반드시 문자열이여야 한다. // 객체 전송 불가능하다.

        // name2=홍길동 // 홍길동은 URL인코딩하지 않은 상태이다. // 저렇게 보내면 안된다. // 코드에도 적어둠

        // name3= // URL 인코딩을 한 상태의 예제이다.

        // 쿠키 생성 // 이름과 값으로 생성

        // 쿠키의 유효기간을 설정하지 않으면 웹브라우저가 종료될 때 까지 유지된다.

        // 웹브라우저를 종료하면 유효기간이 지정되지 않은 쿠키는 모두 삭제된다.

        // 쿠키의 사용범위를 지정하지 않으면 현재 경로에 한정한다.

        // 쿠키를 보낼 때 URL이 /ex10/s1 이라면, 브라우저는 /ex10/* 경로를 요청할 때만 서버에게 쿠키를 보낸다.

    Cookie c1 = new Cookie("name", "hong");
    Cookie c2 = new Cookie("age", "20");
    Cookie c3 = new Cookie("working", "true");
    Cookie c4 = new Cookie("name2", "홍길동");
    Cookie c5 = new Cookie("name3", URLEncoder.encode("홍길동", "UTF-8"));

    response.addCookie(c1);
    response.addCookie(c2);
    response.addCookie(c3);
    response.addCookie(c4);
    response.addCookie(c5);

        // c1 프로토콜 예 // Set-Cookie: name=hong

        // c2, c3 // 값은 반드시 문자열이어야 한다 // 만약 문자열이 아닌 값을 보내려면,

        // Base64와 같은 인코딩 기법을 이용하여 바이너리 데이터를 문자화시켜서 보내야 한다.

        // c4, c5 // 또한 값은 반드시 ISO-8859-1 이어야 한다.

        // 만약 UTF-8을 보내고 싶다면 URL 인코딩 같은 기법을 사용하여 ASCII 코드화시켜 보내야 한다.

        // response.addCookie(쿠키); // 쿠키를 응답 헤더에 포함시키는 방법

Servlet02 -> 쿠키(cookie) 읽기 // 사용 범위가 지정된 쿠키 읽기

        // Cookie[] cookies = request.getCookies(); // 쿠키 꺼내기

        // 쿠키를 이름으로 한 개씩 추출할 수 없다. // 한번에 배열로 받아야 한다.

        // 만약 요청 헤더에 쿠키가 없으면, 리턴되는 것은 null이다. // 사용할 때 유의하자.

        if (c.getName().equals("name3")) {
          out.printf("  => %s\n", URLDecoder.decode(c.getValue(), "UTF-8"));
        }

        // 쿠키 값이 URL 인코딩한 값이라면 원래의 값으로 되돌리기 위해 URL 디코딩 해야한다.

Servlet11 -> 쿠키(cookie) // 유효기간 설정하기

        // 유효기간을 설정하면 웹브라우저를 종료해도 삭제되지 않는다.

        // 단 유효기간이 지나면 웹서버에 보내지 않고 삭제한다.

        // 웹 브라우저는 로컬 디스크에 쿠키를 보관한다.

    Cookie c1 = new Cookie("v1", "aaa");
    Cookie c2 = new Cookie("v2", "bbb");
    c2.setMaxAge(30);
    Cookie c3 = new Cookie("v3", "ccc");
    c3.setMaxAge(60);
    
    response.addCookie(c1);
    response.addCookie(c2);
    response.addCookie(c3);

        // setMaxAge(시간) // 쿠키를 보낸 이후 시간 동안만 유효

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:40 GMT

        // c1 : 시간설정 x // c2 : 30초  // c3 : 60초 

Servlet12 -> 쿠키(cookie) // 유효기간이 설정된 쿠키 읽기

        // 유효기간이 설정된 쿠키는, 유효기간이 지나기 전까지 브라우저를 껐다켜도 컴퓨터를 껐다켜도, 

        // 사라지지 않는다. // 유저 하드디스크에 보관하기 때문 또한 같은 브라우저에서만 사용된다.

        // 시간이 남아있어도 다른 브라우저를 사용하면 나타나지 않는다. // 해당 브라우저에는 남아있음.

        // 유효기간이 설정되지 않은 쿠키는 브라우저를 껐다 키면 사라진다. // 브라우저를 안끄면 계속 남아있음.

Servlet21 -> 쿠키(cookie) // 쿠키 사용 범위 지정

        // 쿠키의 사용 범위를 지정하지 않으면 쿠키를 발행한 URL 범위에 한정된다.

        // 즉 같은 URL로 요청할 때만 쿠키를 보내야 한다.

        //  ex) 쿠키를 발행한 URL : /ex10/s21 // 쿠키를 보낼 수 있는 URL : /ex10/*

        //  쿠키를 보낼 수 없는 URL : /ex10 이외의 모든 URL

HTTP/1.1 200 Set-Cookie: v1=aaa
Set-Cookie: v2=bbb; Path=/bitcamp-java-web/ex10/a
Set-Cookie: v3=ccc; Path=/bitcamp-java-web 
Content-Type: text/plain;charset=UTF-8 
Content-Length:36 
Date: Wed, 08 Apr 2020 02:51:12 GMT

        // Path=값 // 값의 경로 안에 있는 것들로 제한한다.

        // 사용 범위를 지정하지 않은 쿠키 //

    // 쿠키를 발급한 서블릿과 같은 경로이거나 하위 경로의 서블릿을 요청할 때만 웹 브라우저가 서버에 쿠키를 보낸다.

        // 사용 범위 지정한 쿠키 //

    // 쿠키를 발급한 서블릿의 경로에 상관없이 지정된 경로의 서블릿을 요청할 때 웹 브라우저가 서버에 쿠키를 보낸다.

        // 쿠키의 경로를 적을 때 웹 애플리케이션 루트(컨텍스트 루트)로 적어야 한다.

        // 쿠키 경로는 서블릿 컨테이너가 사용하는 경로가 아니거 웹 브라우저가 사용하는 경로이다.

        // 웹 브라우저에서 '/' 은 서버 루트를 의미한다.

        // 서버 루트를 의미하는 '/' // 포트번호 다음에 오는 '/'를 말한다.

        // 따라서 웹 브라우저가 사용하는 경로를 지정할 때는 조심해야 한다. '/'가 서버 루트를 의미하기 때문이다.

        // 그래서 쿠키의 경로를 지정할 때는 웹 애플리케이션 루트(컨텍스트 루트)를 정확하게 지정해야 한다.

        // Servlet22, 23, 24는 테스트용

 

web #ex11

Servlet01 -> 세션(session) //

        // 세션(session) // 클라이언트를 식별하는 기술

        // HTTP 프로토콜은 Stateless 방식으로 통신을 한다. // 연결한 후 요청하고 응답을 받으면 연결을 끊는다.

        // 서버는 클라이언트가 요청할 때 마다 누구인지 알 수 없다.

        // 이를 해결하기 위해 클라이언트가 접속하면 웹 서버는 그 클라이언트를 위한 고유 번호를 발급(쿠키 이용)한다.

        // 이 고유 번호를 '세션 아이디'라 부른다.

        // 웹 브라우저는 세션 아이디를 쿠키에 보관해 두었다가 그 서버에 요청할 때 마다 세션 아이디를 보낸다.

        // 세션 아이디 쿠키는 유효기간을 설정하지 않았기 때문에 웹 브라우저를 종료하면 세션 아이디 쿠키는 삭제된다.

        // 세션 아이디 쿠키의 사용 범위는 웹 애플리케이션이다.

        // 따라서 같은 웹 애플리케이션의 서블릿을 실행할 때는 무조건 세션 아이디를 보낸다.

        // 세션 아이디 발급 시기 //

        // 새 세션을 생성할 때 세션 아이디를 발급한다.

        // 새 세션 생성 시기 //

        // 세션이 없는 상태에서 request.getSesssion()을 호출할 때 생성한다.

        // 생성한 세션의 ID는 쿠키를 통해 응답할 때 웹 브라우저로 보낸다.

        // 세션ID 무효화 //

        // 1. 세션에 지정된 timeout 초과했을 때 // 2. session.invalidate() 호출

Servlet02 -> 세션(session) // 세션에서 값 꺼내기

        // 세션 아이디 보내기 //

// 서버로부터 쿠키를 통해 받은 세션아이디는 웹 브라우저가 해당 서버에 요청할 때마다 쿠키로 세션 아이디를 보낸다.

GET /java-web/ex11/s2 HTTP/1.1 
Host: localhost:9999
... 
Cookie:JSESSIONID=9909D09693CE9E0B8D23BE824313C834

        // 1-1. 세션 아이디가 있고, 유효하다면, 기존에 생성한 HttpSession 객체를 리턴한다.

        // 1-2. 세션 아이디가 있고, 유효하지 않다면, 2번으로 진행함. // 없다고 취급함 유효하지 않으면 있으나 마나임.

        // 2. 세션아이디가 없다면, 새 HttpSession 객체를 생성하여 리턴한다.

        // 응답 프로토콜에 새로 생성한 HttpSession 객체의 세션ID를 쿠키로 보낸다.

        // 세션에서 값 꺼내기 //

        // 웹 브라우저를 종료하면 이전에 서버로부터 받은 세션 아이디 쿠기가 삭제된다.

        // 그런 후에 웹 브라우저에서 이 서블릿을 요청하면 getSession() 메서드를 새 세션 객체를 생성한 후 리턴한다.

        // 새 세션에 값을 넣어주지 않았다면 null을 출력한다.

Servlet11 -> Servlet12,13,14와 묶음이다.

        // Servlet11은 이름을 입력하라는 창을 띄우고 버튼을 누르면 Servlet12로 넘어간다.

        // Servlet12는 나이를 입력하라는 창을 띄우고 버튼을 누르면 Servlet13으로 넘어간다.

        // Servlet13은 전화번호를 입력하라는 창을 띄우고 버튼을 누르면 Servlet14로 넘어간다.

        // 14는 이전에 입력받은 이름과 나이를 세션에서 꺼내고 전화번호를 파라미터에서 꺼낸다.

        // 왜 전화번호는 세션에서 꺼내지 않는가 ? // 

        // Servlet11을 잘 보면 이름을 입력후 버튼을 눌러 넘어간다. 입력 값은 12로 넘어간다.

        // 값을 11에서 받고 넘어가는게 아니기 때문이다.

Servlet21 -> 세션(session) // 타임아웃 설정

        // session.setMaxInactiveInterval(시간); // 마지막 요청으로부터 시간동안 요청이 없으면, 세션이 무효화 된다.

        // session.setMaxInactiveInterval(10); // 마지막 요청으로부터 10초동안 요청이 없으면, 세션이 무효화 된다.

        // 무효화된 세션은 사용할 수 없기 때문에 getSession()은 새 세션을 만들어 리턴한다.

Servlet22 -> 세션(session) // 타임아웃 설정 확인

        // 마지막 요청으로부터 정해진 시간이 지나 세션이 무효화 된 이후 요청이 들어온다면,

        // 새 세션을 만들어 리턴하면 값이 null로 표기된다.

Servlet31 -> 세션(session) // 무효화시키기 1

<session-config>
<session-timeout>1</session-timeout>
</session-config>

        // DD File(web.xml)에 설정하는데, 여기서 time 기준은 분이다 // 위 예시는 1분

Servlet32 -> 세션(session) // 무효화시키기 // 값 꺼내기

        // 1분이 지나고 해당 Session이 호출되면 새 세션 객체를 만들어 리턴된다.

Servlet33 -> 세션(session) // 무효화시키기 2

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

반응형

+ Recent posts