# 오류 보충자료

### 1. 예외 처리가 필요한 이유

1. **정상적인 흐름 유지**:
   * 애플리케이션에서 예기치 못한 상황(에러)이 발생하더라도 **애플리케이션이 중단되지 않고 실행**될 수 있도록 제어.
   * **예시 상황**: 사용자가 잘못된 데이터를 입력하거나 네트워크가 불안정할 경우.
2. **리소스 낭비 방지**:
   * 에러 발생 시 조치를 취하지 않으면 애플리케이션이 **메모리 누수**나 **과부하**로 인해 서버가 중단될 가능성이 있음.
3. **코드 가독성 향상**:
   * 정상 흐름과 예외 흐름을 분리하여 코드의 **가독성 및 유지보수성**을 높임.
4. **디버깅 및 로그**:
   * 발생한 에러를 기록(logging)하여 디버깅에 도움을 줌.
   * 로그를 통해 문제의 원인을 추적 가능.

***

### 2. 예외 처리 예제

#### (1) 기본적인 예외 처리: 정상 흐름 유지

**코드 예시**

```java
public class ExceptionExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0); // 0으로 나누기 시도
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Error: Division by zero is not allowed."); // 에러 처리
        }
        System.out.println("Program continues...");
    }

    public static int divide(int a, int b) {
        return a / b; // ArithmeticException 발생 가능
    }
}
```

**실행 결과**

```vbnet
Error: Division by zero is not allowed.
Program continues...
```

***

#### (2) 에러 발생 시 작업 누적의 문제: 메모리 누수 예제

* **설명**: 예외를 처리하지 않고 계속 실행하면 메모리 누수가 발생할 수 있음.

**코드 예시**

```java
import java.util.ArrayList;
import java.util.List;

public class MemoryLeakExample {
    public static void main(String[] args) {
        List<String> memory = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            try {
                memory.add("Data: " + i);
                if (i % 1000 == 0) {
                    throw new RuntimeException("Simulated exception");
                }
            } catch (RuntimeException e) {
                System.out.println("Exception caught: " + e.getMessage());
            }
        }
        System.out.println("Completed without fixing the memory issue.");
    }
}
```

**실행 결과**

```php
Exception caught: Simulated exception
Exception caught: Simulated exception
...
Completed without fixing the memory issue.
```

* **문제점**: 리스트에 데이터가 계속 쌓이지만, 에러 발생 후 제거되지 않아 메모리 누수가 발생할 가능성이 있음.

***

#### (3) 서버 과부하 및 메모리 누적 문제

* **상황**: 서버가 며칠마다 죽는 문제 발생 → 로그 확인 시 메모리 누적 또는 디스크 용량 부족.

**코드 예시**

```java
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class ServerExample {
    public static void main(String[] args) {
        while (true) {
            try (BufferedWriter writer = new BufferedWriter(new FileWriter("logs.txt", true))) {
                writer.write("Processing request...\n");
                simulateError(); // 에러 발생 가능
            } catch (IOException e) {
                System.out.println("I/O error occurred: " + e.getMessage());
            } catch (RuntimeException e) {
                System.out.println("Application error occurred: " + e.getMessage());
            }
        }
    }

    public static void simulateError() {
        if (Math.random() > 0.9) { // 10% 확률로 에러 발생
            throw new RuntimeException("Simulated runtime error");
        }
    }
}
```

***

#### (4) 정상 흐름과 예외 흐름의 혼합 문제

* **문제**: 정상 흐름과 예외 흐름이 섞이면 코드 이해가 어려워짐.

**반환값으로 에러를 처리하는 코드**

```java
public class ReturnValueExample {
    public static void main(String[] args) {
        String result = divideWithReturn(10, 0);
        if (result.equals("Error")) {
            System.out.println("Division failed.");
        } else {
            System.out.println("Result: " + result);
        }
    }

    public static String divideWithReturn(int a, int b) {
        if (b == 0) {
            return "Error";
        }
        return String.valueOf(a / b);
    }
}
```

**예외 처리를 활용한 코드**

```java
public class ExceptionHandlingExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Error: Division by zero is not allowed.");
        }
    }

    public static int divide(int a, int b) {
        return a / b;
    }
}
```

* **차이점**:
  * 반환값 방식: 에러와 정상 데이터가 동일한 데이터 흐름을 공유하여 혼란 발생.
  * 예외 처리 방식: 에러 흐름과 정상 흐름을 분리하여 가독성 향상.

***

### 3. 예외 처리에서 로그 활용

* **로그 기록의 중요성**:
  * 애플리케이션 실행 중 발생한 에러를 기록하여 디버깅에 도움을 줌.
  * 로그는 **운영 환경에서 문제를 진단**하는 주요 도구.

**코드 예시: 로그 기록**

```java
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public class LoggingExample {
    private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());

    public static void main(String[] args) {
        try {
            FileHandler fileHandler = new FileHandler("application.log", true);
            fileHandler.setFormatter(new SimpleFormatter());
            logger.addHandler(fileHandler);

            int result = divide(10, 0); // 에러 발생
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            logger.severe("Error: Division by zero. " + e.getMessage());
        } catch (IOException e) {
            logger.severe("Failed to set up logging. " + e.getMessage());
        }
    }

    public static int divide(int a, int b) {
        return a / b;
    }
}
```

**로그 결과 (`application.log`)**

```vbnet
SEVERE: Error: Division by zero. / by zero
```

***

#### 4. 자바의 예외 계층 구조

***

#### (1) 예외 계층

자바의 예외는 모두 `Throwable` 클래스를 상속하며, 두 가지 주요 분류로 나뉩니다:

1. **`Error`**:
   * **정의**: 복구 불가능한 시스템 수준의 예외.
   * **예시**: `OutOfMemoryError`, `StackOverflowError`.
2. **`Exception`**:
   * **정의**: 프로그램에서 예외적인 상황을 처리하기 위한 클래스.
   * **세부 분류**:
     * **체크 예외**: 개발자가 명시적으로 처리해야 함.
       * 예시: `IOException`, `SQLException`.
     * **언체크 예외**: 명시적 처리 불필요, 런타임에 발생.
       * 예시: `NullPointerException`, `IllegalArgumentException`.

***

#### (2) 예외 계층 다이어그램

```php
Throwable
   ├── Error (복구 불가능한 예외)
   │    ├── OutOfMemoryError
   │    ├── StackOverflowError
   │
   └── Exception (프로그램 예외)
        ├── Checked Exception (체크 예외)
        │    ├── IOException
        │    ├── SQLException
        │
        └── RuntimeException (언체크 예외)
             ├── NullPointerException
             ├── IllegalArgumentException
             ├── ArithmeticException
```

***

#### (3) 체크 예외와 언체크 예외의 차이점

| **특징**       | **체크 예외**                         | **언체크 예외**                                    |
| ------------ | --------------------------------- | --------------------------------------------- |
| **정의**       | 컴파일 시점에 명시적 예외 처리 요구              | 런타임 시점에 발생, 명시적 처리 불필요                        |
| **대표 클래스**   | `IOException`, `SQLException`     | `NullPointerException`, `ArithmeticException` |
| **처리 필요 여부** | 반드시 `try-catch` 또는 `throws` 선언 필요 | 필요 없음, 자동으로 호출자에게 전달됨                         |
| **예시 상황**    | 파일 읽기/쓰기 실패, 네트워크 연결 실패           | 배열 인덱스 초과, null 객체 접근                         |

***

### 5. 예외 처리 기본 규칙

#### (1) 기본 규칙

1. **예외 발생 시 처리하거나 던져야 함**:
   * 발생한 예외는 `try-catch`로 처리하거나, `throws`로 호출자에게 전달.
2. **상위 타입 예외 처리**:
   * 부모 타입 예외를 잡으면, 해당 타입의 모든 자식 예외도 처리 가능.

***

### 6. 예외 처리 예제 코드

#### (1) 체크 예외: `IOException` 처리

**코드 예시**

```java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class CheckedExceptionExample {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {
            System.out.println(reader.readLine());
        } catch (IOException e) {
            System.out.println("Error: Unable to read file. " + e.getMessage());
        }
    }
}
```

**실행 결과 (파일이 없을 경우)**

```vbnet
Error: Unable to read file. test.txt (No such file or directory)
```

***

#### (2) 언체크 예외: `NullPointerException` 처리

**코드 예시**

```java
public class UncheckedExceptionExample {
    public static void main(String[] args) {
        String str = null;
        try {
            System.out.println(str.length());
        } catch (NullPointerException e) {
            System.out.println("Error: Null value encountered.");
        }
    }
}
```

**실행 결과**

```yaml
Error: Null value encountered.
```

***

#### (3) `throws`를 사용한 예외 전달

**코드 예시**

```java
import java.io.IOException;

public class ThrowsExample {
    public static void main(String[] args) {
        try {
            readFile();
        } catch (IOException e) {
            System.out.println("Handled in main: " + e.getMessage());
        }
    }

    public static void readFile() throws IOException {
        throw new IOException("Simulated file read error.");
    }
}
```

**실행 결과**

```lua
lua코드 복사Handled in main: Simulated file read error.
```

***

#### (4) 상위 예외로 처리

**코드 예시**

```java
public class GeneralExceptionExample {
    public static void main(String[] args) {
        try {
            throwException(1);
            throwException(2);
        } catch (Exception e) { // 부모 타입으로 모든 예외 처리
            System.out.println("Caught: " + e.getClass().getSimpleName());
        }
    }

    public static void throwException(int type) throws Exception {
        if (type == 1) {
            throw new NullPointerException("Null value!");
        } else if (type == 2) {
            throw new IllegalArgumentException("Illegal argument!");
        }
    }
}
```

**실행 결과**

```makefile
Caught: NullPointerException
```

***

#### (5) `Error` 예외 예제 (복구 불가능)

**코드 예시**

```java
public class ErrorExample {
    public static void main(String[] args) {
        try {
            causeError();
        } catch (Error e) {
            System.out.println("Caught: " + e.getMessage());
        }
    }

    public static void causeError() {
        throw new OutOfMemoryError("Simulated out of memory!");
    }
}
```

**실행 결과**

```csharp
Caught: Simulated out of memory!
```

#### 7. `finally` 키워드와 반드시 실행되는 블록

***

#### (1) `finally` 키워드란?

* **정의**: `try-catch` 블록이 종료될 때 **항상 실행되는 코드 블록**.
* **용도**:
  * 리소스 정리, 연결 해제 등 **반드시 실행해야 하는 작업** 처리.
  * 예외 발생 여부와 관계없이 실행됨.
* **주의**:
  * `finally`는 `try` 블록에 리턴값이 있어도 **항상 실행**됨.

***

#### (2) `finally` 사용 예제

**코드 예시**

```java
public class FinallyExample {
    public static void main(String[] args) {
        try {
            System.out.println("Try block executed.");
            throw new RuntimeException("Simulated exception");
        } catch (Exception e) {
            System.out.println("Catch block executed: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed. Always runs.");
        }
    }
}
```

**실행 결과**

```vbnet
Try block executed.
Catch block executed: Simulated exception
Finally block executed. Always runs.
```

***

#### (3) `finally` 블록 활용

* **리소스 해제**:
  * 파일, 네트워크 연결, 데이터베이스 등 외부 자원 해제.
* **예시**: 파일 닫기

```java
import java.io.FileReader;
import java.io.IOException;

public class FinallyFileExample {
    public static void main(String[] args) {
        FileReader reader = null;
        try {
            reader = new FileReader("test.txt");
            System.out.println("File opened.");
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                    System.out.println("File closed.");
                } catch (IOException e) {
                    System.out.println("Error closing file: " + e.getMessage());
                }
            }
        }
    }
}
```

***

#### 8. 언체크 예외와 체크 예외의 현대적 사용

***

#### (1) 언체크 예외 중심 개발

* 현재 자바 애플리케이션 개발은 **언체크 예외 중심**으로 이루어짐.
* **언체크 예외의 장점**:
  * 명시적 처리 없이 자동으로 호출자에게 전달되어 코드 간결화.
* **단점**:
  * 처리하지 않은 예외가 시스템 전반으로 확산될 위험.

***

#### (2) 공통 예외 처리

* **전략**:
  * **공통 예외 처리 로직**을 작성하여 대부분의 예외를 한 곳에서 처리.
  * 시스템에서 **복구 불가능한 에러**만 **체크 예외**로 처리.

**코드 예시: 공통 예외 처리**

```java
public class GlobalExceptionHandler {
    public static void handleException(Exception e) {
        System.out.println("Exception handled globally: " + e.getMessage());
    }
}

public class UncheckedExceptionExample {
    public static void main(String[] args) {
        try {
            String str = null;
            System.out.println(str.length());
        } catch (Exception e) {
            GlobalExceptionHandler.handleException(e);
        }
    }
}
```

**실행 결과**

```php
Exception handled globally: null
```

***

#### 9. Try-with-resources (자바 7 이상)

***

#### (1) `try-with-resources`란?

* **정의**: `try` 블록에서 사용한 리소스를 **자동으로 반납**하는 구조.
* **장점**:
  * `finally` 블록 없이도 리소스 반납 가능.
  * `try`가 종료되면 `close()` 메서드가 자동으로 호출.
* **조건**:
  * 사용된 객체가 **`AutoCloseable` 인터페이스**를 구현해야 함.
  * `close()` 메서드를 오버라이드하여 반납 작업 정의.

***

#### (2) `try-with-resources` 예제

**코드 예시: 파일 읽기**

```java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TryWithResourcesExample {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        }
    }
}
```

**실행 결과 (파일이 존재할 경우)**

```arduino
File content...
```

***

#### (3) `AutoCloseable`과 `close()` 오버라이드

**코드 예시: 커스텀 리소스 관리**

```java
class CustomResource implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("Resource closed automatically.");
    }

    public void use() {
        System.out.println("Using resource.");
    }
}

public class CustomResourceExample {
    public static void main(String[] args) {
        try (CustomResource resource = new CustomResource()) {
            resource.use();
        } catch (Exception e) {
            System.out.println("Exception: " + e.getMessage());
        }
    }
}
```

**실행 결과**

```mathematica
Using resource.
Resource closed automatically.
```

***

####
