적용 배경
프로젝트를 하다보면 에러처리를 해야한다.
이전에는 이것의 중요성을 별로 못느끼고 아무렇게나 사용했었다.
그러나 프론트에서 에러에 대한 반환값을 직접 만들고, 처리하는데 중요하다는것을 알게되었고, 무엇보다 프로그램은 런타임 시점에서 에러가 나면 안된다. 이를 위해 이번 프로젝트는 에러코드를 커스텀할 수 있도록 세팅했다.
참고 블로그: https://bcp0109.tistory.com/303
package dev.kakao5.eyestalkdb.exception;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import static dev.kakao5.eyestalkdb.exception.ErrorCode.MEMBER_NOT_FOUND;
@Slf4j
@RestControllerAdvice //프로젝트 전역에서 발생하는 모든 예외
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
//특정 예외를 잡음
@ExceptionHandler(value = { ConstraintViolationException.class, DataIntegrityViolationException.class})
protected ResponseEntity<ErrorResponse> handleDataException() { // hibernate 관련 에러
log.error("handleDataException throw Exception : {}", MEMBER_NOT_FOUND);
return ErrorResponse.toResponseEntity(MEMBER_NOT_FOUND);
}
@ExceptionHandler(value = { CustomException.class })
protected ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
log.error("handleCustomException throw CustomException : {}", e.getErrorCode()); //직접 정의한 CustomException
return ErrorResponse.toResponseEntity(e.getErrorCode());
}
}
package dev.kakao5.eyestalkdb.exception;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.ResponseEntity;
import java.time.LocalDateTime;
@Getter
@Builder
public class ErrorResponse {
private final LocalDateTime timestamp = LocalDateTime.now();
private final int status;
private final String error;
private final String code;
private final String message;
public static ResponseEntity<ErrorResponse> toResponseEntity(ErrorCode errorCode) {
return ResponseEntity
.status(errorCode.getHttpStatus())
.body(ErrorResponse.builder()
.status(errorCode.getHttpStatus().value())
.error(errorCode.getHttpStatus().name())
.code(errorCode.name())
.message(errorCode.getDetail())
.build()
);
}
}
package dev.kakao5.eyestalkdb.exception;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class CustomException extends RuntimeException{
private final ErrorCode errorCode;
}
package dev.kakao5.eyestalkdb.exception;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import static org.springframework.http.HttpStatus.*;
@Getter
@AllArgsConstructor
public enum ErrorCode {
/* 404 NOT_FOUND : Resource 를 찾을 수 없음 */
MEMBER_NOT_FOUND(NOT_FOUND, "해당 유저 정보를 찾을 수 없습니다."),
ROOM_NOT_FOUND(NOT_FOUND, "해당 방 정보를 찾을 수 없습니다."),
ROOM_IS_EMPTY(NOT_FOUND, "존재하는 방이 없습니다."),
INVALID_PASSWORD(BAD_REQUEST, "잘못된 비밀번호입니다."),
FULL_CAPACITY(UNAUTHORIZED, "인원 초과입니다."),
INVALID_NICKNAME(BAD_REQUEST, "이미 사용 중인 닉네임입니다."),
DUPLICATE_RESOURCE(CONFLICT, "이미 존재하는 방입니다.")
;
private final HttpStatus httpStatus;
private final String detail;
}
package dev.kakao5.eyestalkdb.service;
import dev.kakao5.eyestalkdb.dto.UserDto;
import dev.kakao5.eyestalkdb.entity.UserEntity;
import dev.kakao5.eyestalkdb.exception.CustomException;
import dev.kakao5.eyestalkdb.exception.ErrorCode;
import dev.kakao5.eyestalkdb.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Optional;
@RequiredArgsConstructor
@Service
@Transactional(readOnly = false)
public class UserServiceImpl implements UserServiceInterface{
private final UserRepository userRepository;
@Override
public UserDto createUser(UserDto dto) {
UserEntity createUser = UserEntity.builder()
.userNickname(dto.getUserNickname())
.userCreateAt(LocalDateTime.now())
.build();
UserEntity save = userRepository.save(createUser);
UserDto userDto = UserDto.builder()
.userId(createUser.getUserId())
.userNickname(createUser.getUserNickname())
.userCreateAt(createUser.getUserCreateAt())
.build();
return userDto;
}
@Override
public boolean deleteUser(Long userId) {
if (!userRepository.existsById(userId))
throw new CustomException(ErrorCode.MEMBER_NOT_FOUND);
Optional<UserEntity> userEntity = userRepository.findById(userId);
userRepository.delete(userEntity.get());
return true;
}
}