내가 보려고 정리하는/Spring

웹:0307

보동이용용 2023. 3. 7. 12:47
반응형

🚩오늘 수업 목표

  • 클라이언트의 의도적 전송 데이터 분류에 따른 사칙연산 해보기
  • 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));
	}
}

이넘에서는 기본 생성자 사용 불가 = private Operator(){} ==> 나만 사용가능

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 체킹

반응형