기존 JSP/Servlet을 통해 MVC패턴 구현
1. HomeServlet을 통해 요청하여 index.jsp를 띄움
@WebServlet("/index")
public class HomeServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher dp = req.getRequestDispatcher("/WEB-INF/index.jsp");
dp.forward(req, resp);
}
}
@WebServlet("/chap01/join")
public class RegisterServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String viewName = "/WEB-INF/views/reg_form.jsp";
RequestDispatcher dp
= req.getRequestDispatcher(viewName);
dp.forward(req, resp);
}
}
3. SaveServlet를 통해 저장하고 조회화면을 띄워줌 (조회화면으로 리다이렉트)
- 일반적으로 데이터를 변경하는 작업인 경우(예: 저장, 수정, 삭제 등), 보통 redirect를 사용
- 데이터를 변경한 후에 리다이렉트를 사용하면, 사용자가 새로고침을 하더라도 중복 데이터 변경을 방지
- 또한 새로고침을 눌렀을 때 경고메시지나 에러 방지
@WebServlet("/chap01/save")
public class SaveServlet extends HttpServlet {
private MemberMemoryRepo repo = MemberMemoryRepo.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userName = req.getParameter("userName");
String account = req.getParameter("account");
String password = req.getParameter("password");
Member member = new Member(account, password, userName);
repo.save(member);
resp.sendRedirect("/chap01/show");
}
}
4. ShowServlet을 통해 JSP를 통해 화면을 렌더링하여 띄워줌
@WebServlet("/chap01/show")
public class ShowServlet extends HttpServlet {
private MemberMemoryRepo repo = MemberMemoryRepo.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<Member> memberList = repo.findAll();
req.setAttribute("memberList", memberList);
String viewName = "/WEB-INF/views/m-list.jsp";
RequestDispatcher dp = req.getRequestDispatcher(viewName);
dp.forward(req, resp);
}
}
기존 JSP/Servlet을 통해 MVC패턴 구현의 문제점
- 하나의 요청 당 하나의 Servlet을 만들어야 함
스프링을 통해 MVC패턴 구현
V1) 요청하는 Servlet을 하나로 통합
- FrontControllerV1이라는 통합 Servlet에서 필요한 Controller(이전 Servlet)들을 Map에 넣어서 요청 uri를 key로하는 value에 해당하는 각 Controller을 실행
@WebServlet("/chap02/v1/*")
public class FrontControllerV1 extends HttpServlet {
private Map<String, ControllerV1> controllerMap = new HashMap<>();
public FrontControllerV1() {
controllerMap.put("/chap02/v1/join", new JoinController());
controllerMap.put("/chap02/v1/save", new SaveController());
controllerMap.put("/chap02/v1/show", new ShowController());
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("브라우저 요청이 들어옴!!");
String uri = req.getRequestURI();
System.out.println("요청 uri = " + uri);
ControllerV1 controller = controllerMap.get(uri);
controller.process(req, resp);
}
}
- 나머지 join, save, show에 대한 Controller는 이 전 Servlet들과 동일
- JoinController
public class JoinController implements ControllerV1 {
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String viewName = "/WEB-INF/views/v1/reg_form.jsp";
RequestDispatcher dp
= request.getRequestDispatcher(viewName);
dp.forward(request, response);
}
}
public class SaveController implements ControllerV1{
private MemberMemoryRepo repo = MemberMemoryRepo.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userName = request.getParameter("userName");
String account = request.getParameter("account");
String password = request.getParameter("password");
Member member = new Member(account, password, userName);
repo.save(member);
response.sendRedirect("/chap02/v1/show");
}
}
public class ShowController implements ControllerV1{
private MemberMemoryRepo repo = MemberMemoryRepo.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> memberList = repo.findAll();
request.setAttribute("memberList", memberList);
String viewName = "/WEB-INF/views/v1/m-list.jsp";
RequestDispatcher dp = request.getRequestDispatcher(viewName);
dp.forward(request, response);
}
}
V2) 요청하는 Servlet을 하나로 통합 + Forward, Redirect 기능을 클래스로 분리
@WebServlet("/chap02/v2/*")
public class FrontControllerV2 extends HttpServlet {
private Map<String, ControllerV2> controllerMap = new HashMap<>();
public FrontControllerV2() {
controllerMap.put("/chap02/v2/join", new JoinController());
controllerMap.put("/chap02/v2/save", new SaveController());
controllerMap.put("/chap02/v2/show", new ShowController());
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("브라우저 요청이 들어옴!!");
// 들어온 요청 구분하기
String uri = req.getRequestURI();
// 요청에 맞는 적당한 컨트롤러객체를 맵에서 꺼내기
ControllerV2 controller = controllerMap.get(uri);
View view = controller.process(req, resp);
view.render(req, resp); // 추가
}
public class View {
private String viewName;
private String prefix;
private String suffix;
private boolean redirect;
public View(String viewName) {
this.prefix = "/WEB-INF/views/";
this.suffix = ".jsp";
if(!isRedirect(viewName)) {
this.viewName = prefix +viewName + suffix;
} else {
this.viewName = viewName.substring(viewName.indexOf(":") + 1);
}
}
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if(!this.redirect) {
RequestDispatcher dp = request.getRequestDispatcher(viewName);
dp.forward(request, response);
} else {
response.sendRedirect(viewName);
}
}
private boolean isRedirect(String viewName) {
this.redirect = viewName.startsWith("redirect:");
return this.redirect;
}
}
- 이렇게 코드를 작성하게되면 Join, Save, ShowController에서 따로 forward, redirect 하지않고 아래코드처럼 View로 경로만 리턴해주면 View에서 알아서 처리해줌!
- JoinController
public class JoinController implements ControllerV2 {
@Override
public View process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
return new View("v2/reg_form");
}
}
public class SaveController implements ControllerV2 {
private MemberMemoryRepo repo = MemberMemoryRepo.getInstance();
@Override
public View process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userName = request.getParameter("userName");
String account = request.getParameter("account");
String password = request.getParameter("password");
Member member = new Member(account, password, userName);
repo.save(member);
return new View("redirect:/chap02/v2/show");
}
}
public class ShowController implements ControllerV2 {
private MemberMemoryRepo repo = MemberMemoryRepo.getInstance();
@Override
public View process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> memberList = repo.findAll();
request.setAttribute("memberList", memberList);
return new View("v2/m-list");
}
}
V3) 요청하는 Servlet을 하나로 통합 + Forward, Redirect 기능을 클래스로 분리 + 요청정보(req)나 응답정보(resp)를 처리를 외부로 위임
package com.study.springstudy.webservlet.chap02.v3.controller;
import com.study.springstudy.webservlet.ModelAndView;
import com.study.springstudy.webservlet.View;
import java.util.Map;
public interface ControllerV3 {
ModelAndView process(Map<String, String> paramMap);
}
@WebServlet("/chap02/v3/*")
public class FrontControllerV3 extends HttpServlet {
private Map<String, ControllerV3> controllerMap = new HashMap<>();
public FrontControllerV3() {
controllerMap.put("/chap02/v3/join", new JoinController());
controllerMap.put("/chap02/v3/save", new SaveController());
controllerMap.put("/chap02/v3/show", new ShowController());
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI();
// 요청에 맞는 적당한 컨트롤러객체를 맵에서 꺼내기
ControllerV3 controller = controllerMap.get(uri);
// 요청 파라미터를 전부 읽어서 맵에 담아 리턴하는 메서드 호출
// 요청 파라미터: 클라이언트가 서버로 전달한 데이터
// ?name=xfzf&age=30
Map<String, String> parameterMap = createParamMap(req);
ModelAndView mv = controller.process(parameterMap);
// model데이터 jsp로 보내기
modelToView(req, mv);
// view 렌더링
mv.render(req, resp);
}
private void modelToView(HttpServletRequest req, ModelAndView mv) {
Map<String, Object> modelMap = mv.getModel().getModelMap();
for (String key : modelMap.keySet()) {
req.setAttribute(key, modelMap.get(key));
}
}
private Map<String, String> createParamMap(HttpServletRequest req) {
Enumeration<String> parameterNames = req.getParameterNames();
Map<String, String> paramMap = new HashMap<>();
// parameterNames가 있는 동안에 paramMap에 key,value를 모두 넣어서 리턴
while (parameterNames.hasMoreElements()) {
String key = parameterNames.nextElement();
String value = req.getParameter(key);
paramMap.put(key, value);
}
return paramMap;
}
}
public class ShowController implements ControllerV3 {
private MemberMemoryRepo repo = MemberMemoryRepo.getInstance();
@Override
public ModelAndView process(Map<String, String> paramMap) {
List<Member> memberList = repo.findAll();
ModelAndView modelAndView = new ModelAndView("v3/m-list");
modelAndView.addAttribute("memberList", memberList);
return modelAndView;
}
}
public class Model {
private Map<String, Object> model = new HashMap<>();
public void addAttribute(String key, Object value) {
model.put(key, value);
}
public Map<String, Object> getModelMap() {
return model;
}
}
public class ModelAndView {
private View view;
private Model model;
public ModelAndView(String viewName) {
this.view = new View(viewName);
}
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.view.render(request, response);
}
public void addAttribute(String key, Object value) {
this.model.addAttribute(key, value);
}
public View getView() {
return view;
}
public void setView(View view) {
this.view = view;
}
public Model getModel() {
return model;
}
public void setModel(Model model) {
this.model = model;
}
}
V4) 요청하는 Servlet을 하나로 통합 + Forward, Redirect 기능을 클래스로 분리 + 요청정보(req)나 응답정보(resp)를 처리를 외부로 위임 + 경로를 문자열로 리턴 (ModelAndView처리를 알아서 해줘)
package com.study.springstudy.webservlet.chap02.v4.controller;
import com.study.springstudy.webservlet.Model;
import com.study.springstudy.webservlet.ModelAndView;
import java.util.Map;
public interface ControllerV4 {
String process(Map<String, String> paramMap, Model model);
}
V5
- 스프링을 통해 지금까지 했던 것들을 간편하게 쓸 수 있음
package com.study.springstudy.webservlet.chap02.v5;
import com.study.springstudy.webservlet.MemberMemoryRepo;
import com.study.springstudy.webservlet.entity.Member;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/chap02/v5/*")
public class MemberController {
private MemberMemoryRepo repo = MemberMemoryRepo.getInstance();
@RequestMapping("/join")
public String join() {
return "v5/reg_form";
}
// 회원 저장을 하는 요청
@RequestMapping("/save")
public String save(Member member) {
repo.save(member);
return "redirect:/chap02/v5/show";
}
// 회원 목록을 조회하는 요청
@RequestMapping("/show")
public String show(Model model) {
List<Member> memberList = repo.findAll();
model.addAttribute("memberList", memberList);
return "v5/m-list";
}
// 회원 삭제 처리
@RequestMapping("/delete")
public String delete(String account) {
repo.delete(account);
return "redirect:/chap02/v5/show";
}
// 회원 개별조회 처리
@RequestMapping("/detail")
public String detail(String account, Model model) {
Member member = repo.findOne(account);
model.addAttribute("mem", member);
return "v5/detail";
}
}