복습
서블릿 개발 단계
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();
for (Entry<String, String[]> entry : parameterMap.entrySet()) {
String paramName = entry.getKey();
String[] paramValues = entry.getValue();
dataMap.put(paramName, paramValues.length == 1 ? paramValues[0] : paramValues);
}
템플릿 동작구조
- 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
순서
- 서블릿이 요청을 받는다
- 서블릿이 요청을 분석한다
- 서블릿이 데이터를 가공한다
- 서블릿에서 JSP로 데이터를 넘긴다
- jsp로 데이터로 넘긴다는게 mvc의 model(data)와 흡사
- JSP는 서블릿으로부터 데이터를 받아서 응답데이터를 만든다
- 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)
- 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 : 서버상의 기능을 디버깅하고 싶을 때? 근데 이걸 서버가 내비둘까? 그래서 서버에서 거의 지원안하는 메서드임
- 브라우저에 의해 자동으로 생성된 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
<%
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()%>
- getCharterEncoding: 해당 인코딩에 맞춰 디코딩을 잘해서 써야함
- getContentLength: 서버의 처리범위를 넘어선다면 응답시 상태코드 내보내야됨
- getContentType: 해킹파일(exe)일수도있으니까 확인하고 받아야함
- 사실이런거 다 header에서 가능한데 더 편하게 쓰라고 getter 만들어준거
- getter가 없는 경우
- HttpServletRequest말고 일반ServletRequest (상위타입) 을 가져올 때에는 캐스팅해서 써야됨
- 만약 캐스팅도 불가능하다면? header에서 꺼내쓰면된다
- 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
request.setCharacterEncoding("UTF-8");
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
iterator
- hasNext() 다음엘리먼트가있으면 next꺼내왕
과제
모델1 vs. 모델2 차이점
- 구구단(모델1) vs imagelist (모델2)
- 이둘의 처리방식 보며 우리가 작성했던 중프 코드를 보고 모델2가 어떻게 활용되었고 스코프가 어떻게 활용되었는지 다시 한번 봐라
request
- 어플리케이션 전체를 통틀어 request, response 잘 갖고 노는게 젤중요
- request 배웠으니까 오늘 50%배웟다
- 근데이거 잘 복습안하면 response 해도 소용없다
- api 최대한 마니볼것!~~!!!!!!