TestController

package egovframework.example.test.web;

import java.io.File;
import java.util.HashMap;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import egovframework.example.test.domain.Search;
import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestService;

@Controller
public class TestController {

	@Autowired
	private TestService testServiceImpl;

	@RequestMapping(value = "/testList.do")
	public String testListView() {
		return "test/testList";
	}
	@RequestMapping(value = "/getTestList.do", produces = "application/json")
	@ResponseBody
	public ResponseEntity<HashMap<String, Object>> testListDo(Search search) throws Exception {

		HashMap<String, Object> result = new HashMap<>();

		// 전체 게시글 개수를 얻어와 listCnt에 저장
		int listCnt = testServiceImpl.getBoardListCnt(search);

		// 검색
		search.pageInfo(listCnt);

		// 페이징
		result.put("pagination", search);

		// 게시글 화면 출력
		result.put("list", testServiceImpl.selectTest(search));
		return ResponseEntity.ok(result);
	}

	// 글 작성 버튼 구현
	@RequestMapping(value = "/insertTest.do", produces = "application/json")
	@ResponseBody
	public ResponseEntity<TestVO> write(TestVO testVO) throws Exception {
		String fileName = null;
		MultipartFile uploadFile = testVO.getUploadFile();
		if (uploadFile != null && !uploadFile.isEmpty()) {
			String originalFileName = uploadFile.getOriginalFilename();
			String ext = FilenameUtils.getExtension(originalFileName); // 확장자 구하기
			UUID uuid = UUID.randomUUID(); // UUID 구하기
			fileName = uuid + "." + ext;
			uploadFile.transferTo(new File("C:\\upload\\" + fileName));
		}
		testVO.setFileName(fileName);

		int testId = testServiceImpl.insertTest(testVO);
		testVO.setTestId(testId);
		testVO = testServiceImpl.selectDetail(testVO);
		return ResponseEntity.ok(testVO);
	}

	// 글 작성 클릭시 글 작성 페이지로 이동
	@RequestMapping(value = "/testRegister.do")
	public String testRegister() {
		return "test/testRegister";
	}

	// 제목 클릭 시 상세보기
	@RequestMapping(value = "/testDetail.do")
	public String viewForm(@ModelAttribute("testVO") TestVO testVO, Model model, HttpServletRequest request)
			throws Exception {

		int testId = Integer.parseInt(request.getParameter("testId"));
		testVO.setTestId(testId);

		TestVO resultVO = testServiceImpl.selectDetail(testVO);
		model.addAttribute("result", resultVO);

		return "test/testDetail";
	}

	// 수정하기
	@RequestMapping(value = "/updateTest.do", produces = "application/json")
	@ResponseBody
	public ResponseEntity<TestVO> updateTest(TestVO testVO) throws Exception {
		// 파일 업로드 처리
		String fileName = null;
		MultipartFile uploadFile = testVO.getUploadFile();
		if (uploadFile != null && !uploadFile.isEmpty()) {
			String originalFileName = uploadFile.getOriginalFilename();
			String ext = FilenameUtils.getExtension(originalFileName); // 확장자 구하기
			UUID uuid = UUID.randomUUID(); // UUID 구하기
			fileName = uuid + "." + ext;
			uploadFile.transferTo(new File("C:\\upload\\" + fileName));
		}
		testVO.setFileName(fileName);
		System.out.println(testVO);
		testServiceImpl.updateTest(testVO);
		testVO = testServiceImpl.selectDetail(testVO);
		return ResponseEntity.ok(testVO);
	}

	// 삭제하기
	@RequestMapping(value = "/deleteTest.do", produces = "application/json")
	@ResponseBody
	public ResponseEntity<TestVO> deleteTest(TestVO testVO) throws Exception {
		testVO = testServiceImpl.selectDetail(testVO);
		testServiceImpl.deleteTest(testVO);
		return ResponseEntity.ok(testVO);
	}
}

testRegister

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script
	src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</head>
<body>
	<div class="container">
		<table class="table table-bordered">
			<thead>
				<h1>글쓰기</h1>
			</thead>
			<tbody>
				<!-- enctype="multipart/form-data" 이 부분을 사용해 줘야지만 파일을 전송할 수 있다. -->
				<form id="form_test" action="insertTest.do" method="post"
					encType="multipart/form-data">
					<tr>
						<th>제목:</th>
						<td><input type="text" placeholder="제목을 입력하세요. "
							name="testTitle" id="testTitle" class="form-control" /></td>
					</tr>
					<tr>
						<th>내용:</th>
						<td><textarea placeholder="내용을 입력하세요 . " id="testContent"
								name="testContent" class="form-control" style="height: 200px;"></textarea></td>
					</tr>
					<tr>
						<th>첨부파일:</th>
						<td><input type="file" id="uploadFile" name="uploadFile"></td>
					</tr>
					<tr>
						<td colspan="2">
							<button id="btn_register" type="button" class="btn btn_register">등록</button>
							<button id="btn_previous" type="button" class="btn btn_previous">이전</button>
					</tr>
				</form>
			</tbody>
		</table>
	</div>

</body>
<script type="text/javascript">
	//글쓰기
	$(document).on('click', '#btn_register', function(e) {

		//데이터를 담아내는 부분 상수 const로
		//jquery val() : Form Element 의 값을 받아오는데 쓰인다. (주로 input 이나 textarea 정도?)- 주의해야할 점은 Form Element 이외의 값은 받아오질 못한다는 점.
		//문자열 좌우에서 공백을 제거하는 함수가 trim() 함수 입니다.
		const testTitle = $("#testTitle").val().trim();
		const testContent = $("#testContent").val().trim();
		const uploadFile = $("#uploadFile")[0].files[0];

		//'==' 연산자를 이용하여 서로 다른 유형의 두 변수의 [값] 비교
		//'==='는 엄격한 비교를 하는 것으로 알려져 있다 ([값 & 자료형] -> true). 변수를 비교하거나 어떤 비교를 위해 항상 '===' 연산자를 사용 할 것을 권장한다.
		if (testTitle === '') {
			alert('제목을 입력해주세요.');
			return;
		}

		if (testContent === '') {
			alert('내용을 입력해주세요.');
			return;
		}

		//ajax 통신을 사용해 서버에 데이터를 전송하기 위해 
		//폼데이터 객체를 생성함
		//jquery의 append를 통해서 프로퍼티에 바인딩이 가능하도록 세팅한다..append()선택된 요소의 마지막에 새로운 요소나 콘텐츠를 추가한다.
		var formData = new FormData();
		formData.append("testTitle", testTitle);
		formData.append("testContent", testContent);

		//만약 uploadFile이 undifined거나 null일 경우 폼데이터에 보내지 않도록 한다.
		//이부분 체크하지 않을 경우 undifined가 데이터로 보내지기 때문에 서버에서 에러가 발생한다.
		if (uploadFile)
			formData.append("uploadFile", uploadFile);

		//ajax로 파일전송 폼데이터를 보내기위해
		//enctype, processData, contentType 이 세가지를 반드시 세팅해야한다.
		$.ajax({
			enctype : 'multipart/form-data',
			processData : false,
			contentType : false,
			cache : false,
			url : "./insertTest.do",
			data : formData,
			type : "POST",
			success : function(res) {
				alert('게시글 등록 완료');
				location.href = "./testList.do";
			}
		});
	});

	//이전 클릭 시 testList로 이동
	$("#btn_previous").click(function javascript_onclikc() {
		$(location).attr('href', 'testList.do');
	});
</script>
</html>



testDetail

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
	integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
	crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"
	integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"
	crossorigin="anonymous">

<!-- Latest compiled and minified JavaScript -->
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
	integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
	crossorigin="anonymous"></script>
<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
</head>
<body>
	<div class="container">
		<table class="table table-bordered">
			<thead>
				<h1>글 상세보기 Detail</h1>
			</thead>
			<tbody>
				<form action="updateTest.do" id="viewForm" method="post"
					encType="multipart/form-data">
					<tr>
						<th>글번호:</th>
						<td><input name="testId" id="testId" type="text" value="${result.testId}"
							class="form-control" readonly /></td>

					</tr>
					<tr>
						<th>제목:</th>
						<td><input type="text" value="${result.testTitle}"
							id="testTitle" class="form-control" /></td>
					</tr>
					<tr>
						<th>내용:</th>
						<td><textarea id="testContent" class="form-control"
								style="height: 200px;">${result.testContent}</textarea></td>

					</tr>
					<tr>
						<c:if test="${result.fileName ne null}">
							<tr>
								<td>다운로드</td>

								<td><a href="fileDownload.do?fileName=${result.fileName}">
										<input type="text" id="asd" value="${result.fileName}"
										name="fileName" class="form-control" readonly />
								</a>
									<button id="asdasd" type="button" class="btn_previous">파일지우기</button>
							</tr>
						</c:if>
					</tr>


					<tr>
						<th>첨부파일:</th>
						<td><input type="file" id="uploadFile"></td>
					</tr>
					<tr>

						<td colspan="2">
							<button id="btn_previous" type="button" class="btn_previous">이전</button>
							<button id="btn_delete" type="button" class="btn_previous">삭제</button>
							<button id="btn_modify" type="button" class="btn_register">수정</button>
					</tr>
				</form>
			</tbody>
		</table>
	</div>


</body>
<script type="text/javascript">
	const testId = $("#testId").val();

	$(document).on('click', '#btn_modify', function(e) {
		if (confirm("정말 수정하시겠습니까 ?") == true) {
			
			//데이터를 담아내는 부분
			const testTitle = $("#testTitle").val().trim();
			const testContent = $("#testContent").val().trim();
			const uploadFile = $("#uploadFile")[0].files[0];

			if(testTitle === ''){
				alert('제목을 입력해주세요.');
				return;
			}
			
			if(testContent === ''){
				alert('내용을 입력해주세요.');
				return;
			}

			//ajax 통신을 사용해 서버에 데이터를 전송하기 위해 
			//폼데이터 객체를 생성함
			//append를 통해서 프로퍼티에 바인딩이 가능하도록 세팅한다.
			var formData = new FormData();
			formData.append("testId",testId);
			formData.append("testTitle",testTitle);
			formData.append("testContent",testContent);

			//만약 uploadFile이 undifined거나 null일 경우 폼데이터에 보내지 않도록 한다.
			//이부분 체크하지 않을 경우 undifined가 데이터로 보내지기 때문에 서버에서 에러가 발생한다.
			if(uploadFile)
				formData.append("uploadFile",uploadFile);
			
			//ajax로 파일전송 폼데이터를 보내기위해
			//enctype, processData, contentType 이 세가지를 반드시 세팅해야한다.
			$.ajax({
				enctype: 'multipart/form-data',
				processData: false,
				contentType: false,
				cache: false,
				url : "./updateTest.do",
				data : formData,
				type : "POST",
				success : function(res){
					alert('수정 완료');
					location.href='./testList.do';
				}
			})			
		} 
	});
	
	$(document).on('click', '#btn_delete', function(e) {
		if (confirm("정말 삭제하시겠습니까 ?")) {
			$.ajax({
				url : "./deleteTest.do",
				type : "POST",
				data : {
					testId : testId
				},
				success : function(res){
					alert('삭제 완료');
					location.href='./testList.do';
				}
			})
		}
	});
	
	//이전 클릭 시 testList로 이동
	$("#btn_previous").click(function javascript_onclikc() {

		$(location).attr('href', 'testList.do');

	});
	$("#asdasd").click(function javascript_onclikc() {
		$('#asd').val(null);

	});
</script>
</html>

 

 

 

github.com/jodongyeon/boardTestMiso

 

jodongyeon/boardTestMiso

게시판 formsubmit방식과AJAX비동기방식. Contribute to jodongyeon/boardTestMiso development by creating an account on GitHub.

github.com

게시판연습 깃허브주소

pom.xml

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

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

pom.xml에 dependency추가

 

JSON 값을 RESPONSE 해주기 jackson 라이브러리 사용을 위한 설정. 
Jackson은 JSON 데이터 구조를 처리해주는 라이브러리. 
Spring 3.0 이후로부터, Jacskon과 관련된 API를 제공함으로써, Jackson라이브러리를 사용할 때, 자동화 처리가 가능하게 되었다.
덕분에, JSON데이터를 직접 만들던가, GSON or SimpleJSON방식과 같이 직접 키와 벨류를 세팅하는 방식에서 한 단계 더 발전한 방식. 
Spring3.0 이후로 컨트롤러의 리턴 방식이 @RequestBody 형식이라면, Spring은 MessageConverter API를 통해, 컨트롤러가 리턴하는 객체를 후킹 할 수 있다.
Jackson은 JSON데이터를 출력하기 위한  MappingJacksonHttpMessageConverter를 제공. 
만약 우리가 스프링 MessageConverter를 위의 MappingJacksonHttpMessageConverter으로 등록한다면,
컨트롤러가 리턴하는 객체를 다시 뜯어(자바 리플렉션 사용), Jackson의 ObjectMapper API로 JSON 객체를 만들고 난 후, 출력하여 JSON 데이터를 완성합니다.
더욱 편리해진 점은, Spring 3.1 이후로 만약 클래스패스에 Jackson 라이브러리가 존재한다면, ( 쉽게 말해 Jackson을 설치했느냐 안 했느냐 ) 
자동적으로 MessageConverter가 등록된다는 점입니다. 덕분에 우리는 아래와 같이 매우 편리하게 사용할 수 있다.

 

 


dispatcher-servlet.xml

	<mvc:annotation-driven>
		<mvc:message-converters>
			<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
		</mvc:message-converters>
	</mvc:annotation-driven>

 Spring 과 json연동.

 


TestController

@RequestMapping(value = "/testList.do")
	public String testListView() {
		return "test/testList";
	}
	
	@RequestMapping(value = "/getTestList.do", produces = "application/json")
	@ResponseBody
	public ResponseEntity<HashMap<String, Object>> testListDo(Search search) throws Exception {

		HashMap<String, Object> result = new HashMap<>();

		// 전체 게시글 개수를 얻어와 listCnt에 저장
		int listCnt = testServiceImpl.getBoardListCnt(search);

		// 검색
		search.pageInfo(listCnt);

		// 페이징
		result.put("pagination", search);

		// 게시글 화면 출력
		result.put("list", testServiceImpl.selectTest(search));

		return ResponseEntity.ok(result);
	}

 

이전의 modelandview방식의 view를 return 해주는 컨트롤러를 위처럼 비동기 방식의 컨트롤러로 코드를 수정한다.

 

// ajax url을 getTestList.do로 받기 (후킹??)

// produces = "application/json json형태로 response request요청에 따른 response의 content-type을 변경
// @ResponseBody는 서버에서 클라이언트로 응답데이터를 전송하기 위해 사용

 

Spring Framwork에서 제공하는 클래스중 HttpEntity라는 클래스.

이것은 HTTP 요청(Request) 또는 응답(Response)에 해당하는 HttpHeader와 HttpBody를 포함하는 클래스.

이 HttpEntity 라는 클래스를 상속받아 구현한 클래스가 RequestEntity, ResponseEntity ResponseEntity는 당연히 사용자의 HttpRequest에 대한 응답 데이터를 포함하는 클래스. 따라서 HttpStatus, HttpHeaders, HttpBody를 포함.

 

HashMap은 Map을 구현한다. Key와 value를 묶어 하나의 entry로 저장한다는 특징을 갖는다. 그리고 hashing을
사용하기 때문에 많은양의 데이터를 검색하는데 뛰어난 성능을 보인다. Map 인터페이스의 한 종류로 ( "Key", value)로 이뤄져 있다. key 값을 중복이 불가능하고 value는 중복이 가능. value에 null값도 사용 가능하다. 멀티스레드에서 동시에
HashMap을 건드려 Key - value값을 사용하면 문제가 될 수 있다. 멀티쓰레드에서는 HashTable을 쓴다

 

ResponseEntity의 static 메소드인 ok()는 HttpStatus코드의 OK(200)를 응답 데이터에 포함하도록 하고
ResponseEntity의 BodyBuilder를 return함


 TestServiceImpl

@Override
	public List<TestVO> selectTest(Search search) throws Exception {
		search.setPage(search.getPage() * search.getListSize());
		List<TestVO> result = testDAOService.selectTest(search);
		search.setPage(search.getPage() / search.getListSize());
		return result;
	}

페이지 번호에 따라 10개씩 정상적으로 넘어가기 위한 코드

 


testList.jsp

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>

<head>
<!-- Bootstrap CSS -->

<link rel="stylesheet"
	href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
	integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
	crossorigin="anonymous">

<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>

</head>

<body>
	<div class="container">
		<h1>게시판List</h1>
		<div class="testlist">
			<form id="boardForm" name="boardForm" method="post">
				<table class="table table-hover">
					<colgroup>
						<col width="20%" />
						<col width="50%" />
						<col width="15%" />
						<col width="15%" />
					</colgroup>
					<thead>
						<tr>
							<th>번호</th>
							<th>제목</th>
							<th>작성자</th>
							<th>등록일자</th>
						</tr>
					</thead>
					<tbody id="dataSection"></tbody>
				</table>
			</form>
		</div>


		<div>
			<button id="btn_write" type="button">글작성</button>
		</div>

		<br>


		<!-- pagination{s} -->
		<div class="pagination1">
			<ul id="paginationBox" class="pagination">

			</ul>
		</div>
		<!-- pagination{e} -->

		<!-- search{s} -->

		<div class="form-group row justify-content-center">

			<div class="w100" style="padding-right: 10px">
				<select class="form-control form-control-sm" id="keywordType">
					<option value="testTitle">제목</option>
					<option value="testContent">본문</option>
					<option value="testName">작성자</option>
				</select>
			</div>

			<div class="w300" style="padding-right: 10px">
				<input type="text" class="form-control form-control-sm"
					name="keyword" id="keyword">
			</div>

			<div>
				<button class="btn btn-sm btn-primary" name="btnSearch"
					id="btnSearch">검색</button>
			</div>
		</div>
		<!-- search{e} -->

		<!-- 페이지 목록 갯수   -->
		<div class="form-group row justify-content-center">
			<p>게시판 목록 갯수</p>
			<div class="w100" style="padding-right: 10px">
				<select id="listSize" class="form-control form-control-sm">
					<option value="10">10 개</option>
					<option value="15">15 개</option>
					<option value="20">20 개</option>
				</select>
			</div>
		</div>

	</div>
	<input type="hidden" id="searchInput" value="" />
	<input type="hidden" id="searchType" value="testTitle" />
</body>


<script type="text/javascript">
	$(document).ready(function() {
		getPage();
	})

	$("#listSize").change(function() {
		getPage();
	});

	//	글조회
	//	어떤 게시물을 클릭했는지 게시물의 번호(testId)를 넘겨 줘야 함 따라서 게시물 클릭 이벤트에서 게시물의 번호를 인자 값으로 받습니다.
	//  get 방식으로 데이터를 전송합니다. 따라서 ? 연산자를 사용해 testId를 주소 뒤에 붙여 줍니다
	function showDetail(testId) {

		var form = document.getElementById("boardForm");
		var url = "<c:url value='/testDetail.do'/>";
		url = url + "?testId=" + testId;

		form.action = url;
		form.submit();
	}

	//글 작성 버튼 클릭 시 testRegister로 이동
	$("#btn_write").click(function javascript_onclikc() {
		$(location).attr('href', 'testRegister.do');
	});

	$("#btnSearch").click(function() {
		const search = $("#keyword").val();
		const searchType = $("#keywordType").val();

		$("#searchInput").val(search);
		$("#searchType").val(searchType);
		getPage();
	});

	function getPage(page) {
		var listSize = $("#listSize").val();
		var searchType = $("#searchType").val();
		var search = $("#searchInput").val();
		$
				.ajax({
					url : "getTestList.do", //서비스 주소 
					data : { //서비스 처리에 필요한 인자값
						page : page,
						searchType : searchType,
						keyword : search,
						listSize : listSize
					},
					success : function(res) {
						const list = res['list'];
						const pagination = res['pagination'];
						var data = "";
						var block = "";

						console.log(pagination);
						// 테이블의 row를 삽입하는 부분
						for (var i = 0; i < list.length; i++) {
							data += "<tr style='cursor:pointer' onclick='showDetail("
									+ list[i]['testId'] + ")'>";
							data += "<td>" + list[i]['testId'] + "</td>";
							data += "<td>" + list[i]['testTitle'] + "</td>";
							data += "<td>" + list[i]['testName'] + "</td>";
							data += "<td>" + list[i]['testDate'] + "</td>";
							data += "</tr>";
						}

						// 이전버튼 활성화 여부를 결정하는 부분
						if (pagination['prev']) {
							block += "<li class='page-item'><a class='page-link' href='javascript:getPage("
									+ (pagination['startPage'] - 1)
									+ ")'> < </a></li>";
						} else {
							block += "<li class='page-item disabled'><a class='page-link'> < </a></li>";
						}

						// 번호를 표시하는 부분
						for (var i = pagination['startPage']; i < pagination['endPage']; i++) {
							if (page !== i) {
								block += "<li class='page-item'><a class='page-link' href='javascript:getPage("
										+ i + ")'>" + (i + 1) + "</a></li>";
							} else {
								block += "<li class='page-item disabled'><a class='page-link'>"
										+ (i + 1) + "</a></li>";
							}
						}

						if (pagination['next']) {
							block += "<li class='page-item'><a class='page-link' href='javascript:getPage("
									+ (pagination['endPage'])
									+ ")'>  > </a></li>";
						} else {
							block += "<li class='page-item disabled'><a class='page-link'> > </a></li>";
						}
						$("#dataSection").html(data);
						$("#paginationBox").html(block);
					}
				})
	}
</script>

</html>

 


Console

2020-10-19 14:46:55,266 DEBUG [org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor] Written [{pagination=egovframework.example.test.domain.Search@2e77becd, list=[TestVO [testId=264, testTitle=asdf, testContent=asdf, testDate=2020-10-19], TestVO [testId=263, testTitle=fd, testContent=fd, testDate=2020-10-19], TestVO [testId=262, testTitle=fdas, testContent=asdf, testDate=2020-10-19], TestVO [testId=261, testTitle=d d , testContent=df, testDate=2020-10-19], TestVO [testId=260, testTitle= , testContent=asdfasdf, testDate=2020-10-19], TestVO [testId=259, testTitle=d d, testContent=d, testDate=2020-10-19], TestVO [testId=258, testTitle=sdfg, testContent=sdfg, testDate=2020-10-19], TestVO [testId=257, testTitle=sdfg, testContent=sdfg, testDate=2020-10-19], TestVO [testId=256, testTitle=sdfg, testContent=sdfg, testDate=2020-10-19], TestVO [testId=255, testTitle=asdf, testContent=asdf, testDate=2020-10-19]]}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@3e01cad7]
2020-10-19 14:46:55,266 DEBUG [org.springframework.web.servlet.DispatcherServlet] Null ModelAndView returned to DispatcherServlet with name 'action': assuming HandlerAdapter completed request handling
2020-10-19 14:46:55,266 DEBUG [org.springframework.web.servlet.DispatcherServlet] Successfully completed request

콘솔창에 제이슨 형태로 Convert가 된 것을 볼 수 있다.

 

업로드


testMapper.xml

((((디비 테이블에file_name varchar(40)넣기))))

<!--게시글 삽입 -->
	<insert id="insertTest" parameterType="TestVO">
		<![CDATA[
		INSERT INTO test(testTitle, testContent, testName, testDate, fileName)
		VALUES(#{testTitle}, #{testContent}, '밥샵', now(), #{fileName})
		]]>
	</insert>

	<!--게시글 클릭시 detailView -->
	<select id="selectDetail"
		parameterType="egovframework.example.test.domain.TestVO"
		resultType="egovframework.example.test.domain.TestVO">
		<![CDATA[
			SELECT *
			FROM test
			WHERE testId = #{testId}
		]]>
	</select>

	<!--게시글 업데이트 -->
	<update id="updateTest">
		update test set
		testTitle = #{testTitle}
		,testContent = #{testContent}
		,fileName = #{fileName}
		where
		testId = #{testId}
	</update>

pom.xml

<!-- file upload -->
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3.1</version>
		</dependency>

		<!-- MultipartHttpServletRequset -->
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.6</version>
		</dependency>

 


dispatcher-servlet.xml

<!-- 파일 업로드 설정 -->
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- 파일 사이즈 설정 가능 byte 단위
		<property name="maxUploadSize" value="100000" />
		-->
	</bean>

testRegister.jsp

<tr>
	<th>첨부파일:</th>
	<td><input type="file" name="uploadFile"></td>	
</tr>

TestController

 

package egovframework.example.test.web;

import java.io.File;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import egovframework.example.test.domain.Search;
import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestService;

@Controller
public class TestController {

	@Autowired
	private TestService testServiceImpl;

	// 글 목록 리스트, 페이징, 검색
	@RequestMapping(value = "/testList.do")
	public String testListDo(Model model, @RequestParam(required = false, defaultValue = "1") int page,
			@RequestParam(required = false, defaultValue = "1") int range,
			@RequestParam(required = false, defaultValue = "testTitle") String searchType,
			@RequestParam(required = false) String keyword, @ModelAttribute("search") Search search) throws Exception {

		// 검색
		model.addAttribute("search", search);
		search.setSearchType(searchType);
		search.setKeyword(keyword);

		// 전체 게시글 개수를 얻어와 listCnt에 저장
		int listCnt = testServiceImpl.getBoardListCnt(search);

		// 검색
		search.pageInfo(page, range, listCnt);
		// 페이징
		model.addAttribute("pagination", search);
		// 게시글 화면 출력
		model.addAttribute("list", testServiceImpl.selectTest(search));

		return "test/testList";
	}

	// 글 작성 클릭시 글 작성 페이지로 이동
	@RequestMapping(value = "/testRegister.do")
	public String testRegister() {
		return "test/testRegister";
	}

	// 글 작성 버튼 구현
	@RequestMapping(value = "/insertTest.do")
	public String write(@ModelAttribute("testVO") TestVO testVO, RedirectAttributes rttr) throws Exception {

		// 파일 업로드 처리
		String fileName = null;
		MultipartFile uploadFile = testVO.getUploadFile();
		if (!uploadFile.isEmpty()) {
			String originalFileName = uploadFile.getOriginalFilename();
			String ext = FilenameUtils.getExtension(originalFileName); // 확장자 구하기
			UUID uuid = UUID.randomUUID(); // UUID 구하기
			fileName = uuid + "." + ext;
			uploadFile.transferTo(new File("C:\\upload\\" + fileName));
		}
		testVO.setFileName(fileName);

		testServiceImpl.insertTest(testVO);

		return "redirect:testList.do";
	}

	// HttpServletRequest 객체안에 모든 데이터들이 들어가는데 getParameter메소드로 testId 원하는 데이터 가져옴
	// 제목 클릭 시 상세보기
	@RequestMapping(value = "/testDetail.do")
	public String viewForm(@ModelAttribute("testVO") TestVO testVO, Model model, HttpServletRequest request)
			throws Exception {

		int testId = Integer.parseInt(request.getParameter("testId"));
		testVO.setTestId(testId);

		TestVO resultVO = testServiceImpl.selectDetail(testVO);
		model.addAttribute("result", resultVO);

		return "test/testDetail";
	}

	// 수정하기
	@RequestMapping(value = "/updateTest.do")
	public String updateTest(@ModelAttribute("testVO") TestVO testVO, HttpServletRequest request) throws Exception {
		// 파일 업로드 처리
		String fileName = null;
		MultipartFile uploadFile = testVO.getUploadFile();
		if (!uploadFile.isEmpty()) {
			String originalFileName = uploadFile.getOriginalFilename();
			String ext = FilenameUtils.getExtension(originalFileName); // 확장자 구하기
			UUID uuid = UUID.randomUUID(); // UUID 구하기
			fileName = uuid + "." + ext;
			uploadFile.transferTo(new File("C:\\upload\\" + fileName));
		}
		testVO.setFileName(fileName);
		testServiceImpl.updateTest(testVO);
		return "redirect:testList.do";
	}

	// 삭제하기
	@RequestMapping(value = "/deleteTest.do")
	public String deleteTest(@ModelAttribute("testVO") TestVO testVO) throws Exception {
		testServiceImpl.deleteTest(testVO);
		return "redirect:testList.do";
	}

}

TestVO

	private String fileName;
	private MultipartFile uploadFile;

다운로드

 


FileDownController.java

package egovframework.example.test.web;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class FileDownController {
     
    @RequestMapping(value = "fileDownload.do")
    public void fileDownload4(HttpServletRequest request,HttpServletResponse response) throws Exception {
        //String path =  request.getSession().getServletContext().getRealPath("저장경로");
        
        String filename =request.getParameter("fileName");
        String realFilename="";
        System.out.println(filename);
         
        try {
            String browser = request.getHeader("User-Agent"); 
            //파일 인코딩 
            if (browser.contains("MSIE") || browser.contains("Trident")
                    || browser.contains("Chrome")) {
                filename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+",
                        "%20");
            } else {
                filename = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
            }
        } catch (UnsupportedEncodingException ex) {
            System.out.println("UnsupportedEncodingException");
        }
        realFilename = "C:\\upload\\" + filename;
        System.out.println(realFilename);
        File file1 = new File(realFilename);
        if (!file1.exists()) {
            return ;
        }
         
        // 파일명 지정        
        response.setContentType("application/octer-stream");
        response.setHeader("Content-Transfer-Encoding", "binary;");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
        try {
            OutputStream os = response.getOutputStream();
            FileInputStream fis = new FileInputStream(realFilename);
 
            int ncount = 0;
            byte[] bytes = new byte[512];
 
            while ((ncount = fis.read(bytes)) != -1 ) {
                os.write(bytes, 0, ncount);
            }
            fis.close();
            os.close();
        } catch (Exception e) {
            System.out.println("FileNotFoundException : " + e);
        }
    }
}

testDetail.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
	integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
	crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"
	integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"
	crossorigin="anonymous">

<!-- Latest compiled and minified JavaScript -->
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
	integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
	crossorigin="anonymous"></script>
<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
</head>
<body>
	<div class="container">
		<table class="table table-bordered">
			<thead>
				<h1>글 상세보기 Detail</h1>
			</thead>
			<tbody>
				<form action="updateTest.do" id="viewForm" method="post"
					encType="multipart/form-data">
					<tr>
						<th>글번호:</th>
						<td><input name="testId" type="text" value="${result.testId}"
							class="form-control" readonly /></td>

					</tr>
					<tr>
						<th>제목:</th>
						<td><input type="text" value="${result.testTitle}"
							name="testTitle" class="form-control" /></td>
					</tr>
					<tr>
						<th>내용:</th>
						<td><textarea name="testContent" class="form-control"
								style="height: 200px;">${result.testContent}</textarea></td>

					</tr>
					<tr>
						<c:if test="${result.fileName ne null}">
							<tr>
								<td>다운로드</td>

								<td><a href="fileDownload.do?fileName=${result.fileName}">
										<input type="text" id="asd" value="${result.fileName}"
										name="fileName" class="form-control" readonly />
								</a>
									<button id="asdasd" type="button" class="btn_previous">파일지우기</button>
							</tr>
						</c:if>
					</tr>


					<tr>
						<th>첨부파일:</th>
						<td><input type="file" name="uploadFile"></td>
					</tr>
					<tr>

						<td colspan="2">
							<button id="btn_previous" type="button" class="btn_previous">이전</button>
							<button id="btn_delete" type="button" class="btn_previous">삭제</button>
							<button id="btn_modify" type="button" class="btn_register">수정</button>
					</tr>
				</form>
			</tbody>
		</table>
	</div>


</body>
<script type="text/javascript">
	$(document).on('click', '#btn_modify', function(e) {
		if (confirm("정말 수정하시겠습니까 ?") == true) {
			$("#viewForm").submit();
		} else {
			return;
		}

	});
	$(document).on('click', '#btn_delete', function(e) {
		if (confirm("정말 삭제하시겠습니까 ?") == true) {
			$("#viewForm").attr("action", "deleteTest.do");
			$("#viewForm").submit();
		} else {
			return;
		}

	});

	//이전 클릭 시 testList로 이동
	$("#btn_previous").click(function javascript_onclikc() {

		$(location).attr('href', 'testList.do');

	});
	$("#asdasd").click(function javascript_onclikc() {
		$('#asd').val(null);

	});
</script>
</html>

파일 업로드와 파일 다운로드, 파일 삭제를 구현하였다.

 

 

참고블로그

extsdd.tistory.com/category/IT/Spring(spring의 좋은 설명)

gangnam-americano.tistory.com/3

ming9mon.tistory.com/65?category=825118

freehoon.tistory.com/104

haenny.tistory.com/67(egov 게시판 sample예제 삭제)

badstorage.tistory.com/13(게시글 목록 갯수 변경)

lemontia.tistory.com/408?category=993095 (mapper namespace)

 

 

 

testMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="egovframework.example.test.service.TestMapper">

	<!--게시글 목록 조회 -->
	<select id="selectTest" resultType="TestVO">
		SELECT
		*
		FROM test
		<trim prefix="WHERE" prefixOverrides="AND|OR">
			<if test="searchType=='testTitle' and keyword != null and keyword != '' ">
				AND testTitle like CONCAT('%', #{keyword}, '%')
			</if>
			<if test="searchType=='testContent' and keyword != null and keyword != '' ">
				AND testContent like CONCAT('%', #{keyword}, '%')
			</if>
			<if test="searchType=='testName' and keyword != null and keyword != '' ">
				AND testName like CONCAT('%', #{keyword}, '%')
			</if>
		</trim>
		ORDER BY
		testId DESC
		LIMIT #{startList}, #{listSize}
	</select>

	<!-- 전체 글의 개수를 읽어 오는 SQL문 -->
	<select id="getBoardListCnt" resultType="int">
		SELECT
		count(*) as
		listCnt
		FROM
		test
		<trim prefix="WHERE" prefixOverrides="AND|OR">
			<if test="keyword != null and keyword != '' ">
				<if test="searchType=='testTitle'">
					AND testTitle like CONCAT('%', #{keyword}, '%')
				</if>
				<if test="searchType=='testContent'">
					AND testContent like CONCAT('%', #{keyword}, '%')
				</if>
				<if test="searchType=='testName'">
					AND testName like CONCAT('%', #{keyword}, '%')
				</if>
			</if>
		</trim>
	</select>

	<!--게시글 삽입 -->
	<insert id="insertTest" parameterType="TestVO">
		<![CDATA[
		INSERT INTO test(testTitle, testContent, testName, testDate)
		VALUES(#{testTitle}, #{testContent}, '밥샵', now())
		]]>
	</insert>

	<!--게시글 클릭시 detailView -->
	<select id="selectDetail"
		parameterType="egovframework.example.test.domain.TestVO"
		resultType="egovframework.example.test.domain.TestVO">
		<![CDATA[
			SELECT *
			FROM test
			WHERE testId = #{testId}
		]]>
	</select>

	<!--게시글 업데이트 -->
	<update id="updateTest">
		update test set
		testTitle = #{testTitle}
		,testContent =
		#{testContent}
		where
		testId = #{testId}
	</update>

	<!--게시글 삭제 -->
	<delete id="deleteTest">
		delete from test
		where
		testId = #{testId}
	</delete>
</mapper> 





Pagination과 Search 추가


Pagination

package egovframework.example.test.domain;

public class Pagination {

	private int listSize = 10; // 초기값으로 목록개수를 10으로 셋팅 한 페이지당 보여질 리스트의 개수

	private int rangeSize = 5; // 초기값으로 페이지범위를 5로 셋팅 한 페이지 범위에 보여질 페이지의 개수

	private int page; // 현재목록의 페이지 번호

	private int range; // 각 페이지 범위 시작 번호

	private int listCnt; // 전체 개시물의 개수

	private int pageCnt; // 전체 페이지 범위의 개수

	private int startPage; // 각 페이지 범위 시작 번호

	private int startList; // 게시판 시작번호

	private int endPage; // 각 페이지 범위 끝 번호

	private boolean prev; // 이전 페이지

	private boolean next; // 다음 페이지

	public int getRangeSize() {

		return rangeSize;

	}

	public int getPage() {

		return page;

	}

	public void setPage(int page) {

		this.page = page;

	}

	public int getRange() {

		return range;

	}

	public void setRange(int range) {

		this.range = range;

	}

	public int getStartPage() {

		return startPage;

	}

	public void setStartPage(int startPage) {

		this.startPage = startPage;

	}

	public int getEndPage() {

		return endPage;

	}

	public void setEndPage(int endPage) {

		this.endPage = endPage;

	}

	public boolean isPrev() {

		return prev;

	}

	public void setPrev(boolean prev) {

		this.prev = prev;

	}

	public boolean isNext() {

		return next;

	}

	public void setNext(boolean next) {

		this.next = next;

	}

	public int getListSize() {

		return listSize;

	}

	public void setListSize(int listSize) {

		this.listSize = listSize;

	}

	public int getListCnt() {

		return listCnt;

	}

	public void setListCnt(int listCnt) {

		this.listCnt = listCnt;

	}

	public int getStartList() {

		return startList;

	}

	// 첫번째 인자 page 는 현재 페이지 정보, 두번째 인자 range 는 현재 페이지 범위 정보, 세번째 인자 listCnt는 게시물의 총 개수
	public void pageInfo(int page, int range, int listCnt) {

		this.page = page;

		this.range = range;

		this.listCnt = listCnt;

		// 전체 페이지수
		this.pageCnt = (int) Math.ceil((double) listCnt / listSize);

		// 시작 페이지
		this.startPage = (range - 1) * rangeSize + 1;

		// 끝 페이지
		this.endPage = range * rangeSize;

		// 게시판 시작번호
		this.startList = (page - 1) * listSize;

		// 이전 버튼 상태
		this.prev = range == 1 ? false : true;

		// 다음 버튼 상태
		this.next = endPage > pageCnt ? false : true;
		if (this.endPage > this.pageCnt) {
			this.endPage = this.pageCnt;
			this.next = false;
		}
	}
}

Search

package egovframework.example.test.domain;

//Search 클래스가 Pagination 클래스를 상속 받았으므로 기존 Pagination의 특성을 그대로 사용 할 수 있다.
public class Search extends Pagination{

	private String searchType;
	private String keyword;	

	public String getSearchType() {
		return searchType;
	}

	public void setSearchType(String searchType) {
		this.searchType = searchType;
	}

	public String getKeyword() {
		return keyword;
	}

	public void setKeyword(String keyword) {
		this.keyword = keyword;
	}
}

TestController

package egovframework.example.test.web;


import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
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.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import egovframework.example.test.domain.Search;
import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestService;

@Controller
public class TestController {

	@Autowired
	private TestService testServiceImpl;

	// 글 목록 리스트, 페이징, 검색
	@RequestMapping(value = "/testList.do")
	public String testListDo(Model model
			,@RequestParam(required = false, defaultValue = "1") int page
			,@RequestParam(required = false, defaultValue = "1") int range
			,@RequestParam(required = false, defaultValue = "testTitle") String searchType
			,@RequestParam(required = false) String keyword,
			@ModelAttribute("search") Search search
			) throws Exception {

		//검색
		model.addAttribute("search", search);
		search.setSearchType(searchType);
		search.setKeyword(keyword);
		
		// 전체 게시글 개수를 얻어와 listCnt에 저장
		int listCnt = testServiceImpl.getBoardListCnt(search);

		//검색
		search.pageInfo(page, range, listCnt);
		//페이징
		model.addAttribute("pagination", search);
		//게시글 화면 출력
		model.addAttribute("list", testServiceImpl.selectTest(search));

		return "test/testList";
	}

	// 글 작성 클릭시 글 작성 페이지로 이동
	@RequestMapping(value = "/testRegister.do")
	public String testRegister() {
		return "test/testRegister";
	}

	// 글 작성 버튼 구현
	@RequestMapping(value = "/insertTest.do")
	public String write(@ModelAttribute("testVO") TestVO testVO, RedirectAttributes rttr) throws Exception {
		testServiceImpl.insertTest(testVO);
		return "redirect:testList.do";
	}

	// HttpServletRequest 객체안에 모든 데이터들이 들어가는데 getParameter메소드로 testId 원하는 데이터 가져옴
	// 제목 클릭 시 상세보기
	@RequestMapping(value = "/testDetail.do")
	public String viewForm(@ModelAttribute("testVO") TestVO testVO, Model model, HttpServletRequest request)
			throws Exception {

		int testId = Integer.parseInt(request.getParameter("testId"));
		testVO.setTestId(testId);

		TestVO resultVO = testServiceImpl.selectDetail(testVO);
		model.addAttribute("result", resultVO);

		return "test/testDetail";
	}

	// 수정하기
	@RequestMapping(value = "/updateTest.do")
	public String updateTest(@ModelAttribute("testVO") TestVO testVO, HttpServletRequest request) throws Exception {
		testServiceImpl.updateTest(testVO);
		return "redirect:testList.do";
	}

	// 삭제하기
	@RequestMapping(value = "/deleteTest.do")
	public String deleteTest(@ModelAttribute("testVO") TestVO testVO) throws Exception {
		testServiceImpl.deleteTest(testVO);
		return "redirect:testList.do";
	}

}

TestService

package egovframework.example.test.service;

import java.util.List;

import egovframework.example.test.domain.Search;
import egovframework.example.test.domain.TestVO;

public interface TestService {

	public List<TestVO> selectTest(Search search) throws Exception;

	public void insertTest(TestVO testVO)throws Exception;

	public TestVO selectDetail(TestVO testVO) throws Exception;

	public void updateTest(TestVO testVO) throws Exception;

	public void deleteTest(TestVO testVO) throws Exception;
	
	//총 게시글 개수 확인
	public int getBoardListCnt(Search search) throws Exception;

}

TestServiceImpl

package egovframework.example.test.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import egovframework.example.test.dao.TestDAO;
import egovframework.example.test.domain.Search;
import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestService;

@Service("testServiceImpl")
public class TestServiceImpl implements TestService {

	@Autowired
	private TestDAO testDAOService;

	@Override
	public List<TestVO> selectTest(Search search) throws Exception {
		return testDAOService.selectTest(search);
	}

	@Override
	public void insertTest(TestVO testVO) throws Exception {
		testDAOService.insertTest(testVO);
	}

	@Override
	public TestVO selectDetail(TestVO testVO) throws Exception {
		/*
		 * TestVO resultVO = testDAOService.selectDetail(testVO); return resultVO;
		 */
		return testDAOService.selectDetail(testVO);
	}

	@Override
	public void updateTest(TestVO testVO) throws Exception {
		testDAOService.updateTest(testVO);
	}

	@Override
	public void deleteTest(TestVO testVO) throws Exception {
		testDAOService.deleteTest(testVO);
	}
	// 총 게시글 개수 확인

	@Override
	public int getBoardListCnt(Search search) throws Exception {
		return testDAOService.getBoardListCnt(search);
	}

}

TestDAO

package egovframework.example.test.dao;

import java.util.List;

import egovframework.example.test.domain.Search;
import egovframework.example.test.domain.TestVO;

public interface TestDAO {

	List<TestVO> selectTest(Search search) throws Exception;

	void insertTest(TestVO testVO) throws Exception;
	
	TestVO selectDetail(TestVO testVO) throws Exception;

	void updateTest(TestVO testVO) throws Exception;

	void deleteTest(TestVO testVO) throws Exception;
	
	//총 게시글 개수 확인
	public int getBoardListCnt(Search search) throws Exception;

}

TestDAOService

package egovframework.example.test.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import egovframework.example.test.domain.Search;
import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestMapper;

@Service("testDAOService")
public class TestDAOService implements TestDAO {

	@Autowired
	private SqlSession sqlSession;

	@Override
	public List<TestVO> selectTest(Search search) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		return mapper.selectTest(search);
	}

	@Override
	public void insertTest(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		mapper.insertTest(testVO);
	}

	@Override
	public TestVO selectDetail(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		return mapper.selectDetail(testVO);
	}

	@Override
	public void updateTest(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		mapper.updateTest(testVO);
	}

	@Override
	public void deleteTest(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		mapper.deleteTest(testVO);
	}

	// 총 게시글 개수 확인
	@Override
	public int getBoardListCnt(Search search) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		return mapper.getBoardListCnt(search);
	}
}

TestMapper.java

package egovframework.example.test.service;

import java.util.List;

import egovframework.example.test.domain.Search;
import egovframework.example.test.domain.TestVO;


public interface TestMapper {

	// 게시물 리스트 조회
	public List<TestVO> selectTest(Search search) throws Exception;

	// 게시물 작성
	public void insertTest(TestVO testVO) throws Exception;

	// 게시물 조회
	public TestVO selectDetail(TestVO testVO) throws Exception;

	// 게시물 수정
	public void updateTest(TestVO testVO) throws Exception;

	// 게시물 삭제
	public void deleteTest(TestVO testVO) throws Exception;

	// 총 게시글 개수 확인
	public int getBoardListCnt(Search search) throws Exception;
}

testList.jsp

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>

<head>
<!-- Bootstrap CSS -->

<link rel="stylesheet"
	href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
	integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
	crossorigin="anonymous">

<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>

</head>

<body>

	<h1>게시판List</h1>

	<div class="testlist">
		<form id="boardForm" name="boardForm" method="post">
			<table class="list_table">
				<colgroup>
					<col width="20%" />
					<col width="50%" />
					<col width="15%" />
					<col width="15%" />
				</colgroup>
				<tbody>
				<thead>

					<tr>
						<th>번호</th>
						<th>제목</th>
						<th>작성자</th>
						<th>등록일자</th>
					</tr>
				</thead>

				<!-- jstl 데이터베이스를 검색해 넘겨 받은 list 를 result 라는 이름으로 순차적으로 실행을 시키게 됨  java의 for문같이 순차적으로 실행시킴-->

				<c:forEach items="${list}" var="result">
					<tr>
						<td><c:out value="${result.testId}" /></td>
						<td><a href='#' onClick='fn_view(${result.testId})'><c:out
									value="${result.testTitle }" /></a></td>
						<td><c:out value="${result.testName}" /></td>
						<td><c:out value="${result.testDate}" /></td>
					</tr>
				</c:forEach>
				</tbody>
			</table>
		</form>
	</div>
	<div>
		<button id="btn_write" type="button" class="btn_write">글작성</button>
	</div>
	<br>
	<!-- pagination{s} -->

	<div id="paginationBox" class="pagination1">
		<ul class="pagination">

			<c:if test="${pagination.prev}">
				<li class="page-item"><a class="page-link" href="#"
					onClick="fn_prev('${pagination.page}', '${pagination.range}', '${pagination.rangeSize}', '${pagination.listSize}'
					,'${search.searchType}', '${search.keyword}')">이전</a></li>
			</c:if>

			<c:forEach begin="${pagination.startPage}"
				end="${pagination.endPage}" var="testId">

				<li
					class="page-item <c:out value="${pagination.page == testId ? 'active' : ''}"/> "><a
					class="page-link" href="#"
					onClick="fn_pagination('${testId}', '${pagination.range}', '${pagination.rangeSize}', '${pagination.listSize}'
					 ,'${search.searchType}', '${search.keyword}')">
						${testId} </a></li>
			</c:forEach>

			<c:if test="${pagination.next}">

				<li class="page-item"><a class="page-link" href="#"
					onClick="fn_next('${pagination.range}', '${pagination.range}', '${pagination.rangeSize}', '${pagination.listSize}'
					,'${search.searchType}', '${search.keyword}')">다음</a></li>
			</c:if>
		</ul>
	</div>
	<!-- pagination{e} -->



	<!-- search{s} -->

	<div class="form-group row justify-content-center">

		<div class="w100" style="padding-right: 10px">
			<select class="form-control form-control-sm" name="searchType"
				id="searchType">
				<option value="testTitle">제목</option>
				<option value="testContent">본문</option>
				<option value="testName">작성자</option>
			</select>
		</div>

		<div class="w300" style="padding-right: 10px">
			<input type="text"
				<%-- value="${pagination.keyword}" --%> class="form-control form-control-sm"
				name="keyword" id="keyword">
		</div>

		<div>
			<button class="btn btn-sm btn-primary" name="btnSearch"
				id="btnSearch">검색</button>
		</div>

	</div>

	<!-- search{e} -->


	<!-- 페이지 목록 갯수   -->
	<div class="form-group row justify-content-center">
		<p>게시판 목록 갯수</p>
		<div class="w100" style="padding-right: 10px">
			<select class="form-control form-control-sm" name="searchType"
				id="listSize" onchange="page(1)">
				<option value="10"
					<c:if test="${pagination.getListSize() == 10 }">selected="selected"</c:if>>10개</option>
				<option value="15"
					<c:if test="${pagination.getListSize() == 15 }">selected="selected"</c:if>>15개</option>
				<option value="20"
					<c:if test="${pagination.getListSize() == 20 }">selected="selected"</c:if>>20개</option>
			</select>
		</div>

	</div>
</body>
<script type="text/javascript">
	//글 작성 버튼 클릭 시 testRegister로 이동
	$("#btn_write").click(function javascript_onclikc() {
		$(location).attr('href', 'testRegister.do');

	});
	
	//글조회
	//	어떤 게시물을 클릭했는지 게시물의 번호(testId)를 넘겨 줘야 함 따라서 게시물 클릭 이벤트에서 게시물의 번호를 인자 값으로 받습니다.
	//  get 방식으로 데이터를 전송합니다. 따라서 ? 연산자를 사용해 testId를 주소 뒤에 붙여 줍니다
	function fn_view(testId){
    
  	  var form = document.getElementById("boardForm");
  	  var url = "<c:url value='/testDetail.do'/>";
  	  url = url + "?testId=" + testId;
    
 	   form.action = url;    
 	   form.submit(); 
	}
	//이전 버튼 이벤트
	//5개의 인자값을 가지고 이동 testList.do
	//무조건 이전페이지 범위의 가장 앞 페이지로 이동
	function fn_prev(page, range, rangeSize, listSize, searchType, keyword) {
			
		var page = ((range - 2) * rangeSize) + 1;
		var range = range - 1;
			
		var url = "/testList.do";
		url = url + "?page=" + page;
		url = url + "&range=" + range;
		url = url + "&listSize=" + listSize;
		url = url + "&searchType=" + searchType;
		url = url + "&keyword=" + keyword;
		location.href = url;
		}

	  //페이지 번호 클릭

	function fn_pagination(page, range, rangeSize, listSize, searchType, keyword) {

		var url = "/testList.do";
			url = url + "?page=" + page;
			url = url + "&range=" + range;
			url = url + "&listSize=" + listSize;
			url = url + "&searchType=" + searchType;
			url = url + "&keyword=" + keyword; 

			location.href = url;	
		}

		//다음 버튼 이벤트
		//다음 페이지 범위의 가장 앞 페이지로 이동
	function fn_next(page, range, rangeSize, listSize, searchType, keyword) {
		var page = parseInt((range * rangeSize)) + 1;
		var range = parseInt(range) + 1;			
		var url = "/testList.do";
			url = url + "?page=" + page;
			url = url + "&range=" + range;
			url = url + "&listSize=" + listSize;
			url = url + "&searchType=" + searchType;
			url = url + "&keyword=" + keyword;
			location.href = url;
		}
		
		
	/* $는 jQuery를 시작하는 명령어로
	$(DOM요소) 와 같은 명령어로 각 요소에 접근 할 수 있다.
	e.preventDefault(); 는 버튼 고유의 기능을 막는 명령어 */ 
		
		// 검색
	$(document).on('click', '#btnSearch', function(e){
		e.preventDefault();
		var url = "/testList.do";
		url = url + "?searchType=" + $('#searchType').val();
		url = url + "&keyword=" + $('#keyword').val();
		url = url + "&listSize=" + $('#listSize').val();
		location.href = url;
		console.log(url);

	});	

	/*한페이지당 게시물 */
	function page(testId){
	  var startPage = testId;
	  var listSize = $("#listSize option:selected").val();
		
	  if(listSize == 10){
		  var url="/testList.do?startPage="+startPage+"&listSize="+listSize
	  }else if(listSize == 15){
		  var url ="/testList.do?startPage="+startPage+"&listSize="+listSize
	  }else if(listSize == 20){
	      var url="/testList.do?startPage="+startPage+"&listSize="+listSize
	 
	  }
	  location.href = url;
	}
</script>
</html>

페이징과 검색 selectbox가 다 기능하는 모습입니다. 목록 갯수 변경 후 검색, 페이징 등

testMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="egovframework.example.test.service.TestMapper">

	<!--게시글 목록 조회  -->
	<select id="selectTest" resultType="TestVO">
		SELECT
		*
		FROM test
		ORDER BY
		testId DESC

	</select>

	<!--게시글 삽입  -->
	<insert id="insertTest" parameterType="TestVO">
		<![CDATA[
		INSERT INTO test(testTitle, testContent, testName, testDate)
		VALUES(#{testTitle}, #{testContent}, '밥샵', now())
		]]>
	</insert>
	
	<!--게시글 클릭시 detailView  -->
	<select id="selectDetail"
		parameterType="egovframework.example.test.domain.TestVO"
		resultType="egovframework.example.test.domain.TestVO">
		<![CDATA[
			SELECT *
			FROM test
			WHERE testId = #{testId}
		]]>
	</select>

	<!--게시글 업데이트  -->
	<update id="updateTest">
		update test set
		testTitle = #{testTitle}
		,testContent = #{testContent}
		where
		testId = #{testId}
	</update>

	<!--게시글 삭제  -->
	<delete id="deleteTest">
		delete from test
		where
		testId = #{testId}
	</delete>
</mapper> 





TestController

package egovframework.example.test.web;


import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
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.servlet.mvc.support.RedirectAttributes;

import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestService;

@Controller
public class TestController {

	@Autowired
	private TestService testServiceImpl;

	@RequestMapping(value = "/testList.do")
	public String testListDo(@ModelAttribute TestVO testVO, Model model) throws Exception {
		model.addAttribute("list", testServiceImpl.selectTest(testVO));
		return "test/testList";
	}

	// 글 작성 클릭시 글 작성 페이지로 이동
	@RequestMapping(value = "/testRegister.do")
	public String testRegister() {
		return "test/testRegister";
	}

	// 글 작성 버튼 구현
	@RequestMapping(value = "/insertTest.do")
	public String write(@ModelAttribute("testVO") TestVO testVO, RedirectAttributes rttr) throws Exception {
		testServiceImpl.insertTest(testVO);
		return "redirect:testList.do";
	}

	// HttpServletRequest 객체안에 모든 데이터들이 들어가는데 getParameter메소드로 testId 원하는 데이터 가져옴
	// 제목 클릭 시 상세보기
	@RequestMapping(value = "/testDetail.do")
	public String viewForm(@ModelAttribute("testVO") TestVO testVO, Model model, HttpServletRequest request)
			throws Exception {

		int testId = Integer.parseInt(request.getParameter("testId"));
		testVO.setTestId(testId);

		TestVO resultVO = testServiceImpl.selectDetail(testVO);
		model.addAttribute("result", resultVO);

		return "test/testDetail";
	}

	// 수정하기
	@RequestMapping(value = "/updateTest.do")
	public String updateTest(@ModelAttribute("testVO") TestVO testVO, HttpServletRequest request) throws Exception {
		testServiceImpl.updateTest(testVO);
		return "redirect:testList.do";
	}

	// 삭제하기
	@RequestMapping(value = "/deleteTest.do")
	public String deleteTest(@ModelAttribute("testVO") TestVO testVO) throws Exception {
		testServiceImpl.deleteTest(testVO);
		return "redirect:testList.do";
	}

}

TestService

package egovframework.example.test.service;

import java.util.List;

import egovframework.example.test.domain.TestVO;

public interface TestService {

	public List<TestVO> selectTest(TestVO testVO) throws Exception;

	public void insertTest(TestVO testVO)throws Exception;

	public TestVO selectDetail(TestVO testVO) throws Exception;

	public void updateTest(TestVO testVO) throws Exception;

	public void deleteTest(TestVO testVO) throws Exception;
}

TestServiceImpl

package egovframework.example.test.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import egovframework.example.test.dao.TestDAO;
import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestService;

@Service("testServiceImpl")
public class TestServiceImpl implements TestService {
	
	@Autowired
	private TestDAO testDAOService;

	@Override
	public List<TestVO> selectTest(TestVO testVO) throws Exception {		
		return testDAOService.selectTest(testVO);
	}

	@Override
	public void insertTest(TestVO testVO) throws Exception {
		testDAOService.insertTest(testVO);
	}
	@Override
	public TestVO selectDetail(TestVO testVO) throws Exception {
		/*TestVO resultVO = testDAOService.selectDetail(testVO);
		return resultVO;*/
		return testDAOService.selectDetail(testVO);
	}

	@Override
	public void updateTest(TestVO testVO) throws Exception {
		testDAOService.updateTest(testVO);
	}

	@Override
	public void deleteTest(TestVO testVO) throws Exception {
		testDAOService.deleteTest(testVO);
	}
}

TestDAO

package egovframework.example.test.dao;

import java.util.List;

import egovframework.example.test.domain.TestVO;

public interface TestDAO {

	List<TestVO> selectTest(TestVO testVO) throws Exception;

	void insertTest(TestVO testVO) throws Exception;
	
	TestVO selectDetail(TestVO testVO) throws Exception;

	void updateTest(TestVO testVO) throws Exception;

	void deleteTest(TestVO testVO) throws Exception;
}

TestDAOService

package egovframework.example.test.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestMapper;

@Service("testDAOService")
public class TestDAOService implements TestDAO {

	@Autowired
	private SqlSession sqlSession;

	@Override
	public List<TestVO> selectTest(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		return mapper.selectTest(testVO);
	}

	@Override
	public void insertTest(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		mapper.insertTest(testVO);
	}

	@Override
	public TestVO selectDetail(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		return mapper.selectDetail(testVO);
	}
	
	@Override
	public void updateTest(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		mapper.updateTest(testVO);
	}
	
	@Override
	public void deleteTest(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		mapper.deleteTest(testVO);
	}

}

TestMapper.java

package egovframework.example.test.service;

import java.util.List;

import egovframework.example.test.domain.TestVO;


public interface TestMapper {

	// 게시물 리스트 조회
	public List<TestVO> selectTest(TestVO testVO) throws Exception;

	// 게시물 작성
	public void insertTest(TestVO testVO) throws Exception;

	// 게시물 조회
	public TestVO selectDetail(TestVO testVO) throws Exception;

	// 게시물 수정
	public void updateTest(TestVO testVO) throws Exception;

	// 게시물 삭제
	public void deleteTest(TestVO testVO) throws Exception;
}

testList.jsp(게시판 목록 페이지)

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>

<head>
<!-- Bootstrap CSS -->

<link rel="stylesheet"
	href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
	integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
	crossorigin="anonymous">

<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>

</head>

<body>

	<h1>게시판List</h1>

	<div class="testlist">
		<form id="boardForm" name="boardForm" method="post">
			<table class="list_table">
				<colgroup>
					<col width="20%" />
					<col width="50%" />
					<col width="15%" />
					<col width="15%" />
				</colgroup>
				<tbody>
				<thead>

					<tr>
						<th>번호</th>
						<th>제목</th>
						<th>작성자</th>
						<th>등록일자</th>
					</tr>
				</thead>

				<!-- jstl 데이터베이스를 검색해 넘겨 받은 list 를 result 라는 이름으로 순차적으로 실행을 시키게 됨  java의 for문같이 순차적으로 실행시킴-->

				<c:forEach items="${list}" var="result">
					<tr>
						<td><c:out value="${result.testId}" /></td>
						<td><a href='#' onClick='fn_view(${result.testId})'><c:out
									value="${result.testTitle }" /></a></td>
						<td><c:out value="${result.testName}" /></td>
						<td><c:out value="${result.testDate}" /></td>
					</tr>
				</c:forEach>
				</tbody>
			</table>
		</form>
	</div>
	<div>
		<button id="btn_write" type="button" class="btn_write">글작성</button>
	</div>
	<br>
</body>
<script type="text/javascript">
	//글 작성 버튼 클릭 시 testRegister로 이동
	$("#btn_write").click(function javascript_onclikc() {
		$(location).attr('href', 'testRegister.do');

	});
	
	//글조회
	//	어떤 게시물을 클릭했는지 게시물의 번호(testId)를 넘겨 줘야 함 따라서 게시물 클릭 이벤트에서 게시물의 번호를 인자 값으로 받습니다.
	//  get 방식으로 데이터를 전송합니다. 따라서 ? 연산자를 사용해 testId를 주소 뒤에 붙여 줍니다
	function fn_view(testId){
    
  	  var form = document.getElementById("boardForm");
  	  var url = "<c:url value='/testDetail.do'/>";
  	  url = url + "?testId=" + testId;
    
 	   form.action = url;    
 	   form.submit(); 
	}
</script>
</html>

testRegister.jsp(게시판 작성 페이지)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
	integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
	crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"
	integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"
	crossorigin="anonymous">

<!-- Latest compiled and minified JavaScript -->
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
	integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
	crossorigin="anonymous"></script>
<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
</head>
<body>
	<div class="container">
		<table class="table table-bordered">
			<thead>
				<h1>글쓰기</h1>
			</thead>
			<tbody>
				<form id="form_test" action="insertTest.do" method="post"
					encType="multiplart/form-data">
					<tr>
						<th>제목:</th>
						<td><input type="text" placeholder="제목을 입력하세요. "
							name="testTitle" class="form-control" /></td>
					</tr>
					<tr>
						<th>내용:</th>
						<td><textarea placeholder="내용을 입력하세요 . " name="testContent"
								class="form-control" style="height: 200px;"></textarea></td>
					</tr>
					<tr>
						<td colspan="2">
							<button id="btn_register" type="button" class="btn_register">등록</button>
							<button id="btn_previous" type="button" class="btn_previous">이전</button>
					</tr>

				</form>
			</tbody>
		</table>
	</div>


</body>
<script type="text/javascript">
	//글쓰기

	$(document).on('click', '#btn_register', function(e) {
		$("#form_test").submit();
	});

	//이전 클릭 시 testList로 이동
	$("#btn_previous").click(function javascript_onclikc() {
		$(location).attr('href', 'testList.do');
	});
</script>
</html>



testDetail.jsp(게시판 상세보기, 수정, 삭제 페이지)

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
	integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
	crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"
	integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"
	crossorigin="anonymous">

<!-- Latest compiled and minified JavaScript -->
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
	integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
	crossorigin="anonymous"></script>
<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
</head>
<body>
	<div class="container">
		<table class="table table-bordered">
			<thead>
				<h1>글 상세보기 Detail</h1>
			</thead>
			<tbody>
				<form action="updateTest.do" id="viewForm" method="post"
					encType="multiplart/form-data">
				<tr>
					<th>글번호:</th>
					<td><input name="testId" type="text" value="${result.testId}"
						class="form-control" readonly /></td>

				</tr>
				<tr>
					<th>제목:</th>
					<td><input type="text" value="${result.testTitle}"
						name="testTitle" class="form-control" /></td>
				</tr>
				<tr>
					<th>내용:</th>
					<td><textarea name="testContent" class="form-control"
							style="height: 200px;">${result.testContent}</textarea></td>

				</tr>
				<tr>
					<td colspan="2">
						<button id="btn_previous" type="button" class="btn_previous">이전</button>
						<button id="btn_delete" type="button" class="btn_previous">삭제</button>
						<button id="btn_modify" type="button" class="btn_register">수정</button>
				</tr>
				</form>
			</tbody>
		</table>
	</div>

</body>
<script type="text/javascript">
	$(document).on('click', '#btn_modify', function(e) {
		if(confirm("정말 수정하시겠습니까 ?") == true){
	        $("#viewForm").submit();
	    }
	    else{
	        return ;
	    }
	});
	$(document).on('click', '#btn_delete', function(e) {
		if(confirm("정말 삭제하시겠습니까 ?") == true){
			$("#viewForm").attr("action", "deleteTest.do");
			$("#viewForm").submit();
	    }
	    else{
	        return ;
	    }
	});

	//이전 클릭 시 testList로 이동
	$("#btn_previous").click(function javascript_onclikc() {
		$(location).attr('href', 'testList.do');

	});
</script>
</html>

 


test.css

@charset "UTF-8";

.testlist {
	width: 100%;
}

.list_table {
	width: 100%;
}

.list_table thead th {
	text-align: center;
	border-top: 1px solid #e5e5e5;
	border-bottom: 1px solid #e5e5e5;
	padding: 8px 0;
	background: #faf9fa;
}

.list_table tbody td {
	text-align: center;
	border-bottom: 1px solid #e5e5e5;
	padding: 5px 0;
}

.paging {
	margin-top: 30px;
	text-align: center;
}

.btn_write, .btn_register, .btn_previous {
	float: right;
	margin-right: 10px;
}

.pagination1 {
	margin-top: 60px;
	margin-left: 750px;
}

jo-coder.tistory.com/24

 

1. 게시판 만들기 환경설정 egovframework(전자정부프레임워크) springframework(스프링프레임워크)

extsdd.tistory.com/47?category=851852 [스프링/전자정부 프레임워크 입문] #1 프레임워크란? 전자정부 프레임워크를 알기 앞서 스프링 프레임 워크에 대해서 알아야 한다. 비전공자들도 쉽게 이해할 수 있

jo-coder.tistory.com


패키지 구조 


mariadb 

 

데이터베이스 egovtest에 test테이블을 만들고 

필요한 열들을 추가한다. 

키 id , 제목 , 내용, 작성자, 작성 날짜, 파일 이름 정도로 만들 것이다.

 


pom.xml

jeong-pro.tistory.com/168이 블로그에 maven과 pom.xml에 대한 설명이 잘 되어있다.

maven은 자바 프로젝트의 빌드(build)를 자동화해주는 빌드 툴이며

pom.xml은 프로젝트 내 빌드 옵션을 설정하는 부분이다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>test2</groupId>
	<artifactId>test2</artifactId>
	<packaging>war</packaging>
	<version>1.0.0</version>
	<name>test2</name>
	<url>http://www.egovframe.go.kr</url>

	<licenses>
		<license>
			<name>The Apache Software License, Version 2.0</name>
			<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
		</license>
	</licenses>

	<properties>
		<spring.maven.artifact.version>4.3.16.RELEASE</spring.maven.artifact.version>
		<egovframework.rte.version>3.8.0</egovframework.rte.version>
	</properties>

	<repositories>
		<repository>
			<id>mvn2</id>
			<url>http://repo1.maven.org/maven2/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>egovframe</id>
			<url>http://www.egovframe.go.kr/maven/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>egovframe2</id>
			<url>http://maven.egovframe.kr:8080/maven/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

	<dependencies>
		<!-- 표준프레임워크 실행환경 -->
		<dependency>
			<groupId>egovframework.rte</groupId>
			<artifactId>egovframework.rte.ptl.mvc</artifactId>
			<version>${egovframework.rte.version}</version>
			<exclusions>
				<exclusion>
					<artifactId>commons-logging</artifactId>
					<groupId>commons-logging</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>egovframework.rte</groupId>
			<artifactId>egovframework.rte.psl.dataaccess</artifactId>
			<version>${egovframework.rte.version}</version>
		</dependency>
		<dependency>
			<groupId>egovframework.rte</groupId>
			<artifactId>egovframework.rte.fdl.idgnr</artifactId>
			<version>${egovframework.rte.version}</version>
		</dependency>
		<dependency>
			<groupId>egovframework.rte</groupId>
			<artifactId>egovframework.rte.fdl.property</artifactId>
			<version>${egovframework.rte.version}</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<scope>provided</scope>
			<version>2.5</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<dependency>
			<groupId>taglibs</groupId>
			<artifactId>standard</artifactId>
			<version>1.1.2</version>
		</dependency>

		<dependency>
			<groupId>org.antlr</groupId>
			<artifactId>antlr</artifactId>
			<version>3.5</version>
		</dependency>




		<dependency>
			<groupId>com.googlecode.log4jdbc</groupId>
			<artifactId>log4jdbc</artifactId>
			<version>1.2</version>
			<exclusions>
				<exclusion>
					<artifactId>slf4j-api</artifactId>
					<groupId>org.slf4j</groupId>
				</exclusion>
			</exclusions>
		</dependency>



		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.31</version>
		</dependency>


		<!-- MariaDB 연동을 위해 아래 dependency 추가 -->
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<version>2.4.1</version>
		</dependency>
        <!-- Apache Baisc Data Source -->
		<dependency>
    		<groupId>commons-dbcp</groupId>
    		<artifactId>commons-dbcp</artifactId>
    		<version>1.4</version>
		</dependency>


	</dependencies>

	<build>
		<defaultGoal>install</defaultGoal>
		<directory>${basedir}/target</directory>
		<finalName>${artifactId}-${version}</finalName>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.tomcat.maven</groupId>
					<artifactId>tomcat7-maven-plugin</artifactId>
					<version>2.2</version>
					<configuration>
						<port>80</port>
						<path>/</path>
						<systemProperties>
							<JAVA_OPTS>-Xms256m -Xmx768m -XX:MaxPermSize=256m</JAVA_OPTS>
						</systemProperties>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<configuration>
						<source>1.8</source>
						<target>1.8</target>
						<encoding>UTF-8</encoding>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>hibernate3-maven-plugin</artifactId>
					<version>2.1</version>
					<configuration>
						<components>
							<component>
								<name>hbm2ddl</name>
								<implementation>annotationconfiguration</implementation>
							</component>
						</components>
					</configuration>
					<dependencies>
						<dependency>
							<groupId>org.hsqldb</groupId>
							<artifactId>hsqldb</artifactId>
							<version>2.3.2</version>
						</dependency>
					</dependencies>
				</plugin>
				<!-- EMMA -->
				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>emma-maven-plugin</artifactId>
					<version>1.0-alpha-3</version>
				</plugin>
				<!-- PMD manven plugin -->
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-pmd-plugin</artifactId>
					<version>3.1</version>
				</plugin>
			</plugins>
		</pluginManagement>
		<plugins>
			<!-- EMMA -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<configuration>
					<skipTests>true</skipTests>
					<forkMode>once</forkMode>
					<reportFormat>xml</reportFormat>
					<excludes>
						<exclude>**/Abstract*.java</exclude>
						<exclude>**/*Suite.java</exclude>
					</excludes>
					<includes>
						<include>**/*Test.java</include>
					</includes>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>emma-maven-plugin</artifactId>
				<inherited>true</inherited>
			</plugin>
			<!-- JavaDoc -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-javadoc-plugin</artifactId>
				<version>2.9.1</version>
			</plugin>
		</plugins>
	</build>
	<reporting>
		<outputDirectory>${basedir}/target/site</outputDirectory>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-project-info-reports-plugin</artifactId>
				<version>2.7</version>
				<reportSets>
					<reportSet>
						<id>sunlink</id>
						<reports>
							<report>javadoc</report>
						</reports>
						<inherited>true</inherited>
						<configuration>
							<links>
								<link>http://docs.oracle.com/javase/6/docs/api/</link>
							</links>
						</configuration>
					</reportSet>
				</reportSets>
			</plugin>
			<!-- JUnit Test Results & EMMA Coverage Reporting -->
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>emma-maven-plugin</artifactId>
				<inherited>true</inherited>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>surefire-report-maven-plugin</artifactId>
				<inherited>true</inherited>
				<reportSets>
					<reportSet>
						<reports>
							<report>report-only</report>
						</reports>
					</reportSet>
				</reportSets>
			</plugin>
			<!-- Generating JavaDoc Report -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-javadoc-plugin</artifactId>
				<configuration>
					<minmemory>128m</minmemory>
					<maxmemory>512m</maxmemory>
					<encoding>${encoding}</encoding>
					<docencoding>${encoding}</docencoding>
					<charset>${encoding}</charset>
				</configuration>
			</plugin>
			<!-- Generating Java Source in HTML -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jxr-plugin</artifactId>
				<configuration>
					<inputEncoding>${encoding}</inputEncoding>
					<outputEncoding>${encoding}</outputEncoding>
					<linkJavadoc>true</linkJavadoc>
					<javadocDir>apidocs</javadocDir>
				</configuration>
			</plugin>
		</plugins>
	</reporting>
</project>

 MariaDB연동을 위해 dependency 추가해주기 


context-datasource.xml

JDBC는 클라이언트의 요청마다 커넥션을 생성하고 닫기 때문에 그 과정에서 시간이 소모되고 서버 자원이 낭비될 수 있다. 그래서 이런 문제를 해결하기 위해 등장한 것이 "커넥션 풀(Conntection Pool)"이다.

클라이언트가 요청할 때마다 커넥션을 연결하는 것이 아니라, 웹 컨테이너가 실행할 때 "풀" 안에 미리 커넥션들을 만들고, DB 작업 시 "풀"에서 빌려 사용 후 다시 반납하는 것이다. DataSource는 커넥션 풀을 관리하므로 DataSource를 통해 커넥션을 얻거나 반납할 수 있다.   블로그:gbsb.tistory.com/69 

--마리아 디비 연결을 위한 설정--

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/jdbc  http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd">

	<bean id="dataSource"
		class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName"
			value="org.mariadb.jdbc.Driver" />
		<property name="url"
			value="jdbc:mariadb://127.0.0.1:3306/egovtest" />
		<property name="username" value="root" />
		<property name="password" value="비밀번호" />
	</bean>


</beans>

 


context-mapper.xml

DB 쿼리 이후 VO(Value Object) 객체에 대한 맵핑을
iBatis나 MyBatis 등을 사용해서 할 수 있도록 설정하는 것

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

	
	<bean
		class="egovframework.rte.psl.dataaccess.mapper.MapperConfigurer">
		<property name="basePackage" value="egovframework" />
	</bean>
	
	<!-- mybatis SqlSessionFactoryBean -->
	<bean id="sqlSessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- DB에 접속 하기 위해서 설정 -->
		<property name="dataSource" ref="dataSource" />
		<!-- myBatis 기본 설정 -->
		<property name="configLocation"
			value="classpath:/mybatis-config.xml" />
		<!-- query 적힌 xml 위치 -->
		<property name="mapperLocations"
			value="classpath:/egovframework/sqlmap/mappers/**/*Mapper.xml" />
	</bean>

	<bean id="sqlSession"
		class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg name="sqlSessionFactory"
			ref="sqlSessionFactory"></constructor-arg>
	</bean>

</beans>

mybatis-config.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	
	<settings>
		<!-- 자동으로 카멜케이스 규칙으로 변환 -->
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
	
	<typeAliases>
		<package name="egovframework.example.test.domain"/>
	</typeAliases>
	
</configuration>

 처음에 DB에 test_id 해놨다가 VO에도 test_id 해서 연결이 안 됬었다...  카멜케이스  test_id는 testId로 변환해줌 

나중에 귀찮아져서 DB도 카멜케이스로 바꿔줬음

 


testMappers.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="egovframework.example.test.service.TestMapper">

	<select id="selectTest" resultType="TestVO">
		SELECT
		*
		FROM test
		ORDER BY
		testId DESC

	</select>


</mapper> 

1)parameterType

 

Mapper 파일에 등록된 sql을 실행하려면 sql실행에 필요한 데이터를 외부로부터 받아야 한다. 이때 사용하는 속성이 parameterType속성이다.

 

이때 mybatis 메인 설정 파일에 등록된 <typeAlias>의 Alias를 사용하면 설정을 더 간결하게 처리할 수 있다.

pafameterType으로 지정된 클래스에는 사용자가 입력한 값들을 저장할 여러 변수가 있다. 변수들을 이용하여 sql 구문에 사용자 입력값들을 설정하는데, 이때 #{변수명}으로 사용한다. 

중요한 건 parameterType속성은 생략할 수 있으며 대부분 생략한다.

 

이 쿼리를 실행하기전에 파라미터타입에 객체를 집어 넣을 수 있다. 넣을 수 없다면, 저 쿼리를 봤을때 만약 Where 절에 testId가 1일때, 2일때, 3일때 똑같은 쿼리를 여러번 만들어 줘야하는데 파라미터로 가져온 TestVO안에 있는 저 변수명을 가지고 올 수 있는 것이다. 이 쿼리를 요청할때 함수 구문에 VO를 넣어주는 것이 보일텐데, 여기서 그 VO에 넣어져 있는 값을 사용할 수 있는 것이다. 쿼리문은 하나인데 들어오는 값들에 의해서 쿼리가 동적으로 변화하면서 아주 유연한 쿼링이 가능케하는 것이다. 이게 바로 MyBatis의 강점이라고 할 수 있다.

 

2)resultType 속성

 

검색 관련 sql문이 실행되면 ResultSet이 리턴되며, ResultSet에 저장된 검색 결과를 어떤 자바 객체에 매핑할지 지정해야 하는데, 이때 resultType속성을 사용한다

resultType 속성값으로도 Alias를 사용할 수 있다.

<select> 엘리먼트에서는 생략할 수 없는 필수 속성이다. 다만 resultMap 속성으로 대신 사용할 수 있다.

 

조회하고나서 어떤 데이터 형으로 뱉어줄지다. 머 String이라고 하면 문자열로 나가는등, 원하는 타입으로 선언하면 된다. 하지만, DB특성상 조회하면 여러 컬럼의 형태로 조회되기 때문에 Map형태로 뽑아줘야한다. 이 전자정부프레임워크에는 egovMap이라는 데이터형이 만들어져 있는데 Map을 쓰는게 가장 사용하기 좋다.

 

3)mapper namespace

 namespace는 DAO 구현체 쪽에서 원하는 mapper를 찾기 위해 사용 DAOimpl에서 직접 namespace 사용하는 방법이 있는데 나는 Mapper namespace와 ID를 연결할 TestMapperInterface를 두어서 interface를 호출하는 방법을 사용하였다.


public class TestVO {

	private static final long serialVersionUID = 1L;

	private int testId;
	private String testTitle;
	private String testContent;
	private String testName;
	private String testDate;
    }
    //getter setter

TestVO 

  • 자바와 같은 객체 지향 프로그램에서는 데이터 자체는 외부에서 접근을 할 수 없도록 하고, 메소드만 공개해서 이 중 유효한 값들을 데이터로 저장한다
    • 즉,  객체들이 데이터(필드)를 외부에서 직접적으로 접근하는 것을 막아놓음
  • 필드들은 private 접근 제한자로 막아두고, 각 필드의 Getter, Setter로 접근하는 방식을 취함
    • Getter, Setter는 클래스의 특성중 정보 은닉(Hiding information)을 잘보여줌
  • 이렇게 프로그래밍을 하는 이유는 객체의 무결성을 보장하기 위함

generate Getters  and Setter 하자


package egovframework.example.test.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestService;

@Controller
public class TestController {

	@Autowired
	private TestService testServiceImpl;

	@RequestMapping(value = "/testList.do")
	public String testListDo(TestVO testVO, Model model) throws Exception {

		model.addAttribute("list", testServiceImpl.selectTest(testVO));
		return "test/testList";
	}
}

TestController

 

@Autowired가 적용된 의존 주입 대상에 대해서는
<property> 태그나 <constructor-arg> 태그를 사용하지 않아도 스프링이 알맞게 의존 객체를 주입해준다. 

의존관계를 자동으로 연결해주는 기능을 가진 어노테이션

@Autowired의 경우에는 스프링에서 등장한 어노테이션입니다. 따라서 스프링 이외에서는 사용 할 수 없습니다

@Autowired, @Resource, @Inject의 차이blog.naver.com/PostView.nhn?blogId=platinasnow&logNo=220053030295

 

testList.do에 매핑되게 하였고 

함수 이름은 TestListDo이며

TestVO와 Model을 매개변수로 전달받는다.

매개변수로 전달받은 model.addAttribute("key", "value"); 메소드를 이용하여 view 전달할 데이터를 key, value쌍으로 전달할 수 있다.

 

요청이 오면 TestVo에 담아서 testVO라는 이름으로 이 함수를 내부에서 사용할 것이고 

model을 만들어놓고 로직에 처하면서 여기에 사용자가 볼 화면으로 보내줄 데이터를 모아놓을것 이라 해석하면 되겠다.

 

throws Exception 구문은 만약 에러가 발생할시 작동하는 함수인데 보통 서버 Log에 해당 사항을 기록한다.

 

 

Spring Model, ModelMap, ModelAndView 차이점 >>>  javaoop.tistory.com/56


package egovframework.example.test.service;

import java.util.List;

import egovframework.example.test.domain.TestVO;

public interface TestService {

	public List<TestVO> selectTest(TestVO testVO) throws Exception;
    }

TestService


package egovframework.example.test.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import egovframework.example.test.dao.TestDAO;
import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestService;

@Service("testServiceImpl")
public class TestServiceImpl implements TestService {


	
	@Autowired
	private TestDAO testDAOService;
	
	@Override
	public List<TestVO> selectTest(TestVO testVO) throws Exception {		
		return testDAOService.selectTest(testVO);
	}
}

TestServiceImpl

 

 * extends
부모에서 선언 / 정의를 모두하며 자식은 메소드 / 변수를 그대로 사용할 수 있음
 * implements (interface 구현)
부모 객체는 선언만 하며 정의(내용)은 자식에서 오버라이딩 (재정의) 해서 사용해야함


package egovframework.example.test.dao;

import java.util.List;

import egovframework.example.test.domain.TestVO;

public interface TestDAO {

	List<TestVO> selectTest(TestVO testVO) throws Exception;

}

TestDAO

Data Access Object의 약어로 실질적으로 DB에 접근하여 데이터를 조회하거나 조작하는 기능을 전담하는 객체를 말한다.
DAO의 사용 이유는 효율적인 커넥션 관리와 보안성 때문이다. 
DAO는 저수준의 Logic과 고급 비즈니스 Logic을 분리하고 domain logic으로부터 DB관련 mechanism을 숨기기 위해 사용한다.


package egovframework.example.test.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import egovframework.example.test.domain.TestVO;
import egovframework.example.test.service.TestMapper;

@Service("testDAOService")
public class TestDAOService implements TestDAO {

	@Autowired
	private SqlSession sqlSession;
	
	public List<TestVO> selectTest(TestVO testVO) throws Exception {
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		return mapper.selectTest(testVO);
	}
}

TestDAOService

SqlSession 객체를 통해 interface TestMapper에 접근, testMapper 에 작성해 놓은 SQL문을 실행 할 수 있음
DB 접근에 필요한 connection을 관리를 DAO가 처리하게 됨.
(DAO에서 간단하게 SqlSession 을 주입받아 처리 했는데, 이것이 바로 DB Connection을 알아서 관리해 주는 객체 입니다.

 


package egovframework.example.test.service;
//매퍼의 namespace와는 경로를 맞추어야 한다. 
import java.util.List;

import egovframework.example.test.domain.TestVO;
//query 적힌 xml 위치 
//	<property name="mapperLocations"
//	value="classpath:/egovframework/sqlmap/mappers/**/*Mapper.xml" />
//</bean>
//Mapper namespace 와 ID를 연결할 Interface 를 두어서 interface를 호출하는 방법.
//Mybatis 매핑XML에 기재된 SQL을 호출하기 위한 인터페이스이다. Mybatis3.0부터 생겼다.
//SQL id는 인터페이스에 정의된 메서드명과 동일하게 작성한다
public interface TestMapper {

	// 게시물 리스트 조회
	public List<TestVO> selectTest(TestVO testVO) throws Exception;
}

TestMapper


<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>

<head>
<!-- Bootstrap CSS -->

<link rel="stylesheet"
	href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
	integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
	crossorigin="anonymous">

<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>

</head>

<body>

	<h1>게시판List</h1>

	<div class="testlist">
		<form id="boardForm" name="boardForm" method="post">
			<table class="list_table">
				<colgroup>
					<col width="20%" />
					<col width="50%" />
					<col width="15%" />
					<col width="15%" />
				</colgroup>
				<tbody>
				<thead>

					<tr>
						<th>번호</th>
						<th>제목</th>
						<th>작성자</th>
						<th>등록일자</th>
					</tr>
				</thead>

<!-- jstl 데이터베이스를 검색해 넘겨 받은 list 를 result 라는 이름으로 순차적으로 실행을 시키게 됨  java의 for문같이 순차적으로 실행시킴-->

				<c:forEach items="${list }" var="result">
					<tr>
						<td><c:out value="${result.testId}" /></td>
						<td><c:out value="${result.testTitle}" /></td>
						<td><c:out value="${result.testName}" /></td>
						<td><c:out value="${result.testDate}" /></td>
					</tr>
				</c:forEach>
				</tbody>
			</table>
		</form>
	</div>
	<br>
</body>

</html>

testList.jsp


 


이까지 마리아디비의 연결과 게시물 조회까지 되었다.

 

연습한 게시판 코드에서 코드를 지워서 넣다 보니 이상한 부분이 많은데 다음에 참고할 때 고쳐가며 참고해야 한다.

 

그래서 이미 데이터도 아주 많이 들어가 있다...ㅎㅎ 

 

extsdd.tistory.com/47?category=851852

 

[스프링/전자정부 프레임워크 입문] #1 프레임워크란?

전자정부 프레임워크를 알기 앞서 스프링 프레임 워크에 대해서 알아야 한다. 비전공자들도 쉽게 이해할 수 있도록 예시와 비유를 들어 설명하도록 하겠다. 먼저 프레임워크에 대해서 이해해야

extsdd.tistory.com

이 블로그에 정리가 참 잘 되어있다.

 

 

 

요약하자면

 

springframework는 JAVA플랫폼 기반 프레임워크 (웹서버, 어플리케이션 개발에 특화)

 

전자정부 프레임워크 = springframework + 기업/기관에 특화

 


이제 게시판을 만들기 위한 각종 환경들을 설치하자

 

1. JDK 설치 ( JDK란? Java Development Kit, JDK = JAVA코드를 프로그램으로 바꿔줌

    jo-coder.tistory.com/6?category=413574 (JDK 설치 방법)

 

2. 전자정부프레임워크 설치 

 

3. 톰캣 설치 (톰캣이란?  extsdd.tistory.com/79?category=853192(톰캣에 대한 설명)) 

  jo-coder.tistory.com/7?category=413574 (톰켓 설치 방법)

 

4. 혹시나 maven settings.xml의 경로가 맞는지 확인해주자 (처음 만들 때 경로 잘못되어있어서 에러남)


이번엔 egovframework에서 제공하는 만들어진 게시판을 띄워보자 

(그 이유는 미리 세팅되어있는 환경에서 필요없는 파일들은 지우고 그 위에 내가 다시 게시판을 만들 거임)

 

1. 오른쪽 egovframework 클릭

 

2. File - new -  egovFrame Web Project 클릭 

 

3. Project_name : 본인의 프로젝트 이름 써주고 

  Target Runtime : 톰캣 연결하고 

  Dynamic Web Module Version : 디폴트 값 2.5로 했는데  http://tomcat.apache.org/whichversion.html

 

Apache Tomcat® - Which Version Do I Want?

Apache Tomcat® is an open source software implementation of a subset of the Jakarta EE (formally Java EE) technologies. Different versions of Apache Tomcat are available for different versions of the specifications. The mapping between the specifications

tomcat.apache.org

여기서  Servlet Spec과 톰캣 버전 등을 보며 알맞게 설정해줘야 한다 (샘플 예제를 사용하려면 2.5를 사용해야 한다.)

 

 

Group id : com. 회사 이름으로 보통 명명한다. 나는 그냥 Project_name이랑 똑같이 설정함 

Artifact id : 프로그램 이름이다. Project_name이랑 똑같음

Version : 최초 생성이기 때문에 1.0.0부터 시작한다.

 

4. 다 설정했으면 next를 눌러 Generate Example을 체크 후 finish

 

5. 이제 서버 돌리면 샘플 게시판 예제가 나올 것이다.

 


이제 필요 없는 sample 파일들과 경로들을 수정하자 

 

haenny.tistory.com/67

 

[eGovFramework] Spring 기반 웹 프로젝트 만들기(2) : sample 삭제

[eGovFramework] Spring 기반 웹 프로젝트 만들기(2) : sample 삭제 Spring 기반 웹 프로젝트 만들기(1) [eGovFramework] Spring 기반 웹 프로젝트 만들기(1) : 프로젝트 생성 [eGovFramework] Spring 기..

haenny.tistory.com

이 블로그에 정리가 잘 되어있다. 수정할 부분은 수정하자.


마지막으로 테스트로 jsp 페이지 연결이다 

 

패키지명(참고로 나는 위의 블로그를 그대로 하지 않고 샘플의 패키지명을 사용하였다.)

egovframework.example.test.web    _TestController.java

package egovframework.example.test.web;

@Controller
public class TestController {

	@RequestMapping(value = "/testList.do")
	public String testListDo() {
    
		return "test/testList";
	}

 

WEB-INF/jsp/egovframework/example/test/testList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
hi
</body>
</html>

 

 

톰캣 서버 더블클릭 후 Path설정을 /로 해주고 

 

Window - Preferences - General - Web Browser - 크롬으로 

 

 

서버 스타트하고 

크롬 url에  http://localhost:8080

 

hi가 나오면 성공 

 

혹시나 에러 난다면 Project Clean도 해주자(404가 많이 떴는데 클린 하니까 돌아갈 때가 많았다. )

이클립스가 사용하고 있던 정보나 class를 전부 삭제하고 다시 만드는 것인데이렇게 함으로써 이클립스가 빌드 중에 꼬인걸 다시 새로 정리한다고 한다.

 


 

 

egovframework를 사용한다는 것은 

 

1. /WEB-INF/lib 폴더 아래에 egovframework.rte로 시작하는. jar 파일 존재 여부

2. import egovframework.rt 라는 구문이 검색되는 소스코드가 있는지

3. 상기 소스코드 중 EgovAbstractDAO(EgovMapperDAO) 또는 EgovAbstractServiceImpl(또는

    AbstractServiceImpl1)) 클래스를 상속한 구문이 존재하는지 확인

4. ibatis / Mybatis를 활용하는지

MyBatis나 iBatis를 사용한다면 

DAO 클래스(@Repository) EgovAbstractDAO(iBatis) 또는 EgovAbstractMapper(MyBatis)를 상속해야 한다.

 

 

이게 적용되지 않으면 전자정부 프레임워크 요건에 어긋난다는데... 

 

연습용으로 만들고 보니 그냥 egovframework에 덮어쓴 springframework였다.....   

저번에 이어서 다 못한 Spring MVC패턴을 공부하겠다.


우선 간단하게 View를 만든다.

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<html>
<body>
<form action="bmi" method="post">
	키<input type="text" name="t1"/>cm<br>
	몸무게<input type="text" name="t2"/>kg<br>
	<input type="submit" value="비엠아이"> 
</form>
</body>
</html>

POST 방식을 사용한 이유 - POST 방식은 GET 방식과 달리 데이터 전송을 기반으로 한 요청 메서드이기 때문이다.

 

다음으로는 그 뒤로 넘어갈 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>
</head>
<body>
	 
	키<%=request.getAttribute("num1") %><br>
	몸무게<%=request.getAttribute("num2") %><br>
	bmi<%=request.getAttribute("result") %><br>
	입니다.  
</body>
</html>

 

JSP태그

<% %>        java code를 넣는 태그이다.

<%= %>      출력하기 위한 태그이다. out.println이랑 비슷한 기능을 함.

<%@ %>    지시자 태그이고 Web container가 JSP를 Servlet으로 변환할 때 특정 지시를 내리기 위해서 사용하는 태그

 

 

request와 response

클라이언트가 서버쪽으로 요청하는 것은 request

서버가 클라이언트에게 응답하는 것은 response

 

get과 set

get은 데이터를 빼내 오는 것

set은 데이터를 입력하는 것

 

request.setParameter()와 requestsetAttribute()

request.setParameter() get Parameter는 String의 값 밖에 받을 수 없다

requestsetAttribute() getAttribute는 List를 받기 위해 사용한다 (type이 Object이기 때문에 형변환 필요!)

 

결론적으로
출력 태그를 이용하여 클라이언트가 서버쪽으로 요청하며

num1이라는 인자값을 get 하라는 것이다.

Object타입으로

 


Spring Bean Configuration File

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
		<!-- 자바 어노테이션 기반의 컨피규레이션 설정하겠다는 뜻 -->
		<context:annotation-config></context:annotation-config>
		<!-- @Controller, @Component, Repository, @ service등 
			스프링 컴포넌트 등록할 패키지(멀티 설정)-->
		<context:component-scan base-package="com.yadoc"></context:component-scan>
</beans>

 

yadoc-servlet.xml 파일이고 Spring MVC설정파일 이라는 것.

XML 스키마 설정: Spring Beans, Context, MVC...

 

왜 저렇게 파일을 만들고 어떤 설정들이라는 것인지 이해가 안된다 ㅜㅜ

패키지를 설정해준다는 것과 무언가 여러가지 설정들이라는 것...  다음에 보완해야겠다.


기본적인 흐름은 client 가 요청을 하면 @Controller에 진입하고 그 요청에 대해 작업을 수행 후 뷰로 데이터를 전달한다.

우선 @Controller를 이용해서 클래스를 생성하고 @RequestMapping을 이용해 view의 요청 경로를 지정한다.

요청 처리 메서드를 구현하고 뷰 이름을 리턴한다.

package com.yadoc;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller  
public class AddBmi {
	
	@RequestMapping("/bmi")			
	public ModelAndView add(
		HttpServletRequest request,
		HttpServletResponse response) {

		double i = Double.parseDouble(request.getParameter("t1"));
		double j =  Double.parseDouble(request.getParameter("t2"));
		double k = j/((i*i)*0.0001);
		
		ModelAndView mv = new ModelAndView();
		mv.setViewName("display.jsp");
		mv.addObject("result",k);
		mv.addObject("num1",i);
		mv.addObject("num2",j);
		
		return mv;
	}
}

 

 

@Controller   (이것이 컨트롤러라고 자기 주장 하는 것이다.)

전통적인 Spring MVC의 컨트롤러는 주로 View를 반환하기 위해 사용한다.

  1. Client는 URI 형식으로 웹 서비스에 요청을 보낸다.
  2. Mapping 되는 Handler와 그 Type을 찾는 DispatcherServlet이 요청을 인터셉트한다.
  3. Controller가 요청을 처리한 후에 응답을 DispatcherServlet으로 반환하고, DispatcherServlet은 View를 사용자에게 반환한다.

@RequestMapping

bmi라는 요청에 대한 맵핑된 메서드 이다. jsp의 bmi 요청을 처리한다.

 

 

ModelAndView

Controller의 처리 결과를 보여줄 view와 view에서 사용할 값을 전달하는 클래스이다.

 

HttpServletRequest, HttpServletResponse

WAS는 웹 브라우저로부터 Servlet요청을 받으면

요청할 때 가지고 있는 정보를 HttpServletRequest 객체를 생성하여 저장

웹 브라우저에게 응답을 보낼 때 사용하기 위하여 HttpServletResponse객체를 생성

생성된 HttpServletRequest, HttpServletResponse 객체를 서블릿으로 전달

 

 

mv객체를 생성.

setViewName() 메소드 ("뷰의 경로") 안에 뷰의 경로를 설정한다.

addObject() 메소드 데이터를 보낼 때 사용한다.  ("변수 이름", "데이터 값")

 

 

그리고 return mv;

ModelAndView 객체를 반환한다.

return 이란 메서드의 종료, 값의 반환의 역할을 한다. 메서드를 종료함과 동시에 호출한 부분으로 반환한다.

 

 

 

그렇게 display.jsp가 실행된다.

 

 

 


https://imdsoho.tistory.com/entry/requestsetAttribute%EC%99%80-requestgetAttribute%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C

https://gap85.tistory.com/entry/%EC%95%A1%EC%85%98%ED%83%9C%EA%B7%B8

https://mommoo.tistory.com/60
https://mangkyu.tistory.com/49
https://andamiro25.tistory.com/148

'Spring Framework > spring공부' 카테고리의 다른 글

Spring MVC패턴  (2) 2020.06.14

+ Recent posts