스프링 3.0에서 코어에 통합된 자바콘픽(@Configuration)은 여러모로 매력적이다. 하지만 그게 왜 매력적인지 느끼기는 쉽지 않다.

일반 개발자들이라면 애플리케이션 빈은 @Component로 스캔해서 만들면 그만이다. 트랜잭션, AOP, 유틸 따위의 인프라 빈은 XML의 <namespace:*>을 이용하면 간편하게 등록하고 설정할 수 있다.

반면 <tx:*>나 <aop:*> 등을 자바코드로 빈을 만들어서 등록하라고 하면 막막하다. @Transactional이 적용되게 하려면 <tx:annotation-driven />을 XML에 넣어주기만 하면 되는데, 이걸 @Cofiguration 클래스의 @Bean메소드로 직접 관련된 빈을 만들려면 어떤 빈 클래스를 어떻게 만들고 설정해야 할지 알 수 없기 때문이다. 스프링 소스를 파보기 전에는 파악하기도 불가능하고, 소스를 본다고 해도 스프링 소스중에서도 가장 난해한 NamespaceHandler류와 BeanDefinitionParser는 해석하려면 상당한 노력이 필요하다. 혹시 자신있으면 ConfigBeanDefinitionParser따위를 한번 들여다 보고 어떤 경우+옵션에 어떤 빈이 어떤 설정으로 만들어지는지 파악해보든가.

그래서 임의의 자바코드를 이용한 빈 등록과 설정 방식을 지원하고, 자동 빈 스캔을 이용할 수 있는 3.0에서도 설정용 XML은 반드시 필요했다.

하지만 스프링 3.0에서는 XML의 전용 설정 태그를 대체할 수 있는 @Enable류의 자바콘픽 설정방식이 등장했다. 그래서 XML없이도 간편하고, 타입체크도 쉬우면서, 훨씬 유연한 방법으로 각종 인프라 빈의 등록과 설정이 가능해졌다. 원하면 스프링관련 XML을 모두 제거하는 것도 가능하다.

XML 몇줄 들어가는 것이 무슨 상관이냐, 스키마도 있어서 IDE에서 자동완성과 타입체크도 된는데라고 생각한다면 그냥 XML 써도 그만이겠지만. 새로운 설정방식에 관심이 있다면 @Enable로 대표되는 자바코드 설정 방식에 도전해보는 것도 좋겠다.

XML을 쓸 때 빈의 종류에 따라서 XML파일을 분리해서 사용하는 경우는 흔치 않다. 기껏해야 웹 관련 여부에 따라서 루트 컨텍스트와 웹 컨텍스트 정도를 구분하는 수준이다. 반면에 자바코드를 이용하면 성격에 맞게 각각의 설정을 독립된 @Configuration 클래스에 분리하는 것이 간편하고, 깔끔하게 보일 수 있다.

예를 들면, <tx:*>류에 대응되는 @EnableTransactionManagement가 붙은 클래스라면 관련된 DataSource나 SessionFactory 빈 등만 넣어두면 깔끔하다. 이러면 설정 클래스를 조합해서 재사용하기도 쉬고, 코드도 직관적이고, 테스트할 때도 원하는 설정 클래스만 모아서 돌리면, 애플리케이션 전체 인프라 빈을 다 가동시키지 않아도 되기 때문에 빠른 실행이 가능하다.

@Enable~류의 가장 큰 매력은 단순히 애노테이션 엘리먼트를 통한 설정만 가능한게 아니라, WebMvcConfigurer와 같은 discoverable config. API를 이용해서 좀더 복잡한 설정이 가능하다는 점이다. 대표적인 것이 3.0에서 가장 어정쩡했던 <mvc:*>를 대체할 수 있는 @EnableWebMvc와 WebMvcConfigurer이다. 종류도 많고 설정 방식도 복잡한 DispatcherServlet의 전략을 <mvc:*>로 꾸미면 상당히 지저분한데,  이를 자바코드만으로 편리하게 만들 수 있다.

@Enable~이 가지는 또 다른 매력은, XML 네임스페이스 활용방법이 그랬던 것처럼, 개발자가 프로젝트에 필요한 인프라 빈 또는 앱 빈을 간결한 방식으로 등록, 설정 가능하도록 직접 활용할 수 있다는 점이다. 만들기 까다로운 스키마-전용태그-빈정의파서 등등 방식보다 훨씬 쉽고 편하게 개발가능하다.

프로젝트에 적용한 사내 프레임워크의 기본 설정등을 @Enable 애노테이션과 엘리먼트 값 정도만 가지고 한방에 설정하도록 하는 것도 가능하다. 예를 들어 @EnbaleHibernate 라는 것을 하나 만들어서, database.properties 정보를 활용해서 DataSource를 만들고, SessionFactory도 만들고, 트랜잭션 AOP도 걸고, 기타 관련 빈 등을 한방에 세팅할 수 있도록 만들 수 있다. 몇가지 @Enable~을 조합하는 것만으로 사내 표준 프레임워크의 인프라 빈을 모두 세팅하는 것도 괜찮은 방법일테다. 매번 샘플에 나온 스픠링 XML을 카피카피 해서 조금씩 고쳐쓰다가, 깜빡하고 한줄 잘못 지워서 엉뚱하게 동작하는 따위를 막아줄 수도 있을테고.

다음 번엔 @Enable~등을 어떻게 만들 수 있는지, 스프링 3.1이 제공하고 실제로 적용한 방식이 무엇인지 정리해보자.

그리고 지금은 구상단계긴한데, 1월 2~3째주쯤에 이에 대해서 관심있는 분들과 함께 흥미로운 세미나를 할까도 생각중이다.

사실 3.1 얘기를 인터셉터로 시작하려고 했던 것은 아니다. 지난 포스팅의 핵심은 자바콘픽의 사용방법이었고, @MVC 콘픽 클래스에서 인터셉터를 적용하는 메소드의 예를 든 것은 문제가 생겼을 때 난처한 결과를 맞을 수 있는 적절한 예이기 때문이다. 자세한 설명은 내년중에 마무리할 토비의 스프링 3 개정판에서 다루겠지만, 그래도 눈치를 채고 문제를 짚어낼 분이 있을까 싶어서 질문으로 마무리해본 것인데, 대부분 별 관심이 없는듯하고, 몇몇분은 그냥 답 내놓으라고만 하니…

유일하게 SuHong Lee님이 성의있는 의견을 주셨다.

혹시 인터셉터의 적용 순서를 알 수 없다는 것에대한 문제 인가요??
정확히는 모르겠네요
<mvc:>를 사용 하면 적용 순서를 알 수가 있을텐데
위와 같이 적용하면 순서가 어떻게 적용 될지 모를 수도 있을 듯하네요

스프링에는 Ordered 인터페이스를 구현하고 order프로퍼티를 노출시켜서 적용 순서를 지정할 수 있도록 만들어진 빈들이 제법 많다. 자주 눈에 띄는 것을 꼽자면 HandlerMapping류 빈들이 있다. 여러 핸들러매핑 전략을 동시에 적용할 때, 우선순위를 지정해서 가장 먼저 걸리는 핸들러를 적용하도록 만들 수 있다. 일부 뷰리졸버도 그렇고.

하지만 MVC 인터셉터의 API인 HandlerInterceptor나 그 구현 빈들은 Ordered가 적용되어있지 않다. 따라서 숫자값을 설정해서 그걸로 인터셉터의 적용 순서를 결정하지도 않는다.

인터셉터는 핸들러매핑 안에서 만들어지는 핸들러 호출용 HandlerExecutionChain안에 핸들러(컨트롤러라고도 부르는)와 함께 들어가서 실행체인의 일부로 만들어진다. 이렇게 만들어진 핸들러 체인은 DispatcherServlet에 의해서 적절한 순서에 의해서 인터셉터와 핸들러가 실행된다.

여러개의 인터셉터가 적용됐을 때, 각각이 독립적이고 서로 간섭이 없다면(핸들러체인 실행을 중단하는 따위) 실행 순서가 어떻게 되든 상관없다.  반면에 실행순서가 의미가 있는 경우라면 일정한 실행 순서가 보장이 되야 한다. HandlerInterceptor는 pre와 post가 반대의 반향으로 실행된다. 인터셉터 1,2,3이 있고, 1,2,3의 순서가 정해져있다면 1-2-3-handler-3-2-1-view-3-2-1의 순서로 실행될 것이다.

그럼 실행 순서는 어떻게 결정되는가?

일단 3.1얘기니까 3.1의 핵심인 WebMvcConfigurationSupport이나 WebMvcConfigurer를 사용했다고 보면, addInterceptors() 메소드를 구현 또는 오버라이딩하고, 파라미터로 전달된 InterceptorRegistry를 이용해서 인터셉터를 추가해주면 된다. 3.0에서 등장한 <mvc:interceptors>를 이용한 방법과 거의 동일하다.

InterceptorRegistry는 HandlerInterceptor 타입의 인터셉터와 WebRequestInterceptor 타입의 인터셉트 두 가지를 등록할 수 있게 되어있다. 일반적으로 HandlerInterceptor가 인터셉터를 만들 때 쓰는 대표적인 인터페이스지만, HandlerInterceptor는 Servlet API에 종속적이라는 단점이 있기 때문에, Portlet을 본격적으로 지원(원래 1.3에 들어가기로 한 기능인데, S1 2005인가 하면서 1.3을 건너뛰고 2.0으로 가기로 결정 했고, 그래서 2.0에 뒤늦게 등장했다)기 시작한 2.0부터는 웹 기술 API에 독립적인 WebRequestInterceptor가 등장해서 HandlerInterceptor와 함께 사용되기 시작했다. 서블릿 환경에서만 사용할 것이라면 HandlerInterceptor로 충분하겠지만, 서블릿, 포트릿 환경에 가릴 것 없이 사용될 수 있다면 WebRequestInterceptor가 적합하다. 그래서 지난번에 예로 들었던 OSIV인터셉터도 WebRequestInterceptor를 구현하고 있다.

이렇게 두가지 종류의 인터셉터가 섞여서 등록된다면 그 실행 순서는 어떻게 될까?

기본적으로는 인터셉터의 구현 방식은 순서에 영향을 주지 않는다. 실행순서는 레지스트리에 등록한 순서다. 내부에서 List로 저장해서 관리하기 때문에 순서가 보장된다. WebRequestInterceptor도 결국 실행 환경을 보고 적절한 아답터(WebRequestHandlerInterceptorAdapter 같은)를 통해서 HandlerInterceptor 타입으로 전환되서 실행체인으로 넘어가므로 사실 구분이 없다. 스프링(정확히는 Spring-mvc 모듈)은 내부적으로 이런 인터페이스들을 adapted interceptor라고 부른다.

그러면 그냥 인터셉터는 InterceptorRegistry에 add한 순서라고 보면 될까?

그런데, 그게 또 아니다.

인터셉터는 또 다시 단순 adapted interceptor와 mapped interceptor로 구분될 수 있다. 후자도 결국은 같은 HandlerInterceptor를 구현한 인터셉터지만 특정 URL패턴에 매칭되는 경우에만 실행되는 놈이다. <mvc:>를 썼다면 <mvc:mapping path="/secure/**"/>과 같이 사용했던 바로 그 방식이다.

그런데, 이렇게 매핑패턴을 가지는 mapped interceptor는 일반 인터셉터보다 실행 우선순위가 밀린다. 레지스트리 등록순서에 상관없이 나중에 실행된다.

정리하자면, 인터셉터는 단순 인터셉터(adapted interceptor)가 먼저 실행되고 매핑 인터셉터(mapped interceptor)가 그 다음에 실행되는데, 종류별로는 인터셉터 레지스트리에 등록된 순서대로 실행이 된다.  따라서 아무리 mapped interceptor를 먼저 등록해도 나중에 실행될 것으로 판단해야 한다.

만약 URL매핑을 하는 인터셉터가 우선적으로 실행되게 만들고 싶다면? 그때는 HandlerInterceptor를 구현해서, 거기서 URL비교를 해서 인터셉터 로직을 적용할지 말지를 결정하도록 만들고 URL패턴 없이 일반 인터셉터로 등록해서 사용하면 될 듯.

일단 여기까지.

지금까지 4번 참석했던 SpringOne 컨퍼런스. 초기 오픈소스 커뮤니티의 축제 같던 모습에서 점차 딱딱한 마케팅용 기업 컨퍼런스로 변모해가는 모습들이 기억에. 스프링OSGi나 스프링 배치를 최초로 공개하는 자리에 있었다는 뿌듯함. 그리고 전세계에서 몰려든 고수들 사이에서 내 실력은 정말 보잘 것 없구나라고 느끼며 겸손해졌던 순간들.

한번 방문하려면 적지 않은 돈과 시간을 들여야 하는데, 언제부턴가는(사실은 책 쓴다고 일도 안하고 인생을 탕진했던 때) 그럴 여유도 없어졌고 그래서 최근 몇년은 회사의 빵빵한 지원을 받으며 남이 다녀온 이야기만 침흘리며 들어야 했는데, 올해도 마찬가지.

올해는 제끼고 내년에 같이 가기로 약속했던 기선군은 배신을 때리고 혼자 가버렸다.

기선이의 뽐뿌.

그리고 표준프레임워크 오픈 커뮤니티 커미터인 정호열님의 방문기

그리고 다음과 같은 참석 나늠 세미나를 한다고.

16차 기술세미나(2011.12.28)- Springone2GX 참석 후기 나눔 세미나

100년만에 쓰는 블로그. 그동안 너무 바빴다. 간단한 생각은 구글+에 남기는 것으로 충분하기도 했고.

그래도 스프링 3.1이 나왔으니 블로그에 좀 정리해둘 필요를 느껴서 다시 시작.

3.0에서 시작된 본격적인 Java 코드를 이용한 DI설정은 3.1에서 한층 더 강화됐다. 전용 스키마 태그(tx, aop 등등)에 대응되는 편리한 자바 코드 설정방법도 등장했다. 빈 스캔은 물론이고, 트랜잭션 설정, MVC 설정등등도 모두 자바 코드를 이용해서 정의할 수 있다.

가장 눈에 띄는 것은 @EnableWebMvc을 이용한 스프링MVC 관련 설정이다. 몇가지 방법이 있는데, 나는 가장 정교한 설정이 가능한 WebMvcConfigurationSupport를 선호한다. WebMvcConfigurationSupport를 확장해서 만든 웹 설정 클래스는 <mvc>를 완전히 대체할 수 있다. 물론 그 이상의 정교한 DispatcherServlet 전략 튜닝도 가능하다.

예를 들면, 기존 3.0에선 XML에서 다음과 같이 모든 핸들러 매핑에 하이버네이트 OSIV 패턴을 적용해주는 인터셉터를 정의할 수 있었다.

<mvc:interceptors>
<bean class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</mvc:interceptors>

이를 WebMvcConfigurationSupport를 이용해서 정의한다면 다음과 같이 addInterceptors메소드를 오버라이딩해서 레지스트리에 인터셉터 오브젝트를 직접 등록해주면 된다.

@Override
protected void addInterceptors(InterceptorRegistry registry) {
OpenSessionInViewInterceptor osivInterceptor = new OpenSessionInViewInterceptor();
osivInterceptor.setSessionFactory(sessionFactory);
registry.addWebRequestInterceptor(osivInterceptor);
}

이때 OSIVI에 프로퍼티로 주입해줄 sessionFactory는 어디서 정의되어있든 상관없이 다음 한 줄을 추가해주면 된다.

@Autowired SessionFactory sessionFactory;

SF를 XML에 정의했든, 다른 자바 설정 클래스에 정의했든 상관없이 알아서 가져온다.

이렇게 직접 OSIV인터셉터를 만들어서 등록해주면 <mvc>를 사용했을 때와 동일한 설정 결과를 얻는다.

하지만,

나는 이런 방식이 상당히 위험하다고 생각한다. 물론 지금 이 addInterceptors()의 설정은 별 문제가 없어보인다. 하지만 이런 스타일로 자바 코드를 이용한 설정방식을 사용하게 된다면, 매우 난감한 결과를 맞을 가능성이 높아질 것이다. 그래서 별로 권장하고 싶지 않다.

뭐가 문제일까?

재성이가 보내준 자빌메(자바 세상의 빌드를 이끄는 메이븐)을 두번째 읽기 시작했다.

이 책을 처음 읽은 것은 몇 주 전이다. 책을 받은 날 저녁, 반가운 마음에 바로 읽기 시작해서 그날 밤 잠자리에 들기 전에 끝냈다. 책의 내용은 내가 이미 다 알고 있는 것들이라 친숙한데다, 블로그 글을 읽는 듯한 느낌이 들정도로 흥미롭고 편안하게 쓰여져서인지 술술 잘 읽혔다. KSUG 운영진의 한 명인 benelog님은 한 시간 반만에 이 책을 다 읽어다고 하니 메이븐을 이용한 개발과 경험이 있는 사람이라면 그리 어렵지 않게 끝낼 수 있는 책인게 분명하다.

꽤 오래전부터 책을 읽고 서평을 쓰거나 감상을 적는 일은 삼가해 오고 있다. 여러가지 이유가 있겠지만, 가볍고 진지하지 못한 인상비평을 수준의 글을 공개된 매체에 남겨서 다른 이들에게 어떤 방향으로든 오해를 주고 싶지 않기 때문이다. 누군가는 소중한 가치를 얻을 수 있는 책을 꼼꼼히 읽고 고민하지도 않은 주제에 몇 가지 트집을 잡고 빈정대서 책을 제대로 접할 기회조차 박탈시켜 버리거나, 성의 없는 두리뭉실 칭찬으로 자신이 필요로 하지도 않은 책을 읽게 만들어서 바쁜 개발자들의 시간을 낭비하게 만들고 싶지 않았다. 책 읽고 감상 남기는게 뭐 그리 대수라고 깐깐하게 생각하는가 싶겠지만, 내가 스스로 책을 쓰는 데 적지 않은 인생을 소비해보니 그 창작의 수고에 대해 어떤 면으로든 갖춰야 할 예의가 있다는 생각이 절실히 들었던 것 같다.

혹은 남의 책 잘못 깠다가, 나중에 내 책이 역공을 당해 판매가 저조해지면 출판사에 불려가 혼날까봐 겁이 났던 것도 있다. 자기 생각을 F워드를 섞어가며 거침없이 쏟아내던 개빈 킹 조차, VC로부터 거액의 투자를 받고난 뒤  회사의 이미지에 나쁜 영향을 주는 직원의 말과 행동에 강력한 제제가 가해지던 JBoss에 입사하고는 예의바르고 순한 범생처럼 변해간 것과 비슷할지도 모르겠다.

그러던 중에 재성이가 자기가 쓴 "책을 보낼테니 주소를 알려달라"고 연락이 왔다. 앗싸를 외치고는, 메신저로 주소를 적어주고 있었는데, 뒤 따르는 말이 "책에 대한 냉정한 평가를 해줘"였다. "에이, 우리 사이에 어찌 그런 짓을…"이라고 넘기려는데, "그럼 안 보내줄거야"라는 협박이 돌아왔다. 그래서 어쩔 수 없이 책을 읽고 감상을 공개하기로 약속했고, 지금 다시 내 느낌과 생각을 정리하기 위해서 책을 읽고 있다.

 

내 스타일이 원래 그러니, 한번 시작하면 얘기가 길어질 지도 모른다. 그래서 결론부터 얘기하고 가야겠다.

이 책은 메이븐의 도입/사용을 진지하게 고려하고 있는 사람이라면 반드시 읽어야 할 책이다. 메이븐에 관한 책이 영어나 한글로 여러 권이 이미 나와있고, 인터넷에도 많은 자료가 존재한다. 하지만 이 책, 자.빌.메.는 다른 책과 다르다. 다른 모든 책보다 낫다고 쉽게 말할 수는 없다. 하지만 분명한 것은 다른 책과 차별될만한 요소를 가진 독특한 책이다. 그 점이 매력이고, 장점이다. 물론 그것이 어떤 독자에게는 단점될 수도 있겠지만. (재성이는 내 책의 리뷰를 쓰면 스프링 개발 경험을 몇년 쌓은 뒤에나 보라고 할거라고 했는데, 나는 재성이와 달리 까칠하지 않고 착하기 때문에 "빌드 경험 몇년 쌓고 난 뒤에 이 책을 보라"고 하지는 않을 거다. 삽질 경험을 쌓고 나면 더 얻을 수 있는 가치게 있겠지만, 일단 이해는 안되도 책을 읽으며 이런 것이 있구나 하고 기억만 해둬도 큰 도움이 될 것이기 때문이다)

결론 끝.

참, 노안이 시작되기 시작한, 또는 안구건조증이나 기타 질병으로 눈이 침침한 분들은 절대로 흐릿한 실내 조명 아래서 이 책을 읽지 말기 바란다. 책의 인쇄상태는 가장 큰 단점이다. 특히 명령창에 나온 메이븐 실행화면은 뭉개지고 흐릿해서 아주 밝은 조명아래가 아닌 곳에서 읽으려면 눈이 충혈되고 아파올 수 있다. 나도 한밤중에 스탠드 불빛 아래서 책을 다 읽느라고, 눈이 빠지는 줄 알았다. 그마나 메이븐 실행 결과 내용에 익숙해서 대충 보고 넘어가서 그렇지, 명령창의 내용을 자세히 읽으려면 눈에 힘주다 짜증나서 책을 덮어버렸을 지도 모른다. 다음 인쇄에서는 개선되기를 가장 바라는 부분이다. 이 문제는 출판사의 실책이 아닌가 싶다. 물론 시력이 1.2이상 되고, 명암 구분에 뛰어나며, 눈에 총기가 도는 젊은 분들이라면 괜찮을지도 모르겠지만.

 

다음 할 얘기는 "이 책이 말하는 빌드란 도대체 무엇인가? 메이븐이 과연 빌드툴인가?" 이다. 처음부터 시비를 걸고싶지는 않지만, 어쨌든 책이 다루는 주제의 정체와 책이 사용하는 용어에 대해 언급하지 않고 시작할 수는 없기에, 이 책이 빌드라는 개념에 대해서 두리뭉실 넘어가고, 일관성 없이 쓴 것을 지적하지 않을 수 없겠다. 이 얘기는 다음 편에서 계속.

© 2011 Toby's Epril Suffusion theme by Sayontan Sinha

Switch to our mobile site