-
Notifications
You must be signed in to change notification settings - Fork 302
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 구현하기 - 2 단계 ] 필립(양재필) 미션 제출합니다. #453
Conversation
- 기존 RegisterController를 controller implement기반에서annotation기반으로 변경 - RegisterViewController를 RegisterController로 통합
- DispatcherServlet에서 각각의 HandlerMapping을 가지지 않도록 HanlderMappings 추가 - AnnotationHanlderMapping, ManualHandlerMapping의 HandlerMapping interface 추가 - HandlerMapping추가 위치 이동 : DispatcheServlet -> DispatcheServletInitalizer
- ManualControllerHandlerAdatper - 기존 Controller inteface를 위한 어댑터 - Controller 객체를 필드로 가지고있음 - HandlerExcutionAdapter - Annotation 기반으로 생성된 HanlderExcution을 위한 어댑터 - HandlerExcution을 가지고 있음
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 필립! 리뷰가 많이 늦었네요..
추상화도 잘해주셨고 pr에 자세히 설명해 주셔서 코드를 수월하게 볼 수 있었던 것 같아요.
커밋대로 코드를 보니 필립이 어떤 흐름으로 구현해 나가셨는지 알 수 있었습니다.
리뷰는 거의 궁금한 점 위주여서 바로 approve 하겠습니다!
private final List<HandlerMapping> handlerMappings; | ||
|
||
public HandlerMappings() { | ||
this.handlerMappings = new ArrayList<>(); | ||
} | ||
|
||
public void add(final HandlerMapping handlerMapping) { | ||
handlerMappings.add(handlerMapping); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public HandlerMappings(List<HandlerMapping> handlerMappings) {
this.handlerMappings = handlerMappings;
}
이런 식으로 생성자에서 리스트를 받아서 초기화하는 방법도 있는데, 빈 생성자에서 빈 리스트로 초기화한 후 add 메서드를 통해 하나하나 add 해주는 방식을 채택한 이유가 궁금합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
새로운 핸들러맵핑이 생기고 추가될때에 유연성이 좀 더 좋을것 같다는 생각과,
생성자에서 생성을 하게 되면 HandlerMappings
를 가지고 있는 DispatcherServlet
이 생성될때 인자로 handlerMappings list또한 같이 넘겨줘야 해서, 디스페쳐서블릿을 생성을 먼저 하고, 후에 핸들러맵핑을 추가해주려면 위처럼 add를 통해 추가해줘야기 때문에 이런식으로 했습니다.
DispatcherServlet
을 생성할때 추가를 해주게 되면,, 디스페쳐서블릿 생성 전에 핸들러맵핑과 핸들러어댑터를 초기화 해야한다라는 점과, 이러한식으로 무언가 추가될때마다 디스페쳐서블릿의 생성자 파라미터가 늘어나게 될 것 같아서 좀 꺼려지는것 같습니다...
명확하고 깔끔하게는 이유가 없는것같기는 하네요ㅠ
private final HandlerMappings handlerMappings; | ||
private final HandlerAdapterFactory handlerAdapterFactory; | ||
|
||
public DispatcherServlet() { | ||
this.handlerMappings = new HandlerMappings(); | ||
this.handlerAdapterFactory = new HandlerAdapterFactory(); | ||
} | ||
|
||
public void addHandlerMapping(final HandlerMapping handlerMapping) { | ||
handlerMappings.add(handlerMapping); | ||
} | ||
|
||
public void addHandlerAdapterType(final Class<?> handlerType, final Class<? extends HandlerAdapter> adapterType) { | ||
handlerAdapterFactory.addAdapterType(handlerType, adapterType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분도 마찬가지로 생성자에서 초기화하지 않고 메서드를 통해 값을 넣어주는 이유가 궁금합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
관련 내용 HandlerMappings
코멘트에 남겼습니다!
final HandlerAdapter adapter = handlerAdapterFactory.getAdapter(handler); | ||
final ModelAndView modelAndView = adapter.handle(request, response); | ||
render(modelAndView, request, response); | ||
} catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주어진 코드에서 Throwable을 사용했는데 필립은 Exception으로 바꾸셨군요! catch에 Throwable을 사용하는 것과 Exception을 사용하는 것은 어떤 점에서 다를까요? 필립이 생각하신 Exception으로 바꾸었을 때의 이점이 무엇인지 궁금합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리팩터링을 하며 인텔리제이 자동완성을 쓰다보니 생각없이 Exception으로 변경이 되게 되었네요...
엔초의 코멘트 보고나서 다시 생각을 해보고 찾아보다가 stackoverflow 글을 찾을 수 있었습니다.
해당 내용처럼 Throwable로 하게되면 Error
또한 캐치를 하게 되고 에러를 핸들링을 해주게 되어서 문제가 생길 수 있다고 생각합니다.
만약 Throwable
로 사용을 하면 해당 디스패쳐 서블릿에서 error또한 ServletException
으로 예외
로 변환해서 던져주게 됩니다. 그러고 디스페쳐 서블릿 입장에서는 서블렛익셉션이 밖에서 어떤 방식으로 핸들링 되는지 (무시하거나 처리 후 프로그램이 진행되는지, 어플리케이션을 종료시키는지) 알 수 없습니다.
하지만 Error
의 경우 스택오버플로우, 메모리초과등 시스템적으로 치명적이고, 개발자가 핸들링 하지 않아야 하는, - 어플리케이션을 종료해야하는 - 경우에 해당함으로 그냥 종료가 될 수 있도록 추가적인 핸들링을 안해주는것이 좋지 않을까 라는 생각이 듭니다.
public interface HandlerAdapter { | ||
|
||
boolean isSupport(Object handler); | ||
|
||
ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
추상화 👍
} | ||
log.info("Load Controller - {}", controllerClass.getSimpleName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정상 흐름에도 로그를 찍는다면 어떤 이점이 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기존 코드에서도 (ManualHandlerMapping) 등록된 컨트롤러들을 로그를 찍어서 확인할 수 있게 해주고있어서 통일성을 위해서라도 작성해주는것이 좋다고 생각했습니다.
통일성을 제외한 있어서 좋은점은 서버를 띄우면서 컨트롤러들이 잘 올라가는지 어떤컨트롤러들이 올라가는지를 확인하기 용이해서 좋다고 생각합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
엔초가 남겨주신 코멘트들 덕분에 그냥 진행했던 부분도 좀 더 생각해보고 찾아보며 이유 정리해볼 수 있었습니다!!
관련 내용들 코멘트 남겼습니다!
final HandlerAdapter adapter = handlerAdapterFactory.getAdapter(handler); | ||
final ModelAndView modelAndView = adapter.handle(request, response); | ||
render(modelAndView, request, response); | ||
} catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리팩터링을 하며 인텔리제이 자동완성을 쓰다보니 생각없이 Exception으로 변경이 되게 되었네요...
엔초의 코멘트 보고나서 다시 생각을 해보고 찾아보다가 stackoverflow 글을 찾을 수 있었습니다.
해당 내용처럼 Throwable로 하게되면 Error
또한 캐치를 하게 되고 에러를 핸들링을 해주게 되어서 문제가 생길 수 있다고 생각합니다.
만약 Throwable
로 사용을 하면 해당 디스패쳐 서블릿에서 error또한 ServletException
으로 예외
로 변환해서 던져주게 됩니다. 그러고 디스페쳐 서블릿 입장에서는 서블렛익셉션이 밖에서 어떤 방식으로 핸들링 되는지 (무시하거나 처리 후 프로그램이 진행되는지, 어플리케이션을 종료시키는지) 알 수 없습니다.
하지만 Error
의 경우 스택오버플로우, 메모리초과등 시스템적으로 치명적이고, 개발자가 핸들링 하지 않아야 하는, - 어플리케이션을 종료해야하는 - 경우에 해당함으로 그냥 종료가 될 수 있도록 추가적인 핸들링을 안해주는것이 좋지 않을까 라는 생각이 듭니다.
} | ||
log.info("Load Controller - {}", controllerClass.getSimpleName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기존 코드에서도 (ManualHandlerMapping) 등록된 컨트롤러들을 로그를 찍어서 확인할 수 있게 해주고있어서 통일성을 위해서라도 작성해주는것이 좋다고 생각했습니다.
통일성을 제외한 있어서 좋은점은 서버를 띄우면서 컨트롤러들이 잘 올라가는지 어떤컨트롤러들이 올라가는지를 확인하기 용이해서 좋다고 생각합니다.
private final List<HandlerMapping> handlerMappings; | ||
|
||
public HandlerMappings() { | ||
this.handlerMappings = new ArrayList<>(); | ||
} | ||
|
||
public void add(final HandlerMapping handlerMapping) { | ||
handlerMappings.add(handlerMapping); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
새로운 핸들러맵핑이 생기고 추가될때에 유연성이 좀 더 좋을것 같다는 생각과,
생성자에서 생성을 하게 되면 HandlerMappings
를 가지고 있는 DispatcherServlet
이 생성될때 인자로 handlerMappings list또한 같이 넘겨줘야 해서, 디스페쳐서블릿을 생성을 먼저 하고, 후에 핸들러맵핑을 추가해주려면 위처럼 add를 통해 추가해줘야기 때문에 이런식으로 했습니다.
DispatcherServlet
을 생성할때 추가를 해주게 되면,, 디스페쳐서블릿 생성 전에 핸들러맵핑과 핸들러어댑터를 초기화 해야한다라는 점과, 이러한식으로 무언가 추가될때마다 디스페쳐서블릿의 생성자 파라미터가 늘어나게 될 것 같아서 좀 꺼려지는것 같습니다...
명확하고 깔끔하게는 이유가 없는것같기는 하네요ㅠ
private final HandlerMappings handlerMappings; | ||
private final HandlerAdapterFactory handlerAdapterFactory; | ||
|
||
public DispatcherServlet() { | ||
this.handlerMappings = new HandlerMappings(); | ||
this.handlerAdapterFactory = new HandlerAdapterFactory(); | ||
} | ||
|
||
public void addHandlerMapping(final HandlerMapping handlerMapping) { | ||
handlerMappings.add(handlerMapping); | ||
} | ||
|
||
public void addHandlerAdapterType(final Class<?> handlerType, final Class<? extends HandlerAdapter> adapterType) { | ||
handlerAdapterFactory.addAdapterType(handlerType, adapterType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
관련 내용 HandlerMappings
코멘트에 남겼습니다!
안녕하세요 엔초!! 😄
2단계 미션 제출합니다!!
2단계 진행 내용에 대한 이야기 전에, 1단계에서 코멘트 주신 부분들에 대한 이야기입니다!
request method enum에서 valueOf 사용 에 대한 질문을 듣고 어,, 그러네 하면서 한번 rfc 문서를 찾아보았습니다.
찾아보니 rfc 9110에서 아래와 같은 문구가 있음을 확인할 수 있었습니다.
확인할 수 있듯이 표준이
all-uppercase
라고 하여서,RequestMethod
enum에서는 소문자를 대문자로 변경해주는 처리는 안해도 된다고 판단하였습니다. 그래도 혹시나 클라이언트에서 표준을 안지키고 소문자로 보내는것을 처리해주고싶다 하면 RequestMethod 내부에서는 표준을 지키고, 필요한 경우 외부에서 request method string을.toUpperCase()
로 대문자로 치환해준 후 그대로 valueOf를 사용하게 구현하게 될 것 같습니다. (이번 코드에서는 적용하지는 않았습니다...)두번째 코멘트에서 뎁스 관련도 이번엔 완성된 코드에서 최대한 코드뎁스나 가독성적인 측면을 지켜보려 노력했습니다!!
2단계 진행은
ModelAndView
로 통일시키기순으로 진행을 했고, 리펙터링 한 흐름에 맞게 커밋도 나뉘어져 있습니다.
아래는 이번단계에서 새롭게 생성한 클래스에 대한 내용을 흐름 순서대로 간략하게 작성한 것입니다.
HandlerMappings
Controller
객체 또는 Controller 어노테이션과 RequestMapping을 통해 생성된HandlerExcution
객체)HandlerAdapterFactory
HandlerAdapter
Controller
객체 또는HandlerExcution
객체)를 가지고 hanlde에 대한 결과물을ModelAndView
로 변환하여 반환ManualControllerHandlerAdapter
: Controller interface를 implement해 구현한 기존 컨트롤러 객체용 어댑터HandlerExcutionAdapter
: 어노테이션 기반으로 AnnotaionHandlerMapping을 통해 생성된HandlerExcution
용 어댑터기본적으로 mvc관련된 인터페이스와 구현체들은 mvc모듈에 작성하였지만, 기존 어플리케이션 모듈(
com.techcourse
)에 있던ManualHandlerMapping
을 위한 클래스는 기존 어플리케이션 모듈에 같이 두었습니다.이번단계 리뷰도 잘부탁드립니다!!
+) 프로젝트와 병행하니라 테스트는 작성하지 않고 진행하였습니다.. 😭