2013년 9월 25일 수요일

흥미로운 구조...


흠 흥미로운 클래스구조를 발견함

//

package com.jetddo.study.test;

public interface Page {
 
 public void message();

}

//////////////////////////////////

package com.jetddo.study.test;

public class BasePage implements Page {
 
 protected static Page m_instance;
 
 public static Page getInstance(){
  
  if(m_instance == null){
   m_instance = new BasePage(); 
  }
  
  return m_instance;
 }
 
 public void message(){
  
  System.out.println("Base Page");
 }
}

////////////////////////////////////////

package com.jetddo.study.test;

public class ExecPage extends BasePage {
 
 public static Page getInstance(){
  
  if(m_instance == null){
   m_instance = new ExecPage(); 
  }
  
  return m_instance;
 }
 
 public void message(){
  
  System.out.println("Exec Page");
 }
}

//////////////////////////////////////
 
싱글톤 기법인거 같은데..

protected 로 선언하면서 

확장도 가능하게 변형한거 같다.

//

package com.jetddo.study.test;

import org.junit.Test;

public class TestMain {
 
 @Test
 public void test(){
  
  
  
  ExecPage.getInstance();
  
  BasePage.getInstance().message();
  
 }

}

//

m_Instance 가 static 인것을 이용해 클래스간의 변수를 공유하게 하는 방법이다.
클래스를 재정의 하고 싶다면 구현후 ExecPage.getInstance(); 이것만 추가하면 객체가 바뀐다.

javascript


javascript 에서 java 의 package 나 c 의 namespace 처럼
객체를 체계적으로 관리 할 수 있다.
//

if(typeof window.jetddo =='undefined') window.jetddo = {};
if (!jetddo.test) jetddo.test = {};

jetddo.test.Class = new (function(){
			
	this.initClass = function(htOptions){
		alert(JSON.stringify(arguments[0]));
	};
			
})();
		
jetddo.test.Class = {
				
		initClass : function(htOptions){

					
				alert(JSON.stringify(arguments[0]));
					
		}
				
};

//
위에 javascript 객체를 사용하는데 있어서 두가지 방법이 있다.

위에 있는 것은 Class 라는 객체를 정의하는데 있어서 function 으로 위임하는 것이다.

즉 일종의 생성자가 되는 것이다.

아래에 있는 것은 객체를 json 형태로 규정해서 각 변수를 정의 한다고 볼 수 있다.

2013년 9월 17일 화요일

mybatis 적용


pom.xml 에 다음을 추가한다.




 org.mybatis
 mybatis
 3.1.1


 org.mybatis
 mybatis-spring
 1.1.1




기존 data-context.xml 에 설정되어있던 ibatis 설정을 다음과 같이 바꾼다.




 
            
 
     
     
  
 
  
     
  

 
 
  
  

 




mybatis-config.xml 을 추가한다. 경로는 알아서 잘 하면 댐





  


  
  
  
  
  
  
 
 
 
  
  
  
  
 

  



기존에 있던 sql xml 파일 형식을 다음과 같이 바꾼다.






 
 
  




설정파일 변경은 끝났다 이제 dao 클래스를 보자

////////////////////////////////

package com.jetddo.study.auth.dao;

import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.stereotype.Repository;

import com.jetddo.study.auth.model.AuthUserVo;

@Repository
public class AuthUserDao  extends SqlSessionDaoSupport {
 
 public AuthUserVo selectOnebyUserId(String usr_id){
  
  return getSqlSession().selectOne("auth_usr.selectOnebyUserId", usr_id);
  
 }
}

//////////////////////////
변경 끝~!

2013년 9월 13일 금요일

transaction 적용

/////////////////////////////



package com.jetddo.study.config;

import java.io.IOException;

import javax.sql.DataSource;

import org.apache.tomcat.dbcp.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.jetddo.study.util.Base64Decoder;

@Configuration
public class DataSourceConfig {
 
 @Bean(destroyMethod="close")
 public DataSource dataSource() throws IOException {
  
  BasicDataSource dataSource = new BasicDataSource();
  dataSource.setDriverClassName(Base64Decoder.decode("b3JhY2xlLmpkYmMuZHJpdmVyLk9yYWNsZURyaXZlcg=="));
  dataSource.setUrl(Base64Decoder.decode("amRiYzpvcmFjbGU6dGhpbjpAMTI3LjAuMC4xOjE1MjE6b3JjbA=="));
  dataSource.setUsername(Base64Decoder.decode("amV0ZGRv"));
  dataSource.setPassword(Base64Decoder.decode("Y2RtYTIwMDQ="));
  dataSource.setDefaultAutoCommit(false);
  
  return dataSource;
  
 }
}


/////////////////////////

위의 소스를 봅시다. 아랫줄에 AutoCommit 을 false로 설정하고 있다.
true 로 설정하면 dao 에서 바로 commit 이 일어나게 된다. 문제가 발생한 경우 되돌릴수 없다.
그래서 Transaction 을 적용한다. 매우 간단하다.

ibatis 설정 xml 에 다음을 추가하자.





 
            
 
    
 
       
 
 
 
  
  

 





dao class 에 다음과 같이 설정하자

/////////////////////////////////////

package com.jetddo.study.manage.dao;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.ibatis.sqlmap.client.SqlMapClient;
import com.jetddo.study.manage.model.ManageUserVo;

@Repository
@SuppressWarnings("unchecked")

public class ManageUserDao extends SqlMapClientDaoSupport {
 
 @Resource(name="sqlMapClient")
 public void setSuperSqlMapClient(SqlMapClient sqlMapClient) {
  super.setSqlMapClient(sqlMapClient);
 }
 
 @Transactional
 public List selectAll(){
  
  return (List)getSqlMapClientTemplate().queryForList("mng_usr.selectAll");
  
 }
 
 @Transactional
 public ManageUserVo selectOnebyUserUid(long usr_uid){
  
  return (ManageUserVo)getSqlMapClientTemplate().queryForObject("mng_usr.selectOnebyUserUid",usr_uid);
  
 } 
 
 @Transactional
 public void insertOne(ManageUserVo mngUsrVo){
  
  getSqlMapClientTemplate().insert("mng_usr.insertOne",mngUsrVo);

 }
 
 @Transactional
 public void deleteOnebyUserUid(long usr_uid){
  
  getSqlMapClientTemplate().delete("mng_usr.deleteOnebyUserUid",usr_uid);

 }
}

////////////////////////////////////


참고로 autocommit 을 false로 설정하면 transaction 을 통해서만 commit 이 된다.

tiles 3.0

심심해서 tiles 3.0 을 적용시켜 보았다.
설정들을 봅시다





  org.apache.tiles
  tiles-core
   3.0.0
  

   org.apache.tiles
  tiles-servlet
   3.0.0


   org.apache.tiles
   tiles-jsp
    3.0.0







    
   

 

    
        /WEB-INF/tiles.xml
    

 
 









    
        
        
        
    
    
    
     
    
    
    
     
    
    
    
     
    




설명함

알아서 파일이름 매칭시켜서 복붙하면 되고,
tiles.xml 패턴은 아주 쉽다.

controller 에서 
return "common/tiles/test.jsp" 같이 리턴했다면
common={1}, tiles={2}, test={3} 이 된다.
다른거 건드릴필요 없다.

template.jsp 페이지도 만들어주자

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="t" %>
<%@ page session="false" pageEncoding="UTF-8" %>



 
 

 Home
 
  
  

component-scan 2

component-scan 보충




 
 



spring-security 적용시 root-context.xml 에서 로드가 되어야 하기 때문에 해당되는 Service 나
Dao 객체등을 미리 읽어야 한다. 그래서 위와 같이 필요한 패키지나 클래스만을 읽어 사용 할 수 있다.

물론 spring 은 모든 객체를 singleton registry 형태로 관리하기 때문에 객체가 중복될 염려는 없겠다.

2013년 9월 11일 수요일

datasource 관련

spring 에서 sql connection 을 위한 db 접속정보를 설정할때 보통 datasource 를 통해서 기본정보들을 넣어주게 된다


            
 
    
 
       
 
 


위에는 datasource 생성은 빠져있다. 만약 xml형태로 설정을 한다면 보안상 문제가 될 소지가 있다.
그래서 source 상에서 암호화를 통해 설정하는 방식도 존재한다.

하지만 db connection 을 위해서는 datasource 를 생성해야 하고 xml 상에서 생성 할 수 없다면
source 내에서 new 로 생성을 할 수 밖에 없다. 하지만 spring framework 특성상 DI 법칙에 따라 
생성을 주도 할 수 있는 entrypoint 가 존재하지 않는다.
따라서 다음과 같은 방법을 사용한다.

package com.jetddo.study.config;

import java.io.IOException;

import javax.sql.DataSource;

import org.apache.tomcat.dbcp.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.jetddo.study.util.Base64Decoder;

@Configuration
public class DataSourceConfig {
 
 @Bean(destroyMethod="close")
 public DataSource dataSource() throws IOException {
  
  BasicDataSource dataSource = new BasicDataSource();
  dataSource.setDriverClassName(Base64Decoder.decode("b3JhY2xlLmpkYmMuZHJpdmVyLk9yYWNsZURyaXZlcg=="));
  dataSource.setUrl(Base64Decoder.decode("amRiYzpvcmFjbGU6dGhpbjpAMTI3LjAuMC4xOjE1MjE6b3JjbA=="));
  dataSource.setUsername(Base64Decoder.decode("amV0ZGRv"));
  dataSource.setPassword(Base64Decoder.decode("Y2RtYTIwMDQ="));
  dataSource.setDefaultAutoCommit(false);
  
  return dataSource;
  
 }
}

@Configuration, @Bean 의 어노테이션이 보일거다.
@Configuration 이 것은 이 java class 는 spring의 설정 역할이라는 것을 알려준다.
그리고 @Bean 은 이 메소드에서 리턴 되는 객체는 spring의 Bean객체라는 것을 알려준다.
즉 위 클래스는 소스상에서 자동으로 bean객체를 생성한다.

또한 @Configuration 은 @Component 어노테이션을 meta-annotation 으로 가지고 있기 때문에
component-scan 을 통해 자동으로 bean 으로 등록이 된다.

AOP Logging

Interceptor logging 은 url 요청에 한해서만 로깅이 가능한 단점이 있다. 모든 class 에 대해 logging 관심사를 추가하는 aop 사용이 더 효율적이다.
 

 

설정 파일을 만들어 위의 내용을 작성한다. 필요한 lib 는 spring 관련 aop 와 aspectj, cglib2 이다. 자세한 내용은 검색 ㄱㄱ
package com.jetddo.study.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class LoggingAdvice {
 
 @Before("execution(* com.jetddo.study..*.*(..))") 
 public void loggingAdvice(JoinPoint joinPoint){
  
  String pointStr = joinPoint.toShortString();
  
  if(!pointStr.contains("SessionInterceptor")){
   System.out.println(" Method : "+pointStr);
   System.out.println("[ Arguments ]");
   Object[] objArray = joinPoint.getArgs();
   for(int i=0 ; i < objArray.length ; i++){
    System.out.println(i+" : "+objArray[i]);
   }
   System.out.println("--------------------------------------------------------------------");
  }
  
  
 }
}


위 class 를 추가한다.
execution(* com.jetddo.study..*.*(..))
에 대해서는 나중에 정리

2013년 9월 9일 월요일

component-scan

  
 
@Repository, @Service, @Controller, @Component
위와 같은 annotation 을 spring stereotype 이라고 한다.

compenent-scan 을 통해 자동으로 bean 객체로 등록이 된다.

Session 관리 2

다른 버전도 올리자 이전 소스에서는 restful 형식의 url에 촛점을 맞추느라 일반화가 안되어있다. 그래서 그냥 초기화면으로 돌아가게 하는 식으로 만들어봤다.
package com.jetddo.study.interceptor;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Component
public class SessionInterceptor implements HandlerInterceptor {
 
 private static final Logger logger = LoggerFactory.getLogger(SessionInterceptor.class);

 
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler) throws Exception {
  
  HttpSession session = request.getSession(false);
  
  if(session != null && session.getAttribute("SPRING_SECURITY_CONTEXT") != null
          && session.getAttribute("sessionVo") == null){   
      
   session.invalidate();
        
   logger.info("[sessionVo] has EXPIRED !!!");
   
   response.sendRedirect(request.getContextPath());
      
   return false;   
  }
    
  return true;
 }
 
   
 public void postHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler, ModelAndView modelAndView) throws Exception {

 }

 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
     Object handler, Exception ex) throws Exception {

 }
}

2013년 9월 5일 목요일

Session 관리

오랫동안 자리를 비우거나 할때 session 이 유실되면 로그인 정보와 동기화가 맞지 않아 오류를 낼 때가 있다. 모든 페이지에서 세션검사를 하는 것은 매우 비효율적이다. Interceptor 를 통해서 손쉽게 관리가 가능하다.
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler) throws Exception {
  
  HttpSession session = request.getSession(false);
  
  if(session == null || session.getAttribute("SPRING_SECURITY_CONTEXT") == null){
   
   logger.info("------------NOT LOGINED--------------");
   // 첫 로그인
  } else if(session != null && session.getAttribute("sessionVo") != null
          && session.getAttribute("SPRING_SECURITY_CONTEXT") != null){
   
   logger.info("--------------LOGINED----------------");
   
  } else if(session != null && session.getAttribute("SPRING_SECURITY_CONTEXT") != null
          && session.getAttribute("sessionVo") == null){   
      
   logger.info("----------SESSIONVO EXPIRED----------");
   
   session.invalidate();
        
   response.sendRedirect(request.getRequestURI());
      
   return false;   
  }
    
  return true;
 }

코드 수정함 원래 목적은 session 이 없을때는 spring-security 로 넘기는 식으로 하려 했으나 interceptor의 개입시점이 spring-security 보다 늦는 문제가 있다 그래서 sessionVo 가 소멸됬을때 session 전체를 소멸하고 다시 request url 로 이동하는 식으로 했다 이는 POST 방식일때 같은 url 로 get방식으로 이동하므로 restful 스타일에 알맞다

Interceptor

Controller 가 엄청 많은데 log 형식을 바꾼다면 모든 클래스에 손을 대야한다. 그러나 Interceptor 를 활용하면 손쉽게 관리할 수 있다.
package com.jetddo.study.interceptor;

import java.util.Enumeration;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;


@Component
public class LoggingInterceptor implements HandlerInterceptor {

   private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);

   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler) throws Exception {
    
    logger.info("-------------------------------");
       logger.info("url: "+request.getRequestURI());
       
       String param = "";
       
       Enumeration< ? > e = (Enumeration< ? >)request.getParameterNames();
       while(e.hasMoreElements()){
        
        String name = (String)e.nextElement();
        
        param += "["+name+": "+request.getParameter(name)+"]";
       }
       
       if(param.trim().length() > 0){
        logger.info("params: "+param);
       }
       
       return true;
   }
   
   public void postHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler, ModelAndView modelAndView) throws Exception {

   }

   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
     Object handler, Exception ex) throws Exception {

   }
}

위와 같이 작성 후
 
  
   
   
  
  
   
   
  
 
servlet-context.xml 에 위와 같이 작성한다.

@Autowired, @Resource

1. @Autowired Type 을 통해 자동주입 ( 인터페이스를 통해 2개의 구현체가 있을 경우 사용 불가능) 2. @Resource name 을 통해 자동 주입 ex)
@Resource(name="userDaoImpl") 
UserDao userDao;

//UserDaoImpl 이라는 Class 를 주입함
//spring 설정 파일에서 context-scan 를 사용하거나 bean 객체로 등록되어야 함

2013년 8월 21일 수요일

ObjectMapper 사용법

package com.jetddo.study.test;

import java.util.List;

import lombok.Data;

import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Test;



public class UnitTest {
 
 static String jsonText;
 static
 {
  jsonText = "";
  jsonText += "{";
  jsonText += "\"result\":\"OK\",";
  jsonText += "\"dataType\":\"LIST\",";
  jsonText += "\"userList\": [";
  jsonText += "   {\"username\":\"admin1\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin2\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin3\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin4\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin5\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin6\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin7\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin8\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin9\",\"password\":\"admin\"},";
  jsonText += "   {\"username\":\"admin0\",\"password\":\"admin\"}";
  jsonText += "   ]";
  jsonText += "}";
  
 }

 @Test
 public void test() {
  
  TestMapVo vo = null;
      
  try {
   vo = new ObjectMapper().readValue(jsonText, TestMapVo.class);
  } catch(Exception e){
   e.printStackTrace();
  } 
  
  System.out.println(vo);
 }
 
 @Data 
 public static class TestUserVo {
  
  String username;
  String password;
 }
 
 @Data
 public static class TestMapVo {
  
  String result;
  String dataType;
  List< TestUserVo> userList;
  
 }
}

결과
UnitTest.TestMapVo(result=OK, dataType=LIST, userList=[UnitTest.TestUserVo(username=admin1, password=admin), UnitTest.TestUserVo(username=admin2, password=admin), UnitTest.TestUserVo(username=admin3, password=admin), UnitTest.TestUserVo(username=admin4, password=admin), UnitTest.TestUserVo(username=admin5, password=admin), UnitTest.TestUserVo(username=admin6, password=admin), UnitTest.TestUserVo(username=admin7, password=admin), UnitTest.TestUserVo(username=admin8, password=admin), UnitTest.TestUserVo(username=admin9, password=admin), UnitTest.TestUserVo(username=admin0, password=admin)])

2013년 7월 19일 금요일

eclipse javascript 문법 에러

JQuery.js 파일들이나 JQuery.ui.js 파일 같은 것들을 폴더에 추가해두면 올바른 문법이 아니라면서 에러 표시가 나는 경우가 있다. Properties -> JavaScript -> Include Path -> Source에 가서 Excluded 에 Edit 을 눌러서 **/jquery*.* 등록한다.

2013년 7월 15일 월요일

spring-security url 예제


 

   


1 : 최상위 디렉토리는 권한상관없이 접속 가능 (루트 url)
2 : /login url 은 권한상관없이 접속 가능
3 : 아래 /** 는 user 와 admin 에게만 열어놓았으므로 권한이 없을때 이미지 표시가 안될 수가 있기 때문에 설정
    spring-servlet.xml 에서 설정한 디폴트서블릿보다 spring-security가 우선하기 때문에 따로 설정해야 한다.
4 : /admin/ 하위 디렉토리는 admin 권한에게만 오픈
5 : 그외 모든 디렉토리는 user와 admin에게 오픈한다.

3번 처럼 설정하기 싫다면

< security:http pattern="/resources/**" security="none" / >

이놈을 추가해라

mime-mapping 에 대해...

톰캣 같은 WAS 를 사용할때 정적인 리소스는 보통 브라우져에서 바로 접근이 가능하다.
보통은 다운로드 받기위해 접근 하는 경우가 많은데 
기본적으로 톰캣에 등록되있지 않은 확장자를 가진 파일에 접근할때 브라우져에서는 바로
해석을 하려고 한다. 그에 대한 설정이다

web.xml 에 넣으면 된다.

    js
    text/javascript
 

    css
    text/css

하지만 위에 대한 것은 기본적으로 등록이 되어있으므로 큰 의미는 없다

추가등록정보

  • 엑셀 97-2003 통합문서 (*.xls):application/vnd.ms-excel
  • 엑셀 통합문서 (2007 이상 *.xlsx):application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  • ZIP 파일 (*.zip):application/zip
  • TAR 파일 (*.tar):application/x-bzip
  • 워드 97-2003 문서 (*.doc):application/msword
  • 워드 문서 (2007 이상 *.docx):application/vnd.openxmlformats-officedocument.wordprocessingml.document
  • PDF 파일 (*.pdf):application/pdf
  • 한글 (*.hwp):application/x-hwp

2013년 7월 9일 화요일

ajax 인코딩 관련

기본적으로 ajax 는 utf-8 로 통신하게 되어있기 때문에 한글을 보내거나 받을 경우
그에 맞는 인코딩 설정을 해주어야 한다.
단, json 객체로 보내게 되면 굳이 인코딩을 할필요는 없다.
json 객체를 전송할때는

contentType : "application/json",

json 객체를 받을때는

dataType : "json",

을 명시해주면 된다. 하지만 일반 text일 경우에는
추가로 설정이 필요하다.

전송할때

contentType:"application/x-www-form-urlencoded; charset=UTF-8",

추가해주고 받을때는 

별도의 설정은 필요없다.

컨트롤러단에서 @ResponseBody 사용시
@RequestMapping(value = "/test/encoding5", method = RequestMethod.GET, produces="text/plain; charset=UTF-8")
설정해주고 @ResponseBody 미사용시

response.setCharacterEncoding("utf-8");

이 구문을 추가해준다.

@RequestBody, @ResponseBody

소스부터..
var data = {usr_id : "jack" , usr_pw : "jack", usr_name : "jack"};
     
$.ajax({
 url : "/contextPath/test/json",
 method : "POST", 
 contentType : "application/json",
 data : JSON.stringify(data),           
 success : function() {
  alert("전송 성공");
 },
 error : function(XHR, textStatus, errorThrown) {
       
     alert("Error: " + textStatus);      
     alert("Error: " + errorThrown);

 }
});
위와 같이 json 객체를 JSON.stringify 를 이용하여 문자열로 변환후 전송하려 할때 spring에서를 어떻게 받아야 할까?
@RequestMapping(value = "/test/json", method = RequestMethod.POST, consumes="application/json")
public void ajax_sendJSON(@RequestBody Map< String, Object> data, HttpServletResponse response) {  
    
 logger.info("ajax_sendJSON\n"+data);   
  
 response.setStatus(HttpServletResponse.SC_NO_CONTENT);
        //응답 없음
}
위와 같이 받으면 된다. 만약에 json객체가 [] 로 된 배열형태라면
List< Map< String,Object>> 로 받으면 된다.

json객체를 내보낼때도 마찬가지다.
@ResponseBody
@RequestMapping(value = "/test/json", method = RequestMethod.GET, produces="application/json")
public List< Map< String, Object>> ajax_receiveJSON() {
  
 logger.info("ajax_receiveJSON");
  
 List< Map< String, Object>> list = new ArrayList< Map< String, Object>>();
  
 for(int i=0 ; i<3 ; i++) {
   
  Map< String, Object> map = new HashMap< String, Object>();
   
  map.put("id", "id"+i);
  map.put("name", "name"+i);
   
  list.add(map);
 }
  
 return list;    
} 
위와 같이 리턴해주면 json형태로 손쉽게 받을 수 있다.
$.ajax({
 url : "/contextPath/test/json",
 method : "GET",  
 dataType:"JSON",
 success : function(json) {
       
   alert(JSON.stringify(json));       
       
 },
 error : function(XHR, textStatus, errorThrown) {
       
     alert("Error: " + textStatus);      
     alert("Error: " + errorThrown);

 }
});
받는 것도 위 처럼 받으면 된다
매우 좋은 기능이지만 그냥 사용 할 수는 없다. pom.xml 에서 다음을 추가하면 된다.

 org.codehaus.jackson
 jackson-core-asl
 1.9.12


 org.codehaus.jackson
 jackson-mapper-asl
 1.9.12

그리고 전에 글에 servlet-context.xml 에서 처럼 json 관련 
bean을 추가해주면 된다.

ibatis 설정 및 간단한 구현

기본적으로 몇가지 jar들이 필요한대 그 목록은 다음과 같다.

1. ojdbc14 (oracle jdbc 라이브러리)
2. spring-orm (ibatis와 jdbc를 연결해주는 역할)
3. ibatis-sqlmap (ibatis 의 sqlmap 라이브러리)

Spring Template Project 를 사용하여 프로젝트를 생성했다면
기본적으로 maven이 포함되어 있다. pom.xml 을 수정하여 쉽게 추가가 가능하다.

참고로 Spring Template Project로 프로젝트 생성시 추가 작업으로는
Spring Library 추가 Server Library 추가가 있다.



com.oracle
    ojdbc14
    10.2.0.4.0

	


    org.springframework
    spring-orm
    ${org.springframework-version}


    org.apache.ibatis
    ibatis-sqlmap
    2.3.4.726

귀찮으면 위에꺼 가져가 쓰면 된다.
이제 몇가지 설정 파일이 필요한데 위치는 WEB-INF 밑에 classes 안에 자유롭게 배치해도 된다. 
classes 가 classpath 임.

data-context.xml 이름은 상관없다.


	   					   
	
	
		
		
		
		
	
	
	
		  
	
	      
	
	


sqlMapConfig.xml 이건 이름이 정해져있다.


		

	
        
		

usr.xml 쿼리가 들어가는 파일


		

        
	
	
	
	
	
	
	
		INSERT INTO usr(usr_uid, usr_id, usr_pw, usr_name, usr_enabled, usr_authority, usr_regdate, usr_upddate)
					VALUES
					(usr_seq.nextval, #usr_id#, #usr_pw#, #usr_name#, '1', 'ROLE_USER', sysdate, sysdate)
		
	

dao 구현
@Repository
public class UsrDao extends SqlMapClientDaoSupport {
	
	@Resource(name = "sqlMapClient") 
    public void setSuperSqlMapClient(SqlMapClient sqlMapClient){
		
        super.setSqlMapClient(sqlMapClient);
        
    } 
	
	public UsrVo selectOne(String usr_uid){
		
		return (UsrVo)getSqlMapClientTemplate().queryForObject("usr.selectOne", usr_uid);
		
	}
	
	public List selectAll(){
		
		return getSqlMapClientTemplate().queryForList("usr.selectAll");
		
	}
	
	public void insert(UsrVo usrVo) {
		
		getSqlMapClientTemplate().insert("usr.insert", usrVo);
		
	}
}
service 구현
@Service
public class UsrService {

	@Autowired
	private UsrDao usrDao;
	
	private static final Logger logger = LoggerFactory.getLogger(UsrService.class);
	
	public void getUsrList(Model model) {
		
		model.addAttribute("usrList", usrDao.selectAll());
		
	}
	
	public boolean checkUsrIdDup(String usr_id) {
		
			
		return usrDao.checkUsrIdDup(usr_id);		
		
	}
	
	public UsrVo findUsrDetail(String usr_uid) {
		
		return usrDao.selectOne(usr_uid);		
		
	}
	
	public void submitUsrJoin(UsrVo usrVo) {
		
		usrDao.insert(usrVo);		
		
	}
}

jQuery RESTful 요청

회원 목록

위와 같은 form 이 있을 경우 jquery를 이용하여 RESTful방식으로 요청하는 법.

$(".detailHandler").click(function() {		
					
	var usr_uid = $(this).attr("usr_uid");
	$("input[name=_method]").val("get");
	$("form")
		.attr("method","post")
		.attr("action","/contextPath/admin/usr/"+usr_uid+") 
		.submit();
});	
form 내부에 _method 이름으로 된 요소가 있을 경우 web.xml에서 HiddenHttpMethodFilter 필터를 설정했다면 
위와 같은 방법으로 spring에서도 delete,get,put,post 메소드를 사용 할 수 있다.

onclick 속성을 사용하지 않을 경우에 버튼 태그안에 임의의 속성을 주어 데이터를 저장하게 할 수 있다.
또한 contextPath는  태그를 사용하여 대체 가능하다.

중복 ID 체크


회원가입시 중복체크 쿼리. 
입력된 아이디와 기존 회원 테이블을 조인시켜 
입력된 아이디에 대한 정보가 null인지 아닌지 구분함.
dao 클래스에서 java.lang.Boolean 형식으로 받을 수 있다.
이후,

@ResponseBody 
@RequestMapping(value = "/usr/join_check", method = RequestMethod.POST)
public boolean usr_join_check(@RequestParam String usr_id) {
		
	logger.info("usr_join_check ajax\nusr_id : ["+usr_id+"]");
		
	return usrService.checkUsrIdDup(usr_id);		
}	
컨트롤러 단에서 @ResponseBody 를 이용하여 리턴해준다.

2013년 7월 8일 월요일

url pattern / or /*

 
  appServlet
  /*.do
 
옛날은 위와 같이 매핑해주는 것이 보통이었다. 이렇게 하면 .do 로 끝나는 url 은 쉽게 스프링 서블릿을 
타게 할 수가 있다. 하지만 요즘은 RESTful 스타일을 선호한다. 
예를 들면 

http://localhost:8080/myapp/user/10 (user 수정,탈퇴,보기)
http://localhost:8080/myapp/user (user의 회원가입)
http://localhost:8080/myapp/admin/user/10 (user의 수정,삭제,보기,추가)

이런 식의 url 들이다.

이런 식의 url 을 구현하려면 
 
  appServlet
  /*
 
pattern을 이런식으로 변경해야 한다.그렇지만 static한 js,css,html 파일등의 요청과 .jsp 에 대한 요청도 스프링 서블릿이 작동하여
404 페이지를 보게 된다. 그래서 한가지 해결책으로 urlrewrite 를 사용했다. pattern 은 그대로 두고

  ^/world.jsp?country=([a-z]+)&city=([a-z]+)$
  /world/$1/$2

이와 같이 설정한다.
정규식을 사용하여 

/world.jsp?country=korea&city=seoul 을
/world/korea/seoul

로 변경되는 식이다.

이런 방법도 있지만 web.xml 과 spring 설정을 조작하여 해결하는 방법도 있다.
 
  appServlet
  /
 
이와 같이 설정하고

위 구문을 spring-servlet.xml 에 추가하면 된다.
필터를 통과못한 url 을 잡아서 톰캣 기본 서블릿에 넘겨주는 역할을 한다. pattern을 / 로 설정했기때문에
위 구문이 없으면 톰캣 기본 서블릿은 동작을 하지 못하기 때문이다.

한마디로 /* 는 모든 요청을 자기가 처리하겠다 이고, / 는 모든 요청은 받지만 처리 못하는 것은 넘기겠다 라는 뜻이다.

web.xml 설정




 
 
  contextConfigLocation
  
   /WEB-INF/spring/root-context.xml
   /WEB-INF/spring/appServlet/spring-security.xml
   
  
 
 
 
 
  org.springframework.web.context.ContextLoaderListener
 

 
 
  appServlet
  org.springframework.web.servlet.DispatcherServlet
  
   contextConfigLocation
   /WEB-INF/spring/appServlet/servlet-context.xml
  
  1
  
 
  appServlet
  /
 
 
  
 
     httpMethodFilter
     org.springframework.web.filter.HiddenHttpMethodFilter
 
     
 
     httpMethodFilter
     /*
 
 
 
 
  springSecurityFilterChain
  org.springframework.web.filter.DelegatingFilterProxy  
  
 
  springSecurityFilterChain
  /*
  
 
 
 
  springCharacterEncodingFilter
    org.springframework.web.filter.CharacterEncodingFilter
    
     encoding
     UTF-8
    
    
     forceEncoding
     true
    
 
 
 
  springCharacterEncodingFilter
     /*
 
 

spring-security.xml 기본 설정



 
  
 
 
  
 
  
    
 
  
  
  
        
  
    
  
  
  
  
     
  
     
 
 
 
  
  
  
  
 
 
 
 
 
 
  
 
   
 

위의 설정만으로도 기본적인 로그인 인증 기능은 사용 할 수 있다.
위의 설정에서는 로그인 페이지가 따로 커스텀 되어있는데 몇가지 유의할점이 있다면
암호 기억하기
위와 같이 url명과 아이디와 비밀번호의 name을 설정해야 한다.
또한 인증정보와 권한정보는 session에 저장되는데 

<%@ page session="true" %>

이걸 사용하지 않아도 session에 접근할 수 있다.

<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>



권한 등급 : 사용자
아이디 : []
권한 등급 : 관리자
아이디 : []
권한 등급 : 손님
아이디 : []
로그인 해주세요
위와 같이 사용한다. 또한 로그인 실패시 그에 관한 정보는

 
로그인 실패
위의 변수에 들어있다.

spring-servlet.xml 기본 설정


 


 
 
  
  
 
 

 
 
 
 
 
 
 
 
 
 
 
  
 
  
   
        
   
  
  

 
  
 
 
 
  
  
 



Validator 모듈



var Validator = {
		
		validateElement : function(formName, elName) {
			
			var el = $("form[name="+formName+"] *[name="+elName+"]");			
				
			var classGroup = el.attr("class");	
				
			if(classGroup) {
						
				var classes = classGroup.split(" ");
				var comment = el.attr("comment");
				
				for(var i=0 ; i< classes.length ; i++) {
					
					var className = classes[i];
					
					if(Validator[className]) {					
						
						var msg = Validator[className](el.val());
						
						if(msg) {	
							
							Validator._handleError(msg, comment);	
							return false;
								
						}							
					}
				}
			}
			
			return true;				
		},
		
		validateForm : function(formName) {
			
			var result = true;
			
			$("form[name="+formName+"] *").each(function() {				
				
				var classGroup = $(this).attr("class");	
				
				if(classGroup) {
						
					var classes = classGroup.split(" ");
					var comment = $(this).attr("comment");
					
					for(var i=0 ; i < classes.length ; i++) {
						
						var className = classes[i];
						
						if(Validator[className]) {					
							
							var msg = Validator[className]($(this).val());
							
							if(msg) {	
								
								Validator._handleError(msg, comment);
								result = false;
								return false;
								
							}
							 
						}
						
					}
				}
			});
			
			return result;			
		},
		
		_handleError : function(msg, comment) {
			
			alert(comment+" 는(은) "+msg);
			
		},
		
		trim : function(str)
		{
			var count = str.length;
			var len = count;
			var st = 0;

			while ((st < len) && (str.charAt(st) <= ' '))
			{
				st++;
			}
			while ((st < len) && (str.charAt(len - 1) <= ' '))
			{
				len--;
			}
			return ((st > 0) || (len < count)) ? str.substring(st, len) : str ;
		},
		
		"required" : function(value) {
			
			if(!Validator.trim(value).length) {
				
				return "필수 입력 사항입니다";
				
			}			
		},
		
		"alpha-lower" : function(value) {			
			
			var pattern = new RegExp("^[a-z]{6,18}$");			
			
			if(!pattern.exec(Validator.trim(value))) {
				
				return "소문자만 입력 가능합니다 (6~18 글자)";
				
			}			
		},
		
		"alpha-upper" : function(value) {
			
			var pattern = new RegExp("^[A-Z]{6,18}$");			
			
			if(!pattern.exec(Validator.trim(value))) {
				
				return "대문자만 입력 가능합니다 (6~18 글자)";
				
			}			
		},
		
		"email" : function(value) {
			
			var pattern = new RegExp("^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$");
			
			if(!pattern.exec(Validator.trim(value))) {
				
				return "이메일 형식에 맞지 않습니다";
			}
		},
		
		"mobile" : function(value) {
			
			var pattern = new RegExp("^01([0-9])-([0-9]{3,4})-([0-9]{4,4})$");
			
			if(!pattern.exec(Validator.trim(value))) {
				
				return "핸드폰 번호 형식에 맞지 않습니다";
			}				
		}
};






유효성 검사 모듈.. 
사용법은 유효성검사가 필요한 엘리먼트에 class 추가 후
script 에서 validateForm 또는 validateElement 호출하면 된다.