woniper

Spring Data REST #3 내부 동작 본문

Spring

Spring Data REST #3 내부 동작

woniper1 2017. 6. 17. 14:42

  정말 Repository Interface로만 API가 동작할까? 당연히 Repository Interface만 있다고 해서 API가 동작하는 건 아니다.
  우리가 Spring MVC를 사용해서 Controller를 만들어서 API를 만들듯이, Spring Data REST에는 이미 만들어진 Controller가 존재한다. 차이점이 있다면, 기존에 만들던 Controller와는 조금 다른 Spring Data REST만의 Controller를 만든다.

@RepositoryRestController

  아마도 Spring MVC를 최근까지 사용해본 개발자라면 Controller를 만들기 위해서 @Controller 또는 @RestController 애노테이션을 사용해서 Controller를 만들었을 것이다.

  Spring Data REST에서는 @RepositoryRestController 애노테이션을 제공한다. 기존 Controller 역할을 하는 애노테이션과 차이점이 있다면, @RepositoryRestController는 Repository interface에서 제공되는 API를 확장하기 위한 Controller라고 말하고 싶다.

  사실 Spring MVC Controller(@Controller, @RestController)와 Spring Data REST Controller(@RepositoryRestController)는 동일하게 동작한다.

  Spring Data REST #2 동작 원리에서 설명했지만, Spring Data REST는 Spring MVC + Spring Data가 결합된 프로젝트다. 결국 @RepositoryRestController도 Spring MVC에 의해 동작한다는 것이다.

Spring MVC Architecture

@RepositoryRestController도 동일하게 동작한다는 것을 이해하기 위해서 먼저 Spring MVC가 Controller를 어떻게 찾아 실행하는지 알아야 한다.


DispatcherServlet

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 생략...
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 생략...

            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // 생략...
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // 생략...
        }
            // 생략...
    }
    catch (Exception ex) {
            // 생략...
    }
    catch (Throwable err) {
            // 생략...
    }
    finally {
            // 생략...
    }
}

  DispatcherServlet의 일부 코드인 doDispatch 메소드이며, DispatcherServlet은 J2EE 패턴 중 하나인 FrontController 패턴의 구현체다.

  이 코드에서 설명하고자 하는 부분은 바로 HandlerMapping, HandlerAdapter다. getHandler 메소드를 호출해 HandlerExecutionChain 객체를 받고, getHandlerAdapter 메소드를 호출해 HandlerAdapter 객체를 받는다.

HandlerMapping

HandlerMapping 클래스는 우리가 만든 Controller(@Controller, @RestController, @RepositoryRestController)를 찾는 역할을한다. Spring MVC 3.1 버전 이후로 기본 HandlerMapping 구현체는 RequestMappingHandlerMapping이 기본 HandlerMapping으로 설정된다.

HandlerAdapter

HandlerAdapter 클래스는 HandlerMapping으로 찾은 Controller를 실행하는 역할을 한다. Spring MVC 3.1 버전 이후로 기본 HandlerAdapter 구현체는 RequestMappingHandlerAdapter이 기본 HandlerMapping으로 설정된다.

RepositoryRestHandlerMapping, RepositoryRestHandlerAdapter

  Spring Data REST에서 HandlerMapping의 구현체는 RepositoryRestHandlerMapping이며, HandlerAdapter 구현체는 RepositoryRestHandlerAdapter다.



그렇다면, 지금까지 설명한 이 구현체들은 어떻게 Controller를 찾을까?

RequestMappingHandlerMapping.isHandler 메소드

@Override
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

RepositoryRestHandlerMapping.isHandler 메소드

@Override
protected boolean isHandler(Class<?> beanType) {
    return AnnotationUtils.findAnnotation(beanType, RepositoryRestController.class) != null;
}

  HandlerMapping 구현체의 isHandler 메소드는 요청 URL에 해당하는 Controller가 있는지 체크하기 위한 메소드다. 많이 보던 애노테이션 아닌가? 애노테이션 설명은 생략하겠다.

그런데, 궁금한점이 있다. Spring Data REST를 사용하면 기존에 사용하던 Spring MVC의 Controller는 사용하지 못할까? 답은 아니다. 당연히 사용가능하다. 그 이유는 바로 DispatcherServlet은 HandlerMapping과 HandlerAdapter를 List(List<HandlerMapping>, List<HandlerAdapter>)로 포함하고 있다. 때문에 요청한 URL에 해당하는 HandlerMapping을 찾아 HandlerAdapter에 전달한다.

여기까지가 @RepositoryRestController를 찾아 동작할 수 있었던 이유다.

Repository*Controller

  진짜 우리가 궁금한건 어떻게 Controller를 찾는게 아니였다. Reository interface만으로 API가 동작할 수 있었던 이유가 궁금했던 것이다. 눈치 챘겠지만, Repository interface가 API로 동작 가능하도록 이미 만들어진 @RepositoryRestController가 있다.



다양한 @RepositoryRestController 구현체들이 존재한다.

크게 4개 Controller가 이미 구현되어 있다.

마무리

  기본 사용방법 부터 구조까지 자세히 설명하고 싶었는데, 잘 설명하지 못한거 같다. 아직 Spring Data REST를 깊게 이해하지 못한걸지도 모르겠다. 좀 더 공부하며 어디에, 어떻게 적용하면 좋을지 고민해봐야겠다.   추가로 궁금한 부분 질문 또는 토론이 필요하다면 언제든지 환영합니다.

Comments