일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- jpa 활용
- 예제 도메인 모델
- JPA
- 페이징
- 기본문법
- JPA 활용 2
- Spring Data JPA
- 트위터
- QueryDSL
- 일론머스크
- 실무활용
- JPQL
- 임베디드 타입
- 타임리프 문법
- 컬렉션 조회 최적화
- JPA 활용2
- 로그인
- 검증 애노테이션
- Bean Validation
- 스프링 mvc
- 김영한
- 스프링
- API 개발 고급
- 스프링MVC
- 스프링 데이터 JPA
- 불변 객체
- 프로젝트 환경설정
- 벌크 연산
- 값 타입 컬렉션
- 타임리프
- Today
- Total
RE-Heat 개발자 일지
스프링 MVC 1편 - [5] 스프링 MVC 구조 이해 본문
출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -
www.inflearn.com
인프런 김영한님의 스프링 MVC 1편 강의를 듣고 정리한 내용입니다.
[1] 스프링 MVC 전체 구조
직접 만든 스프링 MVC와 스프링 MVC 구조 비교
■ 직접 만든 프레임워크 => 스프링 MVC 비교
FrontController => DispatcherServlet
handlerMappingMap => HandlerMapping
MyHandlerAdapter => HandlerAdapter
ModelView => ModelAndView
viewResolver => ViewResolver
MyView => View
사실상 구조 자체는 동일. 이름만 바뀐 수준
DispathcerServlet의 구조
- 디스패처 서블릿이 스프링 MVC의 핵심
- 스프링 MVC도 우리가 만든 프론트 컨트롤러 패턴으로 구현됨.
- DispatcherServlet도 우리가 구현한 FrontControllerServlet과 마찬가지로 HttpServlet을 사용.
- DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet 몇 단계를 거침
- DispatcherServlet은 모든 경로(urlPatterns="/")에 대해 핸들러 어댑터·핸들러를 매핑해준다.
- 서블릿이 호출되면 HttpServlet의 service()가 자동 호출 -> 결국, DispatcherServlet의 핵심인 doDisptach()에 도달
DispatcherSerlvet.doDispatch() 일부 발췌
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
// 1. 핸들러 조회
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2. 핸들러 어댑터 조회 - 핸들러를 처리할 수 있는 어댑터
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 핸들러 어댑터 실행 -> 4. 핸들러 어댑터를 통해 핸들러 실행 -> 5. ModelAndView 반환
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {// 뷰 렌더링 호출
render(mv, request, response);
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
// 6. 뷰 리졸버를 통해서 뷰 찾기, 7. View 반환
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// 8. 뷰 렌더링
view.render(mv.getModelInternal(), request, response);
}
스프링 MVC 동작순서
- 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회.
- 핸들러 어댑터 조회 후 실행: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회 후 실행
- 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행.
- ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환.
- viewResolver 호출: 뷰 리졸버를 찾고 실행.
- View 반환: 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환
- 뷰 렌더링: 뷰를 통해서 뷰를 렌더링한다.
[2] 핸들러 매핑과 핸들러 어댑터
핸들러 매핑 : 해당하는 핸들러(컨트롤러) 객체를 찾아 반환하는 역할
스프링 빈 이름으로 핸들러를 찾을 수 있는 핸들러 매핑이 필요
핸들러 어댑터 : 컨트롤러를 실행하고 그 결과를 ModelAndView 규격에 맞춰 반환
컨트롤러 인터페이스를 실행할 수 있는 핸들러 어댑터를 찾아 실행해야 함.
HandlerMapping 순서
0 = RequestMappingHandlerMapping : 어노테이션 기반의 컨트롤러인 @RequestMapping에서 찾는다
1 = BeanNameUrlHandlerMapping : 스프링 빈 이름으로 찾는다
HandlerAdapter 순서
0 = RequestMappingHandlerAdapter : 어노테이션 기반의 컨트롤러인 @RequestMapping에서 찾는다
1 = HttpRequestHandlerAdapter : HttpRequestHandler 처리
2 = SimpleControllerHandlerAdapter : Controller 인터페이스 처리
OldController
@Component("/springmvc/old-controller") //스프링빈 이름
public class OldController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("OldController.handleRequest");
return null;
}
}
1] 컨트롤러가 /spring/mvc/old-controller라는 이름의 빈으로 등록 -> 2] 빈의 이름으로 URL을 매핑
OldController의 핸들러 매핑, 어댑터는
HandlerMapping = BeanNameUrlHandlerMapping, HandlerAdpater = SimpleControllerHandler Adapter 순
MyHttpRequestHandler
@Component("/springmvc/request-handler")
public class MyHttpRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("MyHttpRequestHandler.handleRequest");
}
}
HandlerMapping = BeanNameUrlHandlerMapping, HandlerAdpater = HttpReqeustHandlerAdapter
가장 우선순위가 높은 핸들러 매핑 : RequestMappinghandlerMapping
핸들러 어댑터 : RequestMappinghandlerAdapter
현재 스프링에서 주로 사용하는 애노테이션 기반의 컨트롤러를 지원하는 매핑과 어댑터.
참고 : @RequestMapping의 앞글자를 따서 이름을 붙임.
[3] 뷰 리졸버
@Component("/springmvc/old-controller") //스프링빈 이름
public class OldController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("OldController.handleRequest");
return new ModelAndView("new-form");
}
}
OldController가 ModelAndView 객체에 논리이름을 담아 반환할 수 있게 변경.
application.properties prefix와 suffix 추가해 뷰 리졸버 실행
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
스프링부트는 InternalResourceViewResolver라는 뷰 리졸버를 자동으로 등록. application.properties에 등록한 spring.mvc.view.prefix, spring.mvc.view.suffix 설정 정보를 사용해 등록함.
뷰 리졸버 동작 방식 - 스프링 MVC
1 = BeanNameViewResolver : 빈 이름으로 뷰를 찾아서 반환한다.
2 = InternalResourceViewResolver : JSP를 처리할 수 있는 뷰를 반환한다.
뷰 리졸버도 스프링에서 등록한 뷰 리졸버들을 탐색해 우선순위에 따라 뷰 리졸버를 사용함.
동작 순서
1. 핸들러 어댑터 호출 -> 핸들러 어댑터 통해 "new-form"이라는 논리 뷰 이름 획득
2. ViewResolver 호출
BeanNameViewResolver : new-form이라는 스프링 빈이 등록되지 않았으므로 PASS
InternalResourceViewResolver : InternalResourceView를 반환
3. InternalResourceView는 JSP처럼 fowar()를 호출해서 처리할 수 있는 경우에 사용
4. view.render()가 호출되고 InternalResourceView는 forward()를 사용해 JSP 실행
참고] JSP는 forward()로 해당 JSP로 이동해야 실행. 타임리프 등 그 외 뷰 템플릿은 forward() 없이 바로 렌더링 됨.
[4] 스프링 MVC - 시작하기
@RequestMapping
애노테이션을 활용해 매우 유연하고 실용적인 컨트롤러를 만듦. 이게 @RequestMapping 실무에서는 거의 100% 이 방식을 사용한다.
참고] 과거엔 MVC부분이 취약해 스트럿츠 + 스프링을 사용하는 방법이 애용됐으나, @RequestMapping 기반의 애노테이션 컨트롤러가 도입되면서 스프링이 대세로 자리매김함.
SpringMemberFormControllerV1
@Controller
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form")
public ModelAndView process(){
return new ModelAndView("new-form");
}
}
SpringMemberSaveControllerV1
@Controller
public class SpringMemberSaveControllerV1 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/springmvc/v1/members/save")
public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
ModelAndView mv = new ModelAndView("save-result");
mv.addObject("member", member);
return mv;
}
}
SpringMemberListControllerV1
@Controller
public class SpringMemberListControllerV1 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/springmvc/v1/members")
public ModelAndView process() {
List<Member> members = memberRepository.findAll();
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", members);
return mv;
}
}
@Controller : 스프링이 자동으로 스프링 빈으로 등록. 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식
@RequestMapping : 요청정보 매핑. 해당 URL이 호출되면 이 메서드가 호출됨. 애노테이션을 기준으로 호출되기 때문에 메소드 이름은 임의로 지어도 무방.
RequestMappingHandlerMapping은 스프링 빈 중에서 @RequestMapping 또는 @Controller가 클래스 레벨에 붙어있는 경우 매핑 정보로 인식.
방법 1] @Controller를 클래스 레벨에 넣기
2] 클래스 레벨에 @Component @RequestMapping 붙이기
3] ServletApplication에 Bean을 직접 등록하기
[5] 스프링 MVC - 컨트롤러 통합
SpringMemberFormControllerV1 + SpringMemberSaveControllerV1 + SpringMemberListControllerV1을 하나로 통합
@Controller
@RequestMapping("/springmvc/v2/members")
public class SpringMemberControllerV2 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/new-form")
public ModelAndView newForm(){
return new ModelAndView("new-form");
}
@RequestMapping("/save")
public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
ModelAndView mv = new ModelAndView("save-result");
mv.addObject("member", member);
return mv;
}
@RequestMapping
public ModelAndView members() {
List<Member> members = memberRepository.findAll();
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", members);
return mv;
}
}
회원가입 폼 + 회원가입 등록 + 회원가입 목록보기를 한 클래스에 담음
[6] 스프링 MVC - 실용적인 방식
SpringMemberControllerV3
@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@GetMapping("/new-form")
public String newForm(){
return "new-form";
}
@PostMapping("/save")
public String save(@RequestParam("username") String username,
@RequestParam("age") int age,
Model model
) {
Member member = new Member(username, age);
memberRepository.save(member);
model.addAttribute("member", member);
return "save-result";
}
@GetMapping
public String members(Model model) {
List<Member> members = memberRepository.findAll();
model.addAttribute("members", members);
return "members";
}
}
1. @RequestMapping에 method 도입
- 1] @RequestMapping(value = "/new-form", method = RequestMethod.GET)
- GET을 명시해 POST 방식 등 다른 방식으로 못 쓰게 막음
- 2] @GetMapping
- 번잡한 method=RequestMethod.GET을 제거하기 위해 애노테이션을 수정함.
=> GET, POST 뿐만 아니라 PUT, DELETE, PATCH를 위한 애노테이션도 존재
2. 매개변수 - 파라미터
@RequestParam
HttpServletRequest의 값을 쉽게 꺼내올 수 있음
기존방식
@RequestMapping("/save")
public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
새로운 방식
@PostMapping("/save")
public String save(@RequestParam("username") String username,
@RequestParam("age") int age,
@RequestParam("username")은 request.getParamerter("username")과 거의 같은 코드라고 보면 됨.
3. 매개변수 - Model
파라미터로 Model 객체를 받을 수 있음. 스프링이 제공.
model.addAtrribute("member", member) 이런 식으로 저장해 jsp파일로 forward 가능
4. ViewName 직접 반환
ModelAndView 객체를 반환하던 기존 버전과는 달리 논리이름만 반환 가능.
: String username = request.getParamater("username)
'백엔드 > 스프링' 카테고리의 다른 글
스프링 MVC 1편 - [6] 기본 기능(하편) (0) | 2023.07.02 |
---|---|
스프링 MVC 1편 - [6] 기본 기능(상편) (0) | 2023.06.30 |
스프링 MVC 1편 - [4] MVC 프레임워크 만들기 (1) | 2023.06.23 |
스프링 MVC 1편 - [3] 서블릿·JSP·MVC 패턴 (0) | 2023.06.22 |
스프링 MVC 1편 - [2] 서블릿 (0) | 2023.06.21 |