diff --git a/src/main/java/core/mvc/Controller.java b/src/main/java/core/mvc/Controller.java index df7b7f2ea..949439cc7 100644 --- a/src/main/java/core/mvc/Controller.java +++ b/src/main/java/core/mvc/Controller.java @@ -4,5 +4,5 @@ import javax.servlet.http.HttpServletResponse; public interface Controller { - String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception; + ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) throws Exception; } diff --git a/src/main/java/core/mvc/DispatcherServlet.java b/src/main/java/core/mvc/DispatcherServlet.java index 59e1a23ee..6e4001f4a 100644 --- a/src/main/java/core/mvc/DispatcherServlet.java +++ b/src/main/java/core/mvc/DispatcherServlet.java @@ -1,56 +1,40 @@ package core.mvc; -import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - @WebServlet(name = "dispatcher", urlPatterns = "/", loadOnStartup = 1) public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger logger = LoggerFactory.getLogger(DispatcherServlet.class); - private static final String DEFAULT_REDIRECT_PREFIX = "redirect:"; private RequestMapping rm; @Override - public void init() throws ServletException { + public void init() { rm = new RequestMapping(); rm.initMapping(); } @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException { String requestUri = req.getRequestURI(); logger.debug("Method : {}, Request URI : {}", req.getMethod(), requestUri); Controller controller = rm.findController(requestUri); try { - String viewName = controller.execute(req, resp); - if (viewName != null) { - move(viewName, req, resp); - } + ModelAndView modelAndView = controller.execute(req, resp); + View view = modelAndView.getView(); + view.render(modelAndView.getModel(), req, resp); } catch (Throwable e) { - logger.error("Exception : {}", e); + logger.error("Exception: ", e); throw new ServletException(e.getMessage()); } } - - private void move(String viewName, HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - if (viewName.startsWith(DEFAULT_REDIRECT_PREFIX)) { - resp.sendRedirect(viewName.substring(DEFAULT_REDIRECT_PREFIX.length())); - return; - } - - RequestDispatcher rd = req.getRequestDispatcher(viewName); - rd.forward(req, resp); - } } diff --git a/src/main/java/core/mvc/ForwardController.java b/src/main/java/core/mvc/ForwardController.java index 83c9d89f2..90a701fa7 100644 --- a/src/main/java/core/mvc/ForwardController.java +++ b/src/main/java/core/mvc/ForwardController.java @@ -1,20 +1,23 @@ package core.mvc; +import next.controller.AbstractController; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public class ForwardController implements Controller { - private String forwardUrl; +public class ForwardController extends AbstractController { + private final String forwardUrl; public ForwardController(String forwardUrl) { this.forwardUrl = forwardUrl; + // TODO: null 체크를 생성자 첫 줄에서 하는 건 어떨까? + Objects.requireNonNull() if (forwardUrl == null) { throw new NullPointerException("forwardUrl is null. 이동할 URL을 입력하세요."); } } @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { - return forwardUrl; + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { + return jspView(forwardUrl); } } diff --git a/src/main/java/core/mvc/JsonView.java b/src/main/java/core/mvc/JsonView.java new file mode 100644 index 000000000..37184f72c --- /dev/null +++ b/src/main/java/core/mvc/JsonView.java @@ -0,0 +1,31 @@ +package core.mvc; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +public class JsonView implements View { + + @Override + public void render(final Map model, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + response.setContentType("application/json;charset=UTF-8"); + PrintWriter writer = response.getWriter(); + writer.print(objectMapper.writeValueAsString(model)); + } + + private Map createModelFromRequest(final HttpServletRequest request) { + Map models = new HashMap<>(); + Enumeration attributeNames = request.getAttributeNames(); + while (attributeNames.hasMoreElements()) { + String name = attributeNames.nextElement(); + models.put(name, request.getAttribute(name)); + } + return models; + } +} diff --git a/src/main/java/core/mvc/JspView.java b/src/main/java/core/mvc/JspView.java new file mode 100644 index 000000000..52acbdeea --- /dev/null +++ b/src/main/java/core/mvc/JspView.java @@ -0,0 +1,36 @@ +package core.mvc; + +import javax.servlet.RequestDispatcher; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class JspView implements View { + + private static final String DEFAULT_REDIRECT_PREFIX = "redirect:"; + + private final String viewName; + + public JspView(final String viewName) { + Objects.requireNonNull(viewName, "viewName is null."); + this.viewName = viewName; + } + + @Override + public void render(final Map model, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + if (viewName.startsWith(DEFAULT_REDIRECT_PREFIX)) { + response.sendRedirect(viewName.substring(DEFAULT_REDIRECT_PREFIX.length())); + return; + } + + Set keys = model.keySet(); + for (String key : keys) { + request.setAttribute(key, model.get(key)); + } + + RequestDispatcher rd = request.getRequestDispatcher(viewName); + rd.forward(request, response); + } +} diff --git a/src/main/java/core/mvc/ModelAndView.java b/src/main/java/core/mvc/ModelAndView.java new file mode 100644 index 000000000..0bef62053 --- /dev/null +++ b/src/main/java/core/mvc/ModelAndView.java @@ -0,0 +1,27 @@ +package core.mvc; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class ModelAndView { + private final View view; + private final Map model = new HashMap<>(); + + public ModelAndView(final View view) { + this.view = view; + } + + public ModelAndView addObject(final String attributeName, final Object attributeValue) { + model.put(attributeName, attributeValue); + return this; + } + + public View getView() { + return view; + } + + public Map getModel() { + return Collections.unmodifiableMap(model); + } +} diff --git a/src/main/java/core/mvc/View.java b/src/main/java/core/mvc/View.java new file mode 100644 index 000000000..c7d814ee3 --- /dev/null +++ b/src/main/java/core/mvc/View.java @@ -0,0 +1,9 @@ +package core.mvc; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +public interface View { + void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception; +} diff --git a/src/main/java/next/controller/AbstractController.java b/src/main/java/next/controller/AbstractController.java new file mode 100644 index 000000000..672996d50 --- /dev/null +++ b/src/main/java/next/controller/AbstractController.java @@ -0,0 +1,16 @@ +package next.controller; + +import core.mvc.Controller; +import core.mvc.JsonView; +import core.mvc.JspView; +import core.mvc.ModelAndView; + +public abstract class AbstractController implements Controller { + protected ModelAndView jspView(String forwardUrl) { + return new ModelAndView(new JspView(forwardUrl)); + } + + protected ModelAndView jsonView() { + return new ModelAndView(new JsonView()); + } +} diff --git a/src/main/java/next/controller/HomeController.java b/src/main/java/next/controller/HomeController.java index d01c085a3..1100b7be7 100644 --- a/src/main/java/next/controller/HomeController.java +++ b/src/main/java/next/controller/HomeController.java @@ -1,16 +1,18 @@ package next.controller; +import core.mvc.ModelAndView; +import next.dao.QuestionDao; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import core.mvc.Controller; -import next.dao.QuestionDao; +public class HomeController extends AbstractController { -public class HomeController implements Controller { @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { QuestionDao questionDao = new QuestionDao(); req.setAttribute("questions", questionDao.findAll()); - return "home.jsp"; + return jspView("home.jsp") + .addObject("questions", questionDao.findAll()); } } diff --git a/src/main/java/next/controller/qna/AddAnswerController.java b/src/main/java/next/controller/qna/AddAnswerController.java index e9c9acd40..2067acb22 100644 --- a/src/main/java/next/controller/qna/AddAnswerController.java +++ b/src/main/java/next/controller/qna/AddAnswerController.java @@ -1,34 +1,28 @@ package next.controller.qna; -import java.io.PrintWriter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import core.mvc.ModelAndView; +import next.controller.AbstractController; +import next.dao.AnswerDao; +import next.model.Answer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.ObjectMapper; - -import core.mvc.Controller; -import next.dao.AnswerDao; -import next.model.Answer; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -public class AddAnswerController implements Controller { +public class AddAnswerController extends AbstractController { private static final Logger log = LoggerFactory.getLogger(AddAnswerController.class); @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { + // TODO: Answer에 Builder 패턴 적용, 아래에서 사용 Answer answer = new Answer(req.getParameter("writer"), req.getParameter("contents"), Long.parseLong(req.getParameter("questionId"))); log.debug("answer : {}", answer); AnswerDao answerDao = new AnswerDao(); Answer savedAnswer = answerDao.insert(answer); - ObjectMapper mapper = new ObjectMapper(); - resp.setContentType("application/json;charset=UTF-8"); - PrintWriter out = resp.getWriter(); - out.print(mapper.writeValueAsString(savedAnswer)); - return null; + return jsonView() + .addObject("answer", savedAnswer); } } diff --git a/src/main/java/next/controller/qna/DeleteAnswerController.java b/src/main/java/next/controller/qna/DeleteAnswerController.java index 169e3a617..a946e33ca 100644 --- a/src/main/java/next/controller/qna/DeleteAnswerController.java +++ b/src/main/java/next/controller/qna/DeleteAnswerController.java @@ -1,28 +1,24 @@ package next.controller.qna; -import java.io.PrintWriter; +import core.mvc.ModelAndView; +import next.controller.AbstractController; +import next.dao.AnswerDao; +import next.model.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.fasterxml.jackson.databind.ObjectMapper; +public class DeleteAnswerController extends AbstractController { + private static final Logger log = LoggerFactory.getLogger(DeleteAnswerController.class); -import core.mvc.Controller; -import next.dao.AnswerDao; -import next.model.Result; - -public class DeleteAnswerController implements Controller { @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { Long answerId = Long.parseLong(req.getParameter("answerId")); AnswerDao answerDao = new AnswerDao(); - answerDao.delete(answerId); - - ObjectMapper mapper = new ObjectMapper(); - resp.setContentType("application/json;charset=UTF-8"); - PrintWriter out = resp.getWriter(); - out.print(mapper.writeValueAsString(Result.ok())); - return null; + return jsonView() + .addObject("result", Result.ok()); } } diff --git a/src/main/java/next/controller/qna/ShowController.java b/src/main/java/next/controller/qna/ShowController.java index bdb26e683..2e0b64795 100644 --- a/src/main/java/next/controller/qna/ShowController.java +++ b/src/main/java/next/controller/qna/ShowController.java @@ -1,20 +1,22 @@ package next.controller.qna; +import core.mvc.ModelAndView; +import next.controller.AbstractController; +import next.dao.AnswerDao; +import next.dao.QuestionDao; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import core.mvc.Controller; -import next.dao.AnswerDao; -import next.dao.QuestionDao; +public class ShowController extends AbstractController { -public class ShowController implements Controller { @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { - Long questionId = Long.parseLong(req.getParameter("questionId")); + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { + long questionId = Long.parseLong(req.getParameter("questionId")); QuestionDao questionDao = new QuestionDao(); AnswerDao answerDao = new AnswerDao(); - req.setAttribute("question", questionDao.findById(questionId)); - req.setAttribute("answers", answerDao.findAllByQuestionId(questionId)); - return "/qna/show.jsp"; + return jspView("/qna/show.jsp") + .addObject("question", questionDao.findById(questionId)) + .addObject("answers", answerDao.findAllByQuestionId(questionId)); } } diff --git a/src/main/java/next/controller/user/CreateUserController.java b/src/main/java/next/controller/user/CreateUserController.java index a5db3dc06..109975b3e 100644 --- a/src/main/java/next/controller/user/CreateUserController.java +++ b/src/main/java/next/controller/user/CreateUserController.java @@ -1,26 +1,27 @@ package next.controller.user; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import core.mvc.ModelAndView; +import next.controller.AbstractController; +import next.dao.UserDao; +import next.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import core.mvc.Controller; -import next.dao.UserDao; -import next.model.User; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -public class CreateUserController implements Controller { +public class CreateUserController extends AbstractController { private static final Logger log = LoggerFactory.getLogger(CreateUserController.class); @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { + // TODO: User에 Builder 패턴 적용, 아래에서 사용 User user = new User(req.getParameter("userId"), req.getParameter("password"), req.getParameter("name"), req.getParameter("email")); log.debug("User : {}", user); UserDao userDao = new UserDao(); userDao.insert(user); - return "redirect:/"; + return jspView("redirect:/"); } } diff --git a/src/main/java/next/controller/user/ListUserController.java b/src/main/java/next/controller/user/ListUserController.java index 90aec9081..4ad179b2a 100644 --- a/src/main/java/next/controller/user/ListUserController.java +++ b/src/main/java/next/controller/user/ListUserController.java @@ -1,21 +1,23 @@ package next.controller.user; +import core.mvc.ModelAndView; +import next.controller.AbstractController; +import next.controller.UserSessionUtils; +import next.dao.UserDao; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import core.mvc.Controller; -import next.controller.UserSessionUtils; -import next.dao.UserDao; +public class ListUserController extends AbstractController { -public class ListUserController implements Controller { @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { if (!UserSessionUtils.isLogined(req.getSession())) { - return "redirect:/users/loginForm"; + return jspView("redirect:/users/loginForm"); } UserDao userDao = new UserDao(); - req.setAttribute("users", userDao.findAll()); - return "/user/list.jsp"; + return jspView("/user/list.jsp") + .addObject("users", userDao.findAll()); } } diff --git a/src/main/java/next/controller/user/LoginController.java b/src/main/java/next/controller/user/LoginController.java index 343e91e6f..207077c86 100644 --- a/src/main/java/next/controller/user/LoginController.java +++ b/src/main/java/next/controller/user/LoginController.java @@ -1,32 +1,34 @@ package next.controller.user; +import core.mvc.ModelAndView; +import next.controller.AbstractController; +import next.controller.UserSessionUtils; +import next.dao.UserDao; +import next.model.User; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import core.mvc.Controller; -import next.controller.UserSessionUtils; -import next.dao.UserDao; -import next.model.User; +public class LoginController extends AbstractController { -public class LoginController implements Controller { @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { String userId = req.getParameter("userId"); String password = req.getParameter("password"); UserDao userDao = new UserDao(); User user = userDao.findByUserId(userId); if (user == null) { - req.setAttribute("loginFailed", true); - return "/user/login.jsp"; + return jspView("/user/login.jsp") + .addObject("loginFailed", true); } if (user.matchPassword(password)) { HttpSession session = req.getSession(); - session.setAttribute(UserSessionUtils.USER_SESSION_KEY, user); - return "redirect:/"; + return jspView("redirect:/") + .addObject(UserSessionUtils.USER_SESSION_KEY, user); } else { - req.setAttribute("loginFailed", true); - return "/user/login.jsp"; + return jspView("/user/login.jsp") + .addObject("loginFailed", true); } } } diff --git a/src/main/java/next/controller/user/LogoutController.java b/src/main/java/next/controller/user/LogoutController.java index 08a8cee33..77ca7ff30 100644 --- a/src/main/java/next/controller/user/LogoutController.java +++ b/src/main/java/next/controller/user/LogoutController.java @@ -1,17 +1,19 @@ package next.controller.user; +import core.mvc.ModelAndView; +import next.controller.AbstractController; +import next.controller.UserSessionUtils; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import core.mvc.Controller; -import next.controller.UserSessionUtils; +public class LogoutController extends AbstractController { -public class LogoutController implements Controller { @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { HttpSession session = req.getSession(); session.removeAttribute(UserSessionUtils.USER_SESSION_KEY); - return "redirect:/"; + return jspView("redirect:/"); } } diff --git a/src/main/java/next/controller/user/ProfileController.java b/src/main/java/next/controller/user/ProfileController.java index cf529c3ff..1ec0842c2 100644 --- a/src/main/java/next/controller/user/ProfileController.java +++ b/src/main/java/next/controller/user/ProfileController.java @@ -1,22 +1,24 @@ package next.controller.user; +import core.mvc.ModelAndView; +import next.controller.AbstractController; +import next.dao.UserDao; +import next.model.User; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import core.mvc.Controller; -import next.dao.UserDao; -import next.model.User; +public class ProfileController extends AbstractController { -public class ProfileController implements Controller { @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { String userId = req.getParameter("userId"); UserDao userDao = new UserDao(); User user = userDao.findByUserId(userId); if (user == null) { throw new NullPointerException("사용자를 찾을 수 없습니다."); } - req.setAttribute("user", user); - return "/user/profile.jsp"; + return jspView("/user/profile.jsp") + .addObject("user", user); } } diff --git a/src/main/java/next/controller/user/UpdateFormUserController.java b/src/main/java/next/controller/user/UpdateFormUserController.java index 3d8650f61..568caee96 100644 --- a/src/main/java/next/controller/user/UpdateFormUserController.java +++ b/src/main/java/next/controller/user/UpdateFormUserController.java @@ -1,24 +1,25 @@ package next.controller.user; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import core.mvc.Controller; +import core.mvc.ModelAndView; +import next.controller.AbstractController; import next.controller.UserSessionUtils; import next.dao.UserDao; import next.model.User; -public class UpdateFormUserController implements Controller { +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class UpdateFormUserController extends AbstractController { @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { String userId = req.getParameter("userId"); UserDao userDao = new UserDao(); User user = userDao.findByUserId(userId); if (!UserSessionUtils.isSameUser(req.getSession(), user)) { throw new IllegalStateException("다른 사용자의 정보를 수정할 수 없습니다."); } - req.setAttribute("user", user); - return "/user/updateForm.jsp"; + return jspView("/user/updateForm.jsp") + .addObject("user", user); } } diff --git a/src/main/java/next/controller/user/UpdateUserController.java b/src/main/java/next/controller/user/UpdateUserController.java index 64b1ebbc8..ecaf82c5b 100644 --- a/src/main/java/next/controller/user/UpdateUserController.java +++ b/src/main/java/next/controller/user/UpdateUserController.java @@ -1,21 +1,21 @@ package next.controller.user; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import core.mvc.Controller; +import core.mvc.ModelAndView; +import next.controller.AbstractController; import next.controller.UserSessionUtils; import next.dao.UserDao; import next.model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -public class UpdateUserController implements Controller { +public class UpdateUserController extends AbstractController { private static final Logger log = LoggerFactory.getLogger(UpdateUserController.class); @Override - public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception { + public ModelAndView execute(HttpServletRequest req, HttpServletResponse resp) { UserDao userDao = new UserDao(); User user = userDao.findByUserId(req.getParameter("userId")); if (!UserSessionUtils.isSameUser(req.getSession(), user)) { @@ -26,6 +26,6 @@ public String execute(HttpServletRequest req, HttpServletResponse resp) throws E req.getParameter("email")); log.debug("Update User : {}", updateUser); user.update(updateUser); - return "redirect:/"; + return jspView("redirect:/"); } }