https://www.highcharts.com/demo/line-basic

 

Basic line | Highcharts.com

Default Brand Light Brand Dark Dark Unica Sand Signika Grid Light Basic line chart showing trends in a dataset. This chart includes the series-label module, which adds a label to each line for enhanced readability.

www.highcharts.com

<script src="http://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-3d.js"></script>

실습을 위해서는 위 코드를 통해 라이브러리를 가져와야한다.

EDIT IN 버튼(둘 다)을 통해 들어가면 가져올 수 있다.

 

view options 을 통해 간단하게 모달창을 통해 옵션을 볼 수 있다.

Highcharts.chart('container', {

    title: {
        text: 'Solar Employment Growth by Sector, 2010-2016'
    },

    subtitle: {
        text: 'Source: thesolarfoundation.com'
    },

    yAxis: {
        title: {
            text: 'Number of Employees'
        }
    },

    xAxis: {
        accessibility: {
            rangeDescription: 'Range: 2010 to 2017'
        }
    },

    legend: {
        layout: 'vertical',
        align: 'right',
        verticalAlign: 'middle'
    },

    plotOptions: {
        series: {
            label: {
                connectorAllowed: false
            },
            pointStart: 2010
        }
    },

    series: [{
        name: 'Installation',
        data: [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]
    }, {
        name: 'Manufacturing',
        data: [24916, 24064, 29742, 29851, 32490, 30282, 38121, 40434]
    }, {
        name: 'Sales & Distribution',
        data: [11744, 17722, 16005, 19771, 20185, 24377, 32147, 39387]
    }, {
        name: 'Project Development',
        data: [null, null, 7988, 12169, 15112, 22452, 34400, 34227]
    }, {
        name: 'Other',
        data: [12908, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
    }],

    responsive: {
        rules: [{
            condition: {
                maxWidth: 500
            },
            chartOptions: {
                legend: {
                    layout: 'horizontal',
                    align: 'center',
                    verticalAlign: 'bottom'
                }
            }
        }]
    }

});

실제로 사용된 소스는 아래와 같다.

<script type="text/javascript">
// https://www.highcharts.com/demo
$(function() {
	var url = "${pageContext.request.contextPath}/hchart/line1";
	$.getJSON(url, function(data) { // GET 방식이고 JSON 으로
		Highcharts.chart('lineContainer1', {

		    title: {
		        text: '서울 월별 평균 기온'
		    },

		    yAxis: {
		        title: {
		            text: '기온(C)'
		        }
		    },

		    xAxis: {
		        categories: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월']
		    },

		    series: data.series

		});
	});
});
</script>

 

제일 중요한 부분이 series 인데, 위에 홈페이지에서 세팅한 대로 값을 넘겨주었다.

 

오늘 배운 것 중에 기억해야할 쿼리!

WITH memberAge AS (
    SELECT m2.userId, TRUNC(MONTHS_BETWEEN(SYSDATE, birth)/12) age
    FROM member m
    JOIN member1 m1 ON m.memberIdx = m1.memberIdx
    JOIN member2 m2 ON m1.userId = m2.userId
    WHERE m.membership = 1

)
SELECT '10대' section, COUNT(*) count  FROM memberAge WHERE age>=10 AND age < 20
UNION ALL
SELECT '20대' section, COUNT(*) count  FROM memberAge WHERE age>=20 AND age < 30
UNION ALL
SELECT '30대' section, COUNT(*) count  FROM memberAge WHERE age>=30 AND age < 40
UNION ALL
SELECT '40대' section, COUNT(*) count  FROM memberAge WHERE age>=40 AND age < 50
UNION ALL
SELECT '50대' section, COUNT(*) count  FROM memberAge WHERE age>=50 AND age < 60
UNION ALL
SELECT '60대' section, COUNT(*) count  FROM memberAge WHERE age>=60 AND age < 70
UNION ALL
SELECT '기타' section, COUNT(*) count  FROM memberAge WHERE age <10 OR age>=70;

 

유저관리 차트에서 나이별로 차트를 보고 싶을 때 사용된다.

공공데이터를 쓰기 위해서는 공공데이터포털에서 인증키를 발급받아야한다.

https://www.data.go.kr/index.do

 

공공데이터 포털

국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase

www.data.go.kr

나는 총 2개를 공부용으로 신청했다.

더보기

옆에 활용신청을 눌러서 신청할 수 있다.

 

공공 API 등의 데이터를 XML, JSON 문서를 String 형태로 받기 위한 자바 클래스


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import org.json.JSONObject;
import org.json.XML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service("common.apiSerializer")
public class APISerializer {
	private final Logger logger = LoggerFactory.getLogger(getClass());

	// 공공 API 등의 데이터를 XML,JSON 문서를 String 형태로 받기
	public String receiveToString(String spec) throws Exception {
		String result = null;
		
		HttpURLConnection conn = null;
		
		try {
			conn = (HttpURLConnection) new URL(spec).openConnection();
			BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
			StringBuilder sb = new StringBuilder();
			String s;
			while ((s = br.readLine()) != null) {
				sb.append(s);
			}
			result = sb.toString();
		} catch (Exception e) {
			logger.error(e.toString());

			throw e;
		} finally {
			if (conn != null) {
				try {
					conn.disconnect();
				} catch (Exception e2) {
				}
			}
		}

		return result;
	}

	// 공공 API등의 XML 데이터를 String 형태의 JSON으로 변환하여 받기
	public String receiveXmlToJson(String spec) throws Exception {
		String result = null;

		try {
			String s = receiveToString(spec);
			/*
			 * <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId>
			 * <version>20210307</version> </dependency>
			 * 
			 */
			JSONObject job = XML.toJSONObject(s);
			result = job.toString();

		} catch (Exception e) {
			// logger.error(e.toString());
			throw e;
		}

		return result;
	}
}

 

HttpURLConnection 에 요청변수(Request Parameter)와 함께 서비스 URL을 넣으면 

출력결과를 얻을 수 있다. 사이트 밑에 샘플 코드도 제공되어 있으므로 참고하시면됩니다.

 

자바에서 받아온 후 AJAX를 통해 서버에 요청하면 jsp에 뿌려주는 방식으로 진행했다.

 

@RequestMapping(value = "covid", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
	public String covid(@RequestParam String date) throws Exception {
		String result = null;

		int numOfRows = 20;
		int pageNo = 1;
		
		
		String serviceKey = "개개인의서비스키";
		String spec = "http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19SidoInfStateJson";
		
		spec += "?serviceKey=" + serviceKey + "&numOfRows=" + numOfRows + "&pageNo=" + pageNo +
				"&startCreateDt=" + date + "&endCreateDt=" + date;
		
		result = apiSerializer.receiveXmlToJson(spec);
		
		return result;
	}

@RequestMapping에 produces를 통해 "application/json; charset=utf-8" 을 명시해야 깨지지 않음

사이트에서 안내된 파라미터와 샘플데이터를 보고 맞춰서 넣어주어야한다. (대/소문자도 틀리면안됨)

function ajaxFun(url, method, query, dataType, fn) {
	$.ajax({
		type:method,
		url:url,
		data:query,
		dataType:dataType,
		success:function(data){
			fn(data);
		},
		error:function(e) {
			console.log(e.responseText);
		}
	});
}

$(function(){
	$("#btnCovid").click(function(){
		var url="${pageContext.request.contextPath}/parse/covid";
		var date = "20211213";
		var query = "date=" + date; 
		
		var fn = function(data) {
			printCovid(data);
		};
		ajaxFun(url, "get", query, "json", fn);
	});
	
	function printCovid(data) {
		var out="<h3>코로나 발생 현황</h3><hr>";
		
		console.log(data);
		$.each(data.response.body.items.item, function(index, item) {
			if(index == 0 ) {
				out += "기준일시 : " + item.stdDay + "<br>";
			}
			
			out += item.gubun 
				+ " - 전일대비 확진자 증가수 : " + item.incDec
				+ ", 누적확진자 수 : " + item.defCnt
				+ "<br>";
				
		});
		
		$("#resultLayout").html(out);
	}
});

@ResponseBody

핸들러 메소드에서 @ResponseBody 애노테이션이 적용된 경우 반환 객체를 HTTP 응답으로 전송한다.

- 메소드에서 반환하는 자바 객체를 HTTP 응답 몸체로 변환한다.

- 자바 객체를 HTTP 요청의 body 내용으로 매핑하는 역할

HttpMessageConverter를 통해 HTTP 응답 스트림으로 변환

<mvc:annotation-driven/>를 통해 HttpMessageConverter 구현 클래스를 모두 등록할 수 있다.

주요 HttpMessageConverter 구현 클래스

-  StringHttpMessageConverter : 요청 몸체를 문자열로 변환하거나 문자열을 응답 몸체로 변환

(text/plain;charset=ISO-8859-1)

- Jaxb2RootElementHttpMessageConverter : XML 용청 몸체를 자바 객체로 변환하거나 자바 객체를 XML 응답 몸체로 변환(text/xml, application/xml)

- MappingJackson2HttpMessageConverter : JSON 요청 몸체를 자바 객체로 변환하거나 자바 객체를 JSON 응답 몸체로 변환(text/json, application/json)

- ByteArrayHttpMessageConverter : HTTP 메시지와 byte 배열 사이의 변환을 처리(application/octet-stream)

- 스프링 4.0부터는 MappingJackson2HttpMessageConverter를 이용ㅇ하여 자바 객체를 JSON으로 변환하거나 JSON을 자바 객체로 변환하며, 다음의 의존성을 추가해야 한다.

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.3</version>
</dependency>

- 스프링 3.x 에서는 MappingJacksonHttpMessageConverter를 이용하여 변환 한다.

MappingJacksonHttpMessageConverter는 jackson-mapper-asl, jackson-core-asl의 의존성을 추가해야 한다.

- 스프링 4.0에 추가된 @RestController 애노테이션으로 컨트롤러를 설정하면, @RestController에는 기본적으로 @ResponseBody 애노테이션이 적용되어 메소드 레벨에서 추가하지 않아도 된다.

 

사용 예

1) String을 JSON으로 변환하여 반환

- 문자열을 JSON 반환할 때, 한글을 반환하는 경우 반드시 produces 옵션을 통해 charset을 명시 해야 한다.

@RequestMapping(value = "/user", produces="application/json;charset=utf-8")
@ResponseBody
public String process(@RequestParam String id) {
	String name = service.getName(id);
    return "{\"name\":\"+name+"\"}";
}

 

2) Map<String, Object>을 JSON으로 변환하여 반환

@RequestMapping(value = "/user")
@ResponseBody
public Map<String, Object> process(@RequestParam int idx) {
	Map<String, Object> model = service.readMap(idx);
    return model;
}

@RequestBody

HTTP 요청 몸체를 자바 객체로 전달받음

HTTP 요청의 body 내용을 자바 객체로 매핑하는 역할

POST 형식으로 응답 받는 경우에만 사용할 수 있다.

사용 예 - JSON 형식으로 전송한 요청 파라미터를 전달 받아 JSON 형식으로 응답

- JSON 형식의 요청 파라미터를 member 객체에 전달받아 처리 후, 처리 결과를 가지고 있는 loginMember 객체를 JSON으로 변환하여 응답한다.

@RequestMapping(value = "/member/login", method=RequestMethod.POST)
@ResponseBody
public Member loginSubmit(@RequestBody Member member) throws Exception {
	Member loginMember = memberService.login(member);
    return loginMember;
}

@RestController

@RestController는 @Controller에 @ResponseBody가 추가된 애노테이션이다.

스프링 4.0부터 지원한다.

REST 방식의 데이터 처리를 위해 사용하는 애노테이션

- @RestController는 자바 객체를 JSON/XML 타입으로 반환하는 REST 서비스에 최적화된 컨트롤러

 

@Controller와 @RestController 차이점

- @Controller 

API와 뷰를 동시에 서비스하는 경우에 사용하며, API 서비스는 @ResponseBody를 붙여줘야 한다.

 

- @RestController

뷰가 필요 없는 API를 서비스하는 경우에 사용. @ResponseBody를 포함하고 있다.

 

- @RestController 에서 뷰를 반환해야 하는 경우에는 ModelAndView를 반환한다.

AJAX 를 왜 쓰느냐 ?

-> Guess : 전체 페이지를 서버로 보내는게 아니라 일부분만 업데이트 할 때 그 부분만 보낼 수 있어서 , 부분적으로 업데이트 하거나 그럴때 쓰임 (맞는지 확인 필수) 요새는 많이 쓰기 때문에 꼭 알아두어야한다!

 

AJAX를 위한 함수 설정

function ajaxFun(url, method, query, dataType, fn) {
	$.ajax({
		type:method,
		url:url,
		data:query,
		dataType:dataType,
		success:function(data) {
			fn(data);
		},
		beforeSend:function(jqXHR) {
		},
		error:function(jqXHR) {
			console.log(jqXHR.responseText);
		}
	});
}

type은 GET/POST 형식인지 

url은 서버 주소 어디로 가는건지

data는 말그대로 어떤 데이터를 보내는지

dataType JSON인지 HTML인지 

 

 

처음 page 로드할 때 list 불러오기

$(function () {
	listPage(1);
});

function listPage(page) {
	var url = "${pageContext.request.contextPath}/nscore/list";
	var query = "pageNo=" + page;
	var fn = function(data) {
		printJSON(data);
	};
	
	ajaxFun(url, "get", query, "json", fn);
}

데이터를 불러옴

 

AJAX - JSON으로 리스트 찍기

function printJSON(data) {
	$(".score-list").empty(); // 쌓지않고 페이징 처리를 할 것이므로
	
	var dataCount = data.dataCount;
	var total_page = data.total_page;
	var pageNo = data.pageNo;
	var paging = data.paging;
	
	var str;
	$(data.list).each(function(index, item) {
		var hak = item.hak;
		var name = item.name;
		var birth = item.birth;
		var kor = item.kor;
		var eng = item.eng;
		var mat = item.mat;
		var tot = item.tot;
		var ave = item.ave;
		
		str = "<tr align='center' height='33'></tr>";
		$(str).append("<td>"+hak+"</td>")
			.append("<td>"+name+"</td>")
			.append("<td>"+birth+"</td>")
			.append("<td>"+kor+"</td>")
			.append("<td>"+eng+"</td>")
			.append("<td>"+mat+"</td>")
			.append("<td>"+tot+"</td>")
			.append("<td>"+ave+"</td>")
			.append("<td><span class='btn-update'>수정</span> | <span class='btn-delete'>삭제</span></td>")
			.appendTo(".score-list");
	});
	
}

 

AJAX - JSON 자료 등록

hak=1111&name=유니코드&EE3343

$(function() {
	$("form[name=scoreForm]").submit(function() {
		var query = $(this).serialize();
		var url = "${pageContext.request.contextPath}/nscore/insert";
		
		var fn = function(data) {
			var state = data.state;
			if(state === "true") {
				$(".score-input input").each(function() {
					$(this).val("");
				});
				listPage(1);
				$("#hak").focus();
				
			} else if(state === "notUnique") {
				alert("등록된 학번입니다.");
				return false;
			} else if(state ==="false") {
				alert("추가가 실패했습니다.");
				return false;
			}
		};
		ajaxFun(url, "post", query, "json", fn);
		
		return false; // 서버로 전송하지 못하도록
	});
});

 

AJAX - JSON 자료 삭제

$(function() {
	$("body").on("click", ".btn-delete", function() {
		if(! confirm("자료를 삭제하시겠습니까 ? ") ) {
			return false;
		}
		
		// var hak = $(this).closest("tr").children().first().text();
		var hak = $(this).closest("tr").find("td:first").text();
		var url = "${pageContext.request.contextPath}/nscore/delete";
		var query = "hak="+hak;
		
		var fn = function(data) {
			var state = data.state;
			if(state === "true") {
				listPage(1);
			} else {
				alert("자료를 삭제하지 못했습니다.");
			}
		};
		ajaxFun(url, "post", query, "json", fn);
	});
});

 

AJAX - JSON 자료 수정

$(function() {
	var arr = [];
	
	$("body").on("click", ".btn-update", function() {
		var $tds = $(this).closest("tr").children("td");
		var names = ["hak", "name", "birth", "kor", "eng", "mat"];
		
		var s1, s2;
		$($tds).each(function(idx) {
			if( idx != $tds.length-1 ) {
				arr[idx] = $(this).text(); // 배열에 정보를 다 담음
				
				$(this).empty();
				if(idx < 3 || idx > 5) {
					
					s1 = "";
					if(idx <= 5) {
						s1 = " name='"+names[idx]+"' ";
					}
					
					s2 = "";
					if(idx == 0 || idx >= 6 ){
						s2 = " readonly = 'readonly' ";	
					}
					
					$(this).append("<input type='text' "+ s1 + s2 + " value='"+arr[idx]+"'>");
				} else {
					$(this).append("<input type='number' name='"+names[idx]+"' value='"+arr[idx]+"' min='0' max='100'>");
				}
				
			} else {
				$(this).empty();
				$(this).append("<span class='btn-updateOk'>완료</span> | <span class='btn-updateCancel'>취소</span>")
			}
		});
		
		$($tds[1]).find("input").focus();
		
		// 등록하기 줄의 모든 input과 버튼 비활성화 및 숨기기 
		$(".score-input input").prop("disabled", true);
		$(".score-input button").prop("disabled", true);
		$(".score-input").hide(100);
		
	});
	
	// 수정 완료
	$("body").on("click", ".btn-updateOk", function() {
		var query = $("form[name=scoreForm]").serialize();
		var url = "${pageContext.request.contextPath}/nscore/update";
		
		var fn = function(data) {
			var state = data.state;
			if(state === "true") {
				listPage(1);
				
				$(".score-input input").prop("disabled", false);
				$(".score-input button").prop("disabled", false);
				$(".score-input").show(100);
			} else {
				alert("자료를 수정하지 못했습니다.");
				
				$(".score-input input").prop("disabled", false);
				$(".score-input button").prop("disabled", false);
				$(".score-input").show(100);
			}
		};
		ajaxFun(url, "post", query, "json", fn);
	
	});
	
	// 수정 취소
	$("body").on("click", ".btn-updateCancel", function() {
		var $tds = $(this).closest("tr").children("td");
		$($tds).each(function(idx) {
			if(idx != $tds.length-1) {
				$(this).empty();
				$(this).text(arr[idx]);
			} else {
				$(this).empty();
				$(this).append("<span class='btn-update'>수정</span> | <span class='btn-delete'>삭제</span>")
			}
		});
		
		$(".score-input input").prop("disabled", false);
		$(".score-input button").prop("disabled", false);
		$(".score-input").show(100);
	});
});

 

jsp

<div class="container">

	<div class="title">
	   <h3><span>|</span> 성적 처리</h3>
	</div>
	
	<form name="scoreForm">
		<table class="score-table">
			<thead>
				<tr>
					<th width="80">학번</th>
					<th width="100">이름</th>
					<th width="100">생년월일</th>
					<th width="80">국어</th>
					<th width="80">영어</th>
					<th width="80">수학</th>
					<th width="80">총점</th>
					<th width="80">평균</th>
					<th>변경</th>
				</tr>
			</thead>
			<tbody class="score-input">
				<tr align="center" height="33">
					<td><input type="text" name="hak" id="hak" required="required"></td>
					<td><input type="text" name="name" id="name" required="required"></td>
					<td><input type="text" name="birth" id="birth" required="required"></td>
					<td><input type="number" name="kor" id="kor" min="0" max="100" required="required"></td>
					<td><input type="number" name="eng" id="eng" min="0" max="100" required="required"></td>
					<td><input type="number" name="mat" id="mat" min="0" max="100" required="required"></td>
					<td><input type="text" id="tot" readonly="readonly"></td>
					<td><input type="text" id="ave" readonly="readonly"></td>
					<td>
						<button type="submit" id="btnAdd">등록하기</button>
					</td>
				</tr>
			</tbody>
			<tfoot class="score-list"></tfoot>
		</table>
	</form>
	
</div>

 

Spring - Controller

package com.sp.app.nscore;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.sp.app.common.MyUtil;

@Controller("nscore.scoreController")
@RequestMapping("/nscore/*")
public class ScoreController {
	@Autowired
	private ScoreService service;

	@Autowired
	private MyUtil myUtil;
	
	@RequestMapping("main")
	public String main() throws Exception {
		
		return "nscore/main";
	}
	
	// 성적 리스트 : AJAX - JSON으로 결과 전송
	@RequestMapping("list")
	@ResponseBody
	public Map<String, Object> scoreList(
			@RequestParam(value = "pageNo", defaultValue = "1") int current_page,
			@RequestParam(defaultValue = "hak") String condition,
			@RequestParam(defaultValue = "") String keyword) throws Exception {
		
		int rows = 10;
		int dataCount, total_page;
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("condition", condition);
		map.put("keyword", keyword);
		
		dataCount = service.dataCount(map);
		total_page = myUtil.pageCount(rows, dataCount);
		if(current_page > total_page) {
			current_page = total_page;
		}
		
		int start = (current_page -1) * rows + 1;
		int end = (current_page * rows);
		map.put("start", start);
		map.put("end", end);
		
		List<Score> list = service.listScore(map);
		
		String paging = myUtil.pagingMethod(current_page, total_page, "listPage");
		
		Map<String, Object> model = new HashMap<String, Object>();
		
		model.put("list", list);
		model.put("dataCount", dataCount);
		model.put("total_page", total_page);
		model.put("pageNo", current_page);
		model.put("paging", paging);
		
		return model;
	}
	
	// 성적추가 : AJAX - JSON으로 결과 전송
	@RequestMapping(value = "insert", method = RequestMethod.POST)
	@ResponseBody // 메소드에서 반환하는 자바객체를 HTTP응답으로 변환하여 전송
	public Map<String, Object> scoreSubmit(
			Score dto
			) throws Exception {
		String state = "false";
		
		try {
			service.insertScore(dto);
			state = "true";
		} catch (DuplicateKeyException e) { // 중복학번일 경우
			state = "notUnique";
		} catch (Exception e) {
		}
		
		// @ResponseBody 애노테이션으로 인하여 Map 객체는 JSON으로 변환되어 전송된다.
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("state", state);
		return model;
	}
	
	// 자료 수정 : AJAX - JSON
	@RequestMapping(value = "update", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, Object> updateScore(
			Score dto) throws Exception {
		String state = "false";
		
		try {
			service.updateScore(dto);
			state = "true";
		} catch (Exception e) {
		}
		
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("state", state);
		return model;
	}
	
	// 자료 삭제 : AJAX -JSON
	@RequestMapping(value = "delete", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, Object> deleteScore(
			@RequestParam String hak) throws Exception {
		String state = "false";
		
		try {
			service.deleteScore(hak);
			state = "true";
		} catch (Exception e) {
		}
		
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("state", state);
		return model;

	}
	
}
package com.sp.app.test1;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller("test1.testController")
@RequestMapping("/test1/*")
public class TestController {
	@RequestMapping("main")
	public String main() throws Exception {	
		return "test1/main";
	}
	
	// Map을 리턴하면 모델을 설정
	@RequestMapping("hello")
	public Map<String, Object> execute() throws Exception {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("msg", "Map 인터페이스를 리턴 타입으로 포워딩 JSP에 값 전달");
		return map;
	}
	
	// void를 리턴하는 경우는 뷰가 필요 없는 경우(다운로드 등)
	@RequestMapping("hello2")
	public void execute2(
			HttpServletRequest req,
			HttpServletResponse resp) throws Exception {
		// HttpServletResponse가 있으면 뷰를 자동 설정 하지 않음.
		try {
			String a = req.getParameter("name");
			
			resp.setContentType("text/html; charset=utf-8");
			PrintWriter out = resp.getWriter();
			out.print("<script>alert('" + a + "님 반가워요');history.back();</script>");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@RequestMapping("calc")
	public String calcForm(
			@RequestParam String num,
			Model model) throws Exception {
		
		try {
			int n = Integer.parseInt(num);
			int s = 0;
			for(int i=1; i<=n; i++) {
				s+=i;
			}
			model.addAttribute("msg", "결과:"+s);
		} catch (Exception e) {
			return "redirect:/test1/error"; // 리다이렉트
		}
		return "test1/hello";
	}
	
	@RequestMapping("error")
	public String errorForm() throws Exception {
		return "test1/error";
	}
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3> 리턴 타입 </h3>

<p>
	<a href="${pageContext.request.contextPath}/test1/hello">확인</a>
</p>

<p>
	<a href="${pageContext.request.contextPath}/test1/hello2?name=kim">확인</a>
</p>

<p>
	<a href="${pageContext.request.contextPath}/test1/calc?num=a">계산</a>
</p>


<!-- 

  - @RequestMapping 메소드의 리턴 타입
    String : 뷰의 이름 -> ModelAndView 로 변환하여 처리
	ModelAndView : 모델과 뷰의 이름 설정 
	req.setAttribute("이름", 값); <-- 모델
	Map, Model, ModelMap : 모델을 설정, 뷰는 viewResolver가 등록된 경우 뷰의 이름은 자동으로 uri를 이용하여 설정한다.
		uri가 test1/hello 이면 JSP이면 /WEB-INF/views/test1/hello.jsp 가 됨
	void : HttpServletResponse 파라미터가 존재하지 않으면 뷰가 자동으로 설정
 -->


</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

	<p> 시스템 점검 중입니다. </p>

</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<p> ${msg} </p>


</body>
</html>

 

package com.sp.app.test4;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller("test4.testController")
@RequestMapping("/test4/*")
public class TestController {

	@GetMapping("write")
	public String form() throws Exception {
		return "test4/write";
	}
	
	@PostMapping("write")
	public String submit(User dto,
			RedirectAttributes rAttr
			) throws Exception {
		String s = "회원가입을 축하합니다.";
		
		// DB 작업
		
		// 리다이렉트한 페이지에 값 넘기기(내부적으로 세션을 활용함)
		rAttr.addFlashAttribute("dto", dto);  // 한번만 dto가 나옴
		rAttr.addFlashAttribute("msg", s);
		
		return "redirect:/test4/complete";
	}
	
	@GetMapping("complete")
	public String complete(@ModelAttribute("dto") User dto) throws Exception {
		// @ModelAttribute("dto") 가 없으면 아래에서는 null이 된다.
		// F5를 눌러 새로 고침을 하면 초기화 되어 아래 이름은 null이 된다.
		// jsp에서 출력되는 내용은 위 메소드의 rAttr.addFlashAttribute("dto", dto); 내용이다. 
		System.out.println(dto.getName() + " : " + dto.getId());
		 
		return "test4/result";
	}
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3> 리다이렉트한 페이지에 값 넘기기 </h3>

<form action="${pageContext.request.contextPath}/test4/write"
	method="post">
	<p>이름 : <input type="text" name="name"> </p>
	<p>아이디 : <input type="text" name="id"> </p>
	<p>비밀번호 : <input type="text" name="pwd"> </p>
	<p>생년월일 : <input type="text" name="birth"> </p>
	<p> <button type="submit">확인</button> </p>
</form>

</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3> 결 과 </h3>

<p> 이름 : ${dto.name} </p>
<p> 아이디 : ${dto.id} </p>
<p> 비밀번호 : ${dto.pwd} </p>
<p> 생일 : ${dto.birth} </p>

<p> ${msg} </p>

</body>
</html>
package com.sp.app.test2;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller("test2.testController")
@RequestMapping("/test2/*")
public class TestController {

	@RequestMapping("main")
	public String execute() throws Exception {
		return "test2/main";
	}
	
	@GetMapping("header")
	public String headerInfo(
			@RequestHeader("Accept-Language") String lang,
			@RequestHeader("User-Agent") String agent,
			HttpServletRequest req,
			Model model
			) throws Exception {
		
		String referer = req.getHeader("Referer"); // 이전 주소
		if(referer == null) {
			referer = "";
		}
		
		String s = "헤더 정보...<br>";
		s += "클라이언트 언어 정보 : " + lang + "<br>";
		s += "클라이언트 브라우저 및 OS : " + agent + "<br>";
		s += "이전 클라이언트 URL : "+ referer;
		
		model.addAttribute("msg", s);
		
		return "test2/result";
	}
	
	@RequestMapping("setCookie")
	public String cookieSet(
			HttpServletResponse resp
			) throws Exception {
		// 쿠키 설정하기(유효시간 : 브라우저를 닫으면 쿠키가 제거됨. 기본)
		Cookie ck = new Cookie("subject", "spring");
		resp.addCookie(ck);
		
		return "redirect:/test2/main";
	}
	
	// @CookieValue : 쿠키 가져오기. 기본 required는 true로 쿠키가 없으면 400에러
	// 	defaultValue 속성으로 쿠키값이 없는 경우 초기값 부여 가능
	@RequestMapping("getCookie")
	public String cookieGet(
			@CookieValue(name="subject", defaultValue = "") String subject,
			Model model
			) throws Exception {
		
		String s = "쿠키내용 : " + subject;
		
		model.addAttribute("msg", s);
		return "test2/result";
	}
	
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<p>
	<a href="${pageContext.request.contextPath}/test2/header"> 헤더정보확인 </a>
</p>

<p>
	<a href="${pageContext.request.contextPath}/test2/setCookie"> 쿠키설정 </a>
</p>

<p>
	<a href="${pageContext.request.contextPath}/test2/getCookie"> 쿠키확인 </a>
</p>


</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<p>
 ${msg}
</p>

</body>
</html>
package com.sp.app.join;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

/*
 @SessionAttributes
 - 모델의 객체를 세션에 저장하여 뷰(jsp)에서 공유
 - Controller에서 사용
 - 용도
 	스프링 form태그 라이브러리를 이용할 경우
 	여러 단계에 걸쳐 입력된 값을 처리할 경우(지속적으로 값을 유지)
 		double submit 방지 - 브라우저의 뒤로가기 안됨
 
 */

@SessionAttributes("user") // 클래스명과같아야함
@Controller("join.joinController")
@RequestMapping("/join/*")
public class JoinController {
	
	@ModelAttribute("user") // user 이름으로 모든 페이지가 공유할 수 있는 객체를 만듦
	public User command() {
		return new User(); // 세션에 저장할 객체 메모리 할당
	}
	
	@RequestMapping(value = "main", method = RequestMethod.GET)
	public String joinForm(@ModelAttribute("user") User user) throws Exception {
		// 회원가입 처음화면(step1.jsp) 출력
		return "join/step1";
	}
	
	// @ModelAttribute("user")는 @SessionAttributes("user")에서 설정한
	// 설정이름이 동일하므로 세션에 저장된 user을 사용함
	// User 클래스명 첫글자가 소문자인 이름과 동일한 경우 생략가능
	@RequestMapping(value = "step1", method = RequestMethod.POST)
	public String step1Submit(@ModelAttribute("user") User user) throws Exception {
		
		// 회원가입 두번째 화면 출력
		return "join/step2";
	}
	
	@RequestMapping(value = "step2", method = RequestMethod.POST)
	public String step2Submit(@ModelAttribute("user") User user,
			SessionStatus sessionStatus,
			Model model
			) throws Exception {
		// 회원가입 정보를 DB에 저장
		
		String s = "아이디:" +user.getId() + "<br>";
		s += "이름:"+user.getName() + "<br>";
		s += "이메일:"+user.getEmail() + "<br>";
		s += "패스워드:"+user.getPwd() + "<br>";
		s += "전화번호:"+user.getTel() + "<br>";
		
		// 세션에 저장된 내용 지우기
		sessionStatus.setComplete();
		model.addAttribute("msg", s);
		return "join/complete";
	}
	
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<form method="post"
	action="${pageContext.request.contextPath}/join/step1">
	
	<p> 이름 : <input type="text" name="name" value="${user.name}"> </p>
	<p> 이메일 : <input type="text" name="email" value="${user.email}"> </p>
	<p>
		<button type="submit">다음단계</button>
	</p>

</form>

</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<form method="post" 
	action="${pageContext.request.contextPath}/join/step2">
	
	<p> 아이디 : <input type="text" name="id" value="${user.id}"> </p>
	<p> 패스워드 : <input type="password" name="pwd"> </p>
	<p> 전화번호: <input type="text" name="tel" value="${user.tel}"> </p>
	<p>
		<button type="button"
			onclick="location.href='${pageContext.request.contextPath}/join/main';" >이전단계</button>
		<button type="submit">회원가입</button>
	</p>

</form>

</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3> 환영합니다. </h3>
<p>
	${msg}
</p>

</body>
</html>

+ Recent posts