개발자로 후회없는 삶 살기
[문법] 오류를 핸들링하는 방법 본문
서론
※ 과거에 기록한 내용에서 중요한 부분만 발췌하여 모두가 이해하기 쉽게 다시 서술한다.
본론
- 최고의 오류 핸들링이란?
1) 문제가 발생해도 서비스가 다운되지 않게 하는 것
2) 발생한 문제를 로그로 남기고, 로그를 분석하여 원인을 파악하는 것
자바는 안정성을 추구하는 언어로, 오류를 핸들링할 수 있는 문법을 제공한다.
✅ 오류와 에러
오류가 에러를 포함하는 개념이다. 따라서 에러는 오류라고 볼 수 있다.
런타임 에러 : 프로그램이 실행되는 도중에 발생하는 예기치 못한 오류
컴파일 에러 : 컴파일 시점에 컴파일러에 의해 파악된 오류
논리적 에러 : 곱하기를 기대했지만, 더하기를 해서 기대한 결과와 다른 결과를 출력한 오류
개발자가 가장 중요하게 예외 처리를 해야 하는 것은 런타임 에러로, 런타임 에러가 발생하면 어플리케이션이 죽는다.
-> 에러와 예외 클래스
런타임 에러를 2가지로 분류했다. 둘은 같은 말이지만 용어를 다르게 표현했고, Throwable을 부모로 하고 Error와 Exception 클래스로 나뉜다.
Error(시스템 오류) : JVM에서 발생하며, 개발자가 처리할 수 없는 동적 메모리 부족, 스택 오버플로우 등
Exception(예외) : NPE, 존재하지 않는 파일에 접근 등 개발자가 제어할 수 있는 오류를 의미한다.
에러는 심각한 오류로 개발자가 코드로 해결할 수 없고, 예외는 미약한 오류로 코드로 해결할 수 있는 오류이다. 미약한 오류가 발생하면 프로그램이 죽지 않고 동작할 수 있게 하기 위해 예외를 만들었고, 이 오류 클래스들로 오류를 핸들링 할 수 있다.
에러는 JVM이 다루고 개발자는 Exception으로 오류를 제어할 수 있다.
=> 예외 클래스의 종류
체크드 예외 : Exception과 그 자손으로 반드시 잡거나 던져야 하는 예외
언체크드 예외 : RuntimeException과 그 자손으로 반드시 잡거나 던지지 않아도 되며, 그러면 자동으로 던져지는 예외
Exception 하위에는 엄청 나게 많은 클래스가 있고 이 클래스들을 사용하여 개발자가 예외를 핸들링 할 수 있다. 개발자는 모든 예외가 발생할 수 있는 상황을 Exception으로 프로그램이 다운되지 않게 핸들링해야 한다.
1. 체크드 예외
Exception과 그 자손 예외를 의미하며, User 실수 예외 처리를 정의했다. 잡거나 던지지 않으면 컴파일 에러가 발생한다.
2. 언체크드 예외
모든 예외 클래스가 Exception의 자손이지만 그 중에서도 RuntimeException과 그 자손을 언체크드 예외로 구분했고, 사용자가 아닌 개발자 실수로 발생한 예외 처리를 정의했다. 잡거나 던지지 않아도 컴파일 오류가 발생하지 않는데, 그럼 컴파일러가 자동으로 던져준다.
계속 던져지다 보면, 이를 처음 호출한 곳까지 던져지고 main() 함수에서 오류가 발생한다.
=> printstacktrace와 getMessage
예외가 발생하면 예외 객체가 생기며 예외와 관련된 정보를 저장하고 있고 이를 확인할 수 있는 메서드를 제공한다.
1. printstacktrace()
동일 코드에 printstacktrace를 했을 때는 예외와 관련된 정보가 나온다. 예외 발생 당시 호출 스택에 있었던 예외 메세지와 정보를 화면에 출력한다.
2. getMessage()
발생한 예외 객체의 메세지를 출력할 수 있다.
✅ 안 죽는다는 건 무엇인가? 왜 catch 문법인가?
예외가 발생하면 프로그램은 바로 다운된다. 하지만, 예외 핸들링을 하면 예외가 발생하는 부분을 건너뛰고 다른 정상 코드로 대체할 수 있다. 즉, 예외가 발생해도 꽉 붙잡아서 프로그램이 죽지 않도록 한다.
정말로 안 죽나 확인을 해보면 인덱스가 넘어가는 i = 5 예외 부분을 제외하고 그 밑에 코드가 동작을 해서 죽지 않는 것을 알 수 있다.
예외 처리를 하지 않으면 예외가 발생하여 서비스가 다운되고 이후 로직은 수행되지 않는다.
✅ 파일 자원으로 알아보는 예외 처리
파일 자원을 다루는 것으로 에러 핸들링의 예제를 알아보자 파일 자원을 다룰 때, 꼭 처리해줘야 하는 예외는 뭐가 있을까?
존재하지 않는 파일
NullPointExcetpion
기본적으로 위 2가지를 생각할 수 있다. 존재하지 않는 파일을 접근하거나, 그로인해 NPE가 발생할 수 있다. 그러면 개발자는 발생할 예외를 리스트업하고, 이로 인해 서비스가 다운되지 않도록 핸들링 해주어야 한다.
FileInputStream resource1 = null;
try {
resource1 = new FileInputStream("");
} catch (FileNotFoundException e) {
} finally {
if (resource1 != null) resource1.close();
}
파일이 존재하지 않으면 에러를 잡고, 그로 인해 자원 레퍼런스가 null 일 수 있으니 null이 아닌 경우에만 자원을 반납하도록 하는 것이 예외 핸들링이다.
- catch or throws
예외가 발생하면 잡는다 VS 예외가 발생하면 미룬다.
자바의 예외 처리에는 알아두면 좋은 규칙이 있다. 예외가 발생하면 잡을 수도 있지만, 나중에 처리하기 위해 throws 키워드로 지금은 무시하고 던질 수 있다.
이는 예외가 발생하는 코드 블록을 호출한 측으로 예외가 던져지므로, 호출한 측에서 잡지 않으면, 예외가 발생해 프로그램이 다운된다. 지금까지와 같은 원리이다.
예외가 발생하면 잡으면 예외가 발생하는 코드 대신 다른 부분이 동작하고, 잡지 않으면 서비스가 다운되는데 던지면 이를 호출한 부분으로 예외가 전달돼서 호출한 부분에서 예외가 발생한다.
🚨 둘 중 뭐가 좋을까?
예외를 당장 잡아야 하면 catch가 더 좋다. 근데 사용하다가 에러 로그를 찍고 싶다면? 호출한 쪽으로 던지고 호출한 측에서 핸들링하는 게 좋다. Spring은 예외를 처리하는 공통 예외 처리 모듈을 두어, 에러 로그를 처리한다.
'[백엔드] > [Java | 학습기록]' 카테고리의 다른 글
[Core] JVM의 역할과 동작 원리 (0) | 2024.06.19 |
---|---|
[문법] 자바 해시를 사용한 데이터 형식(HashMap, HashSet) (0) | 2024.06.05 |
[문법] try-with-resources를 사용해야 하는 이유 (0) | 2024.05.29 |
[문법] 자바 입력 데이터 저장 방식 (0) | 2024.05.28 |
[문법] Comparator, Comparable 정렬 원리 (0) | 2024.05.27 |