🚩오늘 수업 목표
- 클라이언트의 의도적 전송 데이터 분류에 따른 사칙연산 해보기
- enum 을 활용한 user-agent 식별
- 모델링의 중요성
<1. 일단, 파라미터로 보내보기>
사용 파일 : case1Form.jsp , Case1Servlet.java
//case1Form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="<%=request.getContextPath() %>/resources/js/jquery-3.6.3.min.js"></script>
</head>
<body>
<form id="calForm" action="<%=request.getContextPath() %>/calculator/case1">
<input type="number" name="leftOp" />
<select name="operator">
<option value="PLUS">+</option>
<option value="MINUS">-</option>
<option value="MULTIPLY">*</option>
<option value="DIVIDE">/</option>
<option value="MODULAR">%</option>
</select>
<input type="number" name="rightOp" />
<input type="submit" value="=" />
</form>
<div id="resultArea"></div>
<script type="text/javascript">
let resultArea = $("#resultArea")
$("#calForm").on("submit", function(event){
event.preventDefault();
let url = this.action;
let method = this.method;
let data = $(this).serialize(); // url encoded string
$.ajax({
url : url,
method : method,
data : data,
dataType : "html"
}).done(function(resp, textStatus, jqXHR) {
resultArea.html(resp);
}).fail(function(jqXHR, status, error) {
console.log(`상태코드 : \${status}, 에러메세지 : \${error}`);
});
return false;
});
</script>
</body>
</html>
//Case1Servlet.java
package kr.or.ddit.calculator;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
@WebServlet("/calculator/case1")
public class Case1Servlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 인코딩확인(server.xml의 설정으로 인해 get메서드에서도 setCaracterEncoding사용가능)
req.setCharacterEncoding("UTF-8");
// 2. 파람 받아오고,
String leftParam = req.getParameter("leftOp");
String rightParam = req.getParameter("rightOp");
String operatorParam = req.getParameter("operator");
// 3. 검증
int status = 200;
if(leftParam==null || rightParam==null || operatorParam==null){
status = HttpServletResponse.SC_BAD_REQUEST;
}else if(!leftParam.matches("\\d+") || !rightParam.matches("\\d+")
|| !operatorParam.matches("PLUS|MINUS|MULTIPLY|DIVIDE|MODULAR")) {
status = 400;
}
if(status==200) {
int leftOp = Integer.parseInt(leftParam);
int rightOp = Integer.parseInt(rightParam);
int result = -1;
String expression = null;
String expPtrn = "%d %c %d = %d";
switch (operatorParam) {
case "PLUS":
result = leftOp + rightOp;
expression = String.format(expPtrn, leftOp, '+', rightOp, result);
break;
case "MINUS":
result = leftOp - rightOp;
expression = String.format(expPtrn, leftOp, '-', rightOp, result);
break;
case "MULTIPLY":
result = leftOp * rightOp;
expression = String.format(expPtrn, leftOp, '*', rightOp, result);
break;
case "DIVIDE":
result = leftOp / rightOp;
expression = String.format(expPtrn, leftOp, '/', rightOp, result);
break;
case "MODULAR":
result = leftOp % rightOp;
expression = String.format(expPtrn, leftOp, '%', rightOp, result);
break;
default:
break;
}
try(
PrintWriter out = resp.getWriter();
){
out.print(expression);
}
}else {
resp.sendError(status);
}
}
}
< 코드 템플릿 만들기 - 커서 초기설정 , PLACEHOLDER >
CASE1문제점.
고객이 MODULAR를 요청하자 5군데를 수정해야했음.
CASE2를 만들어야겠다.
< 2. enum 활용하여 모델링 해보자 >
: 연산에 대한 책임을 오퍼레이터에 모두 주기
사용 파일 : case2Form.jsp , Case2Servlet.java , Operator 이넘 , BinaryOperator 인터페이스
// Case2Servlet.java
package kr.or.ddit.calculator;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
@WebServlet("/calculator/case2")
public class Case2Servlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 인코딩확인(server.xml의 설정으로 인해 get메서드에서도 setCaracterEncoding사용가능)
req.setCharacterEncoding("UTF-8");
// 2. 파람 받아오고,
String leftParam = req.getParameter("leftOp");
String rightParam = req.getParameter("rightOp");
String operatorParam = req.getParameter("operator");
Operator operator = null;
// 3. 검증
int status = 200;
if(leftParam==null || rightParam==null || operatorParam==null){
status = HttpServletResponse.SC_BAD_REQUEST;
}else if(!leftParam.matches("\\d+") || !rightParam.matches("\\d+")) {
status = 400;
}else {
try {
operator = Operator.valueOf(operatorParam);
}catch(Exception e) {
status = 400;
}
}
if(status==200) {
int leftOp = Integer.parseInt(leftParam);
int rightOp = Integer.parseInt(rightParam);
int result = operator.operate(leftOp, rightOp);
String expression = operator.getExpression(leftOp, rightOp);
resp.setContentType("text/html;charset=UTF-8");
try(
PrintWriter out = resp.getWriter();
){
out.print(expression);
}
}else {
resp.sendError(status);
}
}
}
//Operator.java : enum
package kr.or.ddit.calculator;
public enum Operator { // enum도 클래스이다.(확장자명: .class확인할 수 있다.)
PLUS('+', (l,r)->l+r), //{return l+r;} 간단히
MINUS('-', (l,r)->l-r),
MULTIPLY('*',(l,r)->l*r),
DIVIDE('/', new BinaryOperator() {
@Override
public int realOperate(int leftOp, int rightOp) {
return leftOp / rightOp;
}
}),//1.8 이전버전
MODULAR('%', (l,r)->l%r);
private Operator(char sign, BinaryOperator realOperator){
this.sign = sign;
this.realOperator = realOperator;
//람다를 통해 인터페이스 구현체를 생성하고 있었다.
}
private char sign;
private BinaryOperator realOperator;
public char getSign() {
return sign;
}
public int operate(int leftOp, int rightOp) {
return realOperator.realOperate(leftOp, rightOp);
}
private static final String expPtrn = "%d %c %d = %d";
public String getExpression(int leftOp, int rightOp) {
return String.format(expPtrn, leftOp, getSign(), rightOp, operate(leftOp, rightOp));
}
}
enum 상수의 상태가 외부로 인해 바뀌어서는 안된다. ==> setter가 있으면 안된다.
상수가 만들어질 때 상태가 결정되어야한다. ==>생성자에서 설정
enum 상수의 특징 : 자기랑 똑같은 name의 속성이 있다. 그래서 .value of 사용 가능
자바에서는 메소드를 동적으로 만들수 없다. ==> 1.8에서 함수지향을 추가함. 람다식 !
람다를 통해 인터페이스 구현체를 생성하고 있었다.
모델링의 시작점은 책임을 분리한다.
dataType 선택, 직렬화, 마샬
사용 파일 : case3Form.jsp , Case3Servlet.java , Operator 이넘 , CalculateVO
data 다루기
사용 파일 : case4Form.jsp , Case4Servlet.java , Operator 이넘 , CalculateVO
1.jsp에서 json으로 data 바꾸기
2. servlet에서 역직렬화 언마셜링해서 vo객체 만들기
<%@page import="kr.or.ddit.calculator.Operator"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="<%=request.getContextPath() %>/resources/js/jquery-3.6.3.min.js"></script>
</head>
<body>
<div>
<h4>응답 데이터 MIME</h4>
<input type="radio" name="dataType" checked value="json"/>JSON
<input type="radio" name="dataType" value="xml"/>XML
<input type="radio" name="dataType" value="html"/>HTML
</div>
<form id="calForm" action="<%=request.getContextPath() %>/calculator/case4" method="post">
<input type="number" name="leftOp" />
<select name="operator">
<%
String optPtrn = "<option value='%s'>%c</option>";
for( Operator single : Operator.values()){
out.println(
String.format(optPtrn, single.name(), single.getSign())
);
}
%>
</select>
<input type="number" name="rightOp" />
<input type="submit" value="=" />
</form>
<div id="resultArea"></div>
<script type="text/javascript">
let resultArea = $("#resultArea")
let dataTypeTag = $('[name="dataType"]');
$("#calForm").on("submit", function(event){
event.preventDefault();
let url = this.action;
let method = this.method;
let data = {};
$(this).find(":input[name]").each(function(index, input){
// data.param1="default"; , data["param1"]="default";
let propertyName = this.name;
let propertyValue = $(this).val();
data[propertyName] = propertyValue;
});
data = JSON.stringify(data); // marshalling
console.log(data);
let dataType = dataTypeTag.filter(":checked").val();
$.ajax({
url : url,
method : method,
data : data,
contentType : "application/json;charset=UTF-8",
dataType : dataType
}).done(function(resp, textStatus, jqXHR) {
console.log(jqXHR);
if(jqXHR.responseJSON){
resultArea.text(resp['expression']);
}else if(jqXHR.responseXML){
resultArea.text($(resp).find("expression").text());
}else{
resultArea.html(resp);
}
}).fail(function(jqXHR, status, error) {
console.log(`상태코드 : \${status}, 에러메세지 : \${error}`);
});
return false;
});
</script>
</body>
</html>
package kr.or.ddit.calculator;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
@WebServlet("/calculator/case4")
public class Case4Servlet extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 인코딩확인(server.xml의 설정으로 인해 get메서드에서도 setCaracterEncoding사용가능)
req.setCharacterEncoding("UTF-8");
// 2. 비의도적 데이터 받아오기
String accept = req.getHeader("accept");
int status = 200;
CalculateVO vo = null;
try {
vo = getCalculateVOFromBodyContent(req);
}catch(IllegalArgumentException e) {
status=400;
}
if(status==200) {
if(accept.contains("json")) {
marshallingToJson(vo, resp);
}else if(accept.contains("xml")) {
marshallingToXml(vo, resp);
}else {
sendHtmlContents(vo, resp);
}
}else {
resp.sendError(status);
}
}
private void sendHtmlContents(CalculateVO target, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=UTF-8");
try(
PrintWriter out = resp.getWriter();
){
out.print(target.getExpression());
}
}
private void marshallingToJson(CalculateVO target, HttpServletResponse resp) throws IOException {
resp.setContentType("application/json;charset=UTF-8");
try(
PrintWriter out = resp.getWriter();
){
new ObjectMapper().writeValue(out, target);
}
}
private void marshallingToXml(CalculateVO target, HttpServletResponse resp) throws IOException {
resp.setContentType("application/xml;charset=UTF-8");
try(
PrintWriter out = resp.getWriter();
){
new XmlMapper().writeValue(out, target);
}
}
private CalculateVO getCalculateVOFromBodyContent(HttpServletRequest req) throws IOException{
// deSerialization, unMarshalling
try(
InputStream is = req.getInputStream();
){
return new ObjectMapper().readValue(is, CalculateVO.class);
}catch(Exception e) {
throw new IllegalArgumentException(e);
}
}
private CalculateVO getCalculateVOFromParameter(HttpServletRequest req) {
// 2. 파람 받아오고,(의도적 데이터)
String leftParam = req.getParameter("leftOp");
String rightParam = req.getParameter("rightOp");
String operatorParam = req.getParameter("operator");
// 3. 검증
Operator operator = null;
int status = 200;
if(leftParam==null || rightParam==null || operatorParam==null){
status = HttpServletResponse.SC_BAD_REQUEST;
}else if(!leftParam.matches("\\d+") || !rightParam.matches("\\d+")) {
status = 400;
}else {
try {
operator = Operator.valueOf(operatorParam);
}catch(Exception e) {
status = 400;
}
}
if(status==200) {
int leftOp = Integer.parseInt(leftParam);
int rightOp = Integer.parseInt(rightParam);
CalculateVO vo = new CalculateVO();
vo.setLeftOp(leftOp);
vo.setRightOp(rightOp);
vo.setOperator(operator);
return vo;
}else {
throw new IllegalArgumentException("요청 파라미터가 잘못됐음."); // 예외를 던지는 throws, 예외를 만드는 throw, 자바에서 예외는 객체
}
}
}
사람이라는 클라이언트가 요청을 보낼 때는 form tag의 파라미터로 보내는 정도밖에 없다.
그럼에도 post로 보내는 content payload를 사용하는 이유는 서버로부터 요청을 받는 경우가 있기 때문이다.
사용 파일 : case5Form.jsp , Case5Servlet.java , Operator 이넘 , CalculateVO
요청 컨텐츠의 마임 클라이언트 선택
<div>
<h4>요청 컨텐츠의 MIME</h4>
<input type="radio" name="contentType" checked value="application/json"/>JSON
<input type="radio" name="contentType" value="application/x-www-form-urlencoded"/>Parameter
</div>
String requestContentType = Optional.ofNullable(req.getContentType())
.orElse("");
1.8 null 체킹
'내가 보려고 정리하는 > Spring' 카테고리의 다른 글
웹 : 300번 응답코드 : 0309 (0) | 2023.03.09 |
---|---|
웹 : 응답상태코드 : 0308 (0) | 2023.03.08 |
웹 : R.Body, 마샬링, 직렬화 : 0306 (0) | 2023.03.06 |
웹 : 비동기(2), R.head : 0303 (1) | 2023.03.03 |
웹 : 비동기, R.Line : 0302 (0) | 2023.03.02 |