
Spring으로 상품을 관리할 수 있는 서비스를 만들어보자 -상품 도메인 모델- 1.상품 ID 2.상품명 3.가격 -상품 관리 기능- 1.상품 목록 2.상품 상세 3.상품 등록 4.상품 수정 요구사항이 정리되고 디자이너, 웹 퍼블리셔, 백엔드 개발자가 업무를 나누어 진행한다. 디자이너: 요구사항에 맞도록 디자인하고, 디자인 결과물을 웹 퍼블리셔에게 넘겨준다. 웹 퍼블리셔: 다자이너에서 받은 디자인을 기반으로 HTML, CSS를 만들어 개발자에게 제공한다. 백엔드 개발자: 디자이너, 웹 퍼블리셔를 통해서 HTML 화면이 나오기 전까지 시스템을 설계하고, 핵심 비즈니스 모델을 개발한다. 이후 HTML이 나오면 이 HTML을 뷰 템플릿으로 변환해서 동적으로 화면을 그리고, 또 웹 화면의 흐름을 제어한다. Re..

ArgumentResolver 애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있다. HttpServletRequest , Model 은 물론이고, @RequestParam , @ModelAttribute 같은 애노테이션 그리고 @RequestBody , HttpEntity 같은 HTTP 메시지를 처리하는 부분까지 매우 큰 유연함을 보여준다. 이렇게 파라미터를 유연하게 처리할 수 있는 이유가 바로 ArgumentResolver 덕분이다. 애노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdapter 는 바로 이 ArgumentResolver 를 호출해서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성한다. 그리고 이렇게 파리미터의 값이 모두 준비..

@RequestMapping 은 URL 경로를 템플릿화 할 수 있는데, @PathVariable 을 사용하면 매칭 되는 부분을 편리하게 조회할 수 있다. @PathVariable 의 이름과 파라미터 이름이 같으면 생략할 수 있다. 주소에서 userId를 매핑할 수 있다. @GetMapping("mapping/{userId}") public String mappingPath(@PathVariable("userId") String data){ log.info("mappingPath userId={}", data); return "ok"; } 다중 사용도 가능하다. @GetMapping("/mapping/users/{userId}/orders/{orderId}") public String mappingPath..

직접만든 MVC와 스프링 MVC 비교 스프링 MVC 구조 FrontController -> DispatcherServlet handlerMappingMap -> HandlerMapping MyHandlerAdapter -> HandlerAdapter ModelView -> ModelAndView viewResolver -> ViewResolver MyView -> View 동작 순서 1. 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다. 2. 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다. 3. 핸들러 어댑터 실행: 핸들러 어댑터를 실행한다. 서블릿이 호출되면 HttpServlet 이 제공하는 serivce() 가 호출된다. 스프링 MVC는 D..
http://localhost:8080/front-controller/v5/v3/members/new-form 주소가 들어오면 handlerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3()); Object handler = getHandler(request); MemberFromControllerV3가 반환된다. MyHandlerAdapter adapter = getHandlerAdapter(handler); private MyHandlerAdapter getHandlerAdapter(Object handler) { for (MyHandlerAdapter adapter : handlerAdapter..

Controller 인터페이스를 구현한 Controller가 실행되려면 HandlerMapping, HandlerAdapter 두 가지가 필요하다. HandlerMapping Handler Mapping에서 특정 Controller를 찾을 수 있어야 함 HandlerAdapter Handler Mapping을 통해 찾은 Handler를 실행할 수 있는 Handler Adapter가 필요 -과정- 1. 클라이언트의 HTTP 요청 2. 요청 URI 매핑 정보로 FrontController 조회 3. Handler Mapping 정보에서 Handler조회 4. Handler Mapping 정보에서 찾은 Handler를 처리할 수 있는 Handler Adapter를 Handler Adapter목록에서 조회 My..

서블릿 종속성 제거 컨트롤러 입장에서 HttpServletRequest, HttpServletResponse이 꼭 필요할까? 요청 파라미터 정보는 자바의 Map으로 대신 넘기도록 하면 지금 구조에서는 컨트롤러가 서블릿 기술을 몰라도 동작할 수 있다. 그리고 request 객체를 Model로 사용하는 대신에 별도의 Model 객체를 만들어서 반환하면 된다. 우리가 구현하는 컨트롤러가 서블릿 기술을 전혀 사용하지 않도록 변경해보자. 이렇게 하면 구현 코드도 매우 단순해지고, 테스트 코드 작성이 쉽다. 뷰 이름 중복 제거 컨트롤러에서 지정하는 뷰 이름에 중복이 있는 것을 확인할 수 있다. 컨트롤러는 뷰의 논리 이름을 반환하고, 실제 물리 위치의 이름은 프론트 컨트롤러에서 처리하도록 단순화 하자. 이렇게 해두면..

모든 컨트롤러에서 뷰로 이동하는 부분에 중복이 있고, 깔끔하지 않다. String viewPath = "/WEB-INF/views/new-form.jsp"; RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath); dispatcher.forward(request, response); 이 부분을 깔끔하게 분리하기 위해 별도로 뷰를 처리하는 객체를 만들자. http://localhost:8080/front-controller/v2/members/new-form 위의 주소가 들어오면 아래의 서블릿이 호출이 된다. @WebServlet(name = "FrontControllerServletV2", urlPatterns = "/front-con..

본격적으로 스프링 웹 MVC를 공부하기 전에 스프링 웹 MVC의 핵심인 FrontController에 대해 알아보자 FrontController 패턴 특징 1. 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음 2. 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출 3. 입구를 하나로 4. 공통 처리 가능 5. 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨 프론트 컨트롤러 도입 - v1 1. 클라이언트가 HTTP요청을 한다. 2. Front Conrtoller가 그 요청을 받아, HTTP요청에서 매핑된 정보를 매핑 정보에서 찾는다. 3. 매핑 정보에서 찾을 Controller를 호출한다. 4. Controller에서 View(JSP)로 forward한다. 5. JSP가 ..

MVC는 1.Model 2. View 3. Controller 의 약자이다. 컨트롤러: HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다. 모델: 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다. 뷰: 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다. 우리는 외부에서 직접 JSP에 접근할 수 없도록 설계를 해야한다(보안문제) WEB-INF > 이 경로안에 JSP가 있으면 외부에서 직접 JSP를 호출할 수 없다. 우리가 기대하는 것은 항..

OrderService의 할인정책을 위한 코드 public class OrderServiceImpl implements OrderService{ private final MemberRepository memberRepository = new MemoryMemberRepository(); private DiscountPolicy discountPolicy; @Override public Order createOrder(Long memberId, String itemName, int itemPrice) { Member member = memberRepository.findById(memberId); int discountPrice = discountPolicy.discount(member, itemPrice..