오류 보충자료
1. 예외 처리가 필요한 이유
정상적인 흐름 유지:
애플리케이션에서 예기치 못한 상황(에러)이 발생하더라도 애플리케이션이 중단되지 않고 실행될 수 있도록 제어.
예시 상황: 사용자가 잘못된 데이터를 입력하거나 네트워크가 불안정할 경우.
리소스 낭비 방지:
에러 발생 시 조치를 취하지 않으면 애플리케이션이 메모리 누수나 과부하로 인해 서버가 중단될 가능성이 있음.
코드 가독성 향상:
정상 흐름과 예외 흐름을 분리하여 코드의 가독성 및 유지보수성을 높임.
디버깅 및 로그:
발생한 에러를 기록(logging)하여 디버깅에 도움을 줌.
로그를 통해 문제의 원인을 추적 가능.
2. 예외 처리 예제
(1) 기본적인 예외 처리: 정상 흐름 유지
코드 예시
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 발생 가능
}
}
실행 결과
Error: Division by zero is not allowed.
Program continues...
(2) 에러 발생 시 작업 누적의 문제: 메모리 누수 예제
설명: 예외를 처리하지 않고 계속 실행하면 메모리 누수가 발생할 수 있음.
코드 예시
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.");
}
}
실행 결과
Exception caught: Simulated exception
Exception caught: Simulated exception
...
Completed without fixing the memory issue.
문제점: 리스트에 데이터가 계속 쌓이지만, 에러 발생 후 제거되지 않아 메모리 누수가 발생할 가능성이 있음.
(3) 서버 과부하 및 메모리 누적 문제
상황: 서버가 며칠마다 죽는 문제 발생 → 로그 확인 시 메모리 누적 또는 디스크 용량 부족.
코드 예시
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) 정상 흐름과 예외 흐름의 혼합 문제
문제: 정상 흐름과 예외 흐름이 섞이면 코드 이해가 어려워짐.
반환값으로 에러를 처리하는 코드
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);
}
}
예외 처리를 활용한 코드
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. 예외 처리에서 로그 활용
로그 기록의 중요성:
애플리케이션 실행 중 발생한 에러를 기록하여 디버깅에 도움을 줌.
로그는 운영 환경에서 문제를 진단하는 주요 도구.
코드 예시: 로그 기록
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
)
SEVERE: Error: Division by zero. / by zero
4. 자바의 예외 계층 구조
(1) 예외 계층
자바의 예외는 모두 Throwable
클래스를 상속하며, 두 가지 주요 분류로 나뉩니다:
Error
:정의: 복구 불가능한 시스템 수준의 예외.
예시:
OutOfMemoryError
,StackOverflowError
.
Exception
:정의: 프로그램에서 예외적인 상황을 처리하기 위한 클래스.
세부 분류:
체크 예외: 개발자가 명시적으로 처리해야 함.
예시:
IOException
,SQLException
.
언체크 예외: 명시적 처리 불필요, 런타임에 발생.
예시:
NullPointerException
,IllegalArgumentException
.
(2) 예외 계층 다이어그램
Throwable
├── Error (복구 불가능한 예외)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│
└── Exception (프로그램 예외)
├── Checked Exception (체크 예외)
│ ├── IOException
│ ├── SQLException
│
└── RuntimeException (언체크 예외)
├── NullPointerException
├── IllegalArgumentException
├── ArithmeticException
(3) 체크 예외와 언체크 예외의 차이점
특징
체크 예외
언체크 예외
정의
컴파일 시점에 명시적 예외 처리 요구
런타임 시점에 발생, 명시적 처리 불필요
대표 클래스
IOException
, SQLException
NullPointerException
, ArithmeticException
처리 필요 여부
반드시 try-catch
또는 throws
선언 필요
필요 없음, 자동으로 호출자에게 전달됨
예시 상황
파일 읽기/쓰기 실패, 네트워크 연결 실패
배열 인덱스 초과, null 객체 접근
5. 예외 처리 기본 규칙
(1) 기본 규칙
예외 발생 시 처리하거나 던져야 함:
발생한 예외는
try-catch
로 처리하거나,throws
로 호출자에게 전달.
상위 타입 예외 처리:
부모 타입 예외를 잡으면, 해당 타입의 모든 자식 예외도 처리 가능.
6. 예외 처리 예제 코드
(1) 체크 예외: IOException
처리
IOException
처리코드 예시
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());
}
}
}
실행 결과 (파일이 없을 경우)
Error: Unable to read file. test.txt (No such file or directory)
(2) 언체크 예외: NullPointerException
처리
NullPointerException
처리코드 예시
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.");
}
}
}
실행 결과
Error: Null value encountered.
(3) throws
를 사용한 예외 전달
throws
를 사용한 예외 전달코드 예시
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코드 복사Handled in main: Simulated file read error.
(4) 상위 예외로 처리
코드 예시
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!");
}
}
}
실행 결과
Caught: NullPointerException
(5) Error
예외 예제 (복구 불가능)
Error
예외 예제 (복구 불가능)코드 예시
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!");
}
}
실행 결과
Caught: Simulated out of memory!
7. finally
키워드와 반드시 실행되는 블록
finally
키워드와 반드시 실행되는 블록(1) finally
키워드란?
finally
키워드란?정의:
try-catch
블록이 종료될 때 항상 실행되는 코드 블록.용도:
리소스 정리, 연결 해제 등 반드시 실행해야 하는 작업 처리.
예외 발생 여부와 관계없이 실행됨.
주의:
finally
는try
블록에 리턴값이 있어도 항상 실행됨.
(2) finally
사용 예제
finally
사용 예제코드 예시
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.");
}
}
}
실행 결과
Try block executed.
Catch block executed: Simulated exception
Finally block executed. Always runs.
(3) finally
블록 활용
finally
블록 활용리소스 해제:
파일, 네트워크 연결, 데이터베이스 등 외부 자원 해제.
예시: 파일 닫기
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) 공통 예외 처리
전략:
공통 예외 처리 로직을 작성하여 대부분의 예외를 한 곳에서 처리.
시스템에서 복구 불가능한 에러만 체크 예외로 처리.
코드 예시: 공통 예외 처리
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);
}
}
}
실행 결과
Exception handled globally: null
9. Try-with-resources (자바 7 이상)
(1) try-with-resources
란?
try-with-resources
란?정의:
try
블록에서 사용한 리소스를 자동으로 반납하는 구조.장점:
finally
블록 없이도 리소스 반납 가능.try
가 종료되면close()
메서드가 자동으로 호출.
조건:
사용된 객체가
AutoCloseable
인터페이스를 구현해야 함.close()
메서드를 오버라이드하여 반납 작업 정의.
(2) try-with-resources
예제
try-with-resources
예제코드 예시: 파일 읽기
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());
}
}
}
실행 결과 (파일이 존재할 경우)
File content...
(3) AutoCloseable
과 close()
오버라이드
AutoCloseable
과 close()
오버라이드코드 예시: 커스텀 리소스 관리
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());
}
}
}
실행 결과
Using resource.
Resource closed automatically.
Last updated