-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[MVC 구현하기 - 2단계] 페드로(류형욱) 미션 제출합니다. (#726)
* refactor: RegisterController를 어노테이션 기반으로 변경하고 명시적 매핑 제거 * refactor(HandlerMapping): HandlerMapping 인터페이스 추가 * feat(HandlerAdapter): HandlerAdapter 인터페이스 추가 * feat(ControllerHandlerAdapter): 컨트롤러를 담당하는 HandlerAdapter 구현체 구현 * feat(HandlerExecutionHandlerAdapter): HandlerExecution을 담당하는 HandlerAdapter 구현체 구현 * feat: HandlerMapping 구현체에서 핸들러 초기화 시 로깅 추가 * feat(DispatcherServlet): 어노테이션 기반 컨트롤러와 인터페이스 기반 컨트롤러를 모두 지원하도록 구현 * refactor(JspView): 내부 로직 메서드 분리 * test(JspViewTest): redirect/forward 테스트 추가 * refactor(AnnotationHandlerMappingTest): 핸들러 부재 시 더 이상 예외를 던지지 않으므로 관련 테스트 삭제 * refactor(HandlerExecutionHandlerAdapter): 패키지 위치 변경 * test(HandlerExecutionTest): 메서드 접근 불가 시 예외 발생 테스트 추가 * refactor(ControllerScanner): Reflections에서 Controller를 찾고 인스턴스화를 담당하는 클래스 분리 * Test(ControllerScannerTest): 컨트롤러 클래스의 인스턴스화 실패 테스트 추가 * refactor(AnnotationHandlerMapping): 루프 내부의 forEach 구문을 메서드로 분리 * refactor(DispatcherServlet): 어노테이션 기반 컨트롤러의 베이스 패키지 경로 상수화 * test(DispatcherServletTest): DispatcherServlet 예외 테스트 추가 * refactor(DispatcherServlet): HandlerMapping과 HandlerAdapter를 일급 컬렉션으로 관리하도록 변경 * refactor: Registry 클래스들을 프레임워크 패키지로 이동 * style(ControllerScannerTest): 불필요한 개행 제거 * refactor(FakeController): 미 사용 경고 제거 어노테이션 추가 * fix(JspViewTest): `@DisplayName` 오타 수정 * test(ControllerScannerTest): 컨트롤러 클래스 인스턴스화 성공 테스트 추가 * test(HandlerMappingRegistry): HandlerMappingRegistry 테스트 추가 * test(DispatcherServletTest): 어노테이션/인터페이스 기반 컨트롤러 처리 테스트 추가
- Loading branch information
Showing
22 changed files
with
578 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 17 additions & 5 deletions
22
app/src/main/java/com/techcourse/controller/RegisterController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,33 @@ | ||
package com.techcourse.controller; | ||
|
||
import com.interface21.context.stereotype.Controller; | ||
import com.interface21.web.bind.annotation.RequestMapping; | ||
import com.interface21.web.bind.annotation.RequestMethod; | ||
import com.interface21.webmvc.servlet.ModelAndView; | ||
import com.interface21.webmvc.servlet.view.JspView; | ||
import com.techcourse.domain.User; | ||
import com.techcourse.repository.InMemoryUserRepository; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import com.interface21.webmvc.servlet.mvc.asis.Controller; | ||
|
||
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.GET) | ||
public ModelAndView view(HttpServletRequest req, HttpServletResponse res) { | ||
JspView jspView = new JspView("/register.jsp"); | ||
return new ModelAndView(jspView); | ||
} | ||
|
||
@RequestMapping(value = "/register", method = RequestMethod.POST) | ||
public ModelAndView save(HttpServletRequest req, HttpServletResponse res) { | ||
final var user = new User(2, | ||
req.getParameter("account"), | ||
req.getParameter("password"), | ||
req.getParameter("email")); | ||
InMemoryUserRepository.save(user); | ||
|
||
return "redirect:/index.jsp"; | ||
JspView jspView = new JspView("redirect:/index.jsp"); | ||
return new ModelAndView(jspView); | ||
} | ||
} |
13 changes: 0 additions & 13 deletions
13
app/src/main/java/com/techcourse/controller/RegisterViewController.java
This file was deleted.
Oops, something went wrong.
115 changes: 115 additions & 0 deletions
115
app/src/test/java/com/techcourse/DispatcherServletTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package com.techcourse; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
import com.interface21.webmvc.servlet.mvc.HandlerMapping; | ||
import com.techcourse.controller.LoginViewController; | ||
import jakarta.servlet.RequestDispatcher; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import jakarta.servlet.http.HttpSession; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.ArgumentCaptor; | ||
|
||
class DispatcherServletTest { | ||
|
||
private DispatcherServlet dispatcherServlet; | ||
private HttpServletRequest request; | ||
private HttpServletResponse response; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
dispatcherServlet = new DispatcherServlet(); | ||
dispatcherServlet.init(); | ||
request = mock(HttpServletRequest.class); | ||
response = mock(HttpServletResponse.class); | ||
} | ||
|
||
@DisplayName("어노테이션 기반 컨트롤러를 찾아서 처리한다.") | ||
@Test | ||
void processAnnotationBasedController() throws ServletException { | ||
// given | ||
when(request.getMethod()).thenReturn("GET"); | ||
when(request.getRequestURI()).thenReturn("/register"); | ||
|
||
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class); | ||
RequestDispatcher requestDispatcher = mock(RequestDispatcher.class); | ||
when(request.getRequestDispatcher(argumentCaptor.capture())) | ||
.thenReturn(requestDispatcher); | ||
|
||
// when | ||
dispatcherServlet.service(request, response); | ||
|
||
// then | ||
assertThat(argumentCaptor.getValue()) | ||
.isEqualTo("/register.jsp"); | ||
} | ||
|
||
@DisplayName("Controller 인터페이스 기반 컨트롤러를 찾아서 처리한다.") | ||
@Test | ||
void processInterfaceBasedController() throws ServletException { | ||
// given | ||
when(request.getMethod()).thenReturn("GET"); | ||
when(request.getRequestURI()).thenReturn("/login/view"); | ||
when(request.getSession()).thenReturn(mock(HttpSession.class)); | ||
registerFakeHandlerMapping(new LoginViewController()); | ||
|
||
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class); | ||
RequestDispatcher requestDispatcher = mock(RequestDispatcher.class); | ||
when(request.getRequestDispatcher(argumentCaptor.capture())) | ||
.thenReturn(requestDispatcher); | ||
|
||
// when | ||
dispatcherServlet.service(request, response); | ||
|
||
// then | ||
assertThat(argumentCaptor.getValue()) | ||
.isEqualTo("/login.jsp"); | ||
} | ||
|
||
@DisplayName("요청에 해당하는 핸들러를 찾을 수 없으면 예외가 발생한다.") | ||
@Test | ||
void throwsWhenHandlerNotFound() { | ||
// given | ||
when(request.getMethod()).thenReturn("GET"); | ||
when(request.getRequestURI()).thenReturn("/not-found"); | ||
|
||
// when & then | ||
assertThatThrownBy(() -> dispatcherServlet.service(request, response)) | ||
.isInstanceOf(ServletException.class) | ||
.hasMessageContaining("요청에 해당하는 핸들러를 찾을 수 없습니다."); | ||
} | ||
|
||
@DisplayName("요청에 해당하는 핸들러 어댑터를 찾을 수 없으면 예외가 발생한다.") | ||
@Test | ||
void throwsWhenHandlerAdapterNotFound() { | ||
// given | ||
registerFakeHandlerMapping("test"); | ||
when(request.getMethod()).thenReturn("GET"); | ||
when(request.getRequestURI()).thenReturn("/test"); | ||
|
||
// when & then | ||
assertThatThrownBy(() -> dispatcherServlet.service(request, response)) | ||
.isInstanceOf(ServletException.class) | ||
.hasMessageContaining("요청에 해당하는 핸들러 어댑터를 찾을 수 없습니다."); | ||
} | ||
|
||
private void registerFakeHandlerMapping(Object retVal) { | ||
dispatcherServlet.addHandlerMapping(new HandlerMapping() { | ||
@Override | ||
public void initialize() { | ||
} | ||
|
||
@Override | ||
public Object getHandler(HttpServletRequest request) { | ||
return retVal; | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.interface21.webmvc.servlet.mvc; | ||
|
||
import com.interface21.webmvc.servlet.ModelAndView; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
|
||
public interface HandlerAdapter { | ||
|
||
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; | ||
|
||
boolean canHandle(Object handler); | ||
} |
31 changes: 31 additions & 0 deletions
31
mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapterRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.interface21.webmvc.servlet.mvc; | ||
|
||
import com.interface21.webmvc.servlet.mvc.tobe.ControllerHandlerAdapter; | ||
import com.interface21.webmvc.servlet.mvc.tobe.HandlerExecutionHandlerAdapter; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
public class HandlerAdapterRegistry { | ||
|
||
private final List<HandlerAdapter> handlerAdapters; | ||
|
||
public HandlerAdapterRegistry() { | ||
this.handlerAdapters = new ArrayList<>(); | ||
} | ||
|
||
public void initialize() { | ||
handlerAdapters.add(new ControllerHandlerAdapter()); | ||
handlerAdapters.add(new HandlerExecutionHandlerAdapter()); | ||
} | ||
|
||
public void addHandlerAdapter(HandlerAdapter adapter) { | ||
handlerAdapters.add(adapter); | ||
} | ||
|
||
public Optional<HandlerAdapter> getHandlerAdapter(Object handler) { | ||
return handlerAdapters.stream() | ||
.filter(adapter -> adapter.canHandle(handler)) | ||
.findFirst(); | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerMapping.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.interface21.webmvc.servlet.mvc; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
|
||
public interface HandlerMapping { | ||
|
||
void initialize(); | ||
|
||
Object getHandler(HttpServletRequest request); | ||
} |
Oops, something went wrong.