[SPRING] 스프링 MVC 구조 이해 (v1~v3)
뷰 리졸버
resources
>application.properties
에 아래 코드를 추가한다.
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
스프링 부트는 InternalResourceViewResolver
라는 뷰 리졸버를 자동으로 등록하는데, 이때 application.properties
에 등록한 spring.mvc.view.prefix
, spring.mvc.view.suffix
설정 정보를 사용해서 등록한다.
💎 v1 - 스프링 MVC 시작하기
스프링이 제공하는 컨트롤러는 애노테이션 기반으로 동작해서, 매우 유연하고 실용적이다.
SpringMemberFormControllerV1 - 회원 등록 폼
@Controller // 스프링이 자동으로 스프링 빈으로 등록한다. (컴포넌트 스캔의 대상이 됨)
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form") // 요청 정보를 매핑한다. 해당 URL이 호출되면 이 메서드가 호출된다.
public ModelAndView process(){
return new ModelAndView("new-form"); // 모델과 뷰 정보를 담아서 반환한다.
}
}
@Controller
- 스프링이 자동으로 스프링 빈으로 등록한다. (내부에
@Component
애노테이션이 있어서 컴포넌트 스캔의 대상이 됨) - 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식한다.
@RequestMapping
- 요청 정보를 매핑한다.
- 해당 URL이 호출되면 이 메서드가 호출된다.
- 애노테이션을 기반으로 동작하기 때문에 메서드의 이름은 임의로 지으면 된다.
SpringMemberSaveControllerV1 - 회원 저장
@Controller
public class SpringMemberSaveControllerV1 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/springmvc/v1/members/save")
public ModelAndView process(HttpServletRequest req, HttpServletResponse res) {
String username = req.getParameter("username");
int age = Integer.parseInt(req.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
ModelAndView mv = new ModelAndView("save-result"); // 뷰의 전체 path가 아닌 논리적인 이름을 넣는다.
mv.addObject("member", member); // 스프링이 제공하는 ModelAndView를 통해 Model 데이터를 추가할 때는 ddOnject()를 사용한다. 이 데이터는 이후 뷰를 렌더링 할 때 사용된다.
return mv;
}
}
SpringMemberListControllerV1 - 회원 목록
@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;
}
}
💎 v2 - 컨트롤러 통합
@RequestMapping
을 잘 보면 클래스 단위가 아니라 메서드 단위에 적용된 것을 확인할 수 있다.
따라서 컨트롤러 클래스를 유연하게 하나로 통합할 수 있다.
SpringMemberControllerV2
@Controller
@RequestMapping("/springmvc/v2/members") // 🌟 중복 제거
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("/save") // /springmvc/v2/members/save
public ModelAndView save(HttpServletRequest req, HttpServletResponse res) {
String username = req.getParameter("username");
int age = Integer.parseInt(req.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
public ModelAndView members() {
List<Member> members = memberRepository.findAll();
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", members);
return mv;
}
}
조합
컨트롤러 클래스를 통합하는 것을 넘어서 조합도 가능하다.
@RequestMapping("/springmvc/v2/members/new-form")
@RequestMapping("/springmvc/v2/members")
@RequestMapping("/springmvc/v2/members/save")
위 코드는 /springmvc/v2/members
라는 부분에 중복이 있다.
물론 이렇게 사용해도 되지만, 클래스 레벨에 @RequestMapping
을 두어 중복을 제거할 수도 있다.
조합 결과
- 클래스 레벨
@RequestMapping("/springmvc/v2/members")
- 메서드 레벨@RequestMapping("/new-form")
-> /springmvc/v2/members/new-form
- 메서드 레벨@RequestMapping("/save")
-> /springmvc/v2/members/save
- 메서드 레벨@RequestMapping
-> /springmvc/v2/members
💎 v3 - 실용적인 방식
이전에 MVC 프레임워크 만들기에서, v3은 ModelView를 개발자가 직접 생성해서 반환했기 때문에 불편했었다. 따라서 v4를 만들면서 실용적으로 개선할 수 있었다.
실무에서는 이 방식을 주로 사용한다.
SpringMemberControllerV3
@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@GetMapping("/new-form") // GET /springmvc/v3/members/new-form
public String newForm(){
return "new-form"; // ModelAndView 객체가 아닌 String 형태로 반환한다.
}
@PostMapping("/save") // POST /springmvc/v3/members/save
public String save(@RequestParam ("username") String username, @RequestParam ("age") int age, Model model) { // 파라미터를 받기 위해 @RequestParam 사용
Member member = new Member(username, age);
memberRepository.save(member);
model.addAttribute("member", member);
return "save-result";
}
@GetMapping // GET /springmvc/v3/members
public String members(Model model) {
List<Member> members = memberRepository.findAll();
model.addAttribute("members", members);
return "members";
}
}
Model 파라미터
save()
, members()
를 보면 Model을 파라미터로 받는 것을 확인할 수 있다. 스프링 MVC도 이런 편의기능을 제공한다.
ViewName 직접 반환
뷰의 논리 이름을 반환할 수 있다.
@RequestParam 사용
스프링은 HTTP 요청 파라미터를 @RequestParam
으로 받을 수 있다.
@RequestParam("username")
은 request.getParameter("username")
와 거의 같은 코드라 생각하면 된다. 물론 GET 쿼리 파라미터, POST Form 방식을 모두 지원한다.
@RequestMapping 👉 @GetMapping, @PostMapping
@RequestMapping
은 URL만 매칭하는 것이 아니라, HTTP Method도 함께 구분할 수 있다.
예를 들어서 URL이 /new-form 이고, HTTP Method가 GET인 경우를 모두 만족하는 매핑을 하려면 다음과 같이 처리하면 된다.
@RequestMapping(value = "/new-form", method = RequestMethod.GET)
이것을 @GetMapping
, @PostMapping
으로 더 편리하게 사용할 수 있다. 참고로 Get, Post, Put, Delete, Patch 모두 애노테이션이 준비되어 있다.
💛 개인 공부 기록용 블로그입니다. 👻