쌓고 쌓다
REST API에서 DTO 유효성 검사와 예외 처리 응답 방법 본문
API 서버를 만들며 유효성 검사와 예외 응답에 대한 방법을 고민하다가 생각해낸 방법이다.
먼저 유효성 검사를 쉽게하기위해 spring-boot-starter-validation를 build-gradle에 추가해주자.
implementation 'org.springframework.boot:spring-boot-starter-validation'
유효성 검사
간단하게 입력하지 않은 경우의 유효성 검사만 보이겠다.
그 외 다양한 유효성 검사 어노테이션은 찾아보길!
먼저 회원가입시 컨트롤러에서 MemberRequest를 파라미터로 받는다.
MemberRequest 클래스는 다음과 같으며 미입력을 방지하고자
미입력을 검사할 필드에 @NotEmpty 어노테이션을 붙인다.
@ToString
@Setter
public class MemberRequest {
@NotEmpty(message = "아이디를 비울 수 없습니다.")
private String loginId;
@NotEmpty(message = "비밀번호를 비울 수 없습니다.")
private String loginPwd;
@NotEmpty(message = "닉네임을 비울 수 없습니다.")
private String name;
}
message 속성으로 메시지를 넣어줄 수 있으며 예외 처리 및 응답에 유용하게 쓰인다.
이제 유효성 검사를 하고자하는 파라미터 앞에 @Valid 어노테이션을 붙여줌으로써 유효성 검사를 한다고 알릴 수 있다.
@PostMapping("/signup")
public ApiResponse signupMember(@Valid @RequestBody MemberRequest memberRequest)
@Valid 안붙여주면 검사안하니 주의!
예외 처리 및 응답
위에서 작성한 유효성 검사에 문제가 발생했다면 처리할 다음과 같이 핸들러를 만들자.
GlobalExceptionHandler.java
package com.example.spotserver.exception;
import com.example.spotserver.domain.ApiResponse;
import com.example.spotserver.domain.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({MethodArgumentNotValidException.class})
public ErrorResponse argumentValid(MethodArgumentNotValidException e) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setErrorCode(HttpStatus.BAD_REQUEST.value());
if(e.hasErrors()) {
errorResponse.setMessage(e.getAllErrors().get(0).getDefaultMessage());
}
return errorResponse;
}
}
- @RestControllerAdvice : 전역적인 @RESTController에서 발생하는 예외를 처리하는 클래스로 등록한다.
- @ExceptionHandler : 특정 예외를 처리하는 메서드로 등록합니다. 속성으로 처리할 예외를 등록하며 여러개도 가능하다.
- ErrorResponse는 내가 예외 응답으로 주기위해 직접 만든 클래스임을 주의!
이 전역예외핸들러 내부에서 예외를 다루고 요청의 응답을 보내주면 된다.
전역적으로 처리하지 않고 어떤 컨트롤러 단위로 범위를 더 좁게 예외 처리를 다룰 수 있다.
다음과 같이 적용할 컨트롤러에 @ExceptionHandler를 작성해주면 된다.
package com.example.spotserver.controller;
@RestController
@RequestMapping("/members")
public class MemberController {
@ExceptionHandler(value = IllegalArgumentException.class)
public String exceptionHandler() {
return "ok";
}
}
전역과 특정 컨트롤러에서 똑같은 예외를 처리하는 코드가 작성되어 있다면
더 좁은 범위의. 즉 특정 컨트롤러에서 처리하는 코드가 우선적으로 실행된다.
여기서 나는 회원가입시 입력한 정보의 유효성 검사를 통과했으면
이제 컨트롤러에서 DB를 조회하여 이미 아이디가 존재하는지 검사하여
존재할때 예외 처리와 응답을 하길 원했다.
결과적으로 DB 조회 후. 예외를 터트려 예외 핸들러를 통해 예외 응답을 내려주는 방법을 생각했다.
비슷한 예외로 illegalArgumentException.class로 터뜨리기보단 사용자 정의 예외를 만들었다.
이것이 코드 복잡성을 조금 증가시킬진 몰라도 가독성면이 더 중요하다고 생각하기에 추가했다.
사용자 정의 예외와 예외 메시지를 따로 관리하기 위해 두 클래스를 작성했다.
DuplicateException.java
public class DuplicateException extends Exception {
public DuplicateException(String message) {
super(message);
}
}
ErrorMsg
package com.example.spotserver.exception;
public class ErrorMsg {
public static String DUPLICATE_LOGINID = "중복된 아이디가 존재합니다.";
public static String DUPLICATE_NAME = "중복된 닉네임이 존재합니다.";
}
다음은 DB에서 아이디를 조회하여 기존에 존재하는 아이디가 있는지 검사하는 코드이다.
여기서 이미 아이디가 존재할 경우 위에서 정의한 DuplicateException을 터뜨리는것이다.
이 예외는 전역 에러 핸들러에서 처리할 수 있지만 일단~ 이 컨트롤러에 다음과 같이 예외를 처리하고 응답으로 보내게 작성했다.
@ExceptionHandler(value = DuplicateException.class)
public ErrorResponse duplicateException(DuplicateException e) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setErrorCode(409);
errorResponse.setMessage(e.getMessage());
return errorResponse;
}
'프로그래밍 > spring' 카테고리의 다른 글
Spring Security 인증/인가 예외 JSON 처리 (1) | 2024.01.24 |
---|---|
Enum 타입으로 에러 메시지 및 응답 관리하기 (0) | 2024.01.24 |
Entity와 DTO의 분리 필요성 및 방법 (0) | 2024.01.22 |
BCryptPasswordEncoder.encode 값이 매번 달라지는 이유 및 비교방법 (0) | 2024.01.20 |
@AuthenticationPrincipal로 컨트롤러에서 회원 정보 받기 (0) | 2024.01.19 |