인프런 김영한 님의 스프링 강의이며, 섹션 5 - 스프링 MVC 구조 이해를 정리한다.
자세한 설명이 궁금하면 수강을 권장한다.
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원
www.inflearn.com
스프링 MVC 시작하기
스프링이 제공하는 컨트롤러는 애노테이션 기반으로 동작해서, 매우 유연하고 실용적이다.
과거에는 애노테이션이 없었다, 스프링도 초반엔 xml 방식으로 컨트롤러를 제공했었는데 당시엔 스트럿츠 외에도 여러 프레임워크가 있었다.
하지만 애노테이션 기반의 컨트롤러가 등장하면서 스프링 완승으로 끝났다.
@RequestMapping
- RequestMappingHandlerMapping
- RequestMappingHandlerAdapter
위의 RequestMappingHandlerMapping와 RequestMappingHandlerMapping는 @RequestMapping의 앞글자를 따서 만든 이름이다. 대부분 실무의 99.9%는 이 방식의 컨트롤러를 사용한다.
이전 시간에 했던 코드를 재사용하여 스프링MVC 기반으로 하나씩 변경해보자.
@Controller
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form")
public ModelAndView process() {
return new ModelAndView("new-form");
}
}
@Controller
- 스프링이 자동으로 스프링 빈으로 등록한다. 내부에 @Component가 있다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
- 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식
@RequestMapping
- 요청 정보를 매핑한다. 해당 URL이 호출되면 이 메서드가 호출되며, 애노테이션 기반으로 동작해서 메서드의 이름은 임의로 지으면 된다.
ModelAndView
- 모델과 뷰 정보를 담아서 반환한다.
RequestMappingHandlerMapping은 @RequestMapping, @Controller가 클래스 레벨에 붙어 있으면 매핑 정보로 인식한다.
//@Controller
@Component
@RequestMapping
public class SpringMemberFormControllerV1 {
...
}
@Bean 방식으로 설정해도 작동한다.
@Bean
SpringMemberFormControllerV1 newform() {
return new SpringMemberFormControllerV1();
}
하지만 매번 선언하기 불편하니 깔끔하게 @Controller를 애용하자.
@Controller
public class SpringMemberSaveControllerV1 {
private final 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;
}
}
@Controller
public class SpringMemberListControllerV1 {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/springmvc/v1/members")
public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", memberRepository.findAll());
return mv;
}
}
모델을 추가할 때에는 ModelAndView.addObject(key, value)를 이용하면 된다.
정상적으로 동작하니 다음으로 넘어가자.
스프링 MVC - 컨트롤러 통합
@RequestMapping 위치를 보면 메소드 레벨인걸 알 수 있다.
컨트롤러를 여러개 만들 필요 없이 하나로 통합할 수 있다.
@Controller
public class SpringMemberControllerV2 {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/springmvc/v2/members")
public ModelAndView members(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", memberRepository.findAll());
return mv;
}
@RequestMapping("/springmvc/v2/members/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("/springmvc/v2/members/new-form")
public ModelAndView newForm() {
return new ModelAndView("new-form");
}
}
하나로 통합된 모습.
그런데 매핑 경로를 보면 /spring/v2/members 중복되는 걸 볼 수 있다.
이 중복을 해결하는 방법은 간단하다. 클레스 레벨에도 @RequestMapping(중복경로)를 추가하면 된다.
@Controller
@RequestMapping("/springmvc/v2/members")
public class SpringMemberControllerV2 {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping
public ModelAndView members(HttpServletRequest request, HttpServletResponse response) {
...
}
@RequestMapping("/save")
public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
...
}
@RequestMapping("/new-form")
public ModelAndView newForm() {
...
}
}
조합
클래스 레벨과 메소드 레벨을 이용해서 중복을 제거한 걸 조합이라 한다.
클래스 레벨 @RequestMapping("/springmvc/v2/members")
- 메서드 레벨 @RequestMapping("") -> /springmvc/v2/members
- 메서드 레벨 @RequestMapping("/save") -> /springmvc/v2/members/save
- 메서드 레벨 @RequestMapping("/new-form") -> /springmvc/v2/members/new-form
스프링 MVC - 실용적인 방식
이전까지 보면서 매번 반복되는 게 더 있었다.
new ModelAndView <-- 매번 반환
이걸 개선한 방식이 존재한다.
@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping
public String members(Model model) {
model.addAttribute("members", memberRepository.findAll());
return "members";
}
@RequestMapping("/save")
public String save(
@RequestParam("usename") 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("/new-form")
public String newForm() {
return "new-form";
}
}
다양한 변화가 존재한다.
1. ModelAndView 제거.
- 뷰네임을 반환하는 방식으로 변경
- 모델은 Model 파라미터로 받음
2. @RequestParam 사용 (자세한 원리는 나중에)
- HttpServletRequest.getParameter로 받아온 데이터를 직접 관리했지만, @RequestParam(key)를 이용해서 편하게 파라미터를 관리
추가적으로 GET, POST 호출 방식을 제한 걸 수 있다.
@RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(...) {
...
}
이전까지는 GET, POST 상관없이 호출이 되었지만 이제 save는 POST 방식으로만 호출이 된다.
여기서 더 나아가
@RequestMapping(value = "/save", method = RequestMethod.POST)를 한번 더 개선된 기능이 나온다.
@PostMapping, @GetMapping으로 위의 기능을 하나로 합친 애노테이션이다.
@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@GetMapping
public String members(Model model) {
...
}
@PostMapping("/save")
public String save(
...
}
@GetMapping("/new-form")
public String newForm() {
...
}
}
해당 애노테이션 내부에 @RequestMapping이 포함된 모습도 확인할 수 있다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {
'교육 및 인강 > 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 카테고리의 다른 글
스프링 MVC 기본 기능, HTTP 요청 - 기본, 헤더 조회, 파라미터 (0) | 2021.07.20 |
---|---|
스프링 MVC 기본 기능 - 로깅 알아보기, 요청 매핑, API 예시 (0) | 2021.07.19 |
스프링 MVC 이해 - 전체 구조, 핸들러 매핑과 핸들러 어댑터, 뷰 리졸버 (0) | 2021.07.13 |
MVC 프레임워크 만들기 - 실용적인 컨트롤러 (v4), 유연한 컨트롤러 (v5) (0) | 2021.07.13 |
MVC 프레임워크 만들기 - View 분리(v2), Model 추가(v3) (0) | 2021.07.05 |