> For the complete documentation index, see [llms.txt](https://team-everywhere.gitbook.io/backend/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://team-everywhere.gitbook.io/backend/spring/spring-boot/13/undefined.md).

# 예외처리 보충자료

## **REST API 에러 처리**

***

### **Produces와 ResponseEntity를 활용한 에러 처리**

#### **Produces 우선 처리**

* 클라이언트 요청의 `Accept` 헤더에 따라 반환 타입을 결정합니다.
  * `application/json`: JSON 형식으로 반환.
  * `text/html`: HTML 형식으로 반환.
* `@RequestMapping` 또는 `@GetMapping`에서 `produces`를 설정하여 반환 형식을 지정할 수 있습니다.

#### **ResponseEntity**

* HTTP 응답 바디에 직접 데이터를 넣어 반환합니다.
* 상태 코드와 객체를 함께 반환할 수 있습니다.

**ResponseEntity 예시**

```java
@GetMapping(value = "/example", produces = "application/json")
public ResponseEntity<String> exampleEndpoint() {
    String response = "{\"message\": \"Hello, World!\"}";
    return ResponseEntity.ok(response);
}
```

***

### **서버 에러 설정**

#### **application.properties 설정**

* 에러 메시지 출력 옵션:

```properties
server.error.include-binding-errors=always
server.error.include-exception=true
```

* 위 설정을 통해 바인딩 에러나 예외를 자세히 출력할 수 있습니다.

***

### **HandlerExceptionResolver**

#### **개요**

* `HandlerExceptionResolver`를 구현하여 MVC 컨트롤러 밖에서 발생한 예외를 처리할 수 있습니다.
* 예외를 WAS까지 전달하지 않고 Spring MVC에서 해결합니다.

#### **HandlerExceptionResolver 활용**

1. **HTML과 JSON 반환 나누기**
   * 클라이언트 요청의 `Accept` 헤더를 확인하여 반환 형식을 결정합니다.
2. **Spring MVC에서 모든 예외를 처리하고 WAS로 전달하지 않음**
   * 에러가 깔끔하게 처리되어 관리가 용이해집니다.

**HandlerExceptionResolver 예시**

```java
@Component
public class CustomExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(
        HttpServletRequest request, 
        HttpServletResponse response, 
        Object handler, 
        Exception ex
    ) {
        String accept = request.getHeader("Accept");
        if (accept.contains("application/json")) {
            try {
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.setContentType("application/json");
                response.getWriter().write("{\"error\": \"" + ex.getMessage() + "\"}");
                response.getWriter().flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return new ModelAndView();
        } else {
            ModelAndView modelAndView = new ModelAndView("error");
            modelAndView.addObject("message", ex.getMessage());
            return modelAndView;
        }
    }
}
```

***

### **Spring이 제공하는 ExceptionResolver**

1. **`ExceptionHandlerExceptionResolver`**
   * 대부분의 API에서 발생하는 예외를 처리합니다.
2. **`ResponseStatusExceptionResolver`**
   * 예외에 대해 HTTP 상태 코드를 지정합니다.
3. **`DefaultHandlerExceptionResolver`**
   * 스프링 내부에서 발생하는 기본 예외를 처리합니다.

***

### **`@ResponseStatus`를 활용한 예외 처리**

#### **`@ResponseStatus`**

* 특정 예외에 HTTP 상태 코드를 부여할 수 있습니다.
* `messages.properties`와 함께 사용자 정의 메시지를 설정할 수 있습니다.

**사용 예시**

```java
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}
```

**messages.properties**

```properties
resource.not.found=Resource not found.
```

**컨트롤러에서 사용**

```java
@GetMapping("/user/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
    User user = userService.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("User with ID " + id + " not found."));
    return ResponseEntity.ok(user);
}
```

***

### **`@ExceptionHandler`로 예외 처리**

#### **개요**

* 컨트롤러 또는 서비스에서 발생하는 예외를 처리합니다.
* 반환값을 `ResponseEntity`로 설정하여 클라이언트가 원하는 형식으로 에러를 반환할 수 있습니다.

#### **컨트롤러 단위 예시**

```java
@RestController
@RequestMapping("/api/users")
public class UserController {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<Map<String, String>> handleResourceNotFound(ResourceNotFoundException ex) {
        Map<String, String> error = new HashMap<>();
        error.put("error", ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    }
}
```

***

### **전역 예외 처리**

#### **`@ControllerAdvice` 및 `@RestControllerAdvice`**

* 모든 컨트롤러 또는 특정 컨트롤러에서 발생한 예외를 처리합니다.

#### **전역 예외 처리 예시**

```java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<Map<String, String>> handleResourceNotFound(ResourceNotFoundException ex) {
        Map<String, String> error = new HashMap<>();
        error.put("error", ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, String>> handleGenericException(Exception ex) {
        Map<String, String> error = new HashMap<>();
        error.put("error", "An unexpected error occurred: " + ex.getMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}
```

***

### **컨트롤러 적용 범위 지정**

#### **특정 컨트롤러 또는 패키지 하위 적용**

```java
@ControllerAdvice(assignableTypes = {UserController.class})
public class UserControllerAdvice {
    // 특정 컨트롤러(UserController)만 적용
}

@ControllerAdvice(basePackages = "com.example.api")
public class ApiControllerAdvice {
    // 특정 패키지(com.example.api) 하위 컨트롤러만 적용
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://team-everywhere.gitbook.io/backend/spring/spring-boot/13/undefined.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
