모델1 모델2 책임 분리, JSON, Header-22.06.07~22.06.08
<복습>
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 은 보통 서로 다른 컴퓨터 혹은 서로 다른 프로그램 간에 데이터가 이동되어야 할 경우 사용된다.
=>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가 들어가있음
Accept 헤더 : 반드시 답장을 json으로 해
=> 이 데이터가 아니라 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만 수정하면 됨
여기서 배울점 책임을 어떻게 분리하느냐에 따라 달라진다는것이다. 더나은과정을 찾는것이다.