---
# System prepended metadata

title: 프로그래밍의 오류
tags: [과외(하희영)]

---

# 프로그래밍의 오류

- 프로그램에서 오류가 발생하면 시스템 레벨에서 프로그램에 문제를 야기하여 원치 않는 버그를 일으키거나, 심각하면 실행 중인 프로그램을 강제로 종료시키도 한다.
- 프로그래밍의 오류는 다양하지만, 크게 3가지로 나뉜다.
    - 컴파일 에러(compile-time error) : 컴파일시에 발생하는 에러 
    - 런타임 에러(runtime error) : 실행시에 발생하는 에러 
    - 논리적 에러(logical error) : 실행은 되지만 의도와 다르게 동작하는것

## 논리 에러 (Logic Error)

- **버그**
- 작동하는데는 아무런 문제가 없는 오류이지만, 결과가 예상과 달라 사용자가 의도한 작업을 수행하지 못하게 되어 서비스 이용에 지장이 생길 수 있는 에러
- 논리적 오류는 컴퓨터 입장에서는 프로그램이 멀쩡히 돌아가는 것이니 에러 메시지를 알려주지 않는다.
    - 따라서 개발자는 프로그램의 전반적인 코드와 알고리즘을 체크 필요가 있다

## 컴파일 에러 (Compillation Error)

- 컴파일 단계에서 오류 발견하면 컴파일러가 에러 메시지 출력해주는 것을 의미
- 컴파일 에러가 있다는 것은, 곧 컴파일이 안된다는 의미이며, 이는 즉 프로그램이 만들어지지 않아 프로그램 실행 자체가 불가하다
    - 사전에 문제를 파악할 수 있기 대문에 심각한 오류는 아니다

## 런타임 에러 (Runtime Error)

- 프로그램 실행 중에 에러가 발생해서 잘못된 결과를 얻거나, 혹은 외부적인 요인으로 기계적 결함으로 프로그램이 비정상적으로 종료되는 에러
- 자바에서는 런타임 에러를 두가지로 구분하였다
    - 에러(error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
    - 예외(exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류 

# 예외

![](https://i.imgur.com/uqFlK9X.png)

**에러(error)**
- 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
- 메모리 부족(OutOfMemoryError)이나 스택오버플로우(StackOverflowError)와 같이 일단 발생하면 복구할 수 없는 심각한 오류이고 예측이 불가능한 상황에 발생

**예외(exception)**
- 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
    - 수습될 수 있는 비교적 덜 심각한 오류
- 대부분 개발자가 구현한 로직에서 발생한 실수나 사용자의 영향에 의해 발생
- 대응 코드를 미리 작성해 놓음으로써 어느정도 프로그램의 비정상적인 종료 혹은 동작을 막을 수 있다
    - 자바의 예외 처리 문법(try - catch)

> [ 예외 처리(exception handling) ]
> - 예외 처리란 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 행위를 의미
> - 예외 처리의 목적은 예외의 발생으로 인한 실행중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것

---

# 자바의 예외

JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 **예외 클래스로 객체를 생성**하고서 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해준다

> **오류 클래스 계층 구조**
> ![](https://i.imgur.com/uckNalo.png)
> Error 클래스는 외부적인 요인으로 인해 발생하는 오류이기 때문에 개발자가 대처 할 수는 없다. 따라서 중점적으로 봐야할 클래스는 바로 Exception 클래스이다.

---

**자바의 예외는 크게 두 가지로 나뉜다.**
- 체크 예외(Checked Exception)
- 런타임 예외(Runtime Exception)

![](https://i.imgur.com/rx3P1a7.png)

---

### 체크 예외(Checked Exception)
- 컴파일러가 **예외 처리를 강제하는 예외**
    - 컴파일러가 예외처리를 제대로 하는지 확인해준다 (하지 않을시 컴파일 오류)
- 어느 메서드가 어떤 예외를 던지는지 명확히 알 수 있다
- 주로 외부 리소스와 관련된 작업에서 발생하며, 개발자가 예외 처리를 통해 오류 상황에 대처할 수 있도록 설계되어 있다
    - 예) 파일 입출력, 데이터베이스 연결 등의 경우

---

### 런타임 예외(Runtime Exception) / Unchecked Exception
- `RuntimeException`을 상속받는 예외들
- 컴파일러가 **예외 처리를 강제하지 않는 예외**
    - 컴파일러가 따로 검사를 해주지 않는다
- 어디서 어떤 예외가 나오는지 한눈에 보이지 않는다
- 주로 프로그래머의 실수나 논리적인 오류로 인해 발생
    - 예) 배열의 범위를 벗어난 인덱스에 접근하거나, `null` 참조를 사용하는 등의 경우

> **다양한 런타임 예외**
> ![](https://i.imgur.com/GUwOjoU.png)

||Checked Exception|Unchecked Exception|
|---|---|---|
|처리 여부|반드시 예외를 처리해야 함|명시적인 처리를 안해도 됨|
|확인 시점|컴파일 단계|런타임 단계|
|예외 종류|RuntimeException을 제외한, Exception 클래스와 그를 상속받는 하위 예외|RuntimeException 과 그 하위 예외|

---

# 예외 처리

**프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것**

- 목적 : 예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상적인 동작을 막고, 에러를 잡아 복구를 시도하거나 아니면 회피 하는식으로 처리를해서, 프로그램이 정상적인 실행상태를 유지할 수 있도록 하는 것

## try - catch - finally 문

![](https://i.imgur.com/ENs0JJ6.png)

**`try`**
- 예외가 발생할 수 있는 코드 블록을 감싸는 데 사용
- `try`블록 내부의 코드가 실행되다가 예외가 발생하면, 해당 예외와 일치하는 `catch` 블록으로 제어가 이동한다

**`catch`**
- 예외가 발생했을 때 처리할 코드 블록을 정의하는 데 사용
- `catch` 블록은 예외 유형에 따라 여러 개를 사용할 수 있다
    - 각 `catch` 블록은 특정 예외 유형을 처리하도록 정의되며, 발생한 예외와 일치하는 `catch` 블록이 실행된다

---
``` java
try {
    // 예외가 발생할 수 있는 코드
} catch (FileNotFoundException e) {
    // FileNotFoundException 예외 처리
} catch (IOException e) {
    // IOException 예외 처리
}
```
---
``` java
int a, b, c;
try {
    a = 10;
    b = 0;
    c = a / b; // 10 나누기 0 → 산술오류 ArithmeticException
} catch (ArithmeticException e) {
    c = -1;  // 예외가 발생하여 이 문장이 수행된다.
}
```
---


**`finally`**
- 예외가 발생하던, 발생하지 않던 무조건 실행되는 블럭
- 어떤 예외가 발생하더라도 반드시 실행되어야 할 때 사용

> ![](https://i.imgur.com/TEublDJ.png)

---

``` java
int[] numbers = {10, 5, 0};

try {
    int index = 3;
    int divisor = 0;

    int result = numbers[index] / divisor;
    System.out.println("Result: " + result);
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("Error: 배열 인덱스가 범위를 벗어났습니다");
} catch (ArithmeticException e) {
    System.out.println("Error: 0으로 나눌 수 없습니다");
} finally {
    System.out.println("이 메시지는 예외에 관계없이 출력됩니다");
}
```

---

> JDK 1.7부터 여러 catch 블럭을 `|` 기호를 통해서 하나의 `catch` 블럭으로 합칠 수 있게 되었다
>
> ``` java
> try {
>     // ...
> } catch (NullPointException | ArrayIndexOutOfBoundsExcetion e) {
>     // ...
> }
> ```
---

## 예외 던지기

- 예외 처리를 메소드 내부에서 직접 수행하지 않고, 메소드를 호출한 상위 호출자가 적절하게 처리할 수 있다. 이를 예외 던지기라고 부른다
    - 예외 던지기 : 메소드 내에서 발생한 예외를 해당 메소드를 호출한 곳으로 전달하여 처리할 수 있도록 하는 기능
- `throw` 키워드를 사용하여 강제로 예외를 발생시킬 수 있다
    - 이 때 `new` 생성자로 예외 클래스를 초기화하여 던져는데, 이 클래스 생성자에 입력값을 주게되면, `catch`문의 `getMessage()`메서드에서 출력할 메시지를 지정하게 된다.
- `throws` 키워드를 사용하여 상위 클래스에서 예외처리를 강제하도록 할 수 있다.

---

### `throws` / `throw`

**`throw`**
- 예외를 발생시키기 위해 사용
- 메소드 내부에서 사용되며, 실제로 예외를 발생시키는 데 사용된다.
- 생성된 예외 객체를 던져서 예외 처리 흐름을 시작한다.
- 예: `throw new FileNotFoundException("File not found.");`

**`throws`**
- 예외를 떠넘기기 위해 사용
- 메소드 선언부에 사용되며, 해당 메소드가 발생시킬 수 있는 예외를 명시한다.
- 메소드를 호출하는 쪽에서 이 메소드가 발생시킬 수 있는 예외에 대한 처리> 를 대비하도록 한다
- 예: `public void readFile() throws IOException { ... }`
---

``` java
public static void main(String[] args) {
    try {
        methodA();
    } catch (IOException e) {
        System.out.println("Caught exception: " + e.getMessage());
    }
}

public static void methodA() throws IOException {
    methodB();
}

public static void methodB() throws IOException {
    throw new IOException("An error occurred in methodB.");
}
```
1. `methodB`는 `IOException`을 발생시키고, 호출자로 전달한다.
2. `methodA`는 `methodB`를 호출하지만, 예외를 처리하지 않고 다시 호출자인 `main` 메소드로 전달한다.
3. `main` 메소드의 `try-catch` 블록에서 `IOException`을 처리한다
4. 이 과정을 통해 `methodA`, `methodB` 메서드는 예외 처리에 관여하지 않고, 호출자가 적절한 처리를 수행할 수 있다


### **예외가 발생했을 때 진행 순서**
1. `try` 블록의 실행이 중단됨
2. `catch` 블록 중에 발생한 예외를 처리할 수 있는지 찾음
   - 위에서부터 하나씩 평가
3. 예외를 처리할 수 있는 `catch` 블록이 있다면 해당 `catch` 블록안의 코드들이 실행 후 `finally` 블록 실행. `try`블록 이후의 코드들이 실행됨
4. 예외를 처할 수 있는 `catch` 블록이 없다면 `finally`블록을 실행 후 한 단계 높은 `try` 블록으로 전달


---

## 연결된 예외 (Chained Exception)

- 자바에서 예외 처리를 할 때, 하나의 예외가 다른 예외를 유발하거나 함께 처리해야 하는 상황을 다루기 위한 개념
- 발생한 예외를 다른 예외와 연결하여, 하나의 예외 처리 블록에서 여러 예외를 처리하는 것
- 연결된 예외를 통해 원래 발생한 예외의 원인을 추적하고, 예외 처리를 더 명확하고 효과적으로 할 수 있다

### 연결된 예외 사용 방법


- `Throwable` 클래스의 `initCause(Throwable cause)` 메소드를 사용하여 예외 객체에 원인 예외를 설정한다.
- `Throwable` 클래스의 `getCause()` 메소드를 사용하여 원인 예외를 가져온다.

``` java
try {
    // 예외를 발생시키는 코드
} catch (IOException e) {
    // IOException을 처리하면서 다른 예외가 발생하는 상황을 가정한다.
    RuntimeException runtimeException = new RuntimeException("An error occurred while handling IOException");
    runtimeException.initCause(e);
    throw runtimeException;
}
```
> 연결된 예외(chained exception)를 사용하는 또 다른 이유는 checked예외를 unchecked예외로 바꿀 수 있도록 하기 위함이다.



## 커스텀 예외

- 자바의 예외는 결국 클래스다. 따라서 예외 클래스를 상속받아서 확장시킬 수 있다.
- 커스텀 예외 클래스는 `Exception`을 상속받는 클래스를 상속받아 만들 수 있다. (`Exception`을 상속받아도 된다)

``` java
public final class UserNotFoundException extends RuntimeException {
    public UserNotFoundException() {
        super();
    }
    
    public UserNotFoundException(String message) {
        super(message);
    }
    
    public UserNOtFolundException(String message, Throwable cause) {
        super(message, cause);
    }
}

```

---

``` java
public class CustomExceptionExample {
    public static void main(String[] args) {
        try {
            validateUserInput("Invalid input");
        } catch (UserNotFoundException e) {
            System.out.println("Caught custom exception: " + e.getMessage());
        }
    }

    public static void validateUserInput(String input) throws UserNotFoundException {
        if ("Invalid input".equals(input)) {
            throw new UserNotFoundException("The provided input is invalid.");
        }
    }
}

```
---

## Exception Handling

**예외 복구**
- `try-catch`
- 예외가 발생했을 때 프로그램이 정상적인 상태로 돌아올 수 있도록 예외를 처리하는 방법
- 예외 상황을 해결하고 프로그램이 계속 실행될 수 있도록 코드를 작성하는 것이 포함된다
- 예외 복구는 대개 재시도 로직을 구현하거나, 대체 데이터를 사용하여 프로그램이 계속 실행될 수 있는 상태를 만드는 데 사용된다

---

**예외 처리 회피**
- `throws`
-  현재 발생한 예외를 처리하지 않고, 호출한 메소드나 상위 계층에 예외 처리를 위임하는 방법
-  예외를 처리하는 데 필요한 지식이나 자원이 현재 메소드에 없을 때 사용할 수 있는 방법
-  남용하면 예외 처리가 전체 시스템에서 적절히 이루어지지 않을 수 있으므로 주의가 필요하다

---

**예외 전환**
- `Chained Exception`
- 발생한 예외를 새로운 예외로 변경하여 처리하는 방법으로 크게 두 가지 상황에서 사용된다
    1. 발생한 예외를 더 구체적인 예외로 변경하여, 호출자에게 더 유용한 정보를 제공하고자 할 때 사용
    2. 발생한 예외가 시스템 계층간의 의존성을 높이게 되는 경우, 의존성을 낮추기 위해 새로운 예외로 전환한다. 이를 통해 하위 계층에서 발생한 예외가 상위 계층에 영향을 주지 않도록 할 수 있다.
- 예외 전환을 사용할 때는 원래 발생한 예외를 새로운 예외의 원인(Cause)로 설정하여, 추후 디버깅이나 로그 분석 시에 원래 예외 정보를 확인할 수 있도록 해야 한다.

---

## 과제 1

목표 : 제네릭을 배울때 만들었던 `MyArrayList`의 일부 메서드에, 예외처리조건을 추가해본다

- 아래의 메서드 호출 시 `index`가 처리할 수 없는 데이터가 들어온다면 예외를 발생시킨다.
    - `public void add(int index, T data)`
    - `public T remove(int index)`
    - `public T get(int index)`

---

## 과제 2

목표 : 자바로 LinkedList를 구현해본다.

**요구사항**
- 제네릭 클래스를 사용하여 다양한 데이터 타입을 저장할 수 있는 링크드 리스트(`MyLinkedList`)를 클래스를 구현한다.
- `MyLinkedList` 클래스는 다음의 메소드를 포함해야 한다
    - `public void add(T data)`: 링크드 리스트의 끝에 새로운 요소를 추가한다.
    - `public void add(int index, T data)`: 지정된 인덱스에 새로운 요소를 삽입한다.
    - `public T remove(int index)`: 지정된 인덱스의 요소를 삭제하고, 삭제된 요소의 데이터를 반환한다.
    - `public T get(int index)`: 지정된 인덱스의 요소 데이터를 반환한다.
    - `public int size()`: 링크드 리스트의 크기(요소 수)를 반환한다.
    - `public boolean isEmpty()`: 링크드 리스트가 비어 있는지 확인한다.
- 작성한 `MyLinkedList` 클래스를 사용하여 다양한 데이터 타입의 요소를 추가, 삭제, 조회할 수 있는 예제도 같이 작성한다.

---

###### tags: `과외(하희영)`

