지난 글에서 스프링 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 |