Spring 에서 @PathVariable 사용하여 값을 넘겨받을때 값에 . 가 포함되어 있으면 .포함하여 그뒤가 잘려서 들어온다.



@RequestMapping(value = "/user/email/{email}", method=RequestMethod.GET)


위와같은 형식일때 아래와 같이 바꿔주면 제대로 들어온다.



@RequestMapping(value = "user/email/{email:.+}", method = RequestMethod.GET)
public ModelAndView getUserByEmail(@PathVariable("email") String email) {


http://stackoverflow.com/questions/16332092/spring-mvc-pathvariable-with-dot-is-getting-truncated



YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



@Aspect annotation 기반 AOP 를 구현해서, 

1. 로그인 하지 않은 사용자가, 로그인페이지를 제외한 다른페이지에 접근했을 때나,

2. 접근권한이 없는(세션값) 사용자가, 접근할 수 없는 페이지에 접근했을 경우 등

  로깅이나, 공통기능을 사이트 전반에 적용시켜야 할 때 이용한다.

@Aspect 어노테이션은 AspectJ 5버전에 새롭게 추가된 어노테이션으로서, xml파일에 Advice 및 Pointcut 등의 설정을 하지않고도,

자동으로 Advice를 적용할 수 있다.


먼저 필요한 lib - 

aspectjweaver-1.6.5.jar


일단은  servlet xml 설정

<aop:aspectj-autoproxy />

<bean id="loginAspect" class="com.common.LoginAspect"/>


다음은 advice와 pointcut을 구현한 Aspect 클래스

@Aspect
public class LoginAspect {

	@Pointcut("execution(public * com.admin..*(..))")
	private void adminMethod(){}
	
	@Pointcut("execution(public * com.bbs..*(..))")
	private void bbsMethod(){}
	
	@Pointcut("execution(public * com.common..*(..))")
	private void commonMethod(){}
	
	@Pointcut("execution(public * com.onm.administrator..*(..))")
	private void onmAdministratorMethod(){}
	
	@Pointcut("execution(public * com.onm.membership..*(..))")
	private void onmMembershipMethod(){}
	
	@Pointcut("execution(public * com.onm.report..*(..))")
	private void onmReportMethod(){}
		
	private String[] access_url = {
			};
	
	@Around(value ="adminMethod() || bbsMethod()  || commonMethod()
                                || onmAdministratorMethod() || onmMembershipMethod() || onmReportMethod()")
	public Object trace(ProceedingJoinPoint joinPoint) throws Throwable{

System.out.println("#### LoginAspect 시작 ####");		
		HttpServletRequest request = null;
		HttpServletResponse response = null;
		for ( Object o : joinPoint.getArgs() ){ 
			if ( o instanceof HttpServletRequest ) {
				request = (HttpServletRequest)o;
			} 
			if ( o instanceof HttpServletResponse ) {
				response = (HttpServletResponse)o;
			} 
		}
		try{
			HttpSession session = request.getSession();

				String loginId = (String) session.getAttribute("loginId");
				String userEnterType = (String) session
						.getAttribute("UserEnterType");

				System.out.println("### Margo ==> loginId : " + loginId);
				if (loginId == null || "".equals(loginId)) {
					System.out.println("### Margo ==> in if loginId : "
							+ loginId);
					throw new RuntimeException("먼저 로그인을 하셔야 합니다.");
				}			
		}catch(Exception e){
			
			throw new RuntimeException("먼저 로그인을 하셔야 합니다.");

		}		
		Object result = joinPoint.proceed();
System.out.println("#### LoginAspect 끝 ####");		
		return result;
	}
}


1라인 에 적용한 @Aspect 어노테이션을 적용한 클래스는 advice구현메소드나, pointcut정의를 포함할 수 있게된다.


4 라인에 AspectJ의 pointcut 표현식으로 pointcut을 정의한다. 

execution(수식어패턴 리턴타입 클래스이름패턴 이름패턴(파라미터패턴))

수식어패턴: public, protected 등

각패턴은 * 를 이용하여 모든 값을 표현할 수 있고. '..' 를 이용하여 0개 이상이라는 의미를 표현가능

Controller라고 끝나는 이름의 클래스에만 적용할때는 - execution(public * com.bbs..*Controller.*(..))


5라인 처럼 pointcut 정의 메소드는 무조건 리턴타입이 void여야한다.

   이 메소드는 advice 관련 어노테이션인 @around의 값으로 사용될 메소드이다.


이제 trace메서드를 이용해서, 필요한 처리를 구현하면된다.

!!!!!!!!!!!!!!!! 여기서...중요한게 있다!!!!!!

27라인에서 joinPoint는 수행될 메소드인데, 보통 세션값 이용할때 request에서 빼내오는데

그 원래 해당메소드 파라메타로 HttpServletRequest가 지정되어 있지 않으면, request가 null이 되겠지..


끝으로 51,55라인에서 뿌린 Exception을 잡을 놈도 xml에 정의해야한다.

servlet xml에다가 추가해주자

<bean

class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

<property name="exceptionMappings">

<props>

<prop key="java.lang.Exception">

index

</prop>

</property>

</bean>

끝~~



YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



Velocity를 이용한 뷰 구현


  □ Velocity
    ■ 정의
      - 템플릿 엔진.
    ■ 특징
      - 템플릿 파일을 비교적 쉽게 작성 가능.
      - 자바 객체를 템플릿 파일에서 조작할 수 있기 때문에 뷰를 생성하는데 JSP 만큼 널리 사용.

  □ VelocityViewResolver와 VelocityConfigurer를 이용한 Velocity 연동
    ■ VelocityViewResolver
      - 스프링에서 Velocity를 연동할 때에 사용.
      - Velocity 템플릿 파일을 이용해서 뷰를 생성.
      - VelocityConfigurer를 사용해서 Velocity와 관련된 설정 정보를 입력.
      - VelocityConfigurer에서 설정한 경로에서 템플릿 파일을 읽어와 뷰를 생성.
      - prefix 프로퍼티와 suffix 프로퍼티는 뷰 이름으로부터 템플릿 파일의 경로를 생성할 때 사용.
    ■ VelocityConfigurer
      ○ resourceLoaderPath 프로퍼티
        - 템플릿 파일을 로딩할 경로를 입력.
      ○ velocityProperties 프로퍼티
        - Velocity 설정 정보를 지정.
    ■ 설정
 <bean id="velocityConfigurer"
    class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
    <property name="resourceLoaderPath" value="/WEB-INF/viewvm/" />
    <property name="velocityProperties">
        <props>
            <prop key="input.encoding">EUC-KR</prop>
            <prop key="output.encoding">EUC-KR</prop>
        </props>
    </property>
 </bean>

 <bean id="viewResolver"
    class="org.springframework.web.serlvet.view.velocity.VelocityResolver"
    p:suffix=".vm" p:contentType="text/html; charset="EUC-KR" />
         - 컨트롤러가 리턴한 뷰 이름이 "hello"라면 실제 사용되는 템플릿 파일은 "/WEB-INF/viewvm/hello.vm"이 됨.
    ■ 템플릿 파일
      - 템플릿 파일에서는 ModelAndView 객체에 저장된 모델 정보를 사용 가능.
      ○ 컨트롤러
 @Override
 protected ModelAndView handleRequestInternal(HttpServletRequest request, 
    HttpServletResponse response) throws Exception {

    ModelAndView mav = new ModelAndView();
    mav.setViewName("hello");
    mav.addObject("greeting", getGreeting());
    mav.addObject("me", me);

    return mav;
 }
      ○ 템플릿 파일
 <html>
 
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>인사</title>
 </head>
 
 <body>
    전 ${me}입니다. ${greeting}
 </body>

 </html>
    ■ Velocity Tools의 NumberTool과 DateTool 설정
      - Veloctiy는 템플릿 파일에서 유용하게 사용할 수 있는 기능인 다양한 툴을 제공하는 Velocity Tools를 제공.
      - 숫자와 날짜를 형식에 맞게 출력해주는 NumberTool과 DateTool을 제공.
      ○ VelocityViewResolver에서 제공하는 프로퍼티
        - dateToolAttribute : DateTool에 접근할 때 사용할 변수명을 지정.
        - numberToolAttribute : NumberTool에 접근할 때 사용할 변수명을 지정.
      ○ 설정
 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"
    p:suffix=".vm" p:contentType="text/html; charset=EUC-KR"
    p:dateToolAttribute="dateTool" p:numberToolAttribute="numberTool" />
      ○ 사용 예
 <html>

 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>#springMessage("title.currentTime")</title>
 </head>

 <body>
    #springMessage("lagel.currentTime")
    :$dateTool.format("yyyy-MM-dd HH:mm:ss". $time)
 </body>

 </html>
    ■ request 및 session 속성 템플릿 파일에서 사용하기
      ○ request 및 session 속성을 템플릿에 전달하기 위한 VelocityViewResolver 프로퍼티
        - exposeRequestAttributes : 프로퍼티 값을 true 로 지정. (기본값 : false)
        - exposeSessionAttributes : 프로퍼티 값을 true 로 지정. (기본값 : false)
      ○ 설정
 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"
    p:suffix=".vm" p:contentType="text/html; charset=EUC-KR"
    p:dateToolAttribute="dateTool" p:numberToolAttribute="numberTool"
    p:exposeRequestAttributes="true" p:exposeSessionAttributes="true" />

  □ 스프링이 제공하는 Velocity 매크로
    ■ 설정
      - 입력 폼을 위한 HTML 태그와 에러 메시지 등을 생성할 때 매크로를 유용하게 사용.
      - exposeSpringMacroHelpers 프로퍼티의 값을 true로 지정. (스프링 2.5의 경우는 기본 값이 true)
 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"
    ...
    p:exposeRequestAttribute="true" p:exposeSessionAttributes="true"
    p:exposeSpringMacroHelpers="true" />
    ■ 메시지 출력을 위한 #springMessage 매크로와 #spring MessageText 매크로
      - 스프링의 MessageSource로부터 메시지를 읽어와 출력할 때 사용.
      ○ 정의
        ● springMessage(code)
          - 코드에 해당하는 메시지를 출력.
          - 메시지가 존재하지 않을 경우 예외가 발생.
        ● springMessageText(code text)
          - 코드에 해당하는 메시지를 출력.
          - 메시지가 존재하지 않을 경우 text를 출력.
      ○ 사용 예
 <p>
    <label for="id">#springMessage("login.form.id")</label>
    #springFormInput("login.id" "")
    #springShowErrors("<br/>" "")
    #springMessageText("login.form.id.help". "도움말이 없습니다.")
 </p>
         - #springMessage 매크로는 login.form.id 메시지 코드에 해당하는 메시지를 출력.
         - #springMessageText 매크로는 login.form.id.help 메시지 코드에 해당하는 메시지를 출력. 해당 메시지가 존재하지 않으면
           "도움말이 없습니다"를 대신 출력.
    ■ 커맨드 객체 연동을 위한 #springBind 매크로와 #springBindEscaped 매크로
      ○ #springBind 매크로
        - 커맨드 객체와 관련된 BindStatus 정보를 설정.
        - 커맨드 객체와 관련된 경로를 설정하고 관련 BindStatus 정보는 status 변수를 통햇 접근 가능.
      ○ #springBind 매크로 사용 예
 <p>
    <label for="id">#springMessage("login.form.id")</label>
    #springBind("login.id")
    <input type="text" name="${status.expression}" id="${status.expression}"
        value="$!status.value" />
    #springShowErrors("<br/>" "")
 </p>
         - login 커맨드 객체의 id 프로퍼티를 #springBind 매크로를 사용해서 바인딩.
         - id 프로퍼티와 관련된 BindStatus 객체는 status 변수를 통해서 접근 가능.
         - status.expression은 "id"를 출력.
         - status.value는 login 커맨드 객체의 id 프로퍼티의 값을 출력.
      ○ status 변수
        ● status.expression
          - BindStatus 객체와 연관된 커맨드 객체의 경로를 출력.
        ● status.value
          - 커맨드 객체와 관련된 값을 출력.
          - defaultHtmlEscape 컨텍스트 파라미터의 값에 따라 HTML 특수 문자 처리 여부를 결정.
      ○ #springBindEscaped 매크로
        - HTML 특수 문자 처리 여부를 직접 명시.
 <p>
    <label for="id">#springMessage("login.form.id")</label>
    #springBindEscaped("login.id" false)
    <input type="text" name="${status.expression}" id="${status.expression}"
        value="$!status.value" />
    #springShowErrors("<br/>" "")
 </p>
         - springBindEscaped 매크로의 두 번째 파라미터는 특수 문자를 치환할지의 여부를 설정. 
         - false 이므로 특수 문자를 변환하지 않도록 설정.
    ■ <input> 태그를 위한 매크로
      ○ #springFormInput 매크로
        - 커맨드 객체와 관련된 <input> 태그를 손쉽게 출력.
      ○ #springFormInput 매크로 정의
 #springFormInput($path $attributes)
        ● $path
          - <input> 태그를 생성할 때 사용할 커맨드 객체의 경로.
        ● $attributes
          - <input> 태그에 삽입할 HTML 속성 설정.
      ○ 사용 예
 <label for="name">이름</label>
 #springFormInput("memberInfo.name" "class="input")
 #springShowErrors("<br/>" "")
         - #springFormInput 매크로는 memberInfo 커맨드 객체의 name 프로퍼티와 관련된 <input> 태그를 생성.
      ○ hidden 타입의 <input> 태그나 password 타입의 <input> 태그 생성
        ● #springFormPasswordInput($path $attributes)
          - password 타입의 <input> 태그를 생성.
          - 사용 방법은 #springFormInput 매크로와 동일.
        ● #springFormHiddenInput($path $attributes)
          - hidden 타입의 <input> 태그를 생성.
          - 사용 방법은 #springFormInput 매크로와 동일.
      ○ #springBind 매크로를 사용하여 커맨드 객체와 관련된 BindStatus 정보 설정
        - 내부적으로 #springBind 매크로를 사용해서 커맨드 객체와 관련된 BindStatus 정보에 접근.
 #macro(springFormInput $path $attributes)
    #springBind($path)
    <input type="text" id="${status.expression}" name="${status.expression}"
        value="$!status.value" ${attributes}#springCloseTag()
 #end
         - 폼과 관련된 모든 매크로는 위 코드와 같이  #springBind 매크로를 이용해서 커맨드 객체와 관련된 BindStatus 정보를 설정.
    ■ <select> 태그를 위한 매크로
      ○ <select> 태그와 관련한 매크로
        ● #springFormSingleSelect($path $options $attributes)
          - 한 개의 옵션을 선택할 수 있는 <select> 태그를 생성.
          - $options : <option> 태그를 생성할 때 사용할 Map 객체를 지정.
          - $attributes : HTML 태그의 속성 설정을 입력.
        ● #springFormMultiSelect($path $options $attributes)
          - 다중 선택이 가능한 <select> 태그를 생성.
          - $options : <option> 태그를 생성할 때 사용할 Map 객체를 지정.
          - $attributes : HTML 태그의 속성 설정을 입력.
      ○ 사용 예
 Map<String, String> jobCodes = new HashMap<String, String>();
 jobCodes.put("1", "개발자");
 jobCodes.put("2", "UI 개발자");
 jobCodes.put("3", "웹 디자이너");
 jobCodes.put("4", "기획자");

 ModelAndView mav = new ModelAndView();
 mav.addObject("jobCodes", jobCodes);
 <label for="jobCode">직업</label>
 #spriingFormSingleSelect("memberInfo.jobCode" $jobCodes "")
 #springShowErrors("<br/>" "")
      ○ #springFormSingleSelect 매크로
        - $options에 전달받은 Map 객체의 키를 <option> 태그의 value 속성의 값으로 사용.
        - Map 객체의 값을 <option> 태그의 텍스트로 사용.
      ○ List 객체를 이용해서 <option> 태그 생성
        - #springBind 매크로를 이용해서 직접 <select> 태그와 <option> 태그를 생성.
 #springBind("login.loginType")
 <select id="${status.expression}" name="${status.expression}">
    #foreach($option in $loginTypes)
        <option value="${option}"
        #if("$!status.value" == "$option")
            selected="selected"
        #end
        $option</option>
    #end
 </select>
    ■ checkbox 타입 <input> 태그를 위한 매크로
      ○ checkbox 타입을 위한 <input> 태그 생성 매크로
        ● #springFormCheckboxes($path $options $separator $attributes)
          - $options : checkbox 타입의 <input> 태그를 생성할 때 사용할 Map 객체를 전달 받음.
          - $separator : 각각의 선택 항목을 구분하기 위한 구분자를 입력
      ○ 사용 예
 Map<String, String> favoritesOsNames = new HashMap<String, String>();
 favoritesOsNames.put("WIN2000", "윈도우2000");
 favoritesOsNames.put("WINXP", "윈도우XP");
 favoritesOsNames.put("VISTA", "비스타");
 favoritesOsNames.put("UBUNTU", "우분투");
 favoritesOsNames.put("MAC", "맥");
 
 referenceData.put("favoritesOsNames", favoritesOsNames);
 <label for="favorites">선호 OS</label>
 #springFormCheckboxes("memberInfo.favorites" $favoritesOsNames " | " "")
 #springShowErrors("<br/>" "")
    ■ radio 타입 <input> 태그를 위한 매크로
      ○ radio 타입의 <input> 태그 생성 매크로
        ● #springFormRadioButtons($path $options $separator $attributes)
          - 사용 방법은 #springFormCheckboxes 매크로와 동일.
      ○ 사용 예
 <p>
    <label for="tool">주로 사용하는 개발툴</label>
    #springFormRadioButtons("memberInfo.tool" $tools "" "")
 </p>
    ■ <textarea> 태그를 위한 매크로
      ○ <textarea> 태그 생성 매크로
        ● #springFormTextarea($path $attributes)
      ○ 사용 예
 <p>
    <label for="etc">기타</label>
    #springFormTextarea("memberInfo.etc" "cols='20' rows='3'")
 </p>
    ■ 에러 메시지 출력을 위한 #springShowErrors 매크로
      ○ #springShowErrors 매크로
        - 커맨드 객체와 관련된 에러 메시지를 출력할 때 사용.
      ○ 정의
        ● #springShowErrors($separator $class/style)
          - #springBind/#springBindEscaped 매크로로 바인딩 도니 BindStatus와 관련된 에러 메시지들을 출력.
          - $separator : 각 에러 메시지를 구분할 때 출력되는 값.
          - $class/style : 각 에러 메시지를 위한 CSS 관련 정보를 설정할 때 사용.
      ○ 컨트롤러에서 생성한 에러 메시지 출력
 // 커맨드 객체와 관련된 에러 정보를 추가.
 errors.reject("invalidldOrPassword", new Object[] {loginCommand.getId()}, null);
 // 에러 메시지 출력
 #springBind("login")
 #springShowErrors("<br>" "")
      ○ 특정 프로퍼티와 관련된 에러 메시지 출력
        - #springBind 매크로를 사용하거나 폼 관련 매크로를 사용.
 <p>
    <label for="id">#springMessage("login.form.id")</label>
    #springBind("login.id")
    <input type="text" name="${status.expression}" id="${status.expression}"
        value="$!status.value" />
    #springShowErrors("<br/>" "")
 </p>
 <p>
    <label for="password">#springMessage("login.form.password")</label>
    #springFormPasswordInput("login.password" "")
    #springShowErrors("<br/>" "")
 </p>
        - 폼 입력을 위한 매크로는 #springBind 매크로를 사용하기 때문에 폼 관련 매크로 사용한 뒤에는 #springShowErrors 매크로를
          사용해서 입력 폼과 관련된 에러 메시지를 출력.
      ○ #springShowErros 매크로의 두 번째 파라미터($class/style) 값 처리
        ● 파라미터 값이 ""로 지정
          - 에러 메시지를 <b> 태그를 사용해서 강조.
 <b> 잘못된 ID나 암호를 입력하셨습니다. 입력한 ID는 124입니다. </b>
        ● CSS 관련 속성을 사용하여 에러 메시지 출력
          - CSS 관련 정보 입력.
          - ""이 아닐 경우 : <span> 태그를 이용해서 에러 메시지를 감싼다.
          - ":"로 시작할 경우 : <span>의 style 속성의 값을 두 번째 파라미터의 값으로 설정.
 #springFormInput("memberInfo.userId" "")
 #springShowErrors("<br/>" ":font-size:15pt;")
 // 에러 메시지는 두 번째 파라미터로 전달받은 값을 style 속성의 값으로 사용.
 <span style=":font-size:15pt; ">필수 항목입니다. </span>
          - ":"로 시작하지 않을 경우 : class 속성의 값을 두 번째 파라미터 값으로 설정.
 #springFormInput("memberInfo.name" "class='input'")
 #springShowErrors("<br/>" "error")
 // 에러 메시지는 두 번째 파라미터로 전달받은 값을 class 속성의 값으로 사용.
 <span class="error">필수 항목입니다. </span>
         
  □ VelocityLayoutViewResolver를 이용한 Velocity 레이아웃 템플릿 사용
    ■ VelocityLayoutServlet
      - Velocity Tools가 제공.
      - Velocity에서 동일한 레이아웃을 여러 페이지에 적용할 수 있도록 도와줌. (Tiles와 비슷)
      - 여러 Veloticy 템플릿 파일에서 레이아웃 처리를 위해 중복된 HTML 코드를 작성하지 않아도 됨.
    ■ VelocityLayoutViewResolver
      - VelocityLayoutServlet과 동일하게 하나의 레이아웃을 여러 템플릿 파일에 적용.
    ■ VelocityLayoutViewResolver를 이용한 레이아웃 템플릿 적용
      - 결과 화면을 생성해주는 View 클래스로 VelocityLayoutView 객체를 생성.
      - VelocityLayoutViewResolver를 ViewResolver로 사용하도록 설정.
      ○ 설정
 <bean id="velocityConfigurer"
    class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
    <property name="resourceLoaderPath" value="/WEB-INF/viewvmlayout/" />
    <property name="velocityProperties">
        ...
    </property>
 </bean>

 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver"
    p:layoutUrl="/template/layout.vm" 
    p:suffix=".vm" p:contentType="text/html; charset=EUC-KR" ... />
         - prefix나 suffix 등의 프로퍼티는 VelocityViewResolver와 동일.
         - layoutUrl 프로퍼티를 사용하여 레이아웃으로 사용될 Velocity 템플릿 파일을 지정.
      ○ 응답화면 생성
        - VelocityViewLayout 객체는 2단계 과정을 거쳐서 응답 화면 생성.
 1) 컨트롤러의 처리 결과를 보여 줄 템플릿 파일을 찾은 뒤, 템플릿 파일로부터 결과 화면을 생성. 이때 생성된 
     결과 화면을 screen_content 변수에 저장.
 2) layoutUrl 프로퍼티로 지정한 레이아웃 템플릿 파일을 파싱하여 결과 화면을 생성. 이때 레이아웃 템플릿 파일
     은 $screen_content 변수를 사용하여 1단계에서 생성한 결과 화면을 내부에 포함 시킴.

        - /greeting/hello.htm 요청을 처리한 뒤 그 결과를 /hello.vm 템플릿 파일을 통해서 출력할 경우 /hello.vm을 통해서 생성된 결과

          화면이 레이아웃으로 지정한 /template/layout.vm 템플릿 파일의 특정 영역에 삽입.
          (layout.vm이 포함하고 있는 레이아웃이 hello.vm의 출력 결과에 적용되는 것.)

    ■ Velocity 레이아웃 파일 생성
      ○ 레이아웃으로 사용될 Velocity 템플릿 파일과 일반 Velocity 템플릿 파일의 차이점
        - 처리 결과로 생성된 결과를 포함하기 위한 $screen_content 변수 사용.
 <html>
 
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>제목</title>
 </head>

 <body>
    #parse("/template/header.vm")
    <hr />
    $screen_content
    <hr />
    #parse("/template/footer.vm")
 </body>

 </html>
      ○ 특징
        - 다수의 요청에 대해서 동일한 레이아웃 템플릿 파일이 사용되기 때문에 모든 페이지가 동일한 레이아웃을 갖게 됨.
        - 각 요청의 처리 결과를 생성하는 Velocity 템플릿의 출력 결과는 $screen_content 변수에 위치.
        - Velocity의 #parse나 #include를 사용하면 다수의 레이아웃 템플릿 파일에서 사용되는 부분을 재사용.
      ○ 사용 예
        - 왼쪽 메뉴가 추가된 레이아웃.
 <html>

 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>제목</title>
 </head>

 <body>
    #parse("/template/header.vm")
    <hr />
    <div id="left">#parse("/common/left_menu.vm")</div>
    <div id="content" class="content_side">
        $screen_content
    </div>
    <hr />
    #parse("/templat/footer.vm")
 </body>
 </html>
         - #parse를 사용하여 head.vm이나 top_logo.vm 등의 내용을 재사용.
    ■ 레이아웃 커스터마이징
      ○ 예
        - /a.htm 요청과 /b.htm 요청을 처리 결과를 각각 a.vm과 b.vm을 통해 생성.
        - 공통으로 사용되는 레이아웃으로 layout.vm을 사용.
        - layout.vm은 a.vm이나 b.vm에서 지정한 변수 사용 가능.
 /* a.vm 파일의 내용 */
 #set($htmlTitleTail = '카페')
 ...
 #foreach ($content in $contents)
 ...$content.title ...
 #end
 ...
 /* layout.vm의 내용 */
 <html>
    <title>세상을 즐겁게 변화시키는 DAUM
    #if($htmlTitleTail) : $htmlTitleTail #end</title>
    ...
    <body>
    ${screen_content}
    #parse("/common/bottom.vm")
 </html>   
         - a.vm은 $htmlTitleTail 변수의 값을 지정.
         - layout.vm은 $htmlTitleTail 변수가 존재할 경우 해당 값을 출력하는 코드를 갖고 있음.
         - a.vm이 먼저 파싱된 뒤 layout.vm이 파싱되고, 추가적으로 a.vm에서 정의한 변수를 layout.vm에서 사용할 수 있음.
        ● /a.htm의 요청 결과 HTML 코드
 <html>
    <title>세상을 즐겁게 변화시키는 DAUM : 카페 </title>
    ...
    <body>
    ...
    글제목1
    글제목2
    ...
      ○ 레이아웃의 일부 변경
        - 변수를 전달할 수 있는 기능을 사용하면 레이아웃의 일부 변경 가능.
        ● 레이아웃 템플릿 파일
 #if($headVm)
    #parse($headVm)
 #else
    #parse("/templat/head.vm")
 #end
 ...
         - $headVm 변수가 존재할 경우 변수에 해당하는 vm 파일을 파싱해서 포함.
         - $headVm 변수가 존재하지 않을 경우 "/template/head.vm"을 파싱해서 포함.
        ● /a.htm의 결과가 /template/head_wide.vm을 헤드로 사용
          - /a.htm의 처리 결과를 생성하는 a.vm 파일에서 $headVm 변수 값으로 '/template/head_wide.vm'을 지정.
 #set ($headVm = '/template/head_wide.vm')
 ...
      ○ 레이아웃 템플릿 자체 변경
        - $layout 변수에 레이아웃으로 사용할 템플릿 파일을 지정.
 #set($layout = "/template/layout_index.vm")
 ...

      ○ VelocityLayoutViewResolver 특징
        - Tiles나 Sitemesh 처럼 설정 파일을 사용하여 레이아웃을 관리할 수는 없지만, 레이아웃을 관리하는데 있어서 필요한 부분적인

           레이아웃의 변경, 전체 레이아웃의 변경과 같은 기능을 사용.

출처 : 
http://blog.naver.com/PostView.nhn?blogId=chocolleto&logNo=30087127522 

YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. 꼬렙 2012.03.06 11:48 신고  댓글주소  수정/삭제  댓글쓰기

    이건 또 뭐고 -_-;;;
    김주임 희안한거 마이 하는구나



출처 : http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&sselect=wr_subject%7Cwr_content&stext=sencha&soperator=0&page=1&wr_id=36789

본격적으로 Sencha Touch 개발을 준비해 보자.

Sencha Touch 준비과정
Sencha Touch 준비과정은 간단하다. Sencha Touch가 제공해 주는 sencha-touch.js 파일과 sencha-touch.css 파일을 포함시키면 준비는 끝이다(sencha-touch-debug.js와 sencha-touch-debug.css는 개발 용도로 소스코드가 분석하기 편하게 돼 있는 파일이다).

먼저 http://www.sencha.com/products/touch/download. php에서 다운로드하자.


<화면 1> Sencha.com에서 다운받을 수 있다.

받은 sencha-touch-1.0.1a.zip(2011. 1. 2 기준) 파일을 풀어보면 <화면 2>와 같은 많은 파일들을 볼 수 있다.


<화면 2> Sencha Touch 구성파일들

Sencha Touch의 경쟁 제품인 jQuery Mobile(아직 알파 버전)이 “Hello World” 예제조차 제공해 주지 않는 것에 비하면 Sencha Touch의 구성은 종합선물세트처럼 반갑게 느껴진다. 자주 사용하게 될 폴더는 API 문서 폴더(/docs)와 예제 폴더(/examples)를 주로 사용하게 된다. 먼저 index.html 파일을 만들어 sencha-touch-debug.js 파일과 sencha-touch-debug.css 파일을 포함시키자. 이로써 간단하게 Sencha Touch 개발 준비가 끝난다.

<리스트 1> 가장 먼저 실행될 index.html 파일

1 <html>
2 <head>
3 <title>Sencha</title>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <link rel="stylesheet" href="sencha-touch-debug.css" type="text/css">
7 <script type="text/javascript" src="sencha-touch-debug.js"></script>
8 </head>
9 <body>
10 </body>
11 </html>

Sencha Touch 기본 설정
실제 코드는 자바스크립트로 작성하게 되며, 자바스크립트를 index.html 안에 기술해도 상관 없으나 자바스크립트 내용이 많아 별도 index.js 파일을 만들어 기술하기로 한다.

<리스트 2> 기본 설정을 위한 index.js 파일 추가

1 <html>
2 <head>
3 <title>Sencha</title>
4 <meta name="viewport" content="width=device-width, initial-scale=1.0">
5 <link rel="stylesheet" href="sencha-touch-debug.css" type="text/css">
6 <script type="text/javascript" src="sencha-touch-debug.js"></script>
7 <script type="text/javascript" src="intex.js"></script>
8 </head>

index.js를 추가하고 viewport 메타 태그를 이용해 화면 크기를 조절한다(viewport 메타 태그는 모바일 웹킷 기반 브라우저(Android,iPhone)의 화면 크기를 기기의 해상도와 상관없이 설정할 수 있다).

<리스트 3> Ext.setup을 통해 기본 설정

index.js
1 Ext.setup({
2   tableStartupScreen:'tstart.png',   // 타블렛 시작 이미지
3   phoneStartupScreen:'pstart.png',   // 폰 시작 이미지
4   icon:'icon.png',   // 바탕화면 아이콘
5   glossOnIcon:false
6 });

Sencha Touch의 Ext class의 setup이라는 메소드를 통해 다양한 기본 설정들을 할 수 있다. 2번 라인부터 3번 라인은 페이지의 로고와 같은 이미지를 설정하는 파라미터다. 4번 라인은 홈 화면에 추가할 때 사용될 이미지를 설정하는 것이다. 5번 라인은 4번 라인의 이미지에 gloss on icon 효과를 줄 지를 설정하는 파라미터다.

gloss on icon 효과란 애플에서 제공하는 기본적인 아이콘 효과를 얘기한다. <화면 3>의 아이콘을 보면 그 차이를 확실히 알 수 있다. 제공하려는 아이콘에 gloss on icon 효과를 적용하고 싶지 않을 때 우리는 false로 효과를 제어하면 된다.


<화면 3> gloss on icon 효과 적용 전과 후

<리스트 4> “Hello Sencha” 출력

index.js1 Ext.setup({
2    tabletStartupScreen: 'tstart.png',
3    phoneStartupScreen: 'pstart.png',
4    icon: 'icon.png',
5    glossOnIcon: false,
6    onReady: function() {
7       new Ext.Panel({
8          fullscreen: true,
9          layout: 'fit',
10         items: [
11            {html: "Hello Sencha"}
12         ]
13      });
14    }
15 });

기본적인 설정이 끝났으면 “Hello Sencha”를 출력해 보자.

6번 라인의 onReady는 DOM이 준비된 상태일 때 실행되는 함수를 정의할 수 있다. 우리가 호출한 페이지가 시작될 때 가장 먼저 실행되는 곳이라 보면 된다.

Sencha Touch는 HTML 개발 방식과 다르게 컴포넌트 기반으로 구성된다. 자바의 GWT처럼 Panel을 붙이고 그 위에 또 Panel을 붙여 구성한다고 보면 된다. 가장 기본적인 Ext.Panel을 사용했다. fullscreen을 이용해 width와 height를 100%로 설정한다. layout은 auto, card, fit, hbox, vbox 등이 있는데 여기서는 fit으로 처리했다. 이렇게 layout을 설정하고 나면 item으로 내용을 채워줘야 한다. 여기서는 layout:fit과 같이 하나의 item으로 html 속성을 이용해 “Hello Sencha”를 출력한다.


<화면 4> "Hello Sencha" 출력화면

Hello Sencha가 무사히 나왔다면 이제 복합적인 웹앱을 만들어 보자. 예제에서는 카페 웹앱을 만들어 보겠다. 리스트에는 음료와 케이크가 나오고 맵에는 해당 카페의 위치가 표시되게끔 하려고 한다.

먼저 상단에 탭 바를 하나 만든다. 물론 탭 바도 Sencha Touch가 제공하는 Ext.TabPanel을 사용하므로 별도 디자인을 할 필요는 없다.

1) ui : ui에 대한 일종의 테마를 선택하는 속성이다. 기본은 ‘dark’로 설정돼 있다. 
2) tabBarDock : 탭 바를 어디에 두는지 정하는 속성이다. ‘top’ 또는 ‘bottom’으로 탭 바의 위치를 결정한다.
3) cardSwitchAnimation : 페이지 전환 시 줄 애니메이션을 정하는 속성이다. 기본적으로 Ext.anims에서 사용할 수 있는 값을 설정할 수 있다. default로 ‘slide’가 돼 있고, ‘false’ 또는 ‘fade’, ‘slide’, ‘flip’, ‘cube’, ‘pop’, ‘wipe’ 등이 있다.

New Ext.TabPanel을 통해 생성하고 탭 패널의 속성을 <리스트 5>와 같이 설정한다.

<리스트 5> 탭 바 추가

index.js 1 Ext.setup({
 2  tabletStartupScreen: 'tstart.png',
 3  phoneStartupScreen: 'pstart.png',
 4  icon: 'icon.png',
 5  glossOnIcon: false,
 6  onReady: function() {
 7     var panel;
 8                    
 9     panel = new Ext.TabPanel({
10         fullscreen: true,
11         cardSwitchAnimation: 'slide',
12         ui: 'dark',
13         items: [
14            {title: "List"}, {title: "Map"}
15         ]
16      });
17   }
18 });


<화면 5> 바에 List, Map 메뉴 생성

기본적인 설정을 해줬으면 ‘List’, ‘Map’ Panel의 내용을 작성한다. 각 Panel을 담을 변수 listPanel과 mapPanel를 선언하자(<리스트 6>의 7번 라인).

<리스트 6> 탭 바 메뉴 선택 시 출력될 패널 추가

index.js1 Ext.setup({
2    tabletStartupScreen: 'tstart.png',
3    phoneStartupScreen: 'pstart.png',
4    icon: 'icon.png',
5    glossOnIcon: false,
6    onReady: function() {
7       var panel, listPanel, mapPanel;
8                     
9       listPanel = {
10          title: "List",
11          html: "리스트 페이지"
12       };
13                     
14       mapPanel = {
15          title: "Map",
16          html: "맵 페이지"
17       };
18                  
19       panel = new Ext.TabPanel({
20          fullscreen: true,
21          cardSwitchAnimation: 'slide',
22          ui: 'dark',
23          items: [
24             listPanel, mapPanel
25          ]
26       });
27    }
28 });

Sencha Touch에서는 대부분의 값을 JSON 형식으로 전달한다. 아주 간단하면서 간편한 문법이나 처음 접하는 분에게는 낯설게 느껴져 Sencha Touch 전체가 어려워 보이게 마련이다. JSON (JavaScript Object Notation)은 자료를 주고 받을 때 표현하는 방법이다(이러한 것으로 XML도 있으나 어렵고 사용법이 불편해 간단한 데이터를 표현하는 곳에는 적합하지 않다). JSON은 Key와 Value 구조를 갖고 있다. 다음 예제를 보면 쉽게 이해할 수 있을 것이다.

var car_name = ‘SONATA’;
var car_color = [‘red’, ‘blue’];
var car_price_red = 100;
var car_price_blue = 200;

차량 색상에 따른 가격 차이를 표현하는데 쉽지 않다. JSON의 표현방식으로 표현하면 다음과 같다.

var car = {name : ‘SONATA’ , color : { red : 100, blue : 200 } }

이와 같이 JSON으로 표현하면 복잡한 데이터도 간단히 표현할 수 있다. 데이터 호출은 다음과 같이 할 수 있다.

alert( car.name );   // ‘SOANATA’ 출력
alert( car.color.red );   // 100 출력


<화면 6> 탭 바 메뉴 선택시 화면 출력

리스트는 Ext.NestedList를 사용한다. 리스트로 사용할 데이터를 따로 자바스크립트 파일로 만들어 분리해 놓고 <리스트 7>과 같이 내용을 채워 넣는다.

 <리스트 7> Ext.NestedList에 추가할 list.js

list.js1 var data = {
2    text: '카페',
3    items: [{
4       text: '음료',
5       items: [{
6          text: '물',
7          items: [{
8             text: '탄산수',
9             leaf: true
10          },{
11             text: '생수',
12             leaf: true
13          }]
14       },{
15          text: '아메리카노',
16          leaf: true
17       },{
18          text: '에스프레소',
19          leaf: true
20       },{
21          text: '카라멜 마끼아또',
22          leaf: true
23       }]
24    },{
25       text: '케이크',
26       items: [{
27          text: '치즈케이크',
28          leaf: true
29       },{
30          text: '생크림케이크',
31          leaf: true
32       }]
33    }]
34 };
35  
36 Ext.regModel('ListItem', {
37    fields: [{name: 'text', type: 'string'}]
38 });
39  
40 var store = new Ext.data.TreeStore({
41    model: 'ListItem',
42    root: data,
43    proxy: {
44       type: 'ajax',
45       reader: {
46          type: 'tree',
47          root: 'items'
48       }
49    }
50 });

data 변수에 리스트에 추가할 메뉴들을 담았다. leaf:true의 의미는 더 이상의 자식 노드가 없다는 것이다. 이렇게 담았다고 바로 사용할 수 있는 것은 아니다. Ext.regModel을 이용해 정의해야만 Ext.data.TreeStore에서 가져다 사용할 수 있다.

우리가 데이터를 사용할 때 사용할 수 있는 메소드 중 여기서는 TreeStore를 사용했다. model 속성에 Ext.regModel에서 정의한 데이터 패키지를 지정해 주고 root 속성에 data 변수를 대입한다.

proxy 속성에서는 type을 ajax로 정하고 reader 속성의 type을 tree 구조로, root는 items로 정했다. 만약 json으로 한다면 proxy에 url 속성을 추가하고 reader.type:을 json으로 설정한 후 root를 해당 json에 맞게 설정해 사용할 수 있다.

 <리스트 8> json 사용 예

var myStore = new Ext.data.Store({
   model: 'User',
   proxy: {
      type: 'ajax',
      url: '/users.json',
      reader: {
         type: 'json',
         root: 'users'
      }
   }
   autoLoad: true
})

이렇게 만든 리스트(list.js)를 index.html에 추가한다.

 <리스트 9> list.js 파일을 index.html 파일에 추가

1 <!DOCTYPE html>  
2 <html>
3 <head>
4 <title>Sencha</title>
5 <meta charset="utf-8">
6 <meta name="viewport"content="width=device-width, initial-scale=1.0">
7 <link rel="stylesheet"href="sencha-touch-debug.css"type= "text/css">
8 <script type="text/javascript"src="sencha-touch-debug.js"></script>
9 <script type="text/javascript"src="index.js"></script>
10 <script type="text/javascript"src="list.js"></script>
11 </head>
12 <body>
13 </body>
14 </html>

list.js를 index.html에 포함시켰으면 index.js 파일에 추가하는 작업을 시작하자.

 <리스트 10> list.js에서 정의한 store를 listPanel에 추가

1 Ext.setup({
2    tabletStartupScreen: 'tstart.png',
3    phoneStartupScreen: 'pstart.png',
4    icon: 'icon.png',
5    glossOnIcon: false,
6    onReady: function() {
7       var panel, listPanel, mapPanel;
8                      
9       listPanel = new Ext.NestedList({
10          fullscreen: true,
11          title: 'List',
12          displayField: 'text',
13          store: store
14       });
15                      
16       mapPanel = {
17          title: "Map",
18          html: "맵 페이지"
19       };
20                      
21       panel = new Ext.TabPanel({
22          fullscreen: true,
23          cardSwitchAnimation: 'slide',
24          ui: 'dark',
25          items: [
26             listPanel, mapPanel
27          ]
28       });
29    }
30 });

listPanel을 Ext.NestedList 패널로 변경하고 몇 가지 속성을 채운다. store 속성에는 앞서 list.js에서 작성한 store를 추가한다.


<화면 7> Ext.NestedList 적용 모습

Ext.NestedList를 사용했기 때문에 <Back> 버튼이 자동으로 생성된 것을 볼 수 있다. 리스트를 만들어 봤으니 해당되는 리스트의 화면도 만들어 보자.

 <리스트 11> list.js에서 특정 문자열을 가져와 화면에 출력

index.js1 Ext.setup({
2    tabletStartupScreen: 'tstart.png',
3    phoneStartupScreen: 'pstart.png',
4    icon: 'icon.png',
5    glossOnIcon: false,
6    onReady: function() {
7       var panel, listPanel, mapPanel;
8                      
9       listPanel = new Ext.NestedList({
10          fullscreen: true,
11          title: 'List',
12          displayField: 'text',
13          store: store,
14          getDetailCard: function(item, parent) {
15             detailCard = new Ext.Panel({
16                tpl: "{text}"
17             });
18             detailCard.update(item.attributes.record.data);
19                return detailCard;
20          }
21       });        
22                      
23       mapPanel = {
24          title: "Map",
25          html: "맵 페이지"
26       };
27                     
28       panel = new Ext.TabPanel({
29          fullscreen: true,
30          cardSwitchAnimation: 'slide',
31          ui: 'dark',
32          items: [
33             listPanel, mapPanel
34          ]
35       });
36    }
37 });

우리가 사용했던 Ext.NestedList에 list.js에 있는 특정 값을 가져와 Ext.Panel로 만들어 찍어 주기 위해 getDetailCard 속성과 함수를 추가했다. 14번 라인에서 Ext.NestedList의 값을 가져와 detailCard에 새 Panel을 생성, 18번 라인을 통해 업데이트시킨 후 반환함으로써 화면에 보여진다.

이렇게 Sencha Touch를 이용해 리스트를 만들고 화면에 보여주는 다양한 방식 중에 한 방식을 선택해 코딩까지 해봤다. 우리가 리스트를 거쳐 ‘카라멜 마끼아또’를 선택하면 {text}를 가져와 화면에 뿌려준다. 이 부분에 ajax를 활용해서 더 다양한 화면구성이 가능하다.


<화면 8> 리스트 선택 후 화면 출력

이상 설치법과 NestedList를 이용한 화면 구성을 알아봤다. 다음 호에서는 이번 호에 이어 구글 맵 API를 이용해 탭 바 메뉴 Map을 통해 카페 위치를 지도에 표시해 보고, xtype 객체를 사용하지 않고 익숙한 HTML과 Ajax를 이용해 구성해 보자.

'♨ Framework > Sencha-Touch' 카테고리의 다른 글

Sencha Touch 기본 강좌 - 마이크로 소프트웨어  (2) 2011.12.08
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. 꼬렙 2011.12.09 21:14 신고  댓글주소  수정/삭제  댓글쓰기

    김 주임~
    스마트폰 공부한다고 고생이 많소 ㅋㅋㅋㅋㅋ
    나중에 나도 이쪽으로 업무하게 되면 많은 조언 부탁하오~





nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appProcess' defined in ServletContext resource
[/WEB-INF/Appdi-servlet.xml]: Cannot resolve reference to bean 'SimpleJdbcTemplate' while setting bean property 'template';

nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'SimpleJdbcTemplate' defined in ServletContext resource

[/WEB-INF/Appdi-servlet.xml]: Cannot resolve reference to bean 'DataSource' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'DataSource' defined in ServletContext resource

[/WEB-INF/Appdi-servlet.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool/KeyedObjectPoolFactory

 
Spring 에서 mysql 로 JDBC 구현할 때, 저딴 에러가 나면 그냥 파일하나 lib 에 넣어주면된다...ㅠㅠ;

ㅅㅂ...내 2시간......

 

YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. Cheap Moncler vests for women 2015.01.01 16:07  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  2. New Era 59FIFTY Hats Wholesale Free shipping 2015.01.09 16:46  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  3. Wholesale New Era Caps,Cheap Snapback Hats/Caps 2015.01.09 16:49  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  4. louboutin homme 2015.01.17 10:48  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  5. louboutin pas cher 2015.01.20 04:23  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  6. giubbotti peuterey 2015 2015.01.20 06:35  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  7. hogan scarpe 2015.01.20 08:42  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  8. anello tiffany 2015.01.21 07:19  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.





Spring 에서 DAO 처리를 위해서 DataSource 를 만들때 에러가 나는 이유

commons-dbcp.jar 파일이 없어서 그런거다. 

그냥 이 파일을 다운받아서 프로젝트 lib 폴더나, 아파치 lib 폴더에 넣어주고,

프로젝트 클린 한번 해주면 에러가 없어진다.

이걸 몰라서 1시간을....ㅠㅠ  아래에 파일링크




http://apache.tt.co.kr//commons/dbcp/binaries/commons-dbcp-1.4-bin.zip
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



구현하는 자세한 설명은 전편에 있음 ( http://winmargo.tistory.com/117 )

이번 글에서는 전편에 다못한 Statement 처리를 위한 다양한 메소드들의 사용법을 살펴보자.

메소드에 쿼리문에 값을 전달하는 방법에는  인덱스 방식과, 네임드 방식이 있다

인덱스는 전편에서도 했고, 쉽기때문에 넘어가고

여기서는 네임드 방식을 쓰겠다.

네임드 방식은 인덱스에서 쿼리에 ? 를 넣은것과같이, ':변수명 또는 :맵의키값' 형식으로 넣는다 .
':' 콜론을 붙이면 되는것이다.

NamedParameterJdbcTemplate 에는 이름 기반의 파라미터를 설정할 수 있도록 해준다.

그 방식에는 두가지 방식이 있는데,

1. Map 을 이용한 파라미터 값 설정

말 그대로 맵을 이용한다. 쿼리문내에 ':' 콜론을 이용해서 키값과 같이 써주고
맵을 인자로 넣어주면, 매핑된다.


2. SqlParameterSource 를 이용한 파라미터 값 설정( 실제는 BeanPropertySqlParameterSource 클래스)

SqlParameterSource 는 인터페이스이기 때문에 실제로 사용할때는 구현한 클래스를 사용한다.

그 클래스가 바로 BeanPropertySqlParameterSource  이다.

생성할 때 인자로 자바빈 클래스를 넣어주면, 자동으로 Bean 클래스 변수와 쿼리안의 : 콜론과 붙여쓰는 이름을

같게 해주면 자동으로 매핑된다.

(MapSqlParameterSource 클래스도 있긴한데 쓸일이 없을것같다.. 불편하다)




이제부터 볼 예제들은  SimpleJdbcTemplate 을 이용한 것이다.



아래 코드는 Map 방식과 BeanPropertySqlParameterSource가 다들어있는 아주 중요한 코드다.
눈 크게 뜨고 살펴보자!!
'
public int setReply(ItemBean itemBean, int originalNumber, int depth){
		
		
		int state = 0;
		
		int firstNumber = originalNumber - 1;
		
		itemBean.setArticle_num(firstNumber);
		
		int lastNumber = firstNumber - (firstNumber % 1000) + 1;
		
		paramSource = new BeanPropertySqlParameterSource(itemBean);
		
		Map map = new HashMap();
		
		map.put("first", firstNumber);
		
		map.put("end", lastNumber);
		
		
		//  - map 방식
		sql = "UPDATE margo SET article_num = article_num - 1 WHERE article_num BETWEEN :end AND :first";
		
		template.update( sql, map);	
		
	
				
		//  - sqlParameterSource 방식
		sql = "INSERT INTO margo " + 
			  "VALUES(:article_num, :id, :title, :body, :password, :count, :depth, sysdate, :fileName)";
		
		return template.update( sql, paramSource);
		
	} // setReply() end;
	
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

23라인을 보면, 쿼리에 값을 주입할때, (BETWEEN :end AND :first) 라고 되어있다

바로 맵으로 end 와, first 키를 생성해 놓은것을, 키값을 콜론과 같이 쓰면

맵에서 자동으로 뽑아 쓴다.


그 다음은 31라인은 BeanPropertySqlParameterSource() 메소드를 이용해서 값을 주입하는 방식이다.

 13라인에서 paramSource에 생성자로 메소드 인자로 넘겨받은 자바빈 파일을 넣어주면,

자동으로 자바빈안의 변수와 값을 매핑 시켜준다.

위 두가지 방식인 네임드(이름기반) 파라미터방식은 여러모로 쓸모가 많을것 같다. 잘 배워두자~ 

 




아래는 간단한 컬럼하나 가지고오는 예제
_
	// 총글의 개수를 가지고온다. 안에 사용한 두 가지 코드는 결과는 동일하다.
	public int getArticleCount() {
	
		sql = "SELECT COUNT(*) FROM margo";
		
		
		// Object 로 리턴값을 명시해주고 컬럼 하나를 받아온다.
		totNum = template.queryForObject( sql, Integer.class);
		 

		// queryForInt 메소드를 써서 int값 만 받아올때 쓸 수 있다.
		totNum = template.queryForInt(sql);
		
		return totNum;
		
	}
	
 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

코드만 봐도 대충 알수있을 만큼 간단하다.
어떤 객체 하나를 받아올때는 QeuryForObject 를 쓰고, 리턴타입을 지정해주려면,
9라인의 방식을 쓴다. 여기서는 int 지만, 아래 예제에서 클래스를 쓰는법을 설명하겠다.
정수만 받을때는 13라인을 쓰면된다. 






QueryForObject 로 클래스(자바빈)를 리턴받는 법
<T> T
queryForObject(String sql, RowMapper<T> rm, Object... args) 
.
	public ItemBean getContent(final int contentNum) {
		
		sql = "SELECT * FROM margo WHERE article_num=?";
			
		// 익명클래스 이용한 방법.
		return template.queryForObject( sql, 
									
			new RowMapper(){
					
					@Override
					public ItemBean mapRow(ResultSet rs, int rowNum) throws SQLException{
						
						ItemBean itemBean = new ItemBean();
						
						itemBean.setArticle_num(rs.getInt("article_num"));
						itemBean.setId(rs.getString("id"));
						itemBean.setTitle(rs.getString("title"));
						itemBean.setBody(rs.getString("body"));
						itemBean.setCount(rs.getInt("hit"));
						itemBean.setDepth(rs.getInt("depth"));
						itemBean.setPassword(rs.getString("password"));
						itemBean.setWrite_date(rs.getTimestamp("write_date"));
						itemBean.setFileName(rs.getString("filename"));
					
						return itemBean;
					
					}// mapRow() end;
					
			}, // RowMapper 클래스 여기까지임.
			
			new Object[]{ contentNum });
		
	}
 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

익명클래스 방식을 이용해서 리턴 클래스를 지정해주고, ResultSet 을 이용해서 리턴받는 방법이다.

RowMapper클래스는 mapRow() 메소드를 오버라이드 해야된다.
(35라인은 무시, 코드하이라이트 버그같은데, 제네릭을 태그로인식해서 지맘대로 닫는코드를 생성...짜증난다 ㅡㅡ;) 






아래 코드는 UPDATE 메소드를 이용해서 DB에 자료를 Insert 하는 쓰는 법이다.
(SQL쿼리 update와 헷갈리면 안된다. 쓰는건 전부 update() 메소드다)
int update(String sql, Object... args)
public int setContent(ItemBean itemBean){
		
		int state = 0;
		
		String sql = "INSERT INTO margo VALUES(margo_sequence.NEXTVAL,?,?,?,?,?,?,sysdate, ?)";
		
		
		state = template.update( sql, 
							new Object[]{ itemBean.getId(),
											itemBean.getTitle(),
											itemBean.getBody(),
											itemBean.getPassword(),
											itemBean.getCount(),
											itemBean.getDepth(),
											itemBean.getFileName()});
		
		return state;
	}
	

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

update() 메소드가 나왔는데, 
쿼리에 값 주입방식은 인덱스 기반 파라미터 방식으로 집어넣고 있다.
뭐...간단하다  .


YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST




SimpleJdbcTemplate 클래스는

JdbcTemplate 과 NamedParameterJdbcTemplate 을 합쳐 놓은 템플릿 클래스로서

이름 기반의 파라미터 설정인덱스 기반의 파라미터 설정을 모두 지원한다.

이름 기반의 파라미터를 설정할 때에는 Map과 SqlParameterSource 두 가지 방법을 모두 사용할 수 있다.

SimpleJdbcTemplate 클래스가 제공하는 주요메소드는 아래와 같다. 



Method Summary

<T> List<T> 
query(String sql, RowMapper<T> rm, Map<String,?> args)
<T> List<T>
query(String sql, RowMapper<T> rm, Object... args) 
<T> List<T>
query(String sql, RowMapper<T> rm, SqlParameterSource args) 




int queryForInt(String sql, Object... args) 
int queryForInt(String sql, SqlParameterSource args) 



List<Map<String,Object>> queryForList(String sql, Object... args)
List<Map<String,Object>> queryForList(String sql, SqlParameterSource args) 



<T> T
queryForObject(String sql, RowMapper<T> rm, Object... args) 
<T> T
queryForObject(String sql, RowMapper<T> rm, SqlParameterSource args)  



int update(String sql, Object... args)
int update(String sql, SqlParameterSource args) 



이렇게 받은 template을 처리해야될 방법에 따라 잘 만들어진 메소드를 고르기만 하면 된다.

가장 기본인 DB 에서 모든 글을 긁어온다음 list 를 만들어서 보여주는 걸 해보자.
나는  List query(String sql, RowMapper<T> rm, Object... args) 이놈을 골랐다!

그럼이제 리스트 를 보여주기위한 작업을 해보자.


1.  servlet.xml 에 SimpleJdbcTemplate 을 등록한다. (http://winmargo.tistory.com/116 참고)


2. 처리를 수행하는 구현클래스에서 SimpleJdbcTemplate 을 이용해서 Statement 작업을 구현하면 끝이다.
  


구현클래스를 보자
     private SimpleJdbcTemplate template = null;
	
   
	
	
	//모든글을 몽땅 가져온다.
	@Override
	public List getArticles(int index) {
		
						
		sql =  "SELECT * FROM (SELECT ROWNUM rnum,A.* FROM " +
				"(SELECT * FROM margo ORDER BY article_num DESC) A ) "
				+ "WHERE rnum BETWEEN ? AND ?";
	
					
			
		return template.query( sql, 
				
				   				   new ItemBeanRowMapper(),
								   new Object[]{ index, (index + 10)});
		
	}
	
 

코드를 보면 17 라인에 return 값에 simpleJdbcTemplate을 줬다. 리턴값이 List


첫 번째 인자에 sql쿼리를 넣고,

두번째에 3라인에 선언한 ItemBeanRowMapper() 를 넣어주고 있다.(아래에서 자세히)

세 번째 인자에는 쿼리문 안에 있는 '?' 에 대응되어 들어갈 값 2개가 있다. ? 순서와 개수에 맞게 넣어주면 된다.(이것이 인덱스 방식이다)
  
  - 쿼리에 값을 넣는 방법은 두가지가 있는데, 인덱스 파라미터 방식과, 네임드 파라미터(이름 기반) 방식이 있다.
     
     ? 에 순서대로 대입하는 방식은 Index 기반 파라미터 방식이고,
     
      넣어줄 값을 들고있는 맵이나, 빈클래스의 변수을 ':' 콜론과 같이 쓰면 값이 들어가는데
     이 방식이 이름 기반의 파라미터 방식이다.  




기본적인 동작 방식은 template 이 sql쿼리문에 값을 넣고, Statement 처리를 한후

19라인에 있는 ItemBeanRowMapper()  클래스를 이용해서 ResultSet 을 처리한후

리턴되어오는 어떤 클래스(여기서는 Bean파일이다) 를  List 에 자동으로 담은후

그 List 를 리턴해준다.


여기서 중요한건 19라인에 ItemBeanRowMapper()  클래스 이다.

이 클래스는 위설명과 같이 쿼리수행후 리턴된 다수개의 레코드들을 모은 ResultSet에서

레코드컬럼과, 자바빈 변수를 Mapping 시켜

한 개의 레코드를 하나의 자바빈 으로 만들어서 return 시켜주면

Template 이 자동으로 List 에 담아서 Template 수행후 마지막으로 리턴 시킨다.

ItemBeanRowMapper() 클래스는 RowMapper<T> 를 구현해야한다.


ItemBeanRowMapper 클래스를 보자


package Spring;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class ItemBeanRowMapper implements RowMapper {

	
	
	@Override
	public ItemBean mapRow(ResultSet rs, int rowNum) throws SQLException {
		
		ItemBean itemBean = new ItemBean();
		
		String str ="";
				
		itemBean.setArticle_num(rs.getInt("article_num"));
		itemBean.setId(rs.getString("id"));
		itemBean.setTitle(rs.getString("title"));
		itemBean.setCount(rs.getInt("hit"));
		itemBean.setDepth(rs.getInt("depth"));
		itemBean.setWrite_date(rs.getTimestamp("write_date"));
		itemBean.setFileName(rs.getString("filename"));
		
		int cnt = itemBean.getDepth();
		
		while( cnt > 0){
			
			str += "    ";
			cnt--;
		}

		itemBean.setSpace(str);
		
		return itemBean;
	}

}

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
메소드 코드를 대충보면 딱 알수 있을만큼 간단하다.  

mapRow(ResultSet rs, int rowNum)

메소드를 오버라이딩 하면서 인자로  ResultSet 과 정수하나를 주는데

뒤에정수는 레코드 개수이다.

사용할 일이 있을때 사용하면된다.

빈파일에 컬럼을 매핑시킨후, 빈파일을 리턴하면

template 이 자동으로 List 에 쑥쑥 넣는다.


수가지의 방식이 있는데, 일단은 제일 요긴하고 잘쓰일 방법으로 구현해보았다.


다음편에는 다른 여러가지 구현방식을 알아보겠음



YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



DAO Statement 처리를 위해서 Spring에서 JDBC 를 지원하는데 

JdbcTemplate, namedParameterJdbcTemplate, SimpleJdbcTemplate 세가지 방식이 있다.

Simple 방식이 앞의 2가지 방식을 모두사용 할 수 있는 방식이다.

메소드 인자 위치가 조금 다르기때문에, 확인후 사용해야한다.





JdbcTemplate 을 사용하려면, 일단 DataSource 를 넣어준후 생성해야된다.


여러가지가 있겠지만, 일단 Template 에는 DataSource 가 생성자로 넣어주는 방식이 편한데


 JdbcTemplate 클래스에는 setDataSource() 메소드가 있어서, 프로퍼티 방식으로 

생성을 해도 JdbcTemplate 이 문제없이 생성되지만,


SimpleJdbcTemplate 클래스는 setDataSource() 메소드가 없기 때문에

생성자 방식으로만 생성 할 수 있다. 




↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

코드를 보면, 일단 DataSource를 만들어주고,

Template 을 두가지 방식으로 선언했다.

p:dataSource-ref 가 프로퍼티 방식으로 클래스를 주입하는 것이다.

JdbcTemplate 은 프로퍼티 방식으로 setter 를 이용해서 DataSource를 넣어준후 클래스를 생성하고

SimpleJdbcTemplate 은 setter가 마련이 안되있기 때문에, 생성자 방식으로만 클래스 생성이 가능하다.


이제 그냥 자바 클래스에서 Template 선언후, 생성자로 받던지, 프로퍼티로 받아서 
사용하면된다. 
 





YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


1. SELECT 쿼리 예제
// 숫자형을 리턴할때
int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt(
"select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});

// 도메인 객체와 매핑할때
public Collection findAllActors() {
return this.jdbcTemplate.query( "select first_name, surname from t_actor", new ActorMapper());
}
private static final class ActorMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname"));
return actor;
}
}

2. INSERT, UPDATE, DELETE 쿼리 예제
this.jdbcTemplate.update(
"insert into t_actor (first_name, surname) values (?, ?)",
new Object[] {"Leonor", "Watling"});

SQL문만 다를뿐 모두 update 메서드를 사용하면 된다.
또한 간단한 스토어드프로시져도 update메서드를 이용할수 있다.
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
new Object[]{Long.valueOf(unionId)});


3.기타
그밖에 DDL 같은 특수한 쿼리를 실행할때는 execute()메서드를 사용하기도 한다.
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");


출처 : 
http://yunsunghan.tistory.com/239
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST