스프링 MVC 전체 구조
1. 직접 만든 MVC 프레임워크 구조와 스프링 MVC 구조 비교
1.1 직접 만든 MVC 프레임워크 구조
1.2 스프링 MVC 구조
※ 직접 만든 프레임워크와 스프링 MVC는 명칭만 조금 다르고 거의 같은 구조와 기능으로 되어있음
- FrontController → DispatcherServlet
- handlerMappingMap → HandlerMapping
- MyHandlerAdapter → HandlerAdapter
- ModelView → ModelAndView
- viewResolver → ViewResolver
- MyView → View
2. DispatcherServlet
2.1 DispatcherServlet 개요
• FrontController와 마찬가지로 부모 클래스에서 HttpServlet을 상속받아 사용하며, 서블릿으로 동작.
◦ DispatcherServelt → FrameworkServlet → HttpServletBean → HttpServlet
• 스프링 부트 구동시 DispatcherServlet을 서블릿으로 자동등록하며 모든 경로(urlPatterns="/")에 대해 매핑.
• Spring MVC 역시 프론트 컨트롤러 패턴으로 구현되어 있고 DispatcherServlet이 프론트 컨트롤러의 역할
※ DispatcherServlet의 다이어그램
2.2 DispatcherServlet 요청 흐름 및 핵심 로직 분석
• DispatcherServlet 요청 흐름
- 서블릿이 호출되면 HttpServlet이 제공하는 service() 메서드가 호출
- 스프링 MVC는 DispatcherServlet의 부모인 FrameworkServlet에서 service()를 오버라이드 해둠
- FrameworkServlet.service()를 시작으로 여러 메서드가 호출되며 DispatcherServlet.doDispatch()가 호출
• DispacherServlet.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) {
// 적절한 핸들러를 찾지 못한경우 404 에러코드를 반환
noHandlerFound(processedRequest, response);
return;
}
// 2.핸들러 어댑터 조회-핸들러를 처리할 수 있는 어댑터 (찾은 핸들러를 처리할 수 있는 핸들러 어댑터를 찾아줌, 만약 찾지 못할경우 ServletException 발생)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 핸들러 어댑터 실행 -> 4. 핸들러 어댑터를 통해 핸들러 실행 -> 5. ModelAndView 반환
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// 실제 코드는 복잡하게 되있는데 결국 render() 메서드를 호출
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;
// 6. 뷰 리졸버를 통해서 뷰 찾기
// 7. View 반환
String viewName = mv.getViewName();
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// 8. 뷰 렌더링
// ModelAndView에서 View를 찾아 뷰 리졸버를 이용해 뷰의 물리적 이름을 완성해서 forward 해줌
view.render(mv.getModelInternal(), request, response);
}
3. 스프링 MVC 동작 순서
① 핸들러 조회 : 핸들러 매핑을 통해 URL에 매핑된 핸들러(컨트롤러) 조회
② 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터 조회
③ 핸들러 어댑터 실행: 핸들러 어댑터 실행
④ 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행
⑤ ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해 반환.
⑥ viewResolver 호출: 뷰 리졸버를 찾아 실행
(JSP: InternalResourceViewResolver가 자등 등록되어 사용됨)
⑦ View 반환: 뷰 리졸버는 뷰의 논리 이름을 물이 이름으로 바꾸고 렌더링 역할을 담당하는 뷰 객체 반환.
(JSP: InternalResourceView(JstlView)를 반환하는데, 내부에는 forward() 가 있음)
⑧ 뷰 렌더링: 뷰를 통해서 뷰를 렌더링
4. 스프링 MVC 인터페이스
• SpringMVC는 DispatcherServlet 코드 변경을 하지않고도 원하는 기능을 변경하거나 확장을 할 수 있음.
그리고 이런 인터페이스들을 구현해 DispatcherServlet에 등록하면 커스텀 컨트롤러를 만들 수도 있음
• 주요 인터페이스 목록
- 핸들러 매핑: org.springframework.web.servlet.HandlerMapping
- 핸들러 어댑터: org.springframework.web.servlet.HandlerAdapter
- 뷰 리졸버: org.springframework.web.servlet.ViewResolver
- 뷰: org.springframework.web.servlet.View
핸들러 매핑과 핸들러 어댑터
※ 직접 만든 MVC 프레임워크에서는 핸들러 매핑과 핸들러 어댑터를 단순하게 Map, List 콜렉션을 이용해서 등록한 뒤 검색해서 사용함.
※ springMVC에서는 어떻게 핸들러 매핑과 핸들러 어댑터를 사용하는지 확인 해보기.
1. 과거 버전의 스프링 컨트롤러(Controller인터페이스를 구현)
1.1 예제 1
package hello.servlet.web.springmvc.old;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
// @Controller 애노테이션과는 다른 Controller 인터페이스임
// /springmvc/old-controller 라는 이름의 스프링 빈으로 등록되었고, 스프링 빈의 이름으로 URL 매핑함.
// 스프링에서 name을 urlpatterns랑 맞춤
@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.2 스프링 MVC가 URL을 통해 컨트롤러를 찾는 방법
• 이 컨트롤러가 호출되기 위해 HandlerMapping과 HandlerAdapter가 필요
◦ HandlerMapping
- 핸들러 매핑에서 이 컨트롤러를 찾을 수 있어야 함.
- 스프링 빈의 이름으로 핸드러를 찾을 수 있는 핸들러 매핑이 필요.
- 직접 만든 MVC 프레임워크의 관련 소스
private final Map<String, Object> handlerMappingMap = new HashMap<>();
private void initHandlerMappingMap() {
handlerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3());
handlerMappingMap.put("/front-controller/v5/v3/members/save", new MemberSaveControllerV3());
handlerMappingMap.put("/front-controller/v5/v3/members", new MemberListControllerV3());
handlerMappingMap.put("/front-controller/v5/v4/members/new-form", new MemberFormControllerV4());
handlerMappingMap.put("/front-controller/v5/v4/members/save", new MemberSaveControllerV4());
handlerMappingMap.put("/front-controller/v5/v4/members", new MemberListControllerV4());
}
◦ HandlerAdapter
- 핸들러 매핑을 통해 찾은 핸들러를 실행할 수있는 핸들러 어댑터가 필요
- Controller 인터페이스를 실행할 수 있는 핸들러 어댑터를 찾아야 함.
- 직접 만든 MVC 프레임워크의 관련 소스
private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();
// 사용할 어댑터를 리스트 콜렉션에 추가하는 작업을 하는 메서드
private void initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
handlerAdapters.add(new ControllerV4HandlerAdapter());
}
※ 스프링 부트에서는 직접 만든 MVC프레임워크처럼 Map이나 List 같은 객체로 저장하는것이 아니라
인터페이스로 구현된 핸들러 매핑과 핸들러 어댑터 객체를 자동으로 등록해줌
• 스프링 부트가 자동 등록하는 핸들러 매핑 구현체와 핸들러 어댑터 구현체
- HandlerMapping을 찾는 우선순위
0 = RequestMappingHandlerMapping : 애노테이션 기반의 컨트롤러인 @Requestmapping에서 핸들러를 찾음. 1 = BeanNameUrlHandlerMapping : 스프링 빈의 이름으로 핸들러를 찾음. ... |
- HandlerAdapter을 찾는 우선순위
0 = RequestmappingHandlerAdapter : 애노테이션 기반의 컨트롤러인 @Requestmapping를 사용한 핸들러를 처리 1 = HttpRequestHandlerAdapter : HttpRequesthandler 처리 2 = SimpleControllerHandlerAdapter : Controller 인터페이스(애노테이션 X) 처리 ... |
• 예제1의 OldController의 핸들러 매핑과 핸들러 어댑터 찾고 실행하는 순서
※ 핸들러 매핑, 핸들러 어댑터 모두 순서대로 찾고 만약 없을 경우 다음 순서로 넘어간다.
- 핸들러 매핑으로 핸들러 조회
: HandlerMapping에서 순서대로 수행하는데 1 = BeanNameUrlHandlerMapping에서 검색이 되기에 해당 URL을 빈 이름으로 등록한 핸들러(OldController)가 매핑되어 반환됨.
- 핸들러 어댑터 조회
: 어댑터를 순서대로 호출하며 이전에 찾은 핸들러(OldController)가 실행가능한 어댑터를 찾음.
이 중에서 SimpleControllerHandlerAdapter가 Controller 인터페이스를 실행 가능하기에 반환.
- 핸들러 어댑터 실행
: 반환된 어댑터(SimpleControllerHandlerAdapter)는 핸들러인 OldController를 내부에서 실행하면서 핸들러 정보를 넘겨주고 그 결과를 반환.
2. 과거 버전의 스프링 컨트롤러(HttpRequestHandler인터페이스를 구현)
2.1 예제 2
package hello.servlet.web.springmvc.old;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestHandler;
@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");
}
}
2.2 스프링 MVC가 예제2의 HttpRequestHandler 핸들러 매핑과 핸들러 어댑터 찾고 실행하는 순서
- 핸들러 매핑으로 핸들러 조회
: HandlerMapping에서 순서대로 수행하는데 1 = BeanNameUrlHandlerMapping에서 검색이 되기에 해당 URL을 빈 이름으로 등록한 핸들러(MyHttpRequestHandler)가 매핑되어 반환됨.
- 핸들러 어댑터 조회
: 어댑터를 순서대로 호출하며 이전에 찾은 핸들러(MyHttpRequestHandler)가 실행가능한 어댑터를 찾음.
이 중에서 HttpRequestHandlerAdapter가 HttpRequestHandler 인터페이스를 실행 가능하기에 반환.
- 핸들러 어댑터 실행
: 반환된 어댑터(HttpRequestHandlerAdapter)는 핸들러인 MyHttpRequestHandler를 내부에서 실행하면서 핸들러 정보를 넘겨주고 그 결과를 반환.
※ @RequestMapping
- 가장 우선순위가 높은 핸들러 매핑과 핸들러 어댑터는 RequestMappingHandlerMapping, RequestMappingHandlerAdapter임.
- @RequestMapping 의 앞글자를 따서 만든 이름인데, 이것이 바로 지금 스프링에서 주로 사용하는 애노테이션 기반의 컨트롤러를 지원하는 매핑과 어댑터이다.
- 실무에서는 99.9% 이 방식의 컨트롤러를 사용.
- 추후에 다룰 예정
뷰리졸버
1. 뷰리졸버 적용 예시(OldController를 통해)
• 소스 코드
- OldController
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("OldController.handleRequest");
return new ModelAndView("new-form");
}
※ 컨트롤러는 정상 실행 되지만 view 페이지 인 new-form이 호출이 안되고 404에러 발생
- 원하는 viewPath "/WEB-INF/views/new-form.jsp"를 물리이름으로 완성하지 못했기 때문에 페이지를 못찾음
- 어떤 경로인지 상위 경로 prefix와 이게 html인지 jsp인지 확장자를 저장한 suffix를 등록 해줘야함
- application.properties라는 속성 파일에 다음 코드를 추가
spring.mvc.view.prefix=/WEB-INF/views/ spring.mvc.view.suffix=.jsp |
2. 뷰 리졸버 동작 방식
※ 스프링 부트는 InternalResourceViewResolver 라는 뷰 리졸버를 자동으로 등록하는데,
이때 application.properties 에 등록한 spring.mvc.view.prefix , spring.mvc.view.suffix 설정 정보를 사용해서 등록함.
2.1 구성도
※ 위 springMVC 구조에서 6번, 7번 과정에서 뷰 리졸버가 동작.
※ 스프링 부트는 구동시 자동 등록하는 뷰 리졸버가 많은데, 이중 몇가지를 말하면 다음과 같음.
1 = BeanNameViewResolver : 빈 이름으로 뷰를 찾아서 반환한다. (예: 엑셀 파일 생성 기능에 사용) 2 = InternalResourceViewResolver : JSP를 처리할 수 있는 뷰를 반환한다. |
2.2 예제 OldController의 뷰리졸버 동작 순서
① 핸들러 어댑터 호출
- 핸들러 어댑터를 통해 new-form 이라는 논리 뷰 이름을 획득
② ViewResolver 호출
- new-form 이라는 뷰 이름으로 viewResolver를 순서대로 호출.
- BeanNameViewResolver 는 new-form 이라는 이름의 스프링 빈으로 등록된 뷰를 찾아야 하는데 없음.
- 스프링부트에 자동으로 등록된 InternalResourceViewResolver가 호출.
③ InternalResourceViewResolver
- 이 뷰 리졸버는 InternalResourceView 를 반환
④ 뷰 - InternalResourceView
- InternalResourceView 는 JSP처럼 포워드 forward() 를 호출해서 처리할 수 있는 경우에 사용
⑤ view.render()
- view.render() 가 호출되고 InternalResourceView 는 forward() 를 사용해서 JSP를 실행
※ InternalResourceViewResolver는 만약 JSTL 라이브러리가 있으면 InternalResourceView를
상속받은 JstlView를 반환.
JstlView 는 JSTL 태그 사용시 약간의 부가 기능이 추가.
※ 다른 뷰는 실제 뷰를 렌더링하지만, JSP의 경우 forward() 통해서 해당 JSP로 이동(실행)해야 렌더링이
됨.
JSP를 제외한 나머지 뷰 템플릿들은 forward() 과정 없이 바로 렌더링.
※ Thymeleaf 뷰 템플릿을 사용하면 ThymeleafViewResolver 를 등록해야 함.
최근에는 라이브러리만 추가하면 스프링 부트가 이런 작업도 모두 자동화해줌
스프링 MVC 적용하기(version 1)
1. @RequestMapping
• 기존에 @WebServlet에서 urlPattern을 사용해주고, Component에 빈 이름으로 URL을 작성해서 사용해지만,
스프링 MVC가 @RequestMapping 애노테이션을 사용해서 유연하고 편리하게 컨트롤러 구현이 가능해짐.
• @RequestMapping이 사용하는 HandlerMapping과 HandlerAdapter
◦ RequestMappingHandlerMapping
◦ RequestMappingHandlerAdapter
※ 가장 우선순위가 높은 핸들러 매핑과 핸들러 어댑터
2. 코드
2.1 SpringMemberFormControllerV1 - 회원 등록 컨트롤러
package hello.servlet.web.springmvc.v1;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/* @Controller와 같음
// 스프링 빈으로 등록
@Component
// 핸들러로 인식
@RequestMapping
*/
// 스프링이 자동으로 스프링 빈으로 등록한다. (내부에 @Component 애노테이션이 있어서 컴포넌트 스캔의 대상이 됨)
// 스프링 MVC에서 애노테이션 기반 컨트롤러(핸들러)로 인식
// RequestMapnpingHandlerMapping에서 매핑시켜줄수 있는(사용할 수 있는) 핸들러로 인식된다는 의미.
@Controller
public class SpringMemberFormControllerV1 {
// 해당 URL이 호출되면 이 메서드가 호출. 애노테이션을 기반으로 동작하기 때문에, 메서드의 이름은 임의로 지으면 됨
@RequestMapping("/springmvc/v1/members/new-form")
// 모델과 뷰 정보를 담아 반환하면 됨
public ModelAndView process() {
return new ModelAndView("new-form");
}
}
2.2 SpringMemberSaveControllerV1 - 회원 저장 컨트롤러
package hello.servlet.web.springmvc.v1;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
@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.getModel().put("member", member);
// 스프링이 제공하는 ModelAndView를 통해 Model 데이터를 추가할 때는 addObject()를 사용하면 됨
mv.addObject("member", member);
return mv;
}
}
2.3 SpringMemberListControllerV1- 회원 목록 컨트롤러
package hello.servlet.web.springmvc.v1;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
@Controller
public class SpringMemberListControllerV1 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/springmvc/v1/members")
public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
List<Member> members = memberRepository.findAll();
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", members);
return mv;
}
}
※ @Controller 애노테이션이 없어도 직접 설정영역에서 빈으로 등록해줘도 되고,
클래스영역에 @RequestMapping과 @Component 애노테이션을 사용하면 정상적으로 등록되어 사용할 수 있음.
대체적으로 그냥 @Controller를 사용함.
스프링 MVC 컨트롤러 통합(version 2)
※ 애노테이션 기반 컨트롤러 구축시 URL 매핑이 클래스 단위가 아니라 메서드 단위로 되는 것을 확인할 수 있었는데,
이러한 메서드들을 하나의 컨트롤러로 통합할수도 있음.
1. 코드
package hello.servlet.web.springmvc.v2;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
@Controller
// 기존 V1의 매핑 URL을 보면 /springmvc/v1/members 까지는 중복되는데 이런 중복되는 경로를 클래스단위의 @RequestMapping에 작성해서 경로 조합이 가능함.
@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(HttpServletRequest request, HttpServletResponse response) {
List<Member> members = memberRepository.findAll();
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", members);
return mv;
}
}
스프링 MVC 실용적인 방식(version 3)
1. 코드
package hello.servlet.web.springmvc.v3;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
/**
* v3
* Model 도입
* ViewName 직접 반환
* @RequestParam 사용
* @RequestMapping -> @GetMapping, @PostMapping
*/
@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
//@RequestMapping(value = "/new-form", method = RequestMethod.GET)
@GetMapping("/new-form")
public String newForm() {
return "new-form";
}
//@RequestMapping(value = "/save", method = RequestMethod.POST)
@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";
}
//@RequestMapping(method = RequestMethod.GET)
@GetMapping("")
public String members(Model model) {
List<Member> members = memberRepository.findAll();
model.addAttribute("members", members);
return "members";
}
}
• Model model
- save(), members()에서 Model을 파라미터로 받음.
- 추가할 데이터는 이 Model에 추가해주면 되며, 기존처럼 ModelAndView 객체를 만든 뒤 여기에 데이터를 넣어줄 필요가 없어짐.
• viewName 직접 반환
- 애노테이션 기반의 컨트롤러는 ModelAndView 뿐 아니라 ViewName을 직접 반환해도 동작함.
• @RequestParam 사용
- 스프링은 HTTP 요청 파라미터를 @RequestParam으로 받을 수 있음.
- 코드에서 사용 된 @RequestParam("username")은 request.getParameter("username")과 거의 같음.
- 그래서 GET, POST Form 방식의 데이터 가져오는것을 모두 지원한다.
• @RequestMapping → @GetMapping, @PostMapping
- @RequestMapping 애노테이션은 URL만 매칭하는게 아니라 HTTP Method도 구분할 수 있음.
- 예를들어 회원 등록 폼 요청은 GET 으로 올 때만 매핑시키려면 method 속성에 GET을 작성하면 됨.
@RequestMapping(value = "/new-form", method = RequestMethod.GET) |
- @GetMapping, @PostMapping는 이런 method를 미리 지정해둔 애노테이션으로 가독성과 편의성이 좋아 쓰기 편함.
※ Put, Delete, Patch 모두 존재
• 참고
@GetMapping, @PostMapping 내부 코드를 보면
결국 @RequestMapping 애노테이션의 메소드를 미리 지정해둔 애노테이션임
이런 애노테이션의 유연함이 스프링 사용을 편리하게 해줌.
- @GetMapping 에노테이션 소스
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
...
}
'일상의 흔적 > Study' 카테고리의 다른 글
인프런 스프링 MVC 1 (웹개발 핵심 기술) : 스프링 MVC 웹페이지 만들기 - 7 (0) | 2023.03.18 |
---|---|
인프런 스프링 MVC 1 (웹개발 핵심 기술) : 스프링 MVC 기본기능 - 6 (0) | 2023.03.17 |
인프런 스프링 MVC 1 (웹개발 핵심 기술) : MVC 프레임워크 만들기 - 4 (0) | 2023.03.16 |
인프런 스프링 MVC 1 (웹개발 핵심 기술) : 서블릿, JSP, MVC 패턴 - 3 (0) | 2023.03.16 |
인프런 스프링 MVC 1 (웹개발 핵심 기술) : 서블릿 - 2 (0) | 2023.03.16 |