예외처리
기존의 단건 조회처럼 코드 작성 시 에러 메시지가 바뀌면 관련 코드나 테스트코드 다 바꿔야한다.
또한 IllegalArgumentException 은 자바에서 제공해주는 예외이기 때문에 비즈니스를 명확하게 표현하기 힘들다.
기존 코드
public PostResponse get(Long id) {
Post post = postRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("존재하지 않는 글입니다.")
);
return new PostResponse(post);
}
따라서 이름부터 전달하는 메시지가 명확한 PostNotFoundException 이라는 커스텀 예외를 만들어 적용한다.
아래 사진처럼 동작하는 커스텀 예외를 만들어보자.
PostService
컨트롤러는 기존과 동일하고 조회 메서드인 get() 에 PostNotFoundException 을 적용한다.
public PostResponse get(Long id) {
Post post = postRepository.findById(id).orElseThrow(
() -> new PostNotFoundException()
);
return new PostResponse(post);
}
PostNotFoundException
언체크드 익셉션인 RuntimeException 을 상속받는다.
예외는 예외 메시지(message)와 발생한 원인(cause) 를 생성자의 매개변수로 받을 수 있다.
해당 커스텀 예외는 이름부터 게시글을 찾을 수 없다는 메시지가 명확하므로 예외 메시지는 내부에서 처리한다.
이후 추가 생성자가 필요할 시 추가하고 지금은 간단하게 기본 생성자만 구현한다.
public class PostNotFoundException extends RuntimeException{
private static final String MESSAGE = "존재하지 않는 글입니다.";
public PostNotFoundException() {
super(MESSAGE);
}
}
PostServiceTest
존재하지 않는 게시글을 단건 조회 시 PostNotFoundException 이 발생할 것을 기대하는 테스트를 작성한다.
@DisplayName("글 단건 조회 실패 - 존재하지 않는 글")
@Test
void get1() {
// given
Post post = Post.builder()
.title("우리 이쁜 미카공주님")
.content("우리 공주님")
.build();
postRepository.save(post);
// expected
assertThrows(
PostNotFoundException.class,
() -> postService.get(post.getId() + 1000)
);
}
PostControllerTest
존재하지 않는 글을 조회하는 테스트이므로 상태코드는 404 NotFound 로 응답 받길 기대하는 테스트를 작성한다.
@DisplayName("글 단건 조회 실패 - 존재하지 않는 글")
@Test
void get1() throws Exception {
// expected
mockMvc.perform(
MockMvcRequestBuilders.get("/posts/{postId}",1000L)
.contentType(APPLICATION_JSON)
)
.andExpect(status().isNotFound())
.andDo(print());
}
하지만 아직 컨트롤러 어드바이스에서 우리가 만든 커스텀 예외의 예외처리를 하지 않았기 때문에
컨트롤러 테스트 시 아래처럼 500 에러가 뜬다.
PostControllerAdvice
이제 아래처럼 컨트롤러 어드바이스에서 예외처리를 하고 다시 테스트를 돌려보자.
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(PostNotFoundException.class)
public ErrorResponse postNotFoundExceptionHandler(PostNotFoundException e) {
return ErrorResponse.builder()
.code("404")
.message(e.getMessage())
.build();
}
테스트 수행 시 우리가 의도한 대로 동작하며 테스트가 성공함을 알 수 있다.
이제 다른 동일한 예외가 발생하는 수정, 삭제 메서드의 코드들도 수정하자. 그 과정을 적지는 않겠다.
추가로
만약 위 컨트롤러 테스트에서 빈 validation 객체가 응답으로 내려오는게 싫다면
ErrorReponse 를 아래처럼 리팩토링한다.
ErrorResponse
@Getter
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
public class ErrorResponse {
private final String code;
private final String message;
private final Map<String, String> validation = new HashMap<>();
@Builder
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
}
public void addValidation(String fieldName, String errorMessage) {
this.validation.put(fieldName, errorMessage);
}
}
ㄴ@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
해당 애노테이션을 사용하면 빈 값은 JSON 응답으로 내려주지 않는다.
테스트 수행 시 우리가 의도한 대로 빈 validation 값은 JSON 응답으로 보내지 않았다.
하지만 빈 값도 일종의 정보이기 때문에 사용 시 고려해서 써야할것같다.
'API 만들어 보기 > 게시판 API' 카테고리의 다른 글
[Spring REST Docs] 기본설정 (0) | 2023.09.26 |
---|---|
[예외처리] 예외처리 2 (0) | 2023.09.26 |
[게시글 삭제] 게시글 삭제 (0) | 2023.09.26 |
[게시글 수정] 게시글 수정 (0) | 2023.09.26 |
[게시글 조회] 페이징 처리 - Querydsl (0) | 2023.09.25 |