JSP - 5. JSP vs. 서블릿, 모델1 vs. 모델2, 리퀘스트객체

갓김치·2020년 11월 24일
0

JSP+Spring

목록 보기
6/43

복습

서블릿 개발 단계

1. HttpServlet 상속 후 필요 메서드 재정의

2. 자바코드 컴파일 하여 클래스 파일 생성

3. 서블릿 등록

4. 서블릿 매핑

  • 방법1) web.xml 등록
  • 방법2) WebServlet 어노테이션
    • @WebServlet("/imageView.do")
    • 클라이언트는 웹서블릿 바로 밑에 이 파일이 존재한다고 생각하지만 사실은 그렇지않음
    • /imageView.do: 클라이언트는 URL이라고 생각하지만, 개발자입장에서는 URI
    • 지식인: .do 파일받았는데 어떻게 여나요?
    • URL과 URI의 차이
      • URI: 가상의 주소체계를 표현하는 방법
      • URL: 자원의 위치로 식별

Callback Method

Lifecycle Callback

  • init, destory
  • 생애주기 통틀어서 1번
    • 서블릿자체가 컨테이너로 인해 싱글톤으로 관리됨

Request Callback

  • service, do계열 메서드
  • 매번 리퀘스트가 들어올때마다 반복되어 실행됨
  • 요 과정이 템플릿 메서드
    • service 안에서 메서드 판단후 do계열로 요청위임
    • 과정은 동일하지만 과정의 최종목적지가 되는 do계열 메서드에서 하는일은 다르다
    • 순서는 결정되있으나 내용은 다르다

tmpl을 가지고 있는 톰캣 엔진


gugudan.tmpl 예제

MVC 패턴

Model: dataMap

  • model data를 잘 만들기 위해서는 command(클라이언트 요청) 분석 필요
    • 요청분석을 하기위해 getDataMap() 메서드에 request를 파라미터로 받아야함
  • 요소 안에 key-value 쌍이 있어서 Map안의 내용물을 Entry라 부른다

View: .tmpl

Controller: GugudanTemplateUseServlet

중요한 부분

서블릿의 콜백메서드는 단계적으로 움직임

Map 안의 key(String)-value(String[]): 하나의 네임에 여러개 파라미터

Map<String, String[]> parameterMap = request.getParameterMap();
// dataMap.putAll(parameterMap); 
// 맵안에 다른맵의 엔트리를 통으로 집어넣을 수 없음
// 이유: 문자열만 출력할 수 있기때문에 엔트리 그대로 쓸 수 없음 (tmpl엔진의 한계점)

for (Entry<String, String[]> entry : parameterMap.entrySet()) {
	String paramName = entry.getKey();
	String[] paramValues = entry.getValue();
	dataMap.put(paramName, paramValues.length == 1 ? paramValues[0] : paramValues); 
	// 1개 이상이면 paramValue 배열 그대로 보냄
    // 배열이 toString()을 오버라이드 하지 않아 해시코드주소로 넘어감
}

템플릿 동작구조

  • tmpl은 진짜 소스일까? -> 아니다. tmpl 하나 가지고는 아무것도 못하기 때문에.
    • gugudan.tmpl을 진짜 동작하게하는건 GugudanTemplateUseServlet
    • imageList.tmpl도 마찬가지
  • tmpl 파일 안에는 제어문도 반복문도 아무것도 없고 딱 응답데이터 나갈 틀만 존재
  • 모든 일은 ImageListServlet, GugudanServlet 에서 하고 있던 것
  • 그럼똑같은 형태를 jsp로 바꾸면 어떻게 될까?

jsp 스펙 사용하기 실습

서블릿 vs. JSP

서블릿JSP
서버사이드 언어클라이언트 사이드 언어
요청담당언어응답담당언어 -> 모델2와 연관
  • Model2
    • request를 담당하는 서블릿 스펙과 response를 담당 jsp스펙을 동시운영하여 어플리케이션을 구성하는 것

모델2 + MVC 바탕의 아키텍쳐

  • 모델2: MVC패턴을 구현하기 가장 적합한 방식
  • 줄여서 MVC 모델2

순서

  1. 서블릿이 요청을 받는다
  2. 서블릿이 요청을 분석한다
  3. 서블릿이 데이터를 가공한다
  4. 서블릿에서 JSP로 데이터를 넘긴다
    • jsp로 데이터로 넘긴다는게 mvc의 model(data)와 흡사
  5. JSP는 서블릿으로부터 데이터를 받아서 응답데이터를 만든다
  6. JSP에서 가공한 응답데이터를 클라이언트한테로 전송한다.

비교군1) 모델2 방식의 imageList.jsp

  • /WEB-INF/views 경로 밑에 imageList.jsp 생성
    • 해당 경로는 브라우저에서 접근할 수 없기 때문에, 이 주소로 실행하면 404 에러 발생
    • -> 사용자 요청을 받기위해 서블릿이 필요함
  • ImageListServlet_Model2.java

ServletContext

https://docs.oracle.com/javaee/7/api/javax/servlet/ServletContext.html
Defines a set of methods that a servlet uses to communicate with its servlet container

  • ex mime data base
  • 서블릿컨테이너에서 무언가 확인하거나 뭐 받아올때 쓰임

req.setAttribute

객체간 데이터 공유방법 생각해보기

  • 객체 a가 객체 b의 데이터에 접근할때 가장 쉬운 방법
    • 객체 b의 데이터를 전역변수 public 으로 풀어줌
    • ex) 이 상황에서 public String title = "이미지 목록";
    • ★ 하지만 우리의 상황에서는 이 방법을 사용할 수 없다.

불가능한 이유

  • 위의 방식을 사용하기 위해서 선행되어야할 것 : 인스턴스 생성
  • 서블릿의 public 변수 title에 바깥 객체가 접근하기 위해서는 이 서블릿의 객체를 생성해 가져야한다.
  • 하지만 서블릿 객체(인스턴스)생성은 톰캣(서블릿컨테이너)가 관리하는 영역이다.
  • 이때문에 이미 만들어진 서블릿 객체를 받아오기전까지는 외부에서 절대로 그 전역변수에 접근할 수 없다.
  • 전역변수를 이용한 간단한 방식은 일반적인 standalone application에서는 가능하다. 하지만 웹에서는 불가능.

그럼 어떻게 jsp에 보낼까? -> setAttribute == SCOPE 개념을 사용!

  • request객체에 데이터를 여러개 보낼 수 있다는 것 == scope도 컬렉션의 일종이다. (Map과 비슷 key-value)
  • 그러니까 결국 tmpl에서 썼던 dataMap을 request도 가지고 있었던 것....
  • 요약: 이 모든 것이 req.setAttribute 였던 것이다..

비교군2) 모델1 방식의 gugudan.jsp

  • /WEB-INF/밑에 존재하는게 아니기때문에 브라우저에서 다이렉트 접근 가능
    • = 서블렛 굳이 필요하지 않음
    • = 모델2 또한 굳이 필요하지 않음
  • 모델1 방식
    • request, response가 하나의 컴포넌트에 의해 처리되는방식
    • = 구조가 안쪼개져있음
    • = mvc불가
      • 컨트롤러와 뷰가 하나로 합쳐져있기때문, 이런 상황에서는 모델 데이터도 필요없음
      • = datamap도 필요없음 = 스코프필요없음 = ${param.minDAn}도 필요없음
  • ${}
    • 데이터맵여러개, 그중 param이라는 데이터맵에 minDan을 넣어준것
    • 후에 EL context에서 배울것

model1 vs model2 차이

  • mode1 (gugudan.jsp)
    • 데이터맵필요 x = 스코프필요x
  • model2 (imageList.jsp)
    • 데이터맵필요 o = 스코프가 가장 핵심적인 개념


HTTP의 request 패키징 방식

  • 모든서버가 다 지원하는 메서드는 doGet, doPost
<form method="post">
	<input type="hidden" name="_method" value="PUT">
</form>
  • 넘어갈땐 POST, 서버에서 도착후 PUT으로 ㄱㄱ
  • 이때 POST랑같이 바디 넘어감, 이걸로 서버 데이터를 수정하는 것

Request Line

  • URL(수신자), Method, Protocol/version

http method

  • 요청의 목적, 요청의 패키징 방식을 식별하기위해 사용됨 (body 생성할 것인지 결정)

종류

  • GET(R) : 조회 - 바디 필요X
  • POST(C) : 생성 - 바디 필수!~
  • PUT/PATCH(U) : 수정
    • 조건1) 이미 서버상에 데이터가 있어야함
    • 조건2) 그걸 내 데이터로 업데이트
  • DELETE(D): 삭제

  • HEADER : 나중에 돌아올때 응답데이터에 바디없어도됩니다 라는 의미
  • OPTION : 본요청 보내기 전에 본요청 메서드를 서버가 지원하는지 확인하느라 본요청 전에 보내는 preflight요청에 사용 (ex 님아 put돼요?)
  • TRACE : 서버상의 기능을 디버깅하고 싶을 때? 근데 이걸 서버가 내비둘까? 그래서 서버에서 거의 지원안하는 메서드임

Request Header

  • 브라우저에 의해 자동으로 생성된 meta data (data 자체에 대한 data, 부가정보)
  • 클라이언트에게 적합한 응답데이터를 내보내기위해 꼭꼭꼭 알아야하는것들임

속성 설명

  • Accept
    • 응답데이터는 반드시 다음의 타입으로 와야한다.
    • 우선순위 text/html, 적어도 xml, 적어도 image,..., */*(안되면 아무거나줘;;)
    • ajax에서 비동기로 요청시 dataType: json으로 적어준 이유
      • Accept헤더를 결정하는 것이었따.......
        - ★ 그동안 dataType을 고정했었는데 그러지말고 accept헤더를 잘 해석해서 써먹으면 여러방식의 데이터로 내보낼 수 있다
    • ★ accept헤더만 잘 해석하면 클라이언트가 요청한 데이터타입을 알 수 있다
    • ★ 그럴려면 억셉트를 잘가지고놀아야겠지? = 리퀘스트를 잘갖고놀아야지
  • Accept-Encoding
  • Accept-Language : LOCALE
    • ko(언어)-KR(국가), en-US...
    • 응답데이터 언어를 어떻게 구성해서 내보낼지 알 수 있음
    • 어떤 LOCALE문자를 가지고있느냐에따라 응답데이터를 영어로, 한글로, 일어로 내보낼 수 있음
    • 다국적 처리가 가능한 서비스가 필요하다면 이 헤더를 신경써야한다.
  • Cache-Control
  • Cookie
    • 쿠키는 언제 쓰는가?
      • 상태정보를 만들어져서 1주일동안 저장할라고보니까 공간이 두개가있네.
      • 클라이언트pc도 메모리가있고, 서버pc도 있으니 저장peer가 두군데
      • 클라이언트 피어에 저장하면 쿠키, 서버피어는 세션
    • 그동안 우리가 쓰던 쿠키는 그냥 오고가는 헤더였음
    • JSESSIONID: 세션내부의 식별자 SESSIONID가 쿠키의 형태로 오고 갔던 것
  • Host
    • 넘어가넘어가 지금얘기하기힘들다
  • User-Agent
    • 현재 클라이언트의 정보
    • 클라이언트가 pc로 접속했을 때랑 폰으로 접속했을 때, 다른 응답데이터를 내보내려면 네이버 서버는 클라이언트 시스템 환경(os)을 알아야 적절한 ui를 내보낼 수 있다. 이럴때 유저에이전트 정보를 활용한다
    • Mozilla
    • Windows
    • AppleWebkit: 렌더링엔진 html소스 번역
    • Chrome: 직접적으로 사용하고있는 브라우저종류

Request Body (★only POST)

  • content body, message body
  • 무조건 형성되는 영역 아님 -> method와 연결됨

request 뜯어보기

  • HttpServletRequest: http 프로토콜을 이용하여 서블릿을 대상으로 발생한 요청에 대한 캡슐화
  • jsp의 request내장객체
  • request가 동기가아니고 비동기라면 넘어가는게 어떻게 넘어갈까

request 객체의 메서드

  • 클라이언트의 브라우저가 통해 보내온 메타데이터(헤더 내용)들 꺼내는데 도움이 된다
  • 참고: requestDesc.jsp

getHeaderNames()

<%
Enumeration<String> names = request.getHeaderNames();
while(names.hasMoreElements()){
  String headerName = names.nextElement();
  String headerValue = request.getHeader(headerName);
  %>
  <tr>
  <td><%=headerName %></td>
  <td><%=headerValue %></td>
  </tr>
  <%
}
%>
  • HeaderName: value
    • key-value쌍 -> 맵으로 관리
    • request안에는 헤더를 관리하기 위한 맵이 들어있음
    • 우리는 그 정보만 잘 갖고놀면 헤더정보다꺼낼수있음

getAttribute(String name)

  • 스코프를 통해 공유되고있는 일종의 맵에 있는 데이터를 꺼낼 수 있음

getCharterEncoding(), getContentLength(), getContentType()

  • body 관련
  • body 없으면 의미없음
  • 고로 post 방식에서만 유효함
<%=request.getCharacterEncoding()%>
<%=request.getContentLength()%>
<%=request.getContentType()%>
<!-- 겟방식에서는 이거 다 null이나 -1로 나옴 post에서만 유효한 메서드기에 -->
  • getCharterEncoding: 해당 인코딩에 맞춰 디코딩을 잘해서 써야함
  • getContentLength: 서버의 처리범위를 넘어선다면 응답시 상태코드 내보내야됨
  • getContentType: 해킹파일(exe)일수도있으니까 확인하고 받아야함
    • 잘못하면 백도어로 사용됨
  • 사실이런거 다 header에서 가능한데 더 편하게 쓰라고 getter 만들어준거
  • getter가 없는 경우
    • HttpServletRequest말고 일반ServletRequest (상위타입) 을 가져올 때에는 캐스팅해서 써야됨
    • 만약 캐스팅도 불가능하다면? header에서 꺼내쓰면된다

getDateHeader(), getHeader(), getIntHeader()

  • getHeader: String
  • getDateHeader: long
    • 1970년 1월 1일 00:00:00 기준으로 millisecond 로 날짜를 표시하는 것이 기본이여서 long타입
  • getIntHeader: int
    • 만약 header가 숫자면 string으로 받을시또 파싱해야되는데 그런 과정을 생략하기 위한 ㅔ메서드

getLocalAddr(), getLocalName(), getLocalPort()

  • 0:0:0:0:0:0:0:0:1, 0:0:0:0:0:0:0:0:1, 80
  • 자바는 서버코드, 서버한테 로컬은 서버, 그니까 이건 서버의 ip!

getRemoteAddr(), getRemoteHost(), getRemotePort()

  • 클라이언트 포트넘버는 웰노운포트가아님
    client : 0:0:0:0:0:0:0:1, 0:0:0:0:0:0:0:1, 57039

getRequestURI(), getMethod(), getProtocol()

  • Request Line의 정보
  • URI: 식별성이중요, 이미알고있는 도메인네임은 제외하고 나머지 주소만, 주로 서버에서 사용

getQueryString()

  • 쿼리스트링 = 질의문자열 : 일정한형식가지고있음. 물음표기준으로 주소와 파라미터 식별
    • ?sector1§or2...sectorN
    • sector: param_name=param_value
    • 바디가 안만들어지는경우에 라인에 파라미터를 보내기 위해 사용됨 (주로 get방식의 경우)
    • post방식경우에는 body, line 다있기때문에 body와 line 둘다로 파라미터를 보낼 수 있음
  • 쿼리스트링은 라인을 통해 일부데이터를 보낼때 제한적으로 사용
    • 제한적 -> 서버마다다르지만 3가지 제한점을 공통으로가짐
      • 길이의 제한: 브라우저 주소창에 얼마나 긴 데이터가능할까?
      • 보낼 수 있는 건 '문자열'뿐
      • 데이터 노출이되서 보안에 취약
  • 필드나가서먼저듣는말: 겟방식쓰지마셈 폼태그무조건 포스트로!
  • 쿼리스트링: 여러개의 섹터
    • 섹터: 파라미터 한쌍, 엠퍼샌드로 식별
      • 파라미터 한쌍: =연산자를 사용해 key-value로 나뉨
  • param1=value1¶m2=value2
    • 쿼리스트링은 파싱이 안되고 원래 문자열 그대로 내보냄 (앰퍼샌드가 저렇게보임)
    • getParameter()를 사용해야 제대로 파싱할 수 있음

getLocal()

  • 헤더 accept-language 로케일중 우선순위가 가장 높은 로케일만 뽑아냄

폼데이터로 한글보내기

  • 1바이트 = 8비트 => 데이터로 표현할 수 있는 가짓수 = 2^8개
  • 한글: 자모음조합, 초중종성... 1바이트로 절대안댐 -> ASC-II코드 절대 NO
    • 한글은 2개이상 바이트가 묶여야댐 그거시 CHARSET => EUC-KR, UTF-8
  • 영문자,숫자: a-z,A-Z,0-9 62개만 식별하면 식별가능 -> ASC-II코드로 가능

  • view source => url인코딩(퍼센트인코딩)으로 된 글자 볼 수 있음
  • post건 get이건 꺼내기전에 먼저 인코딩을 해줘야함
  • RFC 2396 규약

post

  • post 인코딩이 가장 쉬움
request.setCharacterEncoding("UTF-8"); 
// useBodyEncodingURI="true"를 server.xml에 적용하면 이 메서드가 get에서도 작동
// 하지만 8.5에서는 이 속성이 기본으로 셋팅이 되어있음
String param = request.getParameter("param");

get

  • 톰캣이 먼저 주소를 통해 요청을 받고 어느 서블릿에 그 요청을 처리시킬지 컴포넌트를 물색함. 그렇기떄문에 톰캣단에서 먼저 디코딩해야함
  • 톰캣 server.xml 을 건들여야함
    <Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443"
    		   URIEncoding="UTF-8"
    />
  • 만약 클라이언트가 EUC-KR로 해서 보내면 위의 방식으로는 파싱 불가능.
  • 따라서 권장사항 아님
    <Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443"
    	useBodyEncodingForURI="true"	   
    />
  • 위의 방법 해석: 바디의 인코딩을 적용할 때, uri를 위해서도 똑같이 적용을 해라라는 소리

프로젝트 투입시 확인사항

  • get 에서 깨지는지 확인
    • 먼저 request.setCharacterEncoding("UTF-8")호출 해보고도 깨지면
    • 서버관리자한테 ㄱㄱ해서 얘기해바라
  • post에서 깨지는지 확인
    • request.setCharacterEncoding("UTF-8")호출
  • 어짜피 밖에나가면 톰캣안씀 중요한건 왜 get과 post에서 한글처리방식이 달라야하는지를 알아야된다악~!!!

기타

컬렉션뷰

  • 진짜 컬렉션이 아니고 컬렉션에 대한 접근방법만 정의한다고해서 컬렉션뷰
  • 돌아오는 값이 여러개다
  • enumeration,iterator 둘다 사용방식비슷

enumeration

  • hasMoreElements()

iterator

  • hasNext() 다음엘리먼트가있으면 next꺼내왕

과제

모델1 vs. 모델2 차이점

  • 구구단(모델1) vs imagelist (모델2)
  • 이둘의 처리방식 보며 우리가 작성했던 중프 코드를 보고 모델2가 어떻게 활용되었고 스코프가 어떻게 활용되었는지 다시 한번 봐라

request

  • 어플리케이션 전체를 통틀어 request, response 잘 갖고 노는게 젤중요
  • request 배웠으니까 오늘 50%배웟다
  • 근데이거 잘 복습안하면 response 해도 소용없다
  • api 최대한 마니볼것!~~!!!!!!
profile
갈 길이 멀다

0개의 댓글