<script type="text/javascript">
$(document).ready(function() {
	//임의 숫자 세팅
	$("input[name='ipt_calc']").each(function(){
		$(this).val((Math.random() * 100).toFixed());
	});
});

function fnCalc(targetNm){
	//최대값 구하기
	let maxValue = Math.max.apply(null, $("#" + targetNm + "Area input[type='number']").map(function (){return $(this).val();}).get());
	//0을 제외한 최소값 구하기
	let minValue = Math.min.apply(null, $("#" + targetNm + "Area input[type='number']").map(function (){return $(this).val() == 0 ? 99999 : $(this).val();}).get());

	$("#sp_area").text(targetNm);
	$("#sp_max").text(maxValue);
	$("#sp_min").text(minValue);
}
</script>

	<div>
		<div>area : <span id="sp_area"></span></div>
		<div>max : <span id="sp_max"></span></div>
		<div>min : <span id="sp_min"></span></div>
	</div>
	<br /><br />
	<div id="firstArea">
		<div>firstArea <button type="button" onclick="fnCalc('first');">calc</button></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
	</div>
	<div id="secondArea">
		<div>secondArea <button type="button" onclick="fnCalc('second');">calc</button></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
		<div><input type="number" name="ipt_calc" value="" /></div>
	</div>
반응형

스크립트로 엑셀 업로드가 가능하여 테스트해보고 스크립트로 Export도 가능할듯하여 테스트.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>엑셀 Export 테스트</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.9/xlsx.full.min.js"></script>
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"></script>
<script>
	function upload(event) {
		var input = event.target;
		var reader = new FileReader();
		reader.onload = function() {
			var fdata = reader.result;
			var read_buffer = XLSX.read(fdata, {type : 'binary'});
			
			var xlsArea = document.getElementById("xlsArea");
			var tableStr = "<table id='tableData' style='border:1px solid;'>";
			
			read_buffer.SheetNames.forEach(function(sheetName) {
				var rowdata = XLSX.utils.sheet_to_json(read_buffer.Sheets[sheetName]);
				//console.log("[" + sheetName + "] 시트 데이터 : " + JSON.stringify(rowdata));
				
				$.each(rowdata, function(i, coldata){
					//console.log(rowdata);
					
					//엑셀 Header 세팅
					if(i == 0){
						tableStr += "<tr>";
						$.each(coldata, function(colHeader, colText){
							tableStr += "<th style='color:red;width:500px;border:1px solid;'>" + colHeader + "<th>";
						});
						tableStr += "</tr>";
					}
					
					//엑셀 내용 세팅
					tableStr += "<tr>";
					$.each(coldata, function(colHeader, colText){
						tableStr += "<td style='border:1px solid;'>" + colText + "<td>";
					});
					tableStr += "</tr>";
				});
			});
			
			tableStr += "</table>";
			xlsArea.innerHTML = tableStr;
			//console.log(tableStr);
		};
		reader.readAsBinaryString(input.files[0]);
	}
	
	var excelHandler = {
		getExcelFileName : function() {
			return 'table-test.xlsx';
		},
		getSheetName : function() {
			return 'Table Test Sheet';
		},
		getExcelData : function() {
			return document.getElementById('tableData');
		},
		getWorksheet : function() {
			return XLSX.utils.table_to_sheet(this.getExcelData());
		}
	};
	
	function s2ab(s) { 
		var buf = new ArrayBuffer(s.length); //convert s to arrayBuffer
		var view = new Uint8Array(buf);  //create uint8array as viewer
		for (var i=0; i<s.length; i++) view[i] = s.charCodeAt(i) & 0xFF; //convert to octet
		return buf;
	}
	
	function fnExport(){
		// step 1. workbook 생성
		var wb = XLSX.utils.book_new();
		
		// step 2. 시트 만들기 
		var newWorksheet = excelHandler.getWorksheet();
		
		// step 3. workbook에 새로만든 워크시트에 이름을 주고 붙인다.  
		XLSX.utils.book_append_sheet(wb, newWorksheet, excelHandler.getSheetName());
		
		// step 4. 엑셀 파일 만들기 
		var wbout = XLSX.write(wb, {bookType:'xlsx',  type: 'binary'});
		
		// step 5. 엑셀 파일 내보내기 
		saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), excelHandler.getExcelFileName());
	}
</script>

</head>
<body>
	<div style="float: left;">
		<input type="file" id="id_file_upload" onchange="upload(event)"/>
	</div>
	<div style="float: right;">
		<button type="button" id="btnExport" onclick="fnExport();">엑셀 내보내기</button>
	</div>
	<br /><br />
	<div id="xlsArea"></div>
</body>
반응형

"엑셀 > 홈 > 조건부서식 > 새 규칙 "으로 색상을 표시할 수 있다. 마우스 드래그로 아래로 긁으면 매번 색상을 표시하지 않아도 된다.(현재 조건은 토요일과 일요일이면 배경색을 회색으로 표시하도록 설정)

 

 

반응형
    <script>
        $(function(){
            //datepicker
            $('.datepicker').not('.readonly').datepicker({
                format: 'dd-mm-yyyy',
                startDate: '+1d'
            }).on("blur", function(){
            	var dataRegexp = /[0-9]{4}-?(0[1-9]|1[012])-?(0[1-9]|[12][0-9]|3[01])/;
                if (!dataRegexp.test($(this).val()) ) {
                    $(this).val("");
                }
            });
            $.datepicker.setDefaults({
                dateFormat: 'yymmdd',
                prevText: '이전 달',
                nextText: '다음 달',
                monthNames: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
                monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'],
                dayNames: ['일', '월', '화', '수', '목', '금', '토'],
                dayNamesShort: ['일', '월', '화', '수', '목', '금', '토'],
                dayNamesMin: ['일', '월', '화', '수', '목', '금', '토'],
                showMonthAfterYear: true,
                changeYear: true, //콤보박스에서 년 선택 가능
                changeMonth: true, //콤보박스에서 월 선택 가능
                yearRange: 'c-100:c+10'
            });
        });
    </script>
반응형

스크립트로 엑셀 업로드가 가능하여 테스트해봄.

 

case 1) 엑셀을 업로드하여 화면에 단순 표시

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>엑셀 업로드 테스트</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.9/xlsx.full.min.js"></script>
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script>
	function upload(event) {
		var input = event.target;
		var reader = new FileReader();
		reader.onload = function() {
			var fdata = reader.result;
			var read_buffer = XLSX.read(fdata, {type : 'binary'});
			
			var xlsArea = document.getElementById("xlsArea");
			var tableStr = "<table style='border:1px solid;'>";
			
			read_buffer.SheetNames.forEach(function(sheetName) {
				var rowdata = XLSX.utils.sheet_to_json(read_buffer.Sheets[sheetName]);
				//console.log("[" + sheetName + "] 시트 데이터 : " + JSON.stringify(rowdata));
				
				$.each(rowdata, function(i, coldata){
					//console.log(rowdata);
					
					//엑셀 Header 세팅
					if(i == 0){
						tableStr += "<tr>";
						$.each(coldata, function(colHeader, colText){
							tableStr += "<th style='color:red;width:500px;border:1px solid;'>" + colHeader + "<th>";
						});
						tableStr += "</tr>";
					}
					
					//엑셀 내용 세팅
					tableStr += "<tr>";
					$.each(coldata, function(colHeader, colText){
						tableStr += "<td style='border:1px solid;'>" + colText + "<td>";
					});
					tableStr += "</tr>";
				});
			});
			
			tableStr += "</table>";
			xlsArea.innerHTML = tableStr;
			//console.log(tableStr);
		};
		reader.readAsBinaryString(input.files[0]);
	}
</script>

</head>
<body>
	<input type="file" id="id_file_upload" onchange="upload(event)"/>
	<br /><br />
	<div id="xlsArea"></div>
</body>
</html>

 

case 2) 엑셀을 업로드하여 화면에서 수정 후 서버에 반영

(원래 엑셀을 서버로 업로드하는 부분과 export 하는 부분을 구현하려 하였으나 화면에 보여줄때 input 등으로 표시하고 수정하여 서버에 반영하면 필요없을 듯하여 수정)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>엑셀 업로드 테스트</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.9/xlsx.full.min.js"></script>
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script>
	function upload(event) {
		var input = event.target;
		var reader = new FileReader();
		reader.onload = function() {
			var fdata = reader.result;
			var read_buffer = XLSX.read(fdata, {type : 'binary'});
			
			var $xlsArea = $("#xlsArea");
			var $table = $("<table>", {style:"border:1px solid;"});
			
			read_buffer.SheetNames.forEach(function(sheetName) {
				var rowdata = XLSX.utils.sheet_to_json(read_buffer.Sheets[sheetName]);
				//console.log("[" + sheetName + "] 시트 데이터 : " + JSON.stringify(rowdata));
				
				$.each(rowdata, function(i, coldata){
					//console.log(rowdata);
					
					//엑셀 Header 세팅
					if(i == 0){
						var $headerTr = $("<tr>");
						$.each(coldata, function(colHeader, colText){
							var $th = $("<th>", {style : 'color:red;width:500px;border:1px solid;', text : colHeader});
							$headerTr.append($th);
						});
						$table.append($headerTr);
					}
					
					//엑셀 내용 세팅
					$tr = $("<tr>");
					$.each(coldata, function(colHeader, colText){
						var $td = $("<td>", {style : 'border:1px solid;'});
						var $ipt = $("<input>", {type : "input", value : colText, name : colHeader, style : "width:97%;"});
						$td.append($ipt);
						$tr.append($td);
					});
					$table.append($tr);
				});
			});
			
			$xlsArea.append($table);
			//console.log(tableStr);
		};
		reader.readAsBinaryString(input.files[0]);
	}
</script>

</head>
<body>
	<input type="file" id="id_file_upload" onchange="upload(event)"/>
	<br /><br />
	<div id="xlsArea"></div>
</body>
</html>

 

 

반응형

WebApplicationInitializer 인터페이스를 사용하면 web.xml에서 했던 서블릿, 필터, 리스너 설정 등을 java로 설정할 수 있다.
처음에 그것도 모르고 web.xml에 아무것도 없어 당황을....^^;

프로젝트를 하나 만들어 아래와 같이 간단히 등록하고 테스트를 해보니 정상 동작되는 것을 확인했다.

 

package testweb;
 
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
 
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;
 
public class Init implements WebApplicationInitializer {
 
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		System.out.println("onStartup...");
		addListender(servletContext);
		addDispatcher(servletContext);
		addFilter(servletContext);
	}
 
	public void addListender(ServletContext servletContext) {
		System.out.println("addListender...");
		AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
		appContext.register(AppConfig.class);
		ContextLoaderListener listender = new ContextLoaderListener(appContext);
		servletContext.addListener(listender);
	}
 
	public void addDispatcher(ServletContext servletContext) {
		System.out.println("addDispatcher...");
		AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
		webContext.register(WebConfig.class);
		DispatcherServlet dispatcherServlet = new DispatcherServlet(webContext);
		ServletRegistration.Dynamic webServlet = servletContext.addServlet("webServlet", dispatcherServlet);
		webServlet.addMapping("/");
		webServlet.setLoadOnStartup(1);
	}
 
	public void addFilter(ServletContext servletContext) {
		System.out.println("addFilter...");
		CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter("UTF-8", true, true);
		FilterRegistration.Dynamic webEncodingFilter = servletContext.addFilter("webEncodingFilter", encodingFilter);
		webEncodingFilter.addMappingForUrlPatterns(null, false, "/*");
	}
}

 

반응형

input type이 number 경우 maxlength가 먹히지 않어 스크립트 제어가 필요해짐.

/* ********************************************************
 * 기타 class 이벤트 적용
 * vPoint12 : 정수 10자리 소수점 2자리 까지 입력되도록
 * vNum10 : 정수만 10자리 입력되도록
 ******************************************************** */
function fnEtcClassEvent(){
    $(".vPoint12").each(function(){
        // .(마침표)가 양끝에 있는 케이스를 처리
        $(this).off('blur').on('blur',function(e){
            var value = $(this).val();
            var regExp = /^\.|\.$/;
            if(regExp.test(this.value)){
                $(this).val(value.replace('.',''));
            }
        });
        
        // 소수점 둘째자리까지의 실수만 입력 허용
        $(this).off('input').on('input',function(e){
            var value = $(this).val();
            var regExp = /^\d{0,10}(\.\d{0,2})?$/;
            if(!regExp.test(this.value)){
                $(this).val(value.substring(0,value.length-1));
            }
        });
        
        // 숫자와 .(마침표)만 입력 허용
        $(this).off('keypress').on('keypress',function(e){
            e = e || window.event;
            var charCode = e.which || e.keyCode;
            if (!((charCode >= 48 && charCode <= 57) || charCode === 46)){
                return false;
            }
        });
    });
    
    $(".vNum10").each(function(){
        // 정수만 입력 허용
        $(this).off('input').on('input',function(e){
            var value = $(this).val();
            var regExp = /^\d{0,10}$/;
            if(!regExp.test(this.value)){
                $(this).val(value.substring(0,value.length-1));
            }
        });
        
        // 숫자만 입력 허용
        $(this).off('keypress').on('keypress',function(e){
            e = e || window.event;
            var charCode = e.which || e.keyCode;
            if (!((charCode >= 48 && charCode <= 57))){
                return false;
            }
        });
    });
}

반응형
/* ********************************************************
 * 기타 벨리데이션
 * vReq : 필수입력 체크(class)
 ******************************************************** */
function fnEtcValid(){
	var isPass = true;
	$.each($(".vReq"), function(idx, obj){
		if(obj.value == "" && isPass){
			var vId = obj.id;
			var vLabel = $("label[for='" + vId + "']").text() || $("#" + vId).attr("title");
			var vStr = vLabel == "" ? "" : "[ " + vLabel + " ] 항목은 ";
			isPass = false;
			alert(vStr + "필수입력 항목입니다.");
			obj.focus();
			return false;
		}
	});
	return isPass;
}
반응형
$(document).ready(function() {
    // validate 전화번호 형식 추가
    jQuery.validator.addMethod("phone", function(phone_number, element) {
        phone_number = phone_number.replace(/\s+/g, ""); 
        return this.optional(element) || phone_number.length > 9 && phone_number.match(/^(01[016789]{1}|02|0[3-9]{1}[0-9]{1})-?[0-9]{3,4}-?[0-9]{4}$/);
    }, "잘못된 입력 형식입니다");
    
    // validate date 형식 추가
    jQuery.validator.addMethod("date", function(value, element) {
        return this.optional(element) || value.match(/^(19|20)?\d{2}[/., -](0?[1-9]|1[0-2])[/., -](0?[1-9]|[12][0-9]|3[0-1])$/);
    }, "잘못된 입력 형식입니다");
});
반응형

이런 저런 이유(건강, 일없음.... ㅜ.ㅡ)로 쉬면서 웹사이트를 하나 만들다가 발견한 사이트.

 

무료로 텍스트 로고를 바로 제작해서 다운받아 사용할 수 있다.

 

사업용 같은 사이트를 만들려면 돈 좀 들여 로고 제작을 하겠지만... 취미나 간단한 웹사이트를 만들때는 유용할 듯하다. ^-^;

 

글자 이펙트나 색상 옵션 등 설정가능하고 다른유형의 로고를 덧붙여 만드는 등 여러 옵션기능 존재.

 

Link : Cool Text Graphics Generator

 

Cool Text Graphics Generator

 

반응형

'개발 > 참고사이트' 카테고리의 다른 글

부트스트랩 무료 템플릿 사이트  (1) 2020.08.11
무료 아이콘 사이트  (0) 2020.07.10
원격데스크탑 연결  (0) 2020.07.10
PPT 무료템플릿 모아놓은 사이트  (2) 2019.06.05
Mybatis 매퍼 설정관련  (0) 2019.02.07

봉인 장비 변환이라는 던파 이벤트 때문에 100렙제 에픽이 랜덤 변환이라 


세트가 맞추어 졌는지 확인용으로 엑셀로 작성


던파 100렙제 에픽 목록.xlsx


반응형

예를 들어 아래와 같은 소스가 있다고 가정하고 모든 파라미터 앞에 #{schStartDate}가 아닌 #{SUBINTO.schStartDate}로 바꾸고 싶다고 가정하면


   ...

 WHERE DTC.TIME_STEMP BETWEEN #{schStartDate} AND #{schEndDate}

  AND GI.AREA_ID = A.AREA_ID

  AND GI.AREA_ID = #{schAreaId}

   ...



위와 같이 입력하여 처리가 가능하다. 

Replace with의 \1은 Find에서 "( )"로 감싼 부분이고 Find에 "( )"가 뒤에 또 있다면 Replace with에 \2, \3... 으로 표현 가능하다.


cf) Find나 Replace with 입력창에서 ctrl + space를 입력하여 도움말을 참고할 수 있다.

반응형

1. 이클립스 가동 후 Server 탭에서 IBM 폴더의 Liberty 서버 생성

  -> 필요한 파일을 다운받느라 어느정도 시간이 걸리고 다 받은 후 이클립스 재시작 알림창으로 재시작


2. 아래 Url에서 WebSphere 파일 받은 후 적당한 위치에 압축 풀기

  -> https://developer.ibm.com/wasdev/downloads/#asset/runtimes-wlp-webProfile8


cf) Maven으로 받는 방법도 있음

<dependency>

  <groupId>com.ibm.websphere.appserver.runtime</groupId>

  <artifactId>wlp-webProfile8</artifactId>

  <version>20.0.0.8</version>

  <type>zip</type>

</dependency>


3. 이클립스 Server 탭에서 IBM 폴더의 Liberty 서버 다시 생성하기

  -> Runtime Environment에서 1.번에서 압축 풀은 폴더 지정하기


4. Server 탭에서 해당서버 컨텍스트 루트 및 인코딩 지정하고 서버 시작하여 확인


반응형

'개발 > 기타' 카테고리의 다른 글

JSP에 오늘 날짜 또는 시간 표시  (0) 2019.04.17
JSTL c:out 데이터 표시할때 태그 적용  (0) 2019.03.04

bootstrap template 및 예제, 설명 등


http://bootstrapk.com/getting-started/



해외 bootstrap template 제공 사이트


https://www.bootstrapzero.com/browse

반응형

구글을 검색해서 본 노트북 CMOS, BIOS supervisor password 찾기


case 1. 노트북 배터리 및 CMOS 리튬배터리(시계배터리 모양)을 분리하고 방전되도록 납두어서 초기화


  -> acer nitro 5 노트북에서는 리튬배터리를 못찾겠음 ㅜ.ㅜ



case 2. acer esettings management 유틸 프로그램을 이용하여 패스워드 초기화


  -> acer 홈페이지에서 프로그램 유틸이 없어졌음. 이젠 지원을 안하는듯함 ㅠ.ㅠ



case 3. online bios 패스워스 생성하여 bios 진입

  3.1 노트북 시작시 F2를 클릭하여 바이오스 진입

  3.2 비밀번호를 3번 틀리고 4번째 나오는 unlock key 번호 적어두기

  3.3 https://bios-pw.org/ 접속하여 적어놓은 key 번호 입력하여 키생성하기

  3.4 노트북을 다시 시작해 비밀번호 3번 틀리고 4번째에 생성한 키번호로 bios 진입하여 암호 재설정하기


  -> 1년전쯤 검색하였을때는 3.3의 주소가 아닌 다른 URL 주소를 통해 키 생성하는데가 많아서 키를 생성해도 BIOS에 진입이 안되었음. 

  -> 2020년 8월 10일 현재 혹시나 싶어 다시 검색하여 찾아보니 https://bios-pw.org/ 주소를 알게되어 key 번호를 입력하니 기존 생성했을때와 다르게 하나의 키정보를 더 생성하여 보여줘서 암호를 풀었다. 



반응형

* "ALL_" 대신에 "USER_"를 사용하고 OWNER 조건을 빼서 사용할 수도 있다.


--전체 계정 전체 테이블 조회

SELECT * FROM ALL_TABLES;


--사용자계정 전체 테이블 조회

SELECT * FROM ALL_TABLES WHERE OWNER = 'SUBINTO';

 

 --사용자계정 전체 테이블 커멘트 조회

 SELECT * FROM ALL_TAB_COMMENTS WHERE OWNER = 'SUBINTO';


--사용자계정 전체 컬럼 조회

 SELECT * FROM ALL_TAB_COLUMNS WHERE OWNER = 'SUBINTO';

 

 --사용자계정 전체 컬럼 커멘트 조회

 SELECT * FROM ALL_COL_COMMENTS WHERE OWNER = 'SUBINTO';


* 위 쿼리를 활용하여 쿼리 생성 쿼리를 만들수도 있다.(아래는 SELECT 쿼리를 생성하는 쿼리 참조)


--쿼리 생성 쿼리(SELECT)

SELECT DECODE(ROWNUM, 1, 'SELECT ' ||  CHR(13), ', ')

    || UPPER(COLUMN_NAME) || ' AS ' ||LOWER(SUBSTR(COLUMN_NAME, 1,1))||SUBSTR(REPLACE(INITCAP(LOWER(COLUMN_NAME)), '_',''), 2)    

    || DECODE(ROWNUM, (COUNT(1) OVER()), CHR(13) || '  FROM '|| TABLE_NAME ||';', '') AS "SELECT Query"

 FROM ALL_TAB_COLUMNS

WHERE OWNER = 'SUBINTO'

  AND TABLE_NAME = UPPER('TB_MENU');


반응형

* Tabulator Tree 세팅은 아래의 이전 글 참조


Tabulator Tree 데이터 세팅 및 노드 추가 처리 스크립트



1.Tabulator 생성시 columns에 아래 체크박스 컬럼 추가


            {

                title:"매핑여부",

                field:"mappingGubun",

                width:80,

                hozAlign:"center",

                download:false,

                vertAlign:"middle",

                editor:"tickCross",

                formatter:"tickCross",

                formatterParams: {

                    allowTruthy:true,

                    tickElement:"<input type='checkbox' checked>",

                    crossElement:"<input type='checkbox'>"

                },

                cellEdited:function(cell) { 

                    if (cell.getValue())

                        checkedTreeNode(menuMngSheet, cell, 1); //해당 체크 박스 및 하위 체크박스 체크

                    else

                        checkedTreeNode(menuMngSheet, cell, 0); //해당 체크 박스 및 하위 체크박스 체크해제

                },

                headerFilter:"tickCross",

                headerFilterEmptyCheck:function(value){

                    fnHeaderCheck(value); //체크박스 전체 체크

                    return true;

                }

            },


2. Tabulator Tree 체크박스 관련 함수 추가(3레벨 메뉴까지 있는 Tree를 기준으로 하였음. 상황에 맞게 수정)


//Tree 체크박스 체크시 하위 노드 체크기능

function checkedTreeNode(vSheet, cell, vCheckValue){

    var rows = vSheet.getRows();

    

    var row = cell.getRow();

    var sSeq = row.getData()["sSeq"];

    

    var isChecked = false;

    var isCheckedLvl = -1;

    var isPass = false;


    if(rows != null && rows.length > 0){

        //1레벨 확인

        for(var i = 0 ; i < rows.length ; i++){

            var lvl1Row = rows[i];

            var lvl1Children = rows[i].getTreeChildren();

            

            if(lvl1Row.getData()["sSeq"] == sSeq && !isPass){

                isChecked = true;

                isCheckedLvl = lvl1Row.getData()["menuLvl"];

                if(lvl1Row.getCell("mappingGubun")){

                    cellEditedCallback(lvl1Row.getCell("mappingGubun"), "U", vCheckValue);

                }

            }

            if(lvl1Children != undefined){

                //2레벨 확인

                for(var j = 0 ; j < lvl1Children.length ; j++){

                    var lvl2Row = lvl1Children[j];

                    var lvl2Children = lvl2Row.getTreeChildren();

                    

                    if((lvl2Row.getData()["sSeq"] == sSeq || (isChecked && isCheckedLvl != lvl2Row.getData()["menuLvl"])) && !isPass){

                        isChecked = true;

                        isCheckedLvl = isCheckedLvl == -1 ? lvl2Row.getData()["menuLvl"] : isCheckedLvl;

                        if(lvl2Row.getCell("mappingGubun")){

                            cellEditedCallback(lvl2Row.getCell("mappingGubun"), "U", vCheckValue);

                        }

                    }

                    if(lvl2Children != undefined){

                        //3레벨 확인

                        for(var k = 0 ; k < lvl2Children.length ; k++){

                            var lvl3Row = lvl2Children[k];

                            

                            if((lvl3Row.getData()["sSeq"] == sSeq || (isChecked && isCheckedLvl != lvl3Row.getData()["menuLvl"])) && !isPass){

                                isChecked = true;

                                isCheckedLvl = isCheckedLvl == -1 ? lvl3Row.getData()["menuLvl"] : isCheckedLvl;

                                if(lvl3Row.getCell("mappingGubun")){

                                    cellEditedCallback(lvl3Row.getCell("mappingGubun"), "U", vCheckValue);

                                }

                            }

                            if(isChecked && isCheckedLvl == lvl3Row.getData()["menuLvl"]) isPass = true;

                        }

                    }

                    if(isChecked && isCheckedLvl == lvl2Row.getData()["menuLvl"]) isPass = true;

                }

            }

            if(isChecked && isCheckedLvl == lvl1Row.getData()["menuLvl"]) isPass = true;

        }

    }

}


//Tree 전체 체크박스 체크시 하위 노드 체크기능

function checkedAllTreeNode(vSheet, vCheckValue){

    var treeData = vSheet.getData();

    

    if(treeData != null && treeData.length > 0){

        for(var i = 0 ; i < treeData.length ; i++){

            var menuLvl1Map = treeData[i];

            menuLvl1Map.mappingGubun = vCheckValue;

            menuLvl1Map.sStatus = "U";

            

            //1레벨 하위항목

            if(menuLvl1Map._children != undefined) {

                for(var j = 0 ; j < menuLvl1Map._children.length ; j++){

                    var menuLvl2Map = menuLvl1Map._children[j];

                    menuLvl2Map.mappingGubun = vCheckValue;

                    menuLvl2Map.sStatus = "U";

                    

                    //2레벨 하위항목

                    if(menuLvl2Map._children != undefined) {

                        for(var k = 0 ; k < menuLvl2Map._children.length ; k++){

                            var menuLvl3Map = menuLvl2Map._children[k];

                            menuLvl3Map.mappingGubun = vCheckValue;

                            menuLvl3Map.sStatus = "U";

                        }

                    }

                }

            }

        }    

    }

    vSheet.setData(treeData);

}



반응형

<style type="text/css">

// 미디어쿼리를 이용한 가로모드, 세로모드일때의 css 적용 방법

@media all and (orientation:portrait) {

// 세로모드일때의 css 적용

}

@media all and (orientation:landscape) {

// 가로모드일때의 css 적용

}

</style>


<script type="text/javascript">

$(document).ready(function() {

// matchMedia를 이용한 가로모드, 세로모드일때의 script 적용 방법

var media = window.matchMedia("(orientation: portrait)");

if (media.matches) {

// 세로모드일때의 script 적용

}

else{

// 가로모드일때의 script 적용

}

});

</script>


cf. 같은 내용으로 window.innerWidth와 window.innerHeight의 크기를 비교하여 가로모드인지 세로모드인지 판별할 수 있다.

반응형

무료 아이콘 사이트 / 유료도 있음


https://fontawesome.com/

반응형

여러곳의 원격데스크탑을 연결해야할때 보다 직관적이고 편함



https://mremoteng.org/download

반응형

'개발 > 참고사이트' 카테고리의 다른 글

부트스트랩 무료 템플릿 사이트  (1) 2020.08.11
무료 아이콘 사이트  (0) 2020.07.10
PPT 무료템플릿 모아놓은 사이트  (2) 2019.06.05
Mybatis 매퍼 설정관련  (0) 2019.02.07
jquery ztree  (0) 2018.11.02

* Maven dependency 추가


<dependency>

<groupId>org.java-websocket</groupId>

<artifactId>Java-WebSocket</artifactId>

<version>1.5.1</version>

</dependency>


1. TestEndpointServer.java (Websocket Server)


import javax.websocket.OnClose;

import javax.websocket.OnError;

import javax.websocket.OnMessage;

import javax.websocket.OnOpen;

import javax.websocket.server.ServerEndpoint;


/**

 * Websocket Test Server

 */

@ServerEndpoint("/websocket")

public class TestEndpointServer {

/**

* 웹 소켓이 연결되었을 때 호출(또는 URL 접근시 호출)

*/

@OnOpen

public void connectionOpen() {

System.out.println("################# client is connected!");

}


/**

* 웹 소켓에서 메시지가 날라왔을 때 호출

* cf) 메세지를 받기만 할 경우 return 값을 void로 설정

*/

@OnMessage

public String receiveMessage(String message) {

System.out.println("################# Message from the client : " + message);

String sendMessage = "return message " + message + " -> Lucky Number : " + Math.floor(Math.random() * 100);

System.out.println("################# Echo from the server : " + sendMessage);

return sendMessage;

}


/**

* 웹 소켓이 닫혔을 때 호출(또는 브라우저가 닫혔을 때 호출)

*/

@OnClose

public void connectionClose() {

System.out.println("################# client is disconnected!");

}


/**

* 웹 소켓이 에러가 났을 때 호출

* @param t

*/

@OnError

public void connectionError(Throwable t) {

t.printStackTrace();

}

}


2-1. TestEndpointClient.java (Websocket Client : java 테스트용 1. 서버를 띄운뒤 2-1, 2-2 같이 작성해서 돌려야됨)


import java.io.IOException;

import java.net.URI;


import javax.websocket.ClientEndpoint;

import javax.websocket.ContainerProvider;

import javax.websocket.OnOpen;

import javax.websocket.Session;

import javax.websocket.WebSocketContainer;


/**

 * Websocket Test Client

 */

@ClientEndpoint

public class TestEndpointClient {

public String SocketIp = "127.0.0.1";

public String SocketPort = "7200";

public String SocketPath = "/websocket";


Session userSession = null;


/**

* Websocket 연결

*/

public TestEndpointClient() {

try {

WebSocketContainer container = ContainerProvider.getWebSocketContainer();

container.connectToServer(this, new URI("ws://" + SocketIp + ":" + SocketPort + SocketPath));

} catch (Exception e) {

throw new RuntimeException(e);

}

}


/**

* 웹 소켓이 연결되었을 때 호출

*/

@OnOpen

public void onOpen(Session userSession) {

System.out.println("################# open Websocket");

this.userSession = userSession;

}


/**

* 웹 소켓 닫기

* @OnClose을 사용하였으나 연결이 끈기지 않어(왜?) 직접 연결끈기...ㅜ.ㅡ

*/

public void onClose() {

System.out.println("################# close Websocket");

try {

if(this.userSession != null) {

this.userSession.close();

}

} catch (IOException e) {

e.printStackTrace();

}

finally {

this.userSession = null;

}

}


/**

* 웹 소켓에 메시지 보내기

*/

public void sendMessage(String message) {

System.out.println("################# sendMessage : " + message);

this.userSession.getAsyncRemote().sendText(message);

}

}


2-2. TestEndpointJavaRun.java


public class TestEndpointJavaRun {


public static void main(String[] args) {

try {

// open websocket

TestEndpointClient clientEndPoint = new TestEndpointClient();

// send message

clientEndPoint.sendMessage("메세지를 첫번째로 보냅니다......");

clientEndPoint.sendMessage("메세지를 두번째로 보냅니다......");

clientEndPoint.sendMessage("메세지를 세번째로 보냅니다......");


// close websocket

clientEndPoint.onClose();


} catch (Exception e) {

e.getStackTrace();

}

}

}


3. TestEndpointJspRun.jsp (Websocket Client : jsp 테스트용 1. 서버를 띄운뒤 3. jsp에 접근해서 테스트)


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

<!DOCTYPE html>

<html>

<head>

<title>Websocket Test</title>

</head>

<body>

<form>

<input id="message" type="text">

<input onclick="wsSendMessage();" value="Echo" type="button">

<input onclick="wsCloseConnection();" value="Disconnect" type="button">

</form>

<br>

<textarea id="echoText" rows="30" cols="100"></textarea>

<script type="text/javascript">

var webSocket = new WebSocket("ws://127.0.0.1:7200/websocket");

var echoText = document.getElementById("echoText");

echoText.value = "";

var message = document.getElementById("message");

webSocket.onopen = function(message){ wsOpen(message);};

webSocket.onmessage = function(message){ wsGetMessage(message);};

webSocket.onclose = function(message){ wsClose(message);};

webSocket.onerror = function(message){ wsError(message);};

function wsOpen(message){

echoText.value += "Connected ... \n";

}

function wsSendMessage(){

webSocket.send(message.value);

echoText.value += "Message sent to the server : " + message.value + "\n";

message.value = "";

}

function wsCloseConnection(){

webSocket.close();

}

function wsGetMessage(message){

echoText.value += "Message received from to the server : " + message.data + "\n";

}

function wsClose(message){

echoText.value += "Disconnect ... \n";

console.log("disconnect", message);

}


function wsError(message){

echoText.value += "Error \n";

console.log("error", message);

}

</script>

</body>

</html>






반응형

Ctrl + Shift + R 로 검색을 할때 컴파일된 파일도 같이 검색될때가 있다.


그럴땐 해당 폴더를 클릭한 후 Properties > Resource > Attribute 에서 Derived를 체크 한 후 적용하면 검색할 때 제외되서 나온다.


반응형

1. Tabulator Grid 객체 생성


cfgItemSheet = new Tabulator("#sheet-containerItem", {

    height:"750px", // virtualDom 사용위해 필수

        layout:"fitColumns",

    headerSort:false,

        selectable: 1,

        dataTree:true,

        dataTreeStartExpanded:true,

        rowClick:function(e, row){

        //selected Row 없어지는 것 방지

        var selectedData = cfgItemSheet.getSelectedData();

        if(selectedData == "") row.toggleSelect();

        },

        ajaxConfig: "POST",

        ajaxContentType:"json",

        ajaxResponse:function(url, params, response) {

    if (response.Message === "OK") {

$('#message').css("color", "#FFFFFF");

$('#message').html("* 조회 완료 : 총 " + response.Data.length + "건");

        return bindListToTree(response.Data);

} else {

$('#message').css("color", "#FF3A00");

$('#message').html("* " + response.Message);

return [];

}

        },

        columns:[

            {

            title:"항목관리",

            columns:[

            {

                    title:"번호",

                    field:"sSeq",

                    width:80,

                    minWidth:60,

                    hozAlign:"left"

            },

            {

                title:"상태",

                field:"sStatus",

                width:80,

                minWidth:60,

                hozAlign:"center",

                download:false,

                formatter: "lookup",

                formatterParams: {

                "D":"삭제", "I":"입력", "U":"수정", undefined:""

                }

            },

            {

            title: "항목명",

                    field: "itemNm",

                    hozAlign:"center",

editor:"input",

            cellEdited:function(cell) {

            var rowData = cell.getRow().getData();

            if (rowData.sStatus != "I")

            cellEditedCallback(cell, "U", 0);

            },

                    validator : ["maxLength:100"]

            },

            {

            title: "항목ID",

                    field: "itemId",

                    download: true,

                    hozAlign:"center",

                    visible : false

            },

            {

            title: "상위항목ID",

                    field: "itemTop",

                    hozAlign:"center",

                    download: true,

                    visible : false

            },

            {

            title: "레벨",

                    field: "itemLvl",

                    hozAlign:"center",

                    download: true,

                    visible : false

            },

            {

            title: "정렬순서",

                    field: "sortOrder",

                    hozAlign:"center",

editor:"input",

            cellEdited:function(cell) {

            var rowData = cell.getRow().getData();

            if (rowData.sStatus != "I")

            cellEditedCallback(cell, "U", 0);

            },

                    validator : ["integer", "maxLength:10"]

            },

        {

        title: "사용여부",

                field: "useYn",

                hozAlign:"center",

                editor:"select",

        formatter: "lookup",

        formatterParams: {"Y":"사용", "N":"미사용"},

        editorParams:{

        values:{"Y":"사용", "N":"미사용"}

        },

                    cellEdited:function(cell) {

                    var rowData = cell.getRow().getData();

                    if (rowData.sStatus != "I")

                    cellEditedCallback(cell, "U", 0);

                    }

        }

            ]

            }

        ]

    });


2. 노드 추가 관련 스크립트


//최상위 항목 등록

function addActionHandlerUp(ev) {

var newSSeq = searchTreeDataLength(cfgItemSheet) + 1;

cfgItemSheet.addRow({ "sStatus":"I", "sDelCheck":0, "sSeq":newSSeq, "itemLvl":"0", "itemId":"", "itemTop":vGrpId, "useYn":"Y"}, true);

//selected Row 체크

var rows = cfgItemSheet.getRows();

rows[0].toggleSelect();

}


//하위 항목 등록

function addActionHandlerDown(ev) {

var sheetData = cfgItemSheet.getData();

var selectedData = cfgItemSheet.getSelectedData()[0];


if(selectedData.sStatus == "I"){

$('#message').html("* 상위레벨을 먼저 저장해야 합니다.");

}

else if(parseInt(selectedData.itemLvl, 10) >= 2){

$('#message').html("* 3레벨까지만 생성이 가능합니다.");

}

else{

//Tree Data Row 추가

sheetData = addSelectedTreeDataRow(cfgItemSheet);


//Tree Data 세팅 및 세팅 후 selected Row 처리

cfgItemSheet.setData(sheetData).then(function(){

if(cfgItemSheet.getDataCount() > 0){

//신규 생성 Tree Data Row Selected

selectedTreeNewRow(cfgItemSheet);

}

});

}

}


3. 저장시 관련 스크립트


function saveActionHandler(ev) {

ev.preventDefault();


// 입력/수정/삭제 할 행

let rows = searchTreeData(cfgItemSheet, "sStatus", ["D", "I", "U"]);


//변경된 Row의 데이터를 JSON 형태로 받음.

//var jsonString = cfgItemSheet.GetSaveJson();

if(rows.length < 1) {

$('#message').html("* 저장할 데이터가 없습니다!");

return;

}

// 신호장치명, 장치코드, 정렬순서 공백 체크

let itemNmRows = searchTreeData(cfgItemSheet, "itemNm", [undefined, ""]);

if (itemNmRows.length > 0) {

alert("항목명을 확인해주십시오!");

return;

}

let sortOrderRows = searchTreeData(cfgItemSheet, "sortOrder", [undefined, ""]);

if (sortOrderRows.length > 0) {

alert("정렬순서를 확인해주십시오!");

return;

}

if(confirm("변경하신 항목관리 정보를 저장하시겠습니까?")) {

let obj = {

schGrpId : vGrpId,

data: rows

};


cfgItemSheet.setData("/cfgItemAction.do", obj, "POST").then(function(){

if(cfgItemSheet.getDataCount() > 0){

var rows = cfgItemSheet.getRows();

rows[0].toggleSelect();

}

});

}

}


4. Tree 관련 스크립트


//list데이터 구조를 Tree데이터 구조로 변환하여 리턴

function bindListToTree(listData){

var treeData = new Array();

var itemLvl1Cnt = 0;

var itemLvl2Cnt = 0;

if(listData != null && listData.length > 0){

for(var i = 0 ; i < listData.length ; i++){

var map = listData[i];

//1레벨 세팅

if(map.itemLvl == 0) {

treeData[itemLvl1Cnt++] = map;

itemLvl2Cnt = 0;

}

//2레벨 세팅

else if(map.itemLvl == 1) {

var lvl1Map = treeData[itemLvl1Cnt - 1];

var lvl1SubList = lvl1Map._children;

if(lvl1SubList == undefined) {

lvl1SubList = new Array();

}

lvl1SubList[lvl1SubList.length] = map;

lvl1Map._children = lvl1SubList;

treeData[itemLvl1Cnt - 1] =  lvl1Map;

itemLvl2Cnt++;

}

//3레벨 세팅

else if(map.itemLvl == 2) {

var lvl1Map = treeData[itemLvl1Cnt - 1];

var lvl1SubList = lvl1Map._children;

var lvl2Map = lvl1SubList[itemLvl2Cnt - 1];

var lvl2SubList = lvl2Map._children;

if(lvl2SubList == undefined) {

lvl2SubList = new Array();

}

lvl2SubList[lvl2SubList.length] = map;

lvl2Map._children = lvl2SubList;

lvl1SubList[itemLvl2Cnt - 1] = lvl2Map;

lvl1Map._children = lvl1SubList;

treeData[itemLvl1Cnt - 1] = lvl1Map;

}

}

}

return treeData;

}


//Tree데이터 구조를 list데이터 구조로 변환하여 리턴

function bindTreeToList(treeData){

var listData = new Array();

var listCnt = 0;

if(treeData != null && treeData.length > 0){

for(var i = 0 ; i < treeData.length ; i++){

var lvl1Map = treeData[i];

listData[listCnt++] = lvl1Map;

//1레벨 하위항목

if(lvl1Map._children != undefined) {

for(var j = 0 ; j < lvl1Map._children.length ; j++){

var lvl2Map = lvl1Map._children[j];

listData[listCnt++] = lvl2Map;

//2레벨 하위항목

if(lvl2Map._children != undefined) {

for(var k = 0 ; k < lvl2Map._children.length ; k++){

var lvl3Map = lvl2Map._children[k];

listData[listCnt++] = lvl3Map;

}

}

}

}

}

}

return listData;

}


//Tree데이터에서 조건에 맞는 데이터를 찾아 list데이터 구조로 변환하여 리턴

function searchTreeData(vSheet, vField, vParamArr){

var treeData = vSheet.getData();

var listData = bindTreeToList(treeData);

var searchData = new Array();

var searchCnt = 0;

if(listData != null && listData.length > 0){

for(var i = 0 ; i < listData.length ; i++){

var map = listData[i];

for(var j = 0 ; j < vParamArr.length ; j++){

if(map[vField] == vParamArr[j]){

searchData[searchCnt++] = map;

}

}

}

}

return searchData;

}


//Tree데이터의 총 length를 리턴

function searchTreeDataLength(vSheet){

var treeData = vSheet.getData();

var listData = bindTreeToList(treeData);

return listData.length;

}


//선택된 Tree Data Row 추가

function addSelectedTreeDataRow(vSheet){

var sheetData = vSheet.getData();

var selectedData = vSheet.getSelectedData()[0];

var itemLvl = parseInt(selectedData.itemLvl, 10) + 1;

var itemTop = selectedData.itemTop;

//Tree Data Row 추가

for(var i = 0 ; i < sheetData.length ; i++){

var lvl1Map = sheetData[i];

var newSSeq = searchTreeDataLength(vSheet) + 1;

//1레벨 항목 확인

if(lvl1Map.itemId == selectedData.itemId){

var newItemTop = "";

var newItemLvl = 0;

if(lvl1Map._children != undefined){

newItemTop = lvl1Map.itemId;

newItemLvl = parseInt(lvl1Map.itemLvl, 10) + 1;

}

else{

lvl1Map._children = new Array();

newItemTop = lvl1Map.itemId;

newItemLvl = parseInt(lvl1Map.itemLvl, 10) + 1;

}

lvl1Map._children[lvl1Map._children.length] = {"sStatus":"I", "sDelCheck":0, "sSeq":newSSeq, "itemLvl":newItemLvl, "itemId":"", "itemTop":newItemTop, "useYn":"Y"};

}

else if(lvl1Map._children != undefined) {

for(var j = 0 ; j < lvl1Map._children.length ; j++){

var lvl2Map = lvl1Map._children[j];

//2레벨 항목 확인

if(lvl2Map.itemId == selectedData.itemId){

var newItemTop = "";

var newItemLvl = 0;

if(lvl2Map._children != undefined){

newItemTop = lvl2Map.itemId;

newItemLvl = parseInt(lvl2Map.itemLvl, 10) + 1;

}

else{

lvl2Map._children = new Array();

newItemTop = lvl2Map.itemId;

newItemLvl = parseInt(lvl2Map.itemLvl, 10) + 1;

}

lvl2Map._children[lvl2Map._children.length] = {"sStatus":"I", "sDelCheck":0, "sSeq":newSSeq, "itemLvl":newItemLvl, "itemId":"", "itemTop":newItemTop, "useYn":"Y"};

}

}

}

}

return sheetData;

}


//신규 생성 Tree Data Row Selected

function selectedTreeNewRow(vSheet){

var treeDataLength = searchTreeDataLength(vSheet);

var rows = vSheet.getRows();

for(var i = 0 ; i < rows.length ; i++){

var row1 = rows[i];

//1레벨 selected

if(parseInt(row1.getData().sSeq, 10) == treeDataLength){

row1.select();

}

else if(row1.getTreeChildren().length != undefined){

for(var j = 0 ; j < row1.getTreeChildren().length ; j++){

var row2 = row1.getTreeChildren()[j];

//2레벨 selected

if(parseInt(row2.getData().sSeq, 10) == treeDataLength){

row2.select();

}

else if(row2.getTreeChildren().length != undefined){

for(var k = 0 ; k < row2.getTreeChildren().length ; k++){

var row3 = row2.getTreeChildren()[k];

//3레벨 selected

if(parseInt(row3.getData().sSeq, 10) == treeDataLength){

row3.select();

}

}

}

}

}

}

}



반응형

1. context-common.xml (bean 추가)


<!-- MultipartResolver 설정 -->

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

<property name="maxUploadSize" value="100000000" />

<property name="maxInMemorySize" value="100000000" />

</bean>


2. ExcelUtil.java (공통 엑셀 유틸 추가)


package egovframework.cmn.cmn;


import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;


import org.apache.poi.hssf.usermodel.HSSFDateUtil;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import org.apache.poi.ss.usermodel.Cell;

import org.apache.poi.ss.usermodel.Row;

import org.apache.poi.ss.usermodel.Sheet;

import org.apache.poi.ss.usermodel.Workbook;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import org.springframework.web.multipart.commons.CommonsMultipartFile;


public class ExcelUtil {

/**

* 엑셀 파일 읽기

* @param file

* @param sheetNum

* @param strartRowNum

* @param startCelNum

* @return List<HashMap<Integer, String>>

* @throws Exception

*/

public List<HashMap<Integer, String>> excelReadSetValue(CommonsMultipartFile file, int sheetNum, int strartRowNum, int startCelNum) throws Exception {

List<HashMap<Integer, String>> resultList = new ArrayList<>();

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

//xls, xlsx 구분

Workbook workbook = null;

if(file.getOriginalFilename().toUpperCase().endsWith("XLSX")) {

workbook = new XSSFWorkbook(file.getInputStream());

}

else {

workbook = new HSSFWorkbook(file.getInputStream());

}

//Sheet 수 확인

int sheetCnt = workbook.getNumberOfSheets();

int listNum = 0;


try {

if (sheetCnt > 0) {

//첫번째 Sheet 선택

Sheet sheet = workbook.getSheetAt(sheetNum);

//Sheet의 Row와 Cell 수 확인

int rows = sheet.getPhysicalNumberOfRows();

int cells = sheet.getRow(0).getPhysicalNumberOfCells();

HashMap<Integer, String> valueMap = null;

//Header Row 빼고 시작(0에서 시작)

for(int r = strartRowNum ; r < rows; r++) {

//String device_id = "";

valueMap = new HashMap<Integer, String>();

//한 줄씩 읽고 데이터 저장

Row row = sheet.getRow(r);

if (row != null) {

//Cell 기본값 빼고 시작(0에서 시작)

for(int c = startCelNum ; c < cells ; c++) {

Cell cell = row.getCell(c);

if (cell != null) {

String value = "";

switch(cell.getCellType()) {

case Cell.CELL_TYPE_BLANK :

value = "";

break;

case Cell.CELL_TYPE_BOOLEAN :

value = "" + cell.getBooleanCellValue();

break;

case Cell.CELL_TYPE_ERROR :

value = "" + cell.getErrorCellValue();

break;

case Cell.CELL_TYPE_FORMULA :

value = cell.getCellFormula();

break;

case Cell.CELL_TYPE_NUMERIC :

if(HSSFDateUtil.isInternalDateFormat(cell.getCellStyle().getDataFormat())) {

value = sdf.format(cell.getDateCellValue());

}

else {

cell.setCellType(Cell.CELL_TYPE_STRING ); 

value = cell.getStringCellValue(); 

}

break;

case Cell.CELL_TYPE_STRING :

value = cell.getStringCellValue();

break;

}

//공백과 트림 제거

value = value.trim().replaceAll(" ", "");

valueMap.put(c, value);

}

}//end col for

resultList.add(listNum++, valueMap);

}//end if

}//end row for

}

} catch(Exception e) {

e.getStackTrace();

}

return resultList;

}

}



3. XxxController.java


/**

* 엑셀 업로드 처리

* @param multiRequest

* @param request

* @return

* @throws Exception

*/

@RequestMapping("cfgUploadAction.do")

public String cfgUploadAction(HttpServletRequest request, RedirectAttributes redirectAttributes) throws Exception {

try {

cfgService.excelUpload(request);


redirectAttributes.addFlashAttribute("Code", 0);

redirectAttributes.addFlashAttribute("Message", egovMessageSource.getMessage("proc.success"));

} catch (Exception ex) {

redirectAttributes.addFlashAttribute("Code", 1);

redirectAttributes.addFlashAttribute("Message", "오류가 발생하였습니다. 엑셀양식을 확인해 주세요.");

}

return "redirect:/mng/fac/cfg/cfgUploadPop.do";

}


4. XxxServiceImpl.java


/**

* 엑셀 업로드 처리

* @param multiRequest

* @return String

* @throws Exception

*/

@SuppressWarnings("unchecked")

public void excelUpload(HttpServletRequest request) throws Exception{

MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest)request;

//파일 정보

CommonsMultipartFile file = (CommonsMultipartFile)multiRequest.getFile("excelFile");

//엑셀정보

ExcelUtil eu = new ExcelUtil();

int sheetNum = 0; //1번째 시트 읽음 

int strartRowNum = 1; //2번째 줄부터 읽음

int startCelNum = 2; //3번째 줄부터 읽음(지역ID)

List<HashMap<Integer, String>> excelList = eu.excelReadSetValue(file, sheetNum, strartRowNum, startCelNum);

//테이블 Key 정보

DeviceBaseVO deviceBaseVO = null;


//엑셀 Row 수 만큼 For문 조회 

for(Object obj : excelList) {

Map<Integer, String> mp = (Map<Integer, String>)obj;

Set<Integer> keySet = mp.keySet();

Iterator<Integer> iterator = keySet.iterator();

deviceBaseVO = new DeviceBaseVO();

while(iterator.hasNext()) {

int key = iterator.next();

String value = StringUtil.nullConvert(mp.get(key));

switch(key) {

case 2 :

deviceBaseVO.setAreaId(value);

break;

case 4 :

deviceBaseVO.setFacilityId(value);

break;

case 5 :

deviceBaseVO.setDeviceNm(value);

break;

case 6 :

deviceBaseVO.setDeviceId(value);

break;

case 7 :

deviceBaseVO.setInstDt(value);

break;

case 8 :

deviceBaseVO.setUseYn(value);

break;

}

}

if(!"".equals(deviceBaseVO.getAreaId()) && deviceBaseVO.getAreaId() != null) {

cfgMapper.updateCfgInfo(deviceBaseVO);

}

}

}


5. Xxx.jsp


<form name="popForm" method="post" action="${actionUrl}" enctype="multipart/form-data">

<table>

<caption>엑셀 업로드</caption>

<colgroup>

<col><col>

</colgroup>


<tbody>

<tr>

<th><label for="code2">파일찾기</label></th>

<td><input name="excelFile" id="excelFile" type="file" size="30"></td>

</tr>

</table>


<!-- 버튼 영역 -->

<div class="btn-area">

<a href="#" onclick="doSubmit();" class="btn btn-yellow btn-ok">업로드</a>

<a href="javascript:self.close();" class="btn btn-yellow btn-cancel">창닫기</a>

</div>

</form>


반응형

1. context-common.xml (bean 추가)


<bean id="excelView" class="egovframework.cmn.cmn.ExcelView" />


2. ExcelView.java (공통 엑셀다운로드)


package egovframework.cmn.cmn;


import java.net.URLEncoder;

import java.util.List;

import java.util.Map;


import javax.servlet.ServletOutputStream;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.apache.poi.hssf.usermodel.HSSFCellStyle;

import org.apache.poi.hssf.usermodel.HSSFFont;

import org.apache.poi.hssf.util.HSSFColor;

import org.apache.poi.ss.usermodel.CellStyle;

import org.apache.poi.ss.usermodel.Font;

import org.apache.poi.ss.util.CellRangeAddress;

import org.apache.poi.xssf.usermodel.XSSFCell;

import org.apache.poi.xssf.usermodel.XSSFRow;

import org.apache.poi.xssf.usermodel.XSSFSheet;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import org.springframework.web.servlet.view.AbstractView;


import egovframework.rte.psl.dataaccess.util.EgovMap;


public class ExcelView extends AbstractView {

    /** The content type for an Excel response */

     private static final String CONTENT_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";


    /**

     * Default Constructor. Sets the content type of the view for excel files.

     */

    public ExcelView() {

    }


    @Override

    protected boolean generatesDownloadContent() {

        return true;

    }


    /**

     * Renders the Excel view, given the specified model.

     */

    @Override

    protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {


        XSSFWorkbook workbook = new XSSFWorkbook();


        setContentType(CONTENT_TYPE_XLSX);


        buildExcelDocument(model, workbook, request, response);

        

        // Set the filename

        String sFilename = "";

        if(model.get("filename") != null){

            sFilename = (String)model.get("filename");

        }else if(request.getAttribute("filename") != null){

            sFilename = (String)request.getAttribute("filename");

        }else{

            sFilename = getClass().getSimpleName();

         }


        response.setContentType(getContentType());

        

        String header = request.getHeader("User-Agent");

        sFilename = sFilename.replaceAll("\r","").replaceAll("\n","");

        if(header.contains("MSIE") || header.contains("Trident") || header.contains("Chrome")){

            sFilename = URLEncoder.encode(sFilename,"UTF-8").replaceAll("\\+","%20");

            response.setHeader("Content-Disposition","attachment;filename="+sFilename+".xlsx;");

        }else{

            sFilename = new String(sFilename.getBytes("UTF-8"),"ISO-8859-1");

            response.setHeader("Content-Disposition","attachment;filename=\""+sFilename + ".xlsx\"");

        }

        

        // Flush byte array to servlet output stream.

        ServletOutputStream out = response.getOutputStream();

        out.flush();

        workbook.write(out);

        out.flush();


        // Don't close the stream - we didn't open it, so let the container handle it.

        // http://stackoverflow.com/questions/1829784/should-i-close-the-servlet-outputstream

    }

    

    @SuppressWarnings("unchecked")

    protected void buildExcelDocument(Map<String, Object> model, XSSFWorkbook wb, HttpServletRequest req, HttpServletResponse resp) throws Exception {

    List<Map<String, Object>> dataMapList = (List<Map<String, Object>>) model.get("dataMapList");

   

    for(Map<String, Object> dataMap : dataMapList) {

            XSSFCell cell = null;

     

            String sheetNm = (String) dataMap.get("sheetNm"); // 엑셀 시트 이름

            

            String[] titleArr = (String[]) dataMap.get("titleArr"); // 각 컬럼 이름

            String[] fieldArr = (String[]) dataMap.get("fieldArr"); // 각 컬럼의 변수 이름

            

            List<EgovMap> dataList = (List<EgovMap>) dataMap.get("list"); // 데이터가 담긴 리스트 

            

            CellStyle cellStyle = wb.createCellStyle(); // 제목셀의 셀스타일

            cellStyle.setWrapText(true); // 줄 바꿈            

            cellStyle.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index); // 셀 색상

            cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); // 셀 색상 패턴

            cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 셀 가로 정렬

            cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 셀 세로 정렬

            cellStyle.setDataFormat((short)0x31); // 셀 데이터 형식

            cellStyle.setBorderRight(HSSFCellStyle.BORDER_DOUBLE);

            cellStyle.setBorderLeft(HSSFCellStyle.BORDER_DOUBLE);

            cellStyle.setBorderTop(HSSFCellStyle.BORDER_DOUBLE);

            cellStyle.setBorderBottom(HSSFCellStyle.BORDER_DOUBLE);

            

            // 셀 폰트색상, bold처리

            Font font = wb.createFont();

            font.setColor(HSSFColor.WHITE.index);

            font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

            cellStyle.setFont(font);

            

            CellStyle cellStyle2 = wb.createCellStyle(); // 데이터셀의 셀스타일

            cellStyle2.setWrapText(true); // 줄 바꿈           

            cellStyle2.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 셀 가로 정렬

            cellStyle2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 셀 세로 정렬

            cellStyle2.setDataFormat((short)0x31); // 셀 데이터 형식

            cellStyle2.setBorderRight(HSSFCellStyle.BORDER_THIN);

            cellStyle2.setBorderLeft(HSSFCellStyle.BORDER_THIN);

            cellStyle2.setBorderTop(HSSFCellStyle.BORDER_THIN);

            cellStyle2.setBorderBottom(HSSFCellStyle.BORDER_THIN);

            

            XSSFSheet sheet = wb.createSheet(sheetNm);

            sheet.setDefaultColumnWidth(12);

            

            // 컬럼명 삽입

            for(int i=0; i<titleArr.length; i++){

                setText(getCell(sheet, 0, i), titleArr[i]);

                getCell(sheet, 0, i).setCellStyle(cellStyle);

                sheet.autoSizeColumn(i);

                int columnWidth = (sheet.getColumnWidth(i))*5;

                sheet.setColumnWidth(i, columnWidth);

                

                if(dataList.size() < 1){

                    cell = getCell(sheet, 1, i);

                    if(i==0){

                        setText(cell, "등록된 정보가 없습니다.");

                    }

                    cell.setCellStyle(cellStyle2);

                }

            }

            

            if(dataList.size() > 0){ // 저장된 데이터가 있을때

                // 리스트 데이터 삽입

                for (int i = 0; i<dataList.size(); i++) {

                    EgovMap dataEgovMap = dataList.get(i);

                    

                    // 맨 앞 컬럼인 "번호"는 idx라는 이름으로 여기서 생성하여 넣어준다.

                    dataEgovMap.put("idx", (i+1)+""); 


                    for(int j=0; j<fieldArr.length; j++){

                        String data = String.valueOf(dataEgovMap.get(fieldArr[j]));

                        cell = getCell(sheet, 1 + i, j);

                        setText(cell, data);

                        cell.setCellStyle(cellStyle2);

                    }

                }

            }else{ // 저장된 데이터가 없으면 셀 병합

                // 셀 병합(시작열, 종료열, 시작행, 종료행)

                sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, titleArr.length-1));

            }

    }

    }


    /**

     * Convenient method to obtain the cell in the given sheet, row and column.

     * 

     * <p>Creates the row and the cell if they still doesn't already exist.

     * Thus, the column can be passed as an int, the method making the needed downcasts.</p>

     * 

     * @param sheet a sheet object. The first sheet is usually obtained by workbook.getSheetAt(0)

     * @param row thr row number

     * @param col the column number

     * @return the XSSFCell

     */

    protected XSSFCell getCell(XSSFSheet sheet, int row, int col) {

        XSSFRow sheetRow = sheet.getRow(row);

        if (sheetRow == null) {

            sheetRow = sheet.createRow(row);

        }

        XSSFCell cell = sheetRow.getCell((short) col);

        if (cell == null) {

            cell = sheetRow.createCell((short) col);

        }

        return cell;

    }


    /**

     * Convenient method to set a String as text content in a cell.

     * 

     * @param cell the cell in which the text must be put

     * @param text the text to put in the cell

     */

    protected void setText(XSSFCell cell, String text) {

        cell.setCellType(XSSFCell.CELL_TYPE_STRING);

        cell.setCellValue(text);

    }            

}


3. SearchVO.java (VO 변수 추가)


private String schFileNm = ""; /** 엑셀다운로드 파일명 */

private String[] schSheetNmArr = null; /** 엑셀다운로드 시트명 배열 */

private String[][] schTitleArrArr = null; /** 엑셀다운로드 Title 배열 */

private String[][] schFieldArrArr = null; /** 엑셀다운로드 Field 배열 */


4. XxxController.java (Controller 엑셀다운로드 함수 추가)


@RequestMapping("disorderAllList.do")

public ModelAndView selectDisorderAllList(@ModelAttribute("searchVO") SearchVO searchVO, HttpServletRequest request, HttpServletResponse response, BindingResult bindingResult) throws Exception {

//코드관리 목록 조회

List<?> list = cfgService.selectDisorderList(searchVO);

//코드1관리 목록 조회

List<?> list1 = cfgService.selectCauseList(searchVO);

//코드2관리 목록 조회

List<?> list2 = cfgService.selectActionList(searchVO);

    ModelAndView mav = new ModelAndView("excelView");

    List<Map<String, Object>> dataMapList = new ArrayList<Map<String, Object>>();

    Map<String, Object> subMap = null;

    

    for(int i = 0 ; i < searchVO.getSchSheetNmArr().length ; i++) {

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

    subMap.put("titleArr", searchVO.getSchTitleArrArr()[i]);

    subMap.put("fieldArr", searchVO.getSchFieldArrArr()[i]);

    subMap.put("sheetNm", searchVO.getSchSheetNmArr()[i]);

    if(i == 0) subMap.put("list", list);

    else if(i == 1) subMap.put("list", list1);

    else if(i == 2) subMap.put("list", list2);

   

    dataMapList.add(i, subMap);

    }

    mav.addObject("dataMapList", dataMapList);

    mav.addObject("filename", searchVO.getSchFileNm());

    

    return mav;

}


5. common.js (공통 Tabulator 엑셀 다운로드 함수 추가)


//Tabulator 엑셀 다운로드

function downBaseExcelTabulator(vAction, vFileNm, vSheetNmArr, vSheetArr, vParams){

//action 정보 세팅

var vForm = document.createElement("FORM");

vForm.action = vAction;

vForm.method = "POST";


//파일명 세팅

appendMakeInput(vForm, "schFileNm", vFileNm);


//시트명 세팅

for(var i = 0 ; i < vSheetNmArr.length ; i++){

appendMakeInput(vForm, "schSheetNmArr[" + i + "]", vSheetNmArr[i]);

}

//sheet 컬럼 정보 세팅

for(var j = 0 ; j < vSheetArr.length ; j++){

var vColumns = vSheetArr[j];

for(var jj = 0 ; jj < vColumns.length ; jj++){

if(vColumns[jj].download != false){

appendMakeInput(vForm, "schTitleArrArr[" + j + "]", vColumns[jj].title);

appendMakeInput(vForm, "schFieldArrArr[" + j + "]", vColumns[jj].field);

}

}

}

//params 정보 세팅

if(vParams != null && vParams != ""){

for(var k = 0 ; k < vParams.length ; k++){

appendMakeInput(vForm, vParams[k].name, vParams[k].value);

}

}

window.document.body.appendChild(vForm);

vForm.submit();

}



//Tabulator 단일 시트 엑셀 다운로드

function downOneExcelTabulator(vAction, vFileNm, vSheetNm, vSheet, vParams){

var vSheetNmArr = [vSheetNm];

var vSheetArr = [vSheet.getColumnDefinitions()];

downBaseExcelTabulator(vAction, vFileNm, vSheetNmArr, vSheetArr, vParams);

}


//Tabulator 멀티 시트 엑셀 다운로드

function downMultiExcelTabulator(vAction, vFileNm, vSheetNmArr, vSheetArr, vParams){

var reSheetArr = new Array();

for(var i = 0 ; i < vSheetArr.length ; i++){

var tempArr = new Array();

for(var j = 0 ; j < vSheetArr[i].getColumns().length ; j++){

tempArr[j] = vSheetArr[i].getColumns()[j].getDefinition();

}

reSheetArr[i] = tempArr;

}

downBaseExcelTabulator(vAction, vFileNm, vSheetNmArr, reSheetArr, vParams);

}


//input hidden 객체 만들어 붙이기

function appendMakeInput(obj, nm, val){

var vInput = document.createElement("INPUT");

vInput.name = nm;

vInput.value = val;

vInput.type = "hidden";

obj.append(vInput);

}


6. Xxx.js (각 화면 엑셀 다운로드 함수 추가)


//엑셀 다운로드

function excelDown(opt){

if(opt == "ALL"){

var vAction = "/common/disorderAllList.do";

var vFileNm = "코드관리(전체)";

var vSheetNmArr = ["코드관리", "코드1관리", "코드2관리"];

var vSheetArr = [disorderSheet, causeSheet, actionSheet];

var vParams = null;

//Tabulator 엑셀 다운로드

downMultiExcelTabulator(vAction, vFileNm, vSheetNmArr, vSheetArr, vParams);

}

else{

var sheets = {

    "코드관리": true,

    "코드1관리": causeSheet,

    "코드2관리": actionSheet

};


disorderSheet.download("xlsx", "코드관리.xlsx", {sheets:sheets});

}

}



반응형

이클립스로 프로젝트를 돌리던 중 아무것도 수정한게 없는데 아래와 같은 에러가 오류가 떴다.


org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (IO 오류: The Network Adapter could not establish the connection)


오라클 리스너가 갑자기 실행이 되지 않아 나타나는 오류라서 윈도우 서비스를 실행하여 해당 서비스를 강제 실행을 하니 아래와 같은 메세지가 발생.


OracleOraDb11g_home1TNSListener 서비스가 로컬 컴퓨터에서 시작했다가 중지되었습니다


결과적으로 오라클 설치경로(ex : C:\app\subinto\product\11.2.0\dbhome_2\NETWORK\ADMIN)에서 sqlnet.ora, tnsnames.ora 이 두파일 을 열어보면 HOST가 내 컴퓨터 이름과 같지 않아서 발생하였다.(왜 바뀐거지??? =_=?)


아무튼 컴퓨터 이름으로 맞추고서 리스너 서비스를 다시 실행하니 정상 동작하였다.

반응형

1.메이븐을 사용한다면 pom.xml에 아래 dependency를 추가하고 메이븐을 사용하지 않는다면 아래 사이트에가서 필요한 jar를 다운 받는다.

(https://mvnrepository.com/artifact/org.apache.tiles)

 

  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-api -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-api</artifactId>
      <version>3.0.8</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-autotag-core -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-autotag-core</artifactId>
      <version>1.2</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-autotag-core-runtime -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-autotag-core-runtime</artifactId>
      <version>1.2</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-core -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-core</artifactId>
      <version>3.0.8</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-extras -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-extras</artifactId>
      <version>3.0.8</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-jsp -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-jsp</artifactId>
      <version>3.0.8</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-request-api -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-request-api</artifactId>
      <version>1.0.7</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-request-jsp -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-request-jsp</artifactId>
      <version>1.0.7</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-request-servlet -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-request-servlet</artifactId>
      <version>1.0.7</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-servlet -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-servlet</artifactId>
      <version>3.0.8</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-template -->
  <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-template</artifactId>
      <version>3.0.8</version>
  </dependency>

 

 

2. web.xml 설정(중간에 타일즈를 추가하는 것이면 이미 설정 되어있을 것이다.)

 

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/egovframework/springmvc/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

 

3. dispatcher-servlet.xml 설정(프로젝트마다 파일이름이 다를 수 있다.)

   기존 설정과 중복되는 부분은 주석처리 하거나 필요한 설정이라면 order 순서를 변경한다.

   org.springframework.web.servlet.view.tiles3.SimpleSpringPreparerFactory 부분은 동적메뉴 구성을 위해 추가하였다.(5-1번 참고)

 

    <!-- Ajax jsonView Return (ModelAndView) -->
    <bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"
     p:contentType="application/json;charset=UTF-8">
    </bean>
    <bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"
     p:order="2">
    </bean>

 


 <!-- Tiles 추가로 인해 주석처리
    <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" p:order="2"
     p:viewClass="org.springframework.web.servlet.view.JstlView"
     p:prefix="/WEB-INF/jsp/egovframework/" p:suffix=".jsp" />
  -->
    
    <!-- Tiles 추가 -->
    <bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"
        p:viewClass="org.springframework.web.servlet.view.tiles3.TilesView"
        p:order="1" />
    <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
            <property name="definitions">
                  <list>
                        <value>/WEB-INF/config/egovframework/tiles/tiles.xml</value>
                  </list>
            </property>
            <property name="preparerFactoryClass" value="org.springframework.web.servlet.view.tiles3.SimpleSpringPreparerFactory"></property>
    </bean>

 

4. tiles.xml 설정(definition의 name abc/**/*Viewdef/**/*ViewController의 리턴값과 매칭되어야 해당 Tiles Layout이 적용된다.)

  cf ex1) return "abc/aa/cc/dd/wowView";  cf ex2) return "def/a/goodView";

 

<!DOCTYPE tiles-definitions PUBLIC
  "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
  "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
 
<tiles-definitions>
  
    <!-- Tiles 적용 기본 Layout -->
    <definition name="base" template="/WEB-INF/jsp/egovframework/layout/TilesLayout.jsp" preparer="egovframework.cmn.MenuPreparer">
        <put-attribute name="header" value="/WEB-INF/jsp/egovframework/layout/Header.jsp" />
        <put-attribute name="menuLvl2" value="/WEB-INF/jsp/egovframework/layout/MenuLvl2.jsp" />
        <put-attribute name="menuLvl3" value="/WEB-INF/jsp/egovframework/layout/MenuLvl3.jsp" />
    </definition>
   
    <!-- Tiles 적용 팝업 Layout -->
    <definition name="baseEmpty" template="/WEB-INF/jsp/egovframework/layout/PopLayout.jsp">
    </definition>
    
    

    
 
    <!-- 기본 관리자 화면 - 첫번째관리 Layout -->
    <definition name="abc/**/*View" extends="base">
        <put-attribute name="body" value="/WEB-INF/jsp/egovframework/abc/{1}/{2}View.jsp" />
        <put-attribute name="curPath" value="abc/{1}/{2}View" cascade="true"/>
    </definition>
 
    <!-- 기본 관리자 화면 - 두번째관리 Layout -->
    <definition name="def/**/*View" extends="base">
        <put-attribute name="body" value="/WEB-INF/jsp/egovframework/def/{1}/{2}View.jsp" />
    </definition>
 



    
    <!-- 팝업 화면 Layout -->
    <definition name="**/*Pop" extends="baseEmpty">
        <put-attribute name="body" value="/WEB-INF/jsp/egovframework/{1}/{2}Pop.jsp" />
    </definition>
   
   
   
   
   
   
    <!-- 기타 Tiles 미적용 -->
    <definition name="cmn/*" template="/WEB-INF/jsp/egovframework/cmn/{1}.jsp"></definition>

  
</tiles-definitions>

 

5-1. 동적메뉴 구성을 위한 설정(MenuPreparer.java)

 

public class MenuPreparer implements ViewPreparer {

 /** SysadmService */
 @Resource(name="sysadmService")
 private SysadmService sysadmService;

 @Override
 public void execute(Request tilesRequest, AttributeContext attributeContext) throws PreparerException {
  List<?> menuList;
  try {
   Map<String, String> map = tilesRequest.getParam();
   SearchVO searchVO = new SearchVO();
   
   //로그인한 세션정보 가져오기
   HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
   HttpSession session = request.getSession();
   LoginVO loginVO = (LoginVO)session.getAttribute("loginVO");
   
   //접근사용자구분 세팅
   searchVO.setSchUserGubun(loginVO.getUserGubun());
   
   //관리자메뉴 세팅
   menuList = sysadmService.getSelectMngMenuList(searchVO);
   attributeContext.putAttribute("menuList", new Attribute(menuList), true);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 

 

5-2. TilesLayout.jsp 설정

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="validator" uri="http://www.springmodules.org/tags/commons-validator" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles-extras" prefix="tilesx" %>

<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">

<!-- Stylesheets ============================================= -->
<link rel="stylesheet" href="/css/bootstrap.css" type="text/css" />
<link rel="stylesheet" href="/css/style.css" type="text/css" />

 

<!-- External JavaScripts ============================================= -->
<script type="text/javascript" src="/js/jquery.js"></script>


 

<tilesx:useAttribute name="curPath" />
<script type="text/javascript" src="/script/${curPath}.js"></script>

 

<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>관리자페이지</title>
</head>

<body class="stretched">

<!-- Document Wrapper ============================================= -->
<div id="wrapper" class="clearfix">
 <!-- Header ============================================= -->
 <tiles:insertAttribute name="header"/>
 <!-- #header end -->

    <!-- Content ============================================= -->
    <section id="content" class="content-grid">
        <div class="content-wrap nopadding">
   <div class="container-fluid">
    <div id="tabs" class="card">
     <!-- 2depth 메뉴 -->
     <tiles:insertAttribute name="menuLvl2" />
     <!-- //2depth 메뉴 -->
     
     <div class="tab-content">
      <div class="tab-pane active" id="default">
       <div class="card-body">
        <!-- 3depth 메뉴 -->
        <tiles:insertAttribute name="menuLvl3" />
        <!-- //3depth 메뉴 -->
        
        <!-- bodyContent -->
        <tiles:insertAttribute name="body" />
        <!-- //bodyContent -->
       </div>
      </div>
     </div>
    </div>
   </div>
  </div>
    </section>
    <!-- #content end -->
</div>
<!-- #wrapper end -->
</body>
</html> 

 

 

 

5-3. MenuLvl2.jsp 설정(menuList 변수는 5-1에서 지정한 변수)

 

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

<tiles:importAttribute name="menuList" />
<ul class="nav nav-tabs ui-sortable">
 <c:forEach var="result" items="${menuList}" varStatus="status">
  <c:if test="${result.lvl eq 2}">
   <li ${result.isActive eq 'Y' ? 'class="active"' : ''}><a href="${result.menuUrl }?menuId=${result.menuId}" ${result.popGubun eq 'Y' ? 'target="_blank"' : ''}>${result.menuNm }</a></li>
  </c:if>
 </c:forEach>
</ul> 

 

6. 만드는 것보다 글로 정리하는게 어렵네요... 로그인한 사용자에 대한 동적메뉴를 구성하는 부분에서 많이 막혔었네요.

구글 국내로 검색해도 마땅한 자료를 못찾아 많이 헤멧네요. 5-1 부분에 세션정보를 가져오는 부분이 국내에서 타일즈랑 묶어서 찾기가 참 힘들었네요

반응형

1. web.xml 설정(중간에 로그인 체크를 위한 인터셉터를 추가하는 것이면 이미 설정 되어있을 것이다.)

 

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/egovframework/springmvc/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

 

2. dispatcher-servlet.xml 설정(프로젝트마다 파일이름이 다를 수 있다.)

 

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**/*View.do"/> <!-- 절대경로(/)부터 시작하면 중간경로가 있을 수 있기에 (**)으로 표시 -->
        <mvc:exclude-mapping path="/cmn/login/*.do"/> <!-- 예외 경로 지정 -->
        <mvc:exclude-mapping path="/mon/page/loginView.do"/> <!-- 예외 경로 지정(여러개일 수 있음으로...) -->
        <bean class="egovframework.cmn.cmn.AuthInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

 

3. AuthInterceptor.java 설정

 

public class AuthInterceptor extends HandlerInterceptorAdapter {
    /**
     * 세션에 계정정보(LoginVO)가 있는지 여부로 인증 여부를 체크한다. 계정정보(LoginVO)가 없다면, 로그인 페이지로 이동한다.
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LoginVO loginVO = null;
       
        try {
            loginVO = (LoginVO) request.getSession().getAttribute("loginVO");

            if (loginVO != null && loginVO.getUserId() != null) {
                return true;
            } else {
                ModelAndView modelAndView = new ModelAndView("forward:/loginView.do");
                modelAndView.addObject("message", "세션이 만료되어 로그아웃 되었습니다. 다시 로그인 해주세요.");
                throw new ModelAndViewDefiningException(modelAndView);
            }
        } catch (Exception e) {
            ModelAndView modelAndView = new ModelAndView("forward:/loginView.do");
            modelAndView.addObject("message", "세션이 만료되어 로그아웃 되었습니다. 다시 로그인 해주세요.");
            throw new ModelAndViewDefiningException(modelAndView);
        }
    }

    /**
     * 세션에 메뉴권한(LoginVO.userGubun)을 가지고 메뉴를 조회하여 권한 여부를 체크한다.
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        LoginVO loginVO = null;
        String requestURI = request.getRequestURI();
       
        try {
         if(!"/loginView.do".equals(requestURI)) {
          loginVO = (LoginVO) request.getSession().getAttribute("loginVO");
          if (loginVO != null && loginVO.getUserId() != null) {
           SearchVO searchVO = new SearchVO();
           searchVO.setSchMenuUrl(requestURI);
           searchVO.setSchUserGubun(loginVO.getUserGubun());
           List<?> resultList = sysadmService.getSelectRollMenuCheckList(searchVO);
           
           if(resultList == null || resultList.size() == 0) {
                     ModelAndView mav = new ModelAndView("forward:/monitorView.do"); 
                     mav.addObject("message", "권한이 없습니다.");
                     throw new ModelAndViewDefiningException(mav);
           }
           
          } else {
           ModelAndView mav = new ModelAndView("forward:/loginView.do");
           mav.addObject("message", "세션이 만료되어 로그아웃 되었습니다. 다시 로그인 해주세요.");
           throw new ModelAndViewDefiningException(mav);
          }
         }
        } catch (Exception e) {
            ModelAndView mav = new ModelAndView("forward:/monitorView.do"); 
            mav.addObject("message", "권한이 없습니다.");
            throw new ModelAndViewDefiningException(mav);
        }    }
}

 

4. 현재 진행 중인 프로젝트엔 세션 권한별 접근 메뉴 체크가 없기에 비워놈... 언젠가는 추가할지도??

5. 동일한 환경으로 새로운 프로젝트를 시작하여 권한도 추가하였다.

반응형

ResourceBundle 클래스를 사용하여 properties 파일 정보를 읽어올 수 있다.

 

다음과 같은 파일이 있다면

 

경로 : WEB-INF/classes/egovframework/egovProps/IpInfoSample.properties

 

IpInfoSample.properties 파일 내용

 

#소켓IP정보

SocketIp=127.0.0.1

 

#소켓포트정보

SocketPort=5001 

 

아래와 같이 사용하여 정보를 읽어올 수 있다.

 

ResourceBundle mainResource = ResourceBundle.getBundle("egovframework.egovProps.IpInfoSample");

String SocketIp = mainResource.getString("SocketIp");

String SocketPort = mainResource.getString("SocketPort");

 

System.out.println("[SocketIp] : " + SocketIp);

System.out.println("[SocketPort] : " + SocketPort);

 

결과 :

[SocketIp] : 127.0.0.1
[SocketPort] : 5001

 

properties 파일의 모든 정보를 읽어야 한다면 아래와 같이 사용할수도 있다.

 

ResourceBundle mainResource = ResourceBundle.getBundle("egovframework.egovProps.IpInfoSample");  

Enumeration enm = mainResource.getKeys();

 

while(enm.hasMoreElements()){
    String key = (String) enm.nextElement();
    String value = mainResource.getString(key);


    System.out.println("[" + key + "] : " + value);
}

 

결과 :

[SocketIp] : 127.0.0.1
[SocketPort] : 5001

 

반응형

GPKI 서버 인증서가 만료되어 GPKI를 신규로 발급받아 설치 시 표준API 라이센스 파일(gpkiapi.lic)의 내용을 복사하여

 

~(Web Root)/gpkisecureweb/client/var.js

 

위 파일에 아래 라이센스변수 내용을 수정하고 서버를 돌렸는데 계속 라이센스 만료에러가 발생하였다.

 

var.js

 

ServerCert = "MIID5 ...."

 

결론은 gpkiapi.lic 파일의 라이센스를 Base64Encode 값으로 변환하여 ServerCert 변수에 세팅해야된다. 

 

<%@ page import="com.gpki.gpkiapi.storage.Disk" %>
<%@ page import="com.gpki.gpkiapi.util.Base64" %>
<%@ page import="com.gpki.gpkiapi.cert.X509Certificate" %>
<%
String SERVER_KM_CERT_PATH = "서버인증서파일경로/서버인증서파일(SVR_env.cer)";
Base64 base64 = new Base64();
byte[] bBase64 = null;
String strBase64 = "";
X509Certificate srvCert = Disk.readCert(SERVER_KM_CERT_PATH);
bBase64 = srvCert.getCert();
strBase64 = new String(base64.encode(bBase64));
%>

 

위 내용은 표준API설치가이드에서 제공하는 Base64Encode로 변환하는 가이드인데 저대로 하면 에러가 발생한다.^^;

 

변환하기전에 아래 소스를 먼저 선언하고 돌려야한다. 참고로 가이드문서는 jsp로 되어있지만 java로 돌리면 훨신 간편하다. ^-^

 

GpkiApi.init("/subinto/conf/"); //gpkiapi.lic 가 있는 경로
반응형

+ Recent posts