-
모델1 모델2 책임 분리, JSON, Header-22.06.07~22.06.08웹프로그래밍 2022. 6. 8. 21:24728x90
<복습>
1.책임의 분리구조
2.jsp, 서블릿 컨테이너 차이점 : 개발자와 역할분담 어떻게 하느냐에 따라
서블릿은 개발자가 해야한다면 jsp컨테이너 템플릿 ui만 서블릿 , 클래스만든는것은 jsp컨테이너가 알아서..
3.시큐어 보안 가이드 - > 검증
4. mime text : 피어와피어 사이에서 데이터가오갈때 어떤 형식 어떤것인지 정의한것
Model -> Model2 ->ajax -> XML/JSON(Marshalling)
오늘은 XML/JSON(Marshalling) 이 과정을 해보겠다!!!
request header 개념도 같이 보자.
XML/JSON 왜 쓸까?
https://www.json.org/json-ko.html



마셜링의미:
객체의 메모리 구조를 저장이나 전송을 위해서 적당한 자료형태로 변형하는 것을 의미한다.
Marshalling 은 보통 서로 다른 컴퓨터 혹은 서로 다른 프로그램 간에 데이터가 이동되어야 할 경우 사용된다.


json일때 

html일때 

xml일때 =>Accept : 나는 이러이러한 데이터만 받을 수 있어라고 셋팅하는것
응답데이터는 xml이어야 하는데 Response가 html임


상태코드가 200이지만
콘솔에는 에러가

언마셜링(다른 장소에서 잘왔나 컨테이너를 체크) 하려고했지만 타입이 맞지않아 에러 , json이지만 html이므로...
<FactorialServlet.java>
package kr.or.ddit.servlet04; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; 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("/04/factorial.do") public class FactorialServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String accept =req.getHeader("Accept"); (추가!) //클라이언트 보내는 모든 데이터는 검증이 필요함!!!! String param = req.getParameter("number"); if(param!=null && param.matches("\\d+")){ String pattern ="%d! =%d"; int number =Integer.parseInt(param); //재귀 호출 long result= factorial(number); String expression = String.format(pattern, number, result); Map<String, Object> resultMap = new HashMap<>(); resultMap.put("expression",expression); resultMap.put("result",result); (추가!) if(accept.contains("json")) { //응답 데이터를 json으로 표현 방식 변경. -Marshalling(native 언어로 표현된 데이터를 범용 표현 방식의 언어로 바꾸는 과정) //JSON(JavaScript Object Notation) JSON을 객체화해야함 //{"expresssion":123123} 즉 {"expresssion":"6!=720"} StringBuffer json = new StringBuffer(); json.append("{"); for(Entry<String, Object> entry : resultMap.entrySet()/*맵을 셋으로 바꿈*/) { String key = entry.getKey(); Object value = entry.getValue(); if("expression".equals(key)) { json.append("\"" + key.toString() + "\" : \"" + value.toString() + "\""); } } json.append("}"); PrintWriter out =resp.getWriter(); out.println(json); }else { // request(Map존재) scope사용 req.setAttribute("expression", expression); //서버사이드 방식임 그래서 /WebStudy01없어야함 String view ="/WEB-INF/views/FactorialView.jsp"; req.getRequestDispatcher(view).forward(req, resp); } }else if(param!=null && !param.matches("\\d+")){ //클라이언트에게 잘못됐다는것을 에러메세지 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "펙토리얼 연산은 양수와 숫자만으로 처리"); return; } } public long factorial(int number) { if(number<0) throw new IllegalArgumentException("음수에 대해서는 연산 불가"); if(number ==0){ return 1; }else{ return number*factorial(number-1); } } }
<factorial.jsp>
<%@page import="java.util.Objects"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>04/factorial.jsp</title> <script type="text/javascript" src="<%=request.getContextPath()%>/resources/js/jquery-3.6.0.min.js"></script> </head> <body> <h4></h4> <!-- 1.Model -> Model2 ->ajax -> XML/JSON(Marshalling) --> <!-- 2! ->2*1 =2, 3! ->3*2*1 =6 --> 연산을 자바 스크립틀릿 기호만으로 반복 곱하기 연산 수행 <br> <!-- <form action="명령어_요청URL" method="파라미터 전송 방법 & 요청의 목적" enctype="요청 데이터의 표현 방식"></form> --> <form action="<%=request.getContextPath()%>/04/factorial.do" name="facForm"> <input type="number" name="number" min="0" value="" /> <input type="text" name="dummy" value="asas"/> <input type="submit" value="="/> </form> <div id ="resultArea"> </div> <script> //발생한 이벤트는 파라미터로 들어오며, 이벤트에는 그 이벤트를 발생시킨 타켓이 들어가 있다. let resultArea =$("#resultArea"); //변수로 받아놓는게 좋음 let facForm = $("form[name]").on("submit",function(event){ event.preventDefault(); //form 의 submit 이벤트의 기본 특성은 동기 요청. console.log(event.target); console.log(this); // $(this) -> httpelement 에서 JQuery객체화 // XMLHttpRequest 객체를 활용한 비동기 요청 let action =this.action; //$(this).attr("action"); let method = this.method; let data =$(this).serialize(); //Query String 생성 console.log(data); //ex)parm1=value¶m2=value2 $.ajax({ url: action, method:method, data:data, dataType:"json" //text, html, json, xml, script->main type :text, 파일업로드 처리를 비동기로? (FormData) ,success:function(resp,status,jqXHR){ //resp : html소스 옴, json으로 바꿨으므로 {"expression":"6!=720"} //$("#resultArea").html(resp); $("#resultArea").html(resp.expression); }, error :function(jqXHR, status, error){ console.log(jqXHR) console.log(status) console.log(error) } }); return false; }); console.log(facForm); </script> </body> </html>
serialize : 데이터를 전송 혹은 저장하기 위해 바이트 배열의 형태로 변환하는 작업. 통신이나 저장하기 위해서 데이터를 일렬로 세움. ex) param1 = value1¶m2 = value2 jquery ajax로 호출하기 전에 serialize를 해주면 form 안에 값들을 한 번에 전송 가능한 data로 만들 수 있어 많은 data를 보낼 때 유용하다.]<JSON 형태- 다른시스템으로 데이터를 만들어 보내고 싶을때 적은양의 데이터를 보낼 수 있어서 사용하게됨>
JSON 형태 1) " " :true나 숫자는 안붙어도 됨 let 객체변수 ={ "변수명1" : 값1, "변수명2 ": 값2, ... "변수명n" : 값 n }; 2) let 객체변수 =[값1, 값2,...값n]; ---------------------------------------------------------------------- ex) let man ={ // man이라는객체의 멤버 변수가 2개 있다.. "name":"홍길동", "age" :30 }; man.name ==>홍길동 man.age ==>30 man["name"] ==>홍길동 man["age"] ==>30 //배열 let names =["홍길동","이순신","강감찬","이몽룡"] names[0] ==> "홍길동" names[3] ==> "이몽룡" resp={"expression" : "6!=720"}; resp.expression하고 꺼내면 됨!!
--0608--
선생님 버전

=> 이부분이 바로 마셜링 작업!!! , 직렬화시켜 보냄
받는쪽(클라이언트)에서는 역직렬화를 하고 언마셜링함 복원된 객체가 이제 resp가 들어가있음

메소드가 get일때 Accept 헤더 : 반드시 답장을 json으로 해

메소드가 post일때 
=> 이 데이터가 아니라 view source누르면 이게 진짜 서버로 넘어가는 데이터

=>서버로 넘어가고 있는 데이터





=> contentType이 빠짐



jsp응답 나가는 구조는 모델1이다.
책임을 분리해보겠다.
중복을 없애고 책임을 분리해보자

데이터타입이json일때 -> jsonView.do로 이동하고 -> 이동해서 처리한것을 다시 요청한곳으로 보내는 순서로 진행
package kr.or.ddit.servlet04; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; 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("/04/factorial.do") public class FactorialServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String accept = req.getHeader("Accept"); // 클라이언트 보내는 모든 데이터는 검증이 필요함. String param = req.getParameter("number"); if(param!=null && param.matches("\\d+")){ String pattern = "%d! = %d"; int number = Integer.parseInt(param); // 재귀 호출: recursive call long result = factorial(number); String expression = String.format(pattern, number, result); // 6! = 720 req.setAttribute("expression", expression); req.setAttribute("result", result); req.setAttribute("test", "한글데이터"); String view = null; if(accept.contains("json")) { view = "/jsonView.do"; }else { view = "/WEB-INF/views/FactorialView.jsp"; } req.getRequestDispatcher(view).forward(req, resp); }else if(param!=null && !param.matches("\\d+")){ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "팩토리얼 연산은 양수와 숫자만으로 처리"); return; } } public long factorial(int number){ if(number<0) throw new IllegalArgumentException("음수에 대해서는 연산 불가"); if(number==0){ return 1; }else{ return number * factorial(number - 1); } } }<kr/or/ddit/commons/JsonMarshallingViewServlet.java>
package kr.or.ddit.commons; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; 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("/jsonView.do") public class JsonMarshallingViewServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("application/json;charset=UTF-8"); // 응답데이터를 json 으로 표현 방식 변경. - Marshalling(native 언어로 표현된 데이터를 범용 표현 방식의 언어로 바꾸는 과정) // JSON(JavaScript Object Notation) // {"expression":123123} {"expression": "6! = 720" , } StringBuffer json = new StringBuffer(); String jsonPtrn = "\"%s\":\"%s\" , "; json.append("{"); Enumeration<String> attrNames = req.getAttributeNames(); //여러개의 속성이름들가져옴 while (attrNames.hasMoreElements()) { attrNames="expression" ,"result" 이렇게 이름만!! String key = (String) attrNames.nextElement(); Object value = req.getAttribute(key); //여기서 꺼낼 수 있는 이유는 set으로 담아왔기 때문 json.append(String.format(jsonPtrn, key, value)); } int lastIndex = json.lastIndexOf(","); if(lastIndex >= 0 ) { //0보다 크면 ,가 있다. 해당안되면-1나옴 json.deleteCharAt(lastIndex); } json.append("}"); PrintWriter out = resp.getWriter(); out.println(json); // Serialize : 데이터를 전송 혹은 저장하기 위해 바이트 배열의 형태로 변환하는 작업. } }Enumeration<String> attrNames = req.getAttributeNames();

getAttributeNames() 여기에 키값만 들어옴!!
<04.factorial.jsp>
<%@page import="java.util.Objects"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>04/factorial.jsp</title> <script type="text/javascript" src="<%=request.getContextPath()%>/resources/js/jquery-3.6.0.min.js"></script> </head> <body> <h4></h4> <!-- 1.Model -> Model2 ->ajax -> XML/JSON(Marshalling) --> <!-- 2! ->2*1 =2, 3! ->3*2*1 =6 --> 연산을 자바 스크립틀릿 기호만으로 반복 곱하기 연산 수행 <br> <!-- <form action="명령어_요청URL" method="파라미터 전송 방법 & 요청의 목적" enctype="요청 데이터의 표현 방식"></form> --> <form action="<%=request.getContextPath()%>/04/factorial.do" name="facForm" method="get"> <input type="number" name="number" min="0" value="" /> <input type="text" name="dummy" value="asas"/> <input type="submit" value="="/> </form> <div id ="resultArea"> </div> <script> //발생한 이벤트는 파라미터로 들어오며, 이벤트에는 그 이벤트를 발생시킨 타켓이 들어가 있다. let resultArea =$("#resultArea"); //변수로 받아놓는게 좋음 let facForm = $("form[name]").on("submit",function(event){ event.preventDefault(); //form 의 submit 이벤트의 기본 특성은 동기 요청. console.log(event.target); console.log(this); // $(this) -> httpelement 에서 JQuery객체화 // XMLHttpRequest 객체를 활용한 비동기 요청 let action =this.action; //$(this).attr("action"); let method = this.method; let data =$(this).serialize(); //Query String 생성 console.log(data); //ex)parm1=value¶m2=value2 $.ajax({ url: action, method:method, data:data, dataType:"json" //text, html, json, xml, script->main type :text, 파일업로드 처리를 비동기로? (FormData) ,success:function(resp,status,jqXHR){ //resp : html소스 옴 //$("#resultArea").html(resp); //resultArea.html(JSON.stringfy(resp)); $("#resultArea").html(resp); //alert(resp.test); }, error :function(jqXHR, status, error){ console.log(jqXHR) console.log(status) console.log(error) } }); return false; }); console.log(facForm); </script> </body> </html>
get방식일때


굳이 바디를 만들지 않아도 됨
즉 바디는 메소드가 post일때만 바디가 만들어지고 get방식일때는 body가 안만들어진다.
<form method="post"> <input type="hidden" name="_method" value="PUT"> </form> 바디가 있는put이 된다. 즉 원래 메소드로 정하게 됨 <form method="get"> <input type="hidden" name="_method" value="PUT"> </form> 바디가 없는 put이 됨

메소드를 생략한 모든 메소드는 GET방식 !!!! 바디는 없고 Line과 Header만 존재
수신자에게(URL) 어떤 요청(Method)을 보낼까를 표현하고 있음
a href , img src ,iframe, script, link href : GET방식, form을 제외한 나머지들 get방식이며 form은 메소드에 따라get인지post인지 바뀜...
Accept : 요청을 보냈을때 응답데이터를 어떤형태로 받겠다.(내가 받아야하는 데이터)
응답데이터의 컨텐트 타입으로서, 맨 우선순위 타입(text/html)으로 받겠다..


=>압축 형식 중 이런것들만 받을 수 있다는것

=>로케일 코드, Locale : language /country 언어는 한국어 지역은 한국 en-Us 언어는 영어 지역은 미국
내가 받아야하는 데이터 언어 가능하면 한국어로 해주고 , 우선순위적용으로 최우선은 한국 그다음은 ko(북한), 그래도 없으면 영어라도 줘라

기본객체들을 뜻함
(컬렉션의 개념은 꼭 알아두기)
<requestHeader.jsp>
<%@page import="java.util.Enumeration"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>05/requestHeader.jsp</title> </head> <body> <h4>요청 헤더의 종류</h4> <table> <thead> <tr> <th>헤더이름</th> <th>헤더값</th> </tr> </thead> <tobody> <% String pattern ="<tr><td>%s</td><td>%s</td></tr>"; Enumeration<String> headerNames=request.getHeaderNames(); while(headerNames.hasMoreElements()){ String name =headerNames.nextElement(); String value = request.getHeader(name); out.println(String.format(pattern, name, value)); } %> </tobody> </table> <pre> 요청헤더 : 클라이언트와 요청에 대한 부가 정보를 key/value 형태로 표현한 데이터 </pre> </body> </html>
1.Request Line : Protocol/version URL Method
Request Method (Http Method) : 요청의 목적, 요청의 포장 방식(body 생성 여부 결정)
1)GET (R) -서버의 데이터 조회
2)POST (C) -서버쪽에 새로운 데이터 생성
3)PUT (U) -수정(바디 필요할수도 없을 수도)
4)DELETE (D) -삭제(바디 필요할수도 없을 수도)
5)OPTION : 서버가 현재 method를 지원하는지 여부를 확인하는 preFlight 요청에 사용되는 메소드.
6)HEAD : 응답데이터를 body가 없는 메타 데이터만 받기 위한 요청에 사용.
7)TRACE : 서버를 트래킹해서 디버깅하는 경우 사용.
2.Request Header : client에 대한 부가 정보(meta data)
3.Request Body(Message Body, Content Body) : 클라이언트가 서버로 전송하는 컨텐츠(내용)
method 가 Post인 경우에만, 한정적으로 생성되는 영역.
**method가 GET인 경우, 모든 데이터는 Request line을 통해 Query String(**)의 형태로 전송됨
<script> alert("<%=message%>"); </script>
이것때문에 에러
자바와 자바스크립트 데이터 공유 불가
<script> alert('<%=message%>'); </script>
<userAgent.jsp>
-os가 추가되면 if문을 계속 늘려야하는 단점존재
<%@page import="java.util.Enumeration"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>05/userAgent.jsp</title> </head> <body> <pre> 클라이언트의 시스템을 파악하고, 해당 OS에 대한 정보를 alert 창으로 렌더링. (당신의 OS는 "%s"입니다.) </pre> <% String pattern ="당신의 OS는 \"%s\"입니다."; String agent = request.getHeader("User-Agent"); agent = agent.toUpperCase(); String osName =null; if(agent.contains("WINDOWS")){ osName ="윈도우"; }else if(agent.contains("ANDROID")){ osName ="안드로이드"; }else if(agent.contains("IPHONE")){ osName ="아이폰"; }else{ osName ="식별불가OS"; } String message=String.format(pattern,osName); %> <script> alert('<%=message%>'); </script> </body> </html>
<%@page import="java.util.Map"%> <%@page import="java.util.HashMap"%> <%@page import="java.util.Enumeration"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>05/userAgent.jsp</title> </head> <body> <pre> 클라이언트의 시스템을 파악하고, 해당 OS에 대한 정보를 alert 창으로 렌더링. (당신의 OS는 "%s"입니다.) </pre> <% String pattern ="당신의 OS는 \"%s\"입니다."; String agent = request.getHeader("User-Agent"); agent = agent.toUpperCase(); String osName =null; Map<String, String> osMap = new HashMap<>(); osMap.put("WINDOWS","윈도우"); osMap.put("ANDROID","안드로이드"); osMap.put("IPHONE","아이폰"); osMap.put("UBUNTU","우분투"); osMap.put("UNKNOWN","식별불가OS"); osName =osMap.get("UNKNOWN"); for(String key : osMap.keySet()){ osName =osMap.get(key); if(osName!=null) break; } String message=String.format(pattern,osName); %> <script> alert('<%=message%>'); </script> </body> </html>enum 적용

=>enum도 또다른 클래스임
일반 클래스는 변수,메소드 ,상수 다 넣을 수 있어
enum클래스는 상수의 집합이 들어간다.

s: static f: final
이 상수들의 타입은 OsKind 타입의 상수가 5개 선언
자기 타입의 상수를 가지고 있는것을 enum


alert +shift+ s+밑에서 3번째
<OsKind.java>
package kr.or.ddit.enumpkg; public enum OsKind { WINDOWS("윈도우"), ANDROID("안드로이드"), IPHONE("아이폰"), UBUNTU("우분투"), UNKNOWN("식별불가 OS"); private String osName; -alert +shift+ s+밑에서 3번째 private OsKind(String osName) { this.osName = osName; } public String getOsName() { return osName; } //변경할 수 없으므로 set은 못함 }=>데이터를 완전히 분리시킴
<userAgent.jsp>
<%@page import="kr.or.ddit.enumpkg.OsKind"%> <%@page import="java.util.Map"%> <%@page import="java.util.HashMap"%> <%@page import="java.util.Enumeration"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>05/userAgent.jsp</title> </head> <body> <pre> 클라이언트의 시스템을 파악하고, 해당 OS에 대한 정보를 alert 창으로 렌더링. (당신의 OS는 "%s"입니다.) </pre> <% String pattern ="당신의 OS는 \"%s\"입니다."; String agent = request.getHeader("User-Agent"); agent = agent.toUpperCase(); String osName =null; // Map<String, String> osMap = new HashMap<>(); // osMap.put("WINDOWS","윈도우"); // osMap.put("ANDROID","안드로이드"); // osMap.put("IPHONE","아이폰"); // osMap.put("UBUNTU","우분투"); // osMap.put("UNKNOWN","식별불가OS"); // osName =osMap.get("UNKNOWN"); osName =OsKind.UNKNOWN.getOsName(); for(OsKind os : OsKind.values()){ osName =os.getOsName(); if(osName!=null) break; } String message=String.format(pattern,osName); %> <%=OsKind.IPHONE %> <script> alert('<%=message%>'); </script> </body> </html>
OS를 찾아내는 책임이 이넘으로 이동하고 OS에 대한 책임을 jsp가 아니라 다이넘으로 넘기기!!!
<OsKind.java>
package kr.or.ddit.enumpkg; public enum OsKind { WINDOWS("윈도우"), ANDROID("안드로이드"), IPHONE("아이폰"), UBUNTU("우분투"), UNKNOWN("식별불가 OS"); private String osName; private OsKind(String osName) { this.osName = osName; } public String getOsName() { return osName; } //변경할 수 없으므로 set은 못함 public static OsKind findOs(String agent) { agent = agent.toUpperCase(); OsKind findedOs=OsKind.UNKNOWN; // String osName =null; // osName =OsKind.UNKNOWN.getOsName(); for(OsKind os : OsKind.values()){ if(agent.contains(os.name())){ findedOs=os; break; } } return findedOs; } public static String findOsName(String agent) { OsKind findedOs = findOs(agent); return findedOs.getOsName(); } }<userAgent.jsp>
<%@page import="kr.or.ddit.enumpkg.OsKind"%> <%@page import="java.util.Map"%> <%@page import="java.util.HashMap"%> <%@page import="java.util.Enumeration"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>05/userAgent.jsp</title> </head> <body> <pre> 클라이언트의 시스템을 파악하고, 해당 OS에 대한 정보를 alert 창으로 렌더링. (당신의 OS는 "%s"입니다.) </pre> <% String pattern ="당신의 OS는 \"%s\"입니다."; String agent = request.getHeader("User-Agent"); String osName =OsKind.findOsName(agent); String message=String.format(pattern,osName); %> <%=OsKind.IPHONE %> <script> alert('<%=message%>'); </script> </body> </html>
=> jsp는 건들일 필요 없고 enum만 수정하면 됨
여기서 배울점 책임을 어떻게 분리하느냐에 따라 달라진다는것이다. 더나은과정을 찾는것이다.
728x90'웹프로그래밍' 카테고리의 다른 글
Maven 설정환경-22.06.14 (0) 2022.06.14 인코딩, 학생 등록하기 예제 -22.06.10 (0) 2022.06.10 request, header - 22.06.09 (0) 2022.06.10 JQuery 보강-22.06.07 (0) 2022.06.07 모델1, 모델2 방식의 JSP -22.06.03 (0) 2022.06.03