반응형

jQuery 플러그인 중 form태그 내부의 데이터를 편하게 비동기 처리해주는 기능이 있습니다.

바로 jQueryajaxSubmit(); 인데요.

submit이지만 비동기로 처리할 수 있고 동작 이전의 함수처리, 이후의 함수처리도 정의할 수 있는 장점이 있습니다.

사용법은 아래와 같습니다.

 

사용방식

var options = { 
        target:        '#output2',   // target element(s) to be updated with server response 
        beforeSubmit:  showRequest,  // pre-submit callback 
        success:       showResponse  // post-submit callback 

        // other available options: 
        //url:       url         // override for form's 'action' attribute 
        //type:      type        // 'get' or 'post', override for form's 'method' attribute 
        //dataType:  null        // 'xml', 'script', or 'json' (expected server response type) 
        //clearForm: true        // clear all form fields after successful submit 
        //resetForm: true        // reset the form after successful submit 

        // $.ajax options can be used here too, for example: 
        //timeout:   3000 
    }; 

$(formTag).ajaxSubmit(options);

formTag에 ajaxForm() 메소드를 연결하여 사용합니다.

저런 옵션들이 있구나 정도로 보시면 될 것 같고, 좀 더 자세한 사용법을 보겠습니다.

 

 

ajax동작 처리에 따른 결과값 받는 예제(주의점)

 

form.html

<form id="myForm" action="insertBoard.do" method="post"> 
    title: <input type="text" name="title" /> 
    Content: <textarea name="Content"></textarea> 
    <input type="submit" value="Save" /> 
</form>

form.js

<script>
var option = {
    dataType : 'json', //JSON형태로 전달도 가능합니다.
    url: "insertBoard.do",
    success: function(res){
        alert(res.msg); //res Object안에 msg에는 결과 메시지가 담겨있습니다.
    },
    error: function(res){
        alert("에러가 발생했습니다.")
    }
}

$('#myForm').submit(function() { //submit이 발생하면
    $(this).ajaxSubmit(option); //옵션값대로 ajax비동기 동작을 시키고
    return false; //기본 동작인 submit의 동작을 막아 페이지 reload를 막는다.
});
</script> 

 

여기서 주의하실점은 submit기능 동작 정의 후 마지막에 있는 return false;입니다.

이부분을 처리하지 않으면 아무리 success, error옵션을 처리하여도 페이지가 submit이 먹히기 때문에 페이지 리로드 현상이 발생하고 결과값에 따른 동작(alert() 메시지 출력)을 정상적으로 처리 할 수 없습니다.

 

위 예제에서 사용한 option에서 사용한 기능 외에 다른 기능은  공식 API 문서를 통해 더 많은 정보를 볼 수 있습니다.

아래에 공식 API 정보가 있는 URL을 링크해두었으니 참고하시면 더 좋을 것 같습니다.

http://jquery.malsup.com/form/#getting-started

반응형
반응형

https://myhappyman.tistory.com/86

 

Spring - 파일 다운로드 예제 - IE, Chrome (스프링 파일 다운로드 구성)

스프링 프로젝트에서 파일 업로드와 다운로드를 하는 방법을 알아보겠습니다. 이번 포스팅에선 다운로드 예제를 작성했습니다. 추가 메이븐 다운로드 없이 스프링 설정만 하시면 됩니다. 파일 다운로드 web.xml

myhappyman.tistory.com

이전 포스팅 스프링에서 파일 다운로드 받기에 이어 업로드 예제를 진행해보겠습니다.

 

다운로드는 위 URL의 포스팅을 참고해주세요.

 

 

 

파일 다운로드

servlet-context.xml

<!--  spring-upload -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver" />
<!--  spring-upload -->

multipartResolverbean등록하여 추가합니다.

 

web.xml

파일 전송시 서블릿에서 10메가로 파일제한 처리하였습니다.

<!-- Processes application requests -->
<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/spring/servlet-context.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
	<!-- fileupload multipart-config maxSize set -->
	<multipart-config>
        <max-file-size>104857600</max-file-size> <!-- 10MB limit -->
        <max-request-size>104857600</max-request-size>
        <file-size-threshold>0</file-size-threshold>
    </multipart-config>
    <!-- fileupload multipart-config -->
</servlet>

 

commonController.java

upload요청이 올 경우 처리할 컨트롤러를 추가합니다.

(common/upload매핑)

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

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/common")
public class CommonController{
	
	private static final String FILE_SERVER_PATH = "C:/test";

	@RequestMapping("/upload")
	public String upload(@RequestParam("uploadFile") MultipartFile file, ModelAndView mv, Model model) throws IllegalStateException, IOException {
		if(!file.getOriginalFilename().isEmpty()) {
			file.transferTo(new File(FILE_SERVER_PATH, file.getOriginalFilename()));
			model.addAttribute("msg", "File uploaded successfully.");
		}else {
			model.addAttribute("msg", "Please select a valid mediaFile..");
		}
		
		return "board/board";
	}
}

uploadFile로 요청이 파라미터 값을 확인하고 지정한 경로로 저장합니다.

(C:/test)

 

board.jsp

<%@page import="java.net.URLEncoder"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
  <head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<meta name="description" content="">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>파일 다운로드</title>
  </head>
  <body>
  	<div class="content">
		<br/><br/>
		<h3>파일 업로드</h3>
		<form action="/common/upload.do" method="post" enctype="multipart/form-data">
		  <table>
		    <tr>
		      <td>Select File</td>
		      <td><input type="file" name="uploadFile" /></td>
		      <td>
		        <button type="submit">Upload</button>
		      </td>
		    </tr>
		  </table>
		</form>
	</div>
  </body>
</html>

ui를 작성합니다.

 

파일을 선택하고 submit버튼을 누르면 action 값에 의해 만들어 놓은 컨트롤러로 전달이 됩니다.

 

 


동작 결과

Upload버튼 클릭 후 정상적으로 서버에 저장되는 모습

 

c:/test에 저장된 모습

반응형
반응형

스프링 프로젝트에서 파일 업로드와 다운로드를 하는 방법을 알아보겠습니다.

 

이번 포스팅에선 다운로드 예제를 작성했습니다.

 

추가 메이븐 다운로드 없이 스프링 설정만 하시면 됩니다.

 

 

 

파일 다운로드

web.xml

<!-- Filter -->
<filter>
	<filter-name>encodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>utf-8</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>encodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Filter -->


<!-- Processes application requests -->
<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/spring/servlet-context.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

먼저 web.xml에서 필터 설정과 DispatcherServlet상태를 확인하겠습니다.

필터를 통해 utf-8 인코딩 처리를 하였고 servlet-context.xml의 위치는 /WEB-INF/spring/servlet-context.xml 로 지정하였습니다.

 

servel-context.xml

<!-- spring-donwload bean -->
<beans:bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <beans:property name="order" value="0" />
</beans:bean>
<beans:bean id="downloadView" class="com.psw.myapp.utils.DownloadView" />
<!-- /spring-donwload bean -->

<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<beans:property name="prefix" value="/WEB-INF/views/" />
	<beans:property name="suffix" value=".jsp" />
	<beans:property name="order" value="1" />
</beans:bean>

InternalResourceViewResolverorder순서를 1로 변경하였습니다.

DonwloadView 자바파일에서 다운로드 처리를 진행할 것이며 bean등록 처리를 해기 위해 BeanNameViewResolver를 사용했습니다.

serlet-context.xml 위치

 

 

CommonController.java

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

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/common")
public class CommonController{
	
	private static final String FILE_SERVER_PATH = "C:/test";

	@RequestMapping("/download")
	public ModelAndView download(@RequestParam HashMap<Object, Object> params, ModelAndView mv) {
		String fileName = (String) params.get("fileName");
		String fullPath = FILE_SERVER_PATH + "/" + fileName;
		File file = new File(fullPath);
		
		mv.setViewName("downloadView");
		mv.addObject("downloadFile", file);
		return mv;
	}
}

모든 파일은 C:/test에 있다고 가정하였습니다.

파일은 전달받은 fileName 파라미터값을 통해 파일을 세팅합니다.

downloadFile에 다운로드할 파일을 처리하고 bean등록을한 downloadView로 넘길것입니다.

 

 

DownloadView.java

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;

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

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

public class DownloadView extends AbstractView {

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

		File file = (File)model.get("downloadFile");
        if(file != null) {
            String fileName = null;
            String userAgent = request.getHeader("User-Agent");
            
            if(userAgent.indexOf("MSIE") > -1 || userAgent.indexOf("Trident") > -1){
                fileName = URLEncoder.encode(file.getName(), "utf-8").replaceAll("\\+", "%20");;
            }else if(userAgent.indexOf("Chrome") > -1) {
            	StringBuffer sb = new StringBuffer();
            	for(int i=0; i<file.getName().length(); i++) {
            		char c = file.getName().charAt(i);
            		if(c > '~') {
            			sb.append(URLEncoder.encode(""+c, "UTF-8"));
            		}else {
            			sb.append(c);
            		}
            	}
            	fileName = sb.toString();
            }else {
            	fileName = new String(file.getName().getBytes("utf-8"));
            }
            response.setContentType(getContentType());
            response.setContentLength((int)file.length());
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
            response.setHeader("Content-Transfer-Encoding", "binary");
            
            OutputStream out = response.getOutputStream();
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                FileCopyUtils.copy(fis, out);
            } catch(Exception e){
                e.printStackTrace();
            }finally{
                if(fis != null){
                    try{
                        fis.close();
                    }catch(Exception e){
                    	e.printStackTrace();
                    }
                }
                
                if(out != null) {
                	out.flush();
                }
            }
            
        }
	}
}

donwloadView는 AbstractView를 상속받아 사용합니다.

AbstractViewView구현을 위한 추상클래스로 컨트롤러가 반환한 모델과 병합되는 뷰입니다.

 

앞서 CommonController.java에서 세팅한 file인 downloadFile을 받아서 ie인지 체크 후 헤더 세팅과 OutputStream을 통해 파일을 출력합니다.

 

또한, 주의사항으로 브라우저마다의 인코딩이 달라서 User-Agent를 통해 접속한 브라우저를 체크하고 출력해줄 파일명을 만들때 인코딩 방식을 다르게 해야합니다. IE의 경우엔 11버전부턴 식별문자가 Trident로 변경되어 MSIE와 같이 조건문에 추가하였습니다.

 

controller와 downloadView 위치

 

이제 다운로드가 정상적으로 되는지 테스트 해보겠습니다.

전 board.jsp라는곳에서 처리할 예정이므로 컨트롤러와 jsp를 추가로 만들겠습니다.

 

 

BoardController.java

import java.util.HashMap;

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

@Controller
@RequestMapping("/board")
public class BoardController {
	
	@RequestMapping("/board.do")
	public ModelAndView board(@RequestParam HashMap<Object, Object> params, ModelAndView mv) {
		mv.setViewName("board/board");
		return mv;
	}
}

board/board.do url로 요청이 들어오면 board/board.jsp를 호출하도록 컨트롤러 설정을 했습니다.

 

 

board.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
  <head>
	<meta charset="UTF-8">
	<meta name="description" content="">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>파일 다운로드</title>
  </head>
  <body>
  	<div class="content">
		<br/><br/>
		<h3>파일 다운로드</h3>
		<a href="/common/download.do?fileName=이미지예제.jpg">download</a>
	</div>
  </body>
  
</html>

board.jsp

이제 마지막으로 WEB-INF/views/board 디렉토리와 board.jsp파일을 만들고 board.jsp를 작성했습니다.

 

board.jsp

a태그를 클릭하시면 CommonContorller에 요청을 했다가 DownloadView를 통해 다운로드가 되는 것을 볼 수 있습니다.

 

 

 

c:/test/이미지예제.jpg

 

다운로드된 jpg

 

 

 

IE 인코딩 에러

 

예제를 그대로 진행하면 한글명때문에 익스에서 하시는분들은 에러가 발생할 수 있습니다.

IE 예외처리 인코딩을 위해 jsp부분을 수정해주시면 됩니다.

 

 

board.jsp

<%@page import="java.net.URLEncoder"%>

...

<a href="/common/download.do?fileName=<%=URLEncoder.encode("이미지예제.jpg")%>">download</a>

URLEncoder를 추가 import해주시고 파라미터부분을 인코딩하여 넘기시면 IE에서도 되는 걸 확인 할 수 있습니다.

 

IE에서 정상 다운로드 처리

 

 

 

파일 업로드는 아래 URL에서 진행됩니다.

https://myhappyman.tistory.com/89

 

Spring - 파일 업로드 예제

https://myhappyman.tistory.com/86 Spring - 파일 다운로드 예제 - IE, Chrome (스프링 파일 다운로드 구성) 스프링 프로젝트에서 파일 업로드와 다운로드를 하는 방법을 알아보겠습니다. 이번 포스팅에선 다운로..

myhappyman.tistory.com

 

반응형
반응형

Spring tiles에 대해 다뤄보기 위해 새로운 프로젝트인 Legacy 프로젝트를 생성하고 파일들을 만들고

위치에 따라 설정을 하고 있었는데 'org.springframework.web.servlet.view.tiles3.TilesConfigurer' not found 와 같은 에러가 발생하였다.

 

관련 검색을 해보니 스프링 프레임워크의 버전이 낮아서 발생한 문제였다.

해결 방법으로 버전을 올린 뒤 Maven update - project clean하여 해결하였다.

 

 

기존에 문제가 되던 springframework 버전

3.1.1.RELEASE에서는 tiles3를 사용할 수 없었다.

 

확인을 해보니 3.2이상으로 버전을 올려야 한다고 해서 3.2.18로 변경하였다.

3.2.18.RELEASE로 변경해주었다.

project 우클릭 후 MAVEN - Update project... 처리 후 Proejct - clean... 이후에 에러가 사라진걸 볼 수 있었다.

 

사용한 tiles 버전은 3.0.3이였다.

반응형
반응형

스프링 타일즈란 뷰페이지의 jsp들을 상단, 사이드, 메인, 하단을 설정 상태로 include 처리해주는 구조의 템플릿을 말합니다.

 

페이지들을 일괄관리 할 수 있고, 공통사용하는 부분들을 매번 등록을 따로 해주지 않아도 되기 때문에 편리합니다.

 

적용하는법에 대해 알아보겠습니다.

먼저, 타일즈 적용을 위해 pom.xml에 메이븐 추가부터 해보겠습니다.

 


 

pom.xml - 라이브러리 추가

<properties>
	<org.apache.tiles-version>3.0.3</org.apache.tiles-version>
</properties>

<!-- Tiles -->
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-core</artifactId>
	<version>${org.apache.tiles-version}</version>
</dependency>

<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-servlet</artifactId>
	<version>${org.apache.tiles-version}</version>
</dependency>

<dependency>
          <groupId>org.apache.tiles</groupId>
          <artifactId>tiles-jsp</artifactId>
          <version>${org.apache.tiles-version}</version>
      </dependency>

<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-extras</artifactId>
	<version>${org.apache.tiles-version}</version>
</dependency>

Maven에 추가된 모습

pom.xml을 열어서 원하는 버전으로 입력하시면 다운로드가 될 겁니다.

저는 3.0.3버전으로 진행했습니다.

 

버전 정보들은 아래 URL에서 확인하시면 좋을 것 같습니다.

https://mvnrepository.com/artifact/org.apache.tiles/tiles-jsp

 

Maven Repository: org.apache.tiles » tiles-jsp

Tiles JSP support: Classes and tag libraries to use Tiles in a JSP environment. VersionRepositoryUsagesDate3.0.x3.0.8Central8Sep, 20173.0.7Central12Aug, 20163.0.5Central18Sep, 20143.0.4Central5May, 20143.0.3Central4Nov, 20133.0.1Central7Jul, 20123.0.0Centr

mvnrepository.com

 

다음으로 사용할 상단, 사이드, 메인, 하단으로 사용할 jsp들을 만들어 놓겠습니다.

나중에 타일즈 설정파일에서 연결을 할겁니다.

 

WEB-INF/views/tiles라는 디렉토리를 만들고 안에 각각 JSP들을 구성합니다.

 

 

사용할 JSP구성

JSP 4개 생성

안에 사용할 내용은 비워두셔도 됩니다.

 

다음은 servlet-context.xmlInternalResourceViewResolver를 변경 및 tiles설정을 추가하겠습니다.

 

 

servlet-context.xml

<beans:bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
    <beans:property name="order" value="2" />
</beans:bean>

<!-- tiles 설정 -->
<beans:bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <beans:property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
    <beans:property name="order" value="1" />
</beans:bean>
 
<beans:bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
    <beans:property name="definitions">
        <beans:list>
            <beans:value>/WEB-INF/spring/tiles-define.xml</beans:value>
        </beans:list>
    </beans:property>
</beans:bean>

order 부분을 유의해서 설정해주셔야 합니다. tiles가 우선순위가 되도록 해주어야 합니다.

 

 

tiles 설정을 추가 후 이클립스에서 에러가 발생한다면 아래 포스팅을 참고해주세요

https://myhappyman.tistory.com/82

 

Spring - tiles 적용 에러 발생 'org.springframework.web.servlet.view.tiles3.TilesConfigurer' not found

Spring tiles에 대해 다뤄보기 위해 새로운 프로젝트인 Legacy 프로젝트를 생성하고 파일들을 만들고 위치에 따라 설정을 하고 있었는데 'org.springframework.web.servlet.view.tiles3.TilesConfigurer' not foun..

myhappyman.tistory.com

 

servlet-context.xml을 추가했으면 beans:value에 처리한 tiles-define.xml을 생성하고 설정하겠습니다.

tiles-define.xml

xml을 생성하고 설정을 하겠습니다.

 

 

tiles-define.xml

<?xml version="1.0" encoding="UTF-8"?>
<!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>
	<!-- main layout -->
	<definition name="layout-tiles" template="/WEB-INF/views/tiles/tiles-layout.jsp">
		<put-attribute name="header" value="/WEB-INF/views/tiles/headerTemplate.jsp" />
		<put-attribute name="left" value="/WEB-INF/views/tiles/sidebarTemplate.jsp" />
		<put-attribute name="body" value="" />
		<put-attribute name="foot" value="/WEB-INF/views/tiles/footerTemplate.jsp" />
	</definition>
	
	<definition name="*/*" extends="layout-tiles">
		<put-attribute name="body" value="/WEB-INF/views/{1}/{2}.jsp" />
		<put-attribute name="title" value="게시판" />
	</definition>
</tiles-definitions>

 

이 부분에서는 상단, 사이드, 바닥 메인으로 사용할 템플릿 등을 설정합니다.

definition에서 name은 변수처럼 사용할 이름을 지정하고 template은 사용할 jsp를 지정합니다.

그 내부에는 따라오는 헤더, 사이드, 바닥 jsp들을 처리하고 마찬가지로 name은 사용할 이름 value에는 실제로 들어갈 jsp를 입력합니다.

 

먼저 생성해놓은 JSP 중 tiles-layout.jsp를 메인으로 사용하고 각각 header sidebar, footer부분을 영역별로 사용할 예정입니다.

 

아래서 extends부분은 위에서 선언한 jsp를 상속받아 실제로 사용하는 페이지에 이식하고 body영역은 실제 페이지를 사용하기 위해 설정하는 부분으로 메인 body로 쓸 jsp영역을 설정할 수 있으며, {1}/{2}.jsp을 통해 board/board.jsp로 갈때 자연스럽게 이식되어 사용할 수 있게 합니다.

title과 같은 설정으로 브라우저의 title태그도 변경이 가능합니다.

 

이로써 간단하게 tiles설정이 끝났습니다.

 


 

JSP들 소스 수정과 CSS들을 입력하여 동작하는지 확인해보겠습니다.

 

headerTemplate.jsp

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

 

sidebarTemplate.jsp

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

 

footerTemplate.jsp

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

 

tiles-layout.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="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<!-- 공통변수 처리 -->
<c:set var="CONTEXT_PATH" value="${pageContext.request.contextPath}" scope="application"/>
<c:set var="RESOURCES_PATH" value="${CONTEXT_PATH}/resources" scope="application"/>
<!DOCTYPE html>
<html>
  <head>
	<meta charset="UTF-8">
	<meta name="description" content="">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	<script type="text/javascript">
		var CONTEXT_PATH = "${CONTEXT_PATH}";
		var RESOURCES_PATH = "${RESOURCES_PATH}";
	</script>
	<link rel="stylesheet" href="${RESOURCES_PATH}/css/common.css">
    <title><tiles:insertAttribute name="title" /></title>
  </head>
  
  <body>
  	<div class='wrap'>
  		<tiles:insertAttribute name="header" />
		  <div class='content'>  	
  			<tiles:insertAttribute name="left"/>
	  		<div class="page_content">
	  			<tiles:insertAttribute name="body"/>
	  		</div>
  		</div>
  		<tiles:insertAttribute name="foot" />
  	</div>
  </body>
  
</html>

공통변수 처리부분이나 script부분은 무시하셔도 됩니다.

tiles-define.xml에서 각각 JSP들을 불러왔으니 사용해야겠죠? 해당페이지에서 원하는 영역에 삽입을 할 수 있습니다.

<tiles:insertAttribute name="사용할 이름" />을 통해 적용하시면 됩니다.

 

resources/css/common.css를 만들었습니다.

 

 

common.css

@charset "UTF-8";

*{
	margin: 0;
	padding: 0;
}
.wrap{
	width: 100%;
}

.Header, .content, .Footer{
	width: 100%;
	float: left;
}

.Header, .Footer{
	height: 8em;
}

.SideBar{
	width: 10%;
	height: 600px;
	background-color: #FFBB00;
	float: left;
}

.page_content{
	width: 90%;
	height: 600px;
	background-color: #EAEAEA;
	float: left;
}

.Header{
	background-color: #ABF200;
}

.Footer{
	background-color: #FF00DD;
}

 

 

예시로 board/board.jsp를 이동하게 된 경우 발생한 결과 화면입니다.

tiles 적용

반응형
반응형

일정시간마다 기존에 그려진 차트 데이터에 추가되고 일정 데이터가 쌓이면 기존에 쌓였던 데이터는 지워지는 차트를 구현하게 되었는데 구글차트를 사용하게되어 포스팅을 진행합니다.

 

https://developers.google.com/chart

 

Charts  |  Google Developers

Interactive charts for browsers and mobile devices.

developers.google.com

 

해당 URL에서 데모차트들을 참조하여 시작하였습니다.

 

구글차트는 단순하게 입력하는 데이터의 배열값이 바뀌면 알아서 바뀌어서 처음에 구현할때는 chart의 data부분값을 배열로 따로 처리하여 넣고 빼고를 하였는데, 추가되는 데이터가 애니메이션 효과처럼 추가되는걸 표현하고 싶었고, 구글 차트 중 animation파트를 확인하면서 관련 예제를 기준으로 테스트버전을 만들었습니다.

 

https://developers.google.com/chart/interactive/docs/animation

 

Animation  |  Charts  |  Google Developers

This page describes how to animate modifications made to a chart, instead of applying them instantly. Contents Overview Google charts can animate smoothly in one of two ways, either on startup when you first draw the chart, or when you redraw a chart after

developers.google.com

 

해당 URL에서는 애니메이션이 동작하면서 차트가 그려지는 부분을 예제로 제공합니다.

 

 

세션중 Adding and removing rows 라는 부분이 있는데, 버튼 클릭에 따른 랜덤함수 값에 따른 비교 후 추가해주는 함수부분을 참고하여 만들었습니다.

 


아래는 사용한 테스트용 일정시간마다 데이터를 그리는 구글차트 자바스크립트 코드입니다. 

<html>
  <head>
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <style>
      .chart{
        border: 1px solid orange;
        float: left;
        margin: 30px;
      }
    </style>
  </head>
  <body>
    <div id="chart" class="chart" style="width: 779px; height: 300px"></div>
    <script type="text/javascript">
    //Google Stuff
    google.charts.load('current', {packages: ['corechart']});
    google.charts.setOnLoadCallback(function(){ drawChart(new_option)});
    </script>
    <script type="text/javascript">
      var chartOption = function(target, maxValue, color, name){
        this.name = name;
        this.target = target;
        this.data = null;
        this.chart = null;
        this.options = {
          legend: { position: 'none' },
          vAxis: {minValue:0, maxValue:maxValue},
          hAxis: {
            textStyle: {
              fontSize: 11
            }
          },
          colors: [color],
          animation: {
            duration: 500,
            easing: 'in',
            startup: true
          }
        }
        
      }

      var new_option = new chartOption('chart', 80, '#FF5E00', '온도');
      
      function drawChart(option) {
        var o = option;
        if(o != null){
          //초기값일때만 처리
          if(o.chart == null && o.data == null){
            o.data = new google.visualization.DataTable();
            o.data.addColumn('string', 'time');
            o.data.addColumn('number', o.name);
            o.data.addRow(['', 0]);
            o.chart = new google.visualization.LineChart(document.getElementById(o.target));
          }

          o.chart.draw(o.data, o.options);
        }
      }

      function animateRenewal(option){
        var o = option;
        if (o.data.getNumberOfRows() >= 10) {
          o.data.removeRow(0);
        }


        var value = 0;
        var maxValue = o.options.vAxis.maxValue;
        if(maxValue <= 1){
          value = Number((Math.random() * maxValue).toFixed(1));
        }else {
          value = Math.floor(Math.random() * maxValue);
        }
        o.data.insertRows(o.data.getNumberOfRows(), [[getNowTime(), value]]);
        drawChart(o);
      }

      setInterval(function(){
        animateRenewal(new_option);
      }, 1000);
      
      function getNowTime(){
        var d = new Date();
        var sep = ":";
        var hh = d.getHours();
        var mm = d.getMinutes();
        var ss = d.getSeconds();
        return hh + sep + mm + sep + ss;
      }
      
    </script>
  </body>
</html>

 

동작캡처

 

 

 

 


간단하게 코드 동작 설명을 하겠습니다. 해당 소스들은 구글차트 API 공식 문서가 아니며 제가 사용하기 위해 만든 코드입니다.

 

구글차트를 사용하기 위해 loader.js를 연결하였고 , 아래 2줄부분은 차트를 세팅하고  준비가되면 drawChart 함수에 의해 차트를 그립니다.

<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> //구글차트 사용을 위해 loader.js 추가
<script type="text/javascript">
    //Google Stuff
    google.charts.load('current', {packages: ['corechart']});
    google.charts.setOnLoadCallback(function(){ drawChart(new_option)});
</script>

 

 

다음으로 drawChart메소드에 사용된 new_option은 chartOption 생성자를 통해 클래스기반의 구글차트에서 사용할 데이터입니다.

1. @param >target : 차트를 그릴 요소의 ID값입니다.

2. @param >maxValue : 차트의 Y축 데이터의 맥스값입니다.

3. @param >color: 차트를 그릴때 사용할 색상입니다.

4. @param >name: 차트의 이름을 만들용도로 사용했습니다.

var chartOption = function(target, maxValue, color, name){
  this.name = name;
  this.target = target;
  this.data = null;
  this.chart = null;
  this.options = {
    legend: { position: 'none' },
    vAxis: {minValue:0, maxValue:maxValue},
    hAxis: {
      textStyle: {
        fontSize: 11
      }
    },
    colors: [color],
    animation: {
      duration: 500,
      easing: 'in',
      startup: true
    }
  }
  
}

var new_option = new chartOption('chart', 80, '#FF5E00', '온도');

객체 내부의 chart, options, data부분은 구글차트의 데이터를 집어넣기 위해 사용했습니다.

 

 

다음으로 drawChart 함수를 보겠습니다.

function drawChart(option) {
  var o = option;
  if(o != null){
    //초기값일때만 처리
    if(o.chart == null && o.data == null){
      o.data = new google.visualization.DataTable();
      o.data.addColumn('string', 'time');
      o.data.addColumn('number', o.name);
      o.data.addRow(['', 0]);
      o.chart = new google.visualization.LineChart(document.getElementById(o.target));
    }

    o.chart.draw(o.data, o.options);
  }
}

처음 객체의 chart, data속성을 확인하고 null이라면 처음 new로 생성된 객체이므로 초기값 세팅을 해줍니다.

이부분에서 그릴 데이터의 옵션, 초기값들을 설정하면서 제목과 그리는 차트이름을 설정합니다.

저는 LineChart를 사용했습니다.

처음만 초기값부분을 타고 이후에는 chart.draw동작만 처리합니다.

 

 

마지막으로 볼 부분은 animateRenewal함수입니다.

function animateRenewal(option){
  var o = option;
  if (o.data.getNumberOfRows() >= 10) {
    o.data.removeRow(0);
  }


  var value = 0;
  var maxValue = o.options.vAxis.maxValue;
  if(maxValue <= 1){
    value = Number((Math.random() * maxValue).toFixed(1));
  }else {
    value = Math.floor(Math.random() * maxValue);
  }
  o.data.insertRows(o.data.getNumberOfRows(), [[getNowTime(), value]]);
  drawChart(o);
}

setInterval(function(){
  animateRenewal(new_option);
}, 1000);

해당 함수에는 일정시간마다 반복하면서 데이터를 만들기 위해 만들었습니다.

실제 DB데이터에서 데이터를 가져와서 넣을분들은 이부분을 ajax 비동기처리하여 값을 가져오시고 마지막에 처리한 data.insertRows()메소드, data.removeRow()후 drawChart를 사용하시면 됩니다.

 

insertRows 메소드에 대해 간단히 설명드리면

첫번째 파라미터에 현재 구글차트에 들어간 데이터에 넣을 위치(index)값이며,

두번째 파라미터는 처리할 값입니다. 0번째값은 표기할 값 1번째값은 실제 값입니다. ex) 15:40:30  34도

 

removeRow 메소드는 입력하신 index값의 데이터를 지워줍니다.

저는 제일 과거의 데이터만 삭제하기 위해 0만 지워주도록 했습니다.

 

setInterval 메소드를 1초마다 동작시켜서 1초마다 랜덤함수로 데이터를 가져와 animateRenewal함수를 계속 동작시켜서 계속 그리도록 처리하였습니다.

반응형
반응형

자세한 mybatis mysql 연결 설정법은 이번 글에서 포스팅하지 않겠습니다.

일반적으로 mybatis를 통해 연결하는게 아닌 터널링을 통해 접근하는법을 포스팅입니다.

 

@WebListener 어노테이션은 WAS서버에게 해당 클래스가 리스너임을 명시해줍니다.

 

ServletContextListener 클래스를 상속받으며 인터페이스를 상속받았으므로 2개의 클래스를 재정의하여

사용하시면 됩니다.

 

contextInitialized, contextDestroyed 메소드가 존재합니다.

 

contextInitialized : 해당 메소드는 was가 시작되면서 dispatcherServlet보다 먼저 시작되는 메소드입니다.

contextDestroyed : 해당 메소드는 was가 종료되면 종료되기 직전에 실행되는 메소드입니다.

 

@WebListener를 시작하기전 pom.xml에 라이브러리를 추가합니다.

 

pom.xml

<!-- WebListnener annotation-->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>

<!-- jsch 터널링을 위해 필요합니다. -->
<dependency>
  <groupId>com.jcraft</groupId>
  <artifactId>jsch</artifactId>
  <version>0.1.53</version>
</dependency>

 

SSH 연결 class를 정의합니다.

SSHConnection.java

import java.util.Properties;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

public class SSHConnection {
	private final static String HOST = "SSH연결을 할 IP";
	private final static Integer PORT = 포트; //기본포트는 22
	private final static String SSH_USER = "연결 USER값"; //ex) root
	private final static String SSH_PW = "연결 비밀번호값"; //ex) 1234
	
	private Session session;
	
	public void closeSSH() {
		session.disconnect();
	}
	
	public SSHConnection() {
		try {
			Properties config = new Properties();
			config.put("StrictHostKeyChecking", "no");
			JSch jsch = new JSch();
			session = jsch.getSession(SSH_USER, HOST, PORT);
			session.setPassword(SSH_PW);
			session.setConfig(config);
			session.connect();
			session.setPortForwardingL(3316, "127.0.0.1", 3306); //127.0.0.1/3316으로 접근한 포트를 연결HOST/3306으로 포트포워딩
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
}

 

 

위에서 정의한 클래스를 사용하는 클래스를 정의합니다.

MyContextListener.java @WebListener

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyContextListener implements ServletContextListener {
	
	private SSHConnection sshConnection;

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("init start!");
		try {
			sshConnection = new SSHConnection();
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("init destory!");
		sshConnection.closeSSH();
	}
}

 

contextInitialized 메소드는 was가 동작하면 바로 실행되면서 앞 서 만든 SSHConnection 클래스를 통해 터널링을 할 준비를 합니다.

 

mybatis정보 (mybatis-context.xml)

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close" >
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://127.0.0.1:3316/databaseName" />
  <property name="username" value="username" />
  <property name="password" value="password" />
  <property name="validationQuery" value="SELECT 1 FROM DUAL" />
  <property name="testWhileIdle" value="true" />
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">     
  <property name="dataSource" ref="dataSource" />
  <property name="mapperLocations" value="classpath:mapper/**/*.xml" />
  <property name="configLocation" value="/WEB-INF/mybatis/mybatis-config.xml" />
  <property name="transactionFactory">
  	<bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
  </property>
</bean>

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

3316으로 접근합니다.

하지만 터널링 처리로 인해 3306의 SSH 연결한 3306의 mysql에서 데이터를 가져와서 데이터를 뿌려줍니다.

 

 

결과페이지

local에서 was를 돌렸지만 SSH 터널링으로 인해 연결한 120서버(예시)의 mysql 3306포트의 데이터를 조회하여 결과를 가져왔습니다.

login.jsp

 

 

page 결과

반응형
반응형

바쁘신분들을 위해...

chrome, IE 모두 new Date를 사용하려면 replace 정규식을 활용하여

-, .등으로 구분된 날짜값을 /으로 변경처리한다.

var timedate = "2020-01-31 13:46:55";
new Date(timedate.replace(/[.-]/gi, "/")); //정상동작
//"2020/01/31 13:46:55";

air datepicker 라이브러리를 활용하여 달력을 제어할 일이 있었는데, 여기서 날짜등을 제한할때,

new Date() 메소드를 활용하여 특정 프로퍼티에 넣어주면 제한이 되었다.

 

년월일까지만 있는 경우에는 전혀 문제없이 달력이 동작하였는데,

어느날 버그 신고가 있어 확인해보니 제한 날짜를 입력해주는 부분이

(yyyy-MM-dd HH:mm:ss) 형태였다.

 

 

new Date 사용예시

IE와 크롬에서 결과값을 콘솔에 찍은 사진인데 날짜형태의 yyyy-MM-dd는 문제가 없다.

var str_date = "2020-01-31";
console.log(new Date(str_date));

 

 

 

 

날짜 "." 구분자

하지만 "."구분자는 문제가 있다.

var str_date = "2020.01.31";
console.log(new Date(str_date));

 

IE에서 표준이 아닌듯하다... 인식하지 못한다.

 

 

 

날짜 시간의 구분자 "-"

먼저 사용예제로 보여주었다 "-" 구분자도 시분초까지 있는 경우 문제가 발생한다.

var str_date = "2020-01-31 13:51:55";
console.log(new Date(str_date));

 

크롬은 언제나 잘 동작한다... 크롬짱

 

구분자 "/"

그럼 크롬과 IE에서 모두 동작하려면 어떻게 해야할까?

바로 구분자를 "/"으로 날짜부분을 처리하면 된다.

var str_date = "2020/01/31 13:51:55";
console.log(new Date(str_date));

 

"2020/01/31 13:51:55" new Date() 결과

 

 

 

정규식을 통한 크로스 브라우징

보통 날짜부분은 "-", ".", "/" 으로 구분을 많이 하는데 모두 동작하게 하려면, 정규식을 사용하면 된다.

var str_date = "2020-01-31 13:51:55"; //"2020.01.31 13:51:55" 
str_date = str_date.replace(/[.-]/gi, "/") //. 또는 -으로 되어있는 부분을 치환한다.
console.log(new Date(str_date));

 

.구분자 -구분자 모두 replace 시켜서 두 브라우저 모두 동작한다.

반응형