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단계] 밀리(김미성) 미션 제출합니다. #630

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

/**
* Base class for {@link WebApplicationInitializer}
* implementations that register a {@link DispatcherServlet} in the servlet context.
* Base class for {@link WebApplicationInitializer} implementations that register a {@link DispatcherServlet} in the
* servlet context.
*/
public class DispatcherServletInitializer implements WebApplicationInitializer {

Expand Down
38 changes: 0 additions & 38 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java

This file was deleted.

19 changes: 0 additions & 19 deletions app/src/main/java/com/techcourse/WrappedManualHandlerMapping.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;

@Controller
public class ForwardController {

private static final String PATH = "/index.jsp";

@RequestMapping(value = "/", method = RequestMethod.GET)
public String execute(final HttpServletRequest request, final HttpServletResponse response) {
return PATH;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

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;

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

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

@Override
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
if (UserSession.isLoggedIn(req.getSession())) {
return "redirect:/index.jsp";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
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;

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

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

@Override
@RequestMapping(value = "/login/view", method = RequestMethod.GET)
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
return UserSession.getUserFrom(req.getSession())
.map(user -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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;

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

@Override
@RequestMapping(value = "/logout", method = RequestMethod.GET)
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
final var session = req.getSession();
session.removeAttribute(UserSession.SESSION_KEY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

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;

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

@Override
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
final var user = new User(2,
req.getParameter("account"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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;

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

@Override
@RequestMapping(value = "/register/view", method = RequestMethod.GET)
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
return "/register.jsp";
}
Expand Down
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 {

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

@RequestMapping(value = "/api/user", method = RequestMethod.GET)
public ModelAndView show(final HttpServletRequest request, final 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
Expand Up @@ -6,10 +6,10 @@

public class ModelAndView {

private final View view;
private final Object view;
private final Map<String, Object> model;

public ModelAndView(final View view) {
public ModelAndView(final Object view) {
this.view = view;
this.model = new HashMap<>();
}
Expand All @@ -27,7 +27,7 @@ public Map<String, Object> getModel() {
return Collections.unmodifiableMap(model);
}

public View getView() {
public Object getView() {
return view;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.techcourse;
package webmvc.org.springframework.web.servlet.mvc;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
Expand All @@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.ModelAndView;
Expand All @@ -17,7 +18,10 @@
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerAdapterException;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMapping;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMappingException;
import webmvc.org.springframework.web.servlet.mvc.tobe.ManualHandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.tobe.JsonViewResolver;
import webmvc.org.springframework.web.servlet.mvc.tobe.JspViewResolver;
import webmvc.org.springframework.web.servlet.mvc.tobe.ViewResolver;
import webmvc.org.springframework.web.servlet.mvc.tobe.ViewResolverException;

public class DispatcherServlet extends HttpServlet {

Expand All @@ -26,29 +30,31 @@ public class DispatcherServlet extends HttpServlet {

private final List<HandlerMapping> handlerMappings = new ArrayList<>();
private final List<HandlerAdapter> handlerAdapters = new ArrayList<>();

public DispatcherServlet() {
}
private final List<ViewResolver> viewResolvers = new ArrayList<>();

Copy link

Choose a reason for hiding this comment

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

이 필드들 일급 컬렉션으로 관리하면 DispatcherServlet에서 service 메서드에 집중할 수 있을 것 같아요 !

Copy link
Author

Choose a reason for hiding this comment

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

수정했습니다! 확실히 코드가 깔끔해졌네요 ㅎㅎ

@Override
public void init() {
initHandlerMappings();
initHandlerAdapters();
initViewResolvers();
}

private void initHandlerMappings() {
handlerMappings.add(new WrappedManualHandlerMapping());
handlerMappings.add(new AnnotationHandlerMapping(getClass().getPackageName()));
handlerMappings.add(new AnnotationHandlerMapping());
for (final HandlerMapping handlerMapping : handlerMappings) {
Copy link

Choose a reason for hiding this comment

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

오... AnnotationHandlerMapping 생성할때 basePakage명을 명시해주지 않아도 되는군요...!!

DispatcherServletInitializer 클래스 기준으로 만들어 주는 걸까요???

Copy link
Author

Choose a reason for hiding this comment

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

넵 명시해주지 않아도 AnnotationHandlerMapping 내부의 초기화하는 로직에서 Reflections을 생성할 때 빈 패키지가 들어오면 해당 프로젝트의 클래스를 가져오는 것 같더라구요!

handlerMapping.initialize();
}
}

private void initHandlerAdapters() {
handlerAdapters.add(new ManualHandlerAdapter());
handlerAdapters.add(new AnnotationHandlerAdapter());
}

private void initViewResolvers() {
viewResolvers.add(new JspViewResolver());
viewResolvers.add(new JsonViewResolver());
}

@Override
protected void service(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException {
Expand All @@ -59,9 +65,11 @@ protected void service(final HttpServletRequest request, final HttpServletRespon
final Object handler = getHandler(request);
final HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
final ModelAndView modelAndView = handlerAdapter.handle(handler, request, response);
final View view = modelAndView.getView();
final View view = getView(modelAndView);
final Map<String, Object> model = modelAndView.getModel();
Copy link

Choose a reason for hiding this comment

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

HandlerExecution 클래스 리뷰에 남긴것처럼 수정 된다면,
view resolver도 없어지고 render도 ModelAndView 내부에서 해줄 수 있을까여...??

Copy link
Author

Choose a reason for hiding this comment

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

저는 뷰 리졸버가 있는게 훨씬 확장성이 좋다고 생각해서 그대로 뒀습니다..!!

view.render(model, request, response);
} catch (final HandlerMappingException | ViewResolverException e) {
throw e;
} catch (final Throwable e) {
Copy link

Choose a reason for hiding this comment

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

다시 예외를 던지는 것 보다
응답에 404를 내려 보내주는 것은 어떤가요?!

Copy link
Author

Choose a reason for hiding this comment

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

JspView로 404.jsp를 내려주는 것을 말씀하시는 건가요?!
그렇다면 특정 앱의 jsp 파일명을 디스패쳐 서블릿에 명시해주는 것이라서 고민이 되더라구요..!
만약 그렇게 한다면 이 디스패쳐 서블릿을 사용하는 모든 어플리케이션이 강제로 404.jsp 파일을 만들어야하기 때문에 해당 예외가 터지면 처리는 개발자에게 맡기는게 좋을 것 같다고 생각해서 그냥 예외를 던졌어요!
이 부분은 어떻게 생각하시나요??

Copy link

Choose a reason for hiding this comment

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

response에 뷰를 그려주는것이 아니라,
response.setStatus(CODE); 를 해주는 것을 의미했어요 !
지금은 커스텀 예외여도 500에러로 내려가니까용 !

log.error("Exception : {}", e.getMessage(), e);
throw new ServletException(e.getMessage());
Expand All @@ -70,9 +78,9 @@ protected void service(final HttpServletRequest request, final HttpServletRespon

private Object getHandler(final HttpServletRequest request) {
for (final HandlerMapping handlerMapping : handlerMappings) {
final Object handler = handlerMapping.getHandler(request);
if (handler != null) {
return handler;
final Optional<Object> handler = handlerMapping.getHandler(request);
if (handler.isPresent()) {
return handler.get();
}
}
throw new HandlerMappingException("해당 요청에 대한 핸들러가 없습니다.");
Expand All @@ -86,4 +94,13 @@ private HandlerAdapter getHandlerAdapter(final Object handler) {
}
throw new HandlerAdapterException("해당 요청에 대한 핸들러 어댑터가 없습니다.");
}

private View getView(final ModelAndView modelAndView) {
final Object view = modelAndView.getView();
return viewResolvers.stream()
.filter(viewResolver -> viewResolver.supports(view))
.findFirst()
.map(viewResolver -> viewResolver.resolve(view))
.orElseThrow(() -> new ViewResolverException("해당 요청에 대한 view를 처리할 수 없습니다."));
}
}

This file was deleted.

This file was deleted.

Loading
Loading