사용자의 id가 없을 경우를 대비해 에러를 만들어서 멘트를 보여주자!고 하다가 다른 여러 개의 에러들을 custom 해봤다.
- 똑같은 id를 가진 유저를 생성하려는 경우
- 없는 id를 가진 유저를 찾는 경우
크게 두가지로 나누어졌다. 그외 나머지 에러는 handler를 통해 처리했다.
exception.ErrorCodeEnum
package com.second.spring_study.exception.ywoo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
@AllArgsConstructor
public enum ErrorCodeEnum {
USER_NOT_FOUND(HttpStatus.NOT_FOUND.value(),"User Not Found"),
USER_ALREADY_EXIST(HttpStatus.CONFLICT.value(), "User Already Exist");
private final int status;
private final String message;
}
커스텀할 에러들을 추가해주는 곳이다. 기본적으로 Http Error Code와 Error Message를 가진다. 각각 에러들의 경우에 따른 코드를 가져와 value()를 통해 int형으로 바꿔준다.
exception.ApiException
package com.second.spring_study.exception.ywoo;
import lombok.Getter;
@Getter
public class ApiException extends RuntimeException{
private final ErrorCodeEnum errorCodeEnum;
public ApiException(ErrorCodeEnum e) {
super(e.getMessage());
this.errorCodeEnum=e;
}
}
throw new ApiException()
나중에 에러를 생성할 때 클래스 이름?과 같은 역할을 한다. 이 파일을 호출해 에러를 처리한다.
exception.ApiExceptionAdvice
package com.second.spring_study.exception.ywoo;
import com.second.spring_study.dto.response.ywoo.ErrorResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
@RestControllerAdvice
public class ApiExceptionAdvice {
//생성자를 사용한 방법
@ExceptionHandler({ApiException.class})
protected ResponseEntity<ErrorResponse> exceptionHandler(HttpServletRequest request, final ApiException e){
return new ResponseEntity<>(
new ErrorResponse(
e.getErrorCodeEnum().getStatus(),
e.getErrorCodeEnum().getMessage()),
HttpStatus.valueOf(e.getErrorCodeEnum().getStatus()));
}
//null pointer와 같은 기타 에러들
@ExceptionHandler({Exception.class})
protected ResponseEntity<ErrorResponse> ExceptionHandler(final NullPointerException e){
return new ResponseEntity<>(new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(),e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler({MethodArgumentNotValidException.class})
protected ResponseEntity<ErrorResponse> MethodArgumentNotValidExceptionHandler(final MethodArgumentNotValidException e){
return new ResponseEntity<>(new ErrorResponse(HttpStatus.BAD_REQUEST.value(),e.getMessage()), HttpStatus.BAD_REQUEST);
}
//SQL문이 안될때
@ExceptionHandler({DataIntegrityViolationException.class})
protected ResponseEntity<ErrorResponse> DataIntegrityViolationExceptionHandler(final DataIntegrityViolationException e){
return new ResponseEntity<>(new ErrorResponse(HttpStatus.CONFLICT.value(),e.getMessage()), HttpStatus.CONFLICT);
}
//지원하지 않는 메서드
@ExceptionHandler({HttpRequestMethodNotSupportedException.class})
protected ResponseEntity<ErrorResponse> HttpRequestMethodNotSupportedExceptionHandler(final HttpRequestMethodNotSupportedException e){
return new ResponseEntity<>(new ErrorResponse(HttpStatus.METHOD_NOT_ALLOWED.value(),e.getMessage()), HttpStatus.METHOD_NOT_ALLOWED);
}
}
에러를 직접적으로 핸들링해주는 곳이다. 커스텀한 에러는 exceptionHandler를 통해 처리된다. 그외 에러가 나지만 경우가 많아 일일이 넣어주기 힘든 경우는 나머지와 같은 방식으로 처리했다. 가장 중요한건 첫번째 메서드!!
@ 어노테이션
- @RestControllerAdvice
- @ResponseBody + @ControllerAdvice =>@RestControllerAdvice
- @ControllerAdvice 는 대부분 전역 예외처리를 위해 사용한다.
- @ControllerAdvice 참고사항
- @ExceptionHandler
- @RestControllerAdvice가 있는 메서드 안에서 사용한다.
- 넘겨받는 exception 클래스들의 종류에 따라 다르게 처리한다.
return 값을 생성자를 통해 줄 수도 있지만 builder를 사용할 수도 있다.
@ExceptionHandler({ApiException.class})
public ResponseEntity<ErrorResponse> exceptionHandler(HttpServletRequest request, final ApiException e){
return ResponseEntity
.status(e.getErrorCodeEnum().getStatus())
.body(ErrorResponse.builder()
.status(e.getErrorCodeEnum().getStatus())
.message(e.getErrorCodeEnum().getMessage())
.build());
}
에러들을 커스텀했으니 이제 각각의 경우에 맞는 곳에 사용해준다.
service.UserYwooService
@Transactional
public void createUser(UserRequestDto userRequestDto) {
if(userRepository.existsByUserId(userRequestDto.getUserId())){
throw new ApiException(ErrorCodeEnum.USER_ALREADY_EXIST);
}
UserYwoo user = UserYwoo.createUser(userRequestDto.getUserId(), userRequestDto.getUserName(), userRequestDto.getUserPassword());
userRepository.save(user);
}
첫번째로 회원을 생성하는 곳이다. 회원을 생성할 때 이미 id가 존재한다면 해당되는 에러를 사용해준다.
@Transactional
public void deleteUser(long id) {
userRepository.findById(id).orElseThrow(() -> {
throw new ApiException(ErrorCodeEnum.USER_NOT_FOUND);
});
userRepository.deleteById(id);
}
@Transactional
public UserResponseDto findUser(long id){
UserYwoo userYwoo = userRepository.findById(id).orElseThrow(()->{
throw new ApiException(ErrorCodeEnum.USER_NOT_FOUND);
});
return UserResponseDto.of(userYwoo);
}
@Transactional
public void updateUser(long id, UserRequestUpdateDto userRequestUpdateDto){
userRepository.findById(id).orElseThrow(()->{
throw new ApiException(ErrorCodeEnum.USER_NOT_FOUND);
});
updateUser(id,userRequestUpdateDto);
}
그외 id가 존재해야만 할 수 있는 작업들은 USER_NOT_FOUND를 통해 없을 경우 알려준다.
그동안 자바를 사용하면서 에러가 나면 그저 에러가 났구나~하고 넘어갔던 것 같다. 특히 에러를 커스텀할 수 있다는 점이 새로웠다. 그저 서버 에러가 나면 난대로 에러를 보여줬었는데 클라이언트가 좀 더 이해하기 쉽도록 에러를 알려줄 수 있어서 좋은 것 같다. 모든 에러를 알 필요는 없겠지만 HTTP의 기본적인 에러 코드와 Exception들의 종류들을 공부해야할 필요가 있는 것 같다.
🔗 : ISSUES #26 Controller 예외 처리
🔗 : Spring Study
'스프링 스터디' 카테고리의 다른 글
게시글 추가 기능 구현 / 2022-02-15 (0) | 2022.02.15 |
---|---|
1주차 스프링 스터디 회고록 (0) | 2022.02.14 |
회원 정보 수정 / 2022-02-09 (2) | 2022.02.10 |
회원 전체 조회 및 상세 조회 / 2022-02-08 (0) | 2022.02.09 |
회원 삭제 기능 / 2022-02-07 (0) | 2022.02.08 |