스프링 MVC
01.스프링MVC
-
현시점 국내 IT서비스 실무에서 가장 많이 활용되는 웹 개발 기술
-
Spring MVC 아키텍처
- 핵심은 M(Model), V(View), C(Controller)
- Model: 로직 안에서 이동하고 있는 데이터
- View: 디스플레이 데이터 또는 프리젠테이션
- Controller: 비즈니스 로직을 처리하고 모델과 뷰를 응답으로 준다.
02. HTTP 요청/응답
스프링 기본 HTTP요청 매핑
- Controller/RestController의 차이
- Controller: 응답값이 기본적으로 HTML을 주도록 되어 있음
- RestController: 응답값으로 Rest API 요청에 대한 응답(주로 JSON)을 주도록 되어 있음
- 매핑 어노테이션
- @RequestMapping: GET, POST 등 요청 방식을 직접 지정
1
2
3
4
5
6
7
8
9
10
@Slf4j
@RestController
public class SampleController {
@RequestMapping(value = "/order/1", method = RequestMethod.GET)
public String getOrder() {
log.info("Get some order");
return "orderId:1, orderAmount:1000";
}
}
- 축약형 매핑 어노테이션
- @GetMapping : 데이터를 가져옴
- @PostMapping : 데이터를 전송함
- @PutMapping : 전체 수정
- @PatchMapping : 일부 수정
- @DeleteMapping : 삭제
스프링 HTTP 요청 파라미터 전송
- Get,Delete
- PathVariable : 요새는 id를 path에 넣는 것을 선호
- @PathVariable(”id”) String identity;
- 하지만 이름이 같으면 생략 가능
- 여러개를 넣을 수 있음
- query-params : 추가적인 정보들 입력
- 게시판의 검색 필터 페이징에서 많이 사용
- @RequestParam 사용법
- PathVariable처럼 이름을 동일하게 하면 자동으로 받아줌
- required, defaultValue 옵션 설명
- 사실 없어도 자동으로 나옴
- Map, MultiValueMap으로 요청 받는 방법
- PathVariable : 요새는 id를 path에 넣는 것을 선호
1
2
3
4
5
6
7
8
9
10
11
12
@GetMapping("/order/{orderId}")
public String getOrder(@PathVariable("orderId") String orderId) {
log.info("Get some order information : " + orderId);
return "orderId: " + orderId + ", orderAmount:100";
}
@GetMapping("/order")
public String searchOrder(@RequestParam("orderId") String orderId,
@RequestParam("orderAmount") Integer orderAmount) {
log.info("Search order information : " + orderId + ", orderAmount : " + orderAmount);
return "orderId: " + orderId + ", orderAmount: " + orderAmount;
}
- Post, Put, Patch
- @RequestBody : http body 정보를 편리하게 받을 수 있음
- 주로 사용하는 메시지 포맷은 JSON(현재 사실상의 표준)
- @RequestHeader : http header 정보를 편리하게 받을 수 있음
- @RequestBody : http body 정보를 편리하게 받을 수 있음
1
2
3
4
5
6
7
8
9
10
11
12
13
@PostMapping("/order")
public String getOrder(@RequestBody CreateOrderRequest createOrderRequest,
@RequestHeader String userAccountId) {
log.info("Create order : " + createOrderRequest + ", userAccountId : " + userAccountId);
return "orderId:" + createOrderRequest.getOrderId()
+ ", orderAmount:" + createOrderRequest.getOrderAmount();
}
@Data
public static class CreateOrderRequest {
private String orderId;
private Integer orderAmount;
}
03. 필터, 인터셉터
- 필터
- 스프링 외부의 서블릿에서 제공하는 공통처리 기능
- 스프링 내로 요청이 들어오기 전과 스프링의 요청이 나갈 때 처리 가능
- 조금 더 low level 처리가 가능
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
31
32
@Slf4j
@Component
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
log.info("Hello LogFilter : " + Thread.currentThread());
// 외부 -> filter (-> 처리 ->) filter -> 외부
chain.doFilter(request, response);
log.info("Bye LogFilter : " + Thread.currentThread());
}
}
// 필터를 거는 순서, url패턴등을 조절
// @Component를 제거하면 단순 클래스기때문에 인식하지 못함
// 그래서 Component 제거 후 다른 클래스로 필터 발생을 하게 설정
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<Filter> loggingFilter() {
FilterRegistrationBean<Filter> filterFilterRegistrationBean
= new FilterRegistrationBean<>();
filterFilterRegistrationBean.setFilter(new LogFilter());
filterFilterRegistrationBean.setOrder(1);
filterFilterRegistrationBean.addUrlPatterns("/order/*");
return filterFilterRegistrationBean;
}
}
- 인터셉터
- 스프링에서 제공하는 공통처리 기능
- 실제 매핑된 Handler 정보 확인 가능(어떤 것이 실제 내 요청을 처리하는지도 확인 가 능)
- 조금 더 상세한 조건식과, 세부적인 스펙(pre, post, after)를 통해 구체적인 시점에 구 체적인 동작 가능
- AOP와 비교한다면 AOP는 인터셉터보다 더 구체적인 조건(어노테이션, 파라미터, 주소 등)과 동작 위치(afterThrowing 등)을 갖음
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
log.info("preHandle LogInterceptor : " + Thread.currentThread());
log.info("preHandle handler : " + handler);
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle LogInterceptor : " + Thread.currentThread());
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) throws Exception {
log.info("afterHandle LogInterceptor : " + Thread.currentThread());
if (ex != null) {
log.error("afterCompletion exception : " + ex.getMessage());
}
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean<Filter> loggingFilter() {
FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
filterFilterRegistrationBean.setFilter(new LogFilter());
filterFilterRegistrationBean.setOrder(1);
filterFilterRegistrationBean.addUrlPatterns("/order/*");
return filterFilterRegistrationBean;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/*", "/images/*");
}
}
04. 예외처리
- 예외: 프로그램이 예상치 못한 상황을 만났을 때 오류를 발생시키는 것
@ExceptionHandler
- 컨트롤러 기반 예외처리
- HTTP Status code를 변경하는 방법
- @ResponseStatus
- ResponseEntity 활용
- 예외처리 우선순위
- 해당 Exception이 정확히 지정된 Handler
- 해당 Exception의 부모 예외 Handler
- 이도 저도 아니면 그냥 Exception(모든 예외의 부모)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Slf4j
@RestController
public class SampleController {
// business methods
@ResponseStatus(value = HttpStatus.FORBIDDEN)
@ExceptionHandler(IllegalAccessException.class)
public ErrorResponse handleIllegalAccessException(IllegalAccessException e) {
log.error("Illegal Exception : ", e);
return new ErrorResponse("ACCESS_DENIED", "Illegal Exception occurred.");
}
}
@AllArgsConstructor
@Data
public class ErrorResponse {
private String errorCode;
private String message;
}
@RestControllerAdvice
- 어플리케이션의 전역적 예외 처리
- @ControllerAdvice랑 차이는?
- Controller vs RestController 차이와 동일
- ControllerAdvice: 기본적으로 view를 응답하는 방식
- RestControllerAdvice: REST API용으로 객체를 응답하는 방식(주로 JSON)
- 스프링 백엔드 개발에서 현재 가장 많이 활용되는 기술(일관적인 예외 및 응답처리)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalAccessException.class)
public ResponseEntity<ErrorResponse> handleIllegalAccessException(
IllegalAccessException e) {
log.error("IllegalAccessException ins occurred.", e);
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body(new ErrorResponse(ErrorCode.TOO_BIG_ID_ERROR, "IllegalAccessException is occurred."));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error("Exception ins occurred.", e);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(ErrorCode.INTERNAL_SERVER_ERROR, "Exception is occurred."));
}
}
댓글남기기