# 스프링 애플리케이션 예외 처리 가이드

## 목차
1. [개요](#개요)
2. [전역 예외 처리 아키텍처](#전역-예외-처리-아키텍처)
3. [예외 처리 구현 가이드](#예외-처리-구현-가이드)
4. [예외 응답 형식](#예외-응답-형식)
5. [예외 처리 모범 사례](#예외-처리-모범-사례)
6. [문제 해결 가이드](#문제-해결-가이드)

## 개요

이 문서는 스프링 기반 애플리케이션에서의 예외 처리 메커니즘을 상세히 설명합니다. 
체계적인 예외 처리를 통해 애플리케이션의 안정성을 높이고 클라이언트에게 명확한 오류 정보를 제공할 수 있습니다.

## 예외 처리 가이드라인
- 도메인별 error 패키지 생성: 각 도메인 패키지 내에 error 패키지를 생성하여 예외를 구조화합니다.
- BizBaseException 상속: 도메인별 예외 클래스는 공통 예외 로직을 재사용하기 위해 BizBaseException을 상속받습니다.
- ErrorRule 구현: 도메인별로 발생 가능한 예외 규칙을 ErrorRule로 정의합니다.
- 에러 메시지 관리: 메시지는 절대 하드코딩하지 않고 ErrorRule을 통해 관리합니다.

## 전역 예외 처리 아키텍처

### GlobalExceptionHandler 클래스

```java
@RestControllerAdvice
public class GlobalExceptionHandler {
    // 예외 처리 메서드들이 구현됨
}
```

`@RestControllerAdvice` 어노테이션을 사용하여 전역 예외 처리기를 구현합니다. 이 클래스는 다음과 같은 주요 기능을 제공합니다:

- 애플리케이션 전반의 예외를 일관되게 처리
- HTTP 응답 상태 코드 및 본문 자동 생성
- 구조화된 오류 응답 제공

## 예외 처리 구현 가이드

### 1. 비즈니스 예외 처리 (BizBaseException)

```java
@ExceptionHandler(BizBaseException.class)
public BizErrorResponse handleCustomException(BizBaseException e) {
    return BizErrorResponse.valueOf(e.getErrorRule());
}
```

#### 주요 특징
- 비즈니스 로직에서 발생하는 모든 커스텀 예외 처리
- `ErrorRule`을 통한 체계적인 오류 분류
- 일관된 응답 형식 제공

#### 사용 예시
```java
if (data == null) {
    throw new BizBaseException();
}
```

#### 응답 예시
```json
{
    "timestamp": "2024-11-18T10:59:51.6131807",
    "statusCode": 500,
    "path": "/",
    "data": null,
    "message": "시스템 오류 입니다."
}
```

### 2. 유효성 검증 예외 처리 (MethodArgumentNotValidException)

```java
@ExceptionHandler(MethodArgumentNotValidException.class)
public BizErrorResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {
    return BizErrorResponse.fromFieldError(e.getFieldErrors());
}
```

#### 처리되는 상황
- 요청 DTO의 필드 유효성 검증 실패
- `@Valid` 또는 `@Validated` 어노테이션 사용 시 발생하는 예외
- 잘못된 데이터 형식이나 필수 필드 누락

#### 응답 예시
```json
{
    "timestamp": "2024-11-18T11:03:05.7627081",
    "statusCode": 422,
    "path": "/api/user/sign-up",
    "data": [
        {
            "field": "userPassword",
            "value": "",
            "reason": "비밀번호는 필수값 입니다."
        }
    ],
    "message": "요청을 처리할 수 없습니다."
}
```

### 3. 데이터베이스 예외 처리 (DataAccessException)

```java
@ExceptionHandler(DataAccessException.class)
public BizErrorResponse handleDataAccessException(DataAccessException e) {
    Throwable cause = e.getMostSpecificCause();
    return BizErrorResponse.valueOf(
        new BizBaseException(
            Optional.ofNullable(cause)
            .map(Throwable::getMessage)
            .orElse(ExceptionRule.SYSTEM_ERROR.getMessage()))
            .getErrorRule()
    );
}
```

#### 주요 처리 상황
- 데이터베이스 연결 실패
- 중복 키 위반
- 데이터 무결성 제약 조건 위반
- SQL 문법 오류

## 도메인별 예외 처리
도메인별로 발생할 수 있는 예외를 정의하고, GlobalExceptionHandler에서 처리되도록 구조화합니다.

### 1. 도메인별 error 패키지 생성
각 도메인 패키지 내에 error 패키지를 생성합니다. 예를 들어, 사용자 도메인의 경우 아래와 같은 구조를 가집니다.
- com.skcc.domain.user.error

### 2. BizBaseException 상속
모든 도메인별 예외 클래스는 공통 예외 처리 로직을 재사용하기 위해 BizBaseException을 상속받습니다.

```java
import com.spring.common.error.BizBaseException;

public class UserNotFoundException extends BizBaseException {
    public UserNotFoundException() {
        super(UserRule.USER_NOT_FOUND);
    }
}
```

### 3. ErrorRule 구현

#### 3.1 ErrorRule 인터페이스 정의
도메인별 예외 규칙을 정의하기 위해 ErrorRule 인터페이스를 구현합니다. 이를 통해 예외 메시지와 HTTP 상태 코드를 일관되게 관리할 수 있습니다.

```java
import org.springframework.http.HttpStatus;

public interface ErrorRule {
    HttpStatus getStatus();
    String getMessage();
}
```

#### 3.2 도메인별 ErrorRule 정의
각 도메인에서 발생할 수 있는 예외를 enum으로 정의합니다. 예외 메시지와 HTTP 상태 코드를 설정하여 한 곳에서 관리할 수 있습니다.

```java
@Getter
@RequiredArgsConstructor
public enum UserRule implements ErrorRule {
    
    USER_NOT_FOUND(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."),
    USER_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "사용자 인증에 실패 하였습니다.");

    private final HttpStatus status;
    private String message;

    UserRule(HttpStatus status, String message) {
        this.status = status;
        this.message = message;
    }

    public UserRule message(final String message) {
        this.message = message;
        return this;
    }

}
```

### 4. 에러 메시지 관리

#### 4.1 하드코딩 방지
- 에러 메시지는 절대 하드코딩하지 않고, 각 도메인의 ErrorRule을 통해 관리합니다.
- 이를 통해 에러 메시지의 일관성을 유지하고, 메시지 변경 시 코드 수정 범위를 최소화할 수 있습니다.

```java
public class UserNotFoundException extends BizBaseException {
    public UserNotFoundException() {
        super(UserRule.USER_NOT_FOUND);
    }
}
```

<div style="text-align: left; margin: 20px 0;">
<a href="#" style="color: white; text-decoration: none;">
    <button style="background-color: #007BFF; color: white; padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer;">
        Top
    </button>
</a>
</div>