본문 바로가기

Backend/SpringBoot

SpringBoot의 ControllerAdvice를 이용하여 JSON 형태로 예외처리하기

안녕하세요!

이번 포스팅에서는 SpringBoot의 Optionl과 @RestControllerAdvice를 이용해서

예외를 만들어보고 예외를 발생시켜보도록 하겠습니다.

 

사전 준비

프로젝트 설명

JSON 형태로 POST 요청을 보내면 요청 값에 따라 응답을 보내는 프로젝트를 작성해보겠습니다.

  • 값이 있는 경우 → 값 그대로 리턴
  • 값이 없는 경우(null) → 경고 메세지 리턴

개발 환경

  • IntelliJ Ultimate
  • Gradle
  • Java 1.8
  • SpringBoot

의존성

  • spring-boot-starter-test

구현

1. VO & ResponseDto 만들기

가장 먼저 POST로 요청을 받을 때 데이터를 전달받을 Person VO와 응답을 보낼 때 사용할 ResponseDto를 작성해보도록 하겠습니다.

Person.java

public class Person {

    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
}

변수 name과 기본 생성자, 생성자, Getter를 만들어줍니다.

이 VO는 POST로 요청을 받을 객체입니다.

lombok을 쓰는 경우에는 @NoArgsConstructor, @Getter, @AllArgsConstructor로 대체할 수 있습니다. 변수가 많은 경우에는 어노테이션을 사용하면 가독성이 높아지고 사용이 편해집니다.

 

ResponseDto.java

public class ResponseDto {

    private String message;

    public ResponseDto(String message) {
        this.message = message;
    }

    public static ResponseDto of(String message){
        return new ResponseDto(message);
    }

    public String getMessage() {
        return message;
    }

}

ResponseDto는 보통 code(상태 코드), message(메세지), data(전달 데이터)로 이루어져 있으나,

이번 프로젝트에서는 message만 사용하도록 하겠습니다.

생성자와 ResponseDto를 리턴해주는 메소드, Getter를 만들어줍니다.

 

2. Exception 작성

기존에 있는 Exception이 아닌 직접 사용할 Exception을 생성해보도록 하겠습니다.

JsonException.java

public class JsonException extends RuntimeException {
    public JsonException(String message) { super(message); }
}

RuntimeException을 상속받아 구현했습니다.

후에 예외가 발생한 이유를 변수 message에 담아 넘길 예정이기 때문에 생성자를 만들어줍니다.

 

3. ResponseHandler 작성

방금 만든 JsonException 예외가 발생하면 예외를 잡아 처리하는 Handler가 필요합니다.

아래와 같이 작성해줍니다.

BadResponseHandler.java

@RestControllerAdvice
public class BadResponseHandler {

    @ExceptionHandler(JsonException.class)
    public ResponseDto jsonException(JsonException ex) {
        return ResponseDto.of(ex.getMessage());
    }

}

@RestControllerAdvice는 아까 말한대로 예외가 발생했을 때 JSON 형태로 반환할 때 사용하는 어노테이션입니다.

@ExceptionHandler 어노테이션을 사용하여 JsonException 예외가 일어났을 때 json으로 에러 메세지 전달 처리를 해줍니다.

 

4. Controller 작성

ExceptionController.java

@RestController
@RequestMapping("/exception")
public class ExceptionController {

    @PostMapping("/test")
    public ResponseDto test(@RequestBody Person person){

        String name = (String) Optional.ofNullable(person.getName())
                .orElseThrow( () -> new JsonException("Null Point Exception!!") );

        return ResponseDto.of(name);
    }

}

/exception/testpost로 요청을 하게 되면 Person VO의 name에 대한 값을 ofNullable()을 통해 검색합니다.

만약 null이라면 JsonException 예외를 던짐과 동시에 "Null Point Exception!!"라는 메세지를 전달합니다.

만약 Optional에 대해 잘 모르시는 분이라면 제가 Optional에 대해 감을 잡을 때 도움이 많이 된 아래 포스팅을 추천드립니다. (3부까지 있습니다.)

자바8 Optional 1부: 빠져나올 수 없는 null 처리의 늪


테스트

이제 구현은 끝났으니 위 기능이 잘 동작하는지 테스트 하기 위해 ExceptionControllerTest를 생성합니다.

ExceptionControllerTest.java

@RunWith(SpringRunner.class)
@WebMvcTest(ExceptionController.class)
public class ExceptionControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    public void 테스트_실패_케이스() throws Exception {

        String content = objectMapper.writeValueAsString(new Person(null));

        mockMvc.perform(post("/exception/test")
                .content(content)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$.message").value("Null Point Exception!!"))
                .andDo(print());

    }

    @Test
    public void 테스트_성공_케이스() throws Exception {

        String content = objectMapper.writeValueAsString(new Person("칩앤데일"));

        mockMvc.perform(post("/exception/test")
                .content(content)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$.message").value("칩앤데일"))
                .andDo(print());

    }

}

MockMvc를 이용하여 값을 비교했습니다.

( 만약 MockMvc를 처음 접하신 분은 MockMvc에 대해 쓴 포스팅이 있으니 참고하시면 좋을 것 같습니다 :> )

jsonPath는 응답받는 JSON 형태의 값을 비교할 때 사용합니다.

각각 성공하는 케이스와 실패하는 케이스를 만들어 테스트를 돌려줍니다.

와! 두 케이스 모두 성공했습니다!


정리

이런 식으로 JSON 요청에 대한 null check를 하고 예외를 JSON 으로 처리하는 방법까지 알아봤습니다.

개인적으로 이 방법을 안 이후에 정말 요긴하게 사용하고 있어 한 번쯤 포스팅해보고 싶었습니다! ㅎㅎ

혹시 위 글에서 잘못된 내용이나 보충하고 싶은 내용을 알려주시면 감사하겠습니다!