TIL/academy

국비 TIL(Today I Learned) 20220822 ajaxForm으로 파일 첨부, 다운받기

토희 2022. 8. 22. 15:07
728x90

 Fileupload : 사용자 컴퓨터의 파일을 서버에 올림

샘플 스프링에 예시가 있음!~

 

input type="file"로 할려고 하면,

enctype이 필요

 

 

 

 

기존

serialize()는 파일이 안 넘어가서 추가 기능 필요

 

 

form의 객체 가져옴, ajaxFORM 이 있음

 

 

을 해야 ajax로 동작을 함

https://yoondeng.tistory.com/41

 

[ajax] jQuery ajaxForm 사용하기

프로젝트를 진행하던 중 파일을 업로드 기능을 만들게 되었다. 항상 form submit을 사용하다가 이번 프로젝트의 경우 비동기식으로 처리를 하였다. ajax 처리를 찾아보던 중 ajaxForm이라는 블라블라

yoondeng.tistory.com

 

Controller

 

 

FILE_EXT에서 F3누르면

확장자 제한을 두셨데

 

 

파일 업로드 경로

여기에 올라감

 

 

 

심볼릭 링크 : 바로가기와 비슷하나 물리적으로 직결시켜 연결시킴으로 공간적 효율을 구현함

                위치정보

폴더a  --------------------------------------- 폴더a 바로가기 

사용자가 폴더a 바로가기를 요청하면 위치정보로 폴더a반환

                위치정보+ 폴더a로 인식

폴더a  --------------------------------------- 폴더a 심볼릭

폴더a와 폴더a 심볼릭을 동일시화함, 사용자가 요청하면 폴더a가 폴더a 심볼릭 반환, 폴더a 심볼릭를 사용자에게 반환

 

심볼릭 링크

톰캣이 C드라이브에서 돌고있으면은 D드라이브에게 접근 가능, 파일은 D드라이브에 쌓이고, C드라이브에는 톰캣만 돌고, 공간적 효율 올라감

 

프로젝트할때는 심볼릭 링크 쓸일은 없데

 

 

getPrimaryKey()에서 F3누르면 해당 메서드로 감

날짜 + 랜덤값 생성해서 동일한 파일명이 들어오면 덮어씌워지는것을 방지

 

 

 

transferTo

사실은 jsp파일 업로드 방식이 여러개가 있데

jsp 파일 업로드 cos.jar 이런것도 있고, cos.jar 는 어노셋이라든가 추가 설정이 더 필요한데

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=heartflow89&logNo=221009083830 

 

[JSP] cos.jar 이용 파일 업로드 구현 방법

JAVA에서는 기본적으로 파일을 업로드하는 기능을 제공하지 않는다. 업로드 기능을 개발자가 구현하여 ...

blog.naver.com

transferTo는 스프링 셋팅이 따라서 따로 설정을 해줄게 없뎅

 

 

 

경로지정하기

upload폴더 없으면 만들면 된데

 

C:\MyWork\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\SampleSpring\resources\upload

경로 설정

 

 

파일이름이 날짜 + 랜덤하게 만들어짐

 

 

아까 그 폴더로 가면 실제 파일이 올라와져 있음

 

경로설정 끝난 이후에는 뭐가 가능해지냐면

ck에디터 붙인거에서 이미지 파일 업로드가능

이렇게 바탕화면에 있는 사진업로드하면(사용자)

 

 

우리가 지정해준 경로(서버)에 사진도 올라감

 

 

 

 

기존 TBOARD 테이블 수정 ATT 추가함

DB에는 파일명만 올라가게끔 할 예정

 

list.jsp

http://localhost:8090/SampleSpring/ATInsert 경로

form.js 추가

 

insert.jsp 스크립트 부분 수정

<script type="text/javascript">
$(document).ready(function () {
	// 에디터 연결
	// CKEDITOR.replace(아이디, 옵션)
	CKEDITOR.replace("con", {
		resize_enabled: false, // resize_enabled : 크기조절기능 활용여부
		language : "ko", // 사용언어
		enterMode: "2", // 엔터키처리방법. 2번이면 <br/>
		width : "100%", // 숫자일경우 px, 문자열일경우 css크기
		height : 400
	});
	
	$("#listBtn").on("click", function () {
		$("#backForm").submit();
	});
	
	$("#insertBtn").on("click", function () {
		// CKEditor의 값 취득
		// CKEDITOR.instances[아이디] : CKEditor중 아이디가 같은 것을 찾겠다.
		//.getData() : 작성중인 내용을 취득하겠다.
		$("#con").val(CKEDITOR.instances['con'].getData());
		
		if($.trim($("#title").val()) == ""){
			makeAlert("알림","제목 입력하세요." , function () {
				$("#title").focus();
			})
		} else if($.trim($("#con").val()) == ""){
			makeAlert("알림","내용을 입력하세요." , function () {
				$("#con").focus();
			})
		} else {
			// 1. 파입업로드 -> 2. 업로드 파일명 취득 -> 3. 글 저장
			// 폼 객체 취득
			var form = $("#actionForm");
			// ajaxForm 적용
			form.ajaxForm({
				success: function (res) { // 데이터 주고 받기 성공시
					if(res.result == "SUCCESS"){ // 파일 전송 성공
						// 올라간 파일이 존재한다면, 첨부파일이 없을 수도 있으니 조건문 처리
						if(res.fileName.length > 0){
							$("#att").val(res.fileName[0]); // 올라간 파일명 보관		
						}
						// 3번 단계 글저장 시작, 기존테이터
						var params = $("#actionForm").serialize();
						
						$.ajax({
							url : "ATAction/insert", //restful api라는게 여기서 시작한다고?
							type : "POST", 
							dataType: "json", 
							data: params, 
							success : function(res) { 
								switch(res.msg){
								case "success" : 
									location.href ="ATList";
									break;
								case "fail" :
									makeAlert("알림" , "등록에 실패하였습니다.");
									break;
								case "error" :
									makeAlert("알림" , "등록 중 문제가 발생하였습니다.");
									break;
								}
							},
							error : function(request, status, error) { 
								console.log(request.responseText); 
							}
						});  // 글저장 끝
						
					} else { // 문제발생
						makeAlert("알림", "파일 업로드에<br/>문제가 발생하였습니다.")
					}
				},
				error: function () { // 에러시
					makeAlert("알림", "파일 업로드에<br/>문제가 발생하였습니다.")
				}
			}); // ajaxForm 설정 끝
			
			
			// ajaxForm 실행
			form.submit();
			
		}
	});
});

</script>

 

sql도 수정

 

 

첨부를 하면은 파일이름이 들어옴

 

 

 

js의 typeOf 

첨부파일이 있으면은 아이콘 띄울려고

 

 

 

detail.jsp

상세보기에 파일이름이 보임

근디 이름을 다 보여야 하나, 내가 올린파일명으로 보일수있게 할수없는지

 

DB에서 처리할수도 있고

jsp에서 처리할수도 있고

 

DB에서 처리는 쉬우니까 jsp에서 처리하는법

 

 

 

update.jsp

경우의 수가 많아서 어렵

첨부파일이 들어가있을때와 없을때가 있음

updateBtn 스크립트 부분

$("#updateBtn").on("click", function () {
		// CKEditor의 값 취득
		// CKEDITOR.instances[아이디] : CKEditor중 아이디가 같은 것을 찾겠다.
		//.getData() : 작성중인 내용을 취득하겠다.
		$("#con").val(CKEDITOR.instances['con'].getData());
		
		if($.trim($("#title").val()) == ""){
			makeAlert("알림","제목 입력하세요." , function () {
				$("#title").focus();
			})
		} else if($.trim($("#con").val()) == ""){
			makeAlert("알림","내용을 입력하세요." , function () {
				$("#con").focus();
			})
		} else {
			// 1. 파입업로드 -> 2. 업로드 파일명 취득 -> 3. 글 저장
			// 폼 객체 취득
			var form = $("#actionForm");
			// ajaxForm 적용
			form.ajaxForm({
				success: function (res) { // 데이터 주고 받기 성공시
					if(res.result == "SUCCESS"){ // 파일 전송 성공
						// 올라간 파일이 존재한다면, 첨부파일이 없을 수도 있으니 조건문 처리
						if(res.fileName.length > 0){
							$("#att").val(res.fileName[0]); // 올라간 파일명 보관		
						}
						// 3번 단계 글저장 시작, 기존테이터
						var params = $("#actionForm").serialize();
						
						$.ajax({
							url : "ATAction/update", //restful api라는게 여기서 시작한다고?
							type : "POST", 
							dataType: "json", 
							data: params, 
							success : function(res) { 
								switch(res.msg){
								case "success" : 
									$('#backForm').submit();
									break;
								case "fail" :
									makeAlert("알림" , "등록에 실패하였습니다.");
									break;
								case "error" :
									makeAlert("알림" , "등록 중 문제가 발생하였습니다.");
									break;
								}
							},
							error : function(request, status, error) { 
								console.log(request.responseText); 
							}
						});  // 글저장 끝
						
					} else { // 문제발생
						makeAlert("알림", "파일 업로드에<br/>문제가 발생하였습니다.")
					}
				},
				error: function () { // 에러시
					makeAlert("알림", "파일 업로드에<br/>문제가 발생하였습니다.")
				}
			}); // ajaxForm 설정 끝
			
			
			// ajaxForm 실행
			form.submit();
		}
	});

 

처음에 

이런 오류가 떠서 왜인자 했더니 c:choose 바로 밑에 주석 넣을수없음

https://hclee2575.tistory.com/133

 

[JSP] Illegal text inside "c:choose" tag: JSTL오류

출처 http://m.blog.naver.com/sontaemoo/70106360327 원래 소스는 아래와 같았다.             이렇게 사용하니 choose 태그 밑의 바로 밑 부분 주석을 인식을 못했다. 그래서 아래와 같이 바꾸니 아주..

hclee2575.tistory.com

수정됨, 파일 삭제하고 다시 올려도 되고,

 

https://blog.munilive.com/posts/input-file-type-accept-attribute.html

 

파일 첨부 시 특정 파일 확장자만 선택하게 하기

태그의 accept 속성을 이용해서 첨부파일 추가 시 특정 파일만 선택 가능하도록 하는 방법에 대한 글이다.

blog.munilive.com

input type="file" accept="파일 확장자|audio/*|video/*|image/*|미디어 타입">

파일 확장자 지정해줄수 있음, but 모든 파일 살아있음

 

 

 

강사님 샘플파일

fileUpload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>FileUpload Test</title>
<link rel="shortcut icon" href="resources/favicon/favicon.ico">
<link rel="stylesheet" type="text/css" href="resources/css/common/cmn.css" />
<style type="text/css">
.wrap {
	width: 700px;
	margin: 0 auto;
}

.btn_wrap {
	height: 30px;
}

#uploadInfo {
	margin-top: 10px;
	padding: 5px;
	width: calc(100% - 10px);
	height: 80px;
	overflow: auto;
	border: 1px solid #7b7b7b;
	font-size: 10.5pt;
}
</style>
<script type="text/javascript" 
		src="resources/script/jquery/jquery-1.12.4.min.js"></script>
<script type="text/javascript" 
		src="resources/script/jquery/jquery.form.js"></script>
<script type="text/javascript" 
		src="resources/script/common/common.js"></script>
<script type="text/javascript">
$(document).ready(function() {
	$("#uploadBtn").on("click", function(){
		var fileForm = $("#fileForm");
		// ajaxForm : form의 동작을 동기화에서 비동기화 방식으로 변경
		fileForm.ajaxForm({
			// res 파일이름들이 담겨져있음
			success: function(res){
				if(res.result =="SUCCESS"){
					alert("저장완료");
					
					console.log(res);
					if(res.fileName.length > 0) {
						let html = "";
						for(file of res.fileName) {
							html += "<a href=\"resources/upload/" + file + "\" download>" + file + "</a><br/>";
						}
						
						$("#uploadInfo").append(html);
					}
					
				} else {
					alert("저장실패");
				} 
			}, //ajax error
			error: function(){
				alert("에러발생!!"); 
			}
		});
		
		fileForm.submit();
	});
});
</script>
</head>
<body>
<div class="cmn_title">파일 업로드/다운로드 샘플<div class="cmn_btn_mr float_right_btn" id="sampleListBtn">샘플목록</div></div>
<!-- 
	enctype : form전송 데이터 형태
	multipart : 파일데이터를 포함한 형태
 -->
<div class="wrap">
<form id="fileForm" name="fileForm" action="fileUploadAjax" 
	  method="post" enctype="multipart/form-data">
<div class="headline">첨부파일</div>
<table class="board_detail_table">
	<colgroup>
		<col width="100" />
		<col width="600" />
	</colgroup>
	<tbody>
	<tr>
		<th>첨부파일1</th>
		<td class="board_cont_left"><input type="file" name="attFile1" size="85" /></td>
	</tr>
	<tr>
		<th>첨부파일2</th>
		<td class="board_cont_left"><input type="file" name="attFile2" size="85" /></td>
	</tr>
	<tr>
		<th>첨부파일3</th>
		<td class="board_cont_left"><input type="file" name="attFile3" size="85" /></td>
	</tr>
	</tbody>
</table>
</form>
<div class="btn_wrap">
	<div class="cmn_btn_ml float_right_btn" id="uploadBtn">저장</div>
</div>
<div class="headline">첨부결과</div>
<div id="uploadInfo"></div>
</div>
</body>
</html>

 

FileController.java, 우리는 fileUploadAjax 만 씀

package com.spring.sample.common.controller;

import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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

import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.sample.common.CommonProperties;
import com.spring.sample.util.Utils;

@Controller
public class FileController {
	@RequestMapping(value = "/fileUploadAjax", method = RequestMethod.POST, produces = "text/json;charset=UTF-8")
	@ResponseBody
	public String fileUploadAjax(HttpServletRequest request, ModelAndView modelAndView) throws Throwable {
		ObjectMapper mapper = new ObjectMapper();
		HashMap<String, Object> modelMap = new HashMap<String, Object>();

		/* File Upload Logic */
		// 다운 캐스팅 한거래 넘겨 받은 request를 multipartRequest형태로 변환
		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;

		String uploadExts = CommonProperties.FILE_EXT;
		String uploadPath = CommonProperties.FILE_UPLOAD_PATH;
		String fileFullName = "";

		// 폴더 또는 파일을 나타냄
		File folder = new File(uploadPath);

		if (!folder.exists()) { // 폴더 존재여부, 폴더 존재하지 않으면
			folder.mkdir(); // 폴더 생성
		}

		List<String> fileNames = new ArrayList<String>();
		try {
			@SuppressWarnings("rawtypes") // 노란줄 보기 싫어서 달아준거
			final Map files = multipartRequest.getFileMap();
			Iterator<String> iterator = multipartRequest.getFileNames();

			while (iterator.hasNext()) {
				String key = iterator.next();
				MultipartFile file = (MultipartFile) files.get(key);
				if (file.getSize() > 0) {
					String fileRealName = file.getOriginalFilename(); // 실제파일명
					String fileTmpName = Utils.getPrimaryKey(); // 고유 날짜키 받기
					String fileExt = FilenameUtils.getExtension(file.getOriginalFilename()).toLowerCase(); // 파일
					// 확장자추출

					if (uploadExts.toLowerCase().indexOf(fileExt) < 0) {
						throw new Exception("Not allowded file extension : " + fileExt.toLowerCase());
					} else {
						// 물리적으로 저장되는 파일명(실제파일명을 그대로 저장할지 rename해서 저장할지는 협의 필요)
						fileFullName = fileTmpName + fileRealName;
						// File(경로) - 폴더
						// File(경로, 파일명) - 파일
						// new File(new File(uploadPath), fileFullName)
						// uploadPath경로의 폴더에 fileFullName 이름의 파일
						// 파일.transferTo(새파일) - 새파일에 파일의 내용을 전송
						file.transferTo(new File(new File(uploadPath), fileFullName));

						fileNames.add(fileFullName);
					}
				}
			}

			modelMap.put("result", CommonProperties.RESULT_SUCCESS);
		} catch (Exception e) {
			// 공통 Exception 처리
			e.printStackTrace();
			modelMap.put("result", CommonProperties.RESULT_ERROR);
		}

		modelMap.put("fileName", fileNames);

		return mapper.writeValueAsString(modelMap);
	}

	@RequestMapping(value = "/imageUploadAjax", method = RequestMethod.POST, produces = "text/json;charset=UTF-8")
	@ResponseBody
	public String imageUploadAjax(HttpServletRequest request, ModelAndView modelAndView) throws Throwable {
		ObjectMapper mapper = new ObjectMapper();
		HashMap<String, Object> modelMap = new HashMap<String, Object>();

		/* File Upload Logic */
		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;

		String uploadExts = CommonProperties.IMG_EXT;
		String uploadPath = CommonProperties.FILE_UPLOAD_PATH;
		String fileFullName = "";

		File folder = new File(uploadPath);

		if (!folder.exists()) { // 폴더 존재여부
			folder.mkdir(); // 폴더 생성
		}

		List<String> fileNames = new ArrayList<String>();
		try {
			@SuppressWarnings("rawtypes")
			final Map files = multipartRequest.getFileMap();
			Iterator<String> iterator = multipartRequest.getFileNames();

			// 첨부파일 있을때
			while (iterator.hasNext()) {
				String key = iterator.next(); // 파일 취득
				MultipartFile file = (MultipartFile) files.get(key); // 만약 제네릭 뒀으면 형변환 안해도 됨
				if (file.getSize() > 0) {
					String fileRealName = file.getOriginalFilename(); // 실제파일명
					String fileTmpName = Utils.getPrimaryKey(); // 동일한 파일명이 넘어오면 덮어씌움 그런거 방지하기 위해, 고유 날짜키 받기
					String fileExt = FilenameUtils.getExtension(file.getOriginalFilename()).toLowerCase(); // 파일
					// 확장자추출

					if (uploadExts.toLowerCase().indexOf(fileExt) < 0) {
						throw new Exception("Not allowded image file extension : " + fileExt.toLowerCase());
					} else {
						// 물리적으로 저장되는 파일명(실제파일명을 그대로 저장할지 rename해서 저장할지는 협의필요
						fileFullName = fileTmpName + fileRealName;
						// File(경로) - 폴더
						// File(경로, 파일명) - 파일
						// (new File(new File(uploadPath), fileFullName)
						// uploadPath(경로의 폴더에 fileFullName 이름의 파일
						// file.transferTo(새파일) - 새파일에 파일의 내용을 전송
						// transferTo 스프링에서 새로 나왔데
						file.transferTo(new File(new File(uploadPath), fileFullName));

						fileNames.add(fileFullName);
					}
				}
			}

			modelMap.put("result", CommonProperties.RESULT_SUCCESS);
		} catch (Exception e) {
			// 공통 Exception 처리
			e.printStackTrace();
			modelMap.put("result", CommonProperties.RESULT_ERROR);
		}

		modelMap.put("fileName", fileNames);

		return mapper.writeValueAsString(modelMap);
	}

	/**
	 * CKEditor용 image업로드
	 **/
	@RequestMapping(value = "/imageUpload", method = RequestMethod.POST)
	public void editorImageUploadAjax(HttpServletRequest request, HttpServletResponse response,
			@RequestParam MultipartFile upload, ModelAndView modelAndView) throws Throwable {
		PrintWriter printWriter = null;
		try {
			String uploadExts = CommonProperties.IMG_EXT; // 확장자
			String uploadPath = CommonProperties.FILE_UPLOAD_PATH; // 업로드경로
			String fileFullName = "";

			File fileDir = new File(uploadPath);

			if (!fileDir.exists()) {
				fileDir.mkdirs(); // 디렉토리가 존재하지 않는다면 생성
			}

			if (upload.getSize() > 0) {
				String fileRealName = upload.getOriginalFilename().replace(" ", "_").toLowerCase(); // 실제파일명
				String fileTmpName = Utils.getPrimaryKey(); // 고유 날짜키 받기
				String fileExt = FilenameUtils.getExtension(upload.getOriginalFilename()).toLowerCase(); // 파일

				if (uploadExts.toLowerCase().indexOf(fileExt) >= 0) {
					fileFullName = fileTmpName + fileRealName;
					upload.transferTo(new File(fileDir, fileFullName));

				} else {
					// 파일 확장자가 틀릴 경우
					printWriter = response.getWriter();

					printWriter.println("<script type='text/javascript'>alert('파일 확장자가 지원을 하지 않습니다.');</script>");
					printWriter.flush();
					printWriter.close();
				}

				// 성공 시
				String callback = request.getParameter("CKEditorFuncNum");

				printWriter = response.getWriter();

				printWriter.println("<script type='text/javascript'>" + "window.parent.CKEDITOR.tools.callFunction("
						+ callback + ",'" + "resources/upload/" + fileFullName + "','이미지를 업로드 하였습니다.'" + ")</script>");
				printWriter.flush();
				printWriter.close();

			} else {
				// 파일 크기가 0이거나 없는 경우
				printWriter = response.getWriter();

				printWriter.println("<script type='text/javascript'>alert('파일 업로드에 실패하였습니다.');</script>");
				printWriter.flush();
				printWriter.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (printWriter != null) {
				printWriter.close();
			}
		}
	}
}

 

 

 

CommonProperties.java, 파일 업로드 부분만 봐

package com.spring.sample.common;

public class CommonProperties {
	/**
	 * 기본 셋
	 */
	// 기본 리스트 사이즈
	public static final int VIEWCOUNT = 10;
	// 기본 페이지 사이즈
	public static final int PAGECOUNT = 10;

	/**
	 * Ajax Result
	 */
	public static final String RESULT_SUCCESS = "SUCCESS";

	public static final String RESULT_ERROR = "ERROR";

	/**
	 * 파일 업로드
	 */
	// 파일 업로드 경로
	// 실제 물리적으로 저장될 경로
	// 가상 서버에 직접 올린다고 한거
	// 문제는 clean하면 파일 날라갈수있음
	public static final String FILE_UPLOAD_PATH = "C:\\MyWork\\workspace\\.metadata\\.plugins\\org.eclipse.wst.server.core"
			+ "\\tmp0\\wtpwebapps\\SampleSpring\\resources\\upload";

	// 허용파일 확장자
	public static final String FILE_EXT = "xls|ppt|doc|xlsx|pptx|docx|hwp|csv|jpg|jpeg|png|gif|bmp|tld|txt|pdf|zip|alz|7z";
	public static final String IMG_EXT = "jpg|jpeg|png|gif|bmp";

	/**
	 * 암호화키(AES기반 16글자)
	 */
	public static final String SECURE_KEY = "goodeesmart12345";
}

 

Utils.java, getPrimaryKey()만 참고

package com.spring.sample.util;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang.RandomStringUtils;

import com.spring.sample.common.CommonProperties;

public class Utils {
	/**
	 * Primary Key 생성
	 * 
	 * @return String
	 */
	public static String getPrimaryKey() {
		SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
		return formatter.format(new java.util.Date()) + RandomStringUtils.randomNumeric(6);
	}
	
	/**
	 * 문자열을 key를 통해 암호화 하고 base64 로 인코딩
	 * 
	 * @return String
	 * @throws Throwable
	 */
	public static String encryptAES128(String value) throws Throwable {
		SecretKeySpec keySpec 
				= new SecretKeySpec(CommonProperties.SECURE_KEY.getBytes("UTF-8"), "AES");

		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(Cipher.ENCRYPT_MODE, keySpec);
		byte[] encrypted = cipher.doFinal(value.getBytes()); // 암호화
		
		Encoder encoder = Base64.getEncoder();
		
		String encodeString = encoder.encodeToString(encrypted); // 바이트 타입의 배열을 문자열로 변환

		return encodeString;
	}

	/**
	 * key 를 통해 문자열 base64 디코딩
	 * 
	 * @return String
	 * @throws Throwable
	 */
	public static String decryptAES128(String value) throws Throwable {
		SecretKeySpec keySpec = new SecretKeySpec(CommonProperties.SECURE_KEY.getBytes("UTF-8"), "AES");

		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(Cipher.DECRYPT_MODE, keySpec);
		
		Decoder decoder = Base64.getDecoder();
		
		byte[] decodeBytes = decoder.decode(value); //문자열 형태의 파라메터를 배열에 바이트 변환 후 삽입

		byte[] decryptBytes = cipher.doFinal(decodeBytes); // 복호화

		return new String(decryptBytes);
	}
	
	/**
	 * MAP의 Key를 소문자로 변환
	 * 
	 * @return HashMap<String, String>
	 */
	public static HashMap<String, String> toLowerMapKey(HashMap<String, String> oldMap) throws Throwable {
		Set<String> keySet = oldMap.keySet();
		
		Iterator<String> keys = keySet.iterator();
		
		HashMap<String, String> newMap = new HashMap<String, String>();
		
		while(keys.hasNext()) {
			String key = keys.next();
			newMap.put(key.toLowerCase(), String.valueOf(oldMap.get(key)));
		}
		
		return newMap;
	}
	
	/**
	 * List의 MAP Key를 소문자로 변환
	 * 
	 * @return HashMap<String, String>
	 */
	public static List<HashMap<String, String>> toLowerListMapKey(List<HashMap<String, String>> oldList) {
		List<HashMap<String, String>> newList = new ArrayList<HashMap<String, String>>();
		
		for(HashMap<String, String> oldMap : oldList) {
			Set<String> keySet = oldMap.keySet();
			
			Iterator<String> keys = keySet.iterator();
			
			HashMap<String, String> newMap = new HashMap<String, String>();
			
			while(keys.hasNext()) {
				String key = keys.next();
				newMap.put(key.toLowerCase(), String.valueOf(oldMap.get(key)));
			}
			
			newList.add(newMap);
		}
		
		return newList;
	}
	
	/**
	 * List의 MAP Key를 camel식으로 변환
	 * 
	 * @return HashMap<String, String>
	 */
	public static List<HashMap<String, String>> toCamelListMapKey(List<HashMap<String, String>> oldList) {
		List<HashMap<String, String>> newList = new ArrayList<HashMap<String, String>>();
		
		for(HashMap<String, String> oldMap : oldList) {
			Set<String> keySet = oldMap.keySet();
			
			Iterator<String> keys = keySet.iterator();
			
			HashMap<String, String> newMap = new HashMap<String, String>();
			
			while(keys.hasNext()) {
				String key = keys.next();
				
				String[] temp = key.split("_");
				
				String camelKey = "";
				
				for(int i = 0 ; i < temp.length ; i++) {
					if(i == 0) {
						camelKey += temp[0].toLowerCase();
					} else {
						camelKey += temp[i].substring(0, 1).toUpperCase() + temp[i].substring(1).toLowerCase();
					}
				}
				
				newMap.put(camelKey, String.valueOf(oldMap.get(key)));
			}
			
			newList.add(newMap);
		}
		
		return newList;
	}
}

 

insert.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글쓰기</title>
<!-- Common CSS -->
<link rel="stylesheet" type="text/css" href="resources/css/common/cmn.css" />
<!-- Popup CSS -->
<link rel="stylesheet" type="text/css" href="resources/css/common/popup.css" />
<style type="text/css">
.wrap{
	width: 800px;
	margin: 0 auto;
}


</style>
<script type="text/javascript" src="resources/script/jquery/jquery-1.12.4.min.js"></script>
<script type="text/javascript" 
		src="resources/script/jquery/jquery.form.js"></script>
<script type="text/javascript" 
		src="resources/script/common/popup.js"></script>
<!-- CKEditor -->
<!-- 제이쿼리 뒤에 나와야함, 제이쿼리 기반으로 동작하기 때문에 -->
<script type="text/javascript" src="resources/script/ckeditor/ckeditor.js"></script>
<script type="text/javascript">
$(document).ready(function () {
	// 에디터 연결
	// CKEDITOR.replace(아이디, 옵션)
	CKEDITOR.replace("con", {
		resize_enabled: false, // resize_enabled : 크기조절기능 활용여부
		language : "ko", // 사용언어
		enterMode: "2", // 엔터키처리방법. 2번이면 <br/>
		width : "100%", // 숫자일경우 px, 문자열일경우 css크기
		height : 400
	});
	
	$("#listBtn").on("click", function () {
		$("#backForm").submit();
	});
	
	$("#insertBtn").on("click", function () {
		// CKEditor의 값 취득
		// CKEDITOR.instances[아이디] : CKEditor중 아이디가 같은 것을 찾겠다.
		//.getData() : 작성중인 내용을 취득하겠다.
		$("#con").val(CKEDITOR.instances['con'].getData());
		
		if($.trim($("#title").val()) == ""){
			makeAlert("알림","제목 입력하세요." , function () {
				$("#title").focus();
			})
		} else if($.trim($("#con").val()) == ""){
			makeAlert("알림","내용을 입력하세요." , function () {
				$("#con").focus();
			})
		} else {
			// 1. 파입업로드 -> 2. 업로드 파일명 취득 -> 3. 글 저장
			// 폼 객체 취득
			var form = $("#actionForm");
			// ajaxForm 적용
			form.ajaxForm({
				success: function (res) { // 데이터 주고 받기 성공시
					if(res.result == "SUCCESS"){ // 파일 전송 성공
						// 올라간 파일이 존재한다면, 첨부파일이 없을 수도 있으니 조건문 처리
						if(res.fileName.length > 0){
							$("#att").val(res.fileName[0]); // 올라간 파일명 보관		
						}
						// 3번 단계 글저장 시작, 기존테이터
						var params = $("#actionForm").serialize();
						
						$.ajax({
							url : "ATAction/insert", //restful api라는게 여기서 시작한다고?
							type : "POST", 
							dataType: "json", 
							data: params, 
							success : function(res) { 
								switch(res.msg){
								case "success" : 
									location.href ="ATList";
									break;
								case "fail" :
									makeAlert("알림" , "등록에 실패하였습니다.");
									break;
								case "error" :
									makeAlert("알림" , "등록 중 문제가 발생하였습니다.");
									break;
								}
							},
							error : function(request, status, error) { 
								console.log(request.responseText); 
							}
						});  // 글저장 끝
						
					} else { // 문제발생
						makeAlert("알림", "파일 업로드에<br/>문제가 발생하였습니다.")
					}
				},
				error: function () { // 에러시
					makeAlert("알림", "파일 업로드에<br/>문제가 발생하였습니다.")
				}
			}); // ajaxForm 설정 끝
			
			
			// ajaxForm 실행
			form.submit();
			
		}
	});
});

</script>
</head>
<body>
<c:import url="/testAHeader"></c:import>
<form action="ATList" id="backForm" method="post">
	<input type="hidden" name="page" value="${param.page}" />
	<input type="hidden" name="searchGbn" value="${param.searchGbn}"/>
	<input type="hidden" name="searchTxt" value="${param.searchTxt}"/>
</form>
<!-- ajax쓰기 때문에 gbn 지움 -->
<div class="wrap">
	<form action="fileUploadAjax" id="actionForm" method="post" enctype="multipart/form-data">
		<input type="hidden" name="att" id="att" /> <!-- 실 저장된 파일명 보관용 -->
		<table class="board_detail_table">
			<tr>
				<th>제목</th>
				<td><input type="text" name="title" id="title" /></td>
			</tr>
			<tr>
				<th>작성자</th>
				<td>${sMemNm}<input type="hidden" name="memNo" value="${sMemNo}" /><br/></td>
			</tr>
			<tr>
				<th colspan="2">내용</th>
			</tr>
			<tr>
				<th colspan="2"><textarea rows="10" cols="30" name="con" id="con"></textarea></th>
			</tr>
			
			<tr>
				<th>첨부파일</th>
				<td><input type="file" name="attFile" /></td>
			</tr>
		</table>
	</form>
	<div class="cmn_btn_ml float_right_btn" id="listBtn">목록</div>
	<div class="cmn_btn_ml float_right_btn" id="insertBtn">등록</div>
</div>
</body>
</html>

list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판</title>
<!-- Common CSS -->
<link rel="stylesheet" type="text/css" href="resources/css/common/cmn.css" />
<!-- Popup CSS -->
<link rel="stylesheet" type="text/css" href="resources/css/common/popup.css" />
<style type="text/css">
#searchTxt{
width: 160px;
height: 30px; 
padding: 0px 2px;
text-indent: 5px;
vertical-align: middle;
border: 1px solid #d7d7d7; 
outline-color: #70adf9;
box-sizing: border-box;
}
.search_area{
	width: 800px;
	text-align: right;
	margin: 0 auto;
}
.board_area{
	width: 800px;
	margin: 0 auto;
}

.att {
	display: inline-block; 
	vertical-align: top; 
	width: 18px; 
	height: 18px; 
	background-image: url('resources/images/attFile.png'); 
	background-size: cover;
}

</style>
<script type="text/javascript" src="resources/script/jquery/jquery-1.12.4.min.js"></script>
<script type="text/javascript" 
		src="resources/script/common/popup.js"></script>
<script type="text/javascript">
$(document).ready(function () {
	if("${params.searchGbn}" != ""){
		$("#searchGbn").val("${param.searchGbn}");
	} else {
		$("#oldGbn").val("0");
	}
	
	//목록 조회
	reloadList();
	
	//검색버튼
	$("#searchBtn").on("click", function () {
		$("#page").val("1");
		// 이동을 안하다보니 문제가 기존검색값 셋팅을 안해줌
		// 그래서  신규 상태 적용
		$("#oldGbn").val($("#searchGbn").val());
		$("#oldTxt").val($("#searchTxt").val());
		
		
		// 데이터만 가져와서 다시 그려줌, 화면 이동 없음
		reloadList();
	});
	
	//페이징 버튼
	$(".paging_area").on("click", "span", function () {
		// 기존 검색상태 유지
		$("#searchGbn").val($("#oldGbn").val());
		$("#searchTxt").val($("#oldTxt").val());
			
		
		$("#page").val($(this).attr("page"));
		
		reloadList();
	})
	
	// 등록버튼
	$("#insertBtn").on("click", function () {
		// 기존 검색상태 유지
		$("#searchGbn").val($("#oldGbn").val());
		$("#searchTxt").val($("#oldTxt").val());
		
		$("#actionForm").attr("action", "ATInsert");
		$("#actionForm").submit();
	})
	
	$("tbody").on("click", "tr", function () {
		$("#no").val($(this).attr("no"));
		
		$("#searchGbn").val($("#oldGbn").val());
		$("#searchTxt").val($("#oldTxt").val());
		
		$("#actionForm").attr("action", "ATDetail");
		$("#actionForm").submit();
	})
});

// 목록 조회 호출, ajax불러오기
function reloadList() {
	var params = $("#actionForm").serialize();
	
	$.ajax({
		url : "ATListAjax", //경로
		type : "POST", // 전송방식(GET: 주소형태, POST : 주소 헤더 형태)
		dataType: "json", // 데이터 형태
		data: params, // 보낼데이터
		success : function(res) { //성공했을 때 결과를 res에 받고 함수 실행
			console.log(res); // 콘솔에  pd랑 list 값이 보임
			drawList(res.list);
			drawPaging(res.pd);
		},
		error : function(request, status, error) { // 실패했을 때 함수실행
			console.log(request.responseText); // 실패 상세 태역
		}
	});
	
}

function drawList(list) {
	var html = "";
	
	// list에는 map이 들어있으니까 하나씩 꺼내오겠다
	for(var data of list){
		console.log(typeof(data.ATT) !="undefined");
		html += "<tr no=\"" + data.NO + "\">";
		html += "<td>" + data.NO + "</td>";
		html += "<td>";
		html += data.TITLE;
		if(typeof(data.ATT) !="undefined"){ // 첨부파일이 있다면
			html += "<span class=\"att\"></span>";
		}
		html += "</td>";
		html += "<td>" + data.MEM_NM + "</td>";
		html += "<td>" + data.DT + "</td>";
		html += "<td>" + data.HIT + "</td>";
		html += "</tr>";
	}   
	
	$("tbody").html(html);
}

function drawPaging(pd) {
	var html = "";
	
	html += "<span class=\"page_btn page_first\" page=\"1\">처음</span>";
	// 이전
	if($("#page").val() == "1"){
		html += "<span class=\"page_btn page_prev\" page=\"1\">이전</span>";
	} else{
		// 문자열을 숫자로 바꾸기위해 *1
		html += "<span class=\"page_btn page_prev\" page=\"" + ($("#page").val() *1 - 1) + "\">이전</span>";
	}
	
	for(var i = pd.startP; i <= pd.endP; i++){
		if($("#page").val() * 1 == i){ // 현재 페이지
			html += "<span class=\"page_btn_on\" page=\"" + i + "\">" + i + "</span>";
		} else { // 다른 페이지
			html += "<span class=\"page_btn\" page=\"" + i + "\">" + i + "</span>";
		}
	}
	
	if($("#page").val() *1 == pd.endP){ // 현재페이지가 마지막 페이지라면
		html += "<span class=\"page_btn page_next\" page=\"" +pd.maxP+ "\">다음</span>";
	} else {
		html += "<span class=\"page_btn page_next\" page=\"" + ($("#page").val() *1 + 1) + "\">다음</span>";
	}
	
	html += "<span class=\"page_btn page_last\" page=\"" +pd.maxP+ "\">마지막</span>";
	
	$(".paging_area").html(html);
                                                                     
}
</script>		
</head>
<body>
<c:import url="/testAHeader"></c:import>
<hr/>
<!-- 페이징 때 기존 검색 내용 유지용 -->
<input type="hidden" id="oldGbn" value="${param.searchGbn}"/>
<input type="hidden" id="oldTxt" value="${param.searchTxt}"/>

<!-- 데이터 후 처리해서 로딩이 빨라 -->
<div class="search_area">
	<!--  method="post"는 나중에 쓸 예정, no 보낼때 -->
	<form action="#" id="actionForm" method="post">
	<input type="hidden" name="no" id="no" />
		<input type="hidden" name="page" id="page" value="${page}" />
		<select name="searchGbn" id="searchGbn">
			<option value="0">제목</option>
			<option value="1">작성자</option>
		</select>
		<input type="text" name="searchTxt" id="searchTxt" value="${param.searchTxt}" />
		<div class="cmn_btn_ml" id="searchBtn">검색</div>
		<c:if test="${!empty sMemNo}">
			<div class="cmn_btn_ml" id="insertBtn">등록</div>
		</c:if>
	</form>
</div>
<div class="board_area">
<table class="board_table">
	<colgroup>
		<col width="100" />
		<col width="400" />
		<col width="100" />
		<col width="100" />
		<col width="100" />
	</colgroup>
	<thead>
		<tr>
			<th>번호</th>
			<th>제목</th>
			<th>작성자</th>
			<th>작성일</th>
			<th>조회수</th>
		</tr>
	</thead>
	<tbody></tbody>
</table>
<div class="paging_area"></div>
</div>
</body>
</html>

 

detail.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- jsl의 functions : el tag 추가옵션 -->
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상세보기</title>
<!-- Common CSS -->
<link rel="stylesheet" type="text/css" href="resources/css/common/cmn.css" />
<!-- Popup CSS -->
<link rel="stylesheet" type="text/css" href="resources/css/common/popup.css" />
<style type="text/css">
.wrap{
	width: 800px;
	margin: 0 auto;
}
.con{
text-align: left; 
min-height: 400px;
}
</style>
<script type="text/javascript" 
		src="resources/script/jquery/jquery-1.12.4.min.js"></script>
<script type="text/javascript" 
src="resources/script/common/popup.js"></script>
<script type="text/javascript">
$(document).ready(function () {
	$("#listBtn").on("click", function() {
		$("#actionForm").attr("action","ATList"); 
		$("#actionForm").submit();
	});
	
	// ajax사용 예정
	$("#deleteBtn").on("click", function () {
		  makePopup({
		         title : "알림",
		         contents : "삭제하시겠습니까?",
		         // draggable : true,
		         buttons : [{
		            name : "삭제",
		            func:function() {
		            	var params = $("#actionForm").serialize();
		    			
		    			$.ajax({
		    				url : "ATAction/delete", //restful api라는게 여기서 시작한다고?
		    				type : "POST", 
		    				dataType: "json", 
		    				data: params, 
		    				success : function(res) { 
		    					switch(res.msg){
		    					case "success" : 
		    						location.href ="ATList";
		    						break;
		    					case "fail" :
		    						makeAlert("알림" , "삭제에 실패하였습니다.");
		    						break;
		    					case "error" :
		    						makeAlert("알림" , "삭제 중 문제가 발생하였습니다.");
		    						break;
		    					}
		    				},
		    				error : function(request, status, error) { 
		    					console.log(request.responseText); 
		    				}
		    			});
		            }
		         }, {
		            name : "취소"
		    }]
		});
	});
	
	$("#updateBtn").on("click", function () {
		$("#actionForm").attr("action","ATUpdate");
		$("#actionForm").submit();
	});
});

</script>
</head>
<body>
<c:import url="/testAHeader"></c:import>
<form action="#" id="actionForm" method="post">
	<input type="hidden" name="gbn" value="d"> <!-- selRes는 gbn을 받게 되어있어서 값을 추가해줘야함 -->
	<input type="hidden" name="no" value="${data.NO}">
	<input type="hidden" name="page" value="${param.page}" /> <!-- 전 화면에서 넘어온 페이지 정보 -->
		<!-- 전 화면에서 넘어온 검색 정보 -->
	<input type="hidden" name="searchGbn" value="${param.searchGbn}"/>
	<input type="hidden" name="searchTxt" value="${param.searchTxt}"/>
</form>
<div class="wrap">
<table class="board_detail_table">
	<tr>
		<th>번호</th>
		<td>${data.NO}</td>
	</tr>
	<tr>
		<th>제목</th>
		<td>${data.TITLE}</td>
	</tr>
	<tr>
		<th>작성자</th>
		<td>${data.MEM_NM}</td>
	</tr>
	<tr>
		<th>조회수</th>
		<td> ${data.HIT}</td>
	</tr>
	<tr>
		<th>작성일</th>
		<td> ${data.DT}</td>
	</tr>
	<tr>
		<th colspan="2">내용</th>
	</tr>
	<tr>
		<td class="con" colspan="2">${data.CON}</td>
	</tr>
	<c:if test="${!empty data.ATT}">
	<!-- fn:length(대상) : 대상 문자열의 길이나 배열, 리스트의 크기를 가져온다 -->
	<c:set var="fileLength" value="${fn:length(data.ATT)}"></c:set>
	<!-- fn:substring(값, 숫자1, 숫자2) : 값을 숫자1이상부터, 숫자2미만까지, 인덱스 기준으로 자른다. -->
	<c:set var="fileName" value="${fn:substring(data.ATT, 20, fileLength)}"></c:set>
	<tr>
		<th>첨부파일</th>
		<!-- download 없으면 이미지를 브라우저에 띄움, 파일을 안 받고 -->
		<!-- download="${fileName}" 옵션을 줘야 다운받을때도 파일명으로 받아짐 -->
		<td><a href="resouces/upload/${data.ATT}" download="${fileName}">${fileName}</a></td>
	</tr>
	</c:if>
</table>
	<div class="cmn_btn_ml float_right_btn" id="listBtn">목록</div>
	<c:if test="${sMemNo eq data.MEM_NO}">
		<div class="cmn_btn_ml float_right_btn" id="deleteBtn">삭제</div>
		<div class="cmn_btn_ml float_right_btn" id="updateBtn">수정</div>
	</c:if>
</div>
</body>

 

update.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- jsl의 functions : el tag 추가옵션 -->
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글수정</title>
<!-- Common CSS -->
<link rel="stylesheet" type="text/css" href="resources/css/common/cmn.css" />
<!-- Popup CSS -->
<link rel="stylesheet" type="text/css" href="resources/css/common/popup.css" />
<style type="text/css">
.wrap{
	width: 800px;
	margin: 0 auto;
}

.att {
	display: none;
}


</style>
<script type="text/javascript" src="resources/script/jquery/jquery-1.12.4.min.js"></script>
<script type="text/javascript" 
		src="resources/script/jquery/jquery.form.js"></script>
<script type="text/javascript" 
		src="resources/script/common/popup.js"></script>
<!-- CKEditor -->
<!-- 제이쿼리 뒤에 나와야함, 제이쿼리 기반으로 동작하기 때문에 -->
<script type="text/javascript" src="resources/script/ckeditor/ckeditor.js"></script>
<script type="text/javascript">
$(document).ready(function () {
	// 에디터 연결
	// CKEDITOR.replace(아이디, 옵션)
	CKEDITOR.replace("con", {
		resize_enabled: false, // resize_enabled : 크기조절기능 활용여부
		language : "ko", // 사용언어
		enterMode: "2", // 엔터키처리방법. 2번이면 <br/>
		width : "100%", // 숫자일경우 px, 문자열일경우 css크기
		height : 400
	});
	
	$("#cancelBtn").on("click", function () {
		$("#backForm").submit();
	});
	
	// 첨부파일 파일삭제 버튼 클릭시
	$("#fileDelBtn").on("click", function () {
		// 기존 파일 내역 영역 제거
		$(".attOld").remove();
		// 기존 값 제거
		$("#att").val("");
		// 파일 선택 영역 제공 
		$(".att").show();
	});
	
	$("#updateBtn").on("click", function () {
		// CKEditor의 값 취득
		// CKEDITOR.instances[아이디] : CKEditor중 아이디가 같은 것을 찾겠다.
		//.getData() : 작성중인 내용을 취득하겠다.
		$("#con").val(CKEDITOR.instances['con'].getData());
		
		if($.trim($("#title").val()) == ""){
			makeAlert("알림","제목 입력하세요." , function () {
				$("#title").focus();
			})
		} else if($.trim($("#con").val()) == ""){
			makeAlert("알림","내용을 입력하세요." , function () {
				$("#con").focus();
			})
		} else {
			// 1. 파입업로드 -> 2. 업로드 파일명 취득 -> 3. 글 저장
			// 폼 객체 취득
			var form = $("#actionForm");
			// ajaxForm 적용
			form.ajaxForm({
				success: function (res) { // 데이터 주고 받기 성공시
					if(res.result == "SUCCESS"){ // 파일 전송 성공
						// 올라간 파일이 존재한다면, 첨부파일이 없을 수도 있으니 조건문 처리
						if(res.fileName.length > 0){
							$("#att").val(res.fileName[0]); // 올라간 파일명 보관		
						}
						// 3번 단계 글저장 시작, 기존테이터
						var params = $("#actionForm").serialize();
						
						$.ajax({
							url : "ATAction/update", //restful api라는게 여기서 시작한다고?
							type : "POST", 
							dataType: "json", 
							data: params, 
							success : function(res) { 
								switch(res.msg){
								case "success" : 
									$('#backForm').submit();
									break;
								case "fail" :
									makeAlert("알림" , "등록에 실패하였습니다.");
									break;
								case "error" :
									makeAlert("알림" , "등록 중 문제가 발생하였습니다.");
									break;
								}
							},
							error : function(request, status, error) { 
								console.log(request.responseText); 
							}
						});  // 글저장 끝
						
					} else { // 문제발생
						makeAlert("알림", "파일 업로드에<br/>문제가 발생하였습니다.")
					}
				},
				error: function () { // 에러시
					makeAlert("알림", "파일 업로드에<br/>문제가 발생하였습니다.")
				}
			}); // ajaxForm 설정 끝
			
			
			// ajaxForm 실행
			form.submit();
		}
	});
});

</script>
</head>
<body>
<c:import url="/testAHeader"></c:import>
<form action="ATDetail" id="backForm" method="post">
	<input type="hidden" name="no" value="${data.NO}" />
	<input type="hidden" name="page" value="${param.page}" />
	<input type="hidden" name="searchGbn" value="${param.searchGbn}"/>
	<input type="hidden" name="searchTxt" value="${param.searchTxt}"/>
</form>
<!-- ajax쓰기 때문에 gbn 지움 -->
<div class="wrap">
	<form action="fileUploadAjax" id="actionForm" method="post" enctype="multipart/form-data">
	<input type="hidden" name="no" value="${data.NO}" />
		<table class="board_detail_table">
			<tr>
				<th>번호</th>
				<td>${data.NO}</td>
			</tr>
			<tr>
				<th>제목</th>
				<td><input type="text" name="title" id="title" value="${data.TITLE}"/></td>
			</tr>
			<tr>
				<th>작성자</th>
				<td>${sMemNm}</td>
			</tr>
			<tr>
				<th colspan="2">내용</th>
			</tr>
			<tr>
				<th colspan="2"><textarea rows="10" cols="30" name="con" id="con">${data.CON}</textarea></th>
			</tr>
			<tr>
				<th>첨부파일</th>
				<c:choose>
					<c:when test="${empty data.ATT}">
						<!-- 파일이 없을때 -->
						<td>
							<input type="file" name="attFile" />
							<input type="hidden" name="att" id="att"/>
						</td>
					</c:when>
					<c:otherwise>
					<!-- 파일이 있을때 -->
						<td>
							<span class="attOld"> <!-- 기존 파일 -->
								<!-- fn:length(대상) : 대상 문자열의 길이나 배열, 리스트의 크기를 가져온다 -->
								<c:set var="fileLength" value="${fn:length(data.ATT)}"></c:set>
								<!-- fn:substring(값, 숫자1, 숫자2) : 값을 숫자1이상부터, 숫자2미만까지, 인덱스 기준으로 자른다. -->
								<c:set var="fileName" value="${fn:substring(data.ATT, 20, fileLength)}"></c:set>
								${fileName}
								<div class="cmn_btn_ml float_right_btn" id="fileDelBtn">파일삭제</div>
							</span>
							<span class="att"> <!-- 기존 파일 삭제 후 새 파일 용도 -->
								<input type="file" name="attFile" />
								<input type="hidden" name="att" id="att" value="${data.ATT}"/>
							</span>
						</td>
					</c:otherwise>
				</c:choose>
			</tr>
		</table>
	</form>
	<div class="cmn_btn_ml float_right_btn" id="cancelBtn">취소</div>
	<div class="cmn_btn_ml float_right_btn" id="updateBtn">수정</div>
</div>
</body>
</html>

 

T_SQL.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="T">
	<select id="getTCnt" resultType="Integer" parameterType="hashmap">
		SELECT COUNT(*) AS CNT
		FROM TBOARD T INNER JOIN MEM M ON T.MEM_NO = M.MEM_NO
		AND M.DEL = 1
		WHERE T.DEL = 1
		<if test="searchTxt != null and searchTxt !=''">
			<choose>
				<when test="searchGbn eq 0">
					AND T.TITLE LIKE '%' || #{searchTxt} || '%'
				</when>
				<when test="searchGbn eq 1">
					AND M.MEM_NM LIKE '%' ||  #{searchTxt} || '%'
				</when>
			</choose>
		</if>
	</select>
	
	<select id="getTList" resultType="hashmap" parameterType="hashmap">
		SELECT T.NO, T.TITLE, T.MEM_NM, T.HIT, T.DT, T.ATT
		FROM(
		SELECT T.NO, T.TITLE, M.MEM_NM, T.HIT,
		CASE WHEN TO_CHAR(DT,'YY.MM.DD') = TO_CHAR(SYSDATE, 'YY.MM.DD')
		        THEN TO_CHAR(DT, 'HH24:MI')
		        ELSE TO_CHAR(DT, 'YY.MM.DD')
		        END AS DT, ATT,
		        ROW_NUMBER() OVER(ORDER BY T.NO DESC) AS RNUM
				FROM TBOARD T INNER JOIN MEM M ON T.MEM_NO = M.MEM_NO
				AND M.DEL = 1
		WHERE T.DEL = 1
		<if test="searchTxt != null and searchTxt !=''">
			<choose>
				<when test="searchGbn eq 0">
					AND T.TITLE LIKE '%' || #{searchTxt} || '%'
				</when>
				<when test="searchGbn eq 1">
					AND M.MEM_NM LIKE '%' ||  #{searchTxt} || '%'
				</when>
			</choose>
		</if>) T
		WHERE T.RNUM BETWEEN #{start} AND #{end}
	</select>
	
	<insert id="insertT" parameterType="hashmap">
		INSERT INTO TBOARD(NO, TITLE, MEM_NO, CON, ATT)
		VALUES (TBOARD_SEQ.NEXTVAL, #{title} , #{memNo}, #{con}, #{att})
	</insert>
	 
	<select id="getT" parameterType="hashmap"  resultType="hashmap">
		SELECT T.NO, T.TITLE, M.MEM_NO, M.MEM_NM, T.CON, T.HIT,
	    TO_CHAR(T.DT, 'YYYY-MM-DD') AS DT, T.ATT
		FROM TBOARD T INNER JOIN MEM M ON T.MEM_NO = M.MEM_NO
		AND M.DEL = 1
		WHERE T.DEL = 1
		AND T.NO = #{no}
	</select>
	
	<update id="updateTHit" parameterType="hashmap">
		UPDATE TBOARD SET HIT = HIT + 1
		WHERE NO = #{no}
	</update>
	
	<update id="deleteT"  parameterType="hashmap">
		UPDATE TBOARD SET DEL = 0
		WHERE NO = #{no}
	</update>
	
	<update id="updateT"  parameterType="hashmap">		
		UPDATE TBOARD SET TITLE = #{title},
		CON = #{con},
		ATT = #{att}
		WHERE NO = #{no}
	</update>
</mapper>

 

 

 

 

 

내일 한줄게시판 할예정, 한줄게시판이 댓글이 됨

 

 

728x90