1. DispatcherServlet 구조와 동작 순서
DispatcherServlet 은 스프링 웹 어플리케이션의 핵심 컴포넌트
클라이언트로부터 HTTP 요청을 받아 URL과 HTTP 매서드에 매핑되는 핸들러 조회 및 응답 반환
http 헤더 정보, 컨텐츠 타입 등의 정보를 활용하기도 함

// org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
// 1. 핸들러 조회: @RequestMapping 등의 어노테이션을 기반으로 처리할 컨트롤러 결정
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2. 핸들러 어댑터 조회: 컨트롤러에 알맞은 처리 방식 제공
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 핸들러 어댑터 실행
// 4. 핸들러 실행(@RequestMapping 지정 메서드 실행)
// 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. 뷰 리졸버를 통해서 뷰 찾기:
// application.properties에서 설정한 spring.mvc.view.prefix, spring.mvc.view.suffix, 지정 메서드의 문자열 리턴값을 통한 경로 설정
// 7. View 반환
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// 8. 뷰 렌더링
view.render(mv.getModelInternal(), request, response);
}
2. 구현
// Memeber (DTO)
...
import
...
@Getter @Setter
public class Member {
private Long id;
private String username;
private int age;
public Member() {
}
public Member(String username, int age) {
this.username = username;
this.age = age;
}
}
// MemberRepository (DAO)
...
import
...
public class MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
private static final MemberRepository instance = new MemberRepository();
public static MemberRepository getInstance(){
return instance;
}
// 싱글톤으로 만들때는 private으로 생성자를 생성해 아무나 생성하지 못하도록 막음
private MemberRepository(){
}
public Member save(Member member){
member.setId(++sequence);
store.put(member.getId(),member);
return member;
}
public Member findById(Long id){
return store.get(id);
}
public List<Member> findAll(){
// new 로 선언한 이유: store 값들을 보호하려고
return new ArrayList<>(store.values());
}
public void clearStore(){
store.clear();
}
}
//application.properties
...
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
...
//SpringMemberController (핸들러/컨트롤러)
...
import
...
@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberController {
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";
}
}
3. 동작 단계별 상세 설명
1. 핸들러 조회와 우선순위
- @RequestMapping 등으로 요청 URL과 매핑하는 핸들러(컨트롤러) 조회
- 1. RequestMappingHandlerMapping: 먼저 어노테이션 기반의 컨트롤러인 @RequestMapping 조회
- 2. BeanNameUrlHandlerMapping: 없다면 스프링 빈의 이름으로 핸들러 조회, @Component 사용
2. 어댑터 조회와 핸들러 어댑터 처리 우선순위
- 핸들러 어댑터는 컨트롤러에 알맞은 처리 방식 제공
- 1. RequestMappingHandlerAdapter : @RequestMapping 사용시, 어노테이션 기반 컨트롤러 어댑터 사용
- 2. HttpRequestHandlerAdapter: HttpRequestHandler 상속시 사용되는 컨트롤러 어댑터
- 3. SimpleControllerHandlerAdapter: 컨트롤러 인터페이스 상속시 사용 되는 컨트롤러 어댑터
3. handle(handler): 핸들러 어댑터 실행
4. 핸들러 호출
- @RequestMapping 지정 메서드 실행
5. ModelAndView 반환
- 모델과 뷰정보 담아서 반환
6. ViewResolver 호출
- application.properties 에서 설정한 spring.mvc.view.prefix, spring.mvc.view.suffix, 지정 메서드의 문자열 리턴값을 통한 경로 설정
- 물론 전체 경로로도 호출 가능함
7. View 반환
8. View 랜더링
4. 기타 코멘트
- DTO: 특정 테이블 정보를 레코드 단위로 정의 해놓은 클래스
- DAO: DB 접속, 명령 전송을 전담하는 클래스
- DTO, DAO는 Django models.py 에서 하는 역할을 수행하는 듯 하다
- 실무에선 99% 어노테이션 기반의 컨트롤러를 사용
- mv.addObject("member", member): Model 데이터 추가
'백앤드 개발 > Java & Spring' 카테고리의 다른 글
[Spring boot] 로그인, 로그아웃 구현 by 스프링 시큐리티 (0) | 2023.11.30 |
---|---|
[Spring boot] 타임리프 (0) | 2023.11.22 |
[Spring boot] 서블릿, JSP을 활용한 MVC 패턴 (0) | 2023.11.14 |
[Java] 참조형 변수 (0) | 2023.11.07 |
[Spring boot] 회원, 주문 프로젝트 - 스프링으로 전환 (0) | 2023.11.07 |