[미니프로젝트] 예외 처리 통합

김동혁·2025년 12월 7일

미니프로젝트

목록 보기
7/16

OAuth2를 연결하기 전에...
예외 메시지가 너무 중구난방이고 내가 영어로 쓴 것도 있고 한글로 쓴 것도 있었다
얼마 안되지만 예외 메시지를 좀 통합해서 한글로 적고 싶었다
전 포스트에서 TokenService에 InvalidTokenException을 사용하였다

TokenService

    // 리프레시 토큰 검증
    public void validateRefreshToken(String token) {
        if (token == null) {
            throw new InvalidTokenException("리프레시 토큰이 없습니다.");
        }

        try {
            jwtUtil.isExpired(token);
        } catch (ExpiredJwtException e) {
            throw new InvalidTokenException("만료된 리프레시 토큰입니다.");
        }

        String category = jwtUtil.getCategory(token);
        if (!category.equals("refresh")) {
            throw new InvalidTokenException("유효하지 않은 카테고리의 토큰입니다.");
        }

        if (!existsByTokenValue(token)) {
            throw new InvalidTokenException("DB에 존재하지 않는 리프레시 토큰입니다.");
        }
    }

InvalidTokenException

이 클래스를 만들어서 예외 처리를 전역적으로 관리한다

public class InvalidTokenException extends RuntimeException {
    public InvalidTokenException(String message) {
        super(message);
    }
}

JSON 형태 오류 메시지

오류 메시지를 깔끔하게 JSON 형태로 만들어서 보여주고 싶었다
ErrorResponseDto를 하나 만들어서 형태를 정의하였다

ErrorResponseDto

발생 시간, http 상태 코드, 에러, 메시지를 JSON으로 표현

/**
 * 예외 발생 시 클라이언트에게 반환되는 표준 오류 응답 DTO
 */
@Getter
@Builder
public class ErrorResponseDto {

    private final String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    private final int status;
    private final String error;
    private final String message;

    public static ResponseEntity<ErrorResponseDto> toResponseEntity(int status, String error, String message) {
        return ResponseEntity
                .status(status)
                .body(ErrorResponseDto.builder()
                        .status(status)
                        .error(error)
                        .message(message)
                        .build());
    }
}

그리고 DTO를 사용해서 실제로 오류 메시지를 반환하는 전역 핸들러를 만들었다

GlobalExceptionHandler

@RestControllerAdvice: 모든 @RestController에서 발생하는 예외를 한 곳에서 처리해주는 전역 예외 처리기

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(InvalidTokenException.class)
    public ResponseEntity<ErrorResponseDto> handleInvalidTokenException(InvalidTokenException ex) {
        return ErrorResponseDto.toResponseEntity(HttpStatus.UNAUTHORIZED.value(), "토큰 오류", ex.getMessage());
    }

    @ExceptionHandler(BadCredentialsException.class)
    public ResponseEntity<ErrorResponseDto> handleBadCredentialsException(BadCredentialsException ex) {
        return ErrorResponseDto.toResponseEntity(HttpStatus.UNAUTHORIZED.value(), "로그인 실패", "비밀번호가 일치하지 않습니다.");
    }

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<ErrorResponseDto> handleRuntimeException(RuntimeException ex) {
        // "이미 가입되어 있는 회원입니다." 와 같은 회원가입 시의 예외를 처리
        if ("이미 가입되어 있는 회원입니다.".equals(ex.getMessage())) {
            return ErrorResponseDto.toResponseEntity(HttpStatus.CONFLICT.value(), "회원가입 오류", ex.getMessage());
        }
        // 그 외 다른 런타임 예외는 일반적인 서버 오류로 처리
        return ErrorResponseDto.toResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), "서버 오류", "알 수 없는 런타임 오류가 발생했습니다.");
    }
}
  • 토큰 관련 오류: @ExceptionHandler(InvalidTokenException.class)
    - UNAUTHORIZED(401) 반환
  • 비밀번호 오류: @ExceptionHandler(BadCredentialsException.class)
    - UNAUTHORIZED(401) 반환
  • 런타임 오류: @ExceptionHandler(BadCredentialsException.class)
    - 런타임 오류 중 회원가입 시의 예외는 따로 처리해주었다

테스트

  • 중복 회원가입
  • 비밀번호 틀림
  • 토큰 오류

다음은 진짜 OAuth2 연결!

0개의 댓글