Throwable: 최상이 예외Error: 메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션에서 복구가 불가능한 시스템 예외Exception
- 애플리케이션 로직에서 사용할 수 있는 실질적인 최상위 예외
- Exception과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다. 단 RuntimeException은 예외
RuntimeException: 언체크 예외, 런타임 에외 - 컴파일러가 체크하지 않는 언체크 예외
- RuntimeException과 그 자식 에외는 모두 언체크 예외이다
사용자 정의 예외 계층
예외 계층화의 장점
- 부모의 예외를 잡거나 던지면 자식 예외도 함께 잡거나 던질 수 있다
- 특정 예외를 잡아서 처리하고 싶다면 하위예외를 잡아서 따로 처리하면 된다
사용자정의 최상위 예외
public class NetworkClientExceptionV3 extends Exception{
public NetworkClientExceptionV3(String message) {
super(message);
}
}
네트워크 예외와 관련된 예외는 NetworkClientException을 상속받을 수 있다
이렇게 예외 계층을 만들면 포괄적인 예외처리가 필요할 땐 NetworkClientException을 예외처리하면되고 특정 예외를 잡아서 처리하고 싶을 땐 자식 예외를 예외처리하면 된다.
연결 오류예외
public class ConnectExceptionV3 extends NetworkClientExceptionV3 {
private final String address;
public ConnectExceptionV3(String address, String message) {
super(message);
this.address = address;
}
public String getAddress() {
return address;
}
}
ConnectException은 연결 오류에 대한 예외로 NetworkClientException을 상속받는다.ConnectException예외 클래스는 자체적으로 address 네트워크 연결주소를 필드로 보관하며 get메서드를 통해서 해당 주소를 받을 수 있다.
데이터 전송오류 예외
public class SendExceptionV3 extends NetworkClientExceptionV3 {
private final String sendData;
public SendExceptionV3(String sendData, String message) {
super(message);
this.sendData = sendData;
}
public String getSendData() {
return sendData;
}
}
SendException예외 클래스는 네트워크 연결은 성공했으나 데이터를 전송하는 과정에서 발생하는 예외를 처리하기위한 클래스이다.SendException클래스 내부에는 전송하고자하는 데이터를 내부적으로 필드를 통해 보관하며 이 또한 get메서드를 통해 데이터를 조회할 수 있다.
예외를 발생시키는 클라이언트 코드
public class NetworkClientV3 {
private final String address;
public boolean connectError;
public boolean sendError;
public NetworkClientV3(String address) {
this.address = address;
}
public void connect() throws ConnectExceptionV3 {
if (connectError){
throw new ConnectExceptionV3(address, " 서버 연결 실패");
}
System.out.println(address + "서버 연결 성공");
}
public void send(String data) throws SendExceptionV3 {
if (sendError) {
throw new SendExceptionV3(data, address + " 서버 데이터 전송 실패: " + data);
}
System.out.println(address + " 서버에 데이터 전송: " + data);
}
public void disconnect() {
System.out.println(address + " 서버 연결 해제");
}
public void initError(String data) {
if (data.contains("error1")) {
connectError = true;
}
if (data.contains("error2")) {
sendError = true;
}
}
}
initError()메서드를 통해 예외가 발생하는 문자열이 입력될 경우 예외가 발생하게 된다
connect()
- 만약 "error1"문자열이 입력될 경우
ConnectException예외가 발생하게 되는데 해당 예외는throws를 통해 서비스 계층으로 예외를 넘긴다. - 정상 문자열이 넘어올 경우에는 정상흐름으로 넘어간다
send()
- 만약 "error2"문자열이 입력될 경우
SendException예외가 발생하며connect()메서드와 마찬가지로 서비스 계층으로 예외를 넘긴다 - 정상 문자열이 넘어올 경우에는 정상흐름으로 넘어간다
disconnect()
- 네트워크 연결과정에서 생성된 자원을 해제하는 메서드로 정상흐름으로 넘어던, 예외가 발생하던 반드시 호출되야하는 메서드이므로 예외처리 과정에서는
finally키워드에 작성한다.
예외를 잡는 서비스 계층
public class NetworkServiceV3_1 {
public void sendMessage(String data) {
String address = "http://example.com";
NetworkClientV3 client = new NetworkClientV3(address);
client.initError(data);
try {
client.connect();
client.send(data);
} catch (ConnectExceptionV3 e) {
System.out.println("[연결 오류] 주소: " + e.getAddress() + ", 메시지: " + e.getMessage());
} catch (SendExceptionV3 e) {
System.out.println("[전송 오류] 전송 데이터: " + e.getSendData() + ", 메시지: " + e.getMessage());
} finally {
client.disconnect();
}
}
}
try의 body부분에는 정상흐름 로직을 작성함으로써 정상흐름과 오류처리 부분에대한 코드를 분리할 수 있다catch가 2개 있는 것을 확인할 수 있는데 각각ConnectException(연결 오류),SendException(전송 오류)를 처리하는 catch부분으로 네트워크 연결과정에서 연결 오류가 발생할 수도있고, 전송오류가 발생할 수도있기때문에 catch를 2개 선언해주어야한다.
핵심인 부분은 catch를 어떻게 작성했냐인데
각각의 예외는 처리하는 방법이 다르다는 것을 확인할 수 있다 쉽게 말하면 예외에 맞는 예외 메시지를 제공함으로써 예외 메시지를 명확하게 함으로써 개발자는 에러메시지를 명확하게 확인할 수 있다.
===한마디로 예외 계층을 분리함으로써 연결 오류 예외에 대한 메시지, 전송 오류 예외에 대한 메시지를 구분하여 예외를 명확하게 확인할 수 있다===
예외처리의 일반적인 방법
체크예외를 사용하게되면 컴파일러를 통해 사전에 누락되는 예외를 방지할 수 있어 좋은 예외처리방법일 수 있지만, 실제로는 수많은 라이브러리를 사용하다보니 라이브러리에서 발생하는 예외들을 throw받으면서 특정 계층에서 엄청난 양의 예외처리를 해야하는 불상사가 발생하게 된다.
거기다 이런 예외들은 보통 물리적인 환경문제라던가, 애플리케이션 코드에서 해결할 수 없는 예외들이 대부분이다.
때문에 최근 실무에서의 예외처리 방법은 RuntimeException을 사용하여 대부분의 예외들을 생략하여 넘기고 특정 계층에서 공통적으로 예외처리하는 방법을 선호한다.
RuntimeException 계층 생성
public class NetworkClientExceptionV4 extends RuntimeException{
public NetworkClientExceptionV4(String message) {
super(message);
}
}
public class ConnectExceptionV4 extends NetworkClientExceptionV4 {
private final String address;
public ConnectExceptionV4(String address, String message) {
super(message);
this.address = address;
}
public String getAddress() {
return address;
}
}
public class SendExceptionV4 extends NetworkClientExceptionV4{
private final String data;
public SendExceptionV4(String data, String message) {
super(message);
this.data = data;
}
public String getData() {
return data;
}
}
SendException과 ConnectException 예외는 NetworkClientException을 상속받고 NetworkClientException클래스는 RuntimeException을 상속받기 때문에 런타임(언체크)예외 계층을 생성했다.
특정 문자열 입력시 런타임 예외를 발생하는 Client코드
public class NetworkClientV4 {
private final String address;
private boolean connectError;
private boolean sendError;
public NetworkClientV4(String address) {
this.address = address;
}
public void connect() {
if (connectError) {
throw new ConnectExceptionV4(address, "[연결 오류] 서버: " + address);
}
System.out.println("정상 연결");
}
public void sendMessage(String data) {
if (sendError) {
throw new SendExceptionV4(address, "[전송 오류] 서버: " + address + ", 전송 데이터: " + data);
}
System.out.println("데이터 전송 성공: " + data);
}
public void disconnect() {
System.out.println("연결 종료");
}
public void initError(String data) {
if (data.equals("error1")) {
connectError = true;
}
if (data.equals("error2")) {
sendError = true;
}
}
}
런타임 예외를 throws로 그대로 다음 메서드로 던진다.
(원한다면 catch로 잡을 수 있다)
public class NetworkServiceV4 {
public void sendMessage(String data) {
String address = "https://example.com";
NetworkClientV4 client = new NetworkClientV4(address);
client.initError(data);
try{
client.connect();
client.sendMessage(data);
} finally {
client.disconnect();
}
}
}
예외 공통처리 Main메서드 (핵심)
public class MainV4 {
public static void main(String[] args) {
NetworkServiceV4 networkService = new NetworkServiceV4();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("전송할 문자: ");
String input = sc.nextLine();
if (input.equals("exit")) {
break;
}
try {
networkService.sendMessage(input);
} catch (Exception e) {
exceptionHandler(e);
}
}
System.out.println("프로그램을 정상 종료합니다.");
}
private static void exceptionHandler(Exception e) {
System.out.println("사용자 메시지: 죄송합니다 알 수 없는 문제가 발생했습니다");
System.out.println("=== 개발자용 디버깅 메시지 ===");
e.printStackTrace();
if (e instanceof SendExceptionV4 sendException) {
System.out.println("전송 오류입니다." + sendException.getData());
}
}
}
exceptionHandler(Exception e) 이 메서드로 프로그램의 모든 예외를 처리하였다.
만약 좀 더 디테일한 예외 처리(예외 메시지)가 필요할 경우 if문과 instanceof을 사용하여 특정 예외를 처리할수도 있다.
'Language > Java' 카테고리의 다른 글
| [Java] 람다 표현식과 함수 인터페이스 (0) | 2024.11.08 |
|---|---|
| [JAVA] 자바의 정렬 인터페이스 Comparable과 Comparator (0) | 2024.07.04 |
| 해시 알고리즘 (0) | 2024.06.29 |
| [JAVA - 자료구조] HashSet (0) | 2024.06.29 |
| [JAVA] 불변객체 (0) | 2024.06.24 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!