반응형

Spring Boot에서 예외 처리 및 전역 예외 핸들러를 구현하는 코드를 통해, 다양한 예외 상황에서 일관된 응답을 제공하는 방법을 학습할 수 있습니다. 

@ControllerAdvice를 사용하여 전역 예외 처리기를 구현하고, 다양한 예외에 대해 적절한 응답을 반환하는 방법을 살펴보겠습니다.

 

1. 기본 예외 처리 설정

 

Spring Boot에서는 기본적으로 예외가 발생하면 HTTP 상태 코드와 함께 오류 메시지를 반환합니다. 하지만 이 기본 방식은 사용자 경험(UX)을 향상시키기 위해 커스터마이징이 필요할 수 있습니다.

 

2. 전역 예외 처리 클래스 생성

 

@ControllerAdvice를 사용하여 전역 예외 처리기를 생성합니다. 이 클래스는 애플리케이션 전반에서 발생하는 예외를 한 곳에서 처리하고, 일관된 방식으로 응답을 반환할 수 있게 합니다.

 

package com.example.demo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
@RestController
public class GlobalExceptionHandler {

    // 1. 기본적인 예외 처리
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
        Map<String, Object> response = new HashMap<>();
        response.put("message", ex.getMessage());
        response.put("details", request.getDescription(false));
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    // 2. 특정 예외 처리 (예: NullPointerException)
    @ExceptionHandler(NullPointerException.class)
    public final ResponseEntity<Object> handleNullPointerException(NullPointerException ex, WebRequest request) {
        Map<String, Object> response = new HashMap<>();
        response.put("message", "Null value encountered!");
        response.put("details", request.getDescription(false));
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    // 3. 유효성 검사 실패 예외 처리 (예: MethodArgumentNotValidException)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public final Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error -> 
            errors.put(error.getField(), error.getDefaultMessage()));
        return errors;
    }

    // 4. 사용자 정의 예외 처리
    @ExceptionHandler(ResourceNotFoundException.class)
    public final ResponseEntity<Object> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
        Map<String, Object> response = new HashMap<>();
        response.put("message", ex.getMessage());
        response.put("details", request.getDescription(false));
        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
    }
}

 

3. 사용자 정의 예외 클래스 생성

 

필요에 따라 사용자 정의 예외를 만들어 사용할 수 있습니다. 예를 들어, 리소스를 찾을 수 없을 때 발생시키는 예외를 만들 수 있습니다.

src/main/java/com/example/demo/exception/ResourceNotFoundException.java

package com.example.demo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {

    public ResourceNotFoundException(String message) {
        super(message);
    }
}

어노테이션을 사용하는 경우:

예외 클래스에 @ResponseStatus 어노테이션을 추가하면, 해당 예외가 발생할 때 Spring이 자동으로 지정된 HTTP 상태 코드를 반환합니다.

예를 들어, @ResponseStatus(HttpStatus.NOT_FOUND)를 사용하면, 이 예외가 발생할 때 자동으로 404 Not Found 상태 코드가 반환됩니다.

어노테이션을 사용하지 않는 경우:

@ResponseStatus 어노테이션을 사용하지 않으면, 예외 처리 클래스에서 ResponseEntity를 사용해 상태 코드를 명시적으로 설정할 수 있습니다. 이렇게 하면 예외 상황에 대해 더 유연한 처리가 가능합니다.

 

 

4. 예외 발생 시나리오

 

다음은 리소스를 찾지 못했을 때 ResourceNotFoundException을 발생시키는 서비스 코드의 예입니다.

 

src/main/java/com/example/demo/service/PostService.java

package com.example.demo.service;

import com.example.demo.entity.Post;
import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.repository.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class PostService {

    @Autowired
    private PostRepository postRepository;

    public Post getPostById(Long id) {
        Optional<Post> post = postRepository.findById(id);
        if (!post.isPresent()) {
            throw new ResourceNotFoundException("Post not found with id: " + id);
        }
        return post.get();
    }

    // 기타 CRUD 메서드들...
}

5. 컨트롤러에서의 사용 예시

컨트롤러에서 서비스 메서드를 호출할 때, 예외가 발생하면 GlobalExceptionHandler가 이를 처리하여 일관된 응답을 반환합니다.

 

src/main/java/com/example/demo/controller/PostController.java

package com.example.demo.controller;

import com.example.demo.entity.Post;
import com.example.demo.service.PostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/posts")
public class PostController {

    @Autowired
    private PostService postService;

    // GET /api/posts/{id} - Get a post by id
    @GetMapping("/{id}")
    public ResponseEntity<Post> getPostById(@PathVariable Long id) {
        Post post = postService.getPostById(id);
        return ResponseEntity.ok(post);
    }

    // 기타 CRUD 엔드포인트들...
}

 

반응형

+ Recent posts