일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- SQL
- Programming
- 오라클
- 함수
- 프로그래밍
- web
- frontend
- 데이터베이스
- 프론트엔드
- 웹
- 자바스크립트
- Database
- HTML
- oracle
- 메소드
- 자바
- java
- jsp
- 코딩
- It
- 파이썬
- 문자열
- 서블릿
- String
- PL/SQL
- Method
- Servlet
- Python
- JavaScript
- function
- Today
- Total
Untitled_Blue
[JAVA] 예외 처리 본문
안녕하세요. 이번 글은 예외 처리에 대한 설명입니다.
- 예외란 무엇이며 오류랑 무슨 차이가 있을까?
예외(Exception)란 프로그램 개발 중 또는 사용자가 프로그램을 사용하면서 규격 외의 값을 입력했거나 미숙한 조작으로 인해 발생하는 오류이며 개발자가 해결할 수 있는 범위 내 있는 점이 특징이다.
그러면 예외도 어떻게 보면 오류 내지 에러라고 볼 수 있는데 왜 예외와 에러를 따로 보는 걸까? 해답은 해결가능한 범위인가에 따라 둘의 차이가 명확하게 분류된다. 일단 둘 다 오류라는 큰 범주에 속하고 있다.
- 예외 : 개발자가 해결할 수 있는 범위의 오류 (입력값 규격 위반, 연산 불가능, 인덱스 범위 외의 값 접근 시도 등..)
- 에러 : 개발자가 해결할 수 없는 범위의 오류 (메모리 용량 초과, 쓰레드 종료 현상 등등.. 하드웨어적 문제 등이 있음)
이때 해결할 수 있는 오류는 수정한다고 보는 것이 아닌 오류가 발생하는 상황에서 차선책 내지 우회 방안을 설계한다고 봐야 한다는 점을 알아야 한다.
package classes;
import java.util.*;
public class Exception_Beta {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("INSERT NUMBER >> ");
int value = sc.nextInt();
if (value == 1) {
System.out.println("1를 입력하셨습니다...");
}
else if (value == 2) {
System.out.println("2를 입력하셨습니다...");
}
else if (value == 3) {
System.out.println("2를 입력하셨습니다...");
}
else {
System.out.println("다른 값을 입력하셨습니다.");
}
}
}
예를 들어 입력하는 공간에 숫자만 입력해야 하는데 정수가 아닌 문자를 입력하면 다음과 같은 오류가 발생하는 점을 확인할 수 있다. 예외사항으로 InputMismatchException이 발생했는데 이는 직역하면 입력하는 데이터와의 불일치로 인해 발생하는 예외라는 뜻이다. 이를 풀어서 설명하자면 정수를 입력받는 공간에 문자 같은 다른 값을 입력했을 때 발생하는 예외라는 뜻으로도 해석할 수 있다. 이는 개발자가 해결할 수 있는 범위의 오류이며 해결하기 위해서는 사용자에게 다른 값을 입력했다는 알림과 함께 다시 올바른 값을 입력할 수 있도록 유도해야 한다.
package classes;
import java.util.*;
public class Exception_Beta {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("INSERT NUMBER >> ");
try {
int value = sc.nextInt();
if (value == 1) {
System.out.println("1를 입력하셨습니다...");
} else if (value == 2) {
System.out.println("2를 입력하셨습니다...");
} else if (value == 3) {
System.out.println("2를 입력하셨습니다...");
} else {
System.out.println("다른 값을 입력하셨습니다.");
}
} catch (Exception e) {
System.out.println("다시 입력해주세요..");
}
}
}
해당 소스코드는 예외가 발생하는 부분을 수정한 것이다. 코드를 실행한 결과 정수 외 다른 문자를 입력하면 오류만 바로 출력되는 것이 아닌 사용자에게 다시 입력하라는 메시지를 출력해주고 프로그램이 종료되는 것을 확인할 수 있다. 이렇게 예외처리를 하지 않고 오류를 띄워서 사용자들을 당황시키는 것 보다는 다시 입력하라는 메시지를 보여줌으로써 사용자들에게 올바른 값을 입력하도록 유도함으로써 차선책의 개념으로 예외적인 오류를 해결할 수 있다.
(여담이지만 이렇게 입력받는 부분은 String 문자열로 받고 조건문에서 문자열 관련 메서드를 활용해서 조건을 설정하는 것이 낫다고 생각한다...)
- 예외 종류
상단 이미지와 같이 예외에도 분류가 존재한다. 일반 예외(Checked Exception) 그리고 실행 예외(Unchecked Exception) 이렇게 두 가지로 분류되고 있다. 이 둘은 상속 관계인 만큼 최상위 클래스는 Exception인 점을 확인할 수 있다.
(이미지에 나와있는 예외 말고도 다양한 예외가 존재한다. 이는 자바 API 문서를 참고하는 것을 추천한다.)
(추가로 이 부분에 대해서는 예외별 설명에 관한 글을 포스팅할 예정에 있다.)
- 일반 예외 : 컴파일(Compile)할 때 확인 가능한 예외
- 실행 예외 : 실행 및 테스트 과정에서 확인 가능한 예외
여기서 실행 예외는 실행 과정에서 확인할 수 있는 예외인 만큼 컴파일할 때는 문법적인 오류없이 정상적으로 컴파일된다고 나온다는 점을 확인할 수 있다. (이러한 이유 때문이라도 개발 후 런테스트(Run Test)는 필수이면서 중요하다...)
- try-catch-finally
예외 처리는 if문 같은 조건제어문을 통해서도 작성 가능하지만 try-catch문을 사용해서 에러를 처리할 수도 있다.
- try : 일단 소스코드를 구현해보는 구간
- catch : try 구간에서 예외 발생 시 내부 코드를 구현하는 구간
- finally : 오류 내지 예외에 관계없이 무조건 실행되는 구간 (이 부분은 선택사항)
package classes;
public class Exception_Beta {
public static void main(String[] args) {
try {
int a = 6 / 0;
System.out.println(a);
}
catch (Exception e) {
System.out.println("예외 발생 !");
}
finally {
System.out.println("try-catch-finally문을 최종적으로 종료합니다.");
}
}
}
다음은 try-catch-finally 문을 적용한 소스코드이다. 먼저 try 부분에서 6에 0을 나누고 계산 결과를 출력문을 통해 보여준다. 그러나 예외가 발생하여 catch문에 있는 출력문을 통해 예외가 발생했다는 알림을 보여주는 과정을 거친다. 그리고 마지막으로 finally문 내 코드를 구현하는 것을 끝으로 모든 예외 처리 관련 코드의 실행을 마치고 프로그램이 종료됨을 확인할 수 있다. 이때 해당 코드에서 발생한 예외는 연산 관련 오류라는 점에서 ArithmeticException이 발생한 점을 알 수 있다.
이를 통해 try문에 있는 코드를 먼저 실행하고 예외가 발생했으면 catch문으로 진입해 내부 코드를 작동시킴으로써 예외가 발생함을 알리는 과정을 확인할 수 있다. 또한 예외 발생 여부에 상관없이 finally문은 꼭 작동됨을 알 수 있다.
추가로 예외를 알려주는 과정은 다음과 같다.
- try문에서 예외가 발생한다.
- 해당 try문에서 JVM(자바 가상 머신)에게 발생한 예외를 알려주고 JVM에서는 발생한 예외 클래스의 객체를 생성한다.
- 생성한 객체를 catch문에게 전달함으로써 예외가 발생함을 알려준다.
package classes;
public class Exception_Beta {
public static void main(String[] args) {
try {
int a = 6 / 0;
System.out.println(a);
int k = Integer.parseInt("10A");
}
catch (ArithmeticException e) {
System.out.println("예외 발생 !");
}
catch (NumberFormatException e) {
System.out.println("NumberFormatException 예외 발생 !");
}
}
}
또한 다음과 같이 catch문을 한 번만 설정하는 것이 아닌 2개 이상 여러 예외처리 구문을 작성할 수 있다. 물론 해당 코드를 실행하면 int a 부분에서부터 예외가 발생해서 int k에 대한 예외사항을 별도로 처리하지 않음을 확인할 수 있을 것이다.
이렇게 이전의 예외 오류때문에 다음 줄부터의 오류를 처리할 수 없어서 닿을 수 (사용하지 못한) catch문에 대해서 unreachable code (도달할 수 없는 코드)라고 칭한다.
package classes;
public class Exception_Beta {
public static void main(String[] args) {
try {
int a = 6 / 0;
System.out.println(a);
}
catch (ArithmeticException e) {
System.out.println("예외 발생 !");
}
try {
int k = Integer.parseInt("10A");
}
catch (NumberFormatException e) {
System.out.println("NumberFormatException 예외 발생 !");
}
}
}
다음과 같이 try-catch문을 각각에 맞게 두 개를 별도로 작성한다면 2개의 예외 사항에 대한 처리 결과를 확인 가능하다.
package classes;
public class Exception_Beta {
public static void main(String[] args) {
try {
int a = 6 / 0;
System.out.println(a);
int k = Integer.parseInt("10A");
}
catch (ArithmeticException | NumberFormatException e) {
System.out.println("예외 발생 !");
}
}
}
다음과 같이 catch문의 매개변수 내에 | (OR) 연산자를 통해 여러 예외 처리도 가능하다.
- 예외를 전가하다!
예외는 발생한 지점에서 바로 처리할 수도 있지만 이를 자신을 호출한 지점(JVM)으로 전가(throws)할 수도 있다.
예외에 대처하는 방법은 크게 2가지가 있다.
- try-catch문 사용 : 예외가 발생하면 JVM에게 알려서 예외처리에 대한 객체를 catch문에 전달하는 방법
- throws 키워드 사용 : 예외 처리에 대한 의무를 호출한 메서드에 전달하는 방법
여기서 throws가 예외를 전가하는 방법이다. 이때 호출한 메서드로 전달한다고 했는데 호출한 메서드는 곧 소스코드 기준으로 [메서드명] throws Exception 같이 throws 키워드를 통해 예외를 [메서드명]에 전달하는 방식인 점에서 확인할 수 있을 것이다. 또 다르면서 쉽게 생각하자면 throws Exception를 통해 해당 오류는 넘어가자는 식으로 봐도 무방하다.
이때 throw와 혼동하지 않도록 주의해야 한다. 이는 예외를 발생시키는 요소이며 throws는 예외를 전가하는 키워드임을 확실히 구분할 필요가 있다.
package classes;
public class Exception_Beta {
void calculator_Str() throws Exception { // 이 부분에서 예외를 메인 메서드에게 전가한다.
int a = Integer.parseInt("20K");
System.out.println(a);
}
public static void main(String[] args) {
Exception_Beta obj = new Exception_Beta();
try {
obj.calculator_Str(); // 메서드 호출
} catch (Exception e) { // 예외 처리
System.out.println("main 메서드에서 예외 발생 !");
}
}
}
다음 코드는 예외를 전가하는 예시를 보여주고 있다. 또한 바로 하단 이미지에서는 소스코드가 돌아가는 로직을 보여주고 있다. 순서를 조금 더 설명하자면 먼저 클래스 내에 void 형식의 메서드가 있는데 바로 뒤에 throws Exception이 붙어있는 것을 볼 수 있다. 이는 만약 해당 메서드에서 예외가 발생하면 자신 (메서드)를 호출한 곳으로 예외를 전가하겠다는 뜻이다. 이를 메인 메서드에서 try 구문 안에다 메서드를 호출한다. 그리고 메서드를 구현하는데 예외가 발생해서 이를 메인 메서드에 전가한다. 그러면 예외 처리를 메인 메서드에서 진행해야 되는데 이를 catch 구문에서 처리한다. catch 구문 내 출력문을 출력하는 것을 끝으로 모든 예외 처리가 종료됨을 확인할 수 있다.
상단의 코드에서 왜 예외가 발생하냐고 묻는다면 문자열을 정수로 변환하는 과정에서 올바르지 못한 값이 들어갔기 때문에 Format 관련 예외가 발생한 것이다.
- 사용자가 정의하는 예외 클래스
예외 처리는 위에서 설명한 2가지 방법 외에도 사용자가 상황에 맞게 클래스를 통해 예외 처리를 설정할 수도 있다. 그 외에도 자바 공식 API에 없는 예외도 개발자가 임의로 정의할 수도 있다. 이를 사용자 정의 예외 클래스라고 한다.
package classes;
public class Exception_Beta {
public static void main(String[] args) {
try {
int a = 0;
if (a == 0) {
throw new MyException("0을 대입할 수 없습니다.");
}
else {
System.out.println(a);
}
} catch (MyException e) { // 예외 처리
System.out.println(e.getMessage());
}
}
}
class MyException extends Exception {
MyException(String msg) {
super(msg); // 부모 생성자 Exception 호출
}
}
다음은 임의로 지정한 예외 클래스를 나타내는 소스코드이다. 로직 순서는 먼저 try 구문 안에서부터 시작된다. 안에서 변수 a에 대한 값을 0이라고 할당하고 이를 if else 조건문에서 값에 대한 검사를 진행한다. 여기서 a의 값이 0이면 throw new [사용자 정의 예외 클래스명]을 통해서 예외를 발생시킨다. 그 다음 MyException 클래스 내부로 진입해서 생성자를 사용함과 동시에 msg라는 매개변수를 토대로 부모 클래스인 Exception의 생성자를 호출해서 예외처리를 진행한다. 그 다음 다시 메인 메서드 내 catch 구문으로 돌아와서 받아온 문자열 값을 토대로 .getMessage()를 사용해서 설정해둔 텍스트를 출력해서 예외를 처리하는 식으로 진행된다.
+ 예외 관련 메서드
- .getMessage() : 예외 발생 시 생성자로 전달한 문자열을 리턴하는 역할을 담당하는 메서드
- .printStackTrace() : 예외가 전가 내지 진행된 과정을 보여주는 메서드
package classes;
public class Exception_Beta {
public static void main(String[] args) {
try {
int a = 0;
if (a == 0) {
throw new MyException("0을 대입할 수 없습니다.");
}
else {
System.out.println(a);
}
} catch (MyException e) { // 예외 처리
System.out.println(e.getMessage());
System.out.print("e.printStackTrace() >> ");
e.printStackTrace();
}
}
}
class MyException extends Exception {
MyException(String msg) {
super(msg); // 부모 생성자 Exception 호출
}
}
다음은 예외 처리와 관련된 메서드를 사용한 소스코드와 실행 결과이다. 이처럼 .getMessage()를 통해 예외 발생할 때 설정해둔 메시지를 출력할 수 있으며 .printStackTrace()를 통해 예외가 발생하는 과정을 보여줄 뿐만 아니라 예외의 원인이 몇 번째 줄에 위치되었는지를 확인할 수 있다는 점을 확인할 수 있다.
다음 글은 제너릭에 대한 설명입니다.
'Programming Language > JAVA' 카테고리의 다른 글
[JAVA] 컬렉션 프레임워크 2 - Set (0) | 2023.06.05 |
---|---|
[JAVA] 컬렉션 프레임워크 1 - List, Stack (0) | 2023.06.04 |
[JAVA] 인터페이스 (0) | 2023.05.21 |
[JAVA] final, abstract 제어자 (0) | 2023.05.17 |
[JAVA] 상속 - super(), super 키워드, 오버라이딩, 형변환 (1) | 2023.05.16 |