ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 22-09-15_jsp
    main 2022. 9. 15. 19:01

    model 2, mvc패턴

     

     

     

    우리가 지금 작업하고 있는건 2tier, client tier, server tier

     

    요청과 응답을 하나의 객체로 처리하지, 분리된 책임구조로 두개로 쪼갤지가, model 1과 2의 차이임.

    어제 한 거의 핵심은, 책임을 어떻게 쪼개서 단일화 할 거냐. 

     

    우리는 model2를 하고 있다. 

     

    Controller(명령을 받아서, 이 명령이 필요로 하는 content 만들어야 함. 그러려면 information이 필요하고, inforamtiion을 만들려면 data가 있어야 함. 그러면 어딘가에서 information이 만들어 져야 하고, 이 inforamation을 우리는 model이라고도 함. 이걸 만드는 과정을 modeler 라고 하고

     

    이 modeler가 역할에 따라 몇 개로 쪼개짐.

     

    우리 프로젝트 하면서 설계 할 떄 

    가장 먼저, 요구사항정의서, 만들 때 가장 먼저 정한 것은? 우리 application에서 어떤 것들을 사용하는지 가장 먼저 정의

     ex)사원

     -> 급여관리를 하려면 급여라는게 필요하고...

     

    설계시에 명사를 가장 먼저 설계. 

    명사에 대한 설계가 완료되면 동사를 설계 

     

    그래서 VO를 가장 먼저 만듦. VO는 Domain Layer라고도 함. 

     

    이VO를 보자면, .....

     

    어제 data를 properties 확장자 가진 파일로 관리 함. 

    이 data 안에 가진 속성, property값을 하나의 객체에 정확하게 담아야 함.

     1) 각 값을 어떻게 담아야 할 지 /어떤 값을 담을지

     2) 각 값이 가진 데이터 타입

     3) 값이 있고, 타입이 있다면, 제약조건도 설정되어 있어야 함. 

       -> 그렇게 해서 하나의 객체가 만들어졌다면 객체와 객체가 관계(relation)을 형성할 수 있어야 함. 

     

    ==> data base에서 entity 설계할 때와 같아 보임. 

     

    data base의 scheme를 반영하여 짜게 됨. 

     

    그런데 어제는 DB는 사용을 안 했으니까, properties를 담을 수 있도록만 VO를 짠 거임. 

    그래서 지금 data는 file system의 형식으로 관리되고 있음 (db)

     

    dl data에 접근하는게 persistent Layer (data에 access 하기 때문에 편의상 DAO라고 하는 것), 이 dao에서 data를 가지고 오면, 이건 아직 가공하지 않은 raw data임. 이 raw data를 가공해야 하는데, 정해진 recipe대로 가공해야 하기에 이 recipe를 가진 business Logic Layer가 필요. 

    이 Layer가 information으로 가공 해 주고, 이 information을 model이라고 함.

     

    --

    그러면 이제 controller가 명령의 적합성을 확인하고 적합하면 BLL에 넘기고, PL에 명령을 전달하면 얘가 file system에서 data를 가져와서 넘겨넘겨와서 bll이 inforamtion을 만듦. 그걸 controller에 넘겨주. 이제 이걸 client가 요청한 형태로 가공해서 전달해야 해서 controller에서  view로 넘어가야 함. 

                                  // tomcat을 container라고 부름....

    그럼 이제 controller의 data를 view에 넘겨줘야(공유해야) 함. 그런데 이게 전역변수를 통한 data공유가 불가능해서, serattribute를 이용해서 전달함. 이 때 사용된 개념이 scope라는 개념. 

     

    이 때 쓸 수 있는 scope가 결론적으로 4개가 있음

     어떤 종류가 있고 

     어떻게 사용할 수 있느냐. (누가 가지고 있는지, 데이터들의 생명주기는 어떻게 되는지) 

    이걸 오늘 수업할 내용임. 

     

    --

    Layered, 지금 이렇게 나눈 것 == 각자 자기의 책임이 뚜렷하게 분리됨 == 응집력을 높이기 위한 것. 

    그런데 이제 이렇게 하면 의존력이 발생함. 

    controller는 service에 service는 dao에.. 의존관계가 뚜렷하게 표현됨. 

    그런데 controller는 view와 의존관계가 뚜렷하지 않음. 그래서 flow control 필요하고 scope 필요한 것

     

    dispatch중 forward, include 그리고 redirect방식.

    그런데 어떤 방식을 쓰냐에 따라서 scope가 달라짐.,

     

    * flow control과 scope가 어떤 관계를 가지고 있는지. 

     

    ---

     

    최종적으로 응답 데이터가 view로부터 나가면, Line, header, body가 있는데, 

    지난 시간에 어떻게 이걸 xml, json으로 marshalling할 수 있는지를 봤음. 

    ---

    이번 주 까지가 초반 수업인데, 초반수업의 핵심은 client와 server사이에 data를 어떻게 주고받는지가 핵심임,.

     

    그런데 중반수업부터는 tier가 3개로 쪼개짐.

     

    이제 data를 관리하는 별개의 server가 쪼개짐. Database라는걸 사용함. 그러면 이제 이때부터는 modeler와 Database의 관계에 중점을 맞추고 수업.

     

    ---

     

    3단계에서 중반수업 이후에는 이제 각 Layer를 지원하는 framework들을 볼 것. 

     

    이번 주 까지는 이 client와 modeler 사이의 request, response에 중점이었고, 어제 architecture는 부가적인 것이었음...

     

     

     

    =====

    어제의 과제는 수업 끝나는 상황을 봐서 하자

    =====

     

    새로운 수업

     

    ----

     

     

    오늘은 기본객체를 보자

     

    pdf의 6번타일에 해당함

     

     

    아무것도 안 쓰고 code assistant를 엶면 쓸 수 있는 이런 것들을 기본객체라고 한다

    -

    전역변수 ==  상태를 의미함

    -

     

    <기본 객체>
    1. request (HttpServletRequest) : 클라이언트가 전송한 요청의 모든 정보를 가진 객체.
    2. response (HttpServletResponse) : 서버가 전송한 응답의  모든 정보를 가진 객체.
    3. out (JspWriter) : 응답데이터를 기록하거나, 응답 버퍼를 관리.
    4. session (HttpSession) : 한 클라이언트를 대상으로 해당 사용자를 식별할 수 있는 모든 정보를 가진 객체.
    ( 한 세션과 관련된 모든 정보를 가진 객체)
    5. application (ServletContext) : 서버와 현재 컨텍스트에 대한 모든 정보를 가진 객체.
    6. page (Object) - this
    7. config (ServletConfig)
    8. (exception - Throwable)
    9. pageContext(*** - PageContext)

    모든 기본객체 중에서 가장 먼저 생성되는 기본객체이며, 나머지 기본객체의 참조(refernce)를 소유.
    getXXX형태로 꺼내쓸 수 있음. 

     

     

     

    --

    request와 response가 50이고 50이랬음. 그러면 1,2번을 제외한 나머지는 1,2번을 잘 하도록 도와주는 역할

    3. out (JspWriter)는 출력스트림이라는 것. printWriter의 역할을 해 줌 , 버퍼? 우리는 버퍼라는것도 알아보자

    4. session 얘는 도대체 뭘 가지고 있을까?

     세션이 세션과 관련된 정보를 가지고있어? 말장난 같지만, 이게 무슨 의미인지도 알아보자.

    5. application (ServletContext)도 알아보자. 이건 뭐하는거지

    ServletContext : file의 MIME타입을 결정, 요청을 dispatch할 떄도 사용. log 데이터를 기록할 떄도 사용

    request는 언제 생기고 언제 없어지느냐가 명확함.  클라이언트가 request할 때 생기고, response가 나가면 사라짐

    response도 동일함. 동일한 life cycle을 가짐.

     

    out은? 응답데이터가 나가서 더 이상의 stream이 필요없을 때 없어짐 위의 request, response와 life cycle 동일

     

    그러면 session은? 한 사용자라는 개념이 생길 때 session이 만들어지고, 이 사용자가 떠나면 없어짐.

     그러면 이 session도 분명한 생명주기를 가지고 있지만 위의 1,2,3번보다는 생명주기가 길음. 

     

    request, response가 끝나도 남아있음 좀 더 긴거지 그러면 5.application(ServletContext)는 언제 생기고 없어지냐면, server가 켜질 때 생기고 꺼질 때 사라짐. 가장 넓은 범위의 생명주기를 가짐.

     

    생존 범위가 다른 이 세개의 객체가 가진 저장공간이 하나씩 있음. 이 저장공간을 우리가 scope라고 부름. 

     

    request가 가지면 request Scope라고 부름. 그런데 2,3번은 1.request와 생명주기가 같아서 다른 scope같지 않음

    session이 가진건 session scope, 그리고 application(ServletContext)이 가진 건 application scope라고 함. 

     

    scope는 data를 공유할 떄 사용하는 저장공간임. 

     

    --

     자 그러면 이제 data를 공유할 때 어떤 scope를 써야 하나? 공유 정책에 따라서 선택적으로 저장공간을 선택하게 됨. 

     

    page는 타입?을 안 가짐. 얘는 그럼 뭐냐면

     

    현재 jsp page의 instance 자체 ==this임.

    	<%
    		page == this
    	%>

     

    page는 너무 최상위의 type이 설정되어 있어서 할 수 있는게 별로 없음. 

    this는 그 자체릐 파일을 그대로 가져와서 csat를 안 했는데, page를 쓰느니 this를 쓰는게 낫다

    후반부네 custom tag라는 걸 쓰게 되고, 경우에 따라 제작해서 쓰게 되는데, 이 때 page 객체를 사용함. 이 때가 아니면 쓸 일이 별로 없고, this를 쓰게 된다. 

    7번. 우리는 jsp를 하는데 갑자기 servlet이 나옴 왜? jsp는 servlet이니까?

     

    ServletConfig는 jsp하나당 하나가 생기는 객체임.

     - 얘의 존재이유는 하나임 jsp가 servlet이라는 걸 알려주는 것

     

    8번은 괄호를 쳐 놨는데 code assistant확인해 보면 exception이 없음. 왜?

    없음 exception

     

    exception은 최상위 타입은 throwable타입인데, 문제가 생겼을 때 문제에 대한 정보를 가짐

     

     

    여기서 볼 수 있는데, 여기 이거를

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8" isErrorPage="true"%>

    이렇게 처리하고

    이제 생김

     

    일반적인 상황에서 생기지는 않고, 에러가 생겼을 때 에러를 처리하기 위해서 생기는 것. 

     

    --

    jsp파일 실행했을 때

     

    jsp container로서의 tomcat의 역할은.. 요청에 따라 servlet을 만들고, comfile을 하고 이걸 대상으로 singlton객체를 만들고 thred를 만들고, ....특정 객체를 꺼내옴....

     

    잘 실행이 되었다면 tomcat이 역할을 잘 한거

     

     

    ----

    **아래 내용과 관련하여 덧글에 주석 있음

    여기가서 쟤를 열어보면
    얘가 있는데, 빨간애가 lifecycle이고ㅓ... 파란거는 뭐지
    아래 가서 보면 우리도 모르는 새 out사용중임
    이 기본객체들도 사용중임

    이렇게 보면 기본객체들이라는 놈들의 진짜 정체는 __init 안에 들어있는 지역변수들이라는 것.

     

    이놈들이 기본객첸데, pageContext가 제일 먼저 생김 그리고 밑에도 pageContext에서 꺼내오는데, 얘가 모든 기본객체를 다 가진다. 그리고 얘가 제일 중요하다는 것 알 수 있음.

     

     

    왜 9번은 별표를 쳐 놨냐면, 

    나중에 EL이라는 걸 배울건데 거기에서 pageContext만 유일하게 쓸 수있음. 그런데 얘를 쓸 수 있다는 건 위에 보니까 EL안에서도 나머지 기본객체들을 사용할 수 있음을 앗수 있음. 

     

    ---

     

    오늘내일수업은, 3,4,5,9번에 focus. 

    6,7,8은 일단 예외

    오늘 아마 3,4번정도까지 진도가 나가지 않을까 함

     

     

    ----

    아하, 장고 수업이 왜 어려울수밖에없냐 뭐 그런.. 얘기를 하셨는데 어쩃든 tool에 얽매이지 말고 동작원리를 알아야 한다고 함. 

    ----

     

    버퍼 관리를 해야 함, 왜냐면 데이터가 500byte가 나가야 하는데 한 200번에서 오류가 남. 그런데 바로바로 나갔었다고 하면 반만 나가고 망하는 거임 그런데 buffer에 담아둿다가 한번에 보내버리면 깔끔함

     

    이제 또 buffer는 8000의 용량을 가지고 있는데 data는 10000을 보내야 한다고 하면 이게 buffer가 가득 차버림. 이걸 비울지 안 비울지 관련있는게 

    flush() 임. 

     

    autoflush하면 8000 먼저 보내고 이후에 2000을 담을 수 있음. 

    그런데 여기에서 문제가 나머지 2000을 처리하는 과정에서 에러가 생기면 문제가 생김 8000은 이미 나갔거든

     

     

    여기 buffer 크기 설정 해주고

     

    	buffer size : <%=out.getBufferSize() %>
    	remaining size : <%=out.getRemaining() %>

    이걸 출력 해 주면

    이렇게 현재 buffer의 size와 남은 buffer 용량 size를 볼 수 있음

     

     

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8" buffer="1kb"%>   
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>08/bufferDesc.jsp</title>
    </head>
    <body>
    <h4>JspWriter를 이용한 Buffer 관리</h4>
    <pre>
    	buffer size : <%=out.getBufferSize() %>
    	remaining size : <%=out.getRemaining() %>
    	<%
    	
    		for(int i=1; i<=100; i++){
    			out.print(String.format("%d 번째 반복", i));
    			out.println(String.format("남은 버퍼의 용량 : %d",out.getRemaining()));
    		}
    	%>
    </pre>
    </body>
    </html>

    요렇게 해서 보면

    중간에 buffer 용량 비워줌. Autoflush가 true로 기본으로 설정 되어 있는 거임.

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8" buffer="1kb" autoFlush="false" %>

    Autoflush=false로 설정해주면

     

    500에러가 남

     

     

    -

    첫번째 예외 정보, 그리고 마지막 문단에 첫번째 부분에 보면 무슨 에러인지 알 수 있음. 

    오버플로우. 버퍼 넘쳐서 그럼.

    	<%
    	
    		for(int i=1; i<=100; i++){
    			out.print(String.format("%d 번째 반복", i));
    			out.println(String.format("남은 버퍼의 용량 : %d",out.getRemaining()));
    			if(i==40)
    				throw new RuntimeException("강제 발생 예외");
    			}
    	%>

    이렇게 고쳐주면

     

    이렇게 뜨는데

    client 입장에선 성공이지만 (화면이 나왔으니까)

    서버 입장에선 에러임

     

     

     

    후...빠르다

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8" buffer="1kb" autoFlush="false" %>   
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>08/bufferDesc.jsp</title>
    </head>
    <body>
    <h4>JspWriter를 이용한 Buffer 관리</h4>
    <pre>
    	buffer size : <%=out.getBufferSize() %>
    	remaining size : <%=out.getRemaining() %>
    	<%
    	
    		for(int i=1; i<=100; i++){
    			out.print(String.format("%d 번째 반복", i));
    			out.println(String.format("남은 버퍼의 용량 : %d",out.getRemaining()));
    			if(i==30)
    				out.flush();
    			
    			if(i==40)
    //				throw new RuntimeException("강제 발생 예외");
    				request.getRequestDispatcher("/08/implicit.jsp").forward(request, response);
    			}
    	%>
    </pre>
    </body>
    </html>

    이렇게 해 주면

     

    성공한 것 같지만, eclipse의 console에 가서 보면
    이런 에러가 발생함, forward는 buffer랑 어쨋든 연관이 있다는 건데

    연관이 있다는건데...

     

    이런 식으로 flush할 때 일단 데이터가 나간 이후에는 B가 기록을 못 함

    forwrd방식과 include방식이 어 뭐가 달랐는데요....못들었습니다...

    forward는A처리 후 buffer일단 보내버리고 include는 A,B 이어서 buffer에 기록..?

     

     

    에러처리과정은 Buffer에 담는 중에 error가 나면 buffer 내용을 지우고 error 메세지만 보냄

    그런데 flush가 중간에 처리되어버리면, 

    예를 들면 6번 전에 flush하면, 6번 처리중에 에러가 나더라도 12345는 다 출력되어 나가버려서 에러메세지를 보낼 수 없음

     

     

    include나 forward에서 bufer관리를 잘 못하면 생기는 문제가 이거임.

     

     

    buffer라는 개념이 뭔지 이해를 잘해야함. ...

    buffer의 size가 충분하지 않으면 안됨.

     

    JspWriter 를 잘 사용해서 buffer관리를 해야 함

     

    buffer는 jsp에서 쓰는거고 servlet stack에서 쓰는게 아님

     

    ---

     

    오늘 수업에서 진짜로 봐야 하는건 session이라는 객체에요

     

    --

    <session (HttpSession)>

    곁다리로 Cookie까지.
    Http : Connectless, Stateless
    session(시간) : stateless 단점을 보완하기 위해 서버쪽에 저장된 상태 정보.
    하나의 어플리케이션을 사용하기 시작한 순간부터 사용 종료까지의 기간. 
    생성 : 식별 대상이 되는 사용자가 최초의 요청을 전송. 세션의 ID가 식별성을 갖도록 생성됨.
    session id : <%=session.getId()%>
    session 생성 시점 : <%=new Date(session.getCreationTime()) %>
    session timeout : <%=session.getMaxInactiveInterval() %> 
    유지 : 세션의 식별성, tracking mode(두 피어가 세션 아이디를 공유하는 방식).
    1) COOKIE : Request Header(Cookie)를 통해 세션 아이디 재전송(공유)
    2) URL <a href="sessionDesc.jsp;jsessionid=<%=session.getId()%>"> 쿠키 없이 세션 유지 </a>
      : 세션 파라미터(Request Line)로 세션 아이디 재전송(공유), 보안 취약성.
    3) SSL(Secure Socket Layer) :  : 암호화된 세션 아이디 재전송(공유)

    소멸 : 
    1) 로그아웃.

    --> timeout 이내에 새로운 요청이 발생하지 않을 떄.
    2) 세션 쿠키 삭제
    3) 브라우저 종료

     

    ---

     

    session을 할 때는 cookie를 같이 하는데 이유가 있겠죠?

     

    --

    Cookie와 Session이 필요한 이유는 Http의 Stateless라는 속성 때문임.

     

     --대화가 되려면 응답데이터가 남아 있어야지만, 연속성을 가진 메세지가 교환 될 수 있음. 

    그런데 http는 기본적으로 연속성을 유지할 수 없는 형태임

    이 문제를 보완하기 위해 사용하는게 session과 cookie임. 메세지 교환의 연속성을 유지하기 위해서 그 데이터를 어딘가에 저장했을 때 그 저장데이터를 session,cookie라고 함

     

    최소한의 상태정보를 Server쪽에 저장하면 session이라고 하고 client쪽에 저장하면 cookie라고 함,

     

    ----

    session이라는 단어 들어봤나요

    session man? 공연에서 땜빵 해주는 사람? 정해진 한 공연의 시간 안에서만 연주를 대신 해 줌.

     

     session의 첫 번째 의미는

     1) 시간. 일정 시간을 또한 의미함. (세션 시작부터 만료)

     

     

    세션이 소멸되는 이벤트는 세 가지 정도로 구별할 수 있어요. 

       1) 로그아웃.  (가장 명확, 세션이 바로 만료되는 것)
       2) 세션 쿠키 삭제
       3) 브라우저 종료

     

     

    //....슬슬 따라갈 수 없는 속도로 변하고계씨죠

     

    예시를 하나 보자... naver 잘만 로그인되다가

    저기에 들어가서
    이걸 하시면

    로그인이 안됨

     

    ---

     

    이런것들이 있는데요

    --??????????

     

    session id cookie를 request의 header에 숨기는데, 이게 없으면 최초의 요청이고, 이게 있으면 최초의 요청이 아닌거예요...?

    다시 response data에...에.....에 받아서 A놈을 갱신을 해 그러면 수명이 늘어나 이게 tracking구조?

    쿠키차단한다는거는 이 갱신을 안하,.. 저장을 안하겠다는 소리...

     

     

     tracking mode가 세가지가 있다고 아니 어 뭐라고요

    Enumerated Values : 
    - COOKIE


    - URL
    - SSL

     

    꼭 header로 할 필요는 없..

     

     

    cookie가 저장이 안되어있어도 session을 유지할 순 있는데 노출의 위험이 있음....  (URL)

    이 때 session위조, hijacking이 발생할 위험이 있음. 

    이 문제를 해결 할 수 있어야 함. 

     

    그게 세번째 SSL임

     

     - 암호화시키는건데, https는 보안 인증서가 있어야 되서 할 수 있어서 우리가 이걸 지금 할 수는 없고, 그리고 우리가 지금 https환경에서 서버가 돌고 있는게 아니니까, cookie,  URL방식으로

    tracking을 해보자

     

     

    session이 가지고 있는 것들을 code assistance로 살펴보자

    getId....

    get creation 값 : 생성 시점을 가져옴 --long 타입으로 가져오네. 

    get lastAccessedTime 세션이 마지막으로 접근된...

    getmaxIncativeInterval() 놀아도 되는 최대 시간???????

     

    <%@page import="java.util.Date"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>08/sessionDesc.jsp</title>
    </head>
    <body>
    <h4>session (HttpSession)</h4>
    <pre>
    	곁다리로 Cookie까지.
    	Http : Connectless, Stateless
    	session(시간) : stateless 단점을 보완하기 위해 서버쪽에 저장된 상태 정보.
    					하나의 어플리케이션을 사용하기 시작한 순간부터 사용 종료까지의 기간. 
    	생성 : 식별 대상이 되는 사용자가 최초의 요청을 전송. 세션의 ID가 식별성을 갖도록 생성됨.
    		session id : <%=session.getId()%>
    		session 생성 시점 : <%=new Date(session.getCreationTime()) %>
    		session timeout : <%=session.getMaxInactiveInterval() %> 
    	유지 : 세션의 식별성, tracking mode(두 피어가 세션 아이디를 공유하는 방식).
    		1) COOKIE
    		2) URL
    		3) SSL
    		
    	소멸 : 
    		1) 로그아웃.
    		
    		--> timeout 이내에 새로운 요청이 발생하지 않을 떄.
    		2) 세션 쿠키 삭제
    		3) 브라우저 종료
    </pre>
    
    </body>
    </html>

     

     

    아까 크롬에서 설정을 날려서 session id가 새로고침 할 때마다 바뀜. == 세션이 유지가 안 되는 것

     --> 설정을 다시 바꿔줌.

     

    이제 F12눌러서 개발자 모드 켜고 network-header 확인해 보면

    16진수의 cookie값이 넘어감

    cookie가 있으면 최초발생내용이 아닌거라는 것

     

    여기서 쿠키 내용 확인 할 수 있음.

     

    여기 application에 가서 보면 Cookie라는게 Storage 항목에 분류되어있음., Session도 cookie도 저장소의 개념인 것.
    이 쿠키 아이디가 동일하게 나옴. 그런데 이제

     

    이렇게 날리면 또
    새로운 아이디가 나옴

     

     

    쿠키를 차단한다는 건, 들어오는 걸 안받는다는 거고, 안받으니까 전송도 안한다는 개념임. 

     

     

    그러면 아래처럼 쿠키를 저장 안한다고 설정하면

     

    계속 쿠키 id가 바뀌고 새로고침 할 때마다, 그리고 이전에 저장된 쿠키id와 다름. 이전에 마지막으로 저장된 거랑 지금 새로 만든거랑 다르다는거. 전에는 저장되어 있는게 갱신되지 않으니 값이 다른거임. 그리고 header 이쪽에 가면 cookie 정보가 없음. 새로 받아오는거 자체를 안 하겠다는 거임.

     

     

    2) URL <a href="sessionDesc.jsp;jsessionid=<%=session.getId()%>"> 쿠키 없이 세션 유지 </a>

    이런 코드를 만들어서 넣으면 새로고침같은 역할인건데

     

    URL에 cookie정보가 들어감. 그런데 보안 떄문에 안 씀.

     

    이 웹 페이지의 주소를 다른 브라우저에갖다 붙여 넣으면 지금...

    같은 id를 가져옴.. 원래 그렇지 않다고?

     

    몰라 똑같이 나옴 일단

     

     

    ---

    다음주 쯤 가능하면 암호화 알고리즘, 암호화 설정 등을 해 보자..

    --

    자 이제 쿠키 저장되게 설정하고 여기에 가서 보면 Expire = session이라고 되어있네여, 세션과 같은 life cycle 가진다는 것

    session과 cookie가 같이 날아간다는 거임

     

    그러면 지금 session id 잘 기억하고 있다가 browser 껏다가 다시 키면 session id가 바뀌어 있을 것

    이게 바뀌는 경우도 안 바뀌는 경우도 있는데 이건 브라우저 설정 등에 따라 다르긴 함.

     

     

    -----

     

    자 이제 javascript수업을 하면서 session을 가지고 놀기 가장 좋은 예제인 internet banking 파일 예제를 해 볼거예요

     

    세션 만료시간이 1800s, 30분이라고 되어 있다고 한다면....

    인터넷 뱅킹에서는 보통 timeout을 4분, 5분 이렇게 해 놧음. 

     

     

    왜지? 보통은 보안인데, 보면 세션....계속 유지되고 통신이 이루어지고 있으니까 그 동안에 탈취해 갈 수 있고, 탈취해가서 내 요청을 누가 위조하는 경우가 있음. request kogering??? 이나 hijacking이나 생길 수 있어서 그 공격시간 자체를 짧게 줄여버린 거임. 보안의 이유로

     

    session timeout : <%=session.getMaxInactiveInterval() %>s
    		<%
    			//2분으로 만료시간을 잡아봅시다.
    			session.setMaxInactiveInterval(60*2);
    		%> 
    		session timeout : <%=session.getMaxInactiveInterval() %>s

    이런 코드를 해보자요

     

    이제 보면 1800초에서 120초로 바뀜.
    근데 다시 새로고침하면 시간이 저렇게 바뀜 또

    새로 켤 때마다 최초 시간은 1800s로 나오게 되는데, 항상 어떤 사용자든 120s를 줘야 하니까 설정을 하나 추가로 해 줌. 

     

     

    여기선 분단위로 설정하게 됨

     

    그럼 이제 시작부터 120s이 됨

     

     

    -----

    네 이제 미션...

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>08/sessionTimer.jsp</title>
    </head>
    <body>
    <h4>세션 타이머</h4>
    <div id="timerArea">2:00</div>
    <div id="msgArea">
    	세션 만료 시간이 1분 남았음. 연장 하시겠습니까?
    	<input type="button" value="YES" class="crtlBtn"/>
    	<input type="button" value="NO" class="crtlBtn"/>
    </div>
    
    
    </body>
    </html>

    저 Timer가 0:00까지 discount되도록 만들기

    그리고 0이 되는 시점에는 scheduling이 중단 되어야겠고

    1:00 남은 시점에는 연장여부를 물어보고, 연장여부의 YES/NO여부에 따라서 timer가 reset되게 하기, 

     =>timer 만 reset되는게 아니라 server의 내 session의 생존시간이 늘어나야 함. 돌아오는 contents나 body가 있을 필요는 없고, 그러면 넘어가는 형태는 head? get? 이고

     

    두 버튼에 대한 event 처리... 어떤 버튼을 누르느냐에 따라서 session이 연장이 될 수도 / 안 될 수도 있게

    그리고 비동기요청으로 처리되어야 함. 동기요청으로 처리되면, 페이지의 나머지 영역이 다 새로고침 되어버리니까 안 됨...

     이 세 가지 조건에 맞게 짜야 함....

     

    timerArea div tag에 시간이 넘어가야 하.... ==> session이라는 객체가 가지고 있음. 

    그러면 session 기본객체로 시작하는 코드 작성이 되어야 함. 

     

    ---

    이거 과제로 돌리고.... 내일 해봅시다..

    ---

     

    남은 시간에는 지난 과제를 해보자요.....

     

    ---

    음 어떻게 처리할지 팁을 드리겠습니다

     

     

    음 그동안 디자인을 신경을 안썼었죠

    이런걸 볼건데요 5점대는 jqueery가 없어도 동작해요 5점대로 4점대는 아주 큰 차이가 있어요
    이거 일단 다운로드 보라색 버튼 눌러보시고요

    https://getbootstrap.com/docs/5.1/getting-started/introduction/

     

    Introduction

    Get started with Bootstrap, the world’s most popular framework for building responsive, mobile-first sites, with jsDelivr and a template starter page.

    getbootstrap.com

    이 사용방법들을 볼것이에요

     

    자 다운로드 받은거 압축을 풀어서

    이 폴더를 복사해서
    여기 넣어주시고요...

     

     

    스티브 사울러스의 웹 페이지 최적화 원칙??? ??????? 잘 들은거 맞나

    --

    design은 document의 상단에, script는 하단에서

    html 소스, design 소스, script소스는 합치지 마라 

    등이 있음

    --

     

    이렇게 해서

     

    preScript.jsp에

    지금 쓰이는 저놈을 넣어주고요, 경로는 contextpath로 해주고요

     

    bootstrap의js의  bootstrap.bundle.min.js가 쓰일거니까..

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <scipt type="text/javascript" src="<%=request.getContextPath() %>/resources/js/bootstrap-5.1.3-dist/js/bootstrap.bundle.min.js">

    이걸 만들어서,...

     

     

    이렇게 ...추가해주고요.,..

    https://getbootstrap.com/docs/5.1/components/modal/

     

    Modal

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

    getbootstrap.com

    시간 연장할거라고 창 띄우는 건 modal로 띄우세용...

     

     

    의사코드

    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의 옵션에 공통점이 생김. 버튼을 클릭한다, 메세지를 닫는다.

     

     

    =====

    지난 시간 과제를 봅시다...

    -------

    propertyMng.java를 봅시다

    PropertyControllerServlet.java랑 같이

    ------

    여기 sevservlet 어노테이션을 줄 건데

    value가 있으니 single value annotation으로 쓸 수도 있고, String 배열로 값을 받으니까, 두가지 내용을 한번에 처리할 수 있음. ({"/properties","/property"})

     

    package kr.or.ddit.props.controller;
    
    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({"/properties","/property"})
    public class PropertiesControllerServlet extends HttpServlet {
    
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		// TODO Auto-generated method stub
    		super.doGet(req, resp);
    	}
    	
    	//client가 server쪽으로 뭔가를 보내고 있다는 거...
    	@Override
    	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		// TODO Auto-generated method stub
    		super.doPost(req, resp);
    	}
    
    }

     

    nested uri의 형태..?.....

     

    이제 또

     

    	private PropertyService service = new PropertyServiceImpl();

    얘를 추가,.,,

     

     

    앞으로 수업은 결합력을 낮추는 방향으로 갈 거예요...

     

    쟤를 쓰면 전체를 가져올 수 있는데.....근데 문제가있음

    client가 요청한 request의 주소를 뽑아낼 때 URI대신에, req.getServletPath(); 를 사용할거예요

     

    String servletPath = req.getServletPath();
    		if("/properties".equals(servletPath)) {
    			
    		}else {
    			
    		}

    이렇게 할건데,, 첫번쨰 if문에 걸린다면...

    @Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		String servletPath = req.getServletPath();
    		Object model =null;
    		if("/properties".equals(servletPath)) {
    			//여기서 받아온 게 이 if문에서의 model이 되고
    			service.readProperties();
    			
    		}else {
    			//여기서 받아온 게 이 if문에서의 model이 됨
    			service.readProperty(name);
    		}
    	}

    저 if문과 else문에서 나온 모든 걸 받을 수 있는 model의 타입은 Object임

     

    JSONViewServlet

     

    속성데이터로 블럭 된 데에서 map을 만들고 그걸 가지고 그 아래에서 mashalling함 내가 할 필요가 없이 이거 활용하면 됨

     

     

    언제나 controller가 servlet 될 필요는 없고.. view가.....????

     

     

    status가 200이어서 정상처리 되거나 , 400이어서 검증에 걸리거나 둘 중 하나

     

    statusCode는 위에서 int 로 200을 기본값으로 해 두고 에러나면 400으로 바꾸는 코드

    @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;
    			//여기서 받아온 게 이 if문에서의 model이 됨
    			model = service.readProperty(name);
    		}
    		if(statusCode==200) {
    			req.setAttribute("model", model);
    			//모든 컨트롤러의 마지막에 view로 이동한다면 이것도, ... 모든 controller의 시작과 마지막에 중복 코드가 생긴다는건 중요한거니까 잘 기억해 두세요.
    			String viewName = "/jsonView.do";
    			
    		}else {
    			resp.sendError(statusCode);
    		}

    이릏게

     

     

     

    이건 서버에서 가져와야 하는 거고...
    이거 뭐 도메인이 가진 거랑 이름이 같은데요

     

     

    dao에 넘기고..dao에서 file을 view...

     

     

     

     

    string에 string이시네요...

     

    	//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);
    		}
    		
    		//새 property를 추가 해주어야 함
    		service.createProperty(newProp);
    	}

    아우 개빠르다

     

     

    		boolean valid = validate(newProp);
    		
    		//새 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;

     

     

    네....여기까지 한 게

     

    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;
    			//여기서 받아온 게 이 if문에서의 model이 됨
    			model = service.readProperty(name);
    		}
    		if(statusCode==200) {
    			req.setAttribute("model", model);
    			//모든 컨트롤러의 마지막에 view로 이동한다면 이것도, ... 모든 controller의 시작과 마지막에 중복 코드가 생긴다는건 중요한거니까 잘 기억해 두세요.
    			String viewName = "/jsonView.do";
    			
    		}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);
    		}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;
    	}
    
    }

    //  1. 현재 페이지가 랜더링된 후 전체 프로퍼티 정보를 조회하여 select option 을 완성함. line : /properties (GET)
    //  2. 선택 option이 변경될때마다 해당 프로퍼티의 정보를 조회하여 dataArea 에 랜더링함. line : /property?name=prop1 (GET)

     

    여기까지인거같습니다.

     

     

    3. 하단 form 을 비동기로 전송하여 새 프로퍼티를 추가하고, 기존의 option들의 앞메 추가함. line : /property (POST)

    이제 이걸 할거예요 아마도,...

     

    이렇게 하고 있고요 이게 이제 위의 doGet으로 간다는거 같아요

     

    boolean valid = validate(newProp);
    		if(valid) {
    			service.createProperty(newProp);
    									//name이라는 이름의 parameter넘겨줘야 하고 이 name은
    									//newProp이 가지고 있죠
    			String viewName = "/property?name="+newProp.getPropertyName();
    			resp.sendRedirect(req.getContextPath() + viewName);
    		}else {
    			resp.sendError(400);
    		}

    이렇게 되었습니다. 

     

    이제 비동기 처리가 남았습니다. 그런데 현재 페이지가 렌더링 된 후라는 조건이 있습니다. 

     

    propertyMng에 가봅시다

     

    EDD????

    이벤트 핸들러 안에서 대부분 처리가 된다고 합니다. 

    그걸 우리는 

    $(function(){   }); 이 형태로 처리한 거죠.

     

     

    <%@ 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>
    <jsp:include page="/includee/preScript.jsp" />
    </head>
    <body>
    <select id="propSel" size="10"></select>
    <table>
    	<thead>
    		<tr>
    			<th>프로퍼티명</th>
    			<th>프로퍼티값</th>
    			<th>비고</th>
    		</tr>
    	</thead>
    	<tbody id="dataArea">
    	
    	</tbody>
    </table>
    <form>
    	<input type="text" name="propertyName" placeholder="포르퍼티명"/>
    	<input type="text" name="propertyValue" placeholder="포르퍼티값"/>
    	<input type="submit" value="새 프로퍼티 추가" />
    </form>
    <div id="errArea"></div>
    <script type="text/javascript">
    	$(function(){ 
    		let propSel = $("#propSel");
    		$.ajax({
    			url : "<%=request.getContextPath() %>/properties",
    			success : function(resp) {
    				let dataList = resp.model;
    				let options =[];
    				$.each(dataList, function(index, propertyVO){
    					let option = $("<option>").text(propertyVO.propertyName);
    					options.push(option);
    				});
    				propSel.append(options);
    			},
    			error : function(errorResp) {
    				console.log(errorResp.status);
    			}
    		});
    	});
    
    
    
    </script>
    </body>
    </html>
    <!-- 
    // 	다음의 모든 요청은 비동기 처리를 기반으로 함.
    // 	1. 현재 페이지가 랜더링된 후 전체 프로퍼티 정보를 조회하여 select option 을 완성함. line : /properties (GET)
    // 	2. 선택 option이 변경될때마다 해당 프로퍼티의 정보를 조회하여 dataArea 에 랜더링함. line : /property?name=prop1 (GET)
    // 	3. 하단 form 을 비동기로 전송하여 새 프로퍼티를 추가하고, 기존의 option들의 앞메 추가함. line : /property (POST)
    // 	4. 모든 message(content)는 "json" 형식으로 교환됨.
    // 	5. 요청 처리에 실패한 경우, 해당 상태코드와 응답 메시지를 errArea 에 랜더링함.
     -->

    이렇게 했고요 실행을 할건데요

    비동기 요청이 발생했는지도 봐야 하고요

    property라는게 생겼는데

     

    뭐가 없대요

     

     

    이런게 나오네요
    이렇게 보내줘야 하죠
    이제 뭔가 오죠

     

     

    이제...

    //  2. 선택 option이 변경될때마다 해당 프로퍼티의 정보를 조회하여 dataArea 에 랜더링함. line : /property?name=prop1 (GET)

    여기에서 

    'option이 변경될때 ' 라는걸 처리한건데요

     

    ajax 추가 할 때.. dataType은 안 써도 됨 왜냐면 다 JSON으로 하겠다고 가정을 놓았으니까여

     

     

    dataArea에 그림을 그리려면 tbody가 selecting되어야 하고... 안에 tr 세가지 옵션이 또 있군요

     

    $("<tr>").append

     

    이거 한개의 값을 넣을때는 .html로 넣는데 여러 개의 옵션을 넣을 떄는 .append를 씁니다. 

    				dataType : "json",
    				success : function(resp) {
    					//여기서 뭘 꺼내야 할까요? model을 꺼냈죠 아래에서는?
    					let propertyVO = resp.model;
    					let trTag = $("<tr>").append(
    									$("<tr>").html(propertyVO.propertyName)
    									,$("<tr>").html(propertyVO.propertyValue)
    									,$("<tr>").html(propertyVO.description)
    								);
    					},

     

     

    <form action="<%=request.getContextPath() %>/property" method="post">
    	<input type="text" name="propertyName" placeholder="포르퍼티명"/>
    	<input type="text" name="propertyValue" placeholder="포르퍼티값"/>
    	<input type="submit" value="새 프로퍼티 추가" />
    </form>

    저 버튼을 누를 때마다 form의 action 경로로 method 방식의 요청이 발생

     

     

    <%@ 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>
    <jsp:include page="/includee/preScript.jsp" />
    </head>
    <body>
    <select id="propSel" size="10"></select>
    <table>
    	<thead>
    		<tr>
    			<th>프로퍼티명</th>
    			<th>프로퍼티값</th>
    			<th>비고</th>
    		</tr>
    	</thead>
    	<tbody id="dataArea">
    	
    	</tbody>
    </table>
    
    <form id="propForm" action="<%=request.getContextPath() %>/property" method="post">
    	<input type="text" name="propertyName" placeholder="포르퍼티명"/>
    	<input type="text" name="propertyValue" placeholder="포르퍼티값"/>
    	<input type="submit" value="새 프로퍼티 추가" />
    </form>
    <div id="errArea"></div>
    <script type="text/javascript">
    	$(function(){ 
    				//이 propForm은 jquery type
    		let propForm = $("#propForm").on("submit", function(event){
    			event.preventDefault();
    			//이 중간에서 비동기요청 처리..
    				//이 form은 htmlElement type이고
    			let form = this;
    			$.ajax({
    				url : form.action,
    				method : form.method,
    						//querystring인데 method 때문에 queryString이 body에 들어감
    				data : propForm.serialize(),
    				dataType : "json",
    				success : function(resp) {
    					console.log(resp);
    				},
    				error : function(errorResp) {
    					console.log(errorResp.status);
    				}
    			});
    			
    			return false;
    		});
    		let dataArea = $("#dataArea");
    		let propSel = $("#propSel").on("change", function(){
    			let propertyName = $(this).val();
    			//여기서 다시 비동기 요청이 발생해야 함
    			$.ajax({
    				url : "<%=request.getContextPath() %>/property",
    				data : {
    					name : propertyName
    				},
    				dataType : "json",
    				success : function(resp) {
    					//여기서 뭘 꺼내야 할까요? model을 꺼냈죠 아래에서는?
    					let propertyVO = resp.model;
    					let trTag = $("<tr>").append(
    									$("<tr>").html(propertyVO.propertyName)
    									,$("<tr>").html(propertyVO.propertyValue)
    									,$("<tr>").html(propertyVO.description)
    								);
    						dataArea.html(trTag);
    					},
    				error : function(errorResp) {
    					console.log(errorResp.status);
    				}
    			});
    		});
    		
    		$.ajax({
    			url : "<%=request.getContextPath() %>/properties",
    			success : function(resp) {
    				let dataList = resp.model;
    				let options =[];
    				$.each(dataList, function(index, propertyVO){
    					let option = $("<option>").text(propertyVO.propertyName);
    					options.push(option);
    				});
    				propSel.append(options);
    			},
    			error : function(errorResp) {
    				console.log(errorResp.status);
    			}
    		});
    	});
    
    
    
    </script>
    </body>
    </html>
    <!-- 
    // 	다음의 모든 요청은 비동기 처리를 기반으로 함.
    // 	1. 현재 페이지가 랜더링된 후 전체 프로퍼티 정보를 조회하여 select option 을 완성함. line : /properties (GET)
    // 	2. 선택 option이 변경될때마다 해당 프로퍼티의 정보를 조회하여 dataArea 에 랜더링함. line : /property?name=prop1 (GET)
    // 	3. 하단 form 을 비동기로 전송하여 새 프로퍼티를 추가하고, 기존의 option들의 앞메 추가함. line : /property (POST)
    // 	4. 모든 message(content)는 "json" 형식으로 교환됨.
    // 	5. 요청 처리에 실패한 경우, 해당 상태코드와 응답 메시지를 errArea 에 랜더링함.
     -->

    지금 이건데...

    success 를 console.log로 찍어서 확인해 보면 어떻게 나오는지...

     

     

    이렇게 property 하나의 값을 가지고
    그래서 추가하면 이런 모양으로 나옴

     

    success : function(resp) {
    					let newProp = resp.model;
    					let option = $("<option>").text(newProp.propertyName);
    				},

    success 이렇게 하는데요

     

    3. 하단 form 을 비동기로 전송하여 새 프로퍼티를 추가하고, 기존의 option들의 앞메 추가함. line : /property (POST)

    이게 조건인데 

     

    근데 이제 앞에다가 붙일거니까요 prepend를 또 해야 하고요

    success : function(resp) {
    					let newProp = resp.model;
    					let option = $("<option>").text(newProp.propertyName);
    					propSel.prepend(option);
    				},

    이렇게 해보죠 일단

     

    앞쪽으로 추가가 됩니다.

     

    이제 추가하고 나서 입력창에 있는 내용들을 clear 해 줘야 해요

     

    jquery에는 reset이라는 event구조가 없어요

    그러면 form.reset()으로 해 줘야 함.

    잘 사라집니다 이제

     

    ---

    ???중간에 못들었지만...https://jquery.com/

     

    jQuery

    What is jQuery? jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers.

    jquery.com

    여기로 이동하자

     

    jquery 도 document가 있음. 이 문서들을 잘 못쓰는게 안타까운데요

     

    홈페이지의 api documnet 에 들어가서 보면

     

    selector도 우리 모르는데 여기 와서 보면 있음. 이거 보면서 하면 익숙해지면 안 보고도 할 수 있다 이제
    이거 봐보자
    이렇게 나오고 sample 예제들이 있음.

    handler에 보면

    (Type: Function( Event event, jqXHR jqXHR, PlainObject ajaxSettings, String thrownError ))

    이런 함수들이 있고 이런 parameter가 있다 

     

     

    	$(function(){ 
    		//현재 페이지에서 발생하는 모든 비동기 요청에 대한 처리
    		//에러 처리 한번에 할 거임
    		let errArea = $("#errArea");
    		$(document).ajaxError(function(event, jqXHR, thrownError){
    			//마지막 5번에서 하려고 한 걸 이 안에서 공통적으로 처리해 주면 됩니다.
    							//상태코드 + 구체적인 에러 메세지
    			errArea.html(jqXHR.status + jqXHR.responseText);
    		});

    모든 ajax들에게서 error 부분을 지우고

    가장 위에 

    이렇게 적용을 해 줄거예요

     

    이제 에러가 잘 동작하는지 봐 줘야 하니까 

     

    이렇게 해서 에러를일부러 만들어 줄거예요

     

     

     

    추가를 하면 이렇게 에러가 나와요

     ** 혹시 에러가 500번이 나오면 서버를 재시작하면됩니다. tomcat이 가진 reload특성????인가 떄문이니까여

     

    그리고 혹시 또 500이 나오면 지금 코드에서 

     

    디버깅 해 보니까 저 부분이 있네여 저거 else 추가해줍니다

     

     

    네...400이 잘 나옵니다....

     

     

    ----

    미션을 주실 땐 수업 때 안 한 거는 안 주니까 전에 한 코드들을 차분히 잘 살펴보라고 하셨어요...

    여러분들이 jquery 수업시간에 만들었던 소스들..

    timeout과 interval이라는 소스가 이전에 했던거 중에 있을텐데요... 이걸 활용하면 아까 과제로 내 주신 걸....시간이 흘러가게 하는 걸 할 수 있을거래요..

     

    --

    오늘은 여기까지.....

     

     

    오늘의 전체코드....

    PropertiesControllerServlet.java

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

     

    propertyMng.jsp

    <%@ 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>
    <jsp:include page="/includee/preScript.jsp" />
    </head>
    <body>
    <select id="propSel" size="10"></select>
    <table>
    	<thead>
    		<tr>
    			<th>프로퍼티명</th>
    			<th>프로퍼티값</th>
    			<th>비고</th>
    		</tr>
    	</thead>
    	<tbody id="dataArea">
    	
    	</tbody>
    </table>
    
    <form id="propForm" action="<%=request.getContextPath() %>/property" method="post">
    	<input type="text" name="propertyName" placeholder="포르퍼티명"/>
    	<input type="text" name="propertyValue" placeholder="포르퍼티값"/>
    	<input type="submit" value="새 프로퍼티 추가" />
    </form>
    <div id="errArea"></div>
    <script type="text/javascript">
    	$(function(){ 
    		//현재 페이지에서 발생하는 모든 비동기 요청에 대한 처리
    		//에러 처리 한번에 할 거임
    		let errArea = $("#errArea");
    		$(document).ajaxError(function(event, jqXHR, thrownError){
    			//마지막 5번에서 하려고 한 걸 이 안에서 공통적으로 처리해 주면 됩니다.
    							//상태코드 + 구체적인 에러 메세지
    			errArea.html(jqXHR.status + jqXHR.responseText);
    		});
    				//이 propForm은 jquery type
    		let propForm = $("#propForm").on("submit", function(event){
    			event.preventDefault();
    			//이 중간에서 비동기요청 처리..
    				//이 form은 htmlElement type이고
    			let form = this;
    			$.ajax({
    				url : form.action,
    				method : form.method,
    						//querystring인데 method 때문에 queryString이 body에 들어감
    				data : propForm.serialize(),
    				dataType : "json",
    				success : function(resp) {
    					let newProp = resp.model;
    					let option = $("<option>").text(newProp.propertyName);
    					propSel.prepend(option);
    					form.reset();
    				},
    
    			});
    			
    			return false;
    		});
    		let dataArea = $("#dataArea");
    		let propSel = $("#propSel").on("change", function(){
    			let propertyName = $(this).val();
    			//여기서 다시 비동기 요청이 발생해야 함
    			$.ajax({
    				url : "<%=request.getContextPath() %>/property",
    				/*
    				data : {
    					name : propertyName
    				},
    				*/
    				dataType : "json",
    				success : function(resp) {
    					//여기서 뭘 꺼내야 할까요? model을 꺼냈죠 아래에서는?
    					let propertyVO = resp.model;
    					let trTag = $("<tr>").append(
    									$("<tr>").html(propertyVO.propertyName)
    									,$("<tr>").html(propertyVO.propertyValue)
    									,$("<tr>").html(propertyVO.description)
    								);
    						dataArea.html(trTag);
    					},
    
    			});
    		});
    		
    		$.ajax({
    			url : "<%=request.getContextPath() %>/properties",
    			success : function(resp) {
    				let dataList = resp.model;
    				let options =[];
    				$.each(dataList, function(index, propertyVO){
    					let option = $("<option>").text(propertyVO.propertyName);
    					options.push(option);
    				});
    				propSel.append(options);
    			},
    
    		});
    	});
    
    
    
    </script>
    </body>
    </html>
    <!-- 
    // 	다음의 모든 요청은 비동기 처리를 기반으로 함.
    // 	1. 현재 페이지가 랜더링된 후 전체 프로퍼티 정보를 조회하여 select option 을 완성함. line : /properties (GET)
    // 	2. 선택 option이 변경될때마다 해당 프로퍼티의 정보를 조회하여 dataArea 에 랜더링함. line : /property?name=prop1 (GET)
    // 	3. 하단 form 을 비동기로 전송하여 새 프로퍼티를 추가하고, 기존의 option들의 앞메 추가함. line : /property (POST)
    // 	4. 모든 message(content)는 "json" 형식으로 교환됨.
    // 	5. 요청 처리에 실패한 경우, 해당 상태코드와 응답 메시지를 errArea 에 랜더링함.
     -->

     

    완성된 상태에서의 페이지

    Network에서 처리내용 preview

    생성되는 request, response header

     

    넘어가는 response값

    {"javax.servlet.forward.context_path":"/WebStudy01","javax.servlet.forward.servlet_path":"/property","javax.servlet.forward.mapping":{"matchValue":"property","pattern":"/property","servletName":"kr.or.ddit.props.controller.PropertiesControllerServlet","mappingMatch":"EXACT"},"javax.servlet.forward.request_uri":"/WebStudy01/property","javax.servlet.forward.query_string":"name=prop8","model":{"propertyName":"prop8","propertyValue":"value8","description":null}}

     

    'main' 카테고리의 다른 글

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