흐음... 스프링 컨텐이너에서 관리되지 않는 객체에서 스프링 빈을 사용하고 싶을때가 있다고 치고...
이런 경우 스프링이 제공하는 WebApplicationContextUtils 클래스를 이용해서 WebApplicationContext에 접근할 수 있다.

WebApplicationContextUtils 클래스는 DispatcherServlet이나 ContextLoaderListener가 생성한 WebApplicationContext에 접근할 수 있는
static 메소드를 제공한다.

ㄱ. WebApplicationContext getWebApplicationContext(ServletContext sc)
ContextLoaderListener가 생성한 루트 WebApplicationContext를 구한다.
ㄴ. WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName)
지정한 속성이름 (attrName)으로 저장된 WebApplicationContext를 구한다.
주로 DispatcherServlet이 생성한 WebApplicationContext에 접근할 때 사용된다.

WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
Object commonLogger = rootContext.getbean("commonLogger");

<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

의 경우

org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher 이다.

실제 접근시에

WebApplicationContext dispatcherContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), "org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");

Object helloController = dispatcherContext.getBean("helloController");






 

 

'IT > 스프링' 카테고리의 다른 글

뷰 영역???  (0) 2012.01.26
@RequestBody , @ResponseBody  (0) 2012.01.20
캐시옵션 설정  (0) 2012.01.19
HandlerInterceptor  (0) 2012.01.19
파일 업로드 in spring... 3  (1) 2012.01.18
AnnotationMethodHandlerAdapter는 캐시 헤더를 설정하는 기능을 제공하고 있다.
따라서, 모든 컨트롤러에 동일한 캐시 옵션을 설정해야 할 경우, 중복없이 AnnotationMethodHandlerAdapter의 캐시 관련 프로퍼티를 이용해서 동일한 캐시 헤더를 설정할 수 있다.

  <bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="cacheSeconds" value="0" />
<property name="alwaysUseFullPath" value="true" />
<property name="webBindingInitializer">
<bean class="madvirus.spring.chap06.binder.CustomWebBindingInitializer" />
</property>
<property name="messageConverters">
<list>
<ref bean="byteArrayHttpMessageConverter" />
<ref bean="stringHttpMessageConverter" />
<ref bean="formHttpMessageConverter" />
<ref bean="sourceHttpMessageConverter" />
<ref bean="marshallingHttpMessageConverter" />
<ref bean="jsonHttpMessageConverter" />
</list>
</property>
</bean>

cacheSeconds 값이 0 : 캐시를 하지 않도록 헤더를 생성 / -1 : 캐시관련 헤더를 생성하지 않음 / 1 이상: 해당 시간만큼 캐시하도록 헤더설정







 

'IT > 스프링' 카테고리의 다른 글

@RequestBody , @ResponseBody  (0) 2012.01.20
WebApplicationContext 직접접근?  (0) 2012.01.19
HandlerInterceptor  (0) 2012.01.19
파일 업로드 in spring... 3  (1) 2012.01.18
Validator / Error interface .... in Spring  (0) 2012.01.16
스프링이 기본적으로 제공하는 HandlerMappingHandlerInterceptor를 이용해서 컨트롤러가 요청을 처리하기 전과 처리한 후에 알맞은 기능을 수핼할 수 있도록 하고 있다.

조건에 다라 컨트롤러에 요청을 전달하고 싶지 않거나, 컨트롤러가 요청을 처리한후에 ModelAndView 객체를 조작하고 싶은 경우에 사용된다.

HandlerInterceptor 인터페이스는 
요청처리전(preHandle), 요청처리후(postHandle), 요청처리하고 클라이언트에 응답전송한 뒤( afterCompletion ) .. 3군데이다.
순서대로...

ㄱ. boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)
handler 에는 컨트롤러 객체가 전달됨
한개 이상의 HandlerInterceptor가 체인을 형성하는데 preHandle 에서 false 를 리턴하면 다음 HandlerInterceptor또는 컨트롤러를 실행하지 않고,
요청 처리를 종료한다.

ㄴ. void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler, ModelAndView modelAndView)
컨트롤러가 요청을 처리한 뒤에 호출된다.
HandlerInterceptor체인에서 postHandle() 메소드는 preHandle 메소드의 살향순서를 반대로 해서 실행된다.
컨트롤러 실행 도중에 예외가 발생할 경우 실행되지 않는다.

 
ㄷ. afterCompletion(HttpServletRequest request,HttpServletResponse response, Object handler,Exception ex)
클라이언트의 요청을 처리한뒤, 즉, 뷰를 통해서 클라이언트에 응답을 전송한 뒤에 실행된다.
만약 컨트롤러가 처리중... 뷰처리중.. 에러가 나면..예외가 생기더라도 실행된다!!!
실행순서는 preHandle() 메소드의 실행순서와 반대이다.


HandlerInterceptor 인터페이스를 직접 구현하여 클래스를 작성할수도 있다. 다만 그렇게 되면 모두 구현해야한다;
해서 그냥 HandlerInterceptorAdaptor 클래스를 상속받아 필요한것만 구현한다.


HandlerInterceptor 의 실행순서
뭐 예측대로 움직이는데...  설정은 제대로..


<bean 
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
    <list>
             <ref bean="interceptor1"/> 
             <ref bean="interceptor2"/> 
             <ref bean="interceptor3"/> 
    </list>
</property>

 위의 경우 클라이언트 요청을 처리할때 실행되는 메소드의 실행순서는
preHandle() : 1,2,3
postHandle() : 3,2,1
afterHandle() :3,2,1


HandlerExceptionResolver 를 이용하면 서블리 컨텐이너가 생성한 에러페이지가 아닌 예외타입에 맞는 , 스프링 MVC와
연동된 뷰를 이용해서 에러페이지를 출력할 수 있게 된다.

.. 찾아봐라 ~_~

SimpleMappingExceptionResolver
예외 타입이름과 특정 뷰 이름을 매핑할때 사용된다.

<bean
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">
error/mathException
</prop>
<prop key="java.lang.Exception">
error/exception
</prop>
</props>
</property>
</bean>

java.lang.ArithmeticException 익셉션이면 뷰로 error/mathException 뷰를 사용하게 된다.
 











 





 

'IT > 스프링' 카테고리의 다른 글

WebApplicationContext 직접접근?  (0) 2012.01.19
캐시옵션 설정  (0) 2012.01.19
파일 업로드 in spring... 3  (1) 2012.01.18
Validator / Error interface .... in Spring  (0) 2012.01.16
Ant 경로 패턴 in @RequestMapping  (0) 2012.01.16
흐음... 파일 업로드가 필요한경우 html 폼의 enctype 속성을 "multipart/form-date" 로 설정해야한다.

스프링은 Multipart지원 기능을 제공하고 있기 때문에 쉽게 처리 가능하다고 하네?

우선 언제나 그렇듯이.. 뭔가를 등록해야한다. .. 쓸려면 말야~
뭐겠니?? 빈이지.

MultipartResolver 를 등록해줘야 한다. 해당 빈은 Multipart 형식으로 데이터가 전송된 경우, 해당 데이터를 스프링 MVC 에서 사용할 수 있도록 변환해준다.
예로, @PathVariable 어노테이션을 이용해서 Multipart로 전송된 파라미터와 파일을 사용할 수 있도록 해준다.

스프링이 제공하는 기본 MultupartResolver는 CommonsMultipartResolver 이다.
해당 리졸버는 Commons FileUpload API를 이용해서 Multipart를 처리해준다. 

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

CommonsMultipartResolver 가 제공하는 프로퍼티는..... 찾아보면 나옴; (뭐.. 최대업로드 사이즈, 최대 메모리 사이즈, 인코딩 타입등을 설정할 수 있다)

1. 업로드한 파일을 전달받는 방법

1.1 @RequestParam 어노테이션을 이용한 업로드된 파일 접근

<h3>@RequestParam 사용</h3>
<form action="submitReport1.do" method="post" enctype="multipart/form-data">
학번: <input type="text" name="studentNumber" />
<br/>
리포트파일: <input type="file" name="report" />
<br/>
<input type="submit" />
</form>

파라미터 이름은 report 이고 @RequestParam어노테이션과 MultipartFile 타입의 파라미터를 이용해서 파일 데이터를 전달받는다.


@RequestMapping(value = "/report/submitReport1.do", method = RequestMethod.POST)
public String submitReport1(@RequestParam("studentNumber") String studentNumber, @RequestParam("report") MultipartFile report) {
printInfo(studentNumber, report);
return "report/submissionComplete";
}
MultipartFile을 통해서 파일 이름, 데이터, 크기등을 알 수 있다.

1.2 MultipartHttpServletRequest를 이용한 업로드된 파일 접근


@RequestMapping(value = "/report/submitReport2.do", method = RequestMethod.POST)
public String submitReport2(MultipartHttpServletRequest request) {
String studentNumber = request.getParameter("studentNumber");
MultipartFile report = request.getFile("report");
printInfo(studentNumber, report);
return "report/submissionComplete";
}
MultipartHttpServletRequest 인터페이스는 스프링이 제공하는 인터페이스로서 Multipart 요청이 들어올 때 내부적으로 원본 HttpServletRequest 대신 사용된다.

MultipartHttpServletRequest 는 어떠한 메소드도 선언하고 있지 않고 단지, MultipartRequest 와 HttpServletRequest 를 상속받고 있다.


1.3 커맨드 객체를 이용한 업로드된 파일접근
흐음.. 우리가 파라미터로 받는 커맨드 객체를 통해서도 .. 되겠지

해당 커맨드 클래스에 파라미터와 동일한 이름의 MultipartFile 타입 프로퍼티를 추가해주기만 하면된다.

public class ReportCommand {
....
public void setReport(MultipartFile report) {
this.report = report;
}
 }

public MultipartFile getReport() {
return report;
}
 
업로드 파일의 파라미터 이름이 report 인 경우 위와 같이 report 관련 프로퍼티를 커맨드 클래스에 추가한다.

위처럼 MultuFile 타입의 프로퍼티를 커맨드 클래스(ReportCommand)에 추가했다면 @RequestMapping 메서도의 커맨드 객체로 이용할 수 있다.
        @RequestMapping(value = "/report/submitReport3.do", method = RequestMethod.POST)
public String submitReport3(ReportCommand reportCommand) {
printInfo(reportCommand.getStudentNumber(), reportCommand.getReport());
return "report/submissionComplete";
}











 



 












 

'IT > 스프링' 카테고리의 다른 글

캐시옵션 설정  (0) 2012.01.19
HandlerInterceptor  (0) 2012.01.19
Validator / Error interface .... in Spring  (0) 2012.01.16
Ant 경로 패턴 in @RequestMapping  (0) 2012.01.16
PathVariable 어노테이션을 이용한 URI 템플릿...  (0) 2012.01.16
이걸 제대로 쓰면 좋은데 말이지...

둘다 인터페이스이다.

컨트롤러에서 커맨드 객체 (간단하게는 form 값을 넘겨받은 클래스를 의미 ) 를 생성한뒤에 Validator 에 넘겨서 검증요청한다.

 인터페이스에는 2가지 메소드가 있는데

ㄱ. boolean supports(Class <?> clazz)
: 해당 클래스에 대한 값 검증을 지원하는 지의 여부를 리턴한다.
ㄴ. void validate (Object target, Errors errors)
: target 객체에 대한 검증을 실행한다. 검증결과 문제가 있을 경우 errors 객체에 어떤 문제인지에 대한 정보를 저장한다.
즉, 리턴은 없고 errors에 담겨온다.


if (memberInfo.getId() == null || memberInfo.getId().trim().isEmpty()) {
errors.rejectValue("id", "required");
}

rejectValue 메소드는 "id" 프로퍼티값이 잘못됐고, 에러코드로 "required"를 남겼다.

Validator 클래스를 작성하면, 다음으로 컨트롤러 클래스에서 Validator를 이용해서 커맨드 객체를 검증하는 것이다. 

검증하는 코드를 추가하는 작업은

ㄱ. @RequestMapping 어노테이션 메소드에서 커맨드 객체 다음 파라미터로 BindingResult 타입이나 , Errors 타입의 파라미터를 추가한다.
ㄴ. 메소드내에 Validator 객체를 생성한뒤 validate() 메소드에서 커맨드객체와 BindingResult 또는 Errors 타입 파라미터를 전달한다.
ㄷ. Errors.hasErrors() 메소드를 이용해서 에러가 존재하는지 확인하고, 에러가 존재할 경우 알맞은 처리를 한다.

사용은 아래와 같이...
@RequestMapping(method = RequestMethod.POST)
public String submit(@ModelAttribute("command") MemberInfo memberInfo,BindingResult result) {
new MemberInfoValidator().validate(memberInfo, result);
if (result.hasErrors()) {
return "account/creationForm";
}
return "account/created";
}


2. Errors / BindingResult 인터페이스
Errors 와 BindingResult 인터페이스의 사용처를 보면,
ㄱ. Errors : 유효성 검증 결과를 저장할때 사용
ㄴ. BindingResult : Errors 인터페이스의 하위 인터페이스로 폼 값을 커맨드객체에 바인딩한 결과로 저장함.

2.1 Errors
값이 올바르지 않을경우 reject() 또는 rejectValue()메소드를 이용해서 어떤 필드가 잘못되었는지, 관련된 에러가 무엇인지 입력받는다.

reject() 메소드는 검증 대상 객체의 전체적인 에러를 설정. (이게 글로벌..을 의미함)
rejectValue() 메소드는 특정 프로퍼티의 검증에러를 설정한다.

책에서 찾아봐라 -ㅅ- 귀찮다


        @RequestMapping(method = RequestMethod.POST)
public String submit(@Valid LoginCommand loginCommand, 
BindingResult result) {
if (result.hasErrors()) {
return formViewName;
}
try {
authenticator.authenticate(loginCommand);
return "redirect:/index.jsp";
} catch (AuthenticationException e) {
// 이 경우의 익셉션은 로그인 실패이지 값이 없거나 하는게 아니다. 즉 깂이 잘못된(아이디나 패스워드가 잘못된) 객체의 문제이다.
//  그러면 아래체럼... reject를 이용해 글로벌에러정보(전체적인 에러정보) 를 추가할 수 있다 
result.reject("invalidIdOrPassword", new Object[] { loginCommand.getUserId() }, null);
return formViewName;
}
}

rejectValue 를 이용하면 객체의 개별 프로퍼티(필드)에 대한 에러정보를 추가할 수 있다.

@Override
public void validate(Object target, Errors errors) {
MemberInfo memberInfo = (MemberInfo) target;
if (memberInfo.getId() == null || memberInfo.getId().trim().isEmpty()) {
errors.rejectValue("id", "required");
}
if (memberInfo.getName() == null
|| memberInfo.getName().trim().isEmpty()) {
errors.rejectValue("name", "required");
}
        ....
        }

rejectValue 는 맵과 달리 계속 추가적으로 에러를 등록할 수 있다. (같은 키값에 말야)
그러면 그 만큼 나중에 확인가능하다. (카운트가 가능한게 이 이유임)

에러가 발견됐다면 페이지 이동시켜버리면된다. 

@RequestMapping(method = RequestMethod.POST)

public String submit(@ModelAttribute("command") MemberInfo memberInfo,

BindingResult result) {

new MemberInfoValidator().validate(memberInfo, result);

if (result.hasErrors()) {

return "account/creationForm";

}

return "account/created";

}



보다시피 return 이 String 이고 주소를 줘서 웹이동을 하게 되는 것임.

BindingResult 인터페이스의 기본 구현클래스(AbstractBindingResult) 클래스는 MessageCodesResolver를 사용하여 에러코드에 대한 에러메시지를 추출한다. 기본적으로 DefaultMessageCodeResolver클래스를 MessageCodesResolver로 사용한다네..

DefaultMessageCodeResolver 를 이용해서 자동으로 에러메시지를 생성하려면 MessageSource를 빈 객체로 등록해야한단다.

에러메시지를 출력할 때에는 스프링이 제공하는 <form:errors> 커스텀 태그를 사용하면된다.

뷰로 jsp를 사용할 경우 2가지 방식으로 에러메시지를 설정한다.
ㄱ. <spring:hashBindErrors> 커스텀 태그를 이용하여 에러정보를 설정
ㄴ, <form:form> 태그를 이용하여 에러정보를 설정

아... 귀찮다 예제는 그냥 책봐라...


ValidationUtils
뭐.. null 이냐 그냥 값없음(whitespace) 이냐를 판단하는 유틸을 제공하더라.
ValidationUtils 이며 메소드로 StringUtils와의 차이는 단순 결과리턴이 아닌 reject등의 데이터가 저장되는거랄까? 즉 rejectValue 등을 호출할 필요가 없다 그거지...

ValidationUtils.rejectEmptyOrWhitespace(errors,"id","required");

위처럼 처리가능하다. ... 나머지는 찾아봐라.. 많다 -ㅅ-;

@Valid 어노테이션 ,  @InitBinder 어노테이션을 이용한 검증

위에서는 Validator 객체를 생성하고 validate를 호출해서 검사했지만 어노테이션으로도 처리 가능하다.

즉... 어노테이션 달고 그냥 자동으로 되는거라 그거지.

@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new LoginCommandValidator());
}

어떤 Validator가 커맨드 객체를 검증할지 여부를 위처럼 설정한다.

그리고 validate() 호출하지 않고 그냥 BindingResult 를 보고 판단한다.
        @RequestMapping(method = RequestMethod.POST)
public String submit(@Valid LoginCommand loginCommand, BindingResult result) {
if (result.hasErrors()) {
return formViewName;
}
try {
authenticator.authenticate(loginCommand);
return "redirect:/index.jsp";
} catch (AuthenticationException e) {
result.reject("invalidIdOrPassword", new Object[] { loginCommand
.getUserId() }, null);
return formViewName;
}
}

만일 @Valid 가 적용된 뒤에 BindingResult 나 Errors 타입이 없다는 의미는... 에러시의 처리를 하지 않겠다는 의미가 된다. (500에러가 나올것임)












 



 




'IT > 스프링' 카테고리의 다른 글

HandlerInterceptor  (0) 2012.01.19
파일 업로드 in spring... 3  (1) 2012.01.18
Ant 경로 패턴 in @RequestMapping  (0) 2012.01.16
PathVariable 어노테이션을 이용한 URI 템플릿...  (0) 2012.01.16
URI 매칭.. (서블릿 맵핑)  (0) 2012.01.13
 
참고로 @RequestMapping 어노테이션은 Ant 스타일의 패턴을 지원한다.
? : 1개의 문자와 매칭
* : 0개이상의 문자와 매칭
** : 0개 이상의 디렉토리와 매칭

기억하도록~~

'IT > 스프링' 카테고리의 다른 글

파일 업로드 in spring... 3  (1) 2012.01.18
Validator / Error interface .... in Spring  (0) 2012.01.16
PathVariable 어노테이션을 이용한 URI 템플릿...  (0) 2012.01.16
URI 매칭.. (서블릿 맵핑)  (0) 2012.01.13
@ModelAttribute  (2) 2012.01.13



흐음.. RESTful 서비스의 URI 형태가 이런거였구나.

즉.xxxx/xxxx/info?user=id  형태의 url이 있다면
-->
xxxx/xxxx/info/user/id .. 뭐 이런것 처럼 파라미터가 uri에 포함되도록 하는 형태란다.

이걸 스프링3 에서 흉내?? 낼수 있는데 URI 템플릿 기능이란다.

1. 설정방법
방법은
@RequestMapping 어노테이션값으로 { 템플릿변수 } 를 사용한다. 
@PathVariable 어노테이션을 이용해서 { 템플릿 변수 } 와 동일한 이름을 갖는 파라미터를 추가한다. 

@Controller
public class CharacterInfoController {
    @RequestMapping( "/game/users/{userId}/characters/{characterId}" )
    public String characterInfo(@PathVariable String userId, @PathVariable int characterId,ModelMap model) {
        model.addAttribute("userId",userId);
        model.addAttribute("characterId",characterId);
        ....
    }

RequestMapping 어노테이션에 변수를 포함하고 있다.
이들변수는 @PathVariable 어노테이션이 적용된 동일한 이름을 갖는 파라미터에 매칭된다. 



/game/users/ezsnotes/characters/sorcerer

하면 userId / characterId에 ezsnotes , sorcerer 가 들어간다.

만약 @PathVariable 어노테이션에 값을 주면 변수이름을 동일하게 하지 않고 지정할 수 있게 된다.


2. 추가 설정방법
 @Controller

@RequestMapping("/game/users/{userId}")
public class CharacterInfoController {

@RequestMapping("/characters/{characterId}")
public String characterInfo(@PathVariable String userId,
@PathVariable int characterId, ModelMap model) {
model.addAttribute("userId", userId);
model.addAttribute("characterId", characterId);
return "game/character/info";
}
}

위의 코드처럼 클래스와 메소드에 각각 RequestMapping을 걸어놓게 되면?
메소드에 적용한 @RequestMapping 어노테이션의 값은 클래스에 적용한 @RequestMapping 어노테이션의 값을 기본경로로 쓰게 된다.


메소드에 적용된 어노테이션의 값은 /characters/{characterId} 인데 실제 매칭되는 값은 클래스에 적용된 어노테이션의 값을 포함한
/game/users/{userId}/characters/{characterId} 가 된다.






















'IT > 스프링' 카테고리의 다른 글

Validator / Error interface .... in Spring  (0) 2012.01.16
Ant 경로 패턴 in @RequestMapping  (0) 2012.01.16
URI 매칭.. (서블릿 맵핑)  (0) 2012.01.13
@ModelAttribute  (2) 2012.01.13
모델 생성.... (Model )  (0) 2012.01.13


DispatcherServlet은 DefaultAnnotationHandlerMapping 클래스를 기본 HandlerMapping 구현체로 사용한다.

 @RequestMapping 의 주의사항..

@RequestMapping("/search/game.do")
public String search(...) {
...


@RequestMapping("/game/info")
public String info(...) {
...


코드를 보면 /search/game.do 는 search에서 처리하고, /game/info 는 info에서 처리할듯 하지만...
첫번째것은 맞고 두번째는 틀렸다.

<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
<url-pattern>/game/*</url-pattern>
</servlet-mapping>

url-pattern 에서 /game/* 로 설정했기 때문이라네?
url-pattern값으로 디렉토리를 포함한 패턴을 지정하게 되면 서블릿 경로는 /game 이 되며, 
서블릿 경로를 제외한 나머지 경로를 이용해서 @RequestMapping 어노테이션의 값과 매칭여부를 판단한단다.

해서 game/info 의 요청이 오면 실제 요청 URI 는 /info가 된다. 
허나 RequestMapping 에는 /game/info라고 했으므로 /info와 맞지 않게 된다.


이런 이유는 DispatcherServlet 이 기본적으로 사용하는 HandlerMapping 구현체와 HandlerAdapter 구현체가 전체 경로를 사용하지 않도록 설정되어있기 때문이란다.

해서 사용하게 설정하면... 위의 경우 잘 되겠지?
즉, 서블릿 경로를 포함한 전체 경로를 이용해서 매칭 여부를 판단하도록 설정하려면, 
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" p:alaywaUseFullPath="true"/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerAdapter" p:alwaysUseFullPath ="true"/>
 


 










 



'IT > 스프링' 카테고리의 다른 글

Ant 경로 패턴 in @RequestMapping  (0) 2012.01.16
PathVariable 어노테이션을 이용한 URI 템플릿...  (0) 2012.01.16
@ModelAttribute  (2) 2012.01.13
모델 생성.... (Model )  (0) 2012.01.13
@Controller 어노테이션... - 1 -  (0) 2012.01.10

+ Recent posts