ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 22-09-16_jsp
    main 2022. 9. 16. 17:58

    어제 수업을 review해볼까요

     

    9가지 기본객체. 생성시점과 소멸시점, 그래서 생성주기를 알아봄. 

     

     

    버퍼보다 내보내는 데이터가 많을 때 -> overflow

     -- 그러면 auto flush를 하거나...

     -- session... 생명주기. 생성의 대상은 사람이 아니라, 그 사람이 사용하는 agent(browser)임. 사람이 아님. 

        - 소멸시점은 로그아웃 제외하면 불명확해서 timeout을 설정해줌. 

        - sessionTracking mode에서 data가 전달이 안되거나, agent가 종료되거나  ==> 재전송되는 id가 없어서 서버에서 이미 만들어진 session에서는 길을 잃음 -> 무의미한 session이 남발되는 상황이 생김

     - 이거 쫌 이상하져? 이 쓰레기 세션들은 언제 사라져여? timeout설정 안 해주면 안 사라져서 server에 부하를 발생시킴. 

     

     쿠키는 많이 만들어도 서버에 부하X,(client side에 저장해서) 그런데 세션은 정보를 많이 담아놓을수록, 많이 만들수록 부하가 많이 걸림. 예를 들면 네이버... --> 엄청 큰 부하를 감당해야 하니, 그 부하를 줄이고자 함

     - 그래서 경우에 따라서 서버에서 세션을 안 만드는 경우도 생김. -> 서버에서 세션id가 안 만들어 지면, 첫 번째 요청과 두번째 요청이 한 클라이언트에 의해 발생했다는 것이 확인이 안 됨

     

     토큰 구조의 인증구조를 사용하면.... server side에 session이 없을 수 있겠죠....

     

     크게 부하가 발생하지 않는 server에서는 session을 관리해서 하는거고...

     

    세션이 이용될 때 id라는 식별성을 가진 데이터가 생김. 그리고 그 id를 주고받는걸, session의 tracking  mode이고, 가장 기본적인게 cookie라는 tracking mode. 그런데 이건 client가 agent를 어떻게 설정하느냐에 따라서 될수도 안 될수도 있음. 

     그러면 url 기법으로도 할 수 있고... 그런데 이건 노출이 되고... 그래서 이 두가지 문제를 해결할 수 있는게 암호화 구조...

     

    session timer. 이 과제는 수업 말미에 해결해 보자요

     

     

    오늘은 

    5. application (ServletContext) 

    9. pageContext(*** - PageContext) :

    를 알아봅시다. 

     

    ----

    먼저, Context가 뭘까여

     

    CAC(Context Aware Computing) : 상황 인지 기술... 그래서 상황..환경을 Context라고 함

     

    application(ServletContext), 여기에서 Context의 첫번째 의미는  application 자체임. 그리고 servlet을 둘러싼 환경 중 최종적으로 WAS(server, container)에 대한 정보를가짐. 

     

        so, 5. application (ServletContext) : 서버와 현재 컨텍스트에 대한 모든 정보를 가진 객체.

    라고 함

     

    싱글톤으로 관리 됨. 

     

     

    ---

    진짜 singlton으로 관리되나 확인해보자

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>09/applicationDesc.jsp</title>
    </head>
    <body>
    <h4>application(ServletContext)</h4>
    CAC(Context Aware Computing)
    <pre>
    	application hashcode : <%=application.hashCode() %>
    	<a href = "../08/sessionTimer.jsp">세션 타이머</a>
    	<a href ="<%=application.getContextPath() %>/desc">desc servlet</a>
    
    </pre>
    
    </body>
    </html>

     

    여기에 우리 가진 서블릿들 들어있음.

     

     

    이렇게도 짜서 봅시다

    그러면

     

    다 눌러서 확인해봅시다 System.out.println으로
    동일한 hashCode를 가지고 있음을 알 수 있음.

     

    ---

    총 4군데에 hashCode 출력하는 코드를 넣은 이유는, 어디서든 Servlet Context는 다 같은 모양으로 참조할 수 있음을 확인하기 위함이었습니다. 

     ->singlton객체로 servlet Context를 확보할수 있음을 알았음. 이제 이걸 어디에 써 먹을 수 있을까?

     

     

    여기서 MIME 확인 할 떄도, 27번째 줄에서 parameter 얻을 때도 사용할 수 있음. 같은 거에서 뽑은 거임.

     

    사실 이 application은 서버의 정보를 가져올 때 가장 많이 사용한다. 그리고 정보를 가져와서 서버를 식별할 떄, Mime type 확인할 떄도 사용함. 

     

    이릏게 적으면 서버의 정보와 버전 등을 확인할 수 있음.

    그래서 서버의 major, minor 정보도 확인할 수 있음. 

     

     

    <pre>
    	application hashcode : <%=application.hashCode() %>
    	<a href = "../08/sessionTimer.jsp">세션 타이머</a>
    	<a href ="<%=application.getContextPath() %>/desc">desc servlet</a>
    	1. 서버의 정보를 가져올 떄.
    		- 서버를 식별할 떄. <%=application.getServerInfo() %>
    						<%=application.getMajorVersion() %>,<%=application.getMinorVersion() %>
    		- Mime Type 확인할 때. getMimeType(file_name)
    </pre>

    우리는 3.1 버전임

     

    ( 내가 사용하는 servlet의 version을 확인할 떄도 사용할 수 있음. 이걸 왜 확인하나? parameter말고 part라는 data.)

     왜냐면 내가 개발환경이랑 배포되었을 때 환경이 다를 수 있으니 확인 함

    내 버전은 이러니까

     

     

    2. context 정보를 가져올 때. 
    - 초기화 파라미터 획득.  getInitParameter (web.xml -> context-param 이라는 element이용)
    <%=application.getInitParameter("imageFolderPath") %>
    </pre>

     

     

    동일한 application안에서 관리된다면 siglton으로 관리되고 있어서 원하는 데이터를 뽑을 수 있다는 것

     

    resource도 활용이 되는데 왼쪽의 리소스들을 사용가능하다

    예시로 보자

    cat3.png를 꺼내올수잇져

     

     

     

     

     

     

    진짜경로를 가져오는데, 이게 진짜경로를 통해서 접근하고 가짜경로를 통해 접근하는것들이 있으니까.

     

     

    우리가 지금 진짜 경로가 아닌 가짜경로 즉 논리적 경로를 통해서... 접근하는 방법으로 안되니까. 

    	2. context 정보를 가져올 때. 
    		- 초기화 파라미터 획득.  getInitParameter		(web.xml -> context-param 이라는 element이용)
    				<%=application.getInitParameter("imageFolderPath") %>
    		- 현재 context의 web resource 확보
    		<%
    		
    			String url = "/resources/images/cat3.png";
    			//이렇게만 하면 file 객체를 생성할 수 없음, 절대경로가 필요함. 
    			//그런데 절대경로는 서버가 어디서 도느냐에 따라서 다 달라짐. 이런 경우에 application 사용
    			String path = application.getRealPath(url);
    //			File file = new File(url);
    			File file = new File(path);
    		%>
    		url : <%=url %>
    		path : <%=path %>
    		file : <%=file.getCanonicalPath() %>

     

     

     

    실제로 갖고 있던건 url뿐이었는데 그 논리적 경로를 통해서 절대경로를 얻어냈음. 

     

     

     

    이런거도 봅시다

    			FileInputStream fis = new FileInputStream(file);
    			InputStream is = application.getResourceAsStream(url);

    이렇게 하면 위의 3개 단계를 없애도 되겠져...

     

     

    자 이제 읽을 수있는 준비는 되었는데요 cat3.png 파일을 읽어서 09번 폴더에 복사해봅시다

     

    <%@page import="java.io.BufferedOutputStream"%>
    <%@page import="java.io.FileOutputStream"%>
    <%@page import="java.io.BufferedInputStream"%>
    <%@page import="java.io.InputStream"%>
    <%@page import="java.io.FileInputStream"%>
    <%@page import="java.io.File"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>09/applicationDesc.jsp</title>
    </head>
    <body>
    <h4>application(ServletContext)</h4>
    CAC(Context Aware Computing)
    <pre>
    	application hashcode : <%=application.hashCode() %>
    	<a href = "../08/sessionTimer.jsp">세션 타이머</a>
    	<a href ="<%=application.getContextPath() %>/desc">desc servlet</a>
    	1. 서버의 정보를 가져올 떄.
    		- 서버를 식별할 떄. <%=application.getServerInfo() %>
    						<%=application.getMajorVersion() %>,<%=application.getMinorVersion() %>
    		- Mime Type 확인할 때. getMimeType(file_name)
    	2. context 정보를 가져올 때. 
    		- 초기화 파라미터 획득.  getInitParameter		(web.xml -> context-param 이라는 element이용)
    				<%=application.getInitParameter("imageFolderPath") %>
    		- 현재 context의 web resource 확보
    		<%
    		
    			String url = "/resources/images/cat3.png";
    			String saveUrl = "/09/cat3.png";
    			//이렇게만 하면 file 객체를 생성할 수 없음, 절대경로가 필요함. 
    			//그런데 절대경로는 서버가 어디서 도느냐에 따라서 다 달라짐. 이런 경우에 application 사용
    			
    			String path = application.getRealPath(url);
    			String savePath = application.getRealPath(saveUrl);
    					
    //			File file(url);
    			File file = new File(path);
    			File saveFile = new File(savePath);
    			
    			// ---여기까지는 매체를 생성하는 과정
    			
    			try(
    			
    				FileInputStream fis = new FileInputStream(file);
    				InputStream is = application.getResourceAsStream(url);
    				BufferedInputStream bis = new BufferedInputStream(fis);
    				
    				FileOutputStream fos = new FileOutputStream(saveFile);
    				BufferedOutputStream bos = new BufferedOutputStream(fos);
    			){
    				int temp = -1;
    				while((temp = bis.read())!= -1){
    					bos.write(temp);
    				}
    			}
    			
    		%>
    		url : <%=url %>
    		path : <%=path %>
    		file : <%=file.getCanonicalPath() %>
    </pre>
    <img src="<%=request.getContextPath()%>/09/cat3.png">
    
    
    </body>
    </html>

    ... 이렇게 하시면여....

     

    이릏게 나오시네여....

     

     

     

    ---

    pageContext

    모든 정보를 가짐.

    그래서 EL할때도 쟤만 가지고있으면 나머지 기본객체도 다 갖고있는것과 같음

     

     

    include도 forward도 다 할 수 있는데, 훨씬 간결함 두가지 정보를 이미 pageContext가 다 가지고 있어서 path만 요청함.

     

     

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>09/pageContext.jsp/title>
    </head>
    <body>
    <h4>pageContext (PageContext)</h4>
    <pre>
    	: 가장 먼저 생성되는 기본 객체.
    	: 나머지 기본 객체 참조 소유.
    	
    	1. 흐름 제어 (flow Control, request dispatch)
    		<%
    			pageContext.include("/08/sessionTimer.jsp");
    //			request.getRequestDispatcher("/08/sessionTimer.jsp").include(request, response);
    		%>
    		=========================
    </pre>
    </body>
    </html>

    이렇게 하면

    이렇게 되는데

     

     

    이릏게 하면 뭐가 뒤죽박죽 되서 잘 안쓴다고 함,

    [조수빈] [오후 3:15] 위의 방식은
    [조수빈] [오후 3:15] include한 위치에서 include 되는데
    [조수빈] [오후 3:15] 하단의 방식은 순서가 뒤죽박죽으로 포함되어버려서
    [조수빈] [오후 3:15] 실제로 페이지를 모듈화할 때는 위의 방법을 쓴다

     

    ----

    pageContext (PageContext)

    : 가장 먼저 생성되는 기본 객체.
    : 나머지 기본 객체 참조 소유.

    1. 흐름 제어 (flow Control, request dispatch)
    <%
    // pageContext.include("/08/sessionTimer.jsp");
    request.getRequestDispatcher("/08/sessionTimer.jsp").include(request, response);
    %>
    2. 에러 데이터 확보 (에러 처리 페이지)
    <%
    if(1==1)
    throw new ArithmeticException("강제 0으로 나눴음. 예외 발생!!");
    %>

     

    ---

     

    이건또뭐야

    에러페이지 우리 404 이렇게 안뜨고요

     

    이렇게 커스텀 된 에러페이지가 뜨는데요

    이 떄 필요한게 xml에 저놈이에요

    --

     

     

     

    --

     

    소스를 긁어서 customizing 해봅시다

     

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8" isErrorPage="true"%>
    
    <!DOCTYPE html>
    <html lang="ko" class="os_mac chrome pc version_56_0_2924_87">
    <head>
    	<meta charset="utf-8">
    		<title>서버 오류</title>
    		<meta http-equiv="X-UA-Compatible" content="IE=Edge">	
    	<style type="text/css">
    		/* reset */
    		body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,textarea,p,blockquote,th,td,input,select,button{margin:0;padding:0}
    		fieldset,img{border:0 none}
    		dl,ul,ol,menu,li{list-style:none}
    		blockquote, q{quotes: none}
    		blockquote:before, blockquote:after,q:before, q:after{content:'';content:none}
    		input,select,textarea,button{vertical-align:middle}
    		input::-ms-clear{display:none}
    		button{border:0 none;background-color:transparent;cursor:pointer}
    		body{background:#fbfbfc}
    		body,th,td,input,select,textarea,button{font-size:12px;line-height:1.5;font-family:AppleSDGothicNeo-Regular,'Malgun Gothic','맑은 고딕',dotum,'돋움',sans-serif;color:#222;letter-spacing:-0.5px}
    		a{color:#333;text-decoration:none}
    		a:active, a:hover{text-decoration:underline}
    		a:active{background-color:transparent}
    		address,caption,cite,code,dfn,em,var{font-style:normal;font-weight:normal}
    		
    		/* global */
    		.ir_pm{display:block;overflow:hidden;font-size:0;line-height:0;text-indent:-9999px}
    		.ir_wa{display:block;overflow:hidden;position:relative;z-index:-1;width:100%;height:100%}
    		.screen_out{overflow:hidden;position:absolute;width:0;height:0;line-height:0;text-indent:-9999px}
    		.hide{display:none}
    		
    		/* error */
    		.img_error{overflow:hidden;background:url(http://t1.daumcdn.net/daumtop_deco/error/pc/img_error.png) no-repeat 0 0;font-size:0;line-height:0;vertical-align:top;text-indent:-9999px}
    		.page_error{width:600px;margin:103px auto 0}
    		.page_error .head_error{overflow:hidden}
    		.page_error .head_error h1{float:left}
    		.page_error .link_daum{display:block;width:69px;height:28px;background:url(http://t1.daumcdn.net/daumtop_deco/error/pc/img_error.png) no-repeat 0 0}
    		.page_error .cont_error{position:relative;margin-top:19px;min-height:180px;padding:70px 0 200px;border-top:2px solid #222}
    		.page_error .tit_error{margin-bottom:33px;font-weight:normal;font-size:36px;line-height:45px;letter-spacing:-3.5px}
    		.os_mac .page_error .tit_error{letter-spacing:-0.5px}
    		.page_error .emph_txt{color:#e30000}
    		.page_error .desc_error{margin-top:9px;font-size:14px;line-height:22px}
    		.page_error .info_link{position:absolute;right:0;top:-45px;overflow:hidden}
    		.os_mac .page_error .info_link{top:-38px}
    		.page_error .info_link .link_error{float:left;margin-left:12px;color:#555}
    		.page_error .wrap_form{position:absolute;left:50%;bottom:70px;width:420px;margin-left:-210px}
    		.page_error .wrap_inp{position:relative;height:24px;padding:10px 0;border:1px solid #bfbfbf;background:#fff}
    		.page_error .lab_search{position:absolute;left:14px;top:11px;font-size:14px;color:#888}
    		.os_mac .page_error .lab_search{top:13px}
    		.page_error .inp_search{display:block;width:348px;height:24px;padding-left:14px;font-size:14px;border:0 none;background:none;outline:0}
    		.page_error .btn_search{position:absolute;right:0;top:0;width:50px;height:44px;background-position:0 -30px}
    		.page_error .link_cs{color:#118eff;text-decoration:underline}
    		.page_error .foot_error{padding-top:15px;border-top:1px solid #222}
    		.page_error .info_copyright{font-size:11px;color:#888}
    		.page_error .link_kakao{color:#888}
    		
    		/*** 레티나 대응 ***/
    		@media
    		only screen and (-webkit-min-device-pixel-ratio:1.5),
    		only screen and (min-device-pixel-ratio:1.5),
    		only screen and (min-resolution:144dpi),
    		only screen and (min-resolution:1.5dppx){
    		.img_error{background-image:url(http://t1.daumcdn.net/daumtop_deco/error/pc/rtn/img_error.png);background-size:70px 80px;-webkit-background-size:70px 80px}
    		.page_error .link_daum{background-image:url(http://t1.daumcdn.net/daumtop_deco/error/pc/rtn/img_error.png);background-size:70px 80px;-webkit-background-size:70px 80px}
    		}
    	</style>
    </head>
    <body>
    <div id="kakaoWrap" class="page_error">
    	<div id="kakaoHead" role="banner" class="head_error">
    		<h1>
    			<a href="http://www.daum.net/" class="link_daum"><span class="ir_wa">Daum</span></a>
    		</h1>
    	</div>
    	<hr class="hide">
    	<div id="kakaoContent" class="cont_error" role="main">
    				<h2 class="tit_error">헤당 서비스를 제공하는 과정에서 서버 오류가 방생했습니다. </span></h2>
    			<%
    				ErrorData errorData = pageContext.getErrorData();
    			%>
    				
    		<p class="desc_error">
    			에러 상태 코드 : <%=errorData.getStatusCode() %><br>
    			발생한 예외 : <%=errorData.getThrowable().getMessage() %>
    			
    		</p>
    		<p class="desc_error">
    			관련해 <a href="http://cs.daum.net/" class="link_cs">고객센터</a>로 문의해 주시면 친절하게 안내해 드리겠습니다.
    		</p>
    				<h3 class="screen_out">검색</h3>
    		<div class="wrap_form">
    			<form action="http://search.daum.net/search">
    				<fieldset>
    					<legend class="screen_out">검색어 입력폼</legend>
    					<div class="wrap_inp">
    						<label for="inpSearch" id="searchLabel" class="lab_search">통합검색</label>
    						<input type="text" id="inpSearch" class="inp_search" name="q" autocomplete="off" spellcheck="false" />
    						<button type="submit" class="img_error btn_search">검색</button>
    					</div>
    				</fieldset>
    			</form>
    		</div>
    		<div class="info_link">
    			<a href="http://www.daum.net/" class="link_error">다음첫화면</a>
    			<a href="http://cs.daum.net/" class="link_error ">고객센터</a>
    		</div>
    	</div>
    	<hr class="hide">
    	<div id="kakaoFoot" class="foot_error" role="contentinfo">
    		<small class="info_copyright">Copyright &copy; <a href="http://www.kakaocorp.com/" target="_blank" class="link_kakao">Kakao Corp.</a> All rights reserved.</small>
    	</div>
    </div>
    <script type="text/javascript">
    //<![CDATA[
    function init() {
    	var inpSearch = document.getElementById('inpSearch');
    	var searchLabel = document.getElementById('searchLabel');
    	if(inpSearch) {
    		inpSearch.onfocus = function() {
    			searchLabel.className = 'screen_out';
    		}
    		inpSearch.onblur = function() {
    			if(inpSearch.value.length==0){
    				searchLabel.className = 'lab_search';
    			}
    		}
    	}
    }
    init();
    //]]>
    </script>
    </body>
    </html>

    약간 커스터마이징 해줬고요. 에러코드와 상세 메세지를를 받아올거거든요 

     

    위에놈은 에러를 처리할 목적으로 만드는 페이지예요

     

     

    web.xml은 이렇게 적어주고요

      	<error-page>
      		<!-- 500이라는 에러페이지에서 사용할 수 있는 걸 결정 -->
      		<error-code>500</error-code>
    		<location>/errors/error500.jsp</location>
      	</error-page>

     

    일부러 에러를 발생시켜서

    	2. 에러 데이터 확보 (에러 처리 페이지)
    		<%
    			if(1==1)
    				throw new ArithmeticException("강제 0으로 나눴음. 예외 발생!!");
    		%>

     

     

    --

     

    에러가 발생하면 이렇게 잘 뜹니당

     

     

    ---

    3번을 봅시다

     

    3. 속성 데이터 관리
    <%
    //얘는 아무리 많이 저장되어도 응답데이터가 나가면 비워져서 깔끔해져요. 명확한 생명주기를 가지거든요
    //1)request scope
    // request.setAttribute(arg0, arg1)

    //두놈은 데이터를 많이 저장할 수록 서버가 무거워 져요
    //2)session scope
    // session.setAttribute(arg0, arg1)

    //3)application scope
    // application.setAttribute(arg0, arg1)

    //이 두놈의 사용범위는 어케 되져?
    // pageContext 이놈은 페이지의 정보를 모두 가져여. 즉 이 페이지를 벗어나면 끝나요
    //4) page scope
    // pageContext.setAttribute(name, value)
    // pageContext.setAttribute(name, value, scope)

     

     

    ---

    근데 

    pageContext.setAttribute(name, value, scope)

    이거 하나 가지면 4개의 스코프르 다 쓸 수 있어여

    pageContext.setAttribute(name, value, pageContext.SESSION_SCOPE)

    scope 자리에서 이런 식으로 다 꺼내올 수 있거든여

     

     

    ---

     

    모든 저장공간은 Map이에요 Map은 key와 value가 필요하져...

    ---

    *** Scope : 해당 저장 영역을 관리하는 기본 객체와 생명주기가 동일한 저장 공간.(Map &lt;String : Object&gt;)
    attribute : scope를 통해 공유되는 데이터.
    1. page scope
    2. request scope
    3. session scope
    3. application scope
    <!-- 쌓기만 하면 안됨, 적절한 시점에 없애줘야 함. --> 
    setAttribute(name,value), getAttribute(name), removeAttribute(key)

     

     

     

    ---

    pageContextDesc.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>09/pageContext.jsp</title>
    </head>
    <body>
    <h4>pageContext (PageContext)</h4>
    <pre>
    	: 가장 먼저 생성되는 기본 객체.
    	: 나머지 기본 객체 참조 소유.
    	
    	1. 흐름 제어 (flow Control, request dispatch)
    		<%--
    //			pageContext.include("/08/sessionTimer.jsp");
    			request.getRequestDispatcher("/08/sessionTimer.jsp").include(request, response);
    		--%>
    	2. 에러 데이터 확보 (에러 처리 페이지)
    		<%--
    			if(1==1)
    				throw new ArithmeticException("강제 0으로 나눴음. 예외 발생!!");
    		--%>
    		
    	3. 속성 데이터 관리
    	<%
    		//얘는 아무리 많이 저장되어도 응답데이터가 나가면 비워져서 깔끔해져요. 명확한 생명주기를 가지거든요
    		//1)request scope
    //		request.setAttribute(arg0, arg1)
    
    		//두놈은 데이터를 많이 저장할 수록 서버가 무거워 져요
    		//2)session scope
    //		session.setAttribute(arg0, arg1)
    
    		//3)application scope
    //		application.setAttribute(arg0, arg1)
    		
    		//이 두놈의 사용범위는 어케 되져?
    		// pageContext 이놈은 페이지의 정보를 모두 가져여. 즉 이 페이지를 벗어나면 끝나요
    		//4) page scope
    //		pageContext.setAttribute(name, value)
    //		pageContext.setAttribute(name, value, pageContext.SESSION_SCOPE)
    
    	%>
    	*** Scope : 해당 저장 영역을 관리하는 기본 객체와 생명주기가 동일한 저장 공간.(Map &lt;String : Object&gt;)
    	attribute : scope를 통해 공유되는 데이터.
    	1. page scope
    	2. request scope
    	3. session scope
    	3. application scope
    												<!-- 쌓기만 하면 안됨, 적절한 시점에 없애줘야 함. --> 
    	setAttribute(name,value), getAttribute(name), removeAttribute(key)
    	=========================
    	<%
    		pageContext.setAttribute("pageAttr", "페이지 속성");
    		request.setAttribute("requestAttr", "요청 속성");
    		session.setAttribute("sessionAttr", "세션 속성");
    		application.setAttribute("applicationAttr", "어플리케이션 속성");
    		
    	%>
    	<a href="attributeView.jsp">어트리뷰트 뷰로 이동</a>
    </pre>
    </body>
    </html>

    attributeView

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <pre style="border: 2px solid red;">
    	페이지 : <%=pageContext.getAttribute("pageAttr") %> 
    	요청 : <%=request.getAttribute("requestAttr") %>
    	세션 : <%=session.getAttribute("sessionAttr") %>
    	어플리케이션 : <%=application.getAttribute("applicationAttr") %>
    	
    </pre>
    
    
    </body>
    </html>

    실행해보면 아래와 같다

    ---

    ---

    속성이 두가지가 나오는데, 

    2분 지나면 나머지 두개도 생김.

     

    둘 중에 세션 정보 저장된 쿠키를 날리면, 이 요청을 최초의 요청으로 받아들여서 세션 정보 날아간 거 볼 수 있음. 

    어플리케이션 속성은 브라우저가 종료 안 되었으니까 안 날아감

     

    ---

     

    이건 뭔지 모르겠는데요

    doPost에서

    //이런 메세지를 여기서 만들고 위에서 뽑을수있을까여?
    			String message = "성공";
    			//세션을쓰셔야 해여
    			req.getSession().setAttribute("message", message);

    doGet에서..

    String message =(String)req.getSession().getAttribute("message");
    			//꺼내고 바로 삭제 --> flash attribute 방식. 데이터가 휘발됨.
    			req.getSession().removeAttribute("message");
    			System.out.println(message);

     

    -------------------------------

    -------------------------------

    package kr.or.ddit.props.controller;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    
    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 org.apache.commons.beanutils.BeanUtils;
    
    import kr.or.ddit.props.service.PropertyService;
    import kr.or.ddit.props.service.PropertyServiceImpl;
    import kr.or.ddit.props.vo.PropertyVO;
    
    @WebServlet({"/properties","/property"})
    public class PropertiesControllerServlet extends HttpServlet {
    	private PropertyService service = new PropertyServiceImpl(); 
    	
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		//모든 controller에서 이 작업이 첫 번쨰로 해야 한다는건 모든 controller가 이 코드가 중복이 된다는 것 
    		req.setCharacterEncoding("UTF-8");
    		
    		String servletPath = req.getServletPath();
    		Object model =null;
    		int statusCode = 200;
    		if("/properties".equals(servletPath)) {
    			//여기서 받아온 게 이 if문에서의 model이 되고
    			model = service.readProperties();
    			
    		}else {
    			String name = req.getParameter("name");
    			//name이라는 parameter의 검증이 필요합니다
    			//null이라는건 name이 안 넘어왔다는 것, 어쨌든 누락되었다는것 그러면 StatusCode가 400이 되어야 함. 
    			if(name==null || name.isEmpty())
    				statusCode = HttpServletResponse.SC_BAD_REQUEST;
    			else
    			//여기서 받아온 게 이 if문에서의 model이 됨
    				model = service.readProperty(name);
    			String message =(String)req.getSession().getAttribute("message");
    			//꺼내고 바로 삭제 --> flash attribute 방식. 데이터가 휘발됨.
    			req.getSession().removeAttribute("message");
    			System.out.println(message);
    		}
    		if(statusCode==200) {
    					//이 model안에는 property 하나가 들어있음. 
    			req.setAttribute("model", model);
    			//모든 컨트롤러의 마지막에 view로 이동한다면 이것도, ... 모든 controller의 시작과 마지막에 중복 코드가 생긴다는건 중요한거니까 잘 기억해 두세요.
    			String viewName = "/jsonView.do";
    			req.getRequestDispatcher(viewName).forward(req, resp);
    			
    		}else {
    			resp.sendError(statusCode);
    		}
    		
    		
    		
    	}
    	
    	//client가 server쪽으로 뭔가를 보내고 있다는 거...
    	@Override
    	protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
    			throws ServletException, IOException {
    		req.setCharacterEncoding("UTF-8");
    		
    		//이렇게 추가를 해 주는데, 얘는 빈 거니까 데이터를 추가 해 주어야 함. 빈거로 설정할순없음
    		PropertyVO newProp = new PropertyVO();
    //		newProp.setPropertyName(req.getParameter("propertyName"));
    	
    		try {
    			BeanUtils.populate(newProp, req.getParameterMap());
    		} catch (IllegalAccessException | InvocationTargetException e) {
    			// 이건 population을 못했다는거... 상태코드를 어떻게 해야 하나... 자바 규약을 잘못지켜서 VO를 잘못만들었다는거?
    			// 그러면 500이 나와야 하는데, 잘 생각해봐요 500을 직접 만든적이? 없죠. 
    			// 그러면 이걸 어떻게 해야 하냐면 tomcat이라는 server에 넘겨야 걔가 이걸 500으로 처리할거예요
    			// 이 메서드를 호출한 tomcat이라는 호출자에게 넘겨야 해요 그러면 throw를 해야 하는데요
    			// 
    			throw new ServletException(e);
    		}
    		//이제 이 중간에서 뭘 할거냐면요..... 
    		//시그니쳐가 나왔다고요?
    		boolean valid = validate(newProp);
    		if(valid) {
    			service.createProperty(newProp);
    			//이런 메세지를 여기서 만들고 위에서 뽑을수있을까여?
    			String message = "성공";
    			//세션을쓰셔야 해여
    			req.getSession().setAttribute("message", message);
    									//name이라는 이름의 parameter넘겨줘야 하고 이 name은
    									//newProp이 가지고 있죠
    			//redirection을 하죠...
    			String viewName = "/property?name="+newProp.getPropertyName();
    			resp.sendRedirect(req.getContextPath() + viewName);
    		}else {
    			resp.sendError(400);
    		}
    		
    		//새 property를 추가 해주어야 함
    		service.createProperty(newProp);
    	}
    	
    	//검증에 대한 책임을 별도의 메서드로 분리시킨거예요
    	private boolean validate(PropertyVO newProp) {
    		boolean valid = true;
    		
    		//검증의 둘 중 하나의 case에라도 걸리면 valid가 false가 되는거예요
    		if(newProp.getPropertyName()==null) {
    			valid = false;
    		}
    		if(newProp.getPropertyValue()==null) {
    			valid = false;
    		}
    		
    		return valid;
    	}
    
    }
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <pre style="border: 2px solid red;">
    	페이지 : <%=pageContext.getAttribute("pageAttr") %> 
    	요청 : <%=request.getAttribute("requestAttr") %>
    	세션 : <%=session.getAttribute("sessionAttr") %>
    	어플리케이션 : <%=application.getAttribute("applicationAttr") %>
    	
    </pre>
    
    
    </body>
    </html>

     

     

     

     

     

    ---

    ---

    ---

    어제 과제를 봐봅시다..

    j query plugin을 이러케 만들 수 있다

    ------

     

    https://getbootstrap.com/docs/5.1/components/modal/#live-demo

     

    Modal

    Use Bootstrap’s JavaScript modal plugin to add dialogs to your site for lightboxes, user notifications, or completely custom content.

    getbootstrap.com

    이 modal을 쓸거예요

     

    https://getbootstrap.com/docs/5.1/components/modal/#via-javascript

     

    Modal

    Use Bootstrap’s JavaScript modal plugin to add dialogs to your site for lightboxes, user notifications, or completely custom content.

    getbootstrap.com

    여기서 

    var myModal = new bootstrap.Modal(document.getElementById('myModal'), options)

    이걸 우리는 jquery형태로도 할 수 있어요...

     

    이런식으로....

    이 모달을 가지고 어떤 걸 할 수 있을까여?

    여길 가보세여
    쓸수있는게 많져

     

    --

     

    ---

    YES를 누르면 console에서 시간 갱신되는걸 확인하져

     

    날짜, 시간 핸들링 할 수 있는

    https://momentjs.com/

     

    Moment.js | Home

    Format Dates moment().format('MMMM Do YYYY, h:mm:ss a'); moment().format('dddd'); moment().format("MMM Do YY"); moment().format('YYYY [escaped] YYYY'); moment().format(); Relative Time moment("20111031", "YYYYMMDD").fromNow(); moment("20120620", "YYYYMMDD"

    momentjs.com

    이런걸 활용해서 시간을 200, 100 이렇게 말고 시간답게 보여줍시다

     

    여기서 js파일을 다운로드해서

    저기 넣어주시고요

    moment 객체에서 

    우리는 이런거만 필요한데요

    우리는 현재시간이 필요하지는 않고요 200초라는 시간 내에서 이걸 분,초로 보여주는것만 필요해요

     

    일반 moment 객체는 현재 시간을 가지고요
    이렇게 하면 밀리세컨드로 해 줄수 있고요
    이렇게 해 주면 분,초로 바꿔줄 수 있어여

     근데 이렇게 변환하는 과정을 거치면 timer에 미세하게 오차가 생겨서 이게 정확한 방법은 아니지만, moment라는 라이브러리를 써보기 위해 해보는 것이예요

     

     

    수정 전 코드... 혼자 시간이 돌아요....

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%
    	System.out.println(session.getLastAccessedTime());
    
    %>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>08/sessionTimer.jsp</title>
    <jsp:include page="/includee/preScript.jsp" />
    <!-- js파일이 어디는 위 어디는 아래에있네여. 잘 기억해두세요 -->
    
    </head>
    <body>
    
    <h4>application hashcode : <%=application.hashCode() %></h4>
    <h4>세션 타이머</h4>
    <div id="timerArea">1:59</div>
    <div id="msgArea">
       세션 만료 시간이 1분 남았음. 연장 할래?
       <input type="button" value="YES" class="ctrlBtn"/>
       <input type="button" value="NO" class="ctrlBtn"/>
    </div>
    
    <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body">
            세션 만료 시간이 1분 남았음. 연장 할래?
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary ctrlBtn">YES</button>
            <button type="button" class="btn btn-warning ctrlBtn">NO</button>
          </div>
        </div>
      </div>
    </div>
    
    
    
    
    <script type="text/javascript" src="<%=request.getContextPath() %>/resources/js/sessionTimer.js"></script>
    <script type="text/javascript">
    	$("#timerArea").sessionTimer(<%=session.getMaxInactiveInterval()%>, "#exampleModal");
    </script>
    <script type="text/javascript" src="<%=request.getContextPath() %>/resources/js/moment.min.js"></script>
    
    <script type="text/javascript">
    	
    
    	//좀 빠르게 해봅시다
    	const SPPED = 100;
    	//session timeout 초기값으로 갖는 타이머 출력
    	const timeout = <%=session.getMaxInactiveInterval()%>;
    	
    	//discount 할 변수 선언
    	let timer = timeout;
    	let timerJob =null;
    	let msgJob = null;
    	let timerArea = $("#timerArea");
    	let msgArea = $("#exampleModal").on("click",".crtlBtn", function(){
    		//this ==클릭한 버튼, thml element니까 $으로 감싸줘야 함
    		let command = $(this).text();
    			//연장하겠다
    		if('YES'==command){
    			//초기화 == 세션 연장 + 타이머 초기화, 이 떄 msg에 타시 timeout이 걸려야 함
    			init();
    			//init만 호출해주면 절반이 끝남. 모듈화의 장점임. 
    			
    			//요청을 하나 보내야 함. 비동기요청으로, 화면 전체에 lock을 걸지 않기 위해서 
    			$.ajax({
    				method : "head"
    				//이상해 보이지만 가만히 보면 충분히 이해 가능한 코드임.
    			});
    		}
    		msgArea.modal('hide');
    	});
    	
    	
    	//초기화 해 줄 건데, 함수 형태로 만들 것. 
    	let init = function(){
    		timer = timeout;
    		//아직 타이머가 동작하지 않는다 =null이다
    		if(timerJob==null)
    			//동작시켜야겠죠?, 1초마다 discount, interval, 두가지 종류의 parameter필요
    					//뭘 할지, 얼마만큼의 주기로 반복할건지
    			timerJob = setInterval(function(){
    				//timer discount, html 형태로 timerArea에 출력
    				timerArea.html(moment(--timer*1000).format('mm:ss'));
    				if(timer <=0)
    					clearInterval(timerJob);
    				}, 1*SPPED);
    		if(msgJob!=null)
    			clearTimeout(msgJob);
    		msgJob = setTimeout(function(){
    			msgArea.modal('show');
    				//우리는 SPEEP로 1000ms를 100ms로 처리해줫거든요
    				//interval이 100ms 단위로 갱신되듯 얘도 100ms단위로 갱신. 
    		}, (timeout -60)* SPPED );
    				
    //			}, 1000);//1000 ms =1s마다
    	}
    	
    	//호출
    	init();
    </script>
    
    <jsp:include page="/includee/postScript.jsp"></jsp:include>
    
    
    </body>
    </html>
    <!-- 
    1. session timeout 초기값으로 갖는 타이머 출력.
    
    //핵심은 주기가 설정되는거니까 scheduling을 해야 하고 그러면 interval뿐임
    2. 1초를 주기로  타이머를 디스카운트
    	-> 타이머가 0이 되면, 디스카운트 중단. 
    	
    //1분이 남은 시점에서 띄운다고 하면, total시간이 2분이면 1분(60초) 기다렸다가 띄우기..
    //scheduling에 timeout이라는게 있음.. 한번 작업하고 끝나는..
    3. "timeout - 60" 동안 대기한 후 메시지 창(modal) 디스플레이
    
    4-1. NO버튼 클릭, 메시지창 닫기
    
    4-2. YES버튼 클릭,
    	//서버의 세션을 연장하기 위한 요청일 뿐이고 뭔가 가져오려는 요청이 아님
    	1) 서버의 session을 연장하기 위한 비동기 요청(전송) :  response body가 없도록 method 설정.
    	2) 타이머 리셋
    	3) 메시지창 닫기.
    	
    	//4-1과 4-2의 옵션에 공통점이 생김. 버튼을 클릭한다, 메세지를 닫는다.
     -->

     

    script를 이빠이 길게 안하고

    <script type="text/javascript">
    	#('#timerArea').sessionTimer(120);
    </script>

    이렇게도 된대

     

    네 그걸 위해서 js파일을 만드는거같아요

     

    --

    /**
     * 
     */
    window.addEventListener("DOMContentLoaded", function(event) {
    	$.fn.sessionTimer = function(timeout, msgModal) {
    		//파라미터 검증하는 코드 들어와야 함, timeout이 없다 == timeout이 0, null, undefined 일 떄
    		// timeout의 type의 number가 아니거나
    		//timeout이 0보다 크거나 작다
    		if (!timeout || typeof timeout != 'number' || timeout <= 0)
    			//에러나 예외를 발생시켜서 중단시키겠다
    			throw Error("타이머 처리 불가");
    
    		//timeout parameter는 검증했는데 msgModal은검증을...
    		//해오세요.....?... msgModal....없을 때는 Error를 발생하는게 아니라 없으면 만들어주세요
    		//있으면 있는데로 있는거 쓰세요
    		if (!msgModal)
    
    			const SPEED = 100;
    		let timer = timeout;
    		let timerJob = null;
    		let msgJob = null;
    		let timerArea = this;
    		let msgArea = $(msgModal).on("click", ".ctrlBtn", function() {
    			let command = $(this).text();
    			if ('YES' == command) {
    				init();
    				$.ajax({
    					method: "head"
    				});
    			}
    			msgArea.modal('hide');
    		});
    
    		let init = function() {
    			timer = timeout;
    			if (timerJob == null)
    				timerJob = setInterval(function() {
    					timerArea.html(moment(--timer * 1000).format('mm:ss'));
    					if (timer <= 0)
    						clearInterval(timerJob);
    				}, 1 * SPEED);
    			if (msgJob != null)
    				clearTimeout(msgJob);
    			msgJob = setTimeout(function() {
    				msgArea.modal('show');
    			}, (timeout - 60) * SPEED);
    		}
    
    		init();
    	}
    
    });

    ---

    하드코딩이뭐야..

     

     

     

    숙제가 세개^0^

     

    1. 안돌아가는 시계를 돌려오세요

    2.

    3.

    브라우저를 통해서 이런 UI를 만들어오세요...ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

    감이 안오시는분들은 썜이 코드를 넣어놓으셨대요...보세요...

     

    에 아니래요 보지말고 해보시래요 그리고 나서 다음주에 design pattern과 연계해서 볼거래요....

    'main' 카테고리의 다른 글

    22-09-19_jsp  (2) 2022.09.19
    22-09-19_python  (0) 2022.09.19
    22-09-15_jsp  (4) 2022.09.15
    22-09-15_python  (0) 2022.09.15
    22-09-14_jsp  (1) 2022.09.14
Designed by Tistory.