✏️ Spring MVC 시작하기

박상민·2023년 7월 25일
0

SpringMVC

목록 보기
4/11
post-thumbnail

스프링이 제공하는 컨트롤러는 애노테이션 기반으로 동작해서, 매우 유연하고 실용적이다.

@RequestMapping
스프링은 애노테이션을 활용한 매우 유연하고, 실용적인 컨트롤러를 만들었는데 이것이 바로 @RequestMapping 애노테이션을 사용하는 컨트롤러이다.

  • RequestMappingHandlerMapping
  • RequestMappingHandlerAdapter

@ReqeustMapping 기반의 스프링 MVC Controller를 작성하면서 V1부터 V3까지 개선하는 과정을 작성해보려고 한다.

📌 V1 - 개선 전

✔︎ SpringMemberFormControllerV1 - 회원 등록 폼

package hello.servlet.web.springmvc.v1;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class SpringMemberFormControllerV1 {

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

@Controller

  • 내부에 @Component 애노테이션이 있어서 스프링이 자동으로 스프링 빈으로 등록한다.
  • Spring MVC에서 애노테이션 기반 Controller로 인식한다.

RequestMappingHandlerMapping은 스프링 빈 중에서 @RequestMapping or @Controller가 클래스 레벨에 붙어 있는 경우 매핑 정보로 인식한다.
그러므로 다음 코드도 동일하게 동작한다.

package hello.servlet.web.springmvc.v1;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Component //Component Scan을 통해 스프링 빈으로 등록
@RequestMapping
public class SpringMemberFormControllerV1 {
    @RequestMapping("/springmvc/v1/members/new-form")
    public ModelAndView process() {
        return new ModelAndView("new-form");
    }
}

✔︎ SpringMemberSaveControllerV1 - 회원 저장

package hello.servlet.web.springmvc.v1;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;
import java.util.Map;

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

    @RequestMapping("/springmvc/v1/members")
    public ModelAndView process() {
        List<Member> members = memberRepository.findAll();

        ModelAndView mv = new ModelAndView("members");
        mv.addObject("members", members);
        return mv;
    }
}

mv.addObject("member", member)

  • 스프링이 제공하는 ModelAndView를 통해 Model 데이터를 추가할 때는 addObject()를 사용하면 된다. 이 데이터는 이후 View를 렌더링 할 때 사용된다.

✔︎ SpringMemberListControllerV1 - 회원 목록

package hello.servlet.web.springmvc.v1;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

@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.addObject("member", member);
        return mv;

    }
}

V1은 각 Controller가 따로 분리되어 있어 한 눈에 파악하기가 어렵다. V2에서는 이 부분을 개선해보겠다.

📌 V2 - 컨트롤러 통합

V1의 각 Controller@RequestMapping를 보면 클래스 단위가 아닌 메서드 단위에 적용되어 있다. 그러므로 Controller Class를 유연하게 하나로 통합할 수 있다.

package hello.servlet.web.springmvc.v2;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

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

    @RequestMapping("/new-form")
    public ModelAndView newForm() {
        return new ModelAndView("new-form");
    }

    @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
    public ModelAndView members() {
        List<Member> members = memberRepository.findAll();

        ModelAndView mv = new ModelAndView("members");
        mv.addObject("members", members);
        return mv;
    }
}

클래스 단위에 @RequestMapping("/springmvc/v2/members")를 작성함으로서 중복되는 부분도 제거하였다. 그런데 여전히 개발자가 ModelView를 직접 생성해서 반환해야 한다는 불편한 부분이 존재한다. 개선할 방법이 없을까?

📌 V3 - 실용적인 방식

package hello.servlet.web.springmvc.v3;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

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

    @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";
    }

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

V3에서는 V2의 불편한 부분을 개선하기 위해 다음과 같은 기능을 추가했다.

  • Model 도입
  • 각 메소드가 ModelAndView 객체가 아닌 ViewName를 직접 반환
  • @GetMapping, PostMapping 사용

Model 파라미터

  • save(), members()를 보면 Model을 파라미터로 받는 것을 확인할 수 있다. Spring MVC도 이런 편의 기능을 제공한다.

ViewName 직접 반환

  • View의 논리 이름을 반환할 수 있다.

@GetMapping, @PostMapping

  • @GetMapping@PostMapping은 다음 코드의 간략화 버전이다.
@RequestMapping(value="/...", method = RequestMethod.Get)
@RequestMapping(value="/...", method = RequestMethod.Post)
//@RequestMapping은 URL만 매칭하는 것이 아니라, HTTP Method도 함께 구분할 수 있다. 

출처
김영한님의 Spring MVC 강의

profile
스프링 백엔드를 공부중인 대학생입니다!

0개의 댓글