Notice
Recent Posts
Recent Comments
Link
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
관리 메뉴

개발자

[JPA] exception handling(예외 처리)/error 분류 본문

개발자/workflow 리팩토링 프로젝트(SpringBoot,JPA,MySQL)

[JPA] exception handling(예외 처리)/error 분류

GoGo개발 2023. 3. 21. 10:19
예외 처리란?

 

예외처리란 프로그램 실행 흐름상 오류가 발생했을 때 그 오류를 대처하는 방법

 

프로그램 오류

프로그램 실행 중 어떤 원인에 의해 프로그램이 해당 상황에 대처를 하지 못할 경우 비정상적으로 종료되거나 에러 팝업창이 뜨는 경우가 있다. 이런 상황을 프로그램 오류라 한다.

 

발생시점에 따른 에러 분류

 

Compile Error: 컴파일 시점에서 발생하는 에러소스코드를 컴파일러가 컴파일하는 시점에서 소스의 오타잘못된 구문, 자료형 체크등 검사를 수행하는데 여기서 발생하는 에러를 컴파일 에러라 하며 컴파일러가 에러를 발생시키고 object 파일을 만들지 않는다.

이 시점에서 발생하는 문제들을 수정 후 컴파일을 성공적으로 마칠경우 클래스 파일(*.class) 파일이 생성된다.

 

 

 

Runtime Error: 프로그램 실행 시점에서 발생하는 에러로 컴파일러는 컴파일 시점에서 문법 오류나 오타같은 컴파일시점에서 예측가능한 오류는 잡아줄 수 있지만, 실행 중 발생할 수 있는 잠재적인 에러까지 잡을 순 없다.

그래서 컴파일은 문제없이 완료되어 프로그램 실행이되고 실행도중 의도치않은 동작에 대처하지못해 에러가 발생할 수 있다. 런타임에, 프로그램은 에러 때문에 더 동작하지 않고 실행을 멈춘다

ex) null인 인스턴스의 메서드를 호출하는 경우

 

void stringToInt(String str) {
    int i = Integer.parseInt(str);
    System.out.println("str parsing result = " + i);
}

 

문자가 숫자 형태가 아닌 문자 타입인 경우 parseInt() 메서드에서 NumberFormatException이 발생할 것이다.

 
>>> 런타임 시점에서 발생하는 오류는 에러(error)예외(exception)으로 나뉜다.
 
에러(Error): 회복될 수 없고 프로그램은 멈춰야 한다. 메모리 부족(OutOfMemoryError)이나 스택오버플로우(StackOverflowError)와 같이 일단 발생하면 복구할 수 없는 심각한 오류
 
예외(exception): 에러는 회복될 수 있다. 인자값 Null 에러NPE(NullPointException)같은 발생하더라도 수습이 가능한 덜 심각한 오류.
ex) arithmetic exception, class cast exception, null pointer exception...
프로그래머는 예외(exception)가 발생했을 때 이 예외들을 관리할 수 있는 핸들러(handler)를 구현할 수 있다.
 
 
 

Logical Error:  소스 코드 컴파일도 정상적으로 되고 런타임상 에러가 발생하는 것도아닌 개발자의 의도와는 다르게동작하는 에러를 뜻한다. 버튼을 클릭하면 팝업이 뜨게 만들었으나 팝업이아닌 새로운 페이지가 뜨거나 아무동작을 안하거나 하는 것처럼 시스템상 프로그램이 멈추거나 하지는 않지만, 의도와는 다르게 동작하는 것을 말한다.

즉, 프로그램은 에러 없이 동작하지만, 결과는 프로그래머가 예상하지 못한 것이다.(결론은 개발자 탓ㅠㅠ)

 

Errors and Exceptions

자바는 에러와 예외를 클래스로 정의한다. 이들은 Throwable이라는 클래스의 subclass들이다. Throwable은 Object 클래스의 subclass이다. 예외 클래스는 두 카테고리로 나누어진다.
  • RuntimeException과 다른 예외 클래스로 나뉜다.
  • RuntimeException: 주로 프로그래머의 실수에 의해 발생한다.(0으로 나누기, class cast 등...)
  • Others: 환경 그리고 사용자 에러에 의해 주로 발생한다.(FileNotFoundException, ClassNotFoundException...)

예외 클래스 계층 구도

 

 

 
  • 자바 최상위 객체인 Object를 필두로 에러객체와 예외 객체가 있다. 그리고 에러 최상위 객체인 Throwble을 상속받는 Error와 Exception이 있다.
  • Exception 하위 예외 클래스 중 RuntimeException과 그 하위 예외를 선택적 예외로 개발자가 상황에 맞춰 대응해줘야하는 예외이고, 그외 나머지 예외 클래스와 그 하위 객체들을 필수(checked) 예외라하여 반드시 체크해줘야하는 예외라 한다.
  • RuntimeException 클래스들은 주로 프로그래머의 실수에 의해 발생될 수 있는 예외들이다. (ex: 배열의 범위를 벗어나거나, 값이 null인 참조 변수의 멤버를 호출하려 하는 경우)
  • Exception 클래스들은 주로 외부에 영향으로 발생할 수 있는 것들로, 대표적으로 I/O 입출력에 의해 발생하는 경우가 많다.(ex: 클래스의 이름을 잘못 적거나, 데이터 형식이 잘못되었거나, 사용자가 존재하지않는 파일명을 입력한 경우)
 
Exception Handling: try-catch
 
 
소스코드를 견고하게 작성한다고해도 예외가 줄어들수는 있지만, 프로그래머가 아닌 사용자가 원인으로 발생하는 예외는 개발자가 미리 대처를 해줄 수 있다. 예외 처리(exception handling)란, 프로그래머가 예기치못한 예외의 발생에 미리 대처하는 코드를 작성하는 것으로, 실행중인 프로그램의 비정상적인 종료를 막고, 상태를 정상상태로 유지하는 것이 목적이다.

만약 제대로 예외를 처리하지 못한다면 프로그램은 비정상적으로 종료되며, 종료시점에 처리되지 못한 예외에 대해서 JVM의 예외처리기(UncaughtExceptionHandler)가 받아서 예외의원인을 화면에 출력해준다.

 

위에서 예외는 관리할 수 있다고 언급했다. 예외를 관리함으로써, 프로그램은 무사히 실행을 계속할 수 있게 된다. 자바에서는 어떻게 예외를 관리할 수 있을까?
 
래서 이렇게 예외가 발생했을 경우 이런 예외를 처리하는방법으로는 우선 try-catch문이 존재한다.
 
 
예제 1 
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {"1", "2", "hello"})
void tryCatch(String str) {
    try {
        //예외가 발생할 가능성이 있는 코드 작성
        System.out.println("Integer.parseInt(str) = " + Integer.parseInt(str));

    } catch (NumberFormatException e) {
        //NumberFormatException 이 발생했을 경우 catch 문에서 예외를 처리하기 위한 코드
        System.out.println("NumberFormatException = " + e.getMessage());
    } catch (Exception e) {
        //Exception이 발생했을 경우 예외를 처리하기 위한 코드
        System.out.println("e.getMessage() = " + e.getMessage());
    }
}
  • null, "", "1","2","hello" 가 각각 매개변수로 들어가서 Int형으로 형변환하는 코드이다.
  • ry-catch문은 다른 분기문과 다르게 실행할 코드가 한줄이여도 블록({})을 생략할 수 없다.
  • try-catch문은 다른 문법처럼 중복으로 블록 내부에 또 try-catch문을 작성할 수 있다.
  • catch문을 통해 예외를 잡아 처리해주지 못한다면, 예외는 처리되지 않는다.
 
예제 2
 
public class example {
	public static void main(String[] args){
		int number = 100;
		int result = 0;
		for(int i=0; i<10; i++) {
		result = number / (int)(Math.random() * 10);
		System.out.println(result);
		}
	}
}
  • int(Math.random() * 10)은 0이 될 수도 있다.
  • 만약 예외가 발생하면, 프로그램은 파괴되고 예외 메시지가 출력된다.
위 코드를 ArithmeticException을 다룰 수 있도록 바꿔 보자. 만약 나누는 수가 0이 되면 (그래서 예외가 발생하면), catch 블록이 실행된다. 프로그램은 실행을 계속할 수 있게 되는 것이다.
 
 
public class example {
	public static void main(String[] args) {
		int number = 100;
		int result = 0;
		for (int i = 0; i < 10; i++) {
			try {
				result = number / (int) (Math.random() * 10);
				System.out.println(result);
			} catch (ArithmeticException e) {
				System.out.println("0");
			}
		}
	}
}
 
  • 예외가 발생하면 JVM이 예외 객체를 생성해서 catch 블록에 인자로 넘긴다.

예제 3

@Test
void arithmeticException() {
    String[] strings = new String[]{"hello", "crong", "coco"};

    for (int i = 0; i < 4; i++) {
        System.out.println(strings[i]);
    }
}

 

길이가 3인 배열의 index 3을 찾으려 하기에 에러가 발생했다는데, 예외 클래스명과 연관지어서 생각하면, 배열의 길이를 벗어난 인덱스의 값을 가져오려 했기에 생긴 문제라는 것이다.

 

@Test
void arithmeticException() {
    //before
    String[] strings = new String[]{"hello", "crong", "coco"};

    try {
        for (int i = 0; i < 4; i++) {
            System.out.println(strings[i]);
        }
    } catch (ArrayIndexOutOfBoundsException e) {
        System.out.println("Empty");
    }
}

 

위 코드를 실행하면 에러가 발생하지 않고 범위를 벗어난 주소의 값을 조회하려고 할 경우 catch문에서 해당 예외를 잡아서 Empty 라는 값을 출력하도록 했다.

 
 
 
 
try-catch문을 통해 예측가능한 예외를 처리해주는 것도 중요하지만 이렇게 예외 발생시 try 블럭내의 나머지 코드들이 수행되지 않는다는점을 유의해서 코드를 작성해야 한다.
 
 
catch 블럭은 위와같이 예외객체를 매개변수로 받는 괄호 영역과 해당 예외가 매개변수로 들어왔을 경우 수행해야할 로직이 작성된 블럭이 존재한다.
 
그렇기에 catch문을 작성하려면 예외타입의 참조변수를 선언해야 한다.
 
모든 예외는 Exception 클래스의 자손이기에 catch 블럭 최상단에 Exception을 선언하면 어떤 에러든 해당 catch블럭에서 처리된다.
 
 
Generating an Exception : 예외 발생시키기
 
 
개발자가 의도적으로 예외를 발생시키는 것 역시 가능하다.
throw라는 키워드를 사용하면 되는데, 주로 사용자가 구현하는 비즈니스 로직에서 컴파일의 문법 오류는 없지만 로직 자체가 개발자가 의도한대로 진행되는지에 대한 의 검증(Validation)로직을 통과하지 못했을 경우에 고의로 예외를 발생시켜야 할 때 사용한다.
 
 

사용법

  1. 발생시키고자 하는 예외를 생성한다. Exception e = new Exception("고의 발생 예외");
  2. throw 키워드를 이용해 예외를 발생시킨다. throw e;
  3. 아니면 바로 new 키워드로 예외 객체를 생성해도 된다. throw new Exception("고의 발생 예외");
public static void main(String[] args) {
    try{
        throw new Exception("고의 발생 예외");
    }catch(Exception error){
        error.printStackTrace();
        System.out.println("Invalid str: "+error.getMessage());
    }

    System.out.println("end");
}

 

 
이때 생성하는 예외의 생성자 파라미터로 메세지를 입력하면 해당 메세지를 getMessage에 의해서 얻을 수 있다.
 
 
메서드에 예외 선언하기
 
 
void method() throws Exception1, Exception2, ... ExceptionN {
	//...
}
 
  • 메서드레벨에서 예외를 선언할 수 있다.
  • 메서드레벨에서 선언한 예외와 그 자손타입까지 발생할 수 있다는 걸 의미한다.                                                                 throws RuntimeException 을 선언할 경우 RuntimeException을 상속하는 하위 예외객체들 모두가 발생 할 가능성이 있다.
  • 해당 메서드에서 선언한 예외를 보고 블록 내부 로직을 수행하며 해당 예외가 발생할 수 있다고 추측할 수 있고, 이 부분에 대해 유의할 수 있다
  • 예외 처리를 해당 메서드를 호출하는 로직에게 위임할 수 있다.
  • 예외 던지기
 
finally
 
try {
	//예외발생 가능성이 있는 로직
} catch(Exception err) {
	//예외 처리 로직
} finally {
	//공통 로직
}
 
  • 예외 발생 여부와 상관없이 무조건 수행되어야 할 로직이 있을 경우 사용하는 블럭
  • 예외가 발생하지 않는다면 try 블럭 이후 finally 블럭이 수행된다.
  • 예외가 발생한다면 try블럭 내에서 예외가 발생하기전까지 수행되다가 예외처리를 위한 catch 블럭이 수행된 후 finally 블럭이 수행된다.
    •    ⇒ 사용 예) 커넥션 풀 종료, 임시 파일 삭제, 소켓 종료 등
 
 
사용자 정의 예외 만들기 (내가 사용해볼 것)
 
대부분의 예외는 Exception과 RuntimeException 으로부터 파생되는 하위 예외 객체들이다.

그렇다보니 기본적으로 제공되는 예외 외에 개발자가 구현하는 의미있는 비즈니스 로직상 발생하는 몇몇 예외는 개발자가 직접 예외 객체를 정의해서 발생시킬수도 있다.
 
 
예를들어, 숫자가 10을 넘을 경우 발생하는 예외를 만들어야 한다고 해보자.
 
class TenOverException extends RuntimeException {
	//...
}
 
 
이렇게 RuntimeException을 상속하는 사용자 정의 예외 객체를 만들면 프로젝트 런타임시 비즈니스로직에서 예외가 발생할 경우 해당 예외를 발생시킬 수 있다.
 
 
 
 
출처

https://velog.io/@ssseungzz7/Java-Exception-handling

 

[Java] Exception handling

2019년 10월 15일, 10월 17일 자바 수업 내용을 정리합니다. Exception handling에 대해 알아봅니다.

velog.io

 

https://catsbi.oopy.io/92cfa202-b357-4d47-8de2-b9b3968dfb2e

 

예외처리(exception handling)

목차

catsbi.oopy.io