스프링/3. 스프링 MVC

50. 스프링MVC 구조 전체정리.

sdafdq 2023. 8. 13. 21:58

전체 정리를 한번 해보자.

 

요청이 들어온다.

그러면 먼저 프론트컨트롤러에서 나에게 등록된 컨트롤러들에서 매핑을 해서 가져온다.

(예를 들어 uri 등(더 많지만) 그것과 내가 등록한 컨트롤러에 등록한(이 uri로 요청이 들어오면 이걸 실행해줘~ 라고 내가 RequestMapping으로 등록한 것)이 있는지 찾아본 다음에, 있으면 그걸 가져온다. )

    private void initHandlerMappingMap(Map<String, Object> handlerMappingMap){
        handlerMappingMap.put("/front-controller/v5/v3/members/new-form",new MemberFormControllerV3());
        handlerMappingMap.put("/front-controller/v5/v3/members/save",new MemberSaveControllerV3());
        handlerMappingMap.put("/front-controller/v5/v3/members",new MemberListControllerV3());

        handlerMappingMap.put("/front-controller/v5/v4/members/new-form",new MemberFormControllerV4());
        handlerMappingMap.put("/front-controller/v5/v4/members/save",new MemberSaveControllerV4());
        handlerMappingMap.put("/front-controller/v5/v4/members",new MemberListControllerV4());
    }

우리가 등록했을때는 이렇게 uri와, 그 종류, 그러니까 어떤 인터페이스였는지 그걸 등록했었음.

아마 이게 나중에 가서는 MemberListControllerV3, MemberSaveControllerV3 등등 이렇게 나눠진게 아니라, 같은 결 끼리는 저걸 메소드로 묶는데, 

 

@Controller
@RequestMapping("springmvc/v3/members")
public class SpringMemberControllerV3 {
    MemberRepository memberRepository = MemberRepository.getInstance();

//    @RequestMapping(value = "/save", method = RequestMethod.POST)
    @PostMapping("/save")
    public String save(@RequestParam("username") String username, @RequestParam("age") int age, Model model) {
        Member member = new Member(username, age);

        Member savedMember = memberRepository.save(member);
        model.addAttribute("member",member);
        return "save-result";
    }

//    @RequestMapping(method = RequestMethod.GET)
    @GetMapping
    public String findAll(Model model) {
        List<Member> members = memberRepository.findAll();
        model.addAttribute("members", members);
        return "members";
    }

//    @RequestMapping(value = "/new-form",method = RequestMethod.GET)
    @GetMapping("/new-form")
    public String newMemberForm() {
        return "new-form";
    }
}

이렇게, 아마 이런 것도 다 Bean으로 등록되어서, 싱글톤화 되기 때문에, 좀 컨테이너에서 Bean으로써 가져오는 그런 모양새가 아닐까 함.

 

여튼 그런 식으로 가져오고,

 

그 다음 그 handler를 가져오는데, 

    private void initHandlerAdapters(List<MyHandlerAdapter> handlerAdapters){
        handlerAdapters.add(new ControllerV3HandlerAdapter());
        handlerAdapters.add(new ControllerV4HandlerAdapter());
    }

이런 식이여서 핸들러도 저 Bean을 토대로 다 등록이 될 듯~

 

여기 핸들러 보면 

public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return handler instanceof ControllerV3;
    }

    @Override
    public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        ControllerV3 controller = (ControllerV3) handler;
        Map<String, String> paramMap = createParamMap(request);

        return controller.process(paramMap);
    }

    private static Map<String, String> createParamMap(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
        return paramMap;
    }
}

이런 식임.

그러니까 supports가 있고, 거기서 그냥 자기와 맞는 핸들러인지 확인하고,

맞으면, 

    private MyHandlerAdapter getHandlerAdapter(Object handler){
        for( MyHandlerAdapter adapter : handlerAdapters){
            if(adapter.supports(handler)) return adapter;
        }
        throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler = " + handler);
    }

이런 식으로 그 어댑터를 가져옴.

 

그 다음, 그 어댑터를 통해서 handler를 실행하는 거임.

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object handler = getHandler(request);

        if(handler == null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        MyHandlerAdapter adapter = getHandlerAdapter(handler);
        ModelView mv = adapter.handle(request, response, handler);

        MyView view = viewResolver(mv.getViewName());

        view.render(mv.getModel(), request, response);

    }

저기 보면 mv 받는 곳에 adapter.handle 있음.

위 그림대로 ModelAndView를 반환.

 

그 다음 viewResolver를 통해서 논리이름을 물리이름으로 변환받아서 그걸 그려주는걸 담당하는 View객체에 담아서, return해줌. 그 다음, view.render해주면 그려줌.

view.render같은 경우는 이제 보통 jsp면 실제로 그냥 forward() 해주는 거고, 다른 건 그냥 자바로 response 메시지바디에 html을 넣어서 응답해줌.

 

근데 또 알아야 할 게, 여러 방식에 대해 지원해주는 handler가 인자를 받는 것.

이거는, adapter가 ArgumentResolver를 호출해줘서 handler에 인자로 넣어주는 것 임.

 

이 ArgumentResolver도 마찬가지로 어댑터 패턴으로 계속 맞는지 돌려봐서, 맞으면 request에서 온 정보들을 가지고 있는 객체에서 뒤져봐서(추정), 인자가 필요한 정보에 맞게 잘 섞어서 주는거임. 그걸 이 ArgumentResolver가 모두 객체로 만들어서 전달을 해주는 거임. 먼저 필요한 인자들을 모두 객체로 만들어 준 다음에, 다 만들어 줬으면 전달.

 

그 다음 ReturnValueHandler로 마찬가지임.

우리가 View까지 가는데 필요한 정보들이 있잖음? 이 ReturnValueHandler가 view가 처리할 수 있는 어떤 무언가로 변환해 줌.

 

그럼 그걸 viewResolver가 가지고 가서 또 어댑터패턴으로 찾은 다음에, view를 반환해주고, 그걸통해 render(model)하면 됨.