JSP - 6. 리퀘스트 헤더와 enum

갓김치·2020년 11월 25일
0

JSP+Spring

목록 보기
7/43

복습

HTTP 프로토콜

  • 클라이언트 피어◀──(데이터 교환: 바이너리 데이터 스트림)──▶ 서버 피어
  • 프로토콜
    • 네트워크의 기본인 패킷 통신에서 사용되는 패킷을 위한 포장 규칙
      • packet: 바이너리 데이터를 구간별로 쪼갠것
    • 교환된 바이너리 데이터를 파싱할 때 사용되는 약속
  • 특징
    • STATE-LESS: 상태가 없음
    • CONNECT-LESS: 응답나가면 커넥션 끊어짐

HTTP 내 request의 포장규칙

request line

  • 택배상자 뜯어서 제일 먼저보이는 부분
  • 보안에 취약 (누구나 열어볼 수 있음)

    예시

    • 네트워크 상에는 다수의 서버가 존재 (ex: naver서버, daum서버)
      만약 이메일과 같은 패킷통신이라면 수신자에 상관없이 naver서버, daum서버 모두에게 데이터가 전달됨.
    • request line에 수신자정보가 naver라고 되어있다면?
      • daum이 열어보고 '엥 네이버자나 쓸 필요없네' 하고 버림
    • 결론 : request line의 정보는 모두에게 노출될 수 밖에 없는 구조
      • 이때문에 라인을 통한 데이터 전달 방식이 위험한 것임
  • line에 들어가는 데이터 종류
    • URL: 수신자 주소정보 ()
    • 리퀘스트 메서드 == http 메서드
      • 1. 요청의 목적, 2. 요청의 패키지방식을 식별하기 위해 사용됨
    • 프로토콜에 대한 정보
  • http메서드의 종류 ★★
    • get (R)
    • post (C)
      • 가입정보를 비롯한 보안에 취약한 정보를 보낼땐 body 형성을 위해 post방식 사용
    • put/patch (U)
    • delete (D)
    • header
      • 돌아오는 응답데이터 형태 저장
    • option
      • preflight : 현재 서버가 특정 메서드 지원하는지 확인하기위해
    • trace
      • 디버깅용도, 서버가 잘 지원 하지 않음

request header

  • 현재 요청에 대한 진짜 정보가 아닌 메타데이터
  • 클라이언트가 직접 입력한 정보가 아닌, 브라우저(에이전트)가 주는 정보
  • key-value 쌍의 형태
  • 이 모든게 String으로 전송되기때문에 적절한 형변환 필요
  • body가 없다면 파라미터가 쿼리스트링으로 붙음
  • parameter와 part의 차이: 데이터 전달되는 타입이 다르다
    • parameter: String
    • part: 이진데이터
      • part: 자기만의 라인 헤더를 가지고 있다 = 파트 하나당 하나의 섹션을 가지고 있다
      • enctype=multipart: 바디의 영역을 여러 섹션으로 쪼갠 후, 하나의 섹션마다 헤더를 따로 가지고 있겠다는 의미. 자기만의 섹션을 구성하고 보낼 수 있기때문에 파트마다 데이터타입 다 다를 수 있다. 예를 들어, 하나의 바디만으로도 액셀과 사진을 동시에 보낼 수 있는 방식임.
  • 헤더의 종류
    • accept
    • accept-language
    • accept-encoding
    • user-agent

reqeust body

  • message body, contents body
  • http메서드가 내용을 보내기 위한 메서드라면 body가 필요하다
  • post에만 body가 만들어진다

모델1 vs. 모델2

  • 구구단, 이미지리스트

모델1: 요청과 응답이 한 구조에서 처리됨

  • /02/gugudan.jsp
  • 서블릿과 jsp에서 따로 처리하던 req, resp가 하나의 jsp에서 처리되고있음 = 분리x = 모델1
  • request.getParamter로 직접 클라이언트가 보낸 parameter를 꺼내쓰고있음
    • 모델2 방식에서는 서블릿에서 파라미터를 꺼내 데이터 작업후 setAttribute()로 보내준 값을 jsp에서 getAttribute()로 꺼내썼음
  • 가독성이 떨어며, 수정 사항이 발생하더라도 수정이 쉽지 않음
  • 구구단은 하는게 별로없어서 소스가 심플해서 가능하지만 대형 프로젝트에서는 불가능할 것임.

모델1의 장단점

  • 장점: 구조가 심플해 코드 분석이 쉽다
  • 단점: 보안에 취약하다
    • 이유: 외부에서 접근가능한 경로에 소스가 있음 = 원본 소스를 외부에서 가져갈수있음

모델2: 요청과 응답이 서로 분리된 구조에서 처리됨

  • /WEB-INF/views/imageList.jsp
  • 객체지향 SOLID 원칙중 단일책임원칙과 관련이 있음
    • 하나의 객체, 하나의 메서드, 하나의 클래스에는 하나의 책임만!
    • 변경사항이 발생해도 타겟팅 가능 (IBATIS->MYBATIS여도 DAO만 바꾸면됨)
      • DAO: DB접속, RAW DATA획득
      • SERVICE: RAW DATA 가공
      • CONTROLLER: 일처리
      • JSP: 응답 내보내는 코드

모델2의 작업 순서

  1. 서블릿: 클라이언트의 요청을 받아 분석한 후
  2. 서블릿: 필요한 데이터(컨텐츠)를 생성해냄
  3. 서블릿: 컨텐츠를 스코프를 통해 JSP에 전달해야겠군!
  4. 서블릿: UI를 구성할수 있는 VIEW를 선택
  5. 선택한 VIEW로 이동하는 상황 발생 (--여기까지 1단계)
  6. JSP(VIEW단): 스코프를 통해 공유한 데이터를 꺼낸다(2단계)
  7. JSP:구멍을 치환해서 UI를 구성해서 내보낸다.(3단계)

스코프 ★★★

  • 모델2를 잘 활용하기위해는 스코프 특성과 스코프 활용 방법을 잘 알아야함!
  • 스코프 사용 이유
    • 컨테이너가 객체 관리 권한을 독점하고있기때문에 서블릿과 JSP 사이에서는 전역변수로 데이터 공유 불가
    • WEB이라는 공간 안에서 서로 다른 컴포넌트가 데이터를 공유할수있는 '스코프'라는 영역을 이용해야함
    • 스코프의 공유범위(페이지,리퀘스트,세션,어플리케이션)에 따라 데이터 공유 -> 나중에 다시 수업

모델2의 장단점

  • 장점: WEB-INF밑에 있어 접근불가, 보안강함
  • 단점: 구조 계속 쪼개서 책임분리하기때문에 전체구조를 알려면 쉽지가 않다

JSP vs. 서블릿

  • 정적인 텍스트를 어떻게 구성할 것이냐 = 디자이너와 어케 협업할 것이냐에 포커스
  • jsp: 템플릿 기반의 소스의 형태를 취하고있기때문에 이안에 html,css,js와 같은 정적인 텍스트가 훨씬 많이 들어감



오늘

jsp 내의 변수는 무슨 변수일까

  • 지역변수, 블럭변수.......

user-agent

  • 엣지가 크롬엔진을 써서 만들어서 chrome도 떠있음
  • 렌더링엔진: 각자 브라우저의 번역기
    • 크롬: AppleWebKit
    • 익스플로러: gecko와 비슷한 Trident엔진을 쓴다
  • 키워드
    • 엣지: edg
    • 익스플로러: Trident
    • 크롬: chrome

user-agent 헤더 정보를 이용해 브라우저 종류 추출하기

클라이언트가 전송한 요청에서 시스템에 대한 정보를 추출한 다음,
최종적으로 클라이언트에게 '당신의 브라우저는 "크롬"입니다.'형태의 메시지 전송 (enum 문법 활용)
크롬이라면 크롬이라는 한글데이터, 엣지면 엣지라고 한글

1단계: <% %> 안에서 if문 때리기

String agent = request.getHeader("user-agent");
String name = null;

if(agent.contains("Edg")){
    name = "엣지";
}else if(agent.contains("Chrome")){
    name = "크롬";
}else if(agent.contains("Trident")){
    name = "익스플로러";
}else {
    name = "기타";
}

2단계: enum 사용 불가 (자바 1.5미만), 여전히 <% %>내부

String agent = request.getHeader("user-agent");
String name = "기타";
	
Map<String, String> browsers = new LinkedHashMap<>(); 
/*
 * 일반 HashMap이 아닌 LinkedHashMap을 사용하는 이유
 * - 엣지에 크롬, 엣지 둘다 들어있기 때문에 엣지 먼저 비교하려고 -> 순서 중요
 * - LinkedHashMap은 순서가 유지될 수 있다 -> Linked계 구현체의 특징
 */
	
browsers.put("Edg","엣지");
browsers.put("Chrome","크롬");
browsers.put("Trident","익스플로러");
	
for(Entry<String, String> entry : browsers.entrySet()) {
  if(agent.contains(entry.getKey())){
    name = entry.getValue();
    break; // 조건 충족되니 break
  }
}

3단계: enum 사용 (자바 1.5이상)

  • enum도 하나의 클래스
  • jsp 내 코드는 지역코드여서 안에서 enum선언 불가능
    • <%! %> 선언부이용해 이너클래스 정의필요
    • 스크립틀릿은 지역코드화되기떄문에 전역멤버를 만들기위해 선언부를 사용하는거시다
  • enum 만들면 자동으로 class로 바뀌어서 enum의 static method들이 알아서 할당됨
<%! // 디렉티브: 선언부
enum Browser { // 자기 타입에 해당하는 객체를 미리 만들어놓는 구조. 외부에서 객체 생성 불가능.
  EDG("엣지"), CHROME("크롬"), TRIDENT("익스플로러"), OTHER("기타");

  private String browserName;

/*
 * 매개변수 있는 생성자 정의
 * - 이로써 기본생성자는 없어지게되므로 EDG, CHROME, TRIEDENT로 선언시 컴파일오류발생
 */
  Browser(String browserName){
    this.browserName = browserName;
  }

  public String getBrower(){
    return browserName;
  }
}
%>
<%
  String agent = request.getHeader("user-agent");
  String name = "기타";

  Browser[] browsers = Browser.values(); // values() : enum 메서드, 해당 객체배열 반환
  for(Browser temp : browsers) {
    if(agent.toUpperCase().contains(temp.name())){
      name = temp.getBrower();
    }
  }
%>

4단계

  • 클라이언트 사이드 코드에 끼치는 영향이 점점 줄어들고있음
<%!
enum Browser { // 자기 타입에 해당하는 객체를 미리 만들어놓는 구조. 외부에서 객체 생성 불가능.

  EDG("엣지"), CHROME("크롬"), TRIDENT("익스플로러"), OTHER("기타");

  private String browserName;
  
  Browser(String browserName){
    this.browserName = browserName;
  }
	
  public String getBrower(){
    return browserName;
  }
	
  public static String getBrowserName(String agent){
    Browser[] browsers = values();
    Browser finded = OTHER;
    for(Browser temp : browsers) {
      if(agent.toUpperCase().contains(temp.name())){
        finded = temp;
        break;
      }
    }
    return finded.getBrower();
  }
}
%>
<%
  String agent = request.getHeader("user-agent");
  String name = Browser.getBrowserName(agent);
%>

5단계

  • 사실 선언부는 잘 쓰이지 않음
    • 이유: 내부 클래스를 외부와 공유하겠다는건데 그럼 외부클래스(서블릿) 정보 필요함. 하지만 서블릿객체 관리는 톰캣의 영역
  • enum 클래스를 아예 따로 뺌 -> Browser.java
  • <%@ include,taglib%> : 필요할때만 씀 (필수 아님)
  • <%@ page %> : 필수. 페이지 자체의 환경설정값 변경. import와 같은 전처리과정, 변경시에도 필요

Browser.java

package kr.or.ddit.enumpkg;

public enum Browser { // 자기 타입에 해당하는 객체를 미리 만들어놓는 구조. 외부에서 객체 생성 불가능.
  EDG("엣지"), CHROME("크롬"), TRIDENT("익스플로러"), OTHER("기타");

  private String browserName;
  
  Browser(String browserName){
    this.browserName = browserName;
  }
	
  public String getBrower(){
    return browserName;
  }
	
public static String getBrowserName(String agent){
  Browser[] browsers = values();
  Browser finded = OTHER;
    for(Browser temp : browsers) {
      if(agent.toUpperCase().contains(temp.name())){
        finded = temp;
        break;
      }
    }
    return finded.getBrower();
  }
}

userAgent.jsp

<%@page import="kr.or.ddit.enumpkg.*" %>
<%
  String agent = request.getHeader("user-agent");
  String name = Browser.getBrowserName(agent);
%>
당신의 브라우저는 <%=name %>입니다.	

accept-language

  • 의미로는 1개인데 두가지방식(안녕하세요, hello)로 내보냄
  • 메세지번들: 언어팩
  • 영어,한국어 언어팩2개필요 그럼 resource bundle 2개필요

map vs. properties

  • map: 힙메모리, 컴퓨터 끄면 끝
  • properties: .properites 라는 파일로 실제데이터로 남겨둘수있음
    • 그래서 외부api활용시 .properties를 많이 쓴다
    • .properties 파일은 클래스패스 리소스로 만들어야한다
    • .properties라는 api에서 사용해야하니까 이렇게 만드러야해
  • 베이스이름은 message로 동일 뒤에는 로케일식별자일뿐
    • message_en.properties
    • message_ko.properties

1단계

<body>
<%
  String acceptLanguage = request.getHeader("accept-language");
  Locale locale = request.getLocale();
  ResourceBundle bundle = ResourceBundle.getBundle("kr.or.ddit.msg.message", locale);
  String message = bundle.getString("bow");
%>

<pre>
accept-lanugage : <%=acceptLanguage %>
<%=message %> // 로케일을 식별해주지 않으면 서버의 로케일을 따라가 안녕하세요가 출력됨
</pre>
</body>

2단계

  • 요청을 messageLocale.jsp 에서 비동기로 보냄
  $(".flag").on("click", function(event) {
  let language = $(this).prop("id");

  $.ajax({
    url: "<%=request.getContextPath()%>/02/getMessage.jsp"
    ,data: { lang : language } // "lang" 안해줘도도는건 js객체기때문에 js객체는 이걸 알아서 식별해줌
    ,method:"get" // 기본이니 생략가능
    ,dataType:"text" // 리퀘스트헤더의 mime을 결정 Accept : application/json  | response의 Content-Type과 한쌍
    ,success: function(resp) { // jqeury 컨텍스트가 콜백함수호출, dataType에따라 resp타입 달라짐
      $("#resultArea").text(resp);
    }
    ,error: function(xhr) { // 비동기요청시 기존사용했던 api객체(XMLHttpRequest) 이용하기때문에 에러보기위해 요청,응답정보 다시가져옴
      console.log(xhr.status);
    }
  });
});
  • 비동기 요청을 처리하는 jsp
<%@page import="java.util.ResourceBundle"%>
<%@page import="java.util.Locale"%>
<%@ page language="java" contentType="text/plain; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	String language = request.getParameter("lang");
	String acceptLanguage = request.getHeader("accept-language");
	Locale locale = request.getLocale();
	if(language!=null && !language.isEmpty()) {
		locale = Locale.forLanguageTag(language.toLowerCase());
	}
	ResourceBundle bundle = ResourceBundle.getBundle("kr.or.ddit.msg.message", locale);
	String message = bundle.getString("bow");
%>
<%=message %>
  • 내 로케일이 한국이어도 마지막으로 선택한 영어를 남기고싶으면 locale 정보를 쿠키에 저장해두면된다

accept

동기 vs. 비동기(락x)

  • 스레드에서 동기화
  • 동기
    • 자원에 이 걸린 상태에서 처리
    • 국기 클릭 -> 윈도우 전체에 락이걸림 -> 응답데이터 도착 -> 락 해제 -> 화면 바뀜
    • 브라우저 취소버튼 눌렀던이유 : 락푸르려고
    • a태그, 폼태그, location.href 동기요청발생시킴
  • 비동기
    • 윈도우 자체에 락을 걸지않고 일부분만 변경할때 비동기방식사용
    • XHR: 비동기 요청 발생시킬 때 사용되는 자바스크립트객체

오늘 정리

헤더: 클라이언트 요구 분석용

  • user-agent: 클라이언트시스템판단
  • accept-language: 클라이언트요구ㅠ하는 언어
  • accept: 클라이언트요구하는 데이터타입

enum

  • 생각보다 다양하게 활용가능
  • enum모르면 spring어려움

과제

  • 어떤형태가되도좋으니 사칙연산기를 만들어봐라
  • 제일먼저 ui필요 3개의 입력 (피연산자+연산자 enum)
  • 사칙연산기구현하며 enum구조 + model2구조 사용
  • 데이터입력받는건 jsp
  • 그후 서버로보냄
  • 그다음에 서블릿에서 요청처리
  • 내일은 그럼 람다식을 여기에 집어넣어볼거야

내일

  • json 가지고 비동기
profile
갈 길이 멀다

0개의 댓글