Clean Code that Works.

오 갓~
이런 멋쟁이들이 ReflectionTestUtils 라는 아주 좋은 클래스를 만들어 두다니!!!

컨트롤러 테스트 케이스를 작성 중인데..
컨트롤러에서는 mav로 리턴한 값들을 체크하는게 주요 목적이다.

여기서 리턴한 값들 중에 가장 중요한것이 return url이 잘 맞게 넘어 오느 냐 확인하는 것이다.

근데 이것들 테스트 하기 위해서는 컨트롤러를 실행 시키는 테스트를 만들어야 하는데...
여기서 부터 고민에 빠지기 시작한다.
컨트롤러 안에 있는 Service코드 호출 하는 코드들은 어떻게 할 것인가.

컨트롤러를 @Autowired 해서 하면, Service 및 Repository로 주입 받아서 하기 때문에, 원하지 않는 통합 테스트가 되어 버린다.
컨트롤러 -> 서비스 -> 레파지토리

웁스 -,.-;;

하여, 열심이 구글링을 좀 해봤는데..
사실 목으로 서비스 만들어서 이것을 어떻게 리플렉션으로 해서 컨트롤러에 있는 서비스를 목 서비스로 바꿔 줄 수 없나.. 하는 방법을 찾고 있었다.

그런데 있었다. ㅋ_ㅋ

위 ReflectionTestUtils 라는 클래스를 통해서, 컨트롤러 클래스의 서비스 인스턴스를 목 인스턴스로 바꿔치기 할 수 있다.!!

ReflectionTestUtils.setField(action, "adManageBO", admanageBO);

위에처럼!!! 자세한 내용은 문서를 보면 아주 알 수 있다. 

목키토는 http://mockito.org/ 참고




스프링에서는 Spring Web MVC ModelAndView objects를 테스트하기 위한 메서드를 몇가지 제공 하는데, 
이것에 대해서 알아보자.


assertAndReturnModelAttributeOfType : 모델에서 해당 값이 특정 타입과 일치하는지 확인한다.

assertCompareListModelAttribute : List 타입의 모델의 값을 비교 한다.(sorting 제외)

assertModelAttributeAvailable : 모델에서 해당 값이 있는지 확인한다.

assertModelAttributeValue : 모델에서 해당 값이 expectedValue와 일치 하는지 확인 한다.

assertModelAttributeValues : 모델에서 해당 값이 expectedValue와 일치 하는지 확인 한다.(Map으로 여러개)

assertSortAndCompareListModelAttribute : 순서도 비교 해서 확인

assertViewName : mav가 리턴한 view 값과 일치 하는지 확인


테스트를 잘 작성 하자 @_@
단위 테스트에 집중!

http://blog.idm.fr/2010/09/spring-security-redirecting-to-faviconico.html

만약 스프링 시큐리티를 사용하여 웹사이트 전체를 로그인 폼과 함께 보안적용할 경우, 로그인 인증하는 중에 사용자가 "/favicon.ico"로 리다이렉트 되는 이슈가 발생할 수도 있다. 스프링 시큐리티 문서에서는 아래와 같이 설명되어 있다.

ExceptionTranslationFilter는 사용자가 요청한 원본 요청을 캐쉬 하고 있다. 사용자가 인증 되었을 경우, request handler는 캐쉬된 요청을으로 부터 원본 url 을 얻어서 그쪽으로 redirect한다. 원본 요청은 재 작성 되고 대안으로 사용된다.
이 이슈는 브라우저 캐쉬가 비어있고, 사용자가 이쪽으로 들어 온다면 다음과 같은 일이 발생한다.
  • 사용자가 "/" URL을 요청하고, 이 URL은 캐쉬 된다
  • 브라우저는 "/favicon.ico"에 요청을 한다. 이 URL은 인승시 redirect될 새로운 URL이 된다.
  • 사용자가 로그인 요청을 통해 로그인 한 후 "/favicon.ico"로 리다이렉트 된다.
이것을 수정 하기 위해서는 "/favicon.ico"를 인증을 안하는 resource로 변경을 해야 한다.

<intercept-url pattern="/favicon.ico" access="ROLE_ANONYMOUS" />



If you are using Spring Security to secure an entire website with form login, you might encounter an issue where users are redirected to "/favicon.ico" upon athentication. A note in the Spring Security documentation explains this:

The ExceptionTranslationFilter caches the original request a user makes. When the user authenticates, the request handler makes use of this cached request to obtain the original URL and redirect to it. The original request is then rebuilt and used as an alternative.
The issue is, when the browser cache is empty and a user comes in, here is what happens:
  • the user requests URL "/". This URL is cached.
  • the browser makes a requests to "/favicon.ico". This URL becomes the new URL where to redirect to upon authentication.
  • the user posts the login form and is redirected to "/favicon.ico".
To fix this, you need to set "/favicon.ico" as being a non-secured resources:

<intercept-url pattern="/favicon.ico" access="ROLE_ANONYMOUS" />

작년에는 스프링을 알고 썼다면, 올해는 이해하고 쓸 수 있도록 하자!!
화이팅!!

봄싹에서 메일 보내는 서비스 클래스 코드를 보면
new 해서 메일을 보내는 것을 확인할 수 있다.

이번에 메일 템플릿을 사용해서 메일을 보낼려고 하는데
이렇게 사용하면, 빈들을 DI 받아서 쓸 수가 없기 때문에 이 클래스를 프로토타입 빈 으로 만들어서 쓰기로 결정.

이 프로토타입 빈을 사용하는 서비스 빈에서는 DI 받아서 사용하지 말고(DI는 빈이 처음 생성될 때 한번만 진행되기 때문에,
프로토타입빈을 매번 똑같은것을 사용하게 됨) DL 받아서 사용하는 방식으로 하자.

기존의 코드(모임에 의견이 달리면 메일을 보내게 되어 있다.)
public void notifyCommentAdded(Meeting meeting, Comment comment) {
        Iterator<Attendance> Iter = meeting.getAttendances().iterator();
        Collection<Member> members = new ArrayList<Member>();
        while (Iter.hasNext()) members.add(((Attendance) Iter.next()).getMember());
        commentNotiService.sendMessage( new CommentMailMessage( comment, meeting, null, members));
    }

여기서 저기 new 하는 부분이 메일 템플릿을 생성하는 것이고, 실제 메일을 보내는것은 commentNotiService가 하게 된다.
여기서 문제점은 CommentMailMessage가 bean이 아니기 때문에,
CommentMailMessage에서 사용할 velocityEngine(벨로시티로 메일보낼때 사용) bean을 주입 받을 수 없다.
뭐 생성자에 추가해서 넘겨주면 되긴 하지만, 사실 저렇게 쓰는거 보다야 Prototype Bean을 만들어서 쓰는것이 좀 더 스프링 답다고 할까?

이 Prototype Bean과 이것을 사용하는 방법은 토비의 스프링3에 보면 잘 나와있다.
여기서는 책에 설명되어 있는것 중에서 Provider<T>를 사용하는 방법으로 bean을 생성해서 사용해 보도록 하자.

결론을 보자면..아래와 같이 되겠다.
@Inject Provider<CommentMailMessage> commentMail;

public void notifyCommentAdded(Meeting meeting, Comment comment) {
  Iterator<Attendance> Iter = meeting.getAttendances().iterator();
        Collection<Member> members = new ArrayList<Member>();
  while (Iter.hasNext()) members.add(((Attendance) Iter.next()).getMember());
  CommentMailMessage mail = commentMail.get();
  mail.setDatas(comment, meeting, null, members);
  commentNotiService.sendMessage( mail);
 }

CommentMailMessage를 빈으로 등록 해주는 것을 잊지 말고, get()해서 사용!!

샘플 SVN URL
https://dev.naver.com/svn/springstudy/security

최근 영문 포스팅
http://krams915.blogspot.com/2011/01/spring-security-simple-acl-using_19.html

봄싹 발표 URL
http://www.springsprout.org/study/4494/meeting/4596/presentation/4688

Spring Rest 사용할 때.

Spring2010. 11. 19. 10:20
Ajax 요청으로 Spring Rest 사용할 때 주의 해야 할 점이 있다.
일단 기본적으로 web.xml에 HiddenHttpMethodFilter는 당연히 설정 해야 되고.

만약에 ajax로 DELETE 요청을 날릴 때(ajax가 아닌 경우 method="DELETE"로 해주면 히든 필드(_method)에 DELETE가 담겨서 처리) 파라미터로 _method:'DELETE'를 같이 포함해서 넘겨 줘야 한다.

또, 주의 할 점이 ajax요청의 경우(jquery) 디폴트 type이 GET 이기 때문에 type : 'POST'로 지정을 해 줘야
HiddenHttpMethodFilter가 이를 인식하고 히든 필드에 담긴 method로 바꿔준다.
url : '${study.id}/board/imagePost/' + $this.attr('id'),
data : {_method: 'DELETE'},
type : 'POST',

HiddenHttpMethodFilter 코드를 보면 아래와 같이 POST로 넘어와야지 wrapper에서 메서드를 변경 해 준다.
@_@

@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String paramValue = request.getParameter(this.methodParam);
        if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
            String method = paramValue.toUpperCase(Locale.ENGLISH);
            HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
            filterChain.doFilter(wrapper, response);
        }
        else {
            filterChain.doFilter(request, response);
        }
    }



요건 일반적인 디좌인


이미지 강조 디자인 @_@


2번 하고 비슷

처음에 특정 페이지를 열었을 때, 어떤 url을 요청 했는데 권한이 없을 경우,
로그인 하는 페이지로 이동 해야 하는데 url에 jsessionid가 붙엇 url을 못찾는다는 오류가 생긴다.

간단하게 disable-url-rewriting="true"를 http 태그에 설정 해 주면 발생 하지 않는다.
근데 스프링 문서서도 위 내용은 찾을 수 없다.

http://stackoverflow.com/questions/2291236/how-can-i-prevent-spring-security-from-appending-jsessionidxxx-to-login-redirec

여기서 내용을 찾았는데... 뭐 갸내들이 바빠서 문서 업데이트를 못했나 보다 -_-;;

이전 포스팅에서 커스텀 컨버터를 등록 하는 방법을 알아 봤다.

하지만 이 경우는 <mvc:anontation-driven>을 사용할때만 가능 하다.
봄싹에서는 위 방식을 사용하지 않고, 수동으로 지정 하여 사용한다.

근데 내가 <mvc:anontation-driven>을 예전에 적용해 볼려고 하다가, 설정파일에 적어놓고 삭제하지 않고 commit 되어 봄싹 서버에도 올라가서 사용되고 있었다.
오마이갓. @_@;;;

하여 저것을 삭제 하고, 다시 컨버전 서비스를 적용할 방법을 생각해 봐야 했는데..
토비님 글을 보면 자세히 설명이 되어 있다. http://toby.epril.com/?p=989

글을 잘 읽어 보면...
<mvc:annotation-driven>이 등록해주는 빈이 한가지 더 있다. 바로 MappedInterceptor이다. 정확히 말하자면 모든 경로에 대해서 적용되는 ConversionServiceExposingInterceptor를 가진 MappedInterceptor이다.

그렇다. 봄싹에는 ConversionServiceExposingInterceptor가 등록 되어 있지 않아서, 커스텀 컨버터가 적용 되지 않고 있었던 것이다. 그럼 어디에다가 interceptor를 등록 해야 할 까?

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" p:order="0">
        <property name="interceptors">
            <list>
                <bean class="org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor" >
                    <constructor-arg ref="conversionService"/>
                </bean>
            </list>
        </property>
    </bean>

이런 형식으로 interceptor를 등록 하면 된다.
url 요청에 따라 인터셉터를 적용하고 싶으면 MappedInterceptro를 만들어서 DefaultAnnotationHandlerMapping의 프로퍼티로 해서 추가 하면 된다.


토비의 스프링3에 12장에 있는 내용.

가볍게 동작 과정을 간단히 정리 해 봅시다.
자세한건 꼭 책을 읽어 보세요.

(1) DispatcherServlet의 HTTP 요청 접수
 - DispatcherServlet의 매핑된 모든 요청에 대해서 공통적으로 진행해야 하는 전처리 작업이 있다면 이를 먼저 수행한다.(보안, 파라미터 조작, 한글 디코딩)

(2) DispatcherServlet에서 컨트롤러로 HTTP 요청 위임
 - URL이나 파라미터 정보, HTTP 명령 등을 참고해서 어떤 컨트롤러에 작업을 위임할지 결정.

(3) 컨트롤러의 모델 생성과 정보 등록
 - 모델을 생성하고 모델에 정보를 넣어 줌

(4) 컨트롤러 결과 리턴: 모델과 뷰
 - 모델과 뷰를 넘기고 컨트롤러의 책임은 끝

(5) DispatcherServer의 뷰 호출과 (6) 모델 참조
 - 뷰오브젝트에 모델 전달 후 클라이언트에게 돌려줄 최종 결과물 생성을 요청

(7) HTTP 응답 돌려주기
 - 뷰 생성까지 모든 작업을 마쳤으면 후처리기가 있는지 확인하고 작업후 최종 결과를 서블릿 컨테이너에게 돌려준다.