Web Application 개발 시 SpringFramework(http://spring.io/) 을 주로 사용하고 있다. 


개발 시 공통으로 필요한 Util 을 개발 하여 사용하지만, 기존에 개발되어 있는 좋은 라이브러리가 이미 존재 한다. 

SpringFramework 에서 org.springframework.util package 에서 제공하는 class 이다. 



3.2.0 RELEASE 기준 javadoc 문서 이다. SpringFramework 기능도 위 유틸을 사용하고 있고, 검증도 되어 있기 때문에 사용하는 경우 개발 시간도 단축하고 안정성도 올라간다고 판단 된다.


SpringFramework 3.2.0 RELEASE API 주소: http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/

API 를 참조 하면 제공되는 class의 정보를 활용할 수 있다.


참고 사이트

 ※ Logo Image: http://fr.wikipedia.org/wiki/Fichier:Spring_framework.png

 ※ SpringFramework: http://spring.io/

 ※ SpringFramework 3.2.0 RELEASE API: http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/



WEB 개발을 JAVA 로 하는 경우 SpringFramework 를 많이 사용 하고 있다.


※ 공식 사이트: http://spring.io/


가장 많이 사용하게 되고, 빠질 수 없는 DispatcherServlet 의 구조에 대해 알아보자



위 그림은 Spring MVC 의 전체 흐름도 이다. 중요하게 봐야 할 부분은 HandlerExceptionResolver 이다. 



  • HandlerExceptionResolver
    • DispatcherServlet 에 셋팅을 했을 경우 모든 Exception 을 위임 받아 해당 페이지로 이동 한다.

dispatcher-servlet.xml (Spring 2.5 기준)
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="order" value="1" />
    <property name="exceptionMappings">
        <props>
            <prop key="Exception">error</prop>
        </props>
    </property>
</bean>

HandlerExceptionResolver 에 해당되는 Class 이다. 위에서 설명 한대로 exceptionMappings 에 예외 Class와, 이동할 Page를 설정 한다.

Annotation 지원은 Spring 3.0 이상 부터 된다고 한다. 


SampleController.java

@RequestMapping(value="sample", method=RequestMethod.GET)
public ModelAndView sample(String sample) throws Exception {
    
    ModelAndView mav = new ModelAndView("sample");
    
    try {
        
        // ... logic
        
        return mav;
    } catch(Exception e) {
        e.printStackTrace();
        throw e;
    } 
}

Exception 모두 던질 경우 trace를 할 수 없기 때문에 중요한 부분은 위와 같이 trace 를 하는 부분이 좋다고 생각 한다.


dispatcher-servlet 셋팅 후 아래와 같이 예외를 위임 하는 경우 에러 페이지로 이동하는 것을 확인 할 수 있다.


※ 참고 사이트

  - https://code.google.com/p/developerhaus/wiki/SpringException

  - http://www.cnblogs.com/fangwenyu/archive/2012/10/11/2716665.html




[projectName]-servlet.xml 파일에 추가 하도록 하자.


Resolve 1.  모든 요청에 따른 Interceptor 적용


<!-- Interceptor -->

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">

<property name="interceptors">

<list>

<ref bean="kr.co.whitelife.WhitelifeInterceptor"/>

<ref bean="kr.co.whitelife.BlacklifeInterceptor"/>

</list>

</property>

</bean>



모든 요청에 대한 응답을 해주는 DefaultAnnotationHandlerMapping을 이용 한다. 다 수의 Interceptor를 순차적으로 적용이 가능하다.


Resolve 2.  일부 요청에 따른 Interceptor 적용


<!-- Interceptor -->

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<property name="mappings">

<props>

<prop key="/test">WhitelifeController</prop>

</props>

</property>

<property name="interceptors">

<ref bean="WhitelifeInterceptor"/>

</property>

</bean>



RequestUri에 따른 Controller 분기가 가능한 SimpleUrlHandlerMapping을 이용 했다. Resolve 1 방법과 같은 방법으로 Interceptor을 적용이 가능하다.


Resolve 3.  mvc 태그를 이용 하는 방법


<mvc:interceptors>

<bean class="kr.co.whitelife.WhitelifeInterceptor"/>

<bean class="kr.co.whitelife.Blacklifenterceptor"/>

<mvc:interceptor>

<mvc:mapping path="/test/*"/>

<bean class="kr.co.whitelife.WhitelifeInterceptor"/>

</mvc:interceptor>

</mvc:interceptors>



Spring 3.x 버전 이후로는 mvc 전용 태그를 지원 한다. 2~3번째 줄에 있는 부분은 Resolve 1 방법, 4~7번째 줄에 있는 부분은 Resolve 2 방법과 같다. 


Interceptor Class Sample


/**

 * Sample Interceptor

 * @author whitelife

 * @since 2013-01-31

 */

@Controller("TestInterceptor")

public class TestInterceptor extends HandlerInterceptorAdapter {


@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

System.out.println("TestInterceptor{preHandle}............................................ start");

return true;

}


@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

System.out.println("TestInterceptor{postHandle}............................................ end");

}

}




Interceptor도 Controller과 마찬가지로 RequestUri에 따라 분기를 하기 때문에 Controller과 같은 동작을 한다. @Controller도 적용이 가능하다.




 Spring의 Ioc에 대하여 설명 한다.

 본 글에 들어가기에 앞서 http://www.springsource.org/download/community 에서 Spring 3.x.x 이상을 다운 받고 라이브러리에 추가 하도록 한다.

 Java라는 언어를 이용하면서 new 라는 키워드를 자주 이용 한다. 이와 같은 행위는 Instance 를 생성 하는 것 이다.

 

public class Wcontroller{
 Wservice service = new Wservice();
 ....
}

public class Wservice {
 Wdao dao = new Wdao();
  ....
}

public class Wdao {
  ....
}

 

 위 Sample을 보도록 하자. Wcontroller Class는 Wservice Instance를 가지고 있고, Wservice Class는 Wdao Instance를 가지고 있다. 이런 경우는 서로 강하게 의존을 하고 있는 Code라고 볼 수 있다.

 Instance는 서로 의존(Dependency) 하는 모습을 보여주고 있다. 이런 상황의 경우에는 "의존 관계의 제어권은 개발자가 가지고 있다." 라고 말할 수 있다. Instance에 대한 LifeCycle을 개발자가 직접 관리하게 된다. 제어권을 Container에게 넘겨주는 행위를 제어권의 역전 Ioc(Inversion Of Control) 이라고 한다. 여기서 말하는 Container는 SpringFramework의 ApplicationContext 라고 봐도 된다.

 

 

 위 그림을 보도록 하자. Ioc(Inversion Of Control) 은 두 종류로 분리가 된다. 개발자가 Container 에게 제어권을 위임하게 되면 new 를 Container가 대신 한다. 첫 번째의 경우 DI(Denpendency Injection)은 Container가 직접 주입해주는 경우 이다. 개발자는 Object 선언만 하고 Container가 Instance를 주입하게 되는데, 첫번째 방법에서 많이 이용한다. 하지만 멀티 쓰레드 환경에서 Instance를 이용할 경우는 서로의 간섭이 없어야 하기 때문에,  두 번째 방법 DL(Denpendency Lookup)을 이용한다. Contaciner가 Instance를 가지고 있고, 개발자는 Instance를 직접 참조하여 이용 하는 방법 이다.  

 

@Controller
public class Wcontroller {
 
 @Autowired
 Wservice service;

 
 public void showMsg() {
  System.out.println("Wcontroller.showMsg....");
  service.showMsg();
 }
 
}

@Service
public class Wservice {
 
 @Autowired
 Wdao dao = new Wdao();
 

 public void showMsg() {
  System.out.println("Wservice.showMsg....");
  dao.showMsg();
 }
 
}

@Repository
public class Wdao {
 
 public void showMsg() {
  System.out.println("Wdao.showMsg....");
 }
 
}

 

위 Sample를 보도록 하자. Annotation으로 작성 되었고, Spring Web 환경에서 사용한다. 제일 위에 있던 Sample 과 달리 new 라는 키워드가 사라진 점을 발견할 수 있다. 개발자가 직접 Instance를 관리하지 않는다. @Autowired는 DI(Denpendency Injection)을 해주는 기준이라고 보면 된다. 여기서 끝이 아니다. 위와 같이 작성만 했다고 해서 동작하지는 않는다. Container에 대한 작업을 해야 한다.

 

public class WapplicationContext {
 
 public static void main(String[] args) {
  
  AnnotationConfigApplicationContext annotationConfigApplicationContext
  = new AnnotationConfigApplicationContext();
  
  annotationConfigApplicationContext.register(Wdao.class);
  annotationConfigApplicationContext.register(Wservice.class);
  annotationConfigApplicationContext.register(Wcontroller.class);
  
  annotationConfigApplicationContext.refresh();
  
  Wcontroller wcontroller = annotationConfigApplicationContext.getBean(Wcontroller.class);
  
  wcontroller.showMsg();
  
 }
 

 

 위 Sample를 보도록 하자. 쉽게 이해를 돕기 위하여 코드 기준으로 작성 하였다. Container는 Wdao, Wservice, Wcontroller Class를 등록함 으로써, 각각 Instance 들을 가지고 있다. getBean(Wcontroller.class)는 DL(Denpendency Lookup)을 해주는 기준이라고 보면 된다.

 

Wcontroller.showMsg....
Wservice.showMsg....
Wdao.showMsg....

 

 실행 할 경우 위와 같은 메시지가 출력 된다. 정상적으로 DI(Denpendency Injection), DL(Denpendency Lookup)이 된 것을 확인 할 수 있다.

 실제로 사용할 경우에는 SpringFramework의 AppcationContext Instance가 위의 역활을 대신해 줄 것이다. XML로 설정을 하게 된다.

 Spring을 사용하여 개발을 할 경우, Ioc에 대해서는 꼭 기억 하고 있도록 하자. DI(Denpendency Injection)에 대한 자세한 내용은 다음 글을 참고 하도록 한다.

 Spring Web 환경에서는 기존 Servlet을 이용했을 때의 상황과는 달리, Java의 reflection 기법을 이용하여 parameter 들을 POJO 기반의 Class로 setter 주입을 하여 제공을 하게 된다.

 기본 자료형 변수들이 setter 될때 Type이 일치 하지 않는 경우, 예외가 발생하게 된다. 이런 상황을 방지하기 위하여 initBinder라는 애노테이션을 이용하게 된다.

 

 @InitBinder
public void initBinder(WebDataBinder binder) throws Exception {
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy. MM. dd.");

    binder.registerCustomEditor(Date.class, new CustomDateEditor(simpleDateFormat, true));
}

 

Controller 코드 중 일부 이다. Spring은 initBinder라는 애노테이션을 제공 한다. POJO 객체로 setter 하기 전에 해당 애노테이션이 있는 경우 참고를 하게 된다. 위 샘플 코드는 Date Type이 불일치 하는경우를 해결하기 위하여 작성 되었다.

 Spring의 Web parameter 제공은 좋은 기술이지만, 단점으로 Type 불일치가 존재 한다. 앞으로 이런 상황이 발생할 경우 위의 애노테이션을 활용 하자.

+ Recent posts