백앤드(스프링)

스프링 MVC

유승혁 2022. 3. 20. 12:17

 지난 글에서 스프링 MVC 구조에 대해 정말 정말 자세히 다루었다. 그리고 이 구조를 잘 이해하면 단지 외워서 스프링 MVC를 쓰는 것보다 어떤 문제가 생겼을 때 어느 부분에서 문제가 발생한건지 알아내야 할 때 정말 많은 도움이 될 것이다. 이번 글은 꽤 짧을 것이다. 구조를 이해했으니 사용법만 익히면 되니깐. 그러하니! 지난 글을 잘 이해 못했거나 못 봤다면 꼭 보고 오는 것을 추천한다. 암튼 시작해 봅시다. 앗 시작 전에 구조 그림을 한 번 더 보여줄 테니 쓰윽 훑고 내려보시길.

 

 1. @RequestMapping

 기존의 우리는 프론트 컨트롤러 클래스에 @WebServlet 어노테이션 urlPatterns 인자에 ~~~/* 를 통해 핸들러와 비즈니스 로직을 묶고 로직의 반환값과 jsp를 연결해 주었다. 

 하지만 우리의 친구 스프링께서 @RequestMapping 어노테이션을 선사해 주셨다. 어떻게 사용하냐면! 스프링 빈으로 등록된 클래스의 함수 위에 @RequestMapping("url") 을 붙이면 된다. 즉,,! 이젠 함수 단위로 동작 하게끔 할 수 있다는 것!

 이젠 코드를 보여주겠다.

@Controller
public class SpringMemberFormControllerV1 {

    @RequestMapping("/springmvc/v1/members/new-form")
    public ModelAndView process(){
        return new ModelAndView("new-form");
    }
}

 오오.. 이 짧은 코드에서 정말 다룰 이야기가 많다.. 우선 @Controller 를 보자면, @Component 어노테이션을 들고 있기에 이 클래스를 컨테이너에 빈으로 등록해준다. 뿐만 아니라 스프링 MVC에서 어노테이션 기반 컨트롤러라고 인식을 해주어 다양한 기능을 제공해준다.

 혹시 알아냈을 수도 있는데, 스프링 빈으로 등록 하는 것이 중요하니 @Controller를 지우고 @Component 어노테이션을 붙여서 하고 싶을 수도 있다. 대신 그럴려면 클래스에도 @RequestMapping 어노테이션을 붙여야 한다. 아래 처럼

@Component
@RequestMapping
public class SpringMemberFormControllerV1 {

    @RequestMapping("/springmvc/v1/members/new-form")
    public ModelAndView process(){
        return new ModelAndView("new-form");
    }
}

 

 process 위의 @RequestMapping은 꽤나 직관적이니 넘어가도록 하고, 리턴 타입을 보면 ModelAndView이다. 어랏 익숙하다면 좋다. 지난 글에서 각 핸들러의 리턴 값을 ModelView라고 했는데 그 역할을 수행 하는 것 맞다. 논리 주소와 정보 저장소인 Model을 가지고 있다. 근데 의문이 있으면 좋다. 엥 논리 주소만 줘버리면 제 아무리 똑똑한 스프링이라도 어떻게 찾아내? 설마 하나의 프로젝트에 논리 주소는 고정이야..?

 프로젝트에 보면 application.properties 가 있는데 아래와 같이 기술해 주면 논리주소를 자동으로 url로 만들어준다. 즉, 프론트 컨트롤러의 일을 처리해준다.

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

 이렇게 함으로써 url절대 주소를 알아내고 재밌는 점은 ModelAndView타입으로 저렇게 리턴하면 나머지 뷰까지의 과정은 전혀 신경쓰지 않아도 알아서 스프링이 띄어준다.

 그럼 이제 마지막으로 Model에 정보를 저장하는 방법은..? 코드로 보자.

@Controller
public class SpringMemberSaveControllerV1 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @RequestMapping("/springmvc/v1/members/save")
    public ModelAndView process(HttpServletRequest request, HttpServletResponse response){
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username,age);
        memberRepository.save(member);

        ModelAndView mv = new ModelAndView("save-result");
        //mv.getModel.put("member", member);
        mv.addObject("member",member);
        return mv;
    }
}

 직관적으로 보이듯이 ModelAndView 로 선언한 mv의 메서드 addObject() 를 이용해서 <String,Object> 꼴로 저장하면 된다. 아니면 주석 부분대로 mv.getModel.put()함수를 이용해도 좋다. 근데 귀찮으니 위 방법을 추천!

 

2. 통합

아마 눈치 챘다면 또 한 가지는 이전 방식에서는 프론트 컨트롤러에서 HttpServlet를 상속 받아 service 함수를 @Override를 통해 매핑된 url이 들어오게 되면 자동으로 service 함수를 실행하게끔 했다.

 하지만! 이제는! 함수에 url을 매핑하다 보니 사실 상 함수 명을 자유롭게 붙일 수 있게 되었다. 그렇다면..? 자연스럽게도 하나의 클래스에 모든 함수들을 매핑해놔 버릴 수도 있다. 아래처럼!

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

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

    @RequestMapping
    public ModelAndView members(){
        List<Member> members = memberRepository.findAll();

        ModelAndView mv = new ModelAndView("members");
        mv.addObject("members", members);
        return mv;
    }
    @RequestMapping("/save")
    public ModelAndView save(HttpServletRequest request, HttpServletResponse response){
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username,age);
        memberRepository.save(member);

        ModelAndView mv = new ModelAndView("save-result");
        mv.addObject("member",member);
        return mv;
    }
}

 클래스에 @RequestMapping 어노테이션을 생략해도 되지만 붙인 이유는 함수들의 @RequestMapping 어노테이션 인자를 보면 직관적으로 알 수 있을 것이다. url이 겹치는 부분은 클래스 단위로 묶고 함수들에서는 저렇게 간단하게 할 수 있다. 더욱이나 만약 클래스 단위의 url을 그대로 사용하는 함수가 있다면은 members()함수 처럼 @RequestMapping 어노테이션의 인자를 생략 할 수 있다.

 또한 지금 껏 놓친 부분이 있을 텐데, 아시다시피 이 MVC는 HTTP 기반이다. HTTP는 url 정보를 기반으로 통신하지만, 그만큼 중요한 것은 HTTP 메서드이다.

 newForm()함수에서 볼 수 있듯이, urlPattern은 value=''에 대입하고 메서드는 method= 를 통해 지정 한다. 만약 지정 하지 않는 다면 어떤 메서드든 다 포용 한다는 것이다 즉 save 메서드는 GET 메서드로 오든, POST 메서드로 오든 다 저 함수를 실행하게 된다.

 

3. 더 단순하게!

 지금까지 안 다룬 부분이 있다. 바로 어뎁터! 지난 글의 5과정에서 V3과 V4를 동시에 사용 할 수 있게 했다. 바로 어뎁터를 통해. 스프링 MVC는 이 어뎁터도 제공한다. 무슨 뜻이냐면, @RequestMapping 함수의 리턴 값을 String 타입으로 하면 된다. 뿐만 아니라 더 많은 기능들을 제공한다. 아마 코드를 보면 당황 스럽겠지만, 찬찬히 따라오길 바란다.

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

    @GetMapping("/new-form")
    public String newForm(){
        return "new-form";
    }

    @GetMapping
    public String members(Model model){
        List<Member> members = memberRepository.findAll();

        model.addAttribute("members",members);
        return "members";
    }
    @PostMapping("/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";
    }
}

 어머낫, 함수들의 리턴 타입들이 다 String으로서, 논리주소임을 알 수 있다. 또한 이젠 @RequestMapping이 보이지 않을 텐데, @GetMapping과 @PostMapping이 그것이다. 위에서 @RequestMapping 어노테이션의 method인자를 통해 HTTP 메서드를 지정 하는 것을 볼 수 있었을 텐데, 그 둘을 합치면 @GetMapping이 되는 것이야~~ @PostMapping도 마찬거지

 또한 함수들의 인자를 보면 꽤나 흥미로운데 Model이라는 인자를 넘겨받는다. 지난 글의 V4와 비슷한 것인데 ModelAndView타입으로 리턴하지 않기에 인자로 Model을 넘겨받아 저장 할 값들을 저장해주기에 논리주소만 반환해도 되는 것이다.

 save() 함수의 인자를 보게되면, 꽤나 직관적인데 기존에는 request.getParameter()함수를 통해 값들을 꺼내 썼다면 그러한 과정 없이 @RequestParam("~~~") 어노테이션을 파라미터에 붙여 사용하면 된다. ~~~ 부분은 request에 저장되어있는 <String,Object>map의 String 값이 된다. 너무 좋은 건 age처럼 파라미터의 타입에 맞추어 어노테이션이 값을 꺼내준다는 것이다!

 

 마무리

 캬 역시 정말 스프링 이래서 다들 스프링 스프링 하는 것이다. 너무나 개발자들에게 편리한 기능들을 정말 많이 제공해준다. 이런 기능들을 잘 익혀 우리 프랑스 얼음 왕국의 국민들은 개발에 많은 도움을 받길 바란다.

 다시한번 강조하지만, 3번째 문단 내용을 단순히 익히기 보단 지난 글을 꼭 익혀 스프링의 꽃인 MVC 부분을 깊이 있게 이해하길 바란다! 

 

'백앤드(스프링)' 카테고리의 다른 글

요청 매핑과 요청에 대하여  (0) 2022.05.10
로오오오오기이이이잉 (로깅!)  (0) 2022.05.10
MVC를 만들어 보자!  (0) 2022.03.17
서서블블릿릿  (0) 2022.03.09
HTTP 총 정리 3편  (0) 2022.02.26