2024. 5. 2. 19:53ㆍProject/Naver Cafe
이전 게시글에서 사용자가 회원 가입시 잘못된 정보를 입력하면, 어떠한 이유로 에러가 발생했는지 사용자에게 알려줄 수 있도록 예외 처리가 필요하다고 했다. 그래서 이번 글에서는 전역적(글로벌) 예외 처리 기능을 추가한 내용을 작성하였다.
RequestJoinMember
package CloneCoding.NaverCafe.domain.member.dto;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RequestJoinMember {
@NotNull
@Size(min = 5, max = 20,
message = "계정 아이디는 5 ~ 20자로 제한됩니다.")
@Pattern(regexp = "^[a-z0-9_-]+",
message = "계정 아이디는 영소문자, 영대문자, 숫자만 사용 가능합니다.")
private String accountId;
@NotNull
@Size(min = 8, max = 16,
message = "계정 비밀번호는 8 ~ 16자로 제한됩니다.")
@Pattern(regexp = "^[a-zA-Z0-9~!@#$%]+",
message = "계정 비밀번호는 영소문자, 영대문자, 숫자, 특수문자(~, !, @, #, $, %)만 사용 가능합니다.")
private String accountPassword;
@NotNull
@Pattern(regexp = "^[a-zA-Z0-9_]+@[a-zA-Z]+\\.[a-zA-Z]+(\\.[a-zA-Z]+)?",
message = "올바른 이메일 형식이 아닙니다.")
private String email;
@NotNull
@Size(min = 2, max = 18, message = "이름은 2 ~ 18자로 제한됩니다.")
@Pattern(regexp = "^[a-zA-Z]+", message = "이름은 영소문자, 영대문자만 사용 가능합니다.")
private String username;
@NotNull
@Min(value = 1924, message = "태어난 연도의 입력 가능 범위는 1924 ~ 2024 입니다.")
@Max(value = 2024, message = "태어난 연도의 입력 가능 범위는 1924 ~ 2024 입니다.")
private int year;
@NotNull
@Min(value = 1, message = "태어난 달의 입력 가능 범위는 1 ~ 12 입니다.")
@Max(value = 12, message = "태어난 달의 입력 가능 범위는 1 ~ 12 입니다.")
private int month;
@NotNull
@Min(value = 1, message = "태어난 날의 입력 가능 범위는 1 ~ 31 입니다.")
@Max(value = 31, message = "태어난 날의 입력 가능 범위는 1 ~ 31 입니다.")
private int day;
@NotNull
@Size(min = 11, max = 11, message = "휴대전화번호는 11자 입니다.")
@Pattern(regexp = "^[0-9]+", message = "휴대전화번호는 숫자만 입력 가능합니다.")
private String phoneNumber;
@NotNull
@Size(min = 2, max = 20, message = "별명은 2 ~ 20자로 제한됩니다.")
@Pattern(regexp = "^[a-zA-Z0-9]+", message = "별명은 영소문자, 영대문자, 숫자만 입력 가능합니다.")
private String nickname;
}
이전에 전달 받는 정보를 전달하는 RequestJoinMember에 제약조건들을 설정하였는데, 여기에 각 제약조건 마다 사용자에게 알릴 메시지를 지정하였다.
ErrorCode
package CloneCoding.NaverCafe.exception;
import org.springframework.http.HttpStatus;
public interface ErrorCode {
String name();
HttpStatus getHttpStatus();
String getMessage();
}
사용자에게 보낼 에러코드를 정의하는 인터페이스이다. 에러명, HTTP 상태, 에러 메시지를 제공한다.
CommonErrorCode
package CloneCoding.NaverCafe.exception;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
@Getter
@RequiredArgsConstructor
public enum CommonErrorCode implements ErrorCode {
INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "Invalid parameter included"),
RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "Resource not exists"),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error")
;
private final HttpStatus httpStatus;
private final String message;
}
예외 처리를 할 때 자주 사용되는 에러 코드를 상수로 만들어 저장해 둔 Enum 클래스이다. ErrorCode 인터페이스를 상속 받으며, 각 상수는 HttpStatus와 메시지를 가지며 상수 사용시 두 정보를 꺼내 쓸 수 있도록 하였다.
ResponseError, ValidationError
package CloneCoding.NaverCafe.exception.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.FieldError;
import java.util.List;
@Getter
@Builder
@RequiredArgsConstructor
public class ResponseError {
private final String code;
private final String message;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private final List<ValidationError> errors;
@Getter
@Builder
@RequiredArgsConstructor
public static class ValidationError {
private final String field;
private final String message;
public static ValidationError of(final FieldError fieldError) {
return ValidationError.builder()
.field(fieldError.getField())
.message(fieldError.getDefaultMessage())
.build();
}
}
}
중첩 클래스로 @Valid 검증시 발생한 문제를 사용자에게 알리기 위해 만든 응답용 DTO이다. 사용자에게는 에러 코드명과 코드 메시지 그리고 잘못 입력한 필드명, 제약조건에 지정한 메시지 정보를 알려준다.
GlobalExceptionHandler
package CloneCoding.NaverCafe.exception.handler;
import CloneCoding.NaverCafe.exception.CommonErrorCode;
import CloneCoding.NaverCafe.exception.ErrorCode;
import CloneCoding.NaverCafe.exception.dto.ResponseError;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER;
return handleExceptionInternal(e, errorCode);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleAllException(Exception e) {
log.warn("handleAllException : ", e);
ErrorCode errorCode = CommonErrorCode.INTERNAL_SERVER_ERROR;
return handleExceptionInternal(errorCode);
}
private ResponseEntity<Object> handleExceptionInternal(ErrorCode errorCode) {
return ResponseEntity.status(errorCode.getHttpStatus())
.body(makeResponseError(errorCode));
}
private ResponseError makeResponseError(ErrorCode errorCode) {
return ResponseError.builder()
.code(errorCode.name())
.build();
}
private ResponseEntity<Object> handleExceptionInternal(BindException e, ErrorCode errorCode) {
return ResponseEntity.status(errorCode.getHttpStatus())
.body(makeResponseError(e, errorCode));
}
private ResponseError makeResponseError(BindException e, ErrorCode errorCode) {
List<ResponseError.ValidationError> validationErrors = e.getBindingResult()
.getFieldErrors()
.stream()
.map(ResponseError.ValidationError::of)
.toList();
return ResponseError.builder()
.code(errorCode.name())
.message(errorCode.getMessage())
.errors(validationErrors)
.build();
}
}
발생하는 예외를 처리하기 위한 클래스이다. 이론상으로 발생가능한 예외를 모두 처리할 수 있어야 하기에 기본적으로 Exception.class(최상위 예외 클래스)를 처리하는 예외 핸들러를 구현해 두었다. 이후 구현에서 발생하는 예외들을 처리하는 예외 핸들러들을 순차적으로 추가할 생각이다. 현재 추가한 예외 핸들러들은 아래와 같다.
- MethodArgumentNotValidException : @Valid 검증시 발생하는 예외로 지정한 제약조건과 맞지 않는 데이터를 발견하면 발생한다.
API TEST
일부러 잘못된 정보로 회원 가입을 시도하니 원하는 대로 어떤 값을 잘못 입력했고 원인에 대한 정보를 확인할 수 있으며, 또 단일이 아닌 다수의 정보를 잘못 입력할 수도 있는데 해당 부분도 문제 없이 출력된다.
'Project > Naver Cafe' 카테고리의 다른 글
[클론 코딩] 네이버 카페 - 네이버 로그인 (0) | 2024.05.10 |
---|---|
[클론 코딩] 네이버 카페 - 네이버 회원 정보 조회(읽기) (0) | 2024.05.03 |
[클론 코딩] 네이버 카페 - 네이버 회원 가입(등록) (0) | 2024.04.28 |
[클론 코딩] 네이버 카페 - 프로젝트 설정, 데이터베이스 연결 설정 (0) | 2024.04.27 |
[클론 코딩] 네이버 카페 - 서비스 분석 (0) | 2024.04.23 |