I T H

[프로젝트] 16. 관리자 회원관리페이지 - 그리드 / 엑셀 (Week 4) 본문

Spring ArtGallery Project

[프로젝트] 16. 관리자 회원관리페이지 - 그리드 / 엑셀 (Week 4)

thdev 2024. 1. 24. 09:43

관리자 전용 화면 중

회원관리 페이지를 개발 진행함 

해당 화면은 무료로 제공하는 그리드 API를 활용하여 

테이블 태그 대신 자바스크립트에서 화면에 데이터를 출력해보고자 함.

 

라이브러리 사용을 위해 아래 사이트에서 참고.

 

https://ui.toast.com/tui-grid

 

TOAST UI :: Make Your Web Delicious!

TOAST UI is an open-source JavaScript UI library maintained by NHN Cloud.

ui.toast.com

 

관련 라이브러리를 프로젝트에 임포트하여 사용.

<!-- tui grid -->
<link rel="stylesheet" href="https://uicdn.toast.com/grid/latest/tui-grid.css" />

...

<!-- tui grid -->
<script src="https://uicdn.toast.com/grid/latest/tui-grid.js"></script>

 

페이지 구현 및 컨트롤러 작성을 통해 

회원관리 페이지 개발 및 

기능 중 회원 리스트 조회만 먼저 처리해 보자.

 

먼저 템플릿 버튼 모양이 너무 커서 

살짝 커스터마이징을 진행한 스타일시트 파일을 작성.

 

[ custom.css ]

.amado-btn-custS {
    display: inline-block;
    min-width: 60px;
    height: 30px;
    color: #ffffff !important;
    border: none;
    border-radius: 0;
    padding: 0 7px;
    font-size: 11px;
    line-height: 30px;
    background-color: #fbb710;
    font-weight: 400;
    margin-bottom: 5px;
}
.amado-btn-custR {
    display: inline-block;
    min-width: 60px;
    height: 30px;
    color: #ffffff !important;
    border: none;
    border-radius: 0;
    padding: 0 7px;
    font-size: 11px;
    line-height: 30px;
    background-color: #dc3545;
    font-weight: 400;
    margin-bottom: 5px;
}
.amado-btn-custI {
    display: inline-block;
    min-width: 60px;
    height: 30px;
    color: #ffffff !important;
    border: none;
    border-radius: 0;
    padding: 0 7px;
    font-size: 11px;
    line-height: 30px;
    background-color: #17a2b8;
    font-weight: 400;
    margin-bottom: 5px;
}
.amado-btn-custE {
    display: inline-block;
    min-width: 60px;
    height: 30px;
    color: #ffffff !important;
    border: none;
    border-radius: 0;
    padding: 0 7px;
    font-size: 11px;
    line-height: 30px;
    background-color: #175625;
    font-weight: 400;
    margin-bottom: 5px;
}

 

[ userAdm.jsp ]

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html lang="kr">

<head>
    <meta charset="UTF-8">
    <meta name="description" content="">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- The above 4 meta tags *must* come first in the head; any other head content must come *after* these tags -->

    <!-- Title  -->
    <title>ART - [관리자] 회원관리</title>

	<!-- import CSS -->
	<%@include file="/resources/inc/incCss.jsp"%>
	
	<!-- jQuery modal 팝업창 사용 CSS -->
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css" />
</head>

<body>

    <!-- ##### Main Content Wrapper Start ##### -->
    <div class="main-content-wrapper d-flex clearfix">

        <!-- import Header -->
		<%@include file="/resources/inc/incHeader.jsp"%>

        <div class="products-catagories-area section-padding-100">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-12 col-lg-12">
                        <div class="checkout_details_area mt-50 clearfix">
                            <div class="cart-title">
                                <h4>[관리자] 회원관리</h4>
                            </div>
                        </div>
                    </div>
                    
                    <!-- 조회 조건 -->
                    <div class="col-md-3 mb-3">
                    	<small>사용자 아이디</small>
                        <input type="text" class="form-control" id="txtId" value="" placeholder="" required>
                    </div>
                    <div class="col-md-3 mb-3">
                    	<small>성명</small>
                        <input type="text" class="form-control" id="txtName" value="" placeholder="" required>
                    </div>
                    <div class="col-md-3 mb-3">
                    	<small>권한</small>
                    	<select class="w-100" id="cmbAuth">
	                    	<option value="">전체</option>
	                    	<option value="1">일반사용자</option>
	                    	<option value="2">관리자</option>
                       	</select>
                    </div>
                    
                    <!-- 버튼 -->
                    <div class="col-12 col-lg-12" style="text-align: right;">
                    	<a id="btnSearch" class="btn amado-btn-custS w-10">조회</a>
                    	<a id="btnRemove" class="btn amado-btn-custR w-10">삭제</a>
                    	<a id="btnSave" class="btn amado-btn-custI w-10">추가</a>
                    	<a id="btnExcel" class="btn amado-btn-custE w-10">엑셀</a>
                    </div>
                </div>
                
                <div id="grid"></div>
            </div>
        </div>
        
        <div id="ex1" class="modal">
        	<p>신규 사용자 등록</p>
			<div class="row">
				<div class="col-md-6 mb-3">
					<small>아이디</small> 
					<!-- 한 페이지에 id가 겹치면 안되므로 팝업창에서 작성한 컴포넌트들의 id는 *Pop* 키워드를 붙여주기로 함 (아이디~이메일 모두) 예)txtPopId, txtPopPwd -->
					<input type="text" class="form-control popTxt" id="txtPopId" value="" placeholder="아이디 입력" required>
				</div>
				<div class="col-md-6 mb-3">
					<small>비밀번호</small>
					<input type="password" class="form-control popTxt" id="txtPopPwd" value="" placeholder="비밀번호 입력" required>
				</div>
				<div class="col-md-6 mb-3">
                	<small>사용자 권한</small>
                    <select class="w-100" id="cmbAuthType">
                    	<option value="1">일반사용자</option>
                    	<option value="2">관리자</option>
                	</select>
                </div>
				<div class="col-md-6 mb-3">
					<small>사용자명</small>
					<input type="text" class="form-control popTxt" id="txtPopName" value="" placeholder="사용자명 입력">
				</div>
				<div class="col-md-6 mb-3">
                	<small>성별</small>
                    <select class="w-100" id="cmbSex">
                    	<option value="1">남자</option>
                    	<option value="2">여자</option>
                	</select>
                </div>
                <div class="col-md-6 mb-3">
                	<small>전화번호</small>
                    <input type="number" class="form-control popTxt" id="txtPopTel" min="0" placeholder="전화번호 입력" value="">
                </div>
                <div class="col-md-6 mb-3">
                	<small>이메일</small>
                    <input type="email" class="form-control popTxt" id="txtPopMail" placeholder="이메일 입력" value="">
                </div>
			</div>
			<hr>
			<a id="btnSaveUser" class="btn amado-btn-custS w-10">저장</a>
			<a id="btnModifyUser" class="btn amado-btn-custS w-10" style="display: none;">수정</a>
		  	<a href="#" class="btn amado-btn-custE w-10" rel="modal:close">닫기</a>
		</div>
    </div>
    <!-- ##### Main Content Wrapper End ##### -->

    <!-- import Footer -->
	<%@include file="/resources/inc/incFooter.jsp"%>

    <!-- import JS -->
	<%@include file="/resources/inc/incJs.jsp"%>
	
	<!-- jQuery modal 팝업창 사용 라이브러리 -->
	<!-- https://github.com/kylefox/jquery-modal -->
	<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.js"></script>
	
	<!-- page script -->
	<script src="/resources/views/userAdm.js"></script>

</body>

</html>

 

[ userAdm.js ]

/*******************************************************************************
 * userAdm.js
 * @author thkim
 * @since 2022
 * @DESC 관리자 - 회원관리 화면 스크립트
 ******************************************************************************/
(function() {

	function UserAdm() {

		/* 
		 * private variables
		 */
		 var grid = null; // 그리드 객체를 담기위한 변수 

		/* 
		 * 초기화 메소드
		 */
		function _init() {
			// 이벤트 처리 함수 호출 
			bindEvent();

			// 등록된 사용자 리스트 조회 
			findUserInfo();
		}

		function bindEvent() {
			// 조회 버튼 클릭 이벤트
			$("#btnSearch").on("click", function() {
				findUserInfo();
			});
			
			// 삭제 버튼 클릭 이벤트 - 체크박스에 체크한 항목의 사용자를 삭제할 수 있도록 한다.
			$("#btnRemove").on("click", function() {
				var checkedRow = grid.getCheckedRows();
				if(checkedRow.length <= 0) {
					alert("삭제할 항목을 선택하시기 바랍니다.");
					return;
				}
				
				saveUserInfoRemove(checkedRow);
			});
			
			// 추가 버튼 클릭 이벤트 - 팝업창을 오픈하여 사용자를 추가하거나 수정할 수 있도록 한다.
			$("#btnSave").on("click", function() {
				$("#ex1").modal(); // 모달(팝업) 창 오픈
				
				$("#btnModifyUser").hide();
				$("#btnSaveUser").show();
				
				$("#txtPopId").attr("readOnly", false); // 편집 가능하도록 
				
				// 이미 입력된 정보가 있을 수 있으므로 
				// 각 항목을 초기화 
				$(".popTxt").val("");
			});
			
			// 사용자 등록 버튼 클릭 이벤트 
			$("#btnSaveUser").on("click", function() {
				saveUserInfo();
			});
			
			// 엑셀 버튼 클릭 이벤트 
			$("#btnExcel").on("click", function() {
				var obj = {
					userId: $("#txtId").val(),
					userName: $("#txtName").val(),
					userAuth: $("#cmbAuth").val()
				};
				cfCustomExcelDownloadDyn("/userAdm/findExcel", obj);
			});
			
			// 팝업창 내에서 수정 버튼 클릭 이벤트 
			$("#btnModifyUser").on("click", function() {
				saveModifyUserInfo();
			});
		}
		
		/*
		 * 등록된 사용자 리스트 조회
		 */
		function findUserInfo() {

			var obj = {
				userId: $("#txtId").val(),
				userName: $("#txtName").val(),
				userAuth: $("#cmbAuth").val()
			};
			
			cfFind("/userAdm/findUserInfo", obj, function(data) {
				if (data.length > 0) {
					setGrid(data);
				}
			}, true, "POST");
		}
		
		/*
		 * 체크한 사용자 정보 삭제 
		 */ 
	    function saveUserInfoRemove(obj) {
			var result = confirm('삭제하시겠습니까?'); // confirm message : 예/아니오 응답을 받기위한 메시지창
			if(result) {
				cfSave("/userAdm/saveUserInfoRemove", obj, function(data) {
					if(data.success) {
						alert("사용자 정보가 삭제되었습니다.");
						
						findUserInfo(); // 사용자 정보 재조회
					} else {
						alert("사용자 정보 삭제 실패하였습니다. 확인 후 다시 등록하시기 바랍니다.");
					}
				});
			}
		}
		
		/*
		 * 신규 입력한 사용자 정보 저장
		 */ 
	    function saveUserInfo() {
			var obj = setParam();
		
			var result = confirm('저장하시겠습니까?'); // confirm message : 예/아니오 응답을 받기위한 메시지창
			if(result) {
				cfSave("/userAdm/saveUserInfo", obj, function(data) {
					if(data.success) {
						alert("사용자 정보가 등록되었습니다.");
						
						// 각 항목을 초기화 
						$(".popTxt").val("");
						// modal 팝업창 닫기 
						$.modal.close();
						// 사용자 정보 재조회
						findUserInfo(); 
					} else {
						alert("사용자 정보 등록 실패하였습니다. 확인 후 다시 등록하시기 바랍니다.");
					}
				});
			}
		}
		
		/*
		* grid setting
		*/
		function setGrid(data) {
			// 이미 그리드가 그려져 있으면 
			if(grid != null) {
				grid.destroy(); // 초기화하기 위해 사용
			}
			
			// 그리드 컬럼 정보 세팅
			var columns = [{
				header: '사용자 아이디',
				name: 'USER_ID'
			}, {
				header: '성명',
				name: 'USER_NAME'
			}, {
				header: '권한',
				name: 'AUTH_TYPE'
			}, {
				header: '성별',
				name: 'USER_SEX'
			}, {
				header: '전화번호',
				name: 'USER_PHONE'
			}, {
				header: '이메일',
				name: 'USER_EMAIL'
			}, {
				header: '주소',
				name: 'USER_ADDR'
			}, {
				header: '우편번호',
				name: 'ZIP_CODE'
			}, {
				header: '탈퇴여부',
				name: 'OUT_YN'
			}, {
				header: '등록일자',
				name: 'INPUT_DATETIME'
			}];

			// 그리드 옵션 설정 및 그리드 생성
			grid = new tui.Grid({
				el: document.getElementById('grid'), 
				columns: columns,
				columnOptions: {
			    	resizable: true // 컬럼 사이즈 조정
			    },
			    rowHeaders: ['checkbox'] // 채크박스
			});
			
			grid.resetData(data); // 그리드 데이터 세팅
			tui.Grid.applyTheme('striped'); // 줄무늬 스타일 적용
			
			// 그리드 더블클릭 이벤트 
			// 1개의 행을 더블클릭할 경우 수정할 수 있도록 팝업이 오픈된다.
			grid.on("dblclick", function(selected) {
				var rowIdx = selected.rowKey; // 선택한 행의 인덱스 
				var item = grid.getRow(rowIdx);
				setModifyPop(item);
			});
		}
		
		/**
	     * 수정 팝업 
		 */
		function setModifyPop(item) {
			// 클릭한 사용자 정보 팝업창에 세팅 
			$("#txtPopId").val(item.USER_ID);
			$("#txtPopPwd").val("");
			$("#cmbAuthType").val((item.AUTH_TYPE == "관리자" ? "2" : "1")).niceSelect('update');;
			$("#txtPopName").val(item.USER_NAME);
			$("#cmbSex").val((item.USER_SEX == "남자" ? "1" : "2")).niceSelect('update');;
			var userPhone = item.USER_PHONE;
			userPhone = userPhone.replace("-", "").replace("-", "");
			$("#txtPopTel").val(userPhone);
			$("#txtPopMail").val(item.USER_EMAIL);
			
			$("#ex1").modal(); // 모달 팝업창 오픈
			
			$("#btnSaveUser").hide(); // 저장 버튼은 숨김 
			$("#btnModifyUser").show(); // 수정 버튼은 노출
			$("#txtPopId").attr("readOnly", true); // 사용자 아이디는 키이므로 수정 금지
		}
		
		/**
	     * 수정 팝업창에서 수정 버튼 클릭한 경우 
	     * 입력한 정보로 데이터를 업데이트 한다.
		 */
		function saveModifyUserInfo() {
			var obj = setParam();
			
			var result = confirm('사용자 정보를 수정하시겠습니까?'); // confirm message : 예/아니오 응답을 받기위한 메시지창
			if(result) {
				cfSave("/userAdm/saveModifyUserInfo", obj, function(data) {
					if(data.success) {
						alert("사용자 정보가 수정되었습니다.");
						
						// 각 항목을 초기화 
						$(".popTxt").val("");
						// modal 팝업창 닫기 
						$.modal.close();
						// 사용자 정보 재조회
						findUserInfo(); 
					} else {
						alert("사용자 정보 수정 실패하였습니다. 확인 후 다시 수정하시기 바랍니다.");
					}
				});
			}
		}
		
		/**
		 * 저장 시 사용될 파라미터 정보를 세팅
		 */
		function setParam() {
			var userId = $("#txtPopId").val();
			var userPw = $("#txtPopPwd").val();
			var authType = $("#cmbAuthType").val();
			var userName = $("#txtPopName").val();
			var userSex = $("#cmbSex").val();
			var userPhone = $("#txtPopTel").val();
			var userEmail = $("#txtPopMail").val();
			
			if(userId == null || userId == "") { // 사용자 아이디가 입력되지 않은 경우 
				alert("사용자 아이디를 입력하시기 바랍니다.");
				return;
			}
			if(userPw == null || userPw == "") { // 사용자 비밀번호가 입력되지 않은 경우
				alert("사용자 비밀번호를 입력하시기 바랍니다.");
				return;
			}
			
			var obj = {
				userId: userId,
				userPw: userPw,
				authType: authType,
				userName: userName,
				userSex: userSex,
				userPhone: userPhone,
				userEmail: userEmail
			}
			
			return obj;
		}

		function _finalize() {
		}

		return {
			init: _init,
			finalize: _finalize
		};
	};

	var userAdm = new UserAdm();
	userAdm.init();

})();

//# sourceURL=userAdm.js

 

[ util.js ] - 하단에 내용 추가 

/******************************************************************************
 * 
 * cfCustomExcelDownloadDyn
 * 엑셀 다운로드 (동적 파라미터 형태)
 * 
 ******************************************************************************/
function cfCustomExcelDownloadDyn(url, obj, method) {
	
	var keys = Object.keys(obj);
	
	var form = "<form action='" + url + "' method='" + (method ? method : 'POST') + "'>";
	
	for(var i = 0; i < keys.length; i++) {
		form += "<input type='hidden' name='" + keys[i] + "' value='" + obj[keys[i]] + "' />"; 
	}
	
	form += "</form>";
	
	$(form).appendTo("body").submit().remove();
}

 

 

[ UserAdmController.java ]

package kr.co.art.biz.userAdm.web;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import kr.co.art.biz.userAdm.persistence.UserAdmMapper;
import kr.co.art.biz.userAdm.service.UserAdmService;

@Controller
@RequestMapping("/userAdm")
public class UserAdmController {
	
	@Autowired
	private UserAdmMapper userAdmMapper;
	
	@Autowired
	private UserAdmService userAdmService;
	
	@RequestMapping("")
	public String main() {
		return "userAdm";
	}
	
	/**
	 * 등록된 사용자 리스트 조회
	 * (/userAdm/findUserInfo)
	 * @return
	 */
	@RequestMapping("/findUserInfo")
	@ResponseBody
	public List<Map<String, Object>> findUserInfo(@RequestBody Map<String, Object> param) {
		List<Map<String, Object>> list = userAdmMapper.findUserInfo(param);
		
		return list;
	}
	
	/**
	 * 등록된 사용자 삭제
	 * (/userAdm/saveUserInfoRemove)
	 * @return
	 */
	@RequestMapping("/saveUserInfoRemove")
	@ResponseBody
	public Map<String, Object> saveUserInfoRemove(@RequestBody List<Map<String, Object>> param) {
		Map<String, Object> result = new HashMap<String, Object>();
		userAdmService.saveUserInfoRemove(param);
		
		result.put("success", true);
		return result;
	}
	
	/**
	 * 신규 사용자 등록
	 * (/userAdm/saveUserInfo)
	 * @return
	 */
	@RequestMapping("/saveUserInfo")
	@ResponseBody
	public Map<String, Object> saveUserInfo(@RequestBody Map<String, Object> param) {
		Map<String, Object> result = new HashMap<String, Object>();
		userAdmService.saveUserInfo(param);
		
		result.put("success", true);
		return result;
	}
	
	/**
	 * 사용자 정보 수정
	 * (/userAdm/saveModifyUserInfo)
	 * @return
	 */
	@RequestMapping("/saveModifyUserInfo")
	@ResponseBody
	public Map<String, Object> saveModifyUserInfo(@RequestBody Map<String, Object> param) {
		Map<String, Object> result = new HashMap<String, Object>();
		userAdmService.saveModifyUserInfo(param);
		
		result.put("success", true);
		return result;
	}
	
	/**
	 * 엑셀 다운로드
	 * (/userAdm/findExcel)
	 * @return
	 */
	@RequestMapping("/findExcel")
	public String findExcel(@RequestParam Map<String, Object> param, ModelMap map) {
		List<Map<String, Object>> list = userAdmMapper.findUserInfo(param);
		
		map.put("list", list);
		map.put("excelFile", "userInfo.xlsx");
		
		return "excelView";
	}
	
}

 

[ UserAdmService.java ]

package kr.co.art.biz.userAdm.service;

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

public interface UserAdmService {
	// 등록된 사용자 정보 삭제 
	void saveUserInfoRemove(List<Map<String, Object>> list);
	
	// 신규 사용자 등록
	void saveUserInfo(Map<String, Object> map);
	
	// 사용자 정보 수정 
	void saveModifyUserInfo(Map<String, Object> map);
}

 

[ UserAdmServiceImpl.java ] 

package kr.co.art.biz.userAdm.service;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import kr.co.art.biz.userAdm.persistence.UserAdmMapper;

@Service
public class UserAdmServiceImpl implements UserAdmService {

	@Autowired
	private UserAdmMapper userAdmMapper;
	
	@Autowired
	private PasswordEncoder passwordEncoder;
	
	@Override
	public void saveUserInfoRemove(List<Map<String, Object>> list) {
		
		// 체크한 항목이 여러개일 수 있으므로
		// 리스트로 전달받고 for문 돌려서 1개씩 삭제
		for(Map<String, Object> map : list) {
			userAdmMapper.saveUserInfoRemove(map);
		}
	}
	
	@Override
	public void saveUserInfo(Map<String, Object> map) {
		
		String orgPassword = (String) map.get("userPw");
		String userId = (String) map.get("userId");
		String encPassword = initUserPassword(orgPassword, userId);
		map.put("userPw", encPassword); // 암호화한 비밀번호로 다시 세팅
		userAdmMapper.saveUserInfo(map);
	}
	
	@Override
	public void saveModifyUserInfo(Map<String, Object> map) {
		
		String orgPassword = (String) map.get("userPw");
		String userId = (String) map.get("userId");
		String encPassword = initUserPassword(orgPassword, userId);
		map.put("userPw", encPassword); // 암호화한 비밀번호로 다시 세팅
		userAdmMapper.saveModifyUserInfo(map);
	}
	
	private String initUserPassword(String orgPassword, String userId) {
        return passwordEncoder.encode(orgPassword);
    }

}

 

[ UserAdmMapper.java ]

package kr.co.art.biz.userAdm.persistence;

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

public interface UserAdmMapper {
	
	// 등록된 사용자 리스트 조회
	List<Map<String, Object>> findUserInfo(Map<String, Object> params);
	
	// 사용자 정보 삭제 
	void saveUserInfoRemove(Map<String, Object> params);
	
	// 사용자 정보 등록
	void saveUserInfo(Map<String, Object> params);
	
	// 사용자 정보 수정 
	void saveModifyUserInfo(Map<String, Object> params);
	
}

 

[ UserAdmMapper.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="kr.co.art.biz.userAdm.persistence.UserAdmMapper">

	<select id="findUserInfo" resultType="hashmap" parameterType="hashmap">
		SELECT USER_ID 
		     , USER_NAME 
		     , CASE WHEN AUTH_TYPE = '1' THEN '일반 사용자' ELSE '관리자' END AUTH_TYPE 
		     , CASE WHEN USER_SEX = '1' THEN '남자' ELSE '여자' END USER_SEX 
		     , CONCAT(SUBSTR(USER_PHONE, 1, 3), '-', SUBSTR(USER_PHONE, 4, 4), '-', SUBSTR(USER_PHONE, 8, 4)) USER_PHONE
		     , USER_EMAIL 
		     , USER_ADDR 
		     , ZIP_CODE 
		     , OUT_YN 
		     , DATE_FORMAT(INPUT_DATETIME, '%Y-%m-%d') INPUT_DATETIME 
		  FROM ART.ART_USER
		 WHERE 1=1
		 <if test="!''.equals(userId)">
		 	AND USER_ID LIKE CONCAT('%', #{userId}, '%')
		 </if>
		 <if test="!''.equals(userName)">
		 	AND USER_NAME LIKE CONCAT('%', #{userName}, '%')
		 </if>
		 <if test="!''.equals(userAuth)">
		 	AND AUTH_TYPE = #{userAuth}
		 </if>
	</select>
	
	<delete id="saveUserInfoRemove" parameterType="hashmap">
		DELETE FROM ART.ART_USER
		 WHERE 1=1
		   AND USER_ID = #{USER_ID}
	</delete>
	
	<insert id="saveUserInfo" parameterType="hashmap">
		INSERT INTO ART.ART_USER (
				USER_ID
			  , AUTH_TYPE
			  , USER_PW
			  , USER_NAME
			  , USER_SEX
			  , USER_PHONE
			  , USER_EMAIL
			  , OUT_YN
			  , INPUT_DATETIME
		)
		VALUES (
			  #{userId}
			, #{authType}
			, #{userPw}
			, #{userName}
			, #{userSex}
			, #{userPhone}
			, #{userEmail}
			, 'N' -- default 
			, NOW()
		)
	</insert>
	
	<update id="saveModifyUserInfo" parameterType="hashmap">
		UPDATE ART.ART_USER 
		   SET	USER_NAME = #{userName}
			  , USER_SEX = #{userSex}
			  , USER_PHONE = #{userPhone}
			  , USER_EMAIL = #{userEmail}
			  , USER_PW = #{userPw}
		  WHERE 1=1
		    AND USER_ID = #{userId}
	</update>
	
</mapper>

 

그리드를 활용하니 스타일을 따로 주거나 할 필요가 없어서 편함.

데이터 조회, 삭제, 추가, 엑셀 기능이 포함됨

 

사용자 신규등록, 더블클릭 후 수정 시 사용될 모달 팝업창

 

[ 엑셀 다운로드 기능 ]

 

Apache Poi 활용 - 엑셀 다운로드 기능을 구현하기 위해 

pom.xml 파일에 라이브러리 의존성 추가 

<!-- Excel POI -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.13</version>
		</dependency>

		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.13</version>
		</dependency>

		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml-schemas</artifactId>
			<version>3.13</version>
		</dependency>
		
		<!-- Apache POI CSV -->
		<dependency>
		    <groupId>org.apache.commons</groupId>
		    <artifactId>commons-csv</artifactId>
		    <version>1.5</version>
		</dependency>
		
		<!-- jxls (with poi - custview) -->
		<dependency>
			<groupId>net.sf.jxls</groupId>
			<artifactId>jxls-core</artifactId>
			<version>1.0.6</version>
		</dependency>

 

[ CustomExcelView.java ]

package kr.co.art.sys.framework.ui;

import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

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

import net.sf.jxls.transformer.XLSTransformer;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.view.document.AbstractExcelView;


public class CustomExcelView extends AbstractExcelView {

	@Autowired
	private ServletContext servletContext;
	
	@Override
	protected void buildExcelDocument(Map<String, Object> model,
			HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		
		XLSTransformer transformer = new XLSTransformer();
		
		String excelFileName = (String) model.get("excelFile");
		String saveExcelName = (String) model.get("saveName");
		if(!"".equals(saveExcelName) && saveExcelName != null) {
			saveExcelName = new String(saveExcelName.getBytes("UTF-8"), "ISO-8859-1");
		}
		
		// markAsFixedSizeCollection 설정
		String[] array = (String[]) model.get("fixedSizeCollections");
		if(array != null) {
			for(int i = 0; i < array.length; i++) {
				transformer.markAsFixedSizeCollection(array[i]);
			}
		}
		
		String path = servletContext.getRealPath("/WEB-INF/readExcel/");
		
		String templateFileName = path + "/" + excelFileName;
		
		Workbook resultWorkbook = transformer.transformXLS(new FileInputStream(templateFileName), model);
		
		StringBuffer contentDisposition = new StringBuffer();
		contentDisposition.append("attachment;fileName=\"");
		if(!"".equals(saveExcelName) && saveExcelName != null) {
			contentDisposition.append(saveExcelName);
		} else {
			contentDisposition.append(excelFileName);
		}
		contentDisposition.append("\";");
		
		response.setHeader("Content-Disposition", contentDisposition.toString());
		response.setContentType("application/vnd.ms-excel");
		resultWorkbook.write(response.getOutputStream());
		
	}

}

 

[ excel 템플릿 파일 생성 및 프로젝트 삽입 ]

- /WEB-INF/readExcel 폴더 생성 후 아래에 엑셀 템플릿 파일 위치함

 

 

 

[ dispatcher-servlet.xml ] - 파일 수정 : 아래 내용 추가

<!-- excel view --> <bean id="excelView" class="kr.co.art.sys.framework.ui.CustomExcelView"/>