# 프로그래밍의 오류 - 프로그램에서 오류가 발생하면 시스템 레벨에서 프로그램에 문제를 야기하여 원치 않는 버그를 일으키거나, 심각하면 실행 중인 프로그램을 강제로 종료시키도 한다. - 프로그래밍의 오류는 다양하지만, 크게 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: `과외(하희영)`