문제 : 에러 메시지가 코드에 직접 작성됨
- 다양한 경우의 수의 에러 메시지가 비지니스 코드 내에서 직접 작성되어있기 때문에, 어떤 에러 메시지가 있는지 한눈에 알아보기 힘듦
@Transactional
public SignupResponse signup(SignupRequest signupRequest) {
if (userRepository.existsByEmail(signupRequest.getEmail())) {
throw new InvalidRequestException("이미 존재하는 이메일입니다.");
}
public SigninResponse signin(SigninRequest signinRequest) {
User user = userRepository.findByEmail(signinRequest.getEmail()).orElseThrow(
() -> new InvalidRequestException("가입되지 않은 유저입니다."));
// 로그인 시 이메일과 비밀번호가 일치하지 않을 경우 401을 반환합니다.
if (!passwordEncoder.matches(signinRequest.getPassword(), user.getPassword())) {
throw new AuthException("잘못된 비밀번호입니다.");
}
- 동일한 내용의 에러 메시지가 코드 내에서 여러번 재사용되고 있음
- 만약 같은 내용이지만 여러 곳에 분포되어 있는 에러 메시지가 추후에 변경된다면 직접 그곳을 찾아다니며 수정해줘야 함
@Transactional
public CommentSaveResponse saveComment(AuthUser authUser, long todoId, CommentSaveRequest commentSaveRequest) {
User user = User.fromAuthUser(authUser);
Todo todo = todoRepository.findById(todoId).orElseThrow(() ->
new InvalidRequestException("Todo not found"));
public TodoResponse getTodo(long todoId) {
Todo todo = todoRepository.findByIdWithUser(todoId)
.orElseThrow(() -> new InvalidRequestException("Todo not found"));
@Transactional(readOnly = true)
public List<ManagerResponse> getManagers(long todoId) {
Todo todo = todoRepository.findById(todoId)
.orElseThrow(() -> new InvalidRequestException("Todo not found"));
에러 메시지를 Enum Class에 담아서 관리하여 개선
에러 객체 분리에 대한 고민
- ServerException을 좀더 구체적으로 나눌까(관리에러, 날씨 api에러 등..) 고민
- 구체적으로 나눈다면 추후에 기능이 추가될 때마다 예외 객체들 또한 무수히 많아질 것 같고, 에러를 발생시킨 책임의 주체만 분명히 하고(서버인지, 클라이언트인지) 어차피 구체적인 내용은 에러 메시지를 통하여 전달하면 된다고 생각함
- 기존에 AuthException(인증, 인가 에러), ServerException(서버에서 발생하는 에러), InvalidRequestException(클라이언트가 잘못된 요청으로 인해 발생하는 에러)를 유지함
Enum Class 생성
- common 패키지 하위에 errorcode를 만들고 에러 메시지를 한번에 관리할 수 있는 ErrorCode Enum Class를 생성
- 각각의 Exception Class별로 구분하여 에러 메시지를 배치
public enum ErrorCode {
// AuthException
EMAIL_ALREADY_EXISTS("이미 사용 중인 이메일입니다."),
USER_NOT_FOUND_BY_EMAIL("해당 이메일로 등록된 유저가 없습니다."),
INVALID_PASSWORD("비밀번호가 잘못되었습니다."),
AUTH_AND_AUTHUSER_REQUIRED("@Auth와 AuthUser 타입은 함께 사용되어야 합니다."),
// ServerException
WEATHER_DATA_FETCH_FAILED("날씨 데이터를 가져오는데 실패했습니다."),
NO_WEATHER_DATA_FOUND("날씨 데이터가 없습니다."),
NO_WEATHER_DATA_FOR_TODAY("오늘에 해당하는 날씨 데이터를 찾을 수 없습니다."),
TOKEN_NOT_FOUND("토큰을 찾을 수 없습니다."),
// InvalidRequestException
TODO_NOT_FOUND("해당하는 할일이 없습니다."),
MANAGER_NOT_FOUND("관리자가 없습니다"),
NOT_ASSIGNED_TO_SCHEDULE("해당 일정에 등록된 담당자가 아닙니다."),
CANNOT_REGISTER_AS_SELF("일정 작성자는 본인을 담당자로 등록할 수 없습니다."),
USER_NOT_FOUND("유저가 존재하지 않습니다."),
INVALID_SCHEDULE_CREATOR("담당자를 등록하려고 하는 유저가 일정을 만든 유저가 유효하지 않습니다."),
INVALID_USER_ROLE("유효하지 않은 UserRole입니다."),
USING_PASSWORD("이미 사용 중인 비밀번호 입니다.");
private String message;
private ErrorCode(String message){
this.message = message;
}
public String getMessage() {
return message;
}
}
ExceptionClass 수정
기존
public class InvalidRequestException extends RuntimeException {
public InvalidRequestException(String message) {
super(message);
}
}
수정 후
public class InvalidRequestException extends RuntimeException {
private ErrorCode errorCode;
public InvalidRequestException(ErrorCode errorCode) {
this.errorCode = errorCode;
}
public ErrorCode getErrorCode(){
return this.errorCode;
}
}
GlobalExceptionHandler 수정
기존
@ExceptionHandler(InvalidRequestException.class)
public ResponseEntity<Map<String, Object>> invalidRequestExceptionException(InvalidRequestException ex) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return getErrorResponse(status, ex.getMessage());
}
수정 후
@ExceptionHandler(InvalidRequestException.class)
public ResponseEntity<Map<String, Object>> invalidRequestExceptionException(InvalidRequestException ex) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return getErrorResponse(status, ex.getErrorCode().getMessage());
}
개선점
- enum을 사용하여 에러 메시지를 한곳에 모아뒀기 때문에, 한눈에 에러 메시지를 확인할 수 있음
- 추후에 에러 메시지를 유지보수하기 용이해짐 (동일한 에러 메시지를 코드에서 직접 찾아서 변경할 필요 없이 enum 한 곳에서만 변경하면 됨)
- 중복된 에러 메시지를 일일히 타이핑할일 없이 enum에 있는 메시지를 쓰면 되기 때문에 재사용성이 좋아짐
- 비지니스 로직에서 해당 에러 메시지를 봤을 때, 가독성 측면에서 향상됨
데이터 비교
개선 전 - 동일한 메시지가 여러 곳에서 사용되어 있고, 직접 서비스 코드 내에서 에러 메시지가 작성되어있어 유지보수에 용이하지 않음
개선 후 - Enum Class를 사용하여 에러 메시지를 한곳에서 관리하기 용이해짐
'Spring' 카테고리의 다른 글
N + 1 문제와 해결 방법 (0) | 2025.02.27 |
---|---|
AOP를 이용하여 API 로깅하기 (RequestBody, ResponseBody) (0) | 2025.02.27 |
Spring MVC의 구조와 HttpMessageConverter의 역할 (0) | 2025.02.24 |
NewsFeed 프로젝트 : 친구 관리 기능 트러블 슈팅 (0) | 2025.02.20 |
@RequestBody의 원리와 RequestDto 만드는 법 (1) | 2025.02.13 |