예외 처리 & try-with-resources¶
PHASE 2 | Java 핵심 — 객체지향
키워드:예외,Exception,RuntimeException,checked exception,unchecked exception,try-catch-finally,throw,throws,커스텀 예외,try-with-resources,AutoCloseable
예외(Exception)란?¶
프로그램 실행 중에 발생하는 오류. 처리하지 않으면 프로그램이 죽어버린다.
int[] arr = new int[3];
arr[5] = 10; // ArrayIndexOutOfBoundsException 발생 → 프로그램 종료
예외 계층 구조¶
Throwable
├── Error (시스템 수준 오류 - 개발자가 처리하지 않음)
│ └── OutOfMemoryError, StackOverflowError 등
└── Exception (개발자가 처리해야 하는 예외)
├── IOException, SQLException 등 → checked exception
└── RuntimeException → unchecked exception
└── NullPointerException, IllegalArgumentException 등
checked exception vs unchecked exception¶
| checked exception | unchecked exception | |
|---|---|---|
| 상속 | Exception 직접 상속 |
RuntimeException 상속 |
| 처리 강제 | 컴파일러가 강제 | 강제 안 함 |
| 대표 예시 | IOException, SQLException |
NullPointerException, IllegalArgumentException |
| 실무 사용 | 외부 자원 (파일, DB, 네트워크) | 비즈니스 로직 예외, 개발자 실수 |
checked exception은 컴파일러가 처리를 강제한다.
try-catch로 잡거나 throws로 호출자에게 넘기지 않으면 컴파일 에러가 난다.@Transactional 주의사항과 연결:
Spring의 @Transactional은 기본적으로 unchecked exception(RuntimeException)이 발생할 때만 롤백한다.
checked exception은 rollbackFor 옵션을 명시해야 롤백된다.
try-catch-finally 구조¶
try {
// 예외가 발생할 수 있는 코드
FileReader reader = new FileReader("file.txt");
} catch (FileNotFoundException e) {
// 예외 발생 시 처리
System.out.println("파일을 찾을 수 없습니다: " + e.getMessage());
} finally {
// 예외 발생 여부와 관계없이 항상 실행
// 자원 정리 코드 (파일 닫기, DB 연결 종료 등)
if (reader != null) reader.close();
}
try→ 예외가 날 수 있는 코드
catch→ 예외 발생 시 처리
finally→ 무조건 실행 (주로 자원 정리용)
여러 예외 처리¶
try {
// 코드
} catch (FileNotFoundException e) {
// 파일 없을 때
} catch (IOException e) {
// 그 외 IO 오류
} catch (Exception e) {
// 나머지 모든 예외
}
throw vs throws¶
throw — 예외를 직접 던진다¶
public void validateAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("나이는 0 이상이어야 합니다");
}
}
throws — 예외 처리를 호출자에게 넘긴다¶
public void readFile(String path) throws IOException {
// IOException을 여기서 처리하지 않고 호출자에게 넘김
FileReader reader = new FileReader(path);
}
throw= 예외를 실제로 발생시킨다
throws= 이 메서드는 이 예외가 날 수 있으니 호출자가 처리하라는 선언
커스텀 예외¶
실무에서는 비즈니스 로직에 맞는 예외를 직접 만들어서 쓴다.
// 커스텀 예외 정의
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
// 사용
public User findUser(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("사용자를 찾을 수 없습니다. id=" + id));
}
실무에서는 RuntimeException을 상속하는 커스텀 예외를 주로 만든다.
나중에 @ControllerAdvice에서 이 예외를 잡아서 일관된 에러 응답을 만든다. (PHASE 13)
try-with-resources¶
왜 필요한가?¶
파일, DB 연결 같은 자원은 사용 후 반드시 닫아야 한다. 안 닫으면 메모리 누수가 발생한다.
기존 finally 방식은 코드가 복잡하고 close()를 까먹기 쉽다:
// 기존 방식 - 복잡하고 실수하기 쉬움
FileReader reader = null;
try {
reader = new FileReader("file.txt");
// 파일 읽기
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close(); // 직접 닫아야 함, 까먹으면 누수
} catch (Exception e) {
e.printStackTrace();
}
}
}
try-with-resources로 개선¶
// try-with-resources - 자동으로 닫아줌
try (FileReader reader = new FileReader("file.txt")) {
// 파일 읽기
} catch (Exception e) {
e.printStackTrace();
}
// try 블록이 끝나면 reader.close() 자동 호출 (정상 종료든 예외든)
try (자원 선언)형태로 쓰면,
try 블록이 끝날 때 정상 종료든 예외 발생이든 자동으로close()가 호출된다.
AutoCloseable이란?¶
try-with-resources를 쓰려면 자원 클래스가 AutoCloseable 인터페이스를 구현해야 한다.
public interface AutoCloseable {
void close() throws Exception;
}
FileReader, Connection, InputStream 등 Java 표준 자원 클래스들은 이미 구현되어 있다.
커스텀 자원 클래스¶
public class DatabaseConnection implements AutoCloseable {
public DatabaseConnection() {
System.out.println("DB 연결");
}
public void query() {
System.out.println("쿼리 실행");
}
@Override
public void close() {
System.out.println("DB 연결 종료"); // 자동 호출됨
}
}
// 사용
try (DatabaseConnection conn = new DatabaseConnection()) {
conn.query();
}
// 출력:
// DB 연결
// 쿼리 실행
// DB 연결 종료 ← close() 자동 호출
여러 자원 동시에 사용¶
try (FileReader reader = new FileReader("input.txt");
FileWriter writer = new FileWriter("output.txt")) {
// 읽고 쓰기
}
// 선언 역순으로 close() 자동 호출
// writer.close() → reader.close() 순서
면접 포인트¶
Q. checked exception과 unchecked exception의 차이는?
checked exception은 Exception을 직접 상속하며 컴파일러가 처리를 강제한다.
unchecked exception은 RuntimeException을 상속하며 처리를 강제하지 않는다.
실무에서는 비즈니스 로직 예외는 unchecked exception으로 만드는 게 일반적이다.
Q. throw와 throws의 차이는?
throw는 예외를 실제로 발생시키는 것이고,
throws는 이 메서드에서 예외가 발생할 수 있으니 호출자가 처리하라는 선언이다.
Q. try-with-resources를 왜 사용하나요?
자원을 사용 후 반드시 닫아야 하는데, finally에서 직접 close()를 호출하면
코드가 복잡하고 실수로 누락될 수 있다.
try-with-resources는 AutoCloseable을 구현한 자원을 자동으로 닫아줘서 누수를 방지한다.
Q. @Transactional은 어떤 예외에서 롤백되나요?
기본적으로 unchecked exception(RuntimeException)에서만 롤백된다.
checked exception은 rollbackFor 옵션을 명시해야 롤백된다.