관리 메뉴

나구리의 개발공부기록

스프링 MVC - 구조이해, 스프링 MVC (시작하기/컨트롤러 통합하기/실용적인 방식) 본문

인프런 - 스프링 완전정복 코스 로드맵/스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

스프링 MVC - 구조이해, 스프링 MVC (시작하기/컨트롤러 통합하기/실용적인 방식)

소소한나구리 2024. 2. 29. 23:25

  출처 : 인프런 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 (유료) / 김영한님  
  유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용  

https://inf.run/Gmptq


1. 스프링 MVC - 시작하기

1) 스프링이 제공하는 컨트롤러

  • 스프링이 제공하는 컨트롤러는 애노테이션 기반으로 동작하여 매우 유연하고 실용적임
  • 과거에는 자바 언어에 애노테이션이 없었고 스프링도 처음부터 이런 유연한 컨트롤러를 제공하지 않았음

(1) @RequestMapping

  • 스프링이 제공하는 매우 유연하고 실용적인 애노테이션 기반의 컨트롤러
  • 과거에는 스프링 프레임워크가 MVC 부분이 약해서 스프링을 사용하여도 MVC웹기술은 다른 프레임워크를 사용했지만 @RequestMapping기반의 애노테이션 컨트롤러가 등장하면서 대부분의 실무에서 스프링 프레임워크를 사용함
  • @RequestMapping의 앞글자를 따서 만든 RequestMappingHandlerMapping(핸들러 매핑)RequestMappingHandlerAdapter(핸들러 어댑터)애노테이션 기반의 컨트롤러를 지원하는 가장 우선순위가 높은 핸들러 매핑과 핸들러 어댑터이며 실무에서 99.9% 이 방식의 컨트롤러를 사용

(2) SpringMemberFormControllerV1 - 회원 등록 폼

  • springmvc패키지 하위에 v1패키지 생성 후 작성
  • @Controller : 스프링이 자동으로 스프링 빈으로 등록되며 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식함
    내부에 @Component 애노테이션이 있어서 자동으로 컴포넌트 스캔의 대상이 됨
  • @RequestMapping(메서드 레벨) : 요청 정보를 매핑, 해당 URL이 호출되면 메서드가 호출됨, 애노테이션을 기반으로 동작하기 때문에 메서드의 이름은 임의로 지으면 됨
  • ModelAndView : 모델과 뷰 정보를 담아서 반환
package hello.servlet.web.springmvc.v1;

@Controller
public class SpringMemberFormController {

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

 

** 주의 - 스프링 부트 3.0 이상!

  • 스프링 부트 3.0(스프링 프레임워크 6.0)이전에는 클래스레벨에 @Component, @RequestMapping을 하면 스프링 컨트롤러로 인식하였지만 스프링 부트 3.0 이후 부터는 @Controller가 있어야 스프링 컨트롤러로 인식함
  • RequestMappingHandlerMapping에서 @RequestMapping은 더이상 인식하지 않음
  • @RequestMapping만 사용하고 직접 스프링 빈으로 등록하는 방식도 마찬가지로 사용 불가능하니 컨트롤러 사용시에는 깔끔하게 @Controller를 사용하면 됨
  • 스프링 부트 3.0 미만에서도 동작하는 V1 컨트롤러
@Component //컴포넌트 스캔을 통해 스프링 빈으로 등록
@RequestMapping
public class SpringMemberFormControllerV1 {
    // 메서드는 동일
}

@RequestMapping  // 직접 스프링 빈으로 등록하면 컴포넌트 스캔이 없어도됨
public class SpringMemberFormControllerV1 {
    // 메서드는 동일
}

 

(3) SpringMemberSaveControllerV1 - 회원 저장

  • HttpServletRequest, Response로도 매개변수를 받을 수 있음
  • mv.addObject("member", member): 스프링이 제공하는 ModelAndView를 통해 Model데이터를 추가할 때는 addObject()를 상용하면 되며 이 데이터는 이후 뷰를 렌더링할때 사용됨
package hello.servlet.web.springmvc.v1;

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

 

(4) SpringMemberListControllerV1 - 회원 목록

package hello.servlet.web.springmvc.v1;

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

 

(5) 실행

  • 애플리케이션 실행 후 각 컨트롤러에 매핑된 url로 접속해보면 정상적으로 동작함을 알 수 있음

2. 스프링 MVC - 컨트롤러 통합

1) @RequestMapping을 클래스 단위로 통합

  • @RequestMapping이 클래스 단위가 아니라 메서드 단위에 적용 되어 있음
  • 각 클래스로 나뉘어져있던 컨트롤러를 유연하게 하나의 클래스로 통합이 가능

(1) SpringMemberControllerV2

  • v2패키지를 생성 후 작성
  • 하나의 클래스로 통합이 되었지만 메서드레벨의 @RequestMapping에서 /springmvc/v2/members의 중복이 있음
  • @RequestMapping("/springmvc/v2/members/new-form")
  • @RequestMapping("/springmvc/v2/members")
  • @RequestMapping("/springmvc/v2/members/save")`
package hello.servlet.web.springmvc.v2;

@Controller
public class SpringMemberControllerV2 {
    
    // 코드 일부 생략
    
    @RequestMapping("/springmvc/v2/members/new-form")
    @RequestMapping("/new-form") // 코드 중복 제거
    public ModelAndView newform() {
        return new ModelAndView("new-form");
    }

    @RequestMapping("/springmvc/v2/members/save")
    public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
        // 메서드 구조는 동일
    }

    @RequestMapping("/springmvc/v2/members")
    public ModelAndView members() {
        // 메서드 구조는 동일
    }
}

 

(2) 조합

  • 컨트롤러 클래스를 통합하는 것을 넘어서 조합이 가능함
  • 각 메서드에 @RequestMapping(/springmvc/v2/members)라는 부분이 중복된 부분을 제거하려면 @RequestMapping 애노테이션을 클래스 레벨로 설정하여 코드 중복을 제거할 수 있음
  • 코드를 작성 후 실행해보면 모두 정상 동작함을 알 수 있음
package hello.servlet.web.springmvc.v2;

@Controller
@RequestMapping("/springmvc/v2/members")	// 클래스 단위로 적용
public class SpringMemberControllerV2 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @RequestMapping("/new-form")    // /spring/v2/members의 중복이 제거됨
    public ModelAndView newForm() {
        return new ModelAndView("new-form");
    }

    @RequestMapping("/save")    // /spring/v2/members의 중복이 제거됨
    public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
        // 메서드 구조는 동일

    }

    @RequestMapping    // /spring/v2/members의 중복이 제거됨
    public ModelAndView members() {
        // 메서드 구조는 동일
    }
}

3. 스프링 MVC - 실용적인 방식

1) 실무에서 주로 사용하는 방식

  • MVC 프레임워크 만들기를 하면서 v3 버전의 Controller는 ModelView를 개발자가 직접 생성해서 반환해서 불편했던 것을 v4 버전을 만들면서 실용적으로 개선했었음
  • 스프링 MVC는 개발자가 편리하게 개발할 수 있도록 많은 편의기능을 제공하며 실무에서는 지금 설명하는 방식을 주로 사용함

(1) SpringMemberControllerV3

  • v3패키지 생성 후 작성
  • Model 파라미터 : save()와 members() 에서 Model을 파라미터로 받는데 스프링MVC에서 이러한 편의 기능을 제공함
  • ViewName 반환 : 뷰의 논리 이름을 스트링으로 반환할 수 있음
  • @RequestParam 사용 : HTTP 요청 파라미터를 받음,
    request.getParameter와 거의 같은 코드라고 보면 되며 GET 쿼리파라미터, POST Form 방식을 모두 지원함
  • @RequestMapping -> @GetMapping, @PostMapping으로 변경:
    @RequestMapping은 URL만 매칭하는 것이 아니라 HTTP Method도 구분할 수 있음
    예를 들어 HTTP Method가 GET인 경우를 모두 만족하는 매핑을 하려면 @RequestMapping(value = "/new-form", method = RequestMethod.GET)처럼 처리하면 됨
    그러나 이것을 @GetMapping, @PostMapping으로 더 편리하게 사용가능하며 Put,Delete,Patch 등 모두 애노테이션으로 구현되어있음
package hello.servlet.web.springmvc.v3;

@Controller
@RequestMapping("/springmvc/v3/members") // 클래스 레벨에 @RequestMapping을 적용하여 코드 중복을 제거
public class SpringMemberControllerV3 {

    private final MemberRepository memberRepository = MemberRepository.getInstance();

//    @RequestMapping(value = "/new-form", method = RequestMethod.GET)
    @GetMapping("/new-form") // 위와 동일한 애노테이션, HTTP Method가 GET이여야 함
    public String newform() { // 인터페이스가 아닌 String으로 반환
        return "new-form";
    }

//    @RequestMapping(value= "/save", method = RequestMethod.POST) // 매핑 중복 제거
    @PostMapping("/save") // HTTP Method가 POST여야 함
    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";
    }

//    @RequestMapping(method = RequestMethod.GET) // 매핑 중복 제거
    @GetMapping
    public String members(Model model) {
        List<Member> members = memberRepository.findAll();

        model.addAttribute("members", members);
        return "members";
    }
}