Exception과 Error의 차이

Exception Error 모두 Java에서 예외와 오류의 최상위 클래스인 Throwable 클래스를 상속하며, 프로그램 실행 발생하는 예외적인 상황을 나타냅니다. 하지만 Exception Error 의미와 목적에서 차이가 있습니다.

 

Error는 애플리케이션 코드로 처리할 수 없는 시스템적 문제를 나타내며, 대부분 JVM 레벨에서 발생합니다. Error는 발생할 경우 프로그램이 즉시 종료될 가능성이 높고, 일반적으로 try-catch 구문으로 처리하지 않습니다. Error는 메모리 부족, 스택 오버플로우와 같은 시스템 자원 문제로 인해 발생하며, 개발자가 해결할 수 없는 경우가 대부분입니다.

 

Error의 대표 예시

  1. OutOfMemoryError
    메모리가 부족할 발생하는 오류로, 주로 객체를 너무 많이 생성하거나 메모리 리소스를 과도하게 사용했을 발생합니다.
  2. StackOverflowError
    재귀 호출이 무한히 반복될 발생하는 오류입니다.
  3. VirtualMachineError
    가상 머신의 심각한 문제가 발생했을 발생합니다.

Exception 프로그램 실행 중에 발생할 있는 예상 가능한 예외 상황 나타냅니다. 주로 사용자 입력 오류, 파일 입출력 오류 프로그램이 처리할 있는 문제들로, Java에서는 try-catch 구문을 통해 Exception 처리할 있습니다. Exception 처리함으로써 프로그램이 강제 종료되지 않고 정상 흐름을 유지할 있게 합니다.

구분 Exception Error
의미 예상 가능한 예외 상황 시스템 수준의 심각한 문제
처리 방법 try-catch로 처리 가능 일반적으로 처리하지 않음
대표 예시 IOException, SQLException OutOfMemoryError, StackOverflowError
복구 가능성 복구 가능성이 있음 복구 불가능, 프로그램 종료 가능성 큼

 

개발을 하다 보면 다양한 예외 상황을 만나게 됩니다. Java에서는 이러한 예외를 체크 예외 언체크 예외 구분하며, 예외를 적절히 처리하는 방법으로 예외 복구, 예외 회피, 예외 전환 있습니다. 이어서 예외의 종류와 효과적인 처리 방법을 정리해 보겠습니다.

Exception

Exception 클래스는 체크 예외와 언체크 예외로 구분되는데 체크 예외는 Exception 클래스의 서브클래스이면서 RuntimeException 클래스를 상속하지 않은 것들이고, 언체크 예외는 RuntimeException 클래스를 상속한 클래스들을 말합니다.

 

체크 예외 (Checked Exception)

일반적으로 예외라고 하면 RuntimeException 클래스를 상속 받지 않은 체크 예외를 의미하며 체크 예외가 발생할 수 있는 메소드를 사용할 경우 반드시 예외를 처리하는 코드를 함께 작성해 주어야 합니다. 만약 예외 처리를 해주지 않으면 컴파일 에러가 발생합니다.

 

체크 예외의 예시

  1. IOException: 파일 입출력 시 파일이 없거나 접근할 수 없을 때 발생
  2. SQLException: SQL 문법 에러, DB 접근 실패, 서버나 네트워크 문제로 발생

체크 예외의 장단점

  • 장점: 컴파일 단계에서 예외를 강제하기 때문에, 놓치기 쉬운 예외를 미리 잡을 수 있는 안전장치 역할을 합니다.
  • 단점: 모든 체크 예외를 잡거나 던져야 하기 때문에 번거로워질 있습니다.

언체크 예외 (Unchecked Exception)

언체크 예외는 런타임 예외라고도 불리며 RuntimeException 클래스를 상속한 예외가 여기에 속합니다. 명시적인 예외처리를 강제하지 않기 때문에 catch 문으로 잡거나 throws로 선언하지 않아도 되며 선언하지 않을 경우 자동으로 예외를 던집니다. 주로 프로그램의 로직 오류나 예측 불가능한 상황에서 발생합니다.

 

언체크 예외의 예시

  1. NullPointerException: 할당되지 않은 객체를 사용하려고 할 때 발생
  2. IllegalArgumentException: 허용되지 않는 값으로 메서드를 호출할 때 발생

언체크 예외의 장단점

  • 장점: 신경 쓰고 싶지 않은 예외를 무시할 수 있어 코드 작성이 간편합니다.
  • 단점: 컴파일 단계에서 오류를 잡아주지 않아 예외가 발생할 가능성을 쉽게 놓칠 있습니다.

예외처리 방법

체크 예외와 언체크 예외를 구분해도 실제로 예외가 발생했을 때는 이를 어떻게 처리할지 고민해야 합니다. Java에서는 크게 세 가지 방식(예외 복구, 예외처리 회피, 예외 전환)으로 예외를 처리할 수 있습니다.

예외 복구

첫 번째로 예외상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 방법이 있습니다. 예외가 발생하더라도 정상 상태로 복구 있다면 방법을 사용합니다. 예외 상황에 따라 특정 횟수까지 재시도를 하거나, 대체 로직을 통해 정상 상태로 돌려놓을 있습니다.

int maxretry = MAX_RETRY
int cnt = 0
while (cnt < MAX_RETRY) {
    cnt++
    try {
    	...    		// 예외가 발생할 가능성이 있는 시도
        return;		// 작업 성공
    } catch (SomeException e) {
    	// 로그 출력 후 정해진 시간만큼 대기
    } finally {
    	// 리소스 반납. 정리 작업
    }
}
throw new RetryFailedException(); // 최대 재시도 횟수를 넘기면 직접 예외 발생

예외처리 회피

두 번째로 예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 전달하는 방법이 있습니다. 예외를 회피하는 것은 예외를 복구하는 것처럼 의도가 분명해야 합니다. 다른 오브젝트에게 예외처리 책임을 지게 하거나, 자신을 사용하는 쪽에서 예외를 다루는 게 최선의 방법이라는 확신이 있을때만 사용해야 합니다.

// 예외처리 회피 1
public void add() throws SQLException {
    ...
}
// 예외처리 회피 2
public void add() throws SQLException {
    try {
        ...
    } catch (SQLException e) {
        throw e;
    }
}

예외 전환

마지막으로 발생한 예외를 그대로 던지는 대신 의미를 부여한 새로운 예외로 전환하여 던지는 방법입니다.

 

예외 전환 사용 목적

1. 내부에서 발생한 예외를 그대로 던지는 것이 그 예외상황에 대한 적절한 의미를 부여해주지 못하는 경우, 의미를 분명하게 해줄 수 있는 예외로 바꿔주기 위해 사용합니다.

 

예를 들어 앞서 말했듯이 SQLException경우 문법 문제이거나 로직 문제 또는 서버 쪽에 문제가 생겼을 때도 발생할 수 있는데 이 모든 경우에 단순히 SQLException으로 throw를 하면 개발자 입장에서는 원인을 찾기가 쉽지 않습니다. 그렇기 때문에 같은 SQLException이라도 원인을 세분화해서 다른 Exception으로 전환 후 보내준다면 보다 쉽게 에러를 해결할 수 있게 됩니다.

 

2. 예외를 처리하기 쉽고 단순하게 만들기 위해서 사용합니다.

주로 예외처리를 강제하는 체크 예외를 언체크 예외인 런타임 예외로 바꾸는 경우에 사용한다.

// 회원가입시 id가 중복되어 에러가 발생한 경우
public void add(User user) throws DuplicateUserIdException, SQLException {
    try {
    	...
    } catch (SQLException e) {
    	// ErrorCode가 MySQL의 "Duplicate Entry(1062)"이면 예외 전환
        // ErrorCode는 데이터베이스 종류에 따라 다름
        if (e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY) {
            throw DuplicateUserIdException();
        } else {
            throw e; // 그 외의 경우는 SQlException 그대로 전달
        }
    }
}

 

'Java' 카테고리의 다른 글

[Java] Garbage Collection이란?  (0) 2024.11.11
[Java] String, StringBuilder, StringBuffer  (0) 2024.11.05
[Java] 스트림(Stream)이란  (0) 2024.11.04
[Java] 제네릭이란?  (0) 2024.11.03
[Java] final 필드, 메소드, 클래스  (0) 2024.10.30

제어의 역전(Inversion of Control)이란

  • 프로그램의 제어 흐름 구조가 뒤바뀌는 것
  • 일반적으로 프로그램의 흐름은 아래의 과정이 반복되는데 제어의 역전은 이런 제어 흐름의 개념을 거꾸로 뒤집는다. 즉 프로그램의 제어권이 개발자가 아닌 프레임워크에게 있는 것을 의미한다.
1. 프로그램이 시작되는 지점에서 다음에 사용할 오브젝트를 결정 
2. 결정한 오브젝트를 생성 
3. 만들어진 오브젝트에 있는 메소드를 호출 
4. 그 오브젝트 메소드 안에서 다음에 사용할 것을 결정하고 호출

의존성 주입(Dependency Injection)이란

  • 스프링 IoC 기능의 대표적인 동작원리로 제어의 역전(IoC)는 이 DI를 통해서 일어난다.
  • 객체를 직접 생성하지 않고 외부에서 생성하여 주입시켜주는 방식
  • 의존성 주입을 이용하면 주입받는 객체가 주입되는 객체의 정확한 클래스 타입을 알 필요가 없어지기 때문에 모듈간의 결합도가 낮아지고 응집도가 높아져 객체지향 설계 원칙(SOLID) 중 하나인 개방 폐쇄 원칙을 따르기 쉽게 도와준다.
개방 폐쇄 원칙
- 클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀있어야 한다.

응집도
- 모듈 내부의 기능적인 응집 정도
- 응집도가 높다는 건 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어 있다는 뜻

결합도
- 하나의 오브젝트가 변경이 일어날 때 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도
- 결합도가 낮다는 건 하나의 모듈에서 변경이 발생할 때 관계를 맺고 있는 다른 모듈에 끼지는 영향이 적다는 뜻

의존성 주입 방식

1. 생성자 주입 방식

스프링 빈 객체를 생성하기 전에 주입될 빈 객체를 찾아 주입한 후 생성하기 때문에 컴파일 단계에서 순환참조 오류를 찾을 수 있고 final 변수를 사용함으로써 스프링 빈 객체가 Null아님을 보장하고 예기치 못한 상황에 빈 객체가 수정될 위험을 방지할 수 있어 가장 권장하는 방식이다.

public class UserDao {

    private final ConnectionMaker connectionMaker;
    
    public UserDao(ConnectionMaker connectionMaker) {
    	this.conntectionMaker = connectionMaker
    }
    
}

생성자 주입 방식의 경우 아래와 같이 @RequiredArgsConstructor 어노테이션을 이용하여 쉽게 사용할 수 있다.

@RequiredArgsConstructor
public class UserDao {

    private final ConnectionMaker connectionMaker;
        
}

2. 필드 주입 방식

public class UserDao {

    @Autowired
    private ConnectionMaker connectionMaker;

}

3. 수정자 주입 방식

public class UserDao {

    private ConnectionMaker connectionMaker;
    
    public void setConnectionMaker(ConnectionMaker connectionMaker) {
    	this.connectionMaker = connectionMaker;
    }
    
}

용어 정리

빈(bean)

  • 스프링이 IoC방식으로 관리하는 오브젝트
  • 모든 오브젝트가 다 빈은 아니다. 스프링이 직접 생성과 제어를 담당하는 오브젝트만을 빈이라고 함

빈 팩토리(bean factory)

  • 스프링의 제어의 역전(IoC)를 담당하는 핵심 컨테이너
  • 빈을 등록하고, 생성하고, 조회하고 돌려주고, 그 외에 부가적인 빈을 관리하는 기능을 담당

애플리케이션 컨텍스트(application context)

  • 빈 팩토리를 확장한 IoC 컨테이너
  • 빈 팩토리라고 부를 때는 주로 빈의 생성과 제어의 관점에서 말하는 것이고 애플리케이션 컨텍스트라고 할 때는 스프링이 제공하는 애플리케이션 지원 기능을 모두 포함해서 부르는 것이다.

IoC 컨테이너

  • 제어의 역전 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 IoC 컨테이너라고도 한다. 즉 빈 팩토리과 애플리케이션 컨텍스트 그리고 IoC 컨테이너는 같은 개념이며 어떤 관점에서 보느냐에 따라 다르게 불린다.

+ Recent posts