spring boot 동작방식

- 인터넷으로부터 request가 오면 filter라는 구간을 통과한다.
- 그뒤 despatchservlet에서 handler mapping을 통해서 어떠한 주소를 가진 컨트롤러에 mapping을 해야되는지 결정
- 그뒤로 handler interceptor에 도착
- 그 뒤에 작성된 코드에서 서비스로직, db를 처리한뒤 응답이 돌아간다.
- 하지만 controller에서 예외가 발생하면 에러에 대한 응답을 만들어 처리에 대한 응답을 보낸다.
2.예외처리
1) 많이 알고 있는 예외
@RestController
@RequestMapping("/api")
public class RestApiController {
@GetMapping("/hello")
public void hello(){
throw new RuntimeException("called a new ecception");
}
}
2)에러 처리하는 법
- 우리는 기본적으로 에러가 발생하면 try catch를 사용해서 잡을 수 있지만 그 방법은 하나씩 잡아야되며 불편한 것이 있다
- 이것을 글로벌하게 처리하는 방법으로 exception handler이다
@Slf4j
@RestController
@RequestMapping("/api")
public class RestApiController {
@GetMapping("/hello")
public void hello(){
var list = List.of("hello");
var element = list.get(1);
log.info(element);
}
}
모든 예외 잡기
@Slf4j
@RestControllerAdvice
public class RestApiExceptionHandler {
@ExceptionHandler(value = {Exception.class})
public ResponseEntity excption(Exception e){
log.error("ExceptionHandler",e);
return ResponseEntity.status(200).build();
}
}
- @RestControllerAdvices 어노테이션을 사용하면 해당 클래스는 REST API가 사용하는 곳에 예외가 일어나는 것을 감지한다.
- Exception.class라고 한 이유는 exception들이 exception을 상속받기 때문에 모든 예외를 catch할 수 있다.
해당 예외만 잡기(outofindex)
@ExceptionHandler(value = {IndexOutOfBoundsException.class})
public ResponseEntity boundary(Exception e){
log.error("boundaryExceptionHandler",e);
return ResponseEntity.status(201).build();
}
}
명시적으로 예외처리 하기
public class RestApiControllerB {
@GetMapping("/hello")
public void hello(){
throw new NumberFormatException("dsa");
}
@ExceptionHandler(value = {NumberFormatException.class})
public ResponseEntity Exception(NumberFormatException e){
log.info("dsadsa",e);
return ResponseEntity.status(200).build();
}
}
- 하지만 컨트롤러의 길이가 길어지면 추천은 안한다.-> 단순히 컨트롤러헌틀러를 만들어서 사용하는것이 편하다.
@RestControllerAdvice
- 어떠한 예외를 잡을지 지정하는 것이 가능하다
- 어떤 베이스 패키지 즉, 어떠한 패키지의 내용을 잡을지 지정 가능.
@Slf4j
@RestControllerAdvice(basePackages = "com.example.exception.Controller")
public class RestApiExceptionHandler {
@ExceptionHandler(value = {Exception.class})
public ResponseEntity excption(Exception e){
log.error("ExceptionHandler",e);
return
다른방법
- 또한 클래스만 지정 가능.(배열로 설정 가능하다.)
실제 에외 처리
- 각 서버마다 API규격이 있다. (어떠한 내용으로 요청을하고 어떠한 응답을 할지)
- 그렇기 떄문에 예외가 발생하더라도 정해진 모양대로 응답이 내려와야되지만 기존에 사진을 보면 데이터를 요청하면 응답이 올때 정상적으로 요청하면 정상적으로 비정상적으로 요청이오면 비정상적으로 응답이온다. 이렇게 되면 요청(예외)마다 다른 응답이 오게되며 결국 공통성이 깨지게 된다.
- 그렇기 때문에 웹서버를 만들때 exception handler 즉, advice핸들러를 통해서 동일한 응답을 내릴 수 있도록 예외처리를 해야한다
실습
- 두개의 DTO를 만들고 객체를 생성하기 위한 패턴으로 build패턴 사용
@Data
@NoArgsConstructor
@Builder
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class Api<T> {
private String resultCode;
private String resultMessage;
private T data;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserResponse {
private String id;
private String name;
private Integer age;
}
package com.example.exception.Controller;
import com.example.exception.Model.Api;
import com.example.exception.Model.UserResponse;
import lombok.Builder;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/user")
public class UserApiController {
private static List<UserResponse> userList= List.of(UserResponse.builder()
.id("1")
.age(10)
.name("hong")
.build()
,
UserResponse.builder().id("2").age(11).name("u").build()
);
@GetMapping("/id/{userid}")
public Api<UserResponse> getuser(
@PathVariable String userid
){
var user = userList.stream().filter(
it -> it.getId().equals(userid)
).findFirst().get();
Api<UserResponse> response = Api.<UserResponse>builder()
.resultCode(String.valueOf(HttpStatus.OK.value()))
.resultMessage(HttpStatus.OK.name())
.data(user)
.build();
return response;
}
}
- 이렇게 코드를 작성한 뒤에 get을 하면 해당 값들을 보내지만 만들지 않았던 url을 보내면 예외가 발생하게 된다.

- 이것을 예외처리하기 위하여 handler에서 코드 작성
@ExceptionHandler(value = {NoSuchElementException.class})
public ResponseEntity<Api> nosuchele(NoSuchElementException e){
log.error("no such elementException",e);
var response = Api.builder()
.resultCode(String.valueOf(HttpStatus.NOT_FOUND.value()))
.resultMessage(HttpStatus.NOT_FOUND.getReasonPhrase())
.build();
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
}
}
실제 예외처리2
order
- 예외처리하는 클래스가 2개가 있다면 순서를 지정할 수 있다.
- 이를 통해서 예상하지 못한 예외를 처리하기 위해서 default로 글로벌하게 예외를 처리하는 handler를 만들어야 한다.
코드는 위와 비슷
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = {Exception.class})
public ResponseEntity<Api> globalexception(Exception e){
var response = Api.builder()
.resultCode(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()))
.resultMessage(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase())
.build();
return ResponseEntity.status(HttpStatus
.INTERNAL_SERVER_ERROR).body(response);
}
}