Spring Boot에서 ResponseEntity는 HTTP 응답을 세밀하게 제어하기 위해 제공되는 클래스입니다.
다음과 같은 세 가지 주요 요소를 설정할 수 있습니다:
ResponseMessage는 응답 데이터를 일관성 있게 제공하기 위해 설계된 커스텀 클래스입니다.
모든 API가 동일한 응답 형식을 유지하도록 도와주며, 성공과 실패 결과를 명확히 구분할 수 있습니다.
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ResponseMessage<T> {
private String result;
private List<Errors> errors;
private T data;
public static <T> ResponseMessage<T> success() {
return success(null);
}
public static <T> ResponseMessage<T> success(T data) {
return new ResponseMessage<T>(SUCCESS.toString(), null, data);
}
public static <T> ResponseMessage<T> fail(ErrorCode errorCode) {
return ResponseMessage.<T>builder()
.result(FAIL.toString())
.errors(List.of(new Errors(errorCode)))
.build();
}
public static <T> ResponseMessage<T> fail(List<Errors> errors) {
return ResponseMessage.<T>builder()
.result(FAIL.toString())
.errors(errors)
.build();
}
public enum Result {
SUCCESS, FAIL
}
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ResponseMessage<T> {
private String result; // 성공(SUCCESS) 또는 실패(FAIL) 결과
private List<Errors> errors; // 에러 정보 리스트
private T data; // 응답 데이터
}
result: 응답 결과(SUCCESS 또는 FAIL).errors: 실패 시 포함되는 에러 리스트.data: 성공 시 포함되는 응답 데이터.// 성공 응답
public static <T> ResponseMessage<T> success(T data) {
return new ResponseMessage<>("SUCCESS", null, data);
}
// 실패 응답
public static <T> ResponseMessage<T> fail(ErrorCode errorCode) {
return ResponseMessage.<T>builder()
.result("FAIL")
.errors(List.of(new Errors(errorCode)))
.build();
}
@Valid는 요청 데이터를 검증할 때 사용됩니다.
Spring Boot에서 @Valid를 적용하면 요청 데이터가 DTO의 유효성 조건에 부합하지 않을 경우, 예외(MethodArgumentNotValidException)가 발생합니다.
요청 데이터를 검증하는 DTO입니다.
이 클래스는 사용자 회원가입 요청을 처리하며, 다양한 유효성 조건을 포함합니다.
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JoinDTO {
@NotBlank(message = "USER-ERROR-VALID-00006") // 이름 필수 입력
private String username;
@NotBlank(message = "USER-ERROR-VALID-00007") // 닉네임 필수 입력
private String nickname;
@NotBlank(message = "USER-ERROR-VALID-00008") // 전화번호 필수 입력
private String phone;
@Size(min = 4, message = "USER-ERROR-VALID-00009") // 최소 4자
private String password;
@Email(message = "USER-ERROR-VALID-00011") // 유효한 이메일 형식
private String email;
}
요청 데이터 검증이 @Valid에 의해 수행됩니다.
@PostMapping
public ResponseEntity<ResponseMessage<Void>> joinProcess(@RequestBody @Valid JoinDTO joinDTO) {
userService.joinProcess(joinDTO);
return ResponseEntity.status(HttpStatus.CREATED).body(ResponseMessage.success());
}
@Valid에서 발생한 검증 실패 예외를 처리하는 클래스입니다.
@RestControllerAdvice
public class GlobalExceptionHandler {
// 유효성 검증 실패 시 처리
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ResponseMessage<Void>> handleValidationExceptions(MethodArgumentNotValidException ex) {
List<Errors> errors = ex.getBindingResult().getFieldErrors().stream()
.map(fieldError -> new Errors(ValidErrorCode.findErrorCode(fieldError)))
.collect(Collectors.toList());
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(ResponseMessage.fail(errors));
}
// 기타 예외 처리
@ExceptionHandler(BizException.class)
public ResponseEntity<ResponseMessage<Void>> handleBizException(BizException ex) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(ResponseMessage.fail(ex.getErrorCode()));
}
}
에러를 관리하는 ErrorCode와 Errors 클래스입니다.
에러 코드는 메시지와 함께 프로젝트 전반에서 재사용됩니다.
public interface ErrorCode {
String getErrorCode();
String getErrorMessage();
}
@Getter
@RequiredArgsConstructor
public enum ValidErrorCode implements ErrorCode {
MIN_VALID_ERROR("USER-ERROR-VALID-00001", "값이 최솟값 이상이여야합니다."),
VALID_ERROR("USER-ERROR-VALID-00002", "유효성 검사 실패."),
DATABASE_VALID_ERROR("USER-ERROR-VALID-00003", "데이터베이스 제약 조건 위반."),
FORBIDDEN("USER-ERROR-VALID-00004", "권한이 없습니다."),
USER_NOT_FOUND_ERROR("USER-ERROR-VALID-00005", "사용자가 없습니다."),
USERNAME_ERROR("USER-ERROR-VALID-00006", "사용자 이름은 필수 입니다."),
NICKNAME_ERROR("USER-ERROR-VALID-00007", "사용자 닉네임은 필수 입니다."),
PHONE_ERROR("USER-ERROR-VALID-00008", "사용자 핸드폰번호는 필수 입니다"),
PASSWORD_ERROR("USER-ERROR-VALID-00009", "비밀번호는 4자 이상 입력해야 합니다."),
NEW_PASSWORD_ERROR("USER-ERROR-VALID-00010", "새비밀번호는 4자 이상 입력해야 합니다."),
MAIL_ERROR("USER-ERROR-VALID-00011", "이메일 형식이 잘못되었습니다."),
LOGIN_FAIL_ERROR("USER-ERROR-VALID-00012", "로그인 실패!"),
USER_PW_MISMATCH_ERROR("USER-ERROR-VALID-00013", "사용자 비밀번호 불일치!"),
PROFILE_NOT_FOUND_ERROR("USER-ERROR-VALID-00014", "프로필이 없습니다."),
MISSING_MBTI_ERROR("USER-ERROR-VALID-00015", "MBTI는 필수 입력 항목입니다."),
MISSING_SMOKING_STATUS_ERROR("USER-ERROR-VALID-00016", "흡연 여부는 필수 입력 항목입니다."),
MISSING_GENDER_ERROR("USER-ERROR-VALID-00017", "성별은 필수 입력 항목입니다."),
MISSING_BIRTH_DATE_ERROR("USER-ERROR-VALID-00018", "생년월일은 필수 입력 항목입니다."),
AUTHENTICATION_CODE_ERROR("USER-ERROR-VALID-00019", "인증 코드는 필수입니다.");
private final String errorCode;
private final String errorMessage;
public static ErrorCode findErrorCode(FieldError fieldError) {
return Arrays.stream(ValidErrorCode.values())
.filter(v -> v.getErrorCode().equals(fieldError.getDefaultMessage())) // USER-ERROR-VALID-00002
.findAny()
.get();
}
}
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public class Errors {
private String errorCode;
private String errorMessage;
public Errors(ErrorCode errorCode) {
this.errorCode = errorCode.getErrorCode();
this.errorMessage = errorCode.getErrorMessage();
}
}
ValidErrorCode는 각 필드의 검증 실패에 대한 코드와 메시지를 정의합니다.Errors 객체로 변환되어 클라이언트에 전달됩니다.{
"username": "",
"nickname": "john_doe",
"phone": "123456789",
"password": "123",
"email": "invalid-email"
}
username 필수 값 누락.password 길이 부족.email 형식 유효하지 않음.@Valid에 의해 MethodArgumentNotValidException 예외가 발생합니다.
GlobalExceptionHandler가 이를 처리하여 다음과 같은 응답을 반환합니다.
{
"result": "FAIL",
"errors": [
{
"errorCode": "USER-ERROR-VALID-00006",
"errorMessage": "사용자 이름은 필수입니다."
},
{
"errorCode": "USER-ERROR-VALID-00009",
"errorMessage": "비밀번호는 최소 4자 이상이어야 합니다."
},
{
"errorCode": "USER-ERROR-VALID-00011",
"errorMessage": "유효하지 않은 이메일 형식입니다."
}
],
"data": null
}
JoinDTO를 요청 본문으로 보냄.@Valid에 의해 DTO 유효성 검증 수행.MethodArgumentNotValidException 발생.GlobalExceptionHandler가 예외를 처리하여 에러 응답 반환.Spring Boot에서 ResponseEntity, ResponseMessage, @Valid를 결합하여 유효성 검증과 응답 처리를 일관되게 관리할 수 있었습니다.
명확한 에러 코드와 메시지를 제공함으로써, 클라이언트가 요청의 문제를 정확히 이해할 수 있는 시스템을 구축.