Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MVC 구현하기 - 3단계] 루쿠(백경환) 미션 제출합니다. #621

Merged
merged 11 commits into from
Sep 30, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.WebApplicationInitializer;
import webmvc.org.springframework.web.servlet.DispatcherServlet;

/**
* Base class for {@link WebApplicationInitializer}
Expand Down
48 changes: 0 additions & 48 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java

This file was deleted.

18 changes: 18 additions & 0 deletions app/src/main/java/com/techcourse/controller/ForwardController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.techcourse.controller;

import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

@Controller
public class ForwardController {

@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView execute(final HttpServletRequest request, final HttpServletResponse response) {
return new ModelAndView(new JspView("/index.jsp"));
}
}
30 changes: 17 additions & 13 deletions app/src/main/java/com/techcourse/controller/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,40 @@

import com.techcourse.domain.User;
import com.techcourse.repository.InMemoryUserRepository;
import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

public class LoginController implements Controller {
@Controller
public class LoginController {

private static final Logger log = LoggerFactory.getLogger(LoginController.class);

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
if (UserSession.isLoggedIn(req.getSession())) {
return "redirect:/index.jsp";
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ModelAndView login(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
if (UserSession.isLoggedIn(request.getSession())) {
return new ModelAndView(new JspView("redirect:/index.jsp"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 JspView 에서는 JSP 파일로 이동시켜주는 역할만 해야 한다고 생각해서 redirect 는 다른 뷰로 분리하는 게 좋다고 생각해서 분리했었습니다! 이 부분에 대해 루쿠는 어떻게 생각하시나요? 😄

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 redirect의 역할 분리를 위해 클래스를 생성 하는게 좋을거 같다는 쪽의 의견입니다!
근데 JspRedirectView, JsonRedirectView 와 같은 클래스 분리를 떠올렸었는데
이러면 품이 좀 들거같다는 생각이 들어서 고냥 이대로 진행하게 되었습니다~

}

return InMemoryUserRepository.findByAccount(req.getParameter("account"))
return InMemoryUserRepository.findByAccount(request.getParameter("account"))
.map(user -> {
log.info("User : {}", user);
return login(req, user);
return login(request, user);
})
.orElse("redirect:/401.jsp");
.orElse(new ModelAndView(new JspView("redirect:/401.jsp")));
}

private String login(final HttpServletRequest request, final User user) {
private ModelAndView login(final HttpServletRequest request, final User user) {
if (user.checkPassword(request.getParameter("password"))) {
final var session = request.getSession();
session.setAttribute(UserSession.SESSION_KEY, user);
return "redirect:/index.jsp";
return new ModelAndView(new JspView("redirect:/index.jsp"));
}
return "redirect:/401.jsp";
return new ModelAndView(new JspView("redirect:/401.jsp"));
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
package com.techcourse.controller;

import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

public class LoginViewController implements Controller {
@Controller
public class LoginViewController {

private static final Logger log = LoggerFactory.getLogger(LoginViewController.class);

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
return UserSession.getUserFrom(req.getSession())
@RequestMapping(value = "/login/view", method = RequestMethod.GET)
public ModelAndView getLoginView(final HttpServletRequest request, final HttpServletResponse response) {
return UserSession.getUserFrom(request.getSession())
.map(user -> {
log.info("logged in {}", user.getAccount());
return "redirect:/index.jsp";
return new ModelAndView(new JspView("redirect:/index.jsp"));
})
.orElse("/login.jsp");
.orElse(new ModelAndView(new JspView("/login.jsp")));
}

}
18 changes: 12 additions & 6 deletions app/src/main/java/com/techcourse/controller/LogoutController.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package com.techcourse.controller;

import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

public class LogoutController implements Controller {
@Controller
public class LogoutController{

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
final var session = req.getSession();
@RequestMapping(value = "/logout", method = RequestMethod.GET)
public ModelAndView logout(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
final var session = request.getSession();
session.removeAttribute(UserSession.SESSION_KEY);
return "redirect:/";
return new ModelAndView(new JspView("redirect:/"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@

import com.techcourse.domain.User;
import com.techcourse.repository.InMemoryUserRepository;
import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

public class RegisterController implements Controller {
@Controller
public class RegisterController {

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
@RequestMapping(value = "/register", method = RequestMethod.POST)
public ModelAndView register(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
final var user = new User(2,
req.getParameter("account"),
req.getParameter("password"),
req.getParameter("email"));
request.getParameter("account"),
request.getParameter("password"),
request.getParameter("email"));
InMemoryUserRepository.save(user);

return "redirect:/index.jsp";
return new ModelAndView(new JspView("redirect:/index.jsp"));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package com.techcourse.controller;

import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

public class RegisterViewController implements Controller {
@Controller
public class RegisterViewController {

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
return "/register.jsp";
@RequestMapping(value = "/register/view", method = RequestMethod.GET)
public ModelAndView getRegisterView(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
return new ModelAndView(new JspView("/register.jsp"));
}
}
32 changes: 32 additions & 0 deletions app/src/main/java/com/techcourse/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.techcourse.controller;

import com.techcourse.domain.User;
import com.techcourse.repository.InMemoryUserRepository;
import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JsonView;

@Controller
public class UserController {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UserController 를 추가해주셨네요!
컨트롤러를 따로 분리한 이유가 궁금해요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미션 힌트를 바탕으로 해당 컨트롤러를 추가해서 어노테이션 mvc가 정상적으로 동작하는지 테스트하기 위해 추가하였습니다~!


private static final Logger log = LoggerFactory.getLogger(UserController.class);

@RequestMapping(value = "/api/user", method = RequestMethod.GET)
public ModelAndView show(HttpServletRequest request, HttpServletResponse response) {
final String account = request.getParameter("account");
log.debug("user id : {}", account);

final ModelAndView modelAndView = new ModelAndView(new JsonView());
final User user = InMemoryUserRepository.findByAccount(account)
.orElseThrow();

modelAndView.addObject("user", user);
return modelAndView;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.techcourse;
package webmvc.org.springframework.web.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
Expand All @@ -8,18 +8,16 @@
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMapping;
import webmvc.org.springframework.web.servlet.mvc.tobe.ManualHandlerAdapter;
import webmvc.org.springframework.web.servlet.view.JspView;

public class DispatcherServlet extends HttpServlet {

private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class);
public static final String ANNOTATION_BASE_PACKAGE = "com.techcourse.controller";
private List<HandlerMapping> handlerMappings = new ArrayList<>();
private List<HandlerAdapter> handlerAdapters = new ArrayList<>();

Expand All @@ -28,12 +26,10 @@ public DispatcherServlet() {

@Override
public void init() {
handlerMappings.add(new ManualHandlerMapping());
handlerMappings.add(new AnnotationHandlerMapping("com.techcourse.controller"));
handlerMappings.add(new AnnotationHandlerMapping(ANNOTATION_BASE_PACKAGE));
handlerMappings.forEach(HandlerMapping::initialize);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스프링에서는 리플렉션을 사용해서 클래스들을 가져오는 일을 BeanFactory 가 수행해주는데요, 스프링과 비교했을 때 루쿠가 구현하신 구조는 어떤 차이점이 있고, 어떤 부분에서 좀 더 장점을 갖는다고 생각하세요? (물론 현재 구조와 프로젝트 규모에서는 매우 큰 차이가 있지만, 그런 부분은 차치하고 생각했을 때의 질문입니다! 😄 저도 현재 규모에서는 리플렉션을 AnnotationHandlerMapping 에서 수행하는 것이 오버엔지니어링 하지 않는 가장 좋은 방법이라고 생각해요!)

Copy link
Author

@aiaiaiai1 aiaiaiai1 Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

먼저 구조의 차이점에는 책임 분리가 되어있느냐 안되어있느냐 그 차이 인것 같아요
결국엔 책임 분리가 코드의 재사용성으로 이어지고 유지보수까지 영향을 미치게 된다고 생각해요
그래서 스프링과 비교했을때 스프링은 구조적이고 분리가 잘 되어있지만 구현하기엔 리소스가 굉장히 많이 듦.
제가 구현한 것은 분리는 잘 안되어있어 유지보수가 별로지만 구현이 단순하고 품이 적게 든다
이런 차이가 있다고 생각하네요


handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new ManualHandlerAdapter());
}

@Override
Expand All @@ -45,8 +41,8 @@ protected void service(

try {
ModelAndView modelAndView = execute(request, response);
String viewName = modelAndView.getView().getName();
move(viewName, request, response);
View view = modelAndView.getView();
view.render(modelAndView.getModel(), request, response);
} catch (Throwable e) {
log.error("Exception : {}", e.getMessage(), e);
throw new ServletException(e.getMessage());
Expand All @@ -57,31 +53,25 @@ private ModelAndView execute(
HttpServletRequest request,
HttpServletResponse response
) throws Exception {
Object handler = handlerMappings.stream()
.filter(handlerMapping -> handlerMapping.isMatch(request))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("지원하지 않는 요청입니다."))
.getHandler(request);
Object handler = getHandler(request);
HandlerAdapter adapter = getHandlerAdapter(handler);
return adapter.handle(request, response, handler);
}

private HandlerAdapter getHandlerAdapter(Object handler) {
HandlerAdapter adapter = handlerAdapters.stream()
.filter(handlerAdapter -> handlerAdapter.supports(handler))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("지원하지 않는 요청입니다."));

return adapter.handle(request, response, handler);
return adapter;
}

private void move(
final String viewName,
final HttpServletRequest request,
final HttpServletResponse response
) throws Exception {
if (viewName.startsWith(JspView.REDIRECT_PREFIX)) {
response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length()));
return;
}

final var requestDispatcher = request.getRequestDispatcher(viewName);
requestDispatcher.forward(request, response);
private Object getHandler(HttpServletRequest request) {
Object handler = handlerMappings.stream()
.filter(handlerMapping -> handlerMapping.isMatch(request))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("지원하지 않는 요청입니다."))
.getHandler(request);
return handler;
Comment on lines +70 to +74
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍👍👍

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,4 @@ public Map<String, Object> getModel() {
public View getView() {
return view;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@

public interface View {
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
String getName();
}
Loading
Loading