Spring MVC
1. MVC 패턴
Spring Web MVC에서는 Controller, Service, DAO 계층으로 구성된 MVC 패턴을 따른다.
1) Controller 계층: 클라이언트의 요청을 처리하고, 비즈니스 로직을 실행한 뒤 Model 객체를 반환한다.
2) Service 계층: 비즈니스 로직을 수행한다.
Controller에서 전달받은 Model 객체를 가공하고, DAO에서 전달받은 데이터를 조합하여 비즈니스 로직을 실행한다.
Transaction이 분리되어 있다.
3) DAO 계층: 데이터베이스와의 상호작용을 담당한다.
데이터베이스에 접근하여 데이터를 조회하거나 수정하는 등의 작업을 수행한다.
(DAO는 데이터베이스에 접근하는 객체이고, CRUD가 분리되어 있다.)
이렇게 수행된 결과는 Service 계층으로 전달되어 가공된 후 Controller 계층으로 반환된다.
2. MVC Architecture
3. Spring MVC Request 처리 과정
(1) HTTP Request
- 클라이언트의 request가 DispatcherServlet에 도착한다.
DispatcherServlet은 맨 앞에서 요청을 받아 처리하는 controller라는 의미에서 Front Controller라고도 한다.
DispatcherServlet은 초기화할 때 @Controller, @RequestMapping과 같은 어노테이션을 다 읽어간다.
(2) Handler 조회
- HandlerMapping을 통해 클라이언트의 요청을 매핑할 적절한 Controller을 찾는다.
HandlerMapping 안에는 @Controller, @RequestMapping과 같은 어노테이션에 대한 정보가 있다.
정확하게 누구한테 위임할지는 @RequestMapping에 설정된 내용을 따른다.
(3) handle(handler)
- 어디에 매핑될 지 찾아지면 handler 메소드 호출을 통해 HandlerAdapter을 실행한다.
(HandlerAdapter가 DispatcherServlet과 Handler(Controller) 간의 통신을 중개해준다.)
(4) Handler 호출
- Handler Adapter는 Handler(Controller), 즉 우리가 작성한 실제 기능을 해야하는 메소드를 호출한다.
- cf. Controller 클래스에 작성된 메소드를 핸들러 메소드라 부른다.
(5) ModelAndView 반환
- HandlerAdapter는 DispatcherServlet으로 model 객체와 어떤 view를 사용할지(return 되는 문자열)를 ModelAndView로 변환하여 반환한다.
(6) View Resolver 호출
- DispatcherServlet은 ViewResolver을 통해 어떤 View를 사용할지 결정한다.
(7) View 반환
- ViewResolver는 View의 이름을 통해 해당하는 View 객체를 반환한다.
(8) render(model) 호출
- View 객체는 Model에 담은 데이터를 기반으로 HTML 문서를 렌더링한다.
(9) HTTP Response
- 렌더링된 HTML 문서는 DispatcherServlet을 통해 클라이언트(브라우저) 쪽으로 응답된다.
실습
1. 디렉토리 구조
2. application.yml
톰캣의 기본 포트 번호는 8080인데, 8002로 바꿔줘보았다.
# src\main\resources\application.yml
server:
port: 8002
3. 요청 발생
index.html 파일이 resources > static 에 있는데, static은 정적인 리소스를 제공할 때 사용하는 코드를 넣어둔다.
여기서 "GET 메뉴 등록 요청" button을 클릭하면 '/menu/regist' 요청이 발생한다.
<!--src\main\resources\static\index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>Spring MVC Mapping Test</h1>
<h2>1. 메소드에 요청 매핑하기</h2>
<h3>GET : /menu/regist</h3>
<button onclick="location.href='/menu/regist'">GET 메뉴 등록 요청</button>
<h3>POST : /menu/regist</h3>
<form action="/menu/regist" method="post">
<button>POST 메뉴 등록 요청</button>
</form>
<h3>GET : /menu/modify</h3>
<button onclick="location.href='/menu/modify'">GET 메뉴 수정 요청</button>
<h3>POST : /menu/modify</h3>
<form action="/menu/modify" method="post">
<button>POST 메뉴 수정 요청</button>
</form>
<h3>GET : /menu/delete</h3>
<button onclick="location.href='/menu/delete'">GET 메뉴 삭제 요청</button>
<h3>POST : /menu/delete</h3>
<form action="/menu/delete" method="post">
<button>POST 메뉴 삭제 요청</button>
</form>
<h2>2. 클래스에 매핑 요청하기</h2>
<h3>GET : /order/regist</h3>
<button onclick="location.href='/order/regist'">GET 주문 등록 요청</button>
<h3>POST : /order/modify</h3>
<form action="/order/modify" method="post">
<button>POST 주문 수정 요청</button>
</form>
<h3>POST : /order/delete</h3>
<form action="/order/delete" method="post">
<button>POST 주문 삭제 요청</button>
</form>
<h3>GET : /order/detail/{orderNo}</h3>
<button onclick="location.href='/order/detail/100'">GET 주문 상세 보기 요청</button>
</body>
</html>
(3) 요청 처리하기
애플리케이션을 구동하면 webapplicationcontext(IoC Container)가 생성된다.
그럼 이제 여기에 우리가 우리가 작성한 클래스(POJOs)를 빈으로 등록해서 처리해야 한다.
따라서 @Controller, @Service 등 어노테이션을 붙여서 빈 등록을 하자. (빈 등록이 되어 있어야 동작이 가능하다.)
DispatcherServlet은 웹 요청을 받는 즉시 @Controller가 달린 컨트롤러 클래스에 처리를 위임한다.
1) Method Mapping
@Controller는 요청과 메소드를 매핑하겠다는 의미로 하위에 @requestmapping 어노테이션을 가진 메소드를 작성할 수 있다.
package org.example.requestmapping;
@Controller // 어노테이션: bean을 등록하는 의미
public class MethodMappingTestController {
// 1. 요청 메소드 미지정(메소드 방식과 무관하게 매핑된다.)
@RequestMapping("/menu/regist")
public String menuRegist(Model model) {
/*Model 객체에 addAttribute 메서드를 이용해 key, value를 추가하면
* view(응답할 화면 쪽)에서 사용할 수 있다. -> 뒤에서 다시 다룬다.*/
model.addAttribute("message", "신규 메뉴 등록용 핸들러 메소드 호출");
/*반환하고자 하는 view의 경로를 포함한 이름을 작성한다.
* resources/templates 하위부터의 경로를 작성한다.*/
return "mappingResult";
}
// 2. 요청 메소드 지정
// cf. 주소에 해당하는 요청이 있긴 한데 메서드 타입이 올바르지 않을 경우 405 에러
@RequestMapping(value = "/menu/modify", method = RequestMethod.GET)
public String menuModify(Model model) {
model.addAttribute("message", "GET 방식의 메뉴 수정용 핸들러 메소드 호출");
return "mappingResult";
}
/*3. 요청 메소드 전용 어노테이션 (요청 메소드 지정 부분을 간결하게 사용하기 위해 어노테이션 사용 가능)*/
@GetMapping("/menu/delete")
public String getMenuDelete(Model model) {
model.addAttribute("message", "GET 방식의 메뉴 삭제용 핸들러 메소드 호출");
return "mappingResult";
}
@PostMapping("/menu/delete")
public String postMenuDelete(Model model) {
model.addAttribute("message", "POST 방식의 메뉴 삭제용 핸들러 메소드 호출");
return "mappingResult";
}
}
위에서 '/menu/regist' 요청이 발생했으므로 menuRegist() 메소드가 실행되고,
resources/templates/mappingResult.html view가 반환된다.
<!--resources\templates\mappingResult.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--h3 내부에 들어가야 할 텍스트를 message의 attribute로 처리할게-->
<h3 th:text="${message}"></h3>
</body>
</html>
2) Class Mapping
package org.example.requestmapping;
/*클래스 레벨에도 @RequestMapping 사용이 가능하다.
* URL을 공통 부분을 이용해서 설정하고 나면 매번 핸들러 메소드의 URL에 중복 내용을 작성하지 않아도 된다.*/
@Controller
@RequestMapping("/order/*")
public class ClassMappingTestController {
@GetMapping("/regist") // 이렇게 하면 /regist만 썻어도 /order/regist로 매핑된다.
public String registOrder(Model model) {
model.addAttribute("message", "GET 방식의 주문 등록용 핸들러 메소드 호출");
return "mappingResult";
}
/*여러 개의 패턴 매핑*/
@RequestMapping(value = {"/modify", "/delete"}, method = RequestMethod.POST)
public String modifyAndDeleteOrder(Model model) {
model.addAttribute("message", "GET 방식의 주문 등록용 핸들러 메소드 호출");
return "mappingResult";
}
/*path variable: 요청 주소에 포함된 변수*/
@GetMapping("/detail/{orderNo}")
public String selectOneOrderDetail(Model model, @PathVariable("orderNo") String orderNo) {
model.addAttribute("message", orderNo + "번 주문 상세 내용 조회용 핸들러 메소드 호출");
return "mappingResult";
}
// 아무런 URL을 설정하지 않으면 요청 처리에 대한 핸들러 메소드가 준비되지 않았을 때 해당 메소드를 매핑한다.
@RequestMapping
public String otherRequest(Model model) {
model.addAttribute("message", "order 요청이긴 하지만 다른 기능은 아직 준비되지 않음");
return "mappingResult";
}
}
'Spring Framework > Spring Boot' 카테고리의 다른 글
[Spring Boot] 6. Interceptor (1) | 2024.09.10 |
---|---|
[Spring Boot] 5. Exception Handler (0) | 2024.09.09 |
[Spring Boot] 4. View Resolver (1) | 2024.09.09 |
[Spring Boot] 3. Handler Method (2) | 2024.09.09 |
[Spring Boot] 1. Spring Boot 개요 및 개발환경 구축 (1) | 2024.09.07 |