Clean Code that Works.


 바야흐로 대항해 시대가 아니라 대용량 데이터 시대이다. 사용자들은 직접 데이터를 생산하거나 간접적으로도 다양한 방법으로 데이터들을 생산하고 있다. 여기다가 이 데이터들을 분석한 데이터까지.. 정말 데이터들은 시간이 지나면 지날수록 넘쳐나고 있다. 이 책을 읽으면서 생각의 전환을 많이 할 수 있었던거 같다. 단순히 지금 시스템에 대한 개발 및 유지보수만 하고 있을 때가 아니라 데이터가 넘처나는 지금 상황에서 이 데이터를 어떻게 유용하게 사용할수 있을까에 대한 생각이 많이 들었다.

 책에서 이야기 하는것 처럼 지금은 사용자가 주인공인 그런 사이트나 앱 들이 성공을 거두고 있다. 책에서의 예처럼 유투브도 별다른 홍보 없이도 사용자들에 의해 홍보가 되면서 가히 폭발적인 사용자 증가를 이루었고 또 최근에 카카오톡에서 자주받는 메시지인 게임 초대 메시지만 하더라도 사용자가 단순히 게임만 즐길 뿐만 아니라 결과를 공유할수있도록 하여 게임과 카카오톡 둘다 성공을 거두고 있다고 볼 수 있다. 이처럼 사용자 중심의 애플리케이션이 되기 위해서는 사용자들이 만들어 내는 집단 지성이 중요한 부분을 차지하고 있다고 책에서 이야기 하고 있다. 

궁극적으로 사이트나 애플리케이션 등을 성공으로 이끌기 위해서는 모두 꼭 읽어 봤으면 좋겠다.


여기서부터는 챕터 1 간단 요약.

집단지성은 똑똑한 군중의 힘을 이용해 애플리케이션을 좀더 가치있게 만드는 방법.

집단지성의 간단한 의미

애플리케이션을 개선하기 위해, 사용자들이 제공한 정보를 효과적으로 이용하는 것.


1챕터 마지막 요약.

집단지성은 사용자들이 소통하고, 콘텐츠를 만들고, 다른 사용자와 인맥을 맺고 사이트의 경험을 개인화할 수 있는 새로운 애플리케이션을 만드는 데 크게 기여 했다.

사용자는 다른 사용자에게 영향을 준다. 이런 영향은 인접한 범위부터 시작해 티핑 포인트(tipping point)에 도달하기까지 외부로 퍼져나간다. 유용한 사용자 생성 콘텐츠와 의견들은 입소문에 힘입어 최소한의 마케팅만으로도 널리 알려진다. 

사용자에게서 얻을 수 있는 지능 정보는 세 종류로 나뉜다.

첫째는 사용자로부터 직접 얻는 지능 정보다. 리뷰, 추천, 등급, 투표, 태그, 북마크, 사용자 소통 그리고 사용자 생성 콘텐츠(UGC) 같은 것들이 이런 지능을 얻기 위한 기술이다. 

둘째는 애플리케이션 내부 혹은 외부로부터 얻어지며, 대부분 비 구조화된 텍스트로 구성된 간접적인 지능 정보다. 블로그 포스트, 온라인 커뮤니티의 글, 위키등이 이러한 정보의 원천이다. 

셋째는 데이터 마이닝 기술을 통해 도출한 좀더 고수준의 지능 정보다. 추천 엔진, 개인화를 위해 예측 분석을 이용하는 행위, 프로필 구축, 마켓 구분 그리고 웹 마이닝이나 텍스트 마이닝 등이 좀더 고수준의 지능 정보를 적용하기 위한 작업들이다.


집단지성 프로그래밍 책을 좀 읽고 챕터 별로 정리를 해야겠다.

이전에 읽다가.... 음 좋긴 한데 지금 하는일에선 쓸모가 없겠어.. 라고 했는데..


다시 생각해보니 그래도 다 읽어 두면 좋을 것 같다.

하지만.. 책이 좀 두껍지 -ㅅ-;;

화..화이팅!!

어 티스토리 글쓰는 폼이 바뀌었네.. ㅋㅋ


요즘 모니터도 커지고 그래서, 설정 바가 왼쪽이나 오른쪽에 있는것도 꽤 괜찮은 듯.

좋은 개선이다!!


아무튼...

요즘 인수인계 받아서 유지보수 하는 시스템이 있는데..

여기에 개선할 내용이 있어서 개발 하던중...


insert를 하는 부분에서 select insert를 하는 부분이 있어서..돌아 버리는줄.. -,.-..

나는 DB는 되도록 데이터를 담는 역할을 하고, 쿼리도 데이터를 담거나 불러오는 역할로만 쓰도록 주로 코딩 하는데..

이전 소스를 살펴보면 쓸대없이 복잡하게 쿼리를 꼬아놓은 로직들을 볼 수가 있다.


이게 개발 할때는 좀더 간편하고 소스를 줄일 수 있기 때문에 쉬울지는 몰라도..

나중에 유지보수 하는 사람의 입장에서 보면 상당한 고역이다.


Service에 비즈니스 로직이 다 들어가 있어야 하는데..

ibatis를 사용하는 경우, .sql 파일에다가 로직까지 포함 되어 있으면 디버깅이 너무 어려워 진다.


으흐.. 

되도록 쓰지 말자.. ㅠㅠ

http://blog.springsource.org/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+SpringSourceTeamBlog+%28SpringSource+Team+Blog%29

Posted on May 8th, 2012 by Rossen Stoyanchev in Spring.


이전 포스트에서 new Servlet 3에 대해 소개 하면서 spring mvc 3.2 m1 에 보합된 긴~~ 요청에 대한 비동기 지원에 대해서 설명했다. 비동기 처리에 대한 두번째 매우 중요한 동기는 브라우저들은 실시간으로 요청에 대한 업데이트를 원한다는것이다. 예를 들어 채팅을 포함한 브라우저, 주가 지수, 상태 업데이트, 스포츠 생중계 등등. 모든 예들이 지연에 대해서 동일하게 민갑하지는 않지만 모든것들은 비슷한 요구가 있다.

HTTP request-response에 대한 표준으로는 브라우저가 요청을 시작하고 서버가 결과를 응답한다. 이 뜻은 서버는 브라우저로 부터 요청이 오면 새로운 전달 할 수 없다. 몇가지 방법들이 진화 하고 있는데 전통적인 polling 방식, long polling, HTTP streaming과 가장 최근에 나온 WebSocket protocol을 들 수 있다.

Traditional Polling

브라우저가 새로운 정보가 있는지 확인하고 서버는 매번 즉각적으로 응답한다. 예를들어 메일 클라이언트는 새로운 메세지를 10분마다 체크한다. 간단하고 동작하지만 새로운 정보는 가능한한 최대한 빨리 표시 되어야 하기 때문에 이 접근 방식은 비효율적인 방식이다. 

Long Polling

브라우저는 요청 정보를 유지하고 있지만 서버는 전송할수 있는 새로운 정보가 있을때까지 응답 하지 않는다. 클라이언트 관점에서 이것은 traditional polling과 같은 방식이다. 서버 관점에서는 long-running request와 매우 비슷하고 Part1에서 논의했던 기술을 가지고 확장할 수 있다.

응답에 대해서 얼마나 길게 대기할 수 있을까? 브라우저들은 5분정도 타임 아웃을 설정 하고 프록시들은 가능한한 빨리 끈어 버린다. 그래서 만약 새로운 정보가 도착하지 않는다면, long polling 요청은 브라우저에게 정기적으로 새 요청을 보낼 수 있도록 작성해야 한다. 


스프링 처럼 DI를 받을 수 있는데...

FBUserDetailService를 di 받아서 사용할 경우 일반적으로 
def fbUserDetailService 이런 형식의 카멜 표기법으로 정의하는것으로 생각한다.

하지만 되지 않아!!!!!

http://grails.org/doc/latest/guide/services.html#dependencyInjectionServices 

저기 문서의 가운데를 보면

NOTE: Normally the property name is generated by lower casing the first letter of the type. For example, an instance of the BookService class would map to a property named bookService.

To be consistent with standard JavaBean conventions, if the first 2 letters of the class name are upper case, the property name is the same as the class name. For example, the property name of theJDBCHelperService class would be JDBCHelperService, not jDBCHelperService or jdbcHelperService.

See section 8.8 of the JavaBean specification for more information on de-capitalization rules.



JDBCHelperService를 DI 받을려면 JDBCHelperService로 작명을 해주어야 한다고 한다.

뭐 그럼 나도 사용할 때

def FBUserDetailService 로 하면 됨 @_@

Grails 2.0에서 spring security 플러그인을 지원 하는데, 인증은 Fackbook을 통해서 하고 인증 정보는 spring security를 사용하는 인증 방법.

1. Grails Filter를 사용해서 인증 url을 필터링함.
2. 인증 url로 요청이 들어오고 인증이 안되어 있을 경우 Facebook을 통해 인증 하도록 함.

참고 URL
Grails Filter :http://grails.org/doc/latest/guide/theWebLayer.html#filters 
Facebook 인증 :  http://developers.facebook.com/docs/authentication/ 


FackbookAuthFilters의 코드(약간 자바 스타일 문법도 섞임)
쭉 보면 login으로 요청을 하는데 code 값이 없거나 미인증된 사용자면 Facebook을 통해서 인증을 하도록 한다.
Facebook에서 인증후 code값을 전달해 주는데, 이 code값을 다시 Fackbook을 통해서 accessToken을 받아 오도록 하고, 
이것을 가지고 사용자의 정보를 가져올 수 있다.

def filters = {

all(uri : '/login/**', uriExclude : '/logout/**') {

before = {

String code = params.code


SecurityContext context = SecurityContextHolder.context

String username = context.authentication.getName()


if (code == null && username.equals('anonymousUser')) {

redirect(url: "https://www.facebook.com/dialog/oauth?client_id=" + clientId + "&redirect_uri="+ redirectUri +"&scope=publish_stream,email")

return false

} else {

if (username.equals('anonymousUser')) {

String accessToken = null;

Integer expires = null;


def http = new HTTPBuilder( 'https://graph.facebook.com/oauth/' )


http.get( path: 'access_token',

query: [client_id: clientId, redirect_uri: redirectUri, client_secret: clientSecret, code: code] ) { resp, reader ->

String result = reader.text

String[] pairs = result.split("&")

for (String pair : pairs) {

String[] kv = pair.split("=")

if (kv.length != 2) {

throw new RuntimeException("Unexpected auth response")

} else {

if (kv[0].equals("access_token")) {

accessToken = kv[1]

}

if (kv[0].equals("expires")) {

expires = Integer.valueOf(kv[1])

}

}

}

}

if (checkAuthByAccessToken(accessToken, expires, request, response)) {

redirect(url: serviceUrl)

return false

}

}

}

}


 
Facebook을 통해서 인증 받은 사용자 정보가 서비스에 존재 하지 않은 사용자일경우 기본 롤로 생성을 하고 만약 있으면 조회를 한 후 스프링 시큐리티 세션에 넣어서 인증정보를 가지고 있도록 한다.

def boolean checkAuthByAccessToken(String accessToken, expires, request, response) {

if (accessToken != null && expires != null) {

def fb = new RESTClient('https://graph.facebook.com/')

def resp = fb.get(path: 'me', query: [ access_token: accessToken])

def email

def password = 'test'

ExampleUser user

if (resp.status == 200) {

email = resp.data.email

user = ExampleUser.findByUsername(email)

if (user == null) {

user = new ExampleUser(username: email, password : password, accountExpired: false, accountLocked: false, enabled: true ).save(flush : true)

Role role = Role.findByAuthority('ROLE_USER')

UserRole.create(user, role, true);

} else {

user = userDetailsService.loadUserByUsername(user.username, true)

}

}


UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user, password)

request.getSession().setAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(user.username));

authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request))


SecurityContextHolder.context.authentication = authRequest


return true

} else {

throw new RuntimeException("Access token and expires not found")

}

}



http://blog.goyello.com/2011/12/16/enhancements-spring-mvc31/

역시 취미 삼아 ..

Spring 3.1이 개발된지 수개월이 지난 지금 드디어 릴리즈 됬다. 이번 배포 버전은 캐쉬 추상화, 빈 프로파일, 간단한 컨테이너 설정 등 몇가지 재미난 기능을 포함하고 있다. 아무튼 이 블로그 포스팅에서 스프링 3.1에서 내가 생각하는 TOP 5 개선에 대해서 설명할 것이다.

1. Flash Attributes support
플래쉬 어트리뷰트들은 요청 사이의 

2. @Validated annotation with support for JSR-303 validation groups.

스프링 MVC bean validation은 JSR303 그룹 벨리데이션을 지원하는 새로운 @Validated annotation을  지원한다. 이 그룹은 주어진 객체에 대해 검증을 하고 제약의 하위 집합을 정의 한다. 각 제약 조건에 대한 선언은 그룹이나 그것이 속한 그룹의 목록을 정의할 수 있다.


public class User {
    @NotNull
    private String name;
 
    @NotNull(groups = { Simple.class })
    private String address;
 
    @NotNull(groups = { Simple.class, Extended.class })
    private String licenceType;
 
    @NotNull(groups = { Extended.class })
    private String creditCard;
}



public interface Simple {
 
}
 
public interface Extended {
 
}

새로운 @Validated annotation에 대한 사용은 아래 예제에 나와 있다.


@Controller
public class MyController {
 
    @RequestMapping(value = "/user/default", method = RequestMethod.GET)
    public String createDefaultUser(@Validated({ Default.class }) User user, BindingResult result) {
        if(result.hasErrors()) {
            // Validation on 'name'
        }
        return null;
    }
 
    @RequestMapping(value = "/user/simple", method = RequestMethod.GET)
    public String createSimpleUser(@Validated({ Simple.class }) User user, BindingResult result) {
        if(result.hasErrors()) {
            // Validation on 'licenceType' and 'address'
        }
        return null;
    }
 
    @RequestMapping(value = "/user/extended", method = RequestMethod.GET)
    public String createExtendedUser(@Validated({ Simple.class, Extended.class }) User user, BindingResult result) {
        if(result.hasErrors()) {
            // Validation on 'creditCard' and 'licenceType' and 'address'
        }
 
        return null;
    }
}

스프링 3.1에 추가된 이 그룹 벨리데이션은 정말 편리하다. 이 벨리데이션 그룹에 대해서 더 많은 자료는 download and read the JSR 303 specification.

3. Support @Valid on @RequestBody method arguments

@RequestMapping 핸들러 메서드는 HTTP request body에 접근할 수 있는 @RequestBody 어노테이션을 지원한다. 변환이 가능한 메시지는 메시지 컨버터를 통해 변환이 이루어진다. FormHttpMessageConverter(application/x-www-form-urlencoded), MarshallingHttpMessageConverter(application/xml), MappingJacksonHttpMessageConverter(appliction/json) 그리도 다른 converter들도. 하지만 @RequestBody 어노테이션이 붙은 변수들은 자동적으로 벨리데이션체크를 하지 않는다. 수동으로 체크해주는 로직이 필요.


@Controller
public class MyController {
 
    @Autowired
    private Validator validator;
 
    @RequestMapping(value = "", method = RequestMethod.PUT)
    @ResponseStatus(value = HttpStatus.CREATED)
    @Secured("ROLE_ADMIN")
    @Transactional
    public void save(@RequestBody User user) {
        validate(user);
        dao.save(user);
    }
 
    private void validate(User user) {
        // perfom validation using injected validator
    }
}

스프링 3.1에서 @RequestBody 메서드 어노테이션은 @Valid 메서드를 통해 자동으로 벨리데이션 체크를 할 수 있다. 이 방법은 코드를 심플하게 만들어 준다.

@Controller
public class MyController {
 
    @RequestMapping(value = "/user", method = RequestMethod.POST)
    @ResponseStatus(value = HttpStatus.CREATED)
    @Transactional
    public void save(@Valid @RequestBody User user) {
        dao.save(user);
    }
 
    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ResponseBody
    public String handleMethodArgumentNotValidException(
            MethodArgumentNotValidException error) {
        return "Bad request: " + error.getMessage();
    }
}


위의 예제에서 스프링은 자동으로 유효성검사를 수행하고 에러가 발생할 경우 MethodArgumentNotValidException을 던져준다. 추가적으로 @ExceptionHandler 메서드는 이러한 유형의 에러 처리에 대한 사용자 지정 동작을 추가하여 만들 수 있다.

4. Supporting PUT request with form encoded data

서브릿 구현의 한계는 인코딩된 HTTP PUT 요청에 대한 처리를 하지 않는 다는 것이다. 새롭게 소개되는  HttpPutFormContentFilter는 이 문제를 해결 했다. 스프링 3.1에서 RESTfull or RESTLike API 는 좀 더 심플하게 만들 수 있다. 이것에 대한 샘플은 이전 포스팅에서 찾아볼수 있다.
previous post to read more about the usage of HttpPutFormContentFilter

5. Java based configuration

기존의 Spring MVC 애플리케이션의 XML 설정은 완벽하게 자바 기반의 설정으로 변경할 수 있다. 가장 간단한 웹 애플리케이션의 설정은 @EnableWebMvc 어노테이션을 사용하는 것이다.

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.example" }, excludeFilters = @Filter(type = FilterType.ANNOTATION, value = Configuration.class))
@Import(PersistanceContextConfiguration.class)
public class ServletContextConfiguration extends WebMvcConfigurerAdapter {
 
    @Bean
    public TilesViewResolver configureTilesViewResolver() {
        return new TilesViewResolver();
    }
 
    @Bean
    public TilesConfigurer configureTilesConfigurer() {
        TilesConfigurer configurer = new TilesConfigurer();
        configurer.setDefinitions(new String[] { "/WEB-INF/tiles/tiles.xml",
                "/WEB-INF/views/**/views.xml" });
        return configurer;
    }
 
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("login");
    }
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/").addResourceLocations(
                "/recourses/**");
    }
 
    @Override
    public void configureDefaultServletHandling(
            DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}


WebMvcConfigurerAdapter에 대한 확장은 필요하지 않지만 기존 스프링 MVC의 설정방법도 허락된다. 어댑터는 추가적인 컴포넌트(포맷터,컨버터, 커스텀 벨리데이터, 인터셉터등)을 등록할 수 있도록 한다. 이 사용자 정의 설정은 자바 코드만들 사용하여 가능하다.  configuring a Spring MVC application with no xml in a Servlet 3.0 

Spring MVC 3.1  has a lot to offer.
스프링 MVC 3.1은 많은 향상점을 제공한다. 이 블로그에서 언급한 기능은 스프링 MVC와 애플리케이션 개발에 긍정적인 영향을 끼칠 것이다. 혁명적이지는 않지만 스프링 3.1에서 릴리즈된 모든 확장기능과 기능 향상에 대해서 보면 프레임 워크가 올바른 방향으로 가고 있다고 느낄수 있다. 캐쉬 추상화, 빈 프로파일, 설정 간소화 등 많은 핵심 기능들은 더 효과적으로 애플리케이션 개발을 할 수 있도록 할 것이다.

오메 발번역. ..


http://nurkiewicz.blogspot.com/2011/10/spring-pitfalls-proxying.html

취미 삼아 번역...... 발 번역 '-'

Bean Proxying은 매우 중요하고 스프링에서 제공하는 가장 중요한 기반 기술중에 하나이다. 이것은 매우 중요하고 low-level 수준이지만 우리는 이것들이 분명이 존재하고 있다는 것을 쉽게 인식하지 않는다. 하지만 트랜잭션, AOP, advanced scoping, @Async 들과 다양한 스프링의 사용 방법들은 이 기술 없이는 가능 할 수 없다. 그럼 프록싱이란 무엇인가?

예를 들어 : 만약 DAO를 service에 inject할 때, 스프링은 DAO의 instances를 가지고 바로 inject 한다. 이게 다다. 하지만 때때로 t스프링이 DAO에 대한 서비스에 의해 각각의 호출(다른 빈들에서의)에 대한 인지가 필요 한다. 예를 들어 만약 DAO에 트랜잭션이 마크 되어 있으면 이것들은 호출 되기 전과 커밋 되기전이나 롤백 되기전에 트랜잭션이 시작될 필요가 있다. 물론 수동적으로 이 작업을 할수 있지만 지루한 작업이고 오류가 발생하기 쉽다. 이게 바로 왜 우리가 선언적인 트랜잭션을 먼저 사용하는 이유이다.

그래서 스프링은 어떻게 인터셉션 메카니즘을 구현했는가? 가장 간단한것에서 진보된 3가지 메서드가 있다. 아직 이것들에 대한 장점과 단점을 논의 하지는 않고 곧 구체적인 예제에서 볼 수 있을 것이다.

Java dynamic proxies
가장 쉬운 해결책이다. 만약 DAO가 어떤 인터페이스를 구현하고 있다면 스프링은 인터페이스를 구현하는 Java dynamic proxy를 사용 하여  실제 클래스에 inject 해준다. 실제 구현 클래스는 계속 존재하고 있고 프록시는 그것들을 참조 하고 있다. 하지만 바깥쪽에서는 프록시는 빈이다. DAO에서 계속 메소드를 호출 하면 스프링은 이것들을 가로체서 몇가지 AOP 매직 과 함께 진짜 메서드를 호출 한다.

CGLIB generated classed
자바 동적 프록시의 단점은 빈은 적어도 하나의 인터페이스를 구현해야 한다는 것이다. CGLIB 이런 제한된 상황에서 동적으로 실제 bean에 대한 자식 클래스를 만들고 오버라이딩 가능한 모든 메서드에 인터셉터 로직을 직접적으로 추가한다. 원본 클래스에 대한 호출은 자식클래스를 통해서 이루어 진다.


class DAO {
  def findBy(id: Int) = //...
}
 
class DAO$EnhancerByCGLIB extends DAO {
  override def findBy(id: Int) = {
    startTransaction
    try {
      val result = super.findBy(id)
      commitTransaction()
      result
    } catch {
      case e =>
        rollbackTransaction()
        throw e
    }
  }
}

하지만 이 슈도코드는 이것이 어떻게 실제로 동작하는지 표현 하지는 못한다.

AspectJ weaving
이것은 가장 침투적이지만 개발자의 관점에서는 가장 신뢰할 수 있고 직관적인 방법이다. 이 상태에서의 가로채는 것은 클래스의 바이트코드에 직접적으로 적용되고 이 의미는 JVM의 클래스는 당신이 작성한 것과 일치하지 않다는 의미이다. AspectJ
weaver는 가로채는 로직을 클래스 내부의 바이트코드를 빌드하는 순간(compile time weaving-CTW, load time weaving-LTW) 직접적으로 수정해서 집어 넣는다.

AspectJ의 마법이 어떻게 동작하는지 이상하다고 느낀다면 여기 AspectJ에 의해 컴파일 된 후 디컴파일되고 간단한 .class 파일이 있다.
public void inInterfaceTransactional()
{
  try
  {
    AnnotationTransactionAspect.aspectOf().ajc$before$1$2a73e96c(this, ajc$tjp_2);
    throwIfNotInTransaction();
  }
  catch(Throwable throwable)
  {
    AnnotationTransactionAspect.aspectOf().ajc$afterThrowing$2$2a73e96c(this, throwable);
    throw throwable;
  }
  AnnotationTransactionAspect.aspectOf().ajc$afterReturning$3$2a73e96c(this);
}


로드타임위빙도 런타임시 클래스가 로드될 때 같은 방식으로 변환되어 호출된다. 보시다시피 불안하게 만드는 요소는 없다. 사실 수동 트랙잭션을 프로그램상에서 정확히 구현한 것이다.

프록시 기술을 아는 것은 그 코드에 미치는 영향과 프록시의 동작 방식을 이해하는 것이 중요하다. 우리는 선언적 트랜잭션 경계의 예라고 해두고, 여기가 바로 우리의 전쟁터다.

trait FooService {
  def inInterfaceTransactional()
  def inInterfaceNotTransactional();
}
 
@Service
class DefaultFooService extends FooService {
 
  private def throwIfNotInTransaction() {
    assume(TransactionSynchronizationManager.isActualTransactionActive)
  }
 
  def publicNotInInterfaceAndNotTransactional() {
    inInterfaceTransactional()
    publicNotInInterfaceButTransactional()
    privateMethod();
  }
 
  @Transactional
  def publicNotInInterfaceButTransactional() {
    throwIfNotInTransaction()
  }
 
  @Transactional
  private def privateMethod() {
    throwIfNotInTransaction()
  }
 
  @Transactional
  override def inInterfaceTransactional() {
    throwIfNotInTransaction()
  }
 
  override def inInterfaceNotTransactional() {
    inInterfaceTransactional()
    publicNotInInterfaceButTransactional()
    privateMethod();
  }
}


throwIfNotInTransaction 은 트랜잭션과 함께 메소드가 시작되지 않으면 오류를 던지게 된다. 누가 생각이나 했을까? 이 메서드는 알수없는 위치와 다른 설정에서 호출 된다.


@ResponseBody

Spring2011. 10. 5. 11:37

@ResponseBody 이것을 붙이면 AnnotationMethodHandlerAdapter에 등록된 messageConverters 중 하나로 타입을 결정해서 뷰를 만들어 주게 된다.

만약 ContentNegotiatingViewResolver 를 만들어 주고
지원하는 mediaTypes 에 <entry key="xml" value="application/xml" /> 타입이 있어서
.xml 요청이 ContentNegotiatingViewResolver를 통해서 처리를 받고자 한다면

당연한 말이지만 @ResponseBody 를 컨트롤러 메서드에 붙이면 안된다.
ContentNegotiatingViewResolver 에 까지 요청이 도착 하기 전에 AnnotationMethodHandlerAdapter 가 받아서 처리해 버린다.

ie 와 FF, 크롬의 처리 방식이 각각 틀려서
FF와 크롬에서는 뭐 별 문제 없이 동작 하지만
IE에서는 .xml 요청을 보낼 경우 .xml 요청과 함께 */* 미디어 타입의 요청이 한번 더 날라가게 된다.

하여 결과 xml 을 볼 수가 없다.

@ResponseBody와 ContentNegotiatingViewResolver를 사용할때는 주의를 하도록 하자

http://stackoverflow.com/questions/4624250/where-to-put-default-servlet-handler-in-spring-mvc-configuration

저기를 보도록 하자.

기본적으로, 스프링은 HandlerMapping은 order 프로퍼티에서 낮은 숫자가 우선 순위가 높다. dispatcher에서 처음으로 설정들을 불러들일 때 DispatcherServlet은 이 order 값을 사용해서 HandlerMapping 들을 정렬한다.

만약 order값이 명시적으로 세팅되어있지 않을 경우에는 모든 핸들러들은 Integer.MAX_VALUE 값으로 동일하게 설정된다. 그러므로 정렬 뒤에 빈이 정의된 순서대로 정해지게 된다.

따라서 handler mapping의 값을 명시적으로 세팅해주어야 <mvc:default-servlet-handler /> 태그를 설정파일 아무 위치에다가 두어도 된다.