프로필사진

Go, Vantage point

가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.


Github | https://github.com/overnew/

Blog | https://everenew.tistory.com/





티스토리 뷰

개발/Spring

[Spring] 16. Spring MVC

EVEerNew 2022. 7. 11. 22:21
반응형

 

 

 

* 김영한님의 스프링 MVC 1편 강좌를 수강하며 정리한 글입니다. *

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -

www.inflearn.com

 

 

 

 

이전 글에서는 프런트 컨트롤러와 어댑터 패턴을 적용해 보았다.

 

 

 

 

스프링 MVC 패턴은 프런트 컨트롤러의 역할을 DIspatcher Servlet가 한다.

Spring Mvc 구조

지금까지 직접 구현해보았던 클래스들은 모두 Spring MVC가 지원하는 클래스로 대체가 가능하다.

 

 

DispatcherServlet

스프링 MVC의 프런트 컨트롤러의 역할을 한다.

DispatcherServlet도 상속 계층을 올라가 보면 HttpServlet을 상속 중임을 알 수 있다.

 

스프링 부트는 자동으로 DispatcherServlet를 서블릿으로 프런트 컨트롤러 등록해 준다.

따라서 클라이언트가 어떤 하위 경로를 호출하더라도 DispatcherServlet이 호출된다.

 

DispatcherServlet의 부모 클래스인 FrameServlet은 이미 service()를 구현해두었다.

서블릿은 호출되면 자동으로  service() 메서드가 실행되는데, 이때 DispatcherServlet.doDispatch()가 호출된다.

이 메서드가 지금까지 프런트 컨트롤러가 진행한 역할을 동일하게 수행한다.

(핸들러 조회, Adapter 조회, Adapter를 통한 handler 실행...)

 

스프링  MVC 가 지원하는 인터페이스를 구현하면, DispatcherServlet의 코드 변경 없이 도 새로 만든 컨트롤러(핸들러)를 추가시킬 수 있다. (사실 이미 필요 기능은 대부분 만들어져 있어서 가져와 사용하면 된다.)

 

 

 

핸들러 매핑과 어댑터

 

이전까지 배웠던 컴포넌트(빈)가 하나의 핸들러라고 생각하면, 두가지 작업이 필요한다.

 

1. 핸들러 매핑

핸들러를 다음 순서대로 조회한다.

 - RequestMappingHandlerMapping: @RequestMapping 어노테이션이 붙은 컨트롤러를 조회

 - BeanNameUrlHandlerMapping: url로 넘어온 값과 같은 스프링 빈의 이름을 조회

 

 

2. 핸들러 어댑터

매핑에서 찾은 핸들러를 실행할 수 있는 어댑터를 순서대로 찾아서 실행시킨다.

 

 - RequestMappingHandlerAdapter: @RequestMapping 어노테이션이 붙은 컨트롤러에 사용

 - HttpRequestHandlerAdapter: HttpRequestHandler 처리

 - SimpleControllerHandlerAdapter: Controller 인터페이스 처리

 

 

예를 들어 다음 컴포넌트의  이름을"/springmvc/request-handler" 라고 만들었다.

@Component("/springmvc/request-handler")
public class MyHttpRequestHandler implements HttpRequestHandler {

    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("MyHttpRequestHandler.handleRequest");
    }
}

 

 

클라이언트가 ~/springmvc/request-handler 로 접근한다고 가정하자.

 

일단 매핑 정보를 조회해야한다.

@RequestMapping 어노테이션 적용되지 않았기 때문에 빈 이름으로 조회된다.

 

 

이제 핸들러 어댑터를 조회한다.

어댑터의 조회는 이전 글에서 처럼 각 어댑터의 support() 메서드로 구현 여부를 확인한다.

컴포넌트는 HttpRequestHandler를 구현했으므로 HttpRequestHandlerAdapter.support()가 true로 반환된다.

따라서 HttpRequestHandlerAdapter.handle()이 프런트 컨트롤러인 DispatcherServlet에서 호출된다.

어댑터의 handle()은 위 코드의 handlerRequest()를 대신 호출하면서 어댑터 패턴이 적용된다.

 

실무에서는 대부분 @RequestMapping을 사용하기 때문에 전용 매핑, 핸들러인 RequestMappingHandlerMapping와 RequestMappingHandlerAdapter가 가장 많이 이용된다.

 

 

 

View Resolver

 

어댑터가 핸들러를 실행해 주었다면 Model 데이터를 통해 View를 출력할 시간이다.

view resolver는 프런트 컨트롤러로부터 ModelAndView 객체를 받아와서 View 객체를 반환해 준다.

 

이전 글에서는 viewResolver() 메서드로 접두사와 접미사를 붙여 jsp 파일의 전체 경로를 만들어 주었다.

 

스프링 부트에서는  application.properties에 설정한 접두사와 접미사를 붙여 전체 경로를 자동으로 만들어준다.

 

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix = .jsp

view resolver도 종류가 있는데 대표적인 두가지는 다음과 같다.

 - BeanNameViewResolver: 빈 이름으로 뷰를 찾는다. (실제 뷰를 바로 render)

 - InternalResourceViewResolver: 설정된 접두와 접미를 붙여 jsp 뷰를 반환한다. (render시 forward()가 호출됨)

 

 

 

 

@RequestMapping

 

컨트롤러에서 사용하는 어노테이션이다.

 

@Controller는 @Component를 포함하고 있기 때문에, 컴포넌트 스캔의 대상이 된다.

또한 RequestMappingHandlerMapping 핸들러 매핑의 조회 대상이 된다.

 

이 컨트롤러 내의 @RequestMapping이 적용된 메서드는 설정된 url 호출 시 실행되어 ModelAndView를 반환한다.

(@Controller 적용이 없다면, @RequestMapping가 클래스에 적용되어도 매핑 조회 대상이 된다.)

 

@Controller
public class SpringMemberListControllerV1 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

	// 클라이언트가  ~/springmvc/v1/members 접근 시 호출됨
    @RequestMapping("/springmvc/v1/members")
    public ModelAndView process() {
        List<Member> members = memberRepository.findAll();
        ModelAndView mv = new ModelAndView("members");
        mv.addObject("members", members);

        return mv;
    }
}

이제 스프링에서 제공하는 ModelAndView객체를 사용하면 addObject()로 데이터도 간단히 적용이 된다.

 

@RequestMapping는 메서드 단위로 적용되기 때문에 사실 Controller 객체 하나에 모두 통합시킬 수 있다.

물론 관련 있는 것들끼리 묶어주는 것이 좋다.

 

이때 컨트롤러 전체의 Request prefix도 @RequestMapping를 클래스에 적용해서 설정이 가능하다.

 

@Controller
@RequestMapping("/springmvc/v2/members")	//prefix설정
public class SpringMemberControllerV2 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @RequestMapping("/new-form")	// /springmvc/v2/members/new-form로 조합됨
    public ModelAndView newForm(){
        return new ModelAndView("new-form");
    }
    
    @RequestMapping("/suffix")
    ...

    @RequestMapping	// "/springmvc/v2/members"
    public ModelAndView members() {
        List<Member> members = memberRepository.findAll();
        ModelAndView mv = new ModelAndView("members");
        mv.addObject("members", members);

        return mv;
    }
}

 

 

 

어노테이션 기반의 컨트롤러는 유연하기 때문에 ModelAndView 뿐만 아니라 문자열(뷰 이름)을 반환해도 자동으로 알맞은 절차를 수행해준다.

 

 

추가적으로 더 이상 Response 객체에 파라미터를 담아 넘겨줄 필요도 없다.

@RequestParam("paramName")으로 매개변수로 바로 원하는 파라미터만 받아와 사용할 수 있다.

 

@RequestMapping("/save")
public String save(
       @RequestParam("username") String username,
       @RequestParam("age") int age,
       Model model
) {
    Member member = new Member(username, age);
    memberRepository.save(member);

    model.addAttribute("member", member);
    return "save-result";
}

문자열로 반환한다면 Model을 받아와서 데이터를 저장해 둘 수도 있다.

 

 

 

지금까지는 HTTP의 메서드인 GET(단순 조회)과 POST(파라미터 전달 등..)를 구분해서 매핑하지 않았다.

하지만 @RequestMapping(method = RequestMethod)로 특정 메서드에만 호출되게 만들 수 있다.

 

만약 GET이라면 @RequestMapping(method = RequestMethod) 대신 @GetMapping, POST라면 @PostMapping을 사용해도 된다.

 

 

 

반응형
댓글
반응형
인기글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함